درک GPT: نحوه پیاده سازی یک مدل ساده GPT با PyTorch

در ابتدا در 14/5/2024 در emangini.com ارسال شده است
این راهنمای جامع توضیح مفصلی درباره نحوه پیادهسازی یک مدل ساده GPT (ترانسفورماتور از پیش آموزشدیده) با استفاده از PyTorch ارائه میدهد. ما اجزای لازم، نحوه آموزش مدل و نحوه تولید متن را پوشش خواهیم داد.
برای کسانی از شما که میخواهید دنبال کنید، یک پیادهسازی پایتون و همچنین یک نوت بوک Jupyter در UnderstandingGPT (GitHub) وجود دارد.
معرفی
مدل GPT یک معماری مبتنی بر ترانسفورماتور است که برای وظایف پردازش زبان طبیعی (NLP) مانند تولید متن طراحی شده است. مدل های ترانسفورماتور، معرفی شده توسط Vaswani و همکاران. (2017)، از مکانیسمهای توجه به خود برای پردازش توالی دادهها استفاده میکند و به آنها اجازه میدهد وابستگیهای دوربرد را به طور موثرتری نسبت به شبکههای عصبی بازگشتی سنتی (RNNs) ثبت کنند. معماری GPT، به طور خاص، یک مدل اتورگرسیو است که متن را با پیشبینی کلمه بعدی در یک دنباله تولید میکند و آن را برای کارهایی مانند تکمیل متن، ترجمه و خلاصهسازی قدرتمند میکند. این آموزش شما را از طریق ایجاد یک نسخه ساده شده از GPT، آموزش آن بر روی یک مجموعه داده کوچک و تولید متن راهنمایی می کند. ما از PyTorch و کتابخانه Hugging Face Transformers برای ساخت و آموزش مدل استفاده خواهیم کرد.
برپایی
قبل از شروع، مطمئن شوید که کتابخانه های مورد نیاز را نصب کرده اید. می توانید آنها را با استفاده از pip نصب کنید:
pip install torch transformers
این کتابخانه ها برای ساخت و آموزش مدل GPT ما اساسی هستند. PyTorch یک چارچوب یادگیری عمیق است که انعطافپذیری و سرعت را فراهم میکند، در حالی که کتابخانه Transformers توسط Hugging Face مدلها و نشانههای از پیش آموزشدیدهشده از جمله GPT-2 را ارائه میدهد.
ایجاد مجموعه داده
برای آموزش موثر یک مدل یادگیری ماشینی مانند GPT، پیش پردازش و آماده سازی صحیح داده های متنی بسیار مهم است. این فرآیند با ایجاد یک کلاس داده سفارشی آغاز می شود که ورودی های متن و توکن سازی را مدیریت می کند. توکنسازی فرآیند تبدیل متن خام به نمایشهای عددی (شناسههای رمز) است که مدل میتواند آن را درک کند (دولین و همکاران، 2019). قطعه کد ارائه شده با تعریف کلاسی به نام این کار را انجام می دهد SimpleDataset، که از توکنایزر GPT-2 برای رمزگذاری داده های متنی استفاده می کند.
این SimpleDataset کلاس از ارث می برد torch.utils.data.Dataset و روش های لازم را برای تعامل یکپارچه با DataLoader پیاده سازی می کند. این کلاس سه پارامتر را در مقداردهی اولیه خود می گیرد: لیست متون، توکنایزر و حداکثر طول دنباله ها. این _لن_ متد تعداد متون موجود در مجموعه داده را برمی گرداند، در حالی که the _موردی_ متد یک متن خاص را در نمایه داده شده بازیابی و رمزگذاری می کند. فرآیند رمزگذاری شامل تبدیل متن به نمایشهای عددی با استفاده از توکنایزر و اضافه کردن توالیها به حداکثر طول مشخص برای اطمینان از یکنواختی است. Padding تمرین اضافه کردن نشانههای اضافی به دنبالهها برای ایجاد طول یکسان است که برای پردازش دستهای در شبکههای عصبی مهم است. این روش شناسههای ورودی و ماسکهای توجه را برمیگرداند، جایی که ماسک توجه یک ماسک باینری است که نشان میدهد کدام نشانهها کلمات واقعی هستند و کدامها بالشتک هستند. این به مدل کمک می کند تا توکن های padding را در طول آموزش نادیده بگیرد.
این کد برای مرجع است:
import torch
from torch.utils.data import Dataset, DataLoader
from transformers import GPT2Tokenizer
class SimpleDataset(Dataset):
def __init__(self, texts, tokenizer, max_length):
self.texts = texts
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
text = self.texts[idx]
encoding = self.tokenizer(text, return_tensors='pt', padding='max_length', truncation=True, max_length=self.max_length)
return encoding['input_ids'].squeeze(), encoding['attention_mask'].squeeze()
texts = ["Hello, how are you?", "I am fine, thank you.", "What about you?"]
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
tokenizer.pad_token = tokenizer.eos_token
dataset = SimpleDataset(texts, tokenizer, max_length=20)
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)
در این کد، کلاس SimpleDataset، رمزگذاری متون ورودی را مدیریت می کند و شناسه های ورودی رمزگذاری شده و ماسک های توجه را برمی گرداند. سپس DataLoader داده ها را برای آموزش کارآمد دسته بندی و به هم می ریزد. پردازش دستهای، که شامل تقسیم مجموعه دادهها به دستههای کوچکتر است، به مدل اجازه میدهد تا وزنهای خود را بیشتر بهروزرسانی کند و منجر به همگرایی سریعتر شود. مخلوط کردن داده ها به شکستن هر نظم ذاتی در داده های آموزشی کمک می کند و تعمیم مدل را بهبود می بخشد.
با تنظیم داده ها به این روش، ما اطمینان حاصل می کنیم که مدل دنباله هایی با طول یکنواخت برای آموزش دریافت می کند. این رویکرد همچنین مدیریت ورودیهای با طول متغیر را آسانتر میکند و در عین حال تضمین میکند که نشانههای padding در فرآیند یادگیری مدل تداخلی ایجاد نمیکنند. این مرحله پیش پردازش جامع برای آموزش مدل های یادگیری ماشین موثر و کارآمد بسیار مهم است (براون و همکاران، 2020).
ساخت مدل GPT
برای ساخت یک مدل GPT موثر، با تعریف معماری آن شروع می کنیم. این مدل از دو کلاس اصلی تشکیل شده است: GPTBlock و SimpleGPT. این GPTBlock کلاس نشان دهنده یک بلوک ترانسفورماتور منفرد است، در حالی که SimpleGPT کلاس چندین بلوک ترانسفورماتور را برای ایجاد مدل کامل پشته می کند (واسوانی و همکاران، 2017).
در GPTBlock کلاس، ما اجزای ضروری یک بلوک ترانسفورماتور را کپسوله می کنیم. اینها شامل نرمال سازی لایه، توجه چند سر و شبکه عصبی پیشخور با فعال سازی GELU است. نرمال سازی لایه ها، ورودی های هر زیر لایه را استاندارد می کند و ثبات و همگرایی فرآیند آموزش را بهبود می بخشد. مکانیسم توجه چند سر مدل را قادر میسازد تا بر روی بخشهای مختلف دنباله ورودی به طور همزمان تمرکز کند و توانایی آن را برای گرفتن وابستگیهای پیچیده درون دادهها افزایش دهد (واسوانی و همکاران، 2017). شبکه عصبی پیشخور، با استفاده از GELU (واحد خطی خطای گاوسی) فعال سازی، غیرخطی بودن را معرفی می کند و ظرفیت مدل را برای یادگیری الگوهای پیچیده افزایش می دهد. GELU یک تابع فعال سازی است که به راحتی به آن تقریب می زند ReLU (واحد خطی اصلاح شده) عملکرد و اغلب در عمل بهتر عمل می کند (هندریکس و گیمپل، 2016).
در اینجا کد تعریف این کلاس ها آمده است:
import torch.nn as nn
class GPTBlock(nn.Module):
def __init__(self, config):
super(GPTBlock, self).__init__()
self.ln_1 = nn.LayerNorm(config.n_embd)
self.attn = nn.MultiheadAttention(config.n_embd, config.n_head, dropout=config.attn_pdrop)
self.ln_2 = nn.LayerNorm(config.n_embd)
self.mlp = nn.Sequential(
nn.Linear(config.n_embd, 4 * config.n_embd),
nn.GELU(),
nn.Linear(4 * config.n_embd, config.n_embd),
nn.Dropout(config.resid_pdrop)
)
def forward(self, x, attention_mask=None):
attn_output, _ = self.attn(x, x, x, attn_mask=attention_mask)
x = x + attn_output
x = self.ln_1(x)
mlp_output = self.mlp(x)
x = x + mlp_output
x = self.ln_2(x)
return x
این SimpleGPT دسته چندگانه کلاس GPTBlock نمونه هایی برای تشکیل مدل کامل این کلاس شامل توکن ها و جاسازی های موقعیت، انصراف برای منظم سازی، و یک لایه خطی برای تولید لجیت های خروجی است. تعبیههای رمز، شناسههای نشانه ورودی را به بردارهای متراکم تبدیل میکنند و به مدل اجازه میدهند با نمایشهای عددی کلمات کار کند. جاسازیهای موقعیت اطلاعاتی را درباره موقعیت هر نشانه در دنباله ارائه میدهند که برای مدل برای درک ترتیب کلمات بسیار مهم است. ترک تحصیل یک تکنیک منظمسازی است که بهطور تصادفی برخی از نورونها را در حین تمرین صفر میکند و به جلوگیری از برازش بیش از حد کمک میکند (Srivastava et al., 2014). لایه خطی نهایی حالت های پنهان را به logit تبدیل می کند که برای پیش بینی نشانه بعدی در دنباله استفاده می شود.
class SimpleGPT(nn.Module):
def __init__(self, config):
super(SimpleGPT, self).__init__()
self.token_embedding = nn.Embedding(config.vocab_size, config.n_embd)
self.position_embedding = nn.Embedding(config.n_positions, config.n_embd)
self.drop = nn.Dropout(config.embd_pdrop)
self.blocks = nn.ModuleList([GPTBlock(config) for _ in range(config.n_layer)])
self.ln_f = nn.LayerNorm(config.n_embd)
self.head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
self.config = config
def forward(self, input_ids, attention_mask=None):
positions = torch.arange(0, input_ids.size(1), device=input_ids.device).unsqueeze(0)
x = self.token_embedding(input_ids) + self.position_embedding(positions)
x = self.drop(x)
if attention_mask is not None:
attention_mask = attention_mask.unsqueeze(1).repeat(self.config.n_head, attention_mask.size(1), 1)
attention_mask = attention_mask.to(dtype=torch.float32)
attention_mask = (1.0 - attention_mask) * -10000.0
for block in self.blocks:
x = block(x.transpose(0, 1), attention_mask)
x = x.transpose(0, 1)
x = self.ln_f(x)
logits = self.head(x)
return logits
سپس مدل را با استفاده از آن پیکربندی می کنیم GPT2Config کلاس از کتابخانه ترانسفورماتورها، که فراپارامترهای مختلفی مانند اندازه واژگان، تعداد موقعیتها، ابعاد جاسازی، تعداد لایهها، تعداد سرهای توجه و نرخ خروج را تنظیم میکند. این تنظیمات برای تعریف معماری و رفتار مدل در طول آموزش ضروری هستند.
آموزش مدل
این قطار – تعلیم دادن تابع یک جزء حیاتی در فرآیند آموزش یک مدل ترانسفورماتور از پیش آموزش دیده (GPT) است. این تابع کل حلقه آموزشی را هماهنگ میکند و مراحل کلیدی مانند پاس رو به جلو، محاسبه ضرر، انتشار پسانداز و بهینهسازی را در بر میگیرد. هر یک از این مراحل نقش حیاتی در پالایش پارامترهای مدل بر اساس داده های ورودی ایفا می کند و در نهایت توانایی مدل را برای تولید متن منسجم و مرتبط با زمینه بهبود می بخشد.
فرآیند آموزش با تنظیم مدل به حالت آموزش با استفاده از model.train() روش. این حالت لایههای خاصی مانند ترک تحصیل و نرمالسازی دستهای را قادر میسازد تا در طول آموزش به درستی عمل کنند و اطمینان حاصل شود که به قابلیتهای تعمیم مدل کمک میکنند (Goodfellow et al., 2016). سپس حلقه آموزشی برای تعداد معینی از دوره ها روی مجموعه داده تکرار می شود. یک دوره نشان دهنده یک گذر کامل از کل مجموعه داده آموزشی است که به مدل اجازه می دهد از تمام داده های موجود بیاموزد.
برای هر دوره، تابع دستهای از دادههای ارائه شده توسط آن را پردازش میکند DataLoader، که دسته بندی و ترکیب کارآمد مجموعه داده را مدیریت می کند. دستهبندی، توالیهای ورودی متعدد را در یک دسته گروهبندی میکند، که پردازش موازی و استفاده کارآمد از منابع محاسباتی را ممکن میسازد. مخلوط کردن داده ها به کاهش بیش از حد برازش مدل به ترتیب نمونه های داده کمک می کند.
در هر دسته، شناسههای ورودی و ماسکهای توجه به دستگاه مشخص شده (CPU یا GPU) منتقل میشوند تا قدرت محاسباتی سختافزار را افزایش دهند. پاس رو به جلو شامل عبور شناسه های ورودی از طریق مدل برای به دست آوردن لجیت های خروجی است که پیش بینی های خام و غیر عادی مدل هستند. برای تراز کردن پیشبینیها با اهداف، لاجیتها جابهجا میشوند: shift_logits آخرین پیشبینی توکن را حذف میکند و shift_labels اولین نشانه را حذف میکند، و اطمینان حاصل میکند که توالیهای ورودی و خروجی به درستی برای کار پیشبینی نشانههای بعدی تراز شدهاند.
محاسبه تلفات با استفاده از تابع تلفات متقاطع آنتروپی، یک معیار رایج برای وظایف طبقهبندی که تفاوت بین احتمالات پیشبینیشده و مقادیر هدف واقعی را اندازهگیری میکند، انجام میشود. از دست دادن آنتروپی متقابل به ویژه برای کارهای مدلسازی زبان که هدف آن پیشبینی نشانه بعدی در یک دنباله است، مناسب است (Goodfellow et al., 2016).
پس انتشار، اجرا از طریق loss.backward() روش، گرادیان تابع ضرر را با توجه به پارامترهای مدل محاسبه می کند. این گرادیان ها نشان می دهد که هر پارامتر چقدر باید تغییر کند تا تلفات به حداقل برسد. بهینه ساز، مشخص شده به عنوان Adam (Kingma & Ba، 2015)، پارامترهای مدل را بر اساس این گرادیان ها به روز می کند. Adam (تخمین لحظه تطبیقی) نوعی از نزول گرادیان تصادفی است که نرخ یادگیری را برای هر پارامتر تطبیق می دهد و آن را برای توزیع داده های مختلف کارآمدتر و قوی تر می کند. این یک الگوریتم بهینه سازی محبوب است که نرخ یادگیری تطبیقی را برای هر پارامتر محاسبه می کند.
در طول هر دوره، تلفات کل انباشته شده و در تمام دسته ها به طور میانگین محاسبه می شود و معیاری از عملکرد مدل را ارائه می دهد. نظارت بر ضرر در دورهها به درک پیشرفت یادگیری مدل و تنظیم فراپارامترها در صورت لزوم کمک میکند. این فرآیند اصلاح مداوم برای بهبود دقت مدل و اطمینان از توانایی آن برای تولید متن با کیفیت بالا ضروری است.
معیار از دست دادن آنتروپی متقاطع است که برای اندازه گیری عملکرد مدل طبقه بندی که خروجی آن مقدار احتمال بین 0 و 1 است استفاده می شود.
در اینجا قطعه کد برای قطار – تعلیم دادن عملکرد و اجرای آن:
import torch.optim as optim
def train(model, dataloader, optimizer, criterion, epochs=5, device='cuda'):
model.train()
for epoch in range(epochs):
total_loss = 0
for input_ids, attention_mask in dataloader:
input_ids, attention_mask = input_ids.to(device), attention_mask.to(device)
optimizer.zero_grad()
outputs = model(input_ids, attention_mask)
shift_logits = outputs[..., :-1, :].contiguous()
shift_labels = input_ids[..., 1:].contiguous()
loss = criterion(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch + 1}/{epochs}, Loss: {total_loss / len(dataloader)}")
optimizer = optim.Adam(model.parameters(), lr=1e-4)
criterion = nn.CrossEntropyLoss()
train(model, dataloader, optimizer, criterion, epochs=5, device=device)
تولید متن
این generate_text تابع در کد ما برای تولید متن از یک مدل GPT آموزش دیده بر اساس یک درخواست اولیه طراحی شده است. این تابع برای نشان دادن کاربرد عملی مدل آموزشدیده ضروری است و به ما این امکان را میدهد که ببینیم چگونه میتواند متن منسجم و مرتبط با زمینه تولید کند.
تابع با تنظیم مدل در حالت ارزیابی با استفاده از آن شروع می شود model.eval(). حالت ارزیابی تضمین می کند که لایه هایی مانند ترک تحصیل به درستی رفتار می کنند و بر نتایج پیش بینی تأثیر نمی گذارند (Goodfellow et al., 2016). سپس درخواست با استفاده از روش رمزگذاری توکن ساز به شناسه های ورودی تبدیل می شود، که متن را به قالبی تبدیل می کند که مدل بتواند پردازش کند. این شناسه های ورودی به دستگاه مشخص شده (یا CPU یا GPU) منتقل می شوند تا قدرت محاسباتی موجود را افزایش دهند.
سپس این تابع وارد یک حلقه می شود که تا رسیدن به حداکثر طول متن تولید شده یا تولید یک نشانه پایان دنباله (EOS) ادامه می یابد. در طول هر تکرار، دنباله فعلی توکن های تولید شده از مدل عبور داده می شود تا لجیت های خروجی به دست آید. لاجیت ها پیش بینی های خام و غیر عادی هستند که نشان دهنده اطمینان مدل برای هر نشانه در واژگان هستند. لاجیت ها برای آخرین نشانه در دنباله انتخاب می شوند و توکن با بالاترین احتمال (محتمل ترین نشانه بعدی) با استفاده از آن تعیین می شود. مشعل.argmax. این نشانه به دنباله تولید شده اضافه می شود.
اگر توکن تولید شده، نشانه EOS باشد، حلقه شکسته میشود و نشان میدهد که مدل تولید متن را به پایان رسانده است. در نهایت، توالی توکنهای تولید شده با استفاده از روش رمزگشایی توکنایزر به متن تبدیل میشود، که نمایشهای عددی را به متن قابل خواندن برای انسان تبدیل میکند، و از هر نشانه خاصی صرفنظر میکند.
این فرآیند تکراری پیشبینی نشانه بعدی بر اساس توالی فعلی، توانایی مدل را برای تولید متن به شیوهای مرتبط با زمینه نشان میدهد، که برای کاربردهایی مانند تولید داستان، سیستمهای گفتگو و سایر وظایف پردازش زبان طبیعی بسیار مهم است (واسوانی و همکاران. ، 2017).
کد تابع generate_text در اینجا آمده است:
def generate_text(model, tokenizer, prompt, max_length=50, device='cuda'):
model.eval()
input_ids = tokenizer.encode(prompt, return_tensors='pt').to(device)
generated = input_ids
for _ in range(max_length):
outputs = model(generated)
next_token_logits = outputs[:, -1, :]
next_token = torch.argmax(next_token_logits, dim=-1).unsqueeze(0)
generated = torch.cat((generated, next_token), dim=1)
if next_token.item() == tokenizer.eos_token_id:
break
generated_text = tokenizer.decode(generated[0], skip_special_tokens=True)
return generated_text
prompt = "Once upon a time"
generated_text = generate_text(model, tokenizer, prompt, device=device)
print(generated_text)
نتیجه
در این راهنما، توضیحی جامع و گام به گام در مورد نحوه پیادهسازی یک مدل ساده GPT (ترانسفورماتور از قبل آموزشدیده) با استفاده از PyTorch ارائه کردیم. ما روند ایجاد یک مجموعه داده سفارشی، ساخت مدل GPT، آموزش آن و تولید متن را طی کردیم. این پیاده سازی عملی مفاهیم اساسی پشت معماری GPT را نشان می دهد و به عنوان پایه ای برای برنامه های پیچیده تر عمل می کند. با دنبال کردن این راهنما، اکنون درک اولیه ای از نحوه ایجاد، آموزش و استفاده از یک مدل ساده GPT دارید. این دانش شما را به آزمایش با پیکربندیهای مختلف، مجموعه دادههای بزرگتر و تکنیکهای اضافی برای افزایش عملکرد و قابلیتهای مدل مجهز میکند. اصول و تکنیک های پوشش داده شده در اینجا به شما کمک می کند تا مدل های ترانسفورماتور را در وظایف مختلف NLP اعمال کنید و پتانسیل یادگیری عمیق را در درک و تولید زبان طبیعی باز کنید. روشهای ارائهشده با پیشرفتهای مدلهای ترانسفورماتور که توسط واسوانی و همکاران معرفی شدهاند، همسو هستند. (2017)، با تاکید بر قدرت مکانیسم های خودتوجهی در پردازش توالی داده ها به طور موثرتر از رویکردهای سنتی (واسوانی و همکاران، 2017). این درک مسیرهایی را برای کشف و نوآوری در زمینه پردازش زبان طبیعی با استفاده از تکنیک های پیشرفته یادگیری عمیق باز می کند (Kingma & Ba, 2015).
منابع:
- Devlin، J.، Chang، MW، Lee، K.، & Toutanova، K. (2019). BERT: پیش آموزش ترانسفورماتورهای دو جهته عمیق برای درک زبان. پیش چاپ arXiv arXiv:1810.04805.
- براون، سل، مان، بی.، رایدر، ان.، سابیه، ام.، کاپلان، جی.، دهیوال، پی، … و آمودی، دی. (2020). مدل های زبان یادگیرندگان کمی هستند. پیش چاپ arXiv arXiv:2005.14165.
- Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, AN, … & Polosukhin, I. (2017). توجه تنها چیزی است که نیاز دارید. پیش چاپ arXiv arXiv:1706.03762.
- هندریکس، دی، و گیمپل، ک. (2016). واحدهای خطی خطای گاوسی (GELUs). پیش چاپ arXiv arXiv:1606.08415.
- Srivastava, N., Hinton, G., Krizhevsky, A., Sutskever, I., & Salakhutdinov, R. (2014). Dropout: یک راه ساده برای جلوگیری از برازش شبکه های عصبی. مجله تحقیقات یادگیری ماشین، 15 (1)، 1929-1958.
- رادفورد، ا.، نراسیمهان، ک.، سالیمانز، تی، و سوتسکور، آی. (2018). بهبود درک زبان با پیش آموزش مولد. پیش چاپ OpenAI.
- Goodfellow، I.، Bengio، Y.، و Courville، A. (2016). یادگیری عمیق. مطبوعات MIT.
- Kingma, DP, & Ba, J. (2015). آدام: روشی برای بهینه سازی تصادفی. پیش چاپ arXiv arXiv:1412.6980.