مشروع: محوّل على كتاب عربي
درس 8 من 8
8مشروع: محوّل على كتاب عربي
الصورة الكبيرةفي هذا المشروع تدرّب المحوّل الذي بنيته على نص عربي حقيقي، ثم تجعله يكتب. سيكتب كلامًا عربي الشكل بلا معنى، وهذا بالضبط الدليل على أن كل ما تعلّمته يعمل: من حروف عشوائية إلى كلمات وجمل بإملاء صحيح، في دقائق، من الرمز التالي وحده.
ماذا تشاهد في الرسم: المنحنيان ينزلان معًا من 3.83 في أول 500 خطوة، ثم ينفصلان بعد الخط المتقطّع عند الخطوة 2,000: التدريب يواصل النزول والتحقّق يقف. هذه الفجوة بداية الحفظ، ودليل أن ما سيحسّن النموذج الآن نصّ أكثر، لا تدريب أطول.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
تدرّب محوّلك الصغير على كتاب عربي واحد، ثم تطلب منه أن يكتب. ستخرج جمل غريبة ومضحكة أحيانًا، لكنها ستشبه أسلوب الكتاب وكلماته. هذا الشبه هو الدليل على أن نموذجك تعلّم فعلًا، رغم أنه رأى كتابًا واحدًا فقط.
تجهّز النص وترمّزه، وتقسمه تدريبًا وتحققًا، وتدرّب مع تسجيل الخسارة والحيرة perplexity، ثم تولّد بأخذ العيّنات مع temperature وtop-k. التقرير يقارن الخسارة بخط أساس ويعرض أمثلة توليد عند حرارات مختلفة.
قبل أن تبدأ
🔨ابنِ محوّلًا صغيرًا
عندك gpt.py بصنفيه Block وTinyGPT، ومرّ بالفحوص الثلاثة قبل التدريب.
✂️تقسيم البيانات
تعرف التسرّب بين التدريب والتحقّق، ولماذا يُفصل جزء التحقّق قبل أي تجربة.
الفكرة الجوهرية
في هذا المشروع تجهّز نصًّا عربيًّا، ثم تدرّب عليه المحوّل الذي بنيته وتقرأ منحنى الخسارة، ثم تجعله يكتب بدرجتي حرارة.
1تجهيز النص
تحتاج نصًّا عربيًّا طويلًا، نصف مليون حرف أو أكثر. تنظّفه، وتحوّل كل حرف إلى رقم، ثم تقسمه بالترتيب لا عشوائيًّا.
نحذف التشكيل والتطويل، ونُبقي الحروف العربية والمسافة وثلاث علامات ترقيم فقط. هذا يُبقي المفردات صغيرة (40 حرفًا تقريبًا)، فيتعلّم النموذج الإملاء بسرعة.
import re
raw = open("raw_book.txt", encoding="utf-8").read()
text = re.sub(r"[\u064B-\u0652\u0640]", "", raw) # diacritics and tatweel
text = re.sub(r"[^\u0621-\u064A \.،؟]", " ", text) # keep Arabic letters and . ، ؟
text = re.sub(r"\s+", " ", text)
open("book.txt", "w", encoding="utf-8").write(text)
print(len(text), "characters,", len(set(text)), "distinct")
اقرأ أول 500 حرف من book.txt بعينك قبل أي تدريب. إن رأيت كلمات مقطوعة أو حروفًا غريبة، فأصلح التنظيف الآن، لأن النموذج سيتعلّم كل خطأ فيه بإخلاص.
2التدريب وقراءة المنحنى
في كل خطوة نسحب 32 مقطعًا طول كل منها 128، والهدف المقطع نفسه مُزاحًا بحرف. ونقيس التدريب والتحقّق كل 500 خطوة.
إن نزلت الخسارتان معًا فالنموذج يتعلّم. وإن واصلت خسارة التدريب النزول ووقفت خسارة التحقّق، فقد بدأ يحفظ.
ضعه في train.py بجانب gpt.py وbook.txt، وشغّله.
import math
import time
import torch
from gpt import TinyGPT
torch.manual_seed(0)
text = open("book.txt", encoding="utf-8").read()
chars = sorted(set(text))
stoi = {c: i for i, c in enumerate(chars)}
data = torch.tensor([stoi[c] for c in text])
split = int(len(data) * 0.9)
train_data, val_data = data[:split], data[split:]
print(f"characters: {len(text):,} vocabulary: {len(chars)} ln(vocab) = {math.log(len(chars)):.2f}")
ctx, batch = 128, 32
def get_batch(d):
i = torch.randint(len(d) - ctx - 1, (batch,))
x = torch.stack([d[j:j + ctx] for j in i])
y = torch.stack([d[j + 1:j + ctx + 1] for j in i])
return x, y
@torch.no_grad()
def estimate(model):
model.eval()
out = []
for d in (train_data, val_data):
out.append(sum(model(*get_batch(d))[1].item() for _ in range(20)) / 20)
model.train()
return out
model = TinyGPT(len(chars), ctx=ctx)
opt = torch.optim.AdamW(model.parameters(), lr=1e-3)
start = time.time()
for step in range(3001):
if step % 500 == 0:
tr, va = estimate(model)
print(f"step {step:4} train {tr:.2f} val {va:.2f}")
x, y = get_batch(train_data)
_, loss = model(x, y)
opt.zero_grad()
loss.backward()
opt.step()
print(f"trained in {time.time() - start:.0f}s on CPU")
prompt = torch.tensor([[stoi[c] for c in "البرنامج "]])
for t in (0.5, 1.0):
out = model.generate(prompt, 150, temperature=t)[0]
print(f"\n--- temperature {t}:")
print("".join(chars[i] for i in out))
هذا الكود يحتاج PyTorch على جهازك، فالناتج المعروض مسجَّل من تشغيل حقيقي. ثبّته بـpip install torch وشغّله بنفسك.
اقرأ المنحنى أولًا: الخسارة بدأت 3.83 قرب ln(40) = 3.69، ونزلت بسرعة في أول 500 خطوة (تعلّم الحروف الشائعة والمسافات)، ثم ببطء. وبعد الخطوة 2000 توقّفت خسارة التحقّق عند 1.64 بينما استمرّت خسارة التدريب في النزول إلى 1.37. هذه الفجوة المتّسعة هي بداية الحفظ: النموذج بهذا النص لن يتحسّن بتدريب أطول، بل بنص أكثر.
ثم النص: قارنه بالحروف العشوائية في درس «بناء محوّل صغير». نفس البنية تمامًا، والفرق دقائق من التنبّؤ بالحرف التالي. بحرارة 0.5 كلمات حقيقية متكرّرة، وبحرارة 1.0 تنوّع أكبر وأخطاء إملائية أكثر.
3درجة الحرارة
درجة الحرارة تقسم الدرجات قبل softmax. خذ ثلاثة رموز درجاتها [2, 1, 0]: بدرجة 1.0 يأخذ الأول 67%، وبدرجة 0.5 تصير الدرجات [4, 2, 0] فيأخذ 87%.
انظر إلى الرسم: الحرارة المنخفضة تركّز الاحتمال على الرمز الأعلى درجة، فيصير النص مكرّرًا وآمنًا. والحرارة 1.0 توزّعه أكثر، فيصير النص أجرأ وأكثر أخطاء. هذا ما رأيته في النصين المولّدين.
كيف يعمل، خطوة بخطوة: كيف يتعلّم النموذج الكتابة من كتاب
تنظّف النص وتحوّل كل حرف من الأربعين إلى رقم، وتفصل آخر 10% منه للتحقّق.
في كل خطوة تسحب 32 مقطعًا بطول 128، والهدف هو نفس المقطع مُزاحًا بحرف.
تحسب الخسارة وتحدّث الأوزان بـAdamW، وتقيس التدريب والتحقّق كل 500 خطوة.
في النهاية تولّد 150 حرفًا من «البرنامج» عند حرارتين، وتقارن النصين بالحروف العشوائية قبل التدريب.
مثال محلول
تحتاج نصًّا عربيًّا طويلًا، نصف مليون حرف أو أكثر. كتاب من مكتبة رقمية مفتوحة يكفي، أو مجموعة مقالات. في التشغيل المسجّل هنا استخدمنا النص العربي لدروس مسار هندسة البرمجيات في هذه المنصّة: نحو 546 ألف حرف.
ln(حجم المفردات) ومن نموذج الثنائيات في درس التدريب المسبق. ونصّان مولّدان بحرارتين مختلفتين، مع فقرة منك تصف الفرق بينهما وما تعلّمه النموذج وما لم يتعلّمه.
layers، أو العرض، أو عدد الخطوات، واكتب خسارة التحقّق لكل تجربة في جدول. هذا الجدول الصغير هو أول «قانون توسّع» تكتشفه بنفسك.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| نص بلا معنى يعني أن النموذج فشل. | كلمات صحيحة الإملاء وتراكيب عربية من مليون معامل في دقائق هي النجاح المتوقّع. المعنى يحتاج آلاف أضعاف المعاملات والبيانات. |
| خسارة تدريب أقل تعني دائمًا نموذجًا أفضل. | بعد الخطوة 2,000 نزل التدريب من 1.46 إلى 1.37 ولم يتحسّن التحقّق. الرقم الذي يهمّ هو خسارة التحقّق. |
| الحرارة الأعلى تجعل النموذج أذكى أو أكثر إبداعًا. | الحرارة لا تغيّر ما تعلّمه النموذج، بل كيف يسحب منه. عند 1.0 ظهر تنوّع أكثر وأخطاء إملاء أكثر. |
جرّب بنفسك
لماذا نقسم النص بالترتيب (أول 90% وآخر 10%) لا عشوائيًّا بالحروف؟
كل مقطع تدريب بطول 128 حرفًا. لو اختلطت المواضع، لرأى النموذج أثناء التدريب معظم سياق كل مثال تحقّق. هذا نفس درس التسرّب في مسار التعلّم الآلي.
النموذج المدرّب يكتب كلمات صحيحة الإملاء بلا معنى. ما أفضل تفسير؟
هذا بالضبط ما يُتوقّع من هذا الحجم. المعنى المتماسك يظهر مع مليارات المعاملات وتريليونات الرموز، والبنية نفسها التي كتبتها.
بطاقات مراجعة سريعة
تحقق من فهمك
خسارة التدريب 1.2 وخسارة التحقّق 1.9 وتصعد. ماذا يحدث، وماذا تفعل؟
النموذج يحفظ نص التدريب بدل أن يتعلّم أنماطًا عامة. الحلول بالترتيب: نص أكثر (الأفضل)، أو توقّف عند أدنى خسارة تحقّق، أو أضف Dropout، أو صغّر النموذج. النص الأكثر يفوز دائمًا تقريبًا.
بدأت الخسارة عند 3.83، وln(40) تساوي 3.69. هل هذا مقلق؟
لا، الفرق صغير ومتوقّع لأن الأوزان الأولية العشوائية ليست متساوية تمامًا. المقلق أن تبدأ بعيدًا جدًّا، مثل 20.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.