اعد ضبط Mistral 7B وانشئ نموذجك اللغوي الخاص – الجزء 2

عملية ضبط نماذج اللغات الضخمة LLM عملية مباشرة الى حد ما, نحتاج فقط في بعض النماذج الى اتباع طريقة التعليمات التي سنقوم بتغذية النموذج بها عند اعادة الضبط, هناك طريقتين لإعادة ضبط نماذج اللغات الضخمة LLM

  1. الضبط الخاضع للإشراف Supervised Fine-Tuning (SFT): هنا تكون مجموعة البيانات عبارة عن تعليمات واجابات. يكون الهدف هو ضبط الأوزان لنموذج حتى تكون الإجحابات قريبة من الإجابات في مجموعة البيانات Ground-truth.
  2. الضبط المعزز من التغذية الراجعة البشرية Reinforcement learning from Human feedback (RLHF): هنا النموذج يتفاعل مع البيئة ويحصل على تغذية راجعة.

يوجد ايضًا تقنيات أخرى لعمليات ضبط النموذج. لكن هنا سيكون تركيزنا على الطريقة الأولى وسيكون التنفيذ سهل وواضح ولكن نحتاج الى توضيح بعض المفاهيم.

النماذج اللغوية الضخمة هل نقدر نضيفها على GPU؟ وش هو الـQuantize!

لتوضيح هذه النقطة خلنا ناخذ مثال, تخيل أن لديك مكتبة ضخمة من الصور عالية الوضوح التي تريد تخزينها بشكل أكثر كفاءة على جهاز الكمبيوتر الخاص بك. قررت ضغط هذه الصور إلى تنسيق ملف أصغر، مما يقلل من حجمها بشكل كبير (ما يعادل نموذج التدريب المحتوي على 4 بت). ومع ذلك، لا تزال ترغب في القدرة على إضافة علامات شخصية وملاحظات إلى كل صورة.

الآن، اعتبر تقنية تسمى “مذكرات البكسل”، وهي مشابهة لـ QLoRA. مذكرات البكسل لا تقوم فقط بضغط الصور بل تتيح لك أيضًا إرفاق ملاحظات أو توضيحات بفترة معينة من البكسل. في هذه الحالة، عندما تقوم بإضافة ملاحظة إلى بكسل معين، يضمن لك مذكرات البكسل أن خوارزمية الضغط تتكيف مع هذه الملاحظات أثناء عملية التدريب.

لذا، تمامًا كما يُمكن QLoRA من backpropagation of gradients من خلال النموذج المضغوط إلى المحولات، تُتيح مذكرات البكسل لك دمج المعلومات الشخصية في الصور المضغوطة من خلال تكييف عملية الضغط لاحتواء هذه الملاحظات الإضافية أثناء عملية التدريب.

ما نتيجة هذه الابتكارات؟

يمكنك الآن ضبط نموذج بدقة 65 مليار معلمة على وحدة معالجة رسومات (GPU) واحدة سعة 48 جيجابايت، مع الحفاظ على نفس الأداء مثل الضبط الدقيق الكامل 16 بت. هذا يشبه إمكانية تشغيل نفس عدد البرامج بنفس الكفاءة، ولكن باستخدام طاقة أقل بكثير.

علاوة على ذلك، تسمح QLoRA بضبط النماذج على نطاقات سيكون من المستحيل استكشافها باستخدام الضبط الدقيق الكامل 16 بت على أجهزة البحث الأكاديمية. هذا يفتح إمكانيات جديدة للبحث وتطبيق LLMs.

إحدى أفضل النماذج، المسماة Guanaco، تتفوق على جميع النماذج المفتوحة المصدر السابقة على معيار Vicuna، حيث تصل إلى 99.3% من مستوى أداء ChatGPT بينما تتطلب فقط 24 ساعة من الضبط الدقيق على وحدة معالجة رسومات واحدة. هذا يشبه مهندسنا الذي لا يقتصر على تطوير برمجياته وفقًا للمتطلبات الفنية فحسب، بل يبتكر أيضًا حلولاً تتفوق على جميع الخيارات المتاحة، وكل ذلك في يوم واحد فقط!

تحميل وإضافة المكتبات

سنقوم بتحميل بعض المكتبات المطلوبة لضبط النموذج. ساقوم بتوضيح وظيفة كل مكتبة واستخداماته

%pip install -U bitsandbytes
%pip install -U transformers
%pip install -U peft
%pip install -U accelerate
%pip install -U trl
%pip install -U wandb
المكتبةالغرض
bitsandbytesتوفير امكانيات للتعامل مع البيانات الثنائية والبتات.
transformersمكتبة لتحميل واستخدام نماذج تحويل اللغة الطبيعية (NLP) المدربة مسبقًا والتعامل معها.
peftضبط النماذج الضخمة دون الحاجة لإعادة ضبط جميع المُعلمات الخاص بالنموذج.
accelerateتسريع تدريب النماذج باستخدام PyTorch و TensorFlow.
trlتستخدم لتدريب النماذج والمحولات بطرق تدريب مختلف من ظمنها SFT.
wandbلمتابعة تفاصيل التدريب والتجارب, وعرض تمثيل بياني لعمليات التدريب
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig,HfArgumentParser,TrainingArguments,pipeline, logging
from peft import LoraConfig, PeftModel, prepare_model_for_kbit_training, get_peft_model
import os,torch, wandb
from datasets import load_dataset
from trl import SFTTrainer

الان سنقوم بتسجيل الدخول على موقع Weights & Biases لتسجيل بيانات التجربة التي نقوم بها, طبعًا موقع Weights & Biases غني عن التعريف ويمكنك إستبدالة بـTensorboard.

wandb.login(key = secret_wandb)
run = wandb.init(
    project='Fine tuning mistral Instruct 7B',
    job_type="training",
    anonymous="allow"
)

الآن سنقوم بتعريف بعض المتغيرات التي تخص الـنموذج,

model_name = "mistralai/Mistral-7B-Instruct-v0.1"
dataset_name = "Aljo3aid/share-gpt"
new_model = "mistral-7b-instruct-share-gpt"

تحميل مجموعة البيانات

سنقوم بتحميل مجموعة البيانات التي قمنا بتجهيزها في المقال السابق. المميز في HF هو سهولة تحميل والتعامل مع مجموعات البيانات والنماذج, فقط تحتاج الى كتابة اسم المنشئ لمجموعة البيانات واسمها بشكل مباشر.

dataset = load_dataset(dataset_name, split="train")
dataset["msg"][100]

تحميل Toknizer

الآن سنقوم بتحمل Toknizer حتى نقوم بتجهيز البيانات لإعادة الضبط, في هذه الحالة سنقوم باستخدام mistralai/Mistral-7B-Instruct-v0.1

tokenizer = AutoTokenizer.from_pretrained('mistralai/Mistral-7B-Instruct-v0.1')
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

المتغيرات والإعدادات

التكميم – Quantization

تقنيات Quantization تقلل من مقدار الذاكرة والحساب عن طريق تمثيل الأوزان والتنشيطات بأنواع بيانات أقل دقة، مثل الأعداد الصحيحة 8 بت (int8). هذا يتيح تحميل نماذج أكبر لا يمكنك عادةً وضعها في الذاكرة، وتسريع الاستدلال Infernce. تدعم Transformers خوارزميات التكميم AWQ و GPTQ، كما تدعم Quantization 8 بت و 4 بت باستخدام bitsandbytes.

يعمل Quantization على تصغير حجم البيانات عن طريق استخدام أنواع بيانات أصغر لتمثيلها. هذا يقلل من مقدار الذاكرة التي تحتاجها البيانات، ويجعل من الممكن تحميل نماذج أكبر وتشغيلها بشكل أسرع.

فلنتخيل موقفًا يشبه Quantization من خلال مثال الخزانة الملابس. تخيل أن لديك خزانة ملابس ضخمة، مليئة بالملابس المختلفة، حيث تمثل كل قطعة منها بيانات تأخذ مساحة في الذاكرة. الفكرة هنا هي أنك ترغب في زيادة كمية الملابس التي يمكنك تخزينها في الخزانة الخاصة بك.

الآن، تخيل أنك اكتشفت طريقة لطي كل قطعة من الملابس بدقة ووضعها في أكياس محكمة الإغلاق. هذا التطور يعني أنك الآن تستطيع توفير مساحة أكبر في الخزانة، مما يسمح لك بتخزين المزيد من الملابس مما كنت تستطيع في السابق. وهذا بالضبط ما يحدث عند استخدام تقنيات Quantization للبيانات، حيث تقلل من حجمها بدقة، مما يسهم في توفير مساحة أكبر في الذاكرة وتحسين سرعة الأداء.

الآن سنقوم باستخدام BitsAndBytesConfig للقيام بهذه المهمة وستكون مباشرة وبدون تعقيدات حسابية

bnb_config = BitsAndBytesConfig(
    load_in_4bit= True,
    bnb_4bit_quant_type= "nf4",
    bnb_4bit_compute_dtype= torch.bfloat16,
    bnb_4bit_use_double_quant= False,
)

تهيئة LoRA

في مقال سابق شرحت LoRA والفكرة خلفه, يمكنك الرجوع للمقال وفهمه قبل التكملة هنا. سنقوم بكتابة الكود التالي بشكل بسيط , يمكنك التعديل عليه حتى تجد افضل الإعدادات. هذا الكود يستعمل مكتبة PEFT لتطبيق طريقة LoRA، وهي طريقة لتحسين أداء النماذج اللغوية الكبيرة بدون زيادة كبيرة في عدد المتغيرات.

peft_config = LoraConfig(
    lora_alpha=16,
    lora_dropout=0.1
    r=64,
    bias="none",
    task_type="CAUSAL_LM",
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj","gate_proj"]
)
  • peft_config = LoraConfig(...): هذا السطر ينشئ كائن جديد اسمه peft_config من نوع LoraConfig. هذا الكائن سيحتوي على الإعدادات الخاصة بتطبيق LoRA.
  • lora_alpha=16: هذه القيمة تحدد مقدار تأثير LoRA على النموذج. زيادة هذه القيمة يعني زيادة تأثير LoRA.
  • lora_dropout=0.1: هذه القيمة تحدد نسبة التسرب أثناء التدريب لمنع فرط التجهيز.
  • r=64: هذه القيمة تحدد بُعد المصفوفات منخفضة الرتبة المستخدمة في LoRA.
  • bias="none": هذا يعني أن LoRA لن تضيف أي انحيازات إلى النموذج.
  • task_type="CAUSAL_LM": هذا يعني أن النموذج المستهدف هو نموذج لغة احتمالي.
  • target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj"]: هذه القائمة تحدد وحدات الانتباه Attention المحددة التي سيتم تطبيق LoRA عليها.

تحميل Model

الان سنقوم بكتابة هذا الكود

model =AutoModelForCausalLM.from_pretrained(
        model_name,
        load_in_4bit=True,
        quantization_config=bnb_config,
        torch_dtype=torch.bfloat16,
        device_map="auto",
        trust_remote_code=True,
)

model = prepare_model_for_kbit_training(model)
model.config.use_cache = False !
model.config.pretraining_tp = 1
model.gradient_checkpointing_enable()

1. تحميل النموذج المسبق التدريب:

  • model = AutoModelForCausalLM.from_pretrained(...): هذا السطر يقوم بتحميل نموذج لغة مسبق التدريب من HF.
  • model_name: هذه القيمة تحدد اسم النموذج المراد تحميله (“mistralai/Mistral-7B-Instruct-v0.1”).
  • load_in_4bit=True: هذه القيمة تخبر المكتبة بتحميل النموذج في تمثيل 4 بت لتوفير الذاكرة.
  • quantization_config=bnb_config: يمرر إعدادات Quantization من كائن bnb_config الذي يحتوي على تفاصيل كيفية Quantization النموذج.
  • torch_dtype=torch.bfloat16: يحدد نوع البيانات المراد استخدامها أثناء التدريب، وهو bfloat16 الذي يوازن بين الدقة واستهلاك الذاكرة.
  • device_map="auto": يجعل المكتبة تخصص العمليات الحسابية على الأجهزة المتاحة تلقائيا (CPUأو GPU).
  • trust_remote_code=True: يسمح بتحميل كود خارجي أثناء تحميل النموذج.

2. إعداد النموذج للتدريب بدقة منخفضة:

  • model = prepare_model_for_kbit_training(model): يقوم بتجهيز النموذج للتدريب باستخدام دقة منخفضة (مثل 4 بت).

3. تعطيل التخزين المؤقت:

  • model.config.use_cache = False: يلغي التخزين المؤقت لتجنب تحذيرات أثناء التدريب، ولكن يجب إعادة تفعيله عند استخدام النموذج للاستدلال.

4. تحديد عدد الخطوات الزمنية:

  • model.config.pretraining_tp = 1: يحدد عدد الخطوات الزمنية المستخدمة أثناء التدريب.

5. تفعيل نقاط التفتيش التدرجية:

  • model.gradient_checkpointing_enable(): يقوم بتفعيل تقنية نقاط التفتيش التدرجية لتقليل استهلاك الذاكرة أثناء التدريب.

دمج PEFT مع النموذج المدرب

بمجرد إعداد LoraConfig، قم بإنشاء PeftModel باستخدام دالة get_peft_model(). تأخذ هذه الدالة نموذجًا أساسيًا – الذي يمكنك تحميله من مكتبة Transformers – و LoraConfig الذي يحتوي على المعلمات حول كيفية تكوين نموذج للتدريب باستخدام LoRA والذي قمنا بتهيئته سابقا

model = get_peft_model(model, peft_config)

1. استدعاء دالة get_peft_model():

  • يمرر الكائنين إلى الدالة:
    • model: النموذج اللغوي المراد تحسينه باستخدام PEFT.
    • peft_config: كائن يحتوي على إعدادات PEFT المطلوبة.

2. معالجة النموذج داخل الدالة:

  • تأخذ الدالة النموذج وتقوم بتطبيق تقنية PEFT عليه بناءً على الإعدادات الموجودة في peft_config.
  • قد تتضمن هذه المعالجة:
    • إضافة طبقات تكييف جديدة إلى النموذج.
    • تعديل أو تخفيف بعض الأجزاء الموجودة في النموذج.
    • تطبيق تقنيات تقليل عدد المتغيرات، مثل LoRA أو Adapters.

3. إرجاع النموذج المحسن:

  • تعيد الدالة نموذجًا جديدًا تم تطبيق PEFT عليه.
  • هذا النموذج المحسن عادةً ما يكون أكثر كفاءة في استخدام الذاكرة والحساب، مع الحفاظ على أداء مشابه أو أفضل من النموذج الأصلي.

إعدادات التدريب

الام بقي فقط خطوة إعداد عملية التدريب, وهي عوامل نحددها مثل عدد epoches! او Learning Rate والعديد من العوامل التي يمكن تغييرها قبل عملية التدريب والتي بالطبع سيكون لها تأثير في النتيجة

training_arguments = TrainingArguments(
    output_dir="./results",
    overwrite_output_dir=True,
    seed=42,
    num_train_epochs=1,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=1,
    optim="paged_adamw_32bit",
    save_steps=1000,
    save_total_limit=2,
    eval_steps=1000,
    logging_steps=25,
    learning_rate=2e-4,
    weight_decay=0.001,
    fp16=False,
    bf16=False,
    max_grad_norm=0.3,
    max_steps=-1,
    warmup_ratio=0.03,
    group_by_length=True,
    lr_scheduler_type="constant",
    report_to="wandb"
)

بعض هذه العوامل قد تكون على معرفة بها مثل num_train_epochs و seed والبعض الاخر واضح من اسم المعامل. هنا توضيح بسيط لها:
إليك توضيح الإعدادات الرئيسية:

  • output_dir="./results": يحدد المسار حيث سيتم حفظ نتائج التدريب.
  • overwrite_output_dir=True: يسمح بالكتابة فوق أي ملفات موجودة في مجلد النتائج.
  • seed=42: يضبط Seeds لضمان إمكانية استنساخ النتائج.
  • num_train_epochs=1: يحدد عدد الدورات الكاملة للمرور على مجموعة البيانات أثناء التدريب.
  • per_device_train_batch_size=4: يحدد حجم دفعة التدريب لكل جهاز (CPU أو GPU).
  • gradient_accumulation_steps=1: يحدد عدد الخطوات لتجميع Gradient قبل تحديث الأوزان، مما يقلل من استخدام الذاكرة.
  • optim="paged_adamw_32bit": يحدد خوارزمية المُحسِّن Optimization، وهي هنا AdamW.
  • save_steps=1000: يحدد عدد خطوات التدريب بين حفظ Cherckpoints للنموذج.
  • save_total_limit=2: يحدد العدد الأقصى من Cherckpoints المحفوظة.
  • eval_steps=1000: يحدد عدد خطوات التدريب بين عمليات التقييم على مجموعة الاختبار.
  • logging_steps=25: يحدد عدد خطوات التدريب بين تسجيل المعلومات في سجل التدريب.
  • learning_rate=2e-4: يحدد معدل التعلم، وهو مدى سرعة تحديث الأوزان أثناء التدريب.
  • weight_decay=0.001: يضيف مصطلح تسوية إلى دالة الخسارة لمنع فرط التجهيز Overfitting.
  • fp16=False و bf16=False: يعنيان عدم استخدام دقة 16 بت أو bfloat16 أثناء التدريب.
  • max_steps=-1: يعني عدم تحديد عدد أقصى للخطوات، وسيستمر التدريب حتى تنتهي جميع الدورات.
  • lr_scheduler_type="constant": يحدد نوع جدولة معدل التعلم، وهو هنا ثابت.
  • report_to="wandb": يرسل معلومات التدريب إلى لوحة Wandb للتتبع والمراقبة.

التعلم تحت الإشراف (SFT Trainer)

في هذه الخطوة سنقوم بضبط Supervised Fine-tuning Trainer, تحسين التدريب بالإشراف (أو SFT بإختصار).

trainer = SFTTrainer(
    model=model,
    train_dataset=dataset,
    peft_config=peft_config,
    max_seq_length= 512,
    dataset_text_field="msg",
    tokenizer=tokenizer,
    args=training_arguments,
    packing= False,
)
  • model=model: يمرر النموذج اللغوي الذي سيتم تدريبه.
  • train_dataset=dataset: يمرر مجموعة البيانات التي سيتم استخدامها في التدريب.
  • peft_config=peft_config: يمرر إعدادات PEFT لتحسين أداء النموذج (كما شرحت سابقا).
  • max_seq_length=512: يحدد الحد الأقصى لطول التسلسلات النصية التي يمكن للنموذج معالجتها.
  • dataset_text_field="msg": يحدد اسم الحقل في مجموعة البيانات الذي يحتوي على النص.
  • tokenizer=tokenizer: يمرر Tokenizer الذي سيستخدمه النموذج لToken النص إلى كلمات رمزية.
  • args=training_arguments: يمرر إعدادات التدريب التي قمنا بتحديدها سابقا.
  • packing=False: يمنع جمع أمثلة متعددة في دفعة واحدة، مما يعني أن كل مثال سيتم معالجته على حدة.

بعد ذلك سنقوم بعملية التديب

trainer.train()

حفظ النموذج

الآن وبعد انتهاء عملية التدريب, سنقوم بحفظ النموذج الخاص بنا والمعدل لإستخدامه في الاستلال Inference.

trainer.model.save_pretrained(new_model)
wandb.finish()
model.config.use_cache = True
model.eval()

1. حفظ النموذج المُدرّب:

  • trainer.model.save_pretrained(new_model): يحفظ النموذج الذي تم تدريبه في المجلد المُحدد باسم new_model. يتضمن ذلك الأوزان وإعدادات النموذج.
    2. إنهاء جلسة Wandb:
  • wandb.finish(): يقوم بإغلاق جلسة Wandb، مما يعني إيقاف إرسال معلومات التدريب إلى لوحة Wandb.
    3. تفعيل التخزين المؤقت:
  • model.config.use_cache = True: يقوم بتفعيل التخزين المؤقت للنموذج، مما قد يحسن سرعة الاستدلال.
    4. وضع النموذج في وضع التقييم:
  • model.eval(): يضع النموذج في وضع التقييم، وهو الوضع المناسب لاستخدامه عند توليد النص أو إجراء مهام أخرى لا تتطلب تحديث الأوزان.

الإستدلال

بعد عملية التدريب سنقوم بسؤال النموذج عدة اسئلة ونرى النتائج التي يقدمها لنا

logging.set_verbosity(logging.CRITICAL)
prompt = "How do I create a data science project?"
pipe = pipeline(task="text-generation", model=model, tokenizer=tokenizer, max_length=200)
result = pipe(f"<s>[INST] {prompt} [/INST]")
print(result[0]['generated_text'])

1. اخفاء الرسائل:

  • logging.set_verbosity(logging.CRITICAL): يوقف طباعة رسائل التتبع أثناء تنفيذ الكود، مما يبقي الإخراج نظيفًا.
    2. إعداد الموجه:
  • prompt = "How do I create a data science project?": يحدد السؤال الذي سيتم تقديمه للنموذج لتوليد نص بناءً عليه.
    3. إنشاء Pipe توليد النص:
  • pipe = pipeline(task="text-generation", model=model, tokenizer=tokenizer, max_length=200): ينشئ أنبوباً (pipeline) خاصاً بمهمة توليد النص، باستخدام النموذج والمُكنز المحددين، ويحدد الحد الأقصى لطول النص الناتج بـ 200 كلمة.
    4. توليد النص:
  • result = pipe(f"<s>[INST] {prompt} [/INST]"): يرسل الموجه إلى الأنبوب لتوليد نص، مع إضافة علامات خاصة في بداية ونهاية الموجه لتحديده بوضوح.
    5. طباعة النص الناتج:
  • print(result[0]['generated_text']): يطبع النص الذي تم توليده بواسطة النموذج.

النتيجة

<s>[INST] How do I create a data science project?
[/INST]Creating a data science project involves several steps, including: 1. Identifying the problem or question you want to address. 2. Gathering and cleaning the data you will use. 3. Exploratory data analysis to understand the data and identify patterns. 4. Preparing the data for analysis, including feature engineering and data preprocessing. 5. Building and evaluating models to solve the problem or answer the question. 6. Interpreting the results and drawing conclusions. 7. Communicating the findings to stakeholders. It is important to have a clear understanding of the problem or question you want to address, as well as the data you will use, before beginning the project. It is also important to have a solid understanding of the data and the problem before building and evaluating models. Additionally, it is important to have a

الخاتمة

في هذه المقالة، ناقشنا كيفية إعادة تدريب نموذج لغة كبير باستخدام تقنية PEFT. أظهرنا أن هذه التقنية يمكن أن تساعد في تحسين أداء النموذج مع الحفاظ على حجمه واستهلاكه للذاكرة.

بشكل عام، يمكن أن تكون تقنية PEFT أداة مفيدة لتحسين أداء النماذج اللغوية الكبيرة. يمكن استخدامها لتحسين أداء النماذج على مجموعة متنوعة من المهام، بما في ذلك الترجمة الآلية والاستجابة للأسئلة والكتابة الإبداعية.

فيما يلي بعض النصائح الإضافية لتحسين نتائج إعادة التدريب باستخدام PEFT:

  • استخدام إعدادات التدريب المناسبة: يمكن أن تؤثر إعدادات التدريب بشكل كبير على نتائج إعادة التدريب. من المهم اختيار إعدادات التدريب المناسبة لنموذجك ومهمة التعلم.
  • مراقبة عملية التدريب: من المهم مراقبة عملية التدريب عن كثب لتحديد ما إذا كانت تسير على المسار الصحيح. إذا لاحظت أن أداء النموذج يتدهور، فقد تحتاج إلى تعديل إعدادات التدريب.
  • إجراء التجارب: قد يكون من المفيد إجراء بعض التجارب لتحديد أفضل إعدادات PEFT لنموذجك ومهمة التعلم. يمكنك القيام بذلك عن طريق ضبط قيم المعلمات المختلفة لـ PEFT ومراقبة تأثيرها على أداء النموذج.

أتمنى أن تكون هذه المقالة مفيدة لك.

اترك ردّاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *