کوک کردن مدلها با استفاده از API Trainer
ترنسفورمرهای هاگینگفِیس کلاسی به نام Trainer
دارند که برای کمک به کوک کردن هر مدل از پیش تعلیم دیدهای که روی داده شما ارائه میدهد به کار میرود. به محض اینکه همه کارهای پیشپردازش داده در بخش آخر را انجام دادید، فقط چند مرحله باقیمانده تا تعریف Trainer
دارید. سخت ترین قسمت، احتمالا آمادهسازی محیط جهت اجراي Trainer.train()
میباشد، چرا که این تابع روی CPU بسیار کند اجرا میشود. اگر GPU ندارید، میتوانید از GPU یا TPUهای مجانی روی گوگل کولَب استفاده کنید.
نمونه کدهای زیر فرض میکنند که شما مثالهای بخش قبل را از پیش اجرا کردهاید. این یک خلاصه کوتاه است جهت یادآوری آنچه نیاز دارید:
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
def tokenize_function(example):
return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
تعلیم
قبل از این که بتوانیم Trainer
مان را تعریف کنیم اولین مرحله تعریف کلاس TrainingArguments
میباشد که شامل همه پارامترهای سطح بالایی است که Trainer
برای Training
و Evaluation
استفاده خواهد کرد. تنها آرگومانی که شما باید ارائه کنید آدرسی است که مدل تعلیم دیده به همراه نقاط تعلیم در آن ذخیره خواهند شد. بقیه پارامترها را میتوانید به حالت پیشفرض رها کنید، که برای کوک کردن پایه به خوبی کار خواهد کرد.
from transformers import TrainingArguments
training_args = TrainingArguments("test-trainer")
💡 اگر مایلید مدلتان را به صورت خودکار در حین تعلیم در هاب بارگذاری کنید، پارامتر push_to_hub=True
را در TrainingArguments
ارسال کنید. در فصل ۴ در این باره بیشتر خواهیم آموخت.
مرحله دوم تعریف مدلمان میباشد. مانند فصل قبل، از کلاس AutoModelForSequenceClassification
با دو برچسب کلاس استفاده خواهیم کرد:
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
شما متوجه خواهید شد که برخلاف فصل ۲، بعد از ساختن این مدل از پیش تعلیم دیده یک هشدار دریافت میکنید. این به این خاطر است که BERT برای دستهبندی دو جملهها از پیش تعلیم ندیده است، بنابراین لایه سَر مدل از پیش تعلیم دیده حذف شده و یک لایه سَر مناسب جهت دسته بندی رشتهها به جای آن قرار گرفته است. هشدارها نشان میدهند که برخی از وزنهای مدل استفاده نشدهاند (آنهایی که مربوط به لایه سَر حذف شده مدل از پیش تعلیم دیده هستند) و برخی دیگر به صورت تصادفی مقدار دهی شدهاند (آنهایی که مربوط به لایه سَر جدید هستند). در نتیجه این امر شما را تشویق به تعلیم مدل میکند، که دقیقا همان کاری است که میخواهیم اکنون انجام دهیم.
به محض اینکه مدلمان مشخص شد میتوانیم Trainer
را با ارسال همه اشیائی که تا کنون ساخته شدهاند - model
، training_args
، دیتاسِتهای training
و validation
، data_collator
و tokenizer
به داخل آن تعریف کنیم:
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
)
توجه داشته باشید زمانی که tokenizer
را ارسال میکنید، مثل کاری که ما در اینجا انجام دادیم، data_collator
پیشفرض مورد استفاده Trainer
، همانطور که قبلا تعریف کردیم، DataCollatorWithPadding
خواهد بود، در تنیجه شما میتوانید خط data_collator=data_collator
را در این فراخوانی نادیده بگیرید. این هنوز مهم بود که این بخش از پردازش را در بخش ۲ به شما نشان دهیم!
برای کوک کردن مدل روی دیتاسِتمان ما فقط باید تابع train()
از Trainer
مان را صدا بزنیم:
trainer.train()
این کار، کوک کردن را شروع میکند (که باید چند دقیقه روی GPU طول بکشد) و هزینه تعلیم را هر ۵۰۰ مرحله یکبار گزارش میکند. با این حال به شما نمیگوید که مدلتان چقدر خوب (یا بد) عمل میکند. این به این خاطر است که:
۱. ما به Trainer
نگفتیم که در حین تعلیم کیفیت مدل را اندازهگیری کند. کاری که میتوانستیم با مقداردهی پارامتر evaluation_strategy
به "steps"
(برای ارزیابی در هر eval_steps
) یا به "epoch"
(برای ارزیابی در انتهای هر epoch) انجام دهیم.
۲. ما تابع compute_metrics()
را برای Trainer
فراهم نکردیم تا بتواند معیارها را در حین اصطلاحا ارزیابی محاسبه کند (که در غیر این صورت، ارزیابی فقط هزینه را چاپ میکند که عدد چندان گویایی هم نیست) .
ارزیابی
اجازه دهید ببینیم چگونه میتوانیم تابع compute_metrics()
مفیدی بسازیم و در تعلیم بعدی از آن استفاده کنیم. تابع باید یک شیء EvalPrediction
دریافت کند (که تاپلی است شامل فیلدهای predictions
و label_ids
) و یک دیکشنری باز گرداند که رشتههای متنی را به اعداد حقیقی تبدیل میکند (رشتههای متنی نام معیارهای بازگردانده شونده و اعداد حقیقی مقادیر آنها می باشند). برای استخراج چند پیشبینی از مدلمان، میتوانیم از دستور Trainer.predict()
استفاده کنیم:
predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)
(408, 2) (408,)
خروجی تابع predict()
تاپل نام گذاری شده دیگری شامل سه فیلد: predictions
، label_ids
و metrics
میباشد. فیلد metrics
فقط شامل هزینه داده عبور کرده و برخی معیارهای زمان (پیشبینی، در مجموع و به طور میانگین، چقدر طول کشیده) میباشد. به محض این که تابع compute_metrics()
را کامل کرده و آن را به Trainer
ارسال کنیم، آن فیلد متریکهای بازگشتی از compute_metrics()
را نیز در بر خواهد داشت.
همانطور که میبینید، predictions
آرایهای دو بعدی است با شکل ۴۰۸ x ۲ (که ۴۰۸ تعداد عناصر در دیتاسِت مورد استفاده ما میباشد). این ها logits مربوط به هریک از عناصر دیتاسِتی هستند که ما به تابع predict()
ارسال کردیم (همانطور که در فصل قبل دیدید، همه مدلهای ترَنسفورمِر logits را باز میگردانند). برای تبدیل logits به پیشبینیهایی که بتوانیم با برچسبهایمان مقایسه کنیم، نیاز داریم اندیس مقدار بیشینه روی بعد دوم را برداریم:
import numpy as np
preds = np.argmax(predictions.predictions, axis=-1)
اکنون میتوانیم preds
را با برچسبها مقایسه کنیم. برای ساختن تابع compute_metric()
، به متریکهای کتابخانه دادههای هاگینگفِیس تکیه خواهیم کرد. ما میتوانیم متریکهای وابسته به دیتاسِت MRPC را به راحتی خود دیتاسِت، اما این بار با استفاده از تابع load_metric()
، بارگذاری کنیم. شیء بازگردانده شده تابعی به نام compute()
دارد که میتوانیم برای محاسبه متریک از آن استفاده کنیم:
from datasets import load_metric
metric = load_metric("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
از آنجایی که مقداردهی تصادفی اولیه مدل میتواند متریکهای نهایی را تغییر دهد، نتایج دقیقی که شما بدست میآورید ممکن است متفاوت باشد. در اینجا میتوانیم ببینیم که مدل ما accuracy
معادل ۸۵.۷۸٪ و F1 Score
معادل ۸۹.۹۷٪ روی مجموعه validation
بدست میآورد. آنها دو متریک برای ارزیابی نتایج محک GLUE روی دیتاسِت MRPC هستند. جدول نتایج در مقاله BERT، برای مدل پایه، F1 Score
معادل ۸۸.۹ را گزارش میکند. توجه داشته باشید که آن مدل uncased
بود، حال آن که در اینجا ما از مدل cased
استفاده میکنیم، که دستیابی به نتایج بهتر را توضیح میدهد.
اکنون با قرار دادن همه چیز کنارهم تابع compute_metrics()
را بدست خواهیم آورد:
def compute_metrics(eval_preds):
metric = load_metric("glue", "mrpc")
logits, labels = eval_preds
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
و در اینجا نشان میدهیم که چگونه یک Trainer
جدید با استفاده از تابع compute_metrics()
تعریف میکنیم، تا بتوانیم عملکرد آن را در حین گزارش متریکها در پایان هر epoch مشاهده کنیم:
training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
توجه داشته باشید که ما مدلی جدید و TrainingArguments
جدیدی که evaluation_strategy
آن "epoch"
است میسازیم - در غیر این صورت فقط تعلیم مدلی که از پیش تعلیم دیده بود را ادامه میدادیم. برای راهاندازی دور جدید تعلیم، دستور زیر را اجرا میکنیم:
trainer.train()
این بار هزینه validation و متریکها را در پایان هر epoch و در بالای هزینه تعلیم گزارش میکنیم. دوباره، به خاطر مقدار دهی تصادفی اولیه لایه سر مدل، مقادیر دقیق accuracy/F1 score که شما بدست میآورید ممکن است کمی متفاوت از آنچه ما بدست آوردهایم باشد، اما این مقادیر باید در محدوده تخمینی یکسانی باشند.
به صورت پیش فرض، Trainer
روی چندین GPU یا TPU کار خواهد کرد و گزینههای فراوانی، مثل تعلیم mixed-precision (از مقدار fp16 = True
در آرگومانهای تعلیم استفاده کنید) فراهم میکند. در فصل ۱۰ همه حالتهایی که پشتیبانی میکند را مرور خواهیم کرد.
این پایان مقدمهای بر کوک کردن با استفاده از Trainer
API میباشد. در فصل ۷ مثالی برای نشان دادن چگونگی انجام این کار برای معمولترین مسئلههای NLP ارائه خواهیم کرد، اما اکنون اجازه دهید ببینیم چگونه همین کار را صرفا با استفاده از PyTorch انجام دهیم.
✏️ اتحان کنید! با استفاده از پردازش دادهای که در بخش ۲ انجام دادید، مدلی را روی دیتاسِت GLUE SST-2 کوک کنید.