بسیاری از دورههای آموزشی، چرخهی آموزش مدلهای یادگیری عمیق را به یک حلقهی `for` ساده با فراخوانی متوالی `loss.backward()` و `optimizer.step()` خلاصه میکنند. اما در عمل، اگر حلقهی آموزش ابزار درستی برای سنجش نداشته باشد، ساعتها منابع محاسباتی را بدون هیچ بازدهی مفیدی میسوزاند؛ بدون آنکه متوجه بیشبرازش شوید یا ببینید خروجی متن واقعاً چه تغییری کرده است.
در مدلهای زبانی بزرگ، جزئیاتی مثل تراز کردن توکنهای هدف، تفکیک کاهش وزن (Weight Decay) در بهینهساز AdamW، زمانبندی نرخ یادگیری با وارمآپ و افت کسینوسی، و پایش کیفی متن خروجی تفاوت میان یک اجرای پایدار و یک مدل واگرا را تعیین میکنند. پیادهسازی صحیح این اسکلت نرمافزاری پایهی اصلی تمام پروژههای آموزش مدل دیکودرمحور است.
پیادهسازی این درس کاملاً متکی به کتابخانهی `torch` است و اسکریپت نمایشی آن به دلیل استفاده از یک مدل کوچک و دادههای ساختگی، بدون نیاز به کارت گرافیک گرانقیمت یا سرویسهای ابری، بهراحتی در کمتر از یک دقیقه روی پردازندهی معمولی (CPU) اجرا میشود.
حلقهای که نسنجد، حلقهای است که دروغ میگوید. در این درس، حلقهی آموزشی را میسازیم که مدل GPT را هدایت میکند: AdamW با تفکیک کاهش وزن (weight decay)، برنامهی زمانبندی نرخ یادگیری با وارمآپ (پیشگرمایش) به همراه افت کسینوسی، تابع کمکی
calc_loss_batch، یک مرحلهیevaluate_modelروی دادههای کنارگذاشتهشده (held out)، یک آزمون ارزیابی (probe) کیفی با نامgenerate_and_print_sampleدر هر K گام، و یک لاگ JSONL از مقادیر تابع زیان (loss) که بعداً میتوانید نمودارش را رسم کنید. همین اسکلت اصلی، هر مدل زبانی دیکودرمحوری را که بسازید آموزش میدهد.
اهداف یادگیری
- ساخت حلقهی آموزشی که تابع زیان انتروپی متقاطع (cross entropy loss) را با ترازِ درست ورودی و هدف برای پیشبینی توکن بعدی محاسبه میکند.
- پیکربندی AdamW به شکلی که کاهش وزن فقط روی تنسورهای وزن اعمال شود، نه روی تنسورهای لایهی نرمالسازی (LayerNorm) یا بایاس (bias).
- پیادهسازی برنامهی زمانبندی نرخ یادگیری با وارمآپ خطی و افت کسینوسی، و بررسی مقدار نرخ یادگیری در طول زمان.
- ارزیابی مدل روی دادههای کنارگذاشتهشده با تابع
evaluate_modelتا زیان ارزیابی بین اجراهای مختلف قابل مقایسه باشد. - تولید یک نمونهی کیفی در هر K گام با
generate_and_print_sampleبرای شناسایی واگرایی، پیش از آنکه در نمودار تابع زیان دیده شود. - ذخیرهسازی پایدار تابع زیان در هر گام داخل فایل JSONL تا بتوانید لاگ آموزش را دوباره بارگذاری کنید، نمودارش را بکشید و بهعنوان خروجی تحویل دهید.
صورت مسئله
اسکریپت آموزشی که فقط تابع زیان را چاپ میکند و کار دیگری انجام نمیدهد، از سه جهت شکست میخورد: نمیتواند بگوید آیا کاهش زیان به دلیل درستی اتفاق میافتد یا خیر (شاید مدل دچار بیشبرازش (overfitting) روی دادههای آموزشی شده و چیزی یاد نگرفته است)؛ نمیتواند بگوید آیا واگرایی در حال شروع شدن است یا نه (زیان ممکن است در یک گام جهش کند و برگردد، یا جهش کند و کل مدل فروبپاشد)؛ و نمیتواند بگوید مدل دقیقاً چه یاد گرفته است (زیان فقط یک عدد اسکالر است؛ در حالی که متن تولیدشده یک پاراگراف کامل است). هر سه حالت شکست (failure modes) پنهان میمانند، مگر اینکه حلقه مدام در حال سنجش باشد.
حلقهی این درس از سه طریق اندازهگیری میکند: تابع زیان روی دستهی آموزشی در هر گام، تابع زیان روی دستهی کنارگذاشتهشده در هر K گام، و ادامهی تولید متن از روی یک پرامپت ثابت در هر K گام. لاگ آموزش در قالب JSONL ذخیره میشود تا این دستسازه گواهی بر عملکرد حلقه باشد.
مفاهیم پایه
دو بخش نهچندان بدیهی، تراز کردن تابع زیان و تفکیک کاهش وزن در AdamW هستند.
تراز کردن تابع زیان
مدل در هر موقعیت، توکن بعدی را پیشبینی میکند. اگر دستهی ورودی توکنهای [t0, t1, t2, t3] باشد، دستهی هدف باید [t1, t2, t3, t4] باشد. انتروپی متقاطع روی شکل هموارشدهی (batch * seq, vocab) در برابر هدف هموارشدهی (batch * seq,) محاسبه میشود. اگر این شیفت را فراموش کنید، مدل را طوری آموزش میدهید که خودش را پیشبینی کند؛ در این حالت تابع زیان به صفر میل میکند، ولی مدل هیچ چیز مفیدی یاد نمیگیرد.
تفکیک کاهش وزن در AdamW
کاهش وزن، تنسورهای وزن را منظمسازی (regularize) میکند، نه مقیاسهای نرمالسازی یا بایاسها را. اعمال کاهش وزن روی مقیاس LayerNorm، مقدار آن را بهآرامی به صفر میرساند و نرمالسازی را از کار میاندازد. اعمال کاهش وزن روی بایاس از نظر ریاضی بیخطر است، ولی چرخههای پردازشی را هدر میدهد. تفکیک استاندارد این است: تنسورهایی با شکل ماتریسی (وزنهای لایههای خطی و جداول امبدینگ) مشمول کاهش وزن میشوند، و هر چیزی که شبیه مقیاس یا جابهجایی (shift) باشد، مشمول کاهش نمیشود.
برنامهی زمانبندی وارمآپ به همراه افت کسینوسی
وارمآپ نرخ یادگیری را طی چند صد گام از صفر تا مقدار هدف بالا میبرد تا وضعیت بهینهساز (optimizer state) فرصت شکلگیری داشته باشد. برنامهی زمانبندی کسینوسی (cosine schedule) در طول گامهای باقیمانده نرخ یادگیری را دوباره به سمت صفر کاهش میدهد تا در فاز نهایی، وزنها با اندازهی گامهای کوچک تنظیم دقیق (fine-tune) شوند. این ترکیب، رایجترین برنامهی زمانبندی در آموزش مدلهای زبانی متنباز با وزنهای عمومی است؛ چون بیشتر رفتارهای ناپایدار را در 1000 گام اول و 1000 گام آخر مهار میکند.
ارزیابی روی دادههای کنارگذاشتهشده
تابع evaluate_model تعداد مشخصی از دستههای بخش اعتبارسنجی را اجرا میکند، تابع زیان را جمع میزند، بر تعداد دستهها تقسیم میکند و خروجی میدهد؛ بدون گرادیان و بدون دراپاوت (dropout). با داشتن مقدار دانهی تصادفی (seed) یکسان و دادههای ثابت، این عدد در تمام اجراها بازتولیدپذیر است. ارائهی زیان ارزیابی در کنار زیان آموزش، همان روشی است که بیشبرازش را مشخص میکند.
نمونهگیری کیفی به عنوان سیگنال اولیه
مدلی که تابع زیان آموزش آن بهخوبی پایین میآید اما در نمونههای تولیدی مدام یک توکن تکراری چاپ میکند، خراب است. مدلی که نمودار زیان آن صاف به نظر میرسد اما نمونههای تولیدیاش به کلماتی منسجم و شفاف تبدیل میشوند، دارد یاد میگیرد. این آزمون ارزیابی کیفی، سریعتر از خواندن کل نمودار اجرا میشود و حالتهای شکستی را شکار میکند که این عدد اسکالر قادر به دیدنشان نیست.
پیادهسازی
فایل code/main.py این بخشها را پیادهسازی میکند:
make_batches(token_ids, batch_size, context_length): یک تنسور طولانی از توکنها را به جفتهای ورودی و هدف برش میزند.calc_loss_batch(model, inputs, targets): عمل عبور رو به جلو (forward) را انجام میدهد، خروجی را هموار میکند و انتروپی متقاطع اسکالر را برمیگرداند.evaluate_model(model, val_loader, max_batches): تعداد ثابتی از دستههای اعتبارسنجی را بدون محاسبهی گرادیان پیمایش میکند و میانگین زیان را تحویل میدهد.generate_and_print_sample(model, prompt, max_new_tokens): تابع تولید متن درس 35 را روی یک پرامپت ثابت اجرا میکند و خروجی را چاپ میکند.build_param_groups(model, weight_decay): لیست پارامترهای دومجموعهای بهینهساز AdamW را تولید میکند.cosine_with_warmup(step, warmup_steps, total_steps, max_lr, min_lr): مقدار نرخ یادگیری را در گام دادهشده برمیگرداند.train(...): حلقهی آموزش را اجرا میکند، لاگها را درoutputs/losses.jsonlذخیره میسازد، و در هرeval_everyگام، زیان ارزیابی و نمونهی متن تولیدی را چاپ میکند.- یک نسخهی نمایشی (Demo) که یک مدل بسیار کوچک را روی دادههای ساختگی در تعداد کمی گام آموزش میدهد، لاگ JSONL تولید میکند و زیان ارزیابی و نمونهی متنی را در نقاط ارزیابی نشان میدهد. اجرای این دمو روی CPU بسیار کمتر از 1 دقیقه زمان میبرد.
اجرای کد:
python3 code/main.py
خروجی: خط تابع زیان در هر گام، زیان ارزیابی در هر گامِ آزمون، یک نمونهی تولیدشده در هر گامِ آزمون، و در نهایت فایل outputs/losses.jsonl که خطبهخط با json.loads قابل خواندن است.
پشتهی فناوری
- کتابخانهی
torchبرای مشتقگیری خودکار (autograd)، بهینهساز و ماژولها. - فایل
main.pyمدلGPTModelو ماژولهای کمکی درس 35 را بهصورت محلی پیادهسازی میکند.
الگوهای محیط عملیاتی در پروژههای واقعی
سه الگو وجود دارند که حلقهی آموزشی کتابی را به سیستمی تبدیل میکنند که بتوانید با خیال راحت بگذارید در طول شب اجرا شود.
برش نرم گرادیان غیرقابلمذاکره است. یک دستهی بد (دادههای غیرعادی، جهش نرخ یادگیری، یا حالتهای مرزی محاسبات عددی) گرادیان عظیمی تولید میکند که ساعتها آموزش را در یک لحظه نابود میسازد. فراخوانی torch.nn.utils.clip_grad_norm_(params, max_norm=1.0) بعد از backward و قبل از step، بهینهساز را در محدودهی امن نگه میدارد. مقدار برش یک پارامتر آزاد است؛ اما مقدار 1 گزینهی پیشفرضی است که در بیشتر چیدمانها دوام میآورد.
لاگگیری قابل ادامهدادن با JSONL، نه ذخیرهی وضعیت با pickle. ثبت رکوردهای هر گام بهشکل خطوط {"step": int, "train_loss": float, "lr": float} در فایل JSONL ماندگار است: در صورت کرش کردن برنامه، یک دستسازهی خوانا باقی میماند، میتوانید در آن جستوجو (grep) کنید، با 30 خط کد پایتون نمودارش را بکشید و با خواندن آخرین گام، آموزش را از سر بگیرید. وضعیت ذخیرهشده با pickle شما را به ساختار دقیق ماژولهایی که فایل را ساختهاند وابسته میکند که در برابر تغییر ساختار کد بسیار شکننده است.
دستههای ارزیابی استخراجشده از یک برش ثابت. توکنهای بخش اعتبارسنجی در ابتدای اسکریپت به دستهها برش داده میشوند، نه حین اجرا. بازتولیدپذیری (reproducibility) وابسته به این است که دستههای ارزیابی در تمام اجراها کاملاً یکسان باشند؛ در غیر این صورت، مقایسهی زیان ارزیابی دو اجرای مختلف، بیشتر نحوهی بُر خوردن دستهها را میسنجد تا خود مدل را.
کاربردها
- حلقهی این درس، همان ساختار پایهای است که یک مدل 124M را روی دادههای واقعی آموزش میدهد. کافی است تنسور توکنهای ساختگی را با یک بارگذار به سبک کتابخانهی
datasetsجایگزین کنید تا حلقه بدون تغییر اجرا شود. - لاگ JSONL همان خروجی تحویلی است که یک اجرای آموزشی را به شواهد (evidence) مستند تبدیل میکند. در درس بعد از این فایل برای مقایسهی یک چکپوینت تازهآموزشدیده با یک مدل از پیش آموزشدیده استفاده میکنیم.
- آزمون نمونهگیری کیفی، پوشش کاملی است که زیان اسکالر نمیتواند جایگزین آن شود.
تمرینها
- تستهای واحدی برای
weight_decay_groups()بنویسید که مطمئن شوید پارامترهای مقیاس و بایاس در گروه بدون کاهش وزن، و وزنهای لایههای خطی و امبدینگ در گروه کاهش وزن قرار میگیرند. - توکنهای تصادفی ساختگی را با بایتهای یک فایل متنی کوچک جایگزین کنید تا مدل روی دادههایی خوانا آموزش ببیند. بررسی کنید که نمونهی متنی تولیدشده از کاراکترهای موجود در فایل استفاده کند.
- یک کفِ
min_lrمعادل 10 درصدmax_lrبه برنامهی زمانبندی کسینوسی اضافه کنید و نمودار را دوباره رسم کنید. - علاوه بر لاگ JSONL، در هر
eval_everyگام یک چکپوینت ذخیره کنید. یک فلگresume_fromاضافه کنید که وضعیت مدل و بهینهساز را دوباره بارگذاری کند. - نرخ پردازش (throughput) هر گام (بر حسب توکن بر ثانیه) را در کنار تابع زیان لاگ کنید و مطمئن شوید در یک بازهی پایدار باقی میماند.
اصطلاحات کلیدی
| اصطلاح | در گفتار روزمره | مفهوم فنی واقعی |
|---|---|---|
| تراز کردن تابع زیان (Loss alignment) | «شیفت یکواحدی» | توکنهای ورودی در موقعیتهای 0..T-1 و توکنهای هدف در موقعیتهای 1..T؛ انتروپی متقاطع روی شکلهای هموارشده محاسبه میشود |
| تفکیک کاهش وزن (Decay split) | «دو گروه» | AdamW تنسورهای ماتریسی را با کاهش وزن و تنسورهای مقیاس یا بایاس را بدون کاهش دریافت میکند |
| وارمآپ (Warmup) | «صعود تدریجی» | نرخ یادگیری طی تعداد مشخصی گام از صفر به مقدار هدف میرسد تا وضعیت بهینهساز شکل بگیرد |
| دستههای ارزیابی (Eval batches) | «دستههای کنارگذاشتهشده» | برش ثابتی از تنسور توکنهای اعتبارسنجی که یک بار در ابتدای اسکریپت ساخته شده و در تمام آزمونها عیناً استفاده میشود |
| آزمون ارزیابی کیفی (Qualitative probe) | «چاپ نمونه» | متنی کوتاه از یک پرامپت ثابت که در هر K گام چاپ میشود تا حالتهای شکستی را که تابع زیان به تنهایی پنهان میکند پیدا کند |
برای مطالعهی بیشتر
- فاز 19 درس 35: برای مدلی که این حلقه آن را هدایت میکند.
- فاز 19 درس 37: برای بارگذاری وزنهای از پیش آموزشدیده روی همین مدل.
- فاز 10 درس 04 (پیشآموزش mini GPT): برای روند آموزش روی دادههای واقعی.
- فاز 10 درس 10 (ارزیابی): برای دامنهی ارزیابی گستردهتر فراتر از تابع زیان انتروپی متقاطع.
