एक पूर्ण प्रशिक्षण
अब हम देखेंगे कि Trainer
क्लास का उपयोग किए बिना कैसे हम समान परिणाम प्राप्त करे जैसा की हमने पिछले खंड प्राप्त किया था। फिर से, हम मानते हैं कि आपने अनुभाग 2 में डेटा प्रसंस्करण यानि डेटा प्रोसेसिंग कर ली है। यहां एक संक्षिप्त सारांश दिया गया है जो वह सब कुछ शामिल कर रहा है जिसकी आपको आवश्यकता होगी:
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)
प्रशिक्षण के लिए तैयार करें
हमारे प्रशिक्षण लूप वास्तव में लिखने से पहले, हमें कुछ वस्तुओं को परिभाषित करने की आवश्यकता होगी। पहले है डेटालोडर्स जिनका उपयोग हम बैचों पर पुनरावृति करने के लिए करेंगे। लेकिन इससे पहले कि हम उन डेटालोडर्स को परिभाषित कर सके, हमें अपने tokenized_datasets
में कुछ पोस्टप्रोसेसिंग लागू करने की जरूरत है, ताकि कुछ चीजों का ख्याल रखा जा सके जो Trainer
ने हमारे लिए स्वचालित रूप से किया था। विशेष रूप से, हमें जरूरत है की:
- उन वैल्यूज के अनुरूप कॉलम निकालें जिनकी मॉडल अपेक्षा नहीं करता (जैसे
sentence1
औरsentence2
कॉलम)। - कॉलम
label
का नाम बदलकरlabels
कर दें (क्योंकि मॉडल उम्मीद करता है की वितर्क का नामlabels
हो)। - डेटासेट का प्रारूप सेट करें ताकि वे सूचियों के बजाय PyTorch टेंसर लौटाएं।
हमारे tokenized_datasets
में उनमे से प्रत्येक चरण के लिए एक विधि है:
tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
tokenized_datasets.set_format("torch")
tokenized_datasets["train"].column_names
हम फिर जांच सकते हैं कि परिणाम में केवल कॉलम है जिन्हें हमारा मॉडल स्वीकार करेगा:
["attention_mask", "input_ids", "labels", "token_type_ids"]
अब जब यह हो गया है, तो हम आसानी से अपने डेटालोडर्स को परिभाषित कर सकते हैं:
from torch.utils.data import DataLoader
train_dataloader = DataLoader(
tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
)
eval_dataloader = DataLoader(
tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
)
यह जांचने के लिए कि डेटा प्रोसेसिंग में कोई गलती तो नहीं है, हम इस तरह एक बैच का निरीक्षण कर सकते हैं:
for batch in train_dataloader:
break
{k: v.shape for k, v in batch.items()}
{'attention_mask': torch.Size([8, 65]),
'input_ids': torch.Size([8, 65]),
'labels': torch.Size([8]),
'token_type_ids': torch.Size([8, 65])}
ध्यान दें कि वास्तविक आकार आपके लिए शायद थोड़ा अलग होगा क्योंकि हमने प्रशिक्षण डेटालोडर के लिए shuffle=True
सेट किया है और हम बैच के अंदर अधिकतम लंबाई तक पैडिंग कर रहे हैं।
अब जबकि हम डेटा प्रीप्रोसेसिंग (एक संतोषजनक लेकिन मायावी लक्ष्य किसी भी ML प्रैक्टिशनर के लिए) के साथ पूरी तरह से समाप्त कर चुके हैं, आइए मॉडल की ओर मुड़ें। हम इसे ठीक वैसे ही इन्स्टैन्शीऐट करते हैं जैसे हमने पिछले सेक्शन में किया था:
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
यह सुनिश्चित करने के लिए कि प्रशिक्षण के दौरान सब कुछ सुचारू रूप से चले, हम अपने बैच को इस मॉडल में पास करते हैं:
outputs = model(**batch)
print(outputs.loss, outputs.logits.shape)
tensor(0.5441, grad_fn=<NllLossBackward>) torch.Size([8, 2])
सभी 🤗 ट्रांसफॉर्मर मॉडल लॉस लौटाएंगे जब labels
प्रदान किया जाते है, और हमें logits भी मिलते हैं (हमारे बैच में प्रत्येक इनपुट के लिए दो, इसलिए टेंसर आकार का 8 x 2)।
हम अपना प्रशिक्षण लूप लिखने के लिए लगभग तैयार हैं! हम केवल दो चीजें खो रहे हैं: एक ऑप्टिमाइज़र और एक लर्निंग रेट अनुसूचक। चूंकि Trainer
जो कर रहा था उसे हम खुद से दोहराने की कोशिश कर रहे हैं, तो हम उन्ही डिफ़ॉल्ट का उपयोग करेंगे। Trainer
द्वारा उपयोग किया जाने वाला ऑप्टिमाइज़र AdamW
है, जो Adam के समान है, लेकिन एक मोड़ के साथ वजन क्षय नियमितीकरण के लिए (इल्या लोशिलोव और फ्रैंक हटर द्वारा “डीकपलड वेट डेके रेगुलराइजेशन” देखें):
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=5e-5)
अंत में, लर्निंग रेट अनुसूचक जिसे डिफ़ॉल्ट रूप से उपयोग किया जाता है केवल एक रैखिक क्षय है जो अधिकतम मूल्य (5e-5) से 0 तक है। इसे ठीक से परिभाषित करने के लिए, हमें यह जानना होगा कि हम कितने प्रशिक्षण कदम उठाएंगे, जो कि है युगों यानि एपोक की संख्या जिन्हे हमे रन करना है उसका गुणा प्रशिक्षण बैचों की संख्या से करना (जो कि हमारे प्रशिक्षण डेटालोडर की लंबाई है)। Trainer
डिफ़ॉल्ट रूप से तीन युगों यानि एपोक का उपयोग करता है, इसलिए हम उसका अनुसरण करेंगे:
from transformers import get_scheduler
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=0,
num_training_steps=num_training_steps,
)
print(num_training_steps)
1377
ट्रेनिंग लूप
एक आखिरी बात: हम GPU का उपयोग करना चाहेंगे अगर हमारे पास एक का एक्सेस है तो (CPU पर, प्रशिक्षण में कुछ मिनटों के बजाय कई घंटे लग सकते हैं)। ऐसा करने के लिए, हम एक device
को परिभाषित करेंगे, जिस पर हम अपने मॉडल को और अपने बैचों को रखेंगे:
import torch
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)
device
device(type='cuda')
अब हम प्रशिक्षण के लिए तैयार हैं! यह जानने के लिए कि प्रशिक्षण कब समाप्त होगा, हम tqdm
लाइब्रेरी का उपयोग करके अपने प्रशिक्षण चरणों की संख्या पर एक प्रगति पट्टी जोड़ेगे:
from tqdm.auto import tqdm
progress_bar = tqdm(range(num_training_steps))
model.train()
for epoch in range(num_epochs):
for batch in train_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
loss.backward()
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
progress_bar.update(1)
आप देख सकते हैं कि प्रशिक्षण लूप का मूल जो परिचय में है उसके समान दिखता है। हमने कोई रिपोर्टिंग नहीं मांगी, इसलिए यह प्रशिक्षण लूप हमें इस बारे में कुछ नहीं बताएगा कि मॉडल का किराया कैसा है। हमें उसके लिए एक मूल्यांकन लूप जोड़ने की जरूरत है।
मूल्यांकन लूप
जैसा कि हमने पहले किया था, हम 🤗 मूल्यांकन करना लाइब्रेरी द्वारा प्रदान किए गए मीट्रिक का उपयोग करेंगे। हम पहले ही metric.compute()
विधि देख चुके हैं, लेकिन मेट्रिक्स वास्तव में हमारे लिए बैच जमा कर सकते हैं जब हम भविष्यवाणी लूप पर जाते हैं add_batch()
विधि के साथ । एक बार जब हम सभी बैचों को जमा कर लेते हैं, तो हम metric.compute()
के साथ अंतिम परिणाम प्राप्त कर सकते हैं। मूल्यांकन लूप में इन सभी को कार्यान्वित करने का तरीका यहां दिया गया है:
import evaluate
metric = evaluate.load("glue", "mrpc")
model.eval()
for batch in eval_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
with torch.no_grad():
outputs = model(**batch)
logits = outputs.logits
predictions = torch.argmax(logits, dim=-1)
metric.add_batch(predictions=predictions, references=batch["labels"])
metric.compute()
{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
फिर से, मॉडल हेड इनिशियलाइज़ेशन और डेटा फेरबदल में क्रमरहित होने के कारण आपके परिणाम थोड़े भिन्न होंगे, लेकिन वे एक ही बॉलपार्क में होने चाहिए।
✏️ कोशिश करके देखे! पिछले प्रशिक्षण लूप को संशोधित करें ताकि अपने मॉडल को SST-2 डेटासेट पर फाइन-ट्यून कर सके।
अपने प्रशिक्षण लूप को सुपरचार्ज करें 🤗 Accelerate के साथ।
हमने पहले जो ट्रेनिंग लूप परिभाषित किया था, वह सिंगल CPU या GPU पर ठीक काम करता है। लेकिन 🤗 Accelerate लाइब्रेरी का उपयोग करके, बस कुछ समायोजन के साथ हम कई GPUs या TPUs पर वितरित प्रशिक्षण को सक्षम कर सकते हैं। शुरुआत प्रशिक्षण और सत्यापन डेटा लोडर के निर्माण से हुई, यहाँ हमारा मैनुअल प्रशिक्षण लूप कैसा दिखता है:
from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
optimizer = AdamW(model.parameters(), lr=3e-5)
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=0,
num_training_steps=num_training_steps,
)
progress_bar = tqdm(range(num_training_steps))
model.train()
for epoch in range(num_epochs):
for batch in train_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
loss.backward()
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
progress_bar.update(1)
और परिवर्तन यहाँ हैं:
+ from accelerate import Accelerator
from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
+ accelerator = Accelerator()
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
optimizer = AdamW(model.parameters(), lr=3e-5)
- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
- model.to(device)
+ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
+ train_dataloader, eval_dataloader, model, optimizer
+ )
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=0,
num_training_steps=num_training_steps
)
progress_bar = tqdm(range(num_training_steps))
model.train()
for epoch in range(num_epochs):
for batch in train_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
- loss.backward()
+ accelerator.backward(loss)
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
progress_bar.update(1)
सबसे पहली लाइन जो जोड़नी है वो है इम्पोर्ट लाइन। दूसरी लाइन एक Accelerator
वस्तु को इन्स्टैन्शीऐट करती है जो वातावरण को देखेगी और उचित वितरित सेटअप को इनिशियलाइज़ करेगी। 🤗 Accelerate आपके लिए डिवाइस प्लेसमेंट को हैंडल करता है, ताकि आप उन लाइनों को हटा सकें जो मॉडल को डिवाइस पर रखती हैं (या, यदि आप चाहें, तो उन्हें device
के बजाय accelerator.device
का उपयोग करने के लिए बदलें)।
फिर काम का मुख्य हिस्सा उस लाइन में किया जाता है जो डेटालोडर्स, मॉडल और ऑप्टिमाइज़र को accelerator.prepare()
पर भेजता है। यह उन वस्तुओं को उचित कंटेनर में लपेट देगा ताकि यह सुनिश्चित हो सके कि आपका वितरित प्रशिक्षण उद्देश्य के अनुसार काम करता है। शेष परिवर्तन है उस लाइन को हटाना जो बैच को device
पर रखता है (फिर से, यदि आप इसे रखना चाहते हैं तो आप इसे केवल accelerator.device
का उपयोग करने के लिए बदल सकते हैं) और loss.backward()
को accelerator.backward(loss)
के साथ बदलना।
यदि आप इसे खेलने के लिए कॉपी और पेस्ट करना चाहते हैं, तो यहां बताया गया है कि 🤗 Accelerate के साथ पूरा प्रशिक्षण लूप कैसा दिखता है:
from accelerate import Accelerator
from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
accelerator = Accelerator()
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
optimizer = AdamW(model.parameters(), lr=3e-5)
train_dl, eval_dl, model, optimizer = accelerator.prepare(
train_dataloader, eval_dataloader, model, optimizer
)
num_epochs = 3
num_training_steps = num_epochs * len(train_dl)
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=0,
num_training_steps=num_training_steps,
)
progress_bar = tqdm(range(num_training_steps))
model.train()
for epoch in range(num_epochs):
for batch in train_dl:
outputs = model(**batch)
loss = outputs.loss
accelerator.backward(loss)
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
progress_bar.update(1)
इसे एक train.py
स्क्रिप्ट में रखने से वह स्क्रिप्ट किसी भी प्रकार के वितरित सेटअप पर चलने योग्य हो जाएगी। इसे अपने वितरित सेटअप में आज़माने के लिए, कमांड चलाएँ:
accelerate config
जो आपको कुछ सवालों के जवाब देने के लिए प्रेरित करेगा और इस कमांड द्वारा उपयोग की जाने वाली कॉन्फ़िगरेशन फ़ाइल में आपके उत्तरों को डंप कर देगा:
accelerate launch train.py
जो वितरित प्रशिक्षण को शुरू करेगा।
यदि आप इसे नोटबुक में आज़माना चाहते हैं (उदाहरण के लिए, Colab पर TPUs के साथ इसका परीक्षण करने के लिए), तो बस कोड को training_function()
में पेस्ट करें और एक अंतिम सेल चलाएँ साथ में:
from accelerate import notebook_launcher
notebook_launcher(training_function)
आप कई अधिक उदाहरण 🤗 Accelerate repo में पा सकते है।