وقتی میخواهید یک مدل زبانی پیشآموزشدیده را برای تسکهای دستهبندی متن مانند تشخیص هرزنامه به کار بگیرید، ساخت مدل از صفر با دادههای اندک کارایی پایینی دارد. در عمل، بدنهی مدل زبانی ویژگیهای متنی ارزشمندی آموخته است و تنها با تعویض سرِ پیشبینی توکن با یک لایهی خطی جدید، میتوان دستهبندی دقیق و کارآمد ایجاد کرد.
در این درس یاد میگیرید چگونه سرِ خروجی ترنسفورمر را با یک لایهی دستهبندی جایگزین کنید و تفاوتهای عملی دو راهبرد «آموزش فقط سر با بدنهی منجمد» و «فاینتیونینگ کامل» را با معیارهای Precision، Recall و F1 روی مجموعهی ارزیابی بسنجید. معماری این درس عمداً بسیار سبک پیادهسازی شده است تا بدون نیاز به GPU ابری یا زیرساختهای پرهزینه، مستقیماً روی پردازندهی سیستم شخصی شما در کمتر از دو دقیقه اجرا شود.
نخستین پروژهی پایانی مسیر B. یک مدل زبانی پیشآموزشدیده، دستهای از بلوکهای خوداتنشن (self-attention) است که به یک سرِ پیشبینی توکن ختم میشود. وقتی هدفْ تشخیص هرزنامه (spam) از پیام عادی (ham) باشد، این سرِ خروجی دیگر به کار نمیآید، اما بدنهی مدل بیشترِ مسیر را درست رفته است. در این درس، سرِ خروجی را برمیداریم، یک لایهی خطی دوکلاسه روی نمایش ادغامشده (pooled representation) میگذاریم و دستهبند را به دو شیوهی متفاوت آموزش میدهیم: فقط لایهی پایانی، و فاینتیونینگ کامل. ارزیابی نهایی با دقت (precision)، بازخوانی (recall) و امتیاز F1 روی مجموعهی دادهی کنارگذاشتهشده (held-out) انجام میشود. در این روند یاد میگیرید هر استراتژی چه دستاوردی دارد و هزینهی آن چقدر است.
اهداف یادگیری
- تعویض سرِ مدل زبانی با سرِ دستهبندی بدون مقداردهی اولیهی مجدد به بدنهی مدل.
- پیادهسازی دو سازوکار آموزشی: بدنهی منجمد (فقط سرِ مدل) و فاینتیونینگ کامل با یک حلقهی آموزشی مشترک.
- ساخت یک پایپلاین دادهی آگاه به توکنایزر که پدینگ (حاشیهگذاری) را انجام دهد، بخشهای پدینگ را ماسک کند و خروجیهای اتنشن را ادغام (pool) کند.
- محاسبهی دقت، بازخوانی، F1 و ماتریس درهمریختگی (confusion matrix) مستقیماً از روی لاجیتهای خام.
- استدلال و تحلیل مصالحه میان تعداد پارامترها، زمان آموزش و پتانسیل عملکردی مدل (head-room).
صورت مسئله
فرض کنید یک ترنسفورمر کوچک را روی یک پیکرهی متنی عمومی پیشآموزش دادهاید. سرِ خروجی، آخرین وضعیت پنهان (hidden state) را روی واژگانی با 1000 توکن نگاشت میکند. حالا 800 پیامک با برچسب هرزنامه یا پیام عادی در دست دارید و میخواهید یک دستهبند دودویی بسازید. برای این کار سه مسیر پیش رو دارید.
مسیر اشتباه این است که یک دستهبند جدید را از صفر روی همین 800 نمونه آموزش دهید. بدنهی مدل پیشآموزشدیده ساختارهای مفیدی را از قبل در خود دارد: هویت واژهها، موقعیت مکانی و همآییهای سادهی کلمات. دور انداختن این ساختارها، کل توان پردازشیِ صرفشده برای ساخت آنها را هدر میدهد.
دو گزینهی درست عبارتاند از: تعویض سر با بدنهی منجمد، و تعویض سر با بدنهی آموزشپذیر. آموزشِ فقط سر بسیار سریع است، تقریباً هیچ حافظهای مصرف نمیکند و با چنین حجم کمی از داده بهندرت دچار بیشبرازش (overfitting) میشود. در مقابل، فاینتیونینگ کامل کُندتر است، ممکن است روی دادههای اندک بیشبرازش کند، اما وقتی دامنهی دادههای مقصد با پیکرهی پیشآموزش فاصله میگیرد، دقت بالاتری به دست میدهد.
در این درس هر دو رویکرد را پیادهسازی میکنیم تا بتوانید آنها را در شرایطی کاملاً یکسان مقایسه کنید.
مفهوم اصلی
مدل تابعی به شکل f_theta(tokens) -> hidden_states است. سرِ مدل نیز تابعی به شکل g_phi(hidden) -> logits است. تعویض سر یعنی theta را نگه داریم و g_phi را جایگزین کنیم. بخش پرهزینهی کار، پارامترهای بدنه است؛ در حالی که سرِ مدل فقط یک لایهی خطی ساده است.
دو دسته پارامتر آموزشپذیر اهمیت دارند:
theta(بدنه): دهها هزار وزن در هر بلوک اتنشن.phi(سر): وزنهایی به ابعادhidden_dim * num_classesبهعلاوهی یک بایاس (bias).
در آموزشِ فقط سر، گرادیانها را نسبت به phi محاسبه میکنید و مقدار آنها را برای theta صفر نگه میدارید. در PyTorch این کار با تنظیم requires_grad=False روی پارامترهای بدنه انجام میشود. به این ترتیب بهینهساز فقط سرِ مدل را میبیند و بدنه دستنخورده و منجمد باقی میماند.
در فاینتیونینگ کامل، اجازه میدهید گرادیانها در تمام لایهها به سمت عقب جریان یابند. وزنهای بدنه تغییر میکنند تا با هدف دستهبندی سازگار شوند. خطری که روی دادههای کم وجود دارد، فراموشی فاجعهبار (catastrophic forgetting) است: دانش قبلی بدنه زیر نویز ناشی از بیشبرازش شسته میشود و از بین میرود.
مسئلهی ادغام (Pooling)
دستهبند به ازای هر توالی به یک بردار نیاز دارد، نه یک بردار به ازای هر توکن. سه انتخاب متداول وجود دارد:
- ادغام میانگین (Mean pool): میانگینگیری از وضعیتهای پنهان در طول توالی با وزندهی بر اساس ماسک اتنشن.
- ادغام توکن CLS: قرار دادن یک توکن ویژه در ابتدای توالی و استفادهی انحصاری از خروجی همان توکن؛ کاری که BERT انجام میدهد.
- ادغام آخرین توکن (Last-token pool): استفاده از آخرین توکن غیرپدینگ؛ کاری که دستهبندهای خانوادهی GPT انجام میدهند.
در این درس از روش ادغام میانگین همراه با وزندهی صریحِ ماسک اتنشن استفاده میکنیم. این رویکرد سادهترین گزینه است، سیگنال پایداری در طولهای مختلف توالی به دست میدهد و نیازی هم به پیشآموزشِ توکن اختصاصی CLS ندارد.
دادهها
800 پیامک، کاملاً متوازن (400 هرزنامه و 400 پیام عادی)، بهطور قطعی در code/main.py تولید میشوند. این مولد از یک سید (seed) ثابت استفاده میکند، قالبهایی را برمیگزیند، جایگاههای متغیر را پر میکند و پیامهایی با طول 5 تا 25 توکن تحویل میدهد. دادههای دنیای واقعی نویزهایی دارند که در این مجموعهی نمونه دیده نمیشود؛ هدف اصلی این مجموعه، تضمین بازتولیدپذیری (reproducibility) است.
دادهها با نسبت 80 به 20 تقسیم میشوند: 640 نمونه برای آموزش و 160 نمونه برای آزمون. این تقسیمبندی بهصورت طبقهبندیشده (stratified) انجام میشود تا مجموعهی آزمون توازن 50/50 خود را حفظ کند. وقتی مجموعهی کنارگذاشتهشده توازن مشخصی داشته باشد، دقت و بازخوانی اعدادی واقعی و قابلاعتماد ارائه میدهند.
معیارهای ارزیابی
در دستهبندی دودویی با فرض کلاس 1 به عنوان کلاس مثبت (هرزنامه)، شمارشها به این ترتیب است:
TP: پیشبینیشده هرزنامه، در واقعیت هرزنامه بوده است.FP: پیشبینیشده هرزنامه، در واقعیت پیام عادی بوده است.FN: پیشبینیشده پیام عادی، در واقعیت هرزنامه بوده است.TN: پیشبینیشده پیام عادی، در واقعیت پیام عادی بوده است.
سه معیار ارزیابی اصلی:
precision = TP / (TP + FP): از میان پیامهایی که هرزنامه علامت خوردهاند، چه نسبتی واقعاً هرزنامهاند؟recall = TP / (TP + FN): از کل هرزنامههای واقعی، مدل چه نسبتی را شناسایی کرده است؟F1 = 2 * P * R / (P + R): میانگین همساز (هارمونیک) دقت و بازخوانی.
ماتریس درهمریختگی این چهار مقدار را در یک جدول 2 در 2 نمایش میدهد. برنامهی دمو این جدول را برای هر دو روش آموزشی در خروجی استاندارد چاپ میکند.
معماری
بدنهی این مدل عمداً یک ترنسفورمر بسیار کوچک طراحی شده است: اندازهی واژگان 260، بعد وضعیت پنهان 64، دارای 4 سرِ اتنشن، 2 بلوک و حداکثر طول توالی 32. این ساختار آنقدر سبک است که اجرای هر دو شیوهی آموزشی تا رسیدن به همگرایی روی CPU زیر 90 ثانیه زمان میبرد. در این درس بدنه از قبل آموزش ندیده است؛ در عوض، تابع کمکی pretrain_quick پنج دور (epoch) آموزش مدل زبانی روی متن همین دادهها اجرا میکند تا بدنه نقطهی شروع معناداری داشته باشد و درس کاملاً مستقل و خودکفا بماند.
در این درس چه چیزی میسازید
پیادهسازی شامل یک فایل main.py و یک ماژول تست (code/tests/test_main.py) است:
ByteTokenizer: نگاشت بایتها به شناسهها و رزرو شناسهی پدینگ.Block: یک بلوک ترنسفورمر همراه با اتنشن چندسر (multi-head attention) و یک لایهی پیشخور با پیشنرمالسازی (pre-norm).LMBody: امبدینگهای توکن و موقعیت مکانی بههمراه دستهای از بلوکها؛ این بخش وضعیتهای پنهان را برمیگرداند.MeanPool: میانگینگیریِ وزندهیشده با ماسک روی محور توالی.Classifier: شامل بدنه، بخش ادغام و سرِ خطی. نمونهی بدنه در هر دو رویکرد آموزشی کاملاً یکسان است.freeze_bodyوunfreeze_body: تغییر وضعیتrequires_gradبرای پارامترهای بدنه.train_classifier: یک حلقهی آموزشی مشترک که مدل و بهینهسازِ پیکربندیشده برای هر گروه پارامترِ آموزشپذیر را دریافت میکند.evaluate: ارزیابی مجموعهی آزمون و بازگرداندنMetrics(precision, recall, f1, confusion).run_demo: پیشآموزش کوتاه بدنه، سپس آموزش و ارزیابی در حالت فقط سر، اجرای آموزش کامل، چاپ گزارش هر دو حالت و خروج با کد صفر.
چرا این مقایسه اهمیت دارد؟
آموزشِ فقط سر معمولاً سریعتر پیش میرود و کمبرازشِ کنترلشدهتر و تمیزتری دارد. روی این دادهها، پس از 20 دور آموزشِ فقط سر، معمولاً به دقتی نزدیک به 0.9 و بازخوانی در حدود 0.85 میرسید. فاینتیونینگ کامل تقریباً سه برابر بیشتر زمان میبرد و بسته به سید تصادفی، نتیجهاش ممکن است یکی دو درصد بالا یا پایین شود.
این درس قصد ندارد یکی از دو روش را به عنوان برندهی قطعی معرفی کند؛ هدف این است که خواندن آمارها و ارزیابی هزینهها را یاد بگیرید. برای 800 نمونه داده با یک بدنهی کوچک، آموزشِ فقط سر تصمیم درستی است؛ اما با 80000 نمونه و یک بدنهی بزرگتر، فاینتیونینگ کامل بازدهی واقعیاش را نشان میدهد. دستاورد کاربردی شما از این درس، ساختار API است: همان تابع train_classifier هر دو حالت را مدیریت میکند و جابهجایی میان آنها تنها با یک فراخوانی ساده انجام میشود.
اهداف چالشی
- حالت آموزشی سومی اضافه کنید که فقط آخرین بلوک را از حالت انجماد خارج کند. این روش که گاهی فاینتیونینگ جزئی (partial fine-tuning) نامیده میشود، هزینهاش از فاینتیونینگ کامل کمتر است و نسبت به حالت فقط سر ظرفیت یادگیری بیشتری دارد.
- یک اسکژولر نرخ یادگیری اضافه کنید. استفاده از برنامهی زمانبندی کسینوسی (cosine schedule) برای سر مدل در کنار یک نرخ ثابتِ کوچکتر برای بدنه، ساختاری متداول در محیطهای عملیاتی (پروداکشن) است.
- ادغام میانگین را با یک ادغام مبتنی بر اتنشن یادگرفتنی جایگزین کنید: یک لایهی اتنشن کوچک همراه با یک پرسوجوی (query) یادگرفتنی. این ترفند معمولاً روی توالیهای طولانیتر بهتر از ادغام میانگین عمل میکند.
پیادهسازی این درس بستر و ابزارهای لازم را فراهم میکند و آزمونها درستی قراردادها را میسنجند. اینکه ارقام نهایی را تا کجا push کنید، بر عهدهی شماست.
