ترنسفورمر – Fine-tuning مدلهای زبانی
در پستهای قبلی پیرامون ترنسفورمر، بررسی کردیم که مدلهای زبانی چگونه کار میکنند و چگونه میتوان از آنها برای وظایف مختلف مانند تولید متن و طبقهبندی توالی استفاده کرد. مشاهده کردیم که مدلهای زبانی بدون نیاز به آموزش بیشتر میتوانند در بسیاری از وظایف مفید باشند، با استفاده از prompts مناسب و قابلیتهای zero-shot این مدلها. همچنین برخی از صدها هزار مدل از پیش آموزشدیده شده توسط جامعه را بررسی کردیم. در این پست آموزشی، خواهیم دید که چگونه میتوان عملکرد مدلهای زبانی را بر روی وظایف خاص با Fine-Tune آنها روی دادههای خود بهبود بخشید.
اگر سلسله پستهای قبلی پیرامون ترنسفورمر را ندیدید میتوانید از اینجا مطالعه کنید:
در حالی که مدلهای از پیش آموزشدیده شده قابلیتهای شگفتانگیزی را نشان میدهند، آموزش عمومی آنها ممکن است برای برخی وظایف یا حوزهها مناسب نباشد. Fine-Tune برای تطبیق درک مدل با جزئیات دادهها یا وظیفه خاص ضروری است. به عنوان مثال، در زمینه تحقیق پزشکی، یک مدل زبانی که بر روی متنهای عمومی وب آموزش دیده است، بهخوبی کار نخواهد کرد، بنابراین میتوانیم آن را بر روی مجموعهای از ادبیات پزشکی Fine-Tune کنیم تا توانایی آن در تولید متنهای پزشکی مرتبط یا کمک به استخراج اطلاعات از مستندات بهداشتی را افزایش دهیم. مثال دیگری برای ساخت مدلهای مکالمهای است. مدلهای بزرگ از پیش آموزشدیده شده برای پیشبینی توکن بعدی آموزش دیدهاند، و معمولاً برای مکالمات یا کاربرد چتبات از همان ابتدا کار نمیکند. ما میتوانیم این مدل را بر روی یک مجموعه داده شامل مکالمات روزمره و ساختارهای زبانی غیررسمی Fine-Tune کنیم و مدل را به تولید متنی مکالمهای و جذاب، همچون چیزی که در رابطهایی مانند ChatGPT انتظار دارید، تطبیق دهیم.
هدف این آموزش ایجاد پایههای قوی در Fine-Tune مدلهای زبانی بزرگ (LLMs) است و بنابراین موارد زیر را پوشش خواهیم داد:
- طبقهبندی موضوع یک متن با استفاده از یک مدل ترنسفورمر Fine-Tune شده
- تولید متن به سبک خاص با استفاده از مدل دیکودر
- حل وظایف متعدد با یک مدل واحد از طریق Fine-Tune دستورات
- تکنیکهای Fine-Tune کارآمد از نظر پارامتر که به ما امکان میدهند مدلها را با GPUهای کوچکتر آموزش دهیم
- تکنیکهایی که به ما امکان میدهند مدلها را با محاسبات کمتر اجرا کنیم
طبقهبندی متن
قبل از ورود به دنیای مدلهای مولد، ایده خوبی است که جریان کلی Fine-Tune یک مدل از پیش آموزشدیده شده را درک کنیم. برای این منظور، با طبقهبندی توالی(sequence classification) شروع میکنیم. طبقهبندی یک توالی یکی از مسائل کلاسیک یادگیری ماشینی است که در مسائلی نظیر تشخیص اسپم، شناسایی احساسات، طبقهبندی intent و تشخیص محتوای جعلی کاربرد دارد.
ما یک مدل را برای طبقهبندی موضوع خلاصه مقالات خبری کوتاه Fine-Tune خواهیم کرد. همانطور که در دورهی جامع یادگیری عمیق آموختیم، Fine-Tune نیاز به محاسبات و دادههای بسیار کمتری نسبت به آموزش یک مدل از ابتدا دارد. روند معمولا به این صورت است:
- شناسایی یک مجموعه داده برای وظیفه یا مساله
- تعریف نوع مدل مورد نیاز (انکودر، دیکودر یا انکودر-دیکودر)
- شناسایی یک مدل پایه خوب که نیازهای ما را برآورده میکند
- پیشپردازش مجموعه داده
- تعریف معیارهای ارزیابی
- آموزش مدل
1- شناسایی مجموعه داده
بسته به مساله و مورد استفاده خود، میتوانید از یک مجموعه داده عمومی یا خصوصی (مثلاً یک مجموعه داده از شرکت خود) استفاده کنید. هدف تطبیق یک مدل زبانی بزرگ عمومی (که معمولا برای ادامه دادن متن استفاده میشود) برای وظیقهی طبقهبندی متن است که بدین منظور باید دستههایی که مورد نظرمان است را به آن آموزش دهیم. برخی از مکانهای خوب برای یافتن مجموعه دادههای عمومی، مجموعه دادههای Hugging Face، Kaggle، Zenodo و جستجوی مجموعه داده گوگل هستند.
از جمله مجموعه دادههای پر دانلود انگلیسی، مجموعه داده AG News است. این مجموعه داده یک مجموعه داده غیر تجاری شناخته شده که برای ارزیابی مدلهای طبقهبندی متن و تحقیق در زمینههای دادهکاوی، بازیابی اطلاعات و استریمینگ دادهها استفاده میشود.
اولین کار باید بررسی مجموعه داده باشد. همانطور که در زیر نشان داده شده است، مجموعه داده شامل دو ستون است: یک ستون متن و دیگری برچسب یا لیبل. مجموعه داده ذکر شده 120,000 نمونه آموزشی دارد که برای Fine-Tune یک مدل بیش از اندازه هم هست. Fine-Tune به دادههای بسیار کمی در مقایسه با آموزش اولیه مدل نیاز دارد و فقط با استفاده از چند هزار نمونه باید بتوانیم یک مدل پایه خوب را ایجاد کنیم.
from datasets import load_dataset raw_datasets = load_dataset("ag_news") raw_datasets
DatasetDict({ test: Dataset({ features: ['text', 'label'], num_rows: 7600 }) train: Dataset({ features: ['text', 'label'], num_rows: 120000 }) })
بیایید به یکی از نمونه ها نگاه کنیم:
raw_train_dataset = raw_datasets["train"] raw_train_dataset[0]
{'label': 2, 'text': 'Wall St. Bears Claw Back Into the Black (Reuters) Reuters ' "- Short-sellers, Wall Street's dwindling\\band of " 'ultra-cynics, are seeing green again.'}
اولین نمونه حاوی متن و یک برچسب است که برچسب آن 2 است؟ 2 به کدام کلاس اشاره دارد؟ برای فهمیدن این موضوع، میتوانیم به فیلد برچسب مجموعه داده خود نگاه کنیم:
print(raw_train_dataset.features)
{'label': ClassLabel(names=['World', 'Sports', 'Business', 'Sci/Tech'], id=None), 'text': Value(dtype='string', id=None)}
عالی! بنابراین برچسب یا لیبل 0 به معنای خبرهای مربوط به جهان، 1 به معنای خبرهای ورزشی، 2 به معنای خبرهای تجاری، و 3 به معنای خبرهای علمی و فناوری است. حال بیایید تصمیم بگیریم که از کدام مدل استفاده کنیم بهتر است!
2- انتخاب نوع مدل برای استفاده
اگر مطلب آموزشی قبلی، بخش 1 مربوط به ترنسفورمر را بخواهیم مرور کنیم، بسته به نوع مساله یا وظیفهای که قصد حل کردن آن را داریم، میتوانیم از سه نوع مدل ترنسفورمر استفاده کنیم:
- مدلهای انکودر: این مدلها برای بهدست آوردن نمایشهای غنی از دنبالهها عالی هستند. آنها امبدینگها یا تعبیههایی (embeddings) تولید میکنند که اطلاعات معنایی در مورد ورودی را شامل میشود. سپس میتوانیم یک شبکه کوچک روی این امبدینگها اضافه کنیم و آن را برای یک وظیفه خاص جدید که به اطلاعات معنایی وابسته است (مانند شناسایی موجودیتها در متن یا طبقهبندی دنباله) آموزش دهیم.
- مدلهای دیکودر: این مدلها برای تولید متن جدید ایدهآل هستند.
- مدلهای انکودر-دیکودر: این مدلها برای وظایفی که نیاز به تولید جملات جدید بر اساس یک ورودی دارند، ایدهآل هستند.
حال، با در نظر گرفتن وظیفه طبقهبندی موضوعی برای چکیدههای کوتاه مقالات خبری، ما سه روش ممکن داریم:
- یادگیری zero-shot یا few-shot: میتوانیم از یک مدل پیشآموزش با کیفیت بالا استفاده کنیم، مساله خود یا وظیفه را توضیح دهیم (مثلاً “طبقهبندی به این چهار دسته”) و بگذاریم مدل بقیه کارها را انجام دهد. این روش نیازی به fine-tune کردن ندارد.
- مدل تولید متن: Fine-tune یک مدل تولید متن برای تولید برچسب (مثلاً “تجاری”) با توجه به یک مقاله خبری ورودی. روش اول شبیه کاری است که مدلی به نام T5 انجام میدهد – آن بسیاری از وظایف مختلف را با فرموله کردن آنها به عنوان مشکلات تولید متن حل میکند و در نهایت یک مدل واحد داریم که میتواند بسیاری از وظایف را حل کند.
- مدل انکودر با سر(Head) طبقهبندی: Fine-tune یک مدل انکودر با اضافه کردن یک شبکه طبقهبندی ساده (که Head طبقهبندی نامیده میشود) به امبدینگها. این روش یک مدل خاص و کارآمد برای مساله ارائه میدهد که آن را به یک انتخاب مطلوب برای وظیفه طبقهبندی موضوعی تبدیل میکند.
3. انتخاب یک مدل پایه مناسب
ما یک مدل نیاز داریم که:
- دارای معماری مبتنی بر انکودر باشد.
- به اندازهای کوچک باشد که بتوانیم آن را در چند دقیقه Fine-tune کنیم.
- نتایج از پیشآموزش داده شدهی خوبی داشته باشد.
- بتواند تعداد کمی از توکنها را پردازش کند.
BERT، هرچند قدیمی، یک معماری پایه انکودر عالی برای Fine-tune کردن است. با توجه به اینکه ما میخواهیم مدل را سریع و با قدرت محاسباتی کم آموزش دهیم، میتوانیم از DistilBERT استفاده کنیم که 40٪ کوچکتر و 60٪ سریعتر است در حالی که 97٪ از قابلیتهای BERT را حفظ میکند. با توجه به مدل پایه، میتوانیم آن را برای وظایف مختلف پاییندستی(downstream tasks) تنظیم مجدد کنیم، مانند پاسخ به سوالات یا طبقهبندی متن.
4. پیشپردازش مجموعه داده
همانطور که در پستهای پیشین مشاهده کردیم، هر مدل زبانی دارای توکنایزر خاص خود است. برای تنظیم مجدد DistilBERT، باید اطمینان حاصل کنیم که کل مجموعه داده با همان توکنایزری که برای پیشآموزش مدل استفاده شده است، توکنایز شده باشد. میتوانیم از AutoTokenizer برای لود کردن توکنایزر مناسب استفاده کنیم و سپس یک تابع تعریف کنیم که یک دسته از نمونهها را توکنایز کند. ترنسفورمرها انتظار دارند همه ورودیها در یک دسته به یک اندازه باشند: با اضافه کردن padding=True، صفرهایی به نمونهها اضافه میکنیم تا همه آنها به اندازه بلندترین نمونه ورودی شوند.
مهم است که توجه داشته باشیم که مدلهای ترنسفورمر تصطلاحاً یک حداکثر اندازهی context دارند، یعنی حداکثر تعداد توکنهایی که یک مدل زبانی میتواند هنگام انجام پیشبینیها استفاده کند. برای DistilBERT، این محدودیت 512 توکن است، بنابراین سعی نکنید از آن برای کل کتابها استفاده کنید. خوشبختانه، اکثر نمونههای ما چکیدههای کوتاه هستند. هرچند که اکثر مثالها کوتاه هستند، برخی ممکن است متون بلندی داشته باشند. میتوانیم ازtruncation=True
استفاده کنیم تا همه نمونهها به طول زمینه مدل برش داده شوند تا از بروز مشکلات جلوگیری کنیم. بیایید با دو مثال ببینیم:
from transformers import AutoTokenizer checkpoint = "distilbert-base-uncased" tokenizer = AutoTokenizer.from_pretrained(checkpoint) def tokenize_function(batch): return tokenizer( batch["text"], truncation=True, padding=True, return_tensors="pt" ) tokenize_function(raw_train_dataset[:2])
{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]), 'input_ids': tensor([[ 101, 2813, 2358, 1012, 6468, 15020, 2067, 2046, 1996, 2304, 1006, 26665, 1007, 26665, 1011, 2460, 1011, 19041, 1010, 2813, 2395, 1005, 1055, 1040, 11101, 2989, 1032, 2316, 1997, 11087, 1011, 22330, 8713, 2015, 1010, 2024, 3773, 2665, 2153, 1012, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 101, 18431, 2571, 3504, 2646, 3293, 13395, 1006, 26665, 1007, 26665, 1011, 2797, 5211, 3813, 18431, 2571, 2177, 1010, 1032, 2029, 2038, 1037, 5891, 2005, 2437, 2092, 1011, 22313, 1998, 5681, 1032, 6801, 3248, 1999, 1996, 3639, 3068, 1010, 2038, 5168, 2872, 1032, 2049, 29475, 2006, 2178, 2112, 1997, 1996, 3006, 1012, 102]])}
در این مثال، تابعtokenize_function()
یک دسته یا batch از نمونهها را گرفته، آنها را با استفاده از توکنایزر DistilBERT توکنایز کرده و با استفاده از padding برای جملات کوچیکتر و truncation (برش دادن) برای جملات بزرگتر آنها را به اندازهی یکنواخت میرساند. همانطور که میبینید، عنصر اول کوتاهتر از عنصر دوم بود، بنابراین در انتهای خود چند توکن اضافی با شناسه 0 دارد. این صفرها مربوط به توکن [PAD] هستند که در طول استنتاج نادیده گرفته خواهند شد. توجه کنید که ماسک توجه(attention mask) برای این نمونه نیز در انتها 0 دارد که تضمین میکند که مدل فقط به توکنهای واقعی توجه کند.
حالا که توکنسازی را درک کردیم، میتوانیم از متد map برای توکنایز کردن کل مجموعه داده استفاده کنیم.
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True) tokenized_datasets
DatasetDict({ test: Dataset({ features: ['text', 'label', 'input_ids', 'attention_mask'], num_rows: 7600 }) train: Dataset({ features: ['text', 'label', 'input_ids', 'attention_mask'], num_rows: 120000 }) })
5. تعریف معیارهای ارزیابی
علاوه بر نظارت بر کاهش خطا (loss)، معمولاً تعریف برخی از معیارهای پاییندستی که به ما اجازه میدهند فرایند آموزش را نظارت کنیم، ایده خوبی است. ما از کتابخانه evaluate استفاده خواهیم کرد که ابزاری مفید با یک رابط استاندارد برای معیارهای مختلف است. انتخاب معیارها بستگی به وظیفه یا مساله ما دارد. برای طبقهبندی دنبالهها، موارد زیر میتواند مناسب باشد:
- Accuracy: نسبت پیشبینیهای درست را نشان میدهد و یک نمای کلی از عملکرد مدل ارائه میدهد.
- Precision: نسبت نمونههای مثبت درست برچسبخورده به تمام موارد برچسبخورده به عنوان مثبت است. این معیار به ما در فهمیدن دقت پیشبینیهای مثبت کمک میکند.
- Recall: نسبت نمونههای مثبت واقعی که به درستی توسط مدل پیشبینی شدهاند. این معیار توانایی مدل در تشخیص تمام نمونههای مثبت را نشان میدهد و در صورتی که منفیهای کاذب وجود داشته باشند، پایینتر خواهد بود.
- F1 (F1 Score): میانگین Precisionو Recallاست و یک معیار متعادل ارائه میدهد که هم خطاهای مثبت کاذب و هم منفی کاذب را در نظر میگیرد.
معیارهای موجود در evaluate دارای یک ویژگیdescription
(توضیحات) و یک متد compute()
برای بهدست آوردن معیار با توجه به برچسبها و پیشبینیهای مدل هستند.
import evaluate accuracy = evaluate.load("accuracy") print(accuracy.description) print(accuracy.compute(references=[0, 1, 0, 1], predictions=[1, 0, 0, 1]))
('Accuracy is the proportion of correct predictions among the total ' 'number of cases processed. It can be computed with:\n' 'Accuracy = (TP + TN) / (TP + TN + FP + FN)\n' ' Where:\n' 'TP: True positive\n' 'TN: True negative\n' 'FP: False positive\n' 'FN: False negative\n')
بیایید تابع compute_metrics()
را تعریف کنیم که با دریافت یک نمونه پیشبینی (که شامل برچسب و پیشبینیهای شبکه عصبی است)، یک دیکشنری با accuracy و F1 score را برمیگرداند. وقتی مدل را در طول آموزش ارزیابی میکنیم، به طور خودکار از این تابع برای نظارت بر پیشرفت آن استفاده خواهیم کرد.
f1_score = evaluate.load("f1") def compute_metrics(pred): #1 labels = pred.label_ids preds = pred.predictions.argmax(-1) # Compute F1 score and accuracy f1 = f1_score.compute( references=labels, predictions=preds, average="weighted" )[ "f1" ] #2 acc = accuracy.compute(references=labels, predictions=preds)[ "accuracy" ] #3 return {"accuracy": acc, "f1": f1} #4
در کامنتهای کد بالا 4 جا از شماره 1 تا 4 مشخص شده که در پایین توضیحات آن آمده است:
- تابع
compute_metrics()
انتظار دارد که یکEvalPrediction
دریافت کند، که شامل برچسب و پیشبینیها است. - از
f1_score
بارگذاری شده برای محاسبه نمره F1 بین برچسبها و پیشبینیها استفاده کنید. - این کار را برای دقت (accuracy) نیز تکرار کنید.
- در نهایت، هر دو معیار را با ساختن یک دیکشنری برگردانید.
6. آموزش مدل
وقت آن است که مدل را آموزش دهیم! اگر خاطرتان باشد DistilBERT یک مدل مبتنی بر انکودر است. اگر از مدل خام همانطور که هست استفاده کنیم، همانطور که در اینجا انجام دادیم embeddingهایی که به دست خواهیم آورد، بنابراین نمیتوانیم این مدل را به صورت مستقیم استفاده کنیم. برای طبقهبندی دنبالههای متنی، این embeddingها را به یک Head طبقهبندی میدهیم. وقتی fine-tune میکنیم، از embeddingهای ثابت استفاده نمیکنیم: تمام پارامترهای مدل، وزنهای اصلی و سر طبقهبندی قابل آموزش هستند. این سر embeddingها را به عنوان ورودی گرفته و احتمالات کلاسها را خروجی میدهد. چرا همه وزنها را آموزش میدهیم؟ با آموزش تمام پارامترها، کمک میکنیم embeddingها برای این وظیفه طبقهبندی خاص مفیدتر شوند.
اگرچه ما از یک شبکه feed-forward ساده استفاده خواهیم کرد، میتوانیم از شبکههای پیچیدهتر به عنوان سر یا حتی مدلهای کلاسیک مانند Logistic Regression یا Random Forests استفاده کنیم (در این صورت از مدل به عنوان استخراجکننده ویژگی استفاده میکنیم و وزنها را freeze میکنیم). استفاده از یک لایه ساده هم خوب کار میکند، و هم از نظر محاسباتی کارآمد بوده روش رایجی است.
نکته:
اگر در انتقال یادگیری در بینایی کامپیوتر تجربه دارید، ممکن است با مفهوم ثابت نگه داشتن وزنهای مدل پایه که اصطلاحا freeze کردن نیز گفته میشود آشنا باشید. این کار در NLP کمتر انجام میشود، زیرا هدف ما این است که نمایشهای زبان داخلی برای وظیفه پاییندستی مفیدتر شوند. در بینایی کامپیوتر، اغلب برخی لایهها ثابت نگه داشته میشوند زیرا ویژگیهای یادگرفته شده توسط مدل پایه عمومیتر و برای بسیاری از وظایف مفید هستند. برای مثال، برخی لایهها ویژگیهای عمومی مانند لبهها یا بافتها را دریافت میکنند که به طور گستردهای در وظایف بینایی کاربرد دارند. اینکه آیا لایهها freeze باشند یا نه به زمینه یا مساله بستگی دارد، از جمله موارد تاثیر گذار روی این تصمیم گیری میتوان به اندازه مجموعه داده، مقدار محاسبات و شباهت بین وظیفهی مدل پیشآموزش دیده و مساله جدید اشاره کرد. در ادامه، درباره تکنیکی به نام آداپترها (adapters) یاد خواهیم گرفت که به ما اجازه میدهد با LLMهای freeze شده کار کنیم.
برای آموزش مدل با Head طبقهبندی، میتوانیم از AutoModelForSequenceClassification
استفاده کنیم، که همچنین نیاز به تعیین تعداد برچسبها دارد (تعداد نورونها در لایه آخر را تغییر میدهد).
import torch from transformers import AutoModelForSequenceClassification device = "cuda" if torch.cuda.is_available() else "cpu" num_labels = 4 model = AutoModelForSequenceClassification.from_pretrained( checkpoint, num_labels=num_labels ).to(device)
pp.pprint( """Some weights of DistilBertForSequenceClassification were not initialized from the model checkpoint at distilbert-base-uncased and are newly initialized: ['classifier.bias', 'classifier.weight', 'pre_classifier.bias', 'pre_classifier.weight'] You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.""" )
('Some weights of DistilBertForSequenceClassification were not ' 'initialized from the model checkpoint at distilbert-base-uncased ' "and are newly initialized: ['classifier.bias', " "'classifier.weight', 'pre_classifier.bias', " "'pre_classifier.weight']\n" 'You should probably TRAIN this model on a down-stream task to be ' 'able to use it for predictions and inference.')
شما خطایی را مشاهده خواهید کرد که مربوط به برخی وزنها است که به تازگی مقداردهی اولیه شدهاند. این منطقی است – ما یک Head (سر) جدید مناسب برای وظیفه طبقهبندی خود داریم و نیاز به آموزش آن داریم.
با مقداردهی اولیه مدل، ما میتوانیم بالاخره آن را آموزش دهیم. روشهای مختلفی وجود دارد که میتوانیم برای آموزش مدل استفاده کنیم. اگر با PyTorch آشنا هستید، میتوانید حلقه آموزش خود را بنویسید. در غیر این صورت، کتابخانه transformers یک کلاس سطح بالا به نام Trainer
فراهم میکند که بسیاری از پیچیدگیهای حلقه آموزش را ساده میکند.
اولین قدم قبل از ایجاد Trainer
، تعریفTrainingArguments
است که هایپرپارامترهای مورد استفاده برای آموزش را مشخص میکند، مانند نرخ یادگیری (Learning Rate) و Weight Decay، تعیین تعداد نمونهها در هر دسته (Batch Size)، تنظیم فواصل ارزیابی (Evaluation Intervals) و تصمیمگیری درباره اینکه آیا میخواهیم مدل خود را با اکوسیستم به اشتراک بگذاریم و آن را به Hub ارسال کنیم یا نه. ما هایپرپارامترها را تغییر نخواهیم داد زیرا پیشفرضهای ارائه شده توسط TrainingArguments
عموماً عملکرد خوبی دارند. با این حال، توصیه میشود که خودتان آنها را بررسی کرده و مقادیر مختلف را آزمایش کنید. کلاس Trainer
یک ابزار قوی و انعطافپذیر است.
from transformers import TrainingArguments batch_size = 32 # You can change this if you have a big or small GPU training_args = TrainingArguments( "trainer", push_to_hub=True, #1 num_train_epochs=2, #2 evaluation_strategy="epoch", #3 per_device_train_batch_size=batch_size, #4 per_device_eval_batch_size=batch_size, )
در کد بالا از شماره 1 تا 4 در کامنت مشخص شده که در زیر به شرح هر خط کد با شماره مشخص شده میپردازیم:
- اینکه آیا مدل را هر بار که ذخیره میشود به Hugging Face Hub (هاب هاگینگ فیس) ارسال کنیم یا نه. میتوانید با استفاده از save_strategy (استراتژی ذخیرهسازی)، تعداد دفعات ذخیرهسازی مدل را تغییر دهید که به طور پیشفرض هر چند صد گام انجام میشود.
- تعداد کل epochها برای انجام؛ میدانیم یک دوره (epoch) به معنای گذر کامل از دادههای آموزشی است.
- زمان ارزیابی مدل بر روی مجموعه اعتبارسنجی (validation set). به طور پیشفرض هر ۵۰۰ گام انجام میشود، اما با تعیین epoch، ارزیابی در پایان هر دوره انجام میشود.
- اندازه دسته (batch size) در هر هسته برای آموزش. اگر GPU شما حافظه کم میآورد، میتوانید این مقدار را کاهش دهید.
یک مدل پیشآموزشدیده با یک Head (سر) مناسب آماده برای fine-tuning آرگومانهای آموزشی یک تابع برای محاسبه معیارها (metrics) یک مجموعه داده برای آموزش و ارزیابی یک توکنایزر (tokenizer)، که برای اطمینان از ارسال آن همراه با مدل به Hub (هاب) اضافه میشود
مجموعه داده AG News شامل 120,000 نمونه است که بیشتر از نیاز ما برای به دست آوردن نتایج اولیه خوب است. برای یک اجرای اولیه سریع آموزش، از 10,000 نمونه استفاده خواهیم کرد، اما میتوانید با این عدد بازی کنید – دادههای بیشتر باید نتایج بهتری به همراه داشته باشد. توجه داشته باشید که ما هنوز از کل مجموعه تست برای ارزیابی استفاده خواهیم کرد.
from transformers import Trainer shuffled_dataset = tokenized_datasets["train"].shuffle(seed=42) small_split = shuffled_dataset.select(range(10000)) trainer = Trainer( model=model, args=training_args, compute_metrics=compute_metrics, train_dataset=small_split, eval_dataset=tokenized_datasets["test"], tokenizer=tokenizer, )
با آماده بودن همه چیز و مقداردهی اولیهTrainer
، زمان آموزش فرا رسیده است.
trainer.train()
فرایند آموزش گزارشاتی از جمله میزان خطا (loss)، معیارهای ارزیابی (evaluation metrics) و جزئیات سرعت آموزش ارائه خواهد داد. در اینجا یک جدول خلاصه آمده است:
Metric | Epoch 1 Value | Epoch 2 Value |
---|---|---|
eval_loss | 0.2624 | 0.2433 |
eval_accuracy | 0.9117 | 0.9184 |
eval_f1 | 0.9118 | 0.9183 |
eval_runtime | 15.2709 | 14.5161 |
eval_samples_per_second | 497.678 | 523.557 |
eval_steps_per_second | 15.585 | 16.396 |
train_runtime | – | 213.9327 |
train_samples_per_second | – | 93.487 |
train_steps_per_second | – | 2.926 |
train_loss | – | 0.2714 |
push_to_hub
(ارسال به هاب) انجام دهید.trainer.push_to_hub()
اگرچه استفاده از Trainer ممکن است مانند یک جعبه سیاه که از داخلش خبر نداریم به نظر برسد، اما در واقعیت، و در پس پردهی این تابع، فقط یک حلقه تکرار برای آموزش مدل در PyTorch را اجرا میکند،نوشتن چنین حلقهای از ابتدا ممکن است چیزی شبیه به این باشد:
from transformers import AdamW, get_scheduler optimizer = AdamW(model.parameters(), lr=5e-5) #1 lr_scheduler = get_scheduler("linear", ...) #2 for epoch in range(num_epochs): #3 for batch in train_dataloader: #4 batch = {k: v.to(device) for k, v in batch.items()} #5 outputs = model(**batch) loss = outputs.loss #6 loss.backward() optimizer.step() #7 lr_scheduler.step() optimizer.zero_grad()
در کامنتهای قطعه کد بالا 7 خط کد با شماره هایی مشخص شده اند که توضیحات آن به شرح زیر است:
- بهینهساز (optimizer) وضعیت فعلی مدل را نگه میدارد و بر اساس گرادیانها، پارامترها را بهروزرسانی میکند.
- یک تنظیمکننده نرخ یادگیری یا learning rate scheduler نحوه تغییر نرخ یادگیری را در طول آموزش تعریف میکند.
- چند epoch تکرار بر روی تمامی دادهها
- تکرار بر روی تمامی بستهها (batches) در دادههای آموزشی.
- انتقال بسته به دستگاه و اجرای مدل.
- محاسبهی خطا (loss) و انجام backpropagation
- بهروزرسانی پارامترهای مدل، تنظیم نرخ یادگیری، و صفر کردن گرادیانها برای دور بعدی حلقه
وقتی شما از Trainer استفاده میکنید این موارد را خودش مدیریت میکند: انجام ارزیابیها و پیشبینیها، ارسال مدلها به هاب (Hub)، آموزش بر روی چندین GPU، ذخیره مدل یا checkpoints، ثبت گزارشها و بسیاری از موارد دیگر.
اگر مدل را به هاب ارسال کرده باشید، دیگران اکنون میتوانند با استفاده از AutoModel
یاpipeline()
به آن دسترسی پیدا کنند. بیایید یک مثال را با ببینیم:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="osanseviero/trainer-chapter4") pipe( """The soccer match between Spain and Portugal ended in a terrible result for Portugal.""" )
[{'label': 'LABEL_1', 'score': 0.8631356358528137}]
بیایید به صورت عمیق به بررسی معیارها بپردازیم. میتوانیم از روش Trainer.predict
استفاده کنیم تا پیشبینیها برای تمامی نمونههای موجود در مجموعه دادههای آزمایشی را بدست آوریم. خروجی یک شیءPredictionOutput
است که شامل پیشبینیها، شناسههای برچسب (label IDs) و معیارها میباشد. با بررسی سه نمونه متن اول با پیشبینیها و برچسبهای مرجع آنها، بیایید اطمینان حاصل کنیم که آیا همه چیز منطقی است!
tokenized_datasets["test"].select([0, 1, 2])
Dataset({ features: ['text', 'label', 'input_ids', 'attention_mask'], num_rows: 3 })
# Run inference for all samples trainer_preds = trainer.predict(tokenized_datasets["test"]) # Get the most likely class and the target label preds = trainer_preds.predictions.argmax(-1) references = trainer_preds.label_ids label_names = raw_train_dataset.features["label"].names
0%| | 0/238 [00:00<?, ?it/s]
# Print results of the first 3 samples samples = 3 texts = tokenized_datasets["test"]["text"][:samples] for pred, ref, text in zip(preds[:samples], references[:samples], texts): print(f"Predicted {pred}; Actual {ref}; Target name: {label_names[pred]}.") print(text)
print_wrapped( """Predicted 2; Actual 2; Target name: Business. Fears for T N pension after talks Unions representing workers at Turner Newall say they are 'disappointed' after talks with stricken parent firm Federal Mogul. Predicted 3; Actual 3; Target name: Sci/Tech. The Race is On: Second Private Team Sets Launch Date for Human Spaceflight (SPACE.com) SPACE.com - TORONTO, Canada -- A second\team of rocketeers competing for the #36;10 million Ansari X Prize, a contest for\privately funded suborbital space flight, has officially announced the first\launch date for its manned rocket. Predicted 3; Actual 3; Target name: Sci/Tech. Ky. Company Wins Grant to Study Peptides (AP) AP - A company founded by a chemistry researcher at the University of Louisville won a grant to develop a method of producing better peptides, which are short chains of amino acids, the building blocks of proteins.""" )
Predicted 2; Actual 2; Target name: Business. Fears for T N pension af ter talks Unions representing workers at Turner Newall say they are 'd isappointed' after talks with stricken parent firm Federal Mogul. Pre dicted 3; Actual 3; Target name: Sci/Tech. The Race is On: Second Priv ate Team Sets Launch Date for Human Spaceflight (SPACE.com) SPACE.com - TORONTO, Canada -- A second eam of rocketeers competing for the #36 ;10 million Ansari X Prize, a contest for\privately funded suborbital space flight, has officially announced the first\launch date for its m anned rocket. Predicted 3; Actual 3; Target name: Sci/Tech. Ky. Compa ny Wins Grant to Study Peptides (AP) AP - A company founded by a chemi stry researcher at the University of Louisville won a grant to develop a method of producing better peptides, which are short chains of amin o acids, the building blocks of proteins.
پیشبینی با مرجع همخوانی دارد و برچسب منطقی به نظر میرسد. حالا بیایید به معیارها بپردازیم.
در وظایف طبقهبندی در یادگیری ماشین، یک ماتریس درهمریختگی (confusion matrix) به عنوان جدولی برای خلاصه کردن عملکرد مدل عمل میکند که شامل تعداد پیشبینیهای درست مثبت (true positive)، درست منفی (true negative)، مثبت کاذب (false positive) و منفی کاذب (false negative) است. برای طبقهبندی چندکلاسه، این ماتریس به شکل مربعی با ابعادی برابر با تعداد کلاسها میشود که هر سلول نمایانگر تعداد نمونهها برای ترکیب برچسبها و کلاسهای پیشبینیشده است. ردیفها نشاندهنده کلاسهای واقعی (Ground Truth) هستند، در حالی که ستونها نشاندهنده کلاسهای پیشبینیشده میباشند. تحلیل این ماتریس دیدگاهی نسبت به نقاط قوت و ضعف مدل در تمایز بین کلاسهای خاص ارائه میدهد.
به عنوان مثال، با نگاه به ماتریس درهمریختگی زیر، میتوانیم ببینیم که مقالات تجاری اغلب به عنوان مقالات علمی/فنی (Sci/Tech) اشتباه برچسبگذاری میشوند. برای به دست آوردن ماتریس درهمریختگی، از کتابخانه evaluate استفاده میکنیم که به ما اجازه میدهد معیار ماتریس درهمریختگی را برای استفاده لود کنیم. برای نمایش ماتریس، میتوانیم از تابعplot_confusion_matrix
از کتابخانه sklearn استفاده کنیم.
import matplotlib.pyplot as plt from sklearn.metrics import ConfusionMatrixDisplay confusion_matrix = evaluate.load("confusion_matrix") cm = confusion_matrix.compute( references=references, predictions=preds, normalize="true" )["confusion_matrix"] fig, ax = plt.subplots(figsize=(6, 6)) disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=label_names) disp.plot(cmap="Blues", values_format=".2f", ax=ax, colorbar=False) plt.title("Normalized confusion matrix") plt.show()
تولید متن
ما یک مدل مبتنی بر رمزگذار (Encoder-based) را برای طبقهبندی متن fine-tune کردهایم. اکنون، بیایید به آموزش یک مدل برای تولید متن بپردازیم. برخلاف طبقهبندی متن، مدلهای مولد (generative models) معمولاً نیازی به دادههای برچسبدار (labeled data) ندارند. به عنوان مثال، اگر هدف ما تولید کد باشد، میتوانیم یک مجموعهداده بزرگ از کدهای مجاز (مانند The Stack) جمعآوری کرده و یک مدل را از ابتدا آموزش دهیم. اگرچه این موضوع جالب است، اما برای دستیابی به نتایج مناسب به محاسبات زیادی نیاز دارد (که ممکن است هفتهها یا ماهها به طول بیانجامد!).
توجه
اگرچه میتوانیم مدلهای مولد قوی بدون دادههای برچسبدار بسازیم، اما یک رویکرد جدید به نام RLHF (یادگیری تقویتی با بازخورد انسانی – Reinforcement Learning with Human Feedback) به ما امکان میدهد دادههای برچسبدار انسانی را وارد آموزش کنیم تا خروجی مدل را به سمت خروجیهای ترجیحی خاص هدایت کنیم. مثلا بخواهیم مدل از تولید محتوای سمی اجتناب کند. درباره این رویکرد بعداً بیشتر خواهیم آموخت.
به جای آموزش یک مدل از ابتدا برای تولید متن آزاد، میتوانیم یک مدل موجود را برای تولید متن در یک سبک خاص fine-tune کنیم. این رویکرد به ما این امکان را میدهد که از دانش قبلی مدل درباره زبان بهره ببریم و نیاز به دادهها و توان محاسباتی گسترده را به طور چشمگیری کاهش دهیم. به عنوان مثال، میتوانید از چند صد توییت برای تولید توییتهای جدید در سبک نوشتاری منحصر به فرد خود استفاده کنید. در این مورد، ما از مجموعهداده AG News برای آموزش مدل به منظور تولید اخبار تجاری استفاده خواهیم کرد.
بیایید با فیلتر کردن تمام نمونههای برچسبدار به عنوان تجارت (جایی که برچسب ۲ است) و حذف ستون برچسب غیرضروری شروع کنیم.
filtered_datasets = raw_datasets.filter(lambda example: example["label"] == 2) filtered_datasets = filtered_datasets.remove_columns("label")
سوال دوم این است که از کدام مدل پایه استفاده کنیم. در fine-tuning، انتخاب مدل پایهی مناسب یک تصمیم حیاتی است. چندین عامل در این تصمیم نقش دارند؛ از جمله:
-
اندازه مدل:
استقرار یک مدل با 60 میلیارد پارامتر به صورت local روی رایانه شما عملی نخواهد بود. انتخاب اندازه مدل به عواملی مانند انتظار استنتاج، ظرفیت سختافزار و نیازهای استقرار (deployment requirements) بستگی دارد. در ادامه، به تکنیکهایی میپردازیم که امکان اجرای مدلهای با پارامترهای بیشتر را با استفاده از همان منابع محاسباتی فراهم میکنند.
-
دادههای آموزشی:
عملکرد مدل fine-tune شده شما با میزان تطابق دادههای آموزشی مدل پایه با دادههای تسک اکنون شما (یا اصطلاحا inference data) مرتبط است. به عنوان مثال، fine-tuning یک مدل برای تولید کد در سبک کدبیس شما زمانی مؤثرتر است که با مدل از پیش آموزش دیدهای شروع کنید که به طور خاص بر روی کد آموزش دیده شده باشد. یا برای تولید متن به زبان کرهای، بهتر است از مدلی که عمدتاً بر اساس زبان انگلیسی آموزش دیده است استفاده نکنید. البته همه مدلها منابع دادهای که بر روی آنها آموزش دیدهاند را افشا نمیکنند که این میتواند این کار را برای ما چالشبرانگیز کند.
-
طول متن:
مدلهای مختلف محدودیتهای طول متن یا Context length متفاوتی دارند. طول متن حداکثر تعداد توکنهایی است که مدل میتواند هنگام پیشبینی استفاده کند. به عنوان مثال، اگر طول متن 1024 باشد، مدل میتواند از آخرین 1024 توکن برای پیشبینی استفاده کند. برای تولید متن طولانی، شما به مدلی با طول متن یا Context length بزرگ نیاز دارید.
-
مجوز:
جنبه مجوز در انتخاب مدل پایه نیز بر اساس کاربرد شما میتواند مهم باشد. بررسی کنید که آیا مدل با نیازهای استفاده شما همخوانی دارد یا خیر. مدلها ممکن است مجوزهای تجاری یا غیرتجاری داشته باشند و تفاوت بین مجوزهای منبع باز (open-source) و دسترسی باز (open-access) وجود دارد. درک این مجوزها برای اطمینان از رعایت محدودیتهای قانونی و استفاده برای کاربردهای بین المللی ضروری است. به عنوان مثال، اگرچه برخی مدلها ممکن است استفاده تجاری را مجاز کنند، ممکن است موارد و سناریوهای استفاده مجاز را مشخص کنند که مدل نباید در غیر آنها استفاده شود.
ارزیابی مدلهای مولد همچنان چالشبرانگیز است و معیارهای مختلف جنبههای خاصی را ارزیابی میکنند. معیارهایی مانند ARC برای سوالات علمی، HellaSwag برای استنتاج عقل سلیم و دیگر موارد به عنوان نمایندههای قابلیتهای مختلف عمل میکنند. جدول Open LLM Leaderboard در Hugging Face نتایج ارزیابی هزاران مدل را جمعآوری کرده و امکان فیلتر کردن بر اساس اندازه و نوع مدل را برایمان فراهم میکند. با این حال، توجه به این نکته ضروری است که این معیارها ابزارهایی برای مقایسه سیستماتیک هستند و انتخاب نهایی مدل باید همیشه بر اساس عملکرد آن در وظیفه واقعی شما باشد. به عنوان مثال، معیارهای استفاده شده در Open LLM Leaderboard بر مکالمه تمرکز نداشته و نباید به عنوان معیار اصلی برای انتخاب یک مدل مکالمهای(conversational) استفاده شوند.
جدول زیر چند مدل مشهور پیشآموزش دادهشده (pre-trained) LLM منبع باز را نشان میدهد. با این حال، موارد زیادی برای در نظر گرفتن وجود دارد. این جدول جامع نیست؛ مدلهای باز دیگری مانند Mosaic MPT، Stability StableLM، و Microsoft Phi نیز وجود دارند و زمانی که این پست را شما میخوانید احتمالاً کلی مدل دیگر نیز وجود خواهد داشت. به طور مشابه، این جدول مدلهای کد (code models) را پوشش نمیدهد. اگر دوست دارید میتوانید جدول Big Code Models Leaderboard را نیز بررسی کنید، جایی که میتوانید مدلهایی مانند CodeLlama (یک مدل محبوب از Meta) و مدل BigCode (مدلی که با کدهای با مجوز مجاز آموزش دیده) را پیدا کنید.
Model | Creator | Size | Training Data | Open LLM Performance | Context length | Vocab size | License |
---|---|---|---|---|---|---|---|
GPT-2 |
OpenAI |
117M 345M 762M 1.5B |
Unreleased. Up to 40GB of text from a web scrape |
30.06 33.64 34.08 36.66 |
1024 |
50257 |
MIT |
GPT-Neo |
EleutherAI |
125M 1.3B 2.7B 20B |
The Pile 300B tokens 380B tokens 420B tokens |
31.19 36.04 38.96 43.95 |
2048 |
50257 |
MIT |
Falcon |
TII UAE |
7B 40B 180B |
Partially released RefinedWeb built on top of CommonCrawl 1.5T tokens 1T tokens 3.5T tokens |
47.01 61.48 67.85 |
2048 |
65024 |
Apache 2.0 (7B and 40B) Custom (180B) |
Llama 2 |
Meta |
7B 13B 70B |
Unreleased. 2T tokens |
54.32 58.66 67.35 |
4096 |
32000 |
Custom |
Mistral |
Mistral |
7B |
Unreleased |
60.45 |
8000 |
32000 |
Apache 2.0 |
لازم به ذکر است که این جدول غالبا مدلهایی را شامل شده که عمدتاً بر اساس دادههای انگلیسی آموزش دیدهاند.
با توجه به اینکه ما قصد داریم با دادههای بسیار کم و در محیطی بدون GPU قدرتمند، یک آموزش سریع انجام دهیم، مدل کوچک GPT-2 را تنظیم خواهیم کرد. اما به شدت توصیه میکنم تا خودتان با مدلهای بزرگتر و مجموعه دادههای مختلف آزمایش کنید. در ادامه نیز تکنیکهایی برای استفاده از مدلهای بزرگتر در inference (استنتاج یا دادن ورودی و گرفتن خروجی مدل) و آموزش را خواهیم دید.
مثل قبل با لود مدل و توکنایزر شروع خواهیم کرد. یک نکته خاص درباره GPT-2 این است که این مدل توکن پدینگ خاصی را مشخص نمیکند، اما ما به چنین توکنی نیاز داریم زیرا هنگام توکنایز کردن، این توکن برای اطمینان از اینکه همه نمونهها طول یکسانی دارند استفاده میشود. میتوانیم توکن پدینگ را به همان توکن پایان متن تنظیم کنیم.
from transformers import AutoModelForCausalLM model_id = "gpt2" tokenizer = AutoTokenizer.from_pretrained(model_id) tokenizer.pad_token = ( tokenizer.eos_token ) # Needed as gpt2 does not specify padding token. model = AutoModelForCausalLM.from_pretrained(model_id).to(device)
ما مجموعه داده را توکنایز خواهیم کرد (اما با استفاده از توکنایزر GPT-2).
def tokenize_function(batch): return tokenizer(batch["text"], truncation=True) tokenized_datasets = filtered_datasets.map( tokenize_function, batched=True, remove_columns=["text"], # We only need the input_ids and attention_mask )
tokenized_datasets
DatasetDict({ test: Dataset({ features: ['input_ids', 'attention_mask'], num_rows: 1900 }) train: Dataset({ features: ['input_ids', 'attention_mask'], num_rows: 30000 }) })
در مثال طبقهبندی موضوع (topic classification)، ما همه نمونهها را به منظور اطمینان از اینکه طول یکسانی دارند، پد و کوتاه کردیم (داده های کوچکتر از طول پد داده شده و بزرگتر از طول مشخص شده truncate خواهند شد). علاوه بر این کار در مرحله توکنسازی، میتوانیم این کار را با استفاده از دیتا کاکلکتورها (Data collator) انجام دهیم. دیتا کالکتور (Data collator) ابزارهایی هستند که نمونهها را دسته(batch) میکنند. transformers تعدادی دیتا کالکتور آماده برای وظایف مختلف (مانند مدلسازی زبان) ارائه میدهد. کالکتور به طور پویا نمونهها را در یک دسته به حداکثر طول پد میکند. علاوه بر پد کردن، کالکتور مدلهای زبانی ورودیها را برای وظیفه مدل زبانی ساختاردهی میکند که کمی پیچیدهتر از قبل است. در مدلسازی زبانی، ما ورودیها را به اندازه یک عنصر جابجا میکنیم و از آن به عنوان برچسب استفاده میکنیم. هدف مدل پیشبینی توکن بعدی با توجه به توکنهای قبلی است. در عمل، کالکتور داده یک ستون برچسب با یک کپی از ورودیها ایجاد میکند.
from transformers import DataCollatorForLanguageModeling data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
بیایید بررسی کنیم که این دیتا کالکتور برای سه نمونه کار میکند. همانطور که در پایین مشاهده میکنید، هر نمونه طول متفاوتی دارد (37، 55 و 51).
samples = [tokenized_datasets["train"][i] for i in range(3)] for sample in samples: print(f"input_ids shape: {len(sample['input_ids'])}")
input_ids shape: 37 input_ids shape: 55 input_ids shape: 51
به لطف این کالکتور، نمونهها به طول حداکثر هر دسته یا بچ (55) پد شده و یک ستون برچسب اضافه میگردد.
out = data_collator(samples) for key in out: print(f"{key} shape: {out[key].shape}")
input_ids shape: torch.Size([3, 55]) attention_mask shape: torch.Size([3, 55]) labels shape: torch.Size([3, 55])
در نهایت، نیاز داریم که پارامترهای آموزش را تعریف کنیم. در این مثال، ما چند پارامتر را تغییر میدهیم تا کنترل و انعطافپذیری بالای TrainingArguments
را نشان دهیم. به چند پارامتر مهم نگاهی بیندازیم که تأثیر قابل توجهی بر آموزش مدل داردند:
- کاستن وزن (Weight Decay): کاستن وزن یا Weight Decay یک تکنیک regularization است که از بیشبرازش مدل جلوگیری میکند. این کار با افزودن یک جریمه به تابع خطا انجام شده و الگوریتم یادگیری را از تخصیص وزنهای بزرگ بازمیدارد. تنظیم پارامتر Weight Decay در
TrainingArguments
به شما اجازه میدهد بر قابلیت تعمیم مدل تأثیر بگذارید. - نرخ یادگیری (Learning Rate): نرخ یادگیری یک هایپرپارامتر حیاتی است که اندازه گام بهینهسازی را تعیین میکند. در زمینه TrainingArguments، شما میتوانید نرخ یادگیری را مشخص کنید و بر سرعت همگرایی و پایداری فرایند آموزش تأثیر بگذارید. تنظیم دقیق نرخ یادگیری میتواند تأثیر قابل توجهی بر عملکرد مدل داشته باشد.
- نوع زمانبندی نرخ یادگیری (Learning Rate Scheduler Type): زمانبندی نرخ یادگیری تعیین میکند که چگونه نرخ یادگیری در طول آموزش تغییر میکند. وظایف و معماریهای مختلف مدل ممکن است از استراتژیهای زمانبندی خاصی بهره ببرند.
TrainingArguments
گزینههایی برای تعریف نوع زمانبندی نرخ یادگیری فراهم میکند، و به شما امکان میدهد تا با زمانبندیهای مختلف مانند نرخ یادگیری ثابت، کاهش کسینوسی یا سایر موارد آزمایش کنید.
training_args = TrainingArguments( "sft_cml4", push_to_hub=True, per_device_train_batch_size=8, weight_decay=0.1, lr_scheduler_type="cosine", learning_rate=5e-4, num_train_epochs=2, evaluation_strategy="steps", eval_steps=200, logging_steps=200, )
پس از انجام تمام این تنظیمات، همانند مثال طبقهبندی، آخرین مرحله ایجاد یک نمونهTrainer
با تمام اجزا است. تفاوت اصلی این است که این بار از یک data collator استفاده میکنیم و 5000 نمونه داریم.
trainer = Trainer( model=model, tokenizer=tokenizer, args=training_args, data_collator=data_collator, train_dataset=tokenized_datasets["train"].select(range(5000)), eval_dataset=tokenized_datasets["test"], )
trainer.train()
Metric | Epoch 0.32 Value | Epoch 0.64 Value | Epoch 0.96 Value | Epoch 1.28 Value | Epoch 1.6 Value | Epoch 1.92 Value |
---|---|---|---|---|---|---|
loss | 3.7271 | 3.346 | 3.0685 | 2.1435 | 1.9834 | 1.8937 |
learning_rate | 0.0004690767 | 0.0003839567 | 0.0002656976 | 0.0001435552 | 4.774575e-05 | 1.971325e-06 |
eval_loss | 3.6065 | 3.4732 | 3.3985 | 3.4433 | 3.4203 | 3.3980 |
eval_runtime | 4.7941 | 4.9057 | 4.7142 | 4.7107 | 4.9247 | 4.7953 |
eval_samples_per_second | 396.317 | 387.303 | 403.041 | 403.333 | 385.812 | 396.218 |
eval_steps_per_second | 49.644 | 48.515 | 50.486 | 50.523 | 48.328 | 49.632
|
trainer.push_to_hub()
همانند قبل، میتوانیم از pipeline استفاده کرده و وظیفه (تولید متن) را مشخص کنیم تا مدل را لود کرده و inference را اجرا کنیم.
from transformers import pipeline pipe = pipeline("text-generation", model="osanseviero/sft_cml4", device=device) pipe.tokenizer.pad_token_id = 50256 # pad_token_id for gpt2 print(pipe("Q1", pad_token_id=tokenizer.eos_token_id)[0]["generated_text"]) print(pipe("Wall", pad_token_id=tokenizer.eos_token_id)[0]["generated_text"]) print(pipe("Google", pad_token_id=tokenizer.eos_token_id)[0]["generated_text"])
print_wrapped( pipe("Q1", pad_token_id=tokenizer.eos_token_id)[0]["generated_text"] ) print_wrapped( pipe("Wall", pad_token_id=tokenizer.eos_token_id)[0]["generated_text"] ) print_wrapped( pipe("Google", pad_token_id=tokenizer.eos_token_id)[0]["generated_text"] )
اگر دقت کنید خواهید دید که متن تولید شده ساختاری مشابه به بخش کسبوکار AG News دارد. با این حال، محتوای تولید شده ممکن است همیشه از انسجام کافی برخوردار نباشد. در اینجا با توجه به اینکه از یک مدل پایه کوچک استفاده کرده و همچنین دادههای آموزشی کمی داشتیم خروجی کیفیت بالایی ندارد که البته این مسئله طبیعی است. استفاده از Mistral 7B یا یک مدل بسیار بزرگ مثل نسخه 70B از Llama 2 بدون شک منجر به تولید متن بسیار منسجمتر خواهد شد.
هنگام ارزیابی کیفیت متن تولید شده، یکی از معیارهای معمول استفاده، پریپلکسیتی است. پریپلکسیتی (perplexity) اندازهگیری میکند که یک مدل زبانی چقدر خوب میتواند یک مجموعه داده معین را پیشبینی کند. مقدار کمتر پریپلکسیتی نشاندهنده عملکرد بهتر است، که نشان میدهد مدل میتواند کلمه بعدی را با دقت بیشتری پیشبینی کند. در حالی که پریپلکسیتی یک معیار کمّی ارائه میدهد، ارزیابی کیفی، از جمله قضاوت انسانی، نیز برای سنجش انسجام کلی و ارتباط متن تولید شده با وظیفه مورد نظر بسیار مهم است. تعادل بین ارزیابیهای کمی و کیفی اطمینان میدهد که ارزیابی جامعتری از مدلهای تولید متن صورت گیرد.
دستورالعملها
اگر با یک وظیفه جدید مواجه شویم، مثل تشخیص اینکه آیا یک متن اسپم است یا نه، مدل از پیش آموزشدیدهای در دسترس نخواهیم داشت و نیاز به فاینتیونینگ مدل برای آن خواهیم داشت. بنابراین بیایید به طور مختصر درباره مزایا، محدودیتها و کاربردهای روشهای مختلف بحث کنیم:
- فاینتیونینگ مدلهای متعدد: میتوانیم یک مدل پایه را برای هر وظیفه انتخاب کرده و فاینتیون کنیم تا یک مدل تخصصی بسازیم. تمام وزنهای مدل در طول فاینتیونینگ بهروز میشوند، که بدین معنی است که اگر بخواهیم پنج وظیفه مختلف را حل کنیم، با پنج مدل فاینتیونشده مواجه خواهیم شد.
- آداپتورها: میتوانیم مدل پایه را فریز کرده و یک مدل کمکی کوچک به نام آداپتور را آموزش دهیم به جای تغییر تمام وزنهای مدل. هنوز به آداپتورهای مختلفی برای هر وظیفه جدید نیاز خواهیم داشت، اما آنها بسیار کوچکتر هستند، به این معنا که میتوانیم به راحتی چندین آداپتور داشته باشیم بدون ایجاد سربار. در بخش بعدی درباره آداپتورها بیشتر خواهیم آموخت.
- پرامپتینگ: همانطور که در بخشهای پیشین آموختیم، میتوانیم از قابلیتهای زیرو-شات و فیو-شات مدلهای پیشآموزش دیده قوی برای حل وظایف مختلف استفاده کنیم. با زیرو-شات، یک پرامپت مینویسیم که وظیفه را بهطور کامل توضیح میدهد. با روش فیو-شات، مثالهایی از حل وظیفه اضافه میکنیم و عملکرد مدل را بهبود میبخشیم. عملکرد این قابلیتها به قدرت مدل پایه بستگی دارد. یک مدل بسیار قوی مثل GPT-4 ممکن است نتایج زیرو-شات چشمگیری ارائه دهد، که برای مقابله با انواع وظایف، مانند نوشتن ایمیلهای طولانی یا خلاصه کردن یک فصل کتاب عالی است.
- اینستراکت-تیونینگ: اینستراکت-تیونینگ (Instruct-tuning) یک روش جایگزین و ساده برای بهبود عملکرد زیرو-شات LLMها است. Instruct-tuning وظایف را به صورت دستورالعملهایی مثل “آیا موضوع این پست کسبوکار است یا ورزش؟” یا “ترجمه ‘
how are you
به اسپانیایی چیست؟” فرموله میکند. این رویکرد عمدتاً شامل ساختن یک مجموعه داده از دستورالعملها برای بسیاری از وظایف و سپس فاینتیونینگ یک مدل زبانی پیشآموزشدیده با این ترکیب از مجموعه دادههای دستورالعمل است. ساختن مجموعه داده برای Instruct-tuning یک وظیفه با پیچیدگی قابل مدیریت است؛ برای مثال، میتوان از AG News استفاده کرد و ورودیها و برچسبها را به صورت دستورالعملها با ساختن یک پرامپت مانند این ساختاردهی کرد:
منبع: فصل پنجم کتاب Hands-On Generative AI with Transformers and Diffusion Models
Hands-On Generative AI with Transformers and Diffusion Models
این پست به روز خواهد شد (تمام نشده است…)
دیدگاهتان را بنویسید