التدريب المسبق
درس 6 من 8
6التدريب المسبق
الصورة الكبيرةكل النماذج اللغوية الكبيرة تتدرّب على مهمة واحدة ساذجة: خمّن الرمز التالي. لا أحد يعلّمها القواعد ولا الحقائق ولا البرمجة. لكن لتخمين الكلمة التالية جيدًا في تريليونات الجمل، يضطر النموذج أن يتعلّم كل ذلك. هذا الدرس عن الهدف، والبيانات، والتكلفة.
ماذا تشاهد في الرسم: هذه الدورة هي التدريب المسبق كله: النموذج يقرأ ما سبق، ويخمّن الرمز التالي، ويُقاس خطؤه بالرمز الحقيقي من النص نفسه، ثم تتعدّل أوزانه. لا أحد يكتب العناوين، فكل نص في العالم أمثلة تدريب جاهزة.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
التدريب المسبق لعبة واحدة يلعبها النموذج مليارات المرات: نغطي الكلمة التالية ونطلب منه أن يخمّنها. ليفوز في هذه اللعبة البسيطة على نصوص العالم كله، يضطر أن يتعلّم القواعد والحقائق وطريقة التفكير. لهذا تخرج قدرة كبيرة من هدف ساذج.
الهدف توقّع الرمز التالي بخسارة الإنتروبيا المتقاطعة على نص ضخم بلا تسميات self-supervised. قوانين التوسّع scaling laws تربط الخسارة بحجم النموذج والبيانات والحوسبة. ثم يأتي الضبط بالتعليمات وتفضيلات البشر ليجعل النموذج يتبع الأوامر.
قبل أن تبدأ
🔁حلقة تدريب بيدك
كتبت حلقة تدريب: خسارة، ثم backward، ثم خطوة المُحسِّن. التدريب المسبق هو نفس الحلقة على نص ضخم.
🎭الانتباه والقناع
تعرف أن القناع السببي يمنع كل موضع من رؤية ما بعده، وهو ما يجعل كل موضع مثالًا مستقلًّا.
الفكرة الجوهرية
في هذا الدرس ترى كيف يصنع النص عناوينه بنفسه، ثم كيف تقرأ الخسارة، ثم الهدف في سطر PyTorch، وأخيرًا كم يكلّف التدريب.
1الهدف: الرمز التالي
كل نموذج لغة كبير يتدرّب على مهمة واحدة: خمّن الرمز التالي. لا يحتاج أحد أن يضع عناوين، فالمدخل هو كل ما قبل الموضع، والهدف هو الرمز عنده.
tokens = ["<s>", "the", "cat", "sat", "on", "the", "mat"]
inputs, targets = tokens[:-1], tokens[1:]
for i in range(len(inputs)):
print(f"given {inputs[:i + 1]!s:45} predict {targets[i]!r}")
في الكود الحقيقي لا نصنع هذه الأمثلة واحدًا واحدًا. نأخذ التسلسل كاملًا مدخلًا، ونفس التسلسل مُزاحًا بخطوة هدفًا، والقناع السببي يضمن أن كل موضع لا يرى إلا ما قبله. فتسلسل من 2048 رمزًا يعطي 2048 مثالًا في تمريرة واحدة.
2الخسارة ومرجعها
نقيس الجودة بالخسارة: متوسط −log للاحتمال الذي أعطاه النموذج للرمز الصحيح. التخمين المتساوي يعطي ln(V)، وأي نموذج يتعلّم شيئًا ينزل تحتها.
أبسط نموذج لغوي ممكن: يتنبّأ بالحرف التالي من الحرف الحالي فقط، بعدّ الأزواج. نقيسه بالخسارة (Cross-entropy) على نص لم يره، ونقارنه بالتخمين العشوائي.
import math
from collections import Counter
text = ("العلم نور والجهل ظلام. من طلب العلم سهر الليالي. "
"الكتاب خير جليس في الزمان. القراءة تفتح أبواب المعرفة. "
"من جد وجد ومن زرع حصد. العقل السليم في الجسم السليم. "
"الصبر مفتاح الفرج. خير الكلام ما قل ودل. ")
split = int(len(text) * 0.8)
train, test = text[:split], text[split:]
vocab = sorted(set(text))
V = len(vocab)
pairs = Counter(zip(train, train[1:]))
firsts = Counter(train[:-1])
def prob(a, b):
return (pairs[(a, b)] + 1) / (firsts[a] + V) # add-one smoothing
loss = -sum(math.log(prob(a, b)) for a, b in zip(test, test[1:])) / (len(test) - 1)
print("vocabulary:", V, "characters")
print(f"loss of guessing uniformly: {math.log(V):.2f}")
print(f"loss of the bigram model: {loss:.2f}")
def generate(start, n):
out = start
for _ in range(n):
a = out[-1]
out += max(vocab, key=lambda b: prob(a, b))
return out
print("greedy text:", generate("ال", 30))
الخسارة نزلت من 3.33 (تخمين عشوائي بين 28 حرفًا) إلى 2.91. تحسّن، لكن النص المولَّد يدور في حلقة: «الي الي الي». النموذج يعرف أن «ي» تأتي كثيرًا بعد «ل»، ولا يعرف شيئًا عمّا قبل ذلك. المحوّل يحلّ هذا بالنظر إلى كل ما سبق بدل حرف واحد.
ln(حجم المفردات)، وهي خسارة التخمين العشوائي. ابدأ كل تدريب بالتحقّق أن الخسارة الأولى قريبة من هذا الرقم.3الهدف في PyTorch
الإزاحة التي رأيتها في الرسم الأول والخسارة التي قرأتها للتو تجتمعان في الكود في ثلاثة أسطر.
الإزاحة بخطوة ثم الخسارة. هذا السطر هو كل «التدريب المسبق» من حيث الكود.
import torch.nn.functional as F # tokens: (batch, T + 1) integer ids from the text x, y = tokens[:, :-1], tokens[:, 1:] logits = model(x) # (batch, T, vocab) loss = F.cross_entropy(logits.reshape(-1, logits.size(-1)), y.reshape(-1))
reshape(-1, vocab) يحوّل كل موضع في كل تسلسل إلى مثال تصنيف مستقل بين vocab فئة. ستكتب هذا بنفسك في المحوّل الذي نبنيه في الدرس القادم.
4الثمن: 6 × N × D
الهدف بسيط، لكن ثمنه يكبر مع شيئين معًا: عدد المعاملات N وعدد رموز التدريب D. ضاعف أيًّا منهما يتضاعف الثمن.
القاعدة: كل معامل يحتاج نحو ست عمليات لكل رمز (اثنتان للتمرير الأمامي وأربع للخلفي). نفترض بطاقة تؤدّي 400 تريليون عملية في الثانية وتعمل بكفاءة 40%.
def train_flops(params, tokens):
return 6 * params * tokens # forward + backward, the usual estimate
gpu_flops = 400e12 * 0.4 # a modern GPU at 40% utilisation
for name, n, d in [("our small model", 1e6, 5e6),
("a 1B model", 1e9, 20e9),
("a 7B model", 7e9, 2e12),
("a 70B model", 70e9, 15e12)]:
f = train_flops(n, d)
seconds = f / gpu_flops
when = f"{seconds:.1f} seconds" if seconds < 3600 else f"{seconds / 3600:,.0f} GPU-hours"
print(f"{name:16} {f:9.1e} FLOPs ~ {when}")
نموذجنا الصغير في المشروع ثانية تقريبًا على بطاقة حديثة (ودقائق على معالج عادي). ونموذج بـ70 مليارًا نحو 11 مليون ساعة بطاقة، أي آلاف البطاقات لأشهر. هذا الفرق هو سبب أن عملك الحقيقي سيكون غالبًا ضبط النماذج الموجودة أو استدعاءها، لا تدريبها من الصفر.
لهذا تدرّب شركات قليلة النماذج الكبيرة من الصفر، ويضبطها الباقون أو يستدعونها.
كيف يعمل، خطوة بخطوة: كيف تصير جملة واحدة أمثلة تدريب
تأخذ التسلسل كله مدخلًا، ونفس التسلسل مُزاحًا برمز واحد هدفًا.
القناع السببي يجعل كل موضع يرى ما قبله فقط، فيصير كل موضع سؤالًا مستقلًّا: ما الرمز التالي؟
تحسب الخسارة: متوسط −log للاحتمال الذي أعطاه النموذج للرمز الصحيح في كل موضع.
تقارن الخسارة الأولى بـln(V)، ثم تكرّر الحلقة على مليارات الرموز وتتابع انخفاضها.
أعمق قليلًا
قوانين التوسّع (Scaling laws) وجدت أن الخسارة تنخفض بانتظام يمكن التنبّؤ به مع زيادة المعاملات والبيانات والحساب. ووجد بحث Chinchilla أن العدد الأمثل للرموز نحو 20 ضعف عدد المعاملات لميزانية حساب معيّنة. النماذج الحديثة تتجاوز هذا كثيرًا عمدًا، لأن نموذجًا أصغر مدرّبًا أطول أرخص في الاستخدام بعد ذلك.
مثال محلول
الجميل في هذا الهدف أن البيانات تصنع تسمياتها بنفسها. أي نص في العالم هو آلاف أمثلة تدريب جاهزة: المدخل كل ما قبل الموضع، والهدف الرمز في الموضع.
ln(حجم المفردات) للتخمين العشوائي، ونموذج ثنائيات بسيط. إن لم يتفوّق محوّلك على الثنائيات بوضوح، فهناك خطأ في الكود قبل أي شيء آخر.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| النموذج بعد التدريب المسبق مساعد جاهز. | هو يكمل النص فقط، وقد يجيب سؤالك بأسئلة أخرى. شكل المساعد يأتي من الضبط على المحادثات ثم من تفضيلات البشر. |
| التدريب المسبق يحتاج عناوين يكتبها بشر. | الرمز التالي في النص هو العنوان، وهذا اسمه التعلّم الذاتي الإشراف، ولهذا أمكن التدريب على تريليونات الرموز. |
| بيانات أكثر دائمًا أفضل من بيانات أنظف. | حذف المكرّر والرديء يؤثّر بقدر الحجم. التنظيف جزء أساسي من التدريب المسبق، لا خطوة جانبية. |
جرّب بنفسك
نموذج بمفردات 50 ألف رمز. خسارته في أول خطوة تدريب 10.8. هل هذا طبيعي؟
نموذج لم يتعلّم شيئًا يعطي احتمالًا متساويًا تقريبًا لكل رمز، فخسارته ln(V). إن بدأت أعلى بكثير، فالأوزان الابتدائية كبيرة جدًّا.
من أين تأتي تسميات التدريب المسبق؟
هذا ما يسمّى التعلّم الذاتي الإشراف (Self-supervised): النص يشرف على نفسه. ولهذا أمكن التدريب على تريليونات الرموز دون أي تصنيف بشري.
بطاقات مراجعة سريعة
−log الاحتمال الذي أعطاه النموذج للرمز الصحيح.6 × المعاملات × الرموز عملية حسابية. نموذج بسبعة مليارات معامل على تريليوني رمز يحتاج عشرات آلاف ساعات المعالجة على بطاقات رسوميات حديثة.تحقق من فهمك
لماذا يكمل النموذج المدرّب مسبقًا سؤالك بأسئلة أخرى بدل الإجابة؟
لأنه تعلّم فقط ما يأتي عادة بعد نص كهذا، وفي الويب كثيرًا ما تأتي قائمة أسئلة بعد سؤال. لم يتعلّم بعد أن دوره «مساعد يجيب». هذا ما يضيفه الضبط على المحادثات. ولهذا تختار نسخة Instruct أو Chat من النموذج عند بناء تطبيق.
لماذا كرّر نموذج الأزواج الحرفية «الي الي الي» بلا نهاية؟
لأنه يرى الحرف الحالي فقط، ويختار دائمًا الحرف الأكثر احتمالًا بعده، فيدخل في حلقة. المحوّل يكسرها لأنه ينظر إلى كل ما سبق، لا إلى حرف واحد.
حسب قاعدة 6 × المعاملات × الرموز، كم ضعفًا يكلّف تدريب نموذج على ضعف الرموز؟
ضعفًا واحدًا: التكلفة تتناسب خطّيًّا مع عدد الرموز ومع عدد المعاملات، فمضاعفة أي منهما تضاعفها.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي1
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.