مدلهای زبانی بزرگ (LLM: Large Language Models) در چند سال اخیر به عنوان یکی از پیشرفتهترین و تاثیرگذارترین فناوریهای هوش مصنوعی مطرح شدهاند. این مدلها با تحلیل میلیاردها کلمه از متون موجود، توانایی درک زبان طبیعی، تولید متن، ترجمه، خلاصهسازی، کدنویسی، پاسخ به سوالات و انجام بسیاری از وظایف پیچیده زبانی را پیدا کردهاند.
با پیشرفت سختافزارهای محاسباتی و در دسترس بودن دادههای عظیم، امکان آموزش شبکههای عصبی بسیار بزرگ فراهم شد. شرکتهایی مانند OpenAI، Google و Meta با ارائه مدلهایی مانند GPT، BERT و LLaMA قدرت بیسابقهای به مدلهای زبانی بخشیدهاند. این مدلها نه تنها از لحاظ فنی پیچیده هستند، بلکه پیامدهای فلسفی، اجتماعی، حقوقی و اقتصادی نیز به همراه دارند.
هدف این ارائه، بررسی جامع از ابتدا تا انتهای مسیر تکامل LLMها است. در ابتدا، تاریخچهای از پردازش زبان طبیعی و مدلهای زبانی سنتی ارائه میشود. سپس ساختارهای کلیدی مانند Transformer، آشنایی با معماری مدلها و روند آموزش بررسی خواهد شد. در ادامه با کاربردهای مدلهای زبانی در زمینههای مختلف، چالشها و محدودیتهای آنها و در نهایت آینده این حوزه آشنا خواهیم شد.
در انتهای ، یک مدل زبان ساده با پایتون پیادهسازی خواهد شد تا درک عملیتری از نحوه کار این مدلها حاصل شود. همچنین مروری بر معروفترین LLMهای موجود و ویژگیهای آنها انجام خواهد گرفت.
این تحقیق برای دانشجویان، پژوهشگران و علاقهمندان به هوش مصنوعی و پردازش زبان طبیعی میتواند نقطه آغازی برای یادگیری عمیقتر باشد.
پردازش زبان طبیعی (Natural Language Processing) شاخهای از هوش مصنوعی است که هدف آن تحلیل و درک زبان انسانی توسط ماشین است. NLP یکی از قدیمیترین حوزههای پژوهشی در علم رایانه است که از دهه ۱۹۵۰ میلادی با تلاش برای ترجمه ماشینی زبانها آغاز شد.
نخستین پروژههای ترجمه ماشینی با حمایت دولتها، به ویژه در دوران جنگ سرد، شروع شد. مثلاً پروژه ترجمه روسی به انگلیسی یکی از اولین پروژههای NLP بود. اما این سیستمها بسیار محدود و ناکارآمد بودند و در دهه ۶۰ مشخص شد که زبان انسانی بسیار پیچیدهتر از آن است که بتوان با قوانین ساده آن را مدلسازی کرد.
در دهه ۷۰ و ۸۰، رویکردهای مبتنی بر قوانین (rule-based) محبوب شدند. در این سیستمها مجموعهای از قواعد نحوی و معنایی توسط زبانشناسان برای تحلیل زبان طراحی میشدند. این روشها اگرچه دقیق بودند، اما توسعه آنها نیازمند صرف زمان و تخصص فراوان بود.
دهه ۹۰ را میتوان آغاز دوران آماری در NLP دانست. با ظهور مدلهای آماری مانند n-gram، Hidden Markov Models و بعدها Conditional Random Fields، پردازش زبان وارد فاز دادهمحور شد. این مدلها بر پایه احتمالات و یادگیری از دادههای واقعی بنا شده بودند و به نتایج بهتری در مقیاس بزرگ دست یافتند.
دهه ۲۰۱۰ با پیدایش یادگیری عمیق، تحولی بنیادین در NLP ایجاد شد. استفاده از شبکههای عصبی بازگشتی (RNN) و بعدها LSTM و GRU باعث شد مدلها توانایی درک وابستگیهای زمانی را داشته باشند. اما انقلاب واقعی با معرفی معماری Transformer در سال ۲۰۱۷ توسط گوگل رقم خورد که مسیر را برای ظهور مدلهای زبانی بزرگ باز کرد.
پیش از ورود یادگیری عمیق، مدلهای زبانی بر پایه روشهای آماری ساده و قوانین زبانی طراحی میشدند. یکی از معروفترین مدلها در این دوره، مدل n-gram بود که بر اساس احتمال وقوع توالیای از کلمات عمل میکرد. برای مثال در مدل trigram، احتمال وقوع یک کلمه با توجه به دو کلمه قبلی محاسبه میشد.
مدلهای آماری مانند Hidden Markov Models (HMM) و Maximum Entropy Models نیز برای وظایفی نظیر برچسبگذاری اجزای سخن (POS tagging)، تحلیل نحو و تشخیص موجودیتهای نامدار (NER) استفاده میشدند. این مدلها دادهمحور بودند اما به علت فرضیات سادهای که داشتند، در درک ساختار پیچیده زبان انسانی با محدودیت مواجه بودند.
محدودیتهای اصلی این مدلها شامل موارد زیر بود:
-
عدم توانایی در نگهداری حافظه بلندمدت از اطلاعات قبلی
-
مشکلات در درک ساختار نحوی پیچیده
-
نیاز به حجم زیاد داده برای دقت بالا
-
ضعف در درک معنای واژگان در زمینههای مختلف (context)
-
نیاز زیاد به مهندسی ویژگی (Feature Engineering) دستی
اگرچه این مدلها نتوانستند به سطح درک زبان انسانی برسند، اما پایهگذار تحول بعدی در حوزه یادگیری زبان طبیعی شدند و مفاهیم کلیدی مانند احتمال شرطی، توالی، و مدلسازی زبان را به دنیای NLP وارد کردند.
ورود یادگیری عمیق (Deep Learning) به حوزه NLP، نقطه عطف بزرگی بود. شبکههای عصبی توانستند بدون نیاز به مهندسی ویژگی، خودشان از دادهها یاد بگیرند و الگوهای زبانی را کشف کنند. اولین گامها با استفاده از شبکههای عصبی بازگشتی (RNN) برداشته شد که مناسب مدلسازی توالی بودند.
شبکههای LSTM (Long Short-Term Memory) و GRU (Gated Recurrent Unit) به منظور رفع مشکل ناپدید شدن گرادیان در شبکههای RNN معرفی شدند و عملکرد بهتری در نگهداری حافظه برای توالیهای طولانی داشتند. این مدلها در ترجمه ماشینی، تحلیل احساسات، و تولید متن به کار گرفته شدند.
در همین زمان، مدلهای توزیعی کلمات مانند Word2Vec و GloVe معرفی شدند که هر کلمه را به صورت یک بردار در فضای برداری نمایش میدادند. این نمایش برداری کمک بزرگی به درک معنایی زبان توسط ماشین کرد.
با این حال، مدلهای بازگشتی دارای محدودیتهایی مانند عدم قابلیت پردازش موازی و ضعف در درک روابط طولانیمدت بودند. این مسائل باعث شد تا نیاز به معماریهای جدیدی احساس شود.
این نیاز با معرفی مدل Transformer در سال ۲۰۱۷ توسط گوگل پاسخ داده شد، که در بخش بعدی به طور مفصل به آن میپردازیم.
معماری Transformer که در مقاله معروف "Attention Is All You Need" توسط تیم Google Brain در سال ۲۰۱۷ معرفی شد، نقطه عطفی در تاریخ NLP بود. برخلاف شبکههای بازگشتی که اطلاعات را به صورت دنبالهای پردازش میکردند، Transformer امکان پردازش موازی کل توالی را فراهم میکرد.
هسته اصلی Transformer مکانیزم Attention است، به ویژه "Self-Attention" که به مدل اجازه میدهد هر کلمه در جمله را در ارتباط با سایر کلمات بررسی کند. این باعث میشود مدل بهتر بتواند وابستگیهای معنایی و نحوی طولانی را درک کند.
Transformer از دو بخش اصلی تشکیل شده است: Encoder و Decoder. در کاربردهای مختلف مانند ترجمه ماشینی، Encoder اطلاعات جمله ورودی را تحلیل میکند و Decoder بر اساس آن جمله خروجی را تولید میکند. در مدلهای زبانی مانند GPT فقط از بخش Decoder استفاده میشود.
مزایای معماری Transformer شامل موارد زیر است:
-
قابلیت موازیسازی بالا و در نتیجه سرعت بیشتر در آموزش
-
درک بهتر از وابستگیهای طولانی در متن
-
سازگاری با انواع دادههای متنی و توالیای
-
پایهگذاری برای مدلهای عظیم مانند BERT و GPT
این معماری به سرعت به استانداردی در تمام پروژههای NLP تبدیل شد و تقریباً تمامی مدلهای موفق بعدی بر پایه آن طراحی شدند.
فرآیند آموزش مدلهای زبانی بزرگ معمولاً در چند مرحلهی اساسی انجام میشود که هر کدام نقش مهمی در کیفیت نهایی مدل ایفا میکنند:
قبل از آموزش، دادههای خام نیاز به پاکسازی، حذف نویز، نرمالسازی، جداسازی جملات و توکنسازی دارند. همچنین در این مرحله دادههای حساس یا تکراری نیز حذف میشوند.
در این مرحله، مدل روی حجم عظیمی از متون بدون برچسب آموزش میبیند تا ساختار زبان، قواعد دستوری، روابط معنایی و اطلاعات عمومی را فراگیرد. معمولاً از روشهای self-supervised مانند پیشبینی کلمه بعدی (Causal Language Modeling) یا پرکردن ماسک (Masked Language Modeling) استفاده میشود.
پس از پیشآموزش، مدل برای وظایف خاص (مانند ترجمه، خلاصهسازی یا پاسخگویی) روی دادههای هدفمند آموزش داده میشود. این مرحله با دادههای کوچکتر و برچسبدار انجام میشود و باعث میشود مدل بهتر با کاربرد مشخص سازگار شود.
برای برخی مدلها مانند ChatGPT، مرحلهای تحت عنوان RLHF به کار میرود که در آن با کمک بازخورد انسان، مدل برای پاسخهای اخلاقیتر، سودمندتر و طبیعیتر بهینه میشود.
در پایان، مدل با استفاده از مجموعهای از معیارها ارزیابی میشود (که در بخشهای بعدی خواهیم گفت). در صورت نیاز، تنظیمات نهایی یا pruning برای کاهش حجم و افزایش کارایی انجام میگیرد.
این مراحل به شدت نیازمند منابع محاسباتی بالا و مدیریت دقیق داده هستند. اجرای موفق این چرخه تفاوت بین یک LLM معمولی و یک مدل قدرتمند مانند GPT-4 یا Gemini را رقم میزند.
موفقیت مدلهای زبانی بزرگ تا حد زیادی به کیفیت و تنوع دادههای آموزشی آنها بستگی دارد. این دادهها نه تنها باید شامل حجم عظیمی از متون باشند، بلکه باید از لحاظ نوع، موضوع و سبک نیز گوناگون باشند.
بیشتر LLMها از دادههایی که با خزندههای وب جمعآوری شدهاند استفاده میکنند. این متون شامل صفحات ویکیپدیا، وبلاگها، فرومها، اخبار و پستهای شبکههای اجتماعی هستند. مزیت آنها حجم زیاد و پوشش موضوعی گسترده است، اما ممکن است حاوی نویز، اطلاعات نادرست یا محتوای غیراخلاقی باشند.
کتابها به دلیل داشتن ساختار منظم و زبان غنی، دادههای ارزشمندی محسوب میشوند. مقالات علمی نیز دانش تخصصی را در اختیار مدل قرار میدهند. برای مثال، پروژههایی مانند Pile یا C4 مجموعههایی از این نوع متون را در خود دارند.
برای آموزش مدلهای مکالمهمحور (مانند ChatGPT)، مجموعه دادههای گفتگویی شامل مکالمات انسان-انسان یا انسان-ماشین ضروری هستند. این دادهها به مدل کمک میکنند تا سبک طبیعی صحبت کردن و نوبتگیری در گفتگو را بیاموزد.
مدلهایی مانند Codex و AlphaCode از مخازن کد مانند GitHub برای آموزش استفاده کردهاند. همچنین مجموعههایی از سوالات ریاضی یا اثباتهای منطقی نیز برای مدلسازی استدلال و حل مسئله بهکار گرفته میشوند.
برای جلوگیری از یادگیری اطلاعات نادرست، تبعیضآمیز یا غیراخلاقی، فرآیندهایی برای فیلتر کردن دادهها به کار میرود. همچنین برخی شرکتها مانند OpenAI و Anthropic از تیمهای انسانی برای مرور دادهها و حذف محتوای مضر استفاده میکنند.
بسیاری از LLMها، به ویژه مدلهای جهانی مانند mBERT یا XLM-R، برای پشتیبانی از زبانهای مختلف دنیا نیازمند دادههایی به زبانهای گوناگون هستند. این دادهها شامل ترجمههای موازی، وبسایتهای چندزبانه (مانند ویکیپدیای زبانهای مختلف)، مقالات خبری و متون رسمی دولتها هستند. چالش اصلی در دادههای چندزبانه، نابرابری حجم و کیفیت بین زبانهاست؛ مثلاً زبانهایی مانند انگلیسی و چینی حجم بالایی دارند، اما زبانهایی مثل فارسی، پشتو یا زبانهای آفریقایی دادههای محدودی دارند.
مدلهای زبانی فقط با متن خام آموزش نمیبینند. دادههایی مانند جداول، نمودارها، کدهای JSON، ساختارهای XML و فایلهای Markdown، ساختار اطلاعاتی ارزشمندی دارند. استفاده از این دادهها به مدل کمک میکند تا با ساختارهای اطلاعاتی دنیای واقعی بهتر آشنا شود. برای مثال، برخی مدلها میتوانند جداول HTML را درک کرده و تبدیل به متن یا بالعکس کنند.
برای استفاده تخصصی در حوزههایی مانند پزشکی، حقوق، مهندسی یا امور مالی، مدلها باید بر دادههای همان حوزهها آموزش ببینند. مثلاً Med-PaLM از متون پزشکی مانند مقالات PubMed یا مکالمات پزشک-بیمار استفاده کرده است. استفاده از دادههای تخصصی باعث افزایش دقت و توان مدل در وظایف آن حوزه میشود، اما ممکن است باعث اُفت عملکرد در وظایف عمومی شود، که به آن overfitting دامنهای میگویند.
برای مدلهایی که قرار است با انسان تعامل داشته باشند (مانند چتباتها یا دستیارهای هوشمند)، جمعآوری دادههای انسانی اهمیت ویژهای دارد. این دادهها میتوانند شامل:
-
رتبهبندی پاسخهای مدل توسط انسان
-
اصلاح خروجیهای مدل توسط انسان
-
نمونههایی از پاسخهای ایدهآل باشند
این دادهها در مرحله RLHF (Reinforcement Learning with Human Feedback) به کار میروند تا مدل نهفقط زبان، بلکه نیت و سلیقه کاربران را بهتر درک کند.
در برخی مواقع، بهجای جمعآوری داده واقعی، از مدلهای قبلی برای تولید داده استفاده میشود. مثلاً با مدلهای سادهتر میتوان سوال و جواب ساخت و به مدل جدید آموزش داد. این روش به کاهش هزینه جمعآوری داده و افزایش تنوع کمک میکند، اما خطر تقویت خطاهای مدلهای قدیمی وجود دارد (error amplification).
توکنسازی، یکی از مراحل حیاتی قبل از آموزش مدل زبانی است. این فرآیند به مدل کمک میکند تا متن خام را به قطعاتی قابل فهم تبدیل کند. این قطعات میتوانند کلمات، زیرکلمات یا کاراکترها باشند، اما در اکثر مدلهای مدرن از توکنهای زیرکلمهای استفاده میشود.
استفاده از زیرکلمهها مزایای متعددی دارد:
-
جلوگیری از افزایش بیشازحد اندازه واژگان (vocabulary)
-
توانایی تشخیص و پردازش کلمات ناشناخته یا ترکیبی (مانند "self-driving")
-
انعطافپذیری بالا در زبانهای با صرف پیچیده مانند فارسی یا آلمانی
-
استفاده از زیرکلمهها کمک میکند مدل بتواند کلمات جدید، ناآشنا یا ترکیبی را درک کند، حتی اگر قبلاً آنها را ندیده باشد.
کلمهی جدید «خودران» اگر بهصورت کامل در واژگان نباشد، ممکن است به صورت دو زیرکلمه «خود» و «ران» شکسته شود، و مدل بتواند با درک معنای این دو، معنی کلی را حدس بزند.
-
Byte Pair Encoding (BPE): متداولترین روش. ابتدا پرتکرارترین جفتهای کاراکتری را پیدا میکند و به صورت تکراری آنها را ادغام میکند.
مثال:
کلمهیunhappinessممکن است ابتدا به صورتu n h a p p i n e s sشکسته شود، سپس با توجه به فراوانی، به صورتun,hap,pinessتبدیل شود. -
WordPiece: توسط گوگل برای BERT توسعه داده شد. مشابه BPE است اما با معیار احتمال حداکثر ، بهترین تقسیمبندی را انتخاب میکند.
مثال:
برای کلمهی نا آشنایplayingfullyممکن است خروجی توکنهاplaying,##ful,##lyباشد که##نشاندهنده ادامه کلمه است. -
Unigram Language Model: توسط SentencePiece استفاده میشود. تمام توکنهای ممکن را در نظر میگیرد و بهترین ترکیب را انتخاب میکند.
مثال:
برای کلمهیnationalizationممکن است تقسیمهایی مانندnational+izationیاnation+alizationبررسی شود و بهترین بر اساس احتمال انتخاب گردد. -
Tiktoken (OpenAI): سیستم توکنسازی اختصاصی OpenAI برای مدلهای GPT، که با بازدهی بالا طراحی شده است و از توکنهایی استفاده میکند که حتی شامل نویسههای خاص یا اموجیها هستند.
مثال:
عبارتI ❤️ GPT-4!ممکن است به صورت توکنهایI,❤️,GPT,-,4,!شکسته شود، برخلاف BPE که شایدGPT-4را به چند تکه بشکند.
توکنسازهای عمومی اغلب برای زبان انگلیسی بهینهسازی شدهاند. این موضوع برای زبانهایی مانند فارسی، عربی، اردو یا ترکی که ساختار صرفی پیچیده و الفبای متفاوت دارند، مشکلساز میشود. مثلاً عبارت «مینویسم» ممکن است به چند توکن جدا تبدیل شود که یادگیری معنای آن را سختتر میکند.
این کلمه از چند جزء تشکیل شده:
-
«می» → پیشوند زمان حال استمراری
-
«نویس» → ریشهی فعل
-
«م» → شناسهی اول شخص مفرد
| روش توکنسازی | خروجی توکنها برای "مینویسم" | توضیح |
|---|---|---|
| BPE | می, نویس, م |
با یادگیری پرتکرارترین ترکیبها، سه زیرواژه رایج را حفظ میکند. |
| WordPiece | می, ##نویس, ##م |
نشانهی ## یعنی این بخشها ادامه کلمه هستند. |
| Unigram | مینویس, م یا می, نویسم |
چندین احتمال را بررسی میکند، سپس بهترین را برمیگزیند. |
| Tiktoken | می, نوی, سم یا حتی کاملتر |
توکنبندی بسیار خاص و سریع برای سازگاری با GPT. |
اگر مدل نتواند «مینویسم» را به شکل مناسبی تجزیه کند، ممکن است:
-
مفهوم زمان حال استمراری را درک نکند (اگر «می» حذف شود)
-
ضمیر را از دست بدهد (اگر «م» حذف شود)
-
یا ریشه فعل را گم کند (اگر «نویس» اشتباه شکسته شود)
پس از توکنسازی، هر توکن به یک عدد صحیح (Token ID) تبدیل میشود تا توسط مدل پردازش شود. این نگاشت بهصورت دیکشنری انجام میشود و مدل فقط با اعداد کار میکند، نه کلمات.
مدلهای زبانی بزرگ تقریباً همگی بر پایه معماری Transformer ساخته شدهاند که در سال ۲۰۱۷ توسط Google معرفی شد. این معماری انقلابی، باعث شد تا مدلها بدون نیاز به ساختارهای سلسلهمراتبی مانند RNN یا LSTM، بتوانند وابستگیهای طولانی در متن را یاد بگیرند.
-
Embedding Layer: تبدیل توکن IDها به بردارهای قابل یادگیری
-
Multi-Head Self-Attention: یادگیری رابطه بین هر توکن با دیگر توکنها
-
Feed Forward Network (FFN): اعمال نگاشتهای غیرخطی برای افزایش قدرت مدل
-
Layer Normalization و Residual Connections: برای پایدارسازی آموزش
فرض کنید مدل ما جمله ی زیر رو پردازش کنه:
امروز هوا خیلی خوبه.
هدف مدل اینه که بفهمه معنای جمله چیه و مثلاً پیشبینی کنه که توکن بعدی چیه یا جواب سوالی بده.
بیایم ببینیم چه اتفاقی میافته:
-
جمله به توکنها تبدیل میشه (مثلاً با BPE یا WordPiece):
["امروز", "هوا", "خیلی", "خوبه", "."] -
هر توکن به یک عدد نگاشت میشه:
[587, 905, 342, 881, 3] -
سپس هر عدد به یک بردار واقعی (مثلاً با 768 بعد) تبدیل میشه.
این بردارها قابل یادگیری هستن و میتونن نماینده معنای توکن باشن.
-
چون ترنسفورمر ترتیب توکنها رو نمیفهمه، به هر بردار، اطلاعاتی درباره موقعیت اون توکن هم اضافه میشه. مثلاً توکن اول، دوم، ... ششم.
-
نتیجه: بردارهایی که هم معنا و هم جایگاه رو در خودشون دارن.
-
مدل نگاه میکنه هر توکن با کدوم توکنهای دیگه باید ارتباط برقرار کنه.
-
مثلاً:
-
«خوبه» به «هوا» توجه میکنه تا بفهمه «چی خوبه؟»
-
«امروز» به «هوا» توجه میکنه برای ساخت زمینه زمانی
-
-
این اتفاق با محاسبهی ماتریس Attention میافته، برای تمام جفتهای توکن.
-
در "multi-head"، این توجه در چند فضای معنایی مختلف انجام میشه (مثلاً یک head برای دستور زبان، یکی برای زمان، یکی برای جنسیت و ...)
-
برای هر توکن، یک شبکهی کوچک دو لایهای (MLP) داریم که بردارش رو به شکلی غیرخطی تغییر میده.
-
انگار یه پردازش محلی برای هر توکن انجام میدیم.
-
هدف: افزایش ظرفیت مدل برای درک الگوهای پیچیده.
-
خروجی attention و FFN با ورودی قبلی جمع میشن (Residual)، بعد نرمالسازی میشن.
-
کمک میکنه که مدل در عمق بالا هم دچار ناپایداری نشه و گرادیانها بهتر منتقل شن.
این کل بلاک (Attention + FFN + Normalization) معمولاً n بار تکرار میشه (مثلاً 12 یا 96 بار بسته به اندازه مدل).
توکنها به بردارهایی تبدیل میشن که میتونن برای کارهایی مثل:
-
پیشبینی کلمه بعدی
-
پاسخ به سؤال
-
تولید متن
مورد استفاده قرار بگیرن.
مثلاً مدل بفهمه که در جمله:
«امروز هوا خیلی خوبه.»
توکن «خوبه» باید به «هوا» توجه کنه تا معنای جمله درست درک بشه. این «توجه» همون attention هست که مدل خودش یاد میگیره، بدون اینکه ما براش مشخص کنیم.
مدلهایی مانند GPT، فقط از قسمت Decoder ترنسفورمر استفاده میکنند. این مدلها در تولید متن بسیار مؤثرند و آموزش آنها به صورت خودپیشبینی (causal language modeling) انجام میشود.
مدلهایی مانند BERT فقط از Encoder استفاده میکنند. این مدلها برای درک معنا و استخراج ویژگیها مناسب هستند و اغلب در وظایف دستهبندی یا پرسشپاسخ به کار میروند.
مدلهایی مانند T5 یا BART از هر دو بخش Encoder و Decoder استفاده میکنند و برای وظایف تبدیل متن به متن (مانند ترجمه، خلاصهسازی یا اصلاح گرامر) مناسب هستند.
آمادهسازی دادهها یکی از حساسترین مراحل آموزش LLM است. این مرحله شامل پاکسازی، فیلتر، نرمالسازی و ساخت فرمت نهایی برای تغذیه به مدل است.
-
پاکسازی نویز: حذف HTMLهای اضافی، تبلیغات، ایمیلها و دادههای تکراری
-
نرمالسازی: یکسانسازی فاصلهها، علائم نگارشی، یونیکد و حذف نویسههای شکسته
-
حذف دادههای سمی یا مغرضانه: مانند نژادپرستی، توهین، اطلاعات نادرست
-
ساخت توکنها و تقسیمبندی طولی: محدود کردن طول هر نمونه به حداکثر توکن مجاز مدل (مثلاً 2048)
-
شافلکردن: برای جلوگیری از هموابستگی موضوعی در مینیبچها
فرض کنید جملهی زیر یکی از ورودیهای خام ماست:
"سلام!!! چطوری؟ من ۳ بار ایمیلت رو فرستادم ولی جواب ندادی :("
-
«۳» به «سه» تبدیل میشه یا حداقل با فرمت عددی قابل فهم مدل ذخیره میشه.
-
نویسههای عربی مثل «ي» و «ك» به فارسی «ی» و «ک» تبدیل میشن (اگه وجود داشته باشن).
-
مثال:
"سلام!!! چطوری؟ من سه بار ایمیلت رو فرستادم ولی جواب ندادی :("
-
نشانههای نگارشی تکراری مثل «!!!»، فاصلههای زیاد، یا صورتکها حذف یا جایگزین میشن.
-
مثال:
"سلام! چطوری؟ من سه بار ایمیلت رو فرستادم ولی جواب ندادی"
- برای زبان فارسی تفاوت بزرگی نداره، ولی در انگلیسی حروف بزرگ به کوچک تبدیل میشن.
- در برخی مدلها، واژههایی مثل «من»، «رو»، «ولی» حذف میشن (اگر هدف، تحلیل معنا نباشه).
-
جمله به بخشهای کوچکتر (توکنها) تقسیم میشه.
مثال خروجی:["سلام", "!", "چطوری", "؟", "من", "سه", "بار", "ایمیلت", "رو", "فرستادم", "ولی", "جواب", "ندادی"]
-
هر توکن با استفاده از واژهنامه (Vocabulary) به یک عدد نگاشت میشه:
[342, 23, 912, 51, 10, 104, 207, 591, 11, 803, 55, 401, 721]
- اگر طول جمله کمتر از مقدار مورد نظر باشه، صفر اضافه میشه؛ اگر بیشتر باشه، انتهای جمله بریده میشه.
-
مشخص میکنه کدوم توکنها معنیدار هستن و کدوم فقط پدینگن. مثلاً:
Attention mask: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
بعد از این فرآیندها، جملهی فارسی ما آماده تغذیه به مدل میشه، بهطوریکه مدل دقیقاً میفهمه چه چیزی گفته شده، کجا مکث وجود داشته، و کدوم توکنها اهمیت دارن.
مدلهای زبانی بزرگ معمولاً با اندازههای مختلفی عرضه میشوند: کوچک، متوسط، بزرگ و غولپیکر (مانند GPT-3 با 175 میلیارد پارامتر). تصمیم درباره اندازه مدل بستگی به منابع محاسباتی، کاربرد هدف و محدودیتهای فنی دارد.
-
افزایش پارامتر لزوماً به معنای افزایش کیفیت نیست
-
مدلهای بزرگتر به داده، GPU و زمان آموزش بیشتری نیاز دارند
-
ممکن است مدل کوچک با آموزش بهتر از مدل بزرگ آموزشندیده عملکرد بهتری داشته باشد
-
مدلهای کوچکتر برای اجرا روی موبایل یا لبه مناسبترند (مانند LLaMA 2 7B)
آموزش مدل شامل تغذیه دادهها به شبکه عصبی و بهروزرسانی وزنها با استفاده از الگوریتمهای بهینهسازی است. در LLMها، این فرایند ممکن است هفتهها یا ماهها طول بکشد.
-
Loss Function: اغلب Cross-Entropy برای پیشبینی توکن بعدی
-
Optimization: استفاده از Adam یا AdamW
-
Gradient Clipping: برای کنترل انفجار گرادیان
-
Mixed Precision Training: برای کاهش حافظه و افزایش سرعت
-
Checkpointing: ذخیره مرحلهای مدل برای بازیابی یا ادامه آموزش
-
Distributed Training: استفاده از چند GPU یا حتی چندین سرور برای آموزش موازی
آموزش LLM یعنی یاد دادن به مدل که:
-
درک کنه توی متنها چه الگوهایی وجود داره
-
و بتونه توکن بعدی رو با دقت بالا پیشبینی کنه
مثل اینه که هزاران کتاب، مکالمه، پست وبلاگی و کد رو به مدل بدیم و بگیم:
«هر بار فقط یکی از کلمهها رو حذف میکنیم، تو حدس بزن چی بوده!»
برای اینکه بفهمیم مدل چقدر خوب عمل کرده، از تابع خطا یا Loss Function استفاده میکنیم.
در LLMها، معمولاً از Cross-Entropy Loss استفاده میکنیم.
این تابع بررسی میکنه که آیا احتمال دادهشده توسط مدل برای توکن درست، بالا بوده یا نه.
مثال: اگر مدل جمله «من امروز به…» رو دید و باید «مدرسه» رو پیشبینی کنه:
-
اگه احتمال «مدرسه» رو بده 0.8 → Loss پایین
-
اگه بده 0.01 → Loss بالا
هدف آموزش: کم کردن میانگین Loss روی تمام دادهها
حالا که فهمیدیم مدل چقدر اشتباه کرده (Loss)، باید کاری کنیم که دفعه بعد اشتباه کمتری کنه.
برای اینکار از الگوریتمهای بهینهسازی استفاده میکنیم. معروفترینها:
-
Adam: نسخهی پیشرفته SGD که با نرخ یادگیری تطبیقی کار میکنه.
-
AdamW: مثل Adam هست، ولی وزنها رو بهصورت خاصی regularize میکنه (وزنها رو خیلی بزرگ نکنه).
مدل با این الگوریتم، وزنها (parameters) رو قدم به قدم طوری تغییر میده که Loss کمتر بشه.
گاهی در مدلهای بزرگ، هنگام backpropagation، مقدار گرادیان خیلی بزرگ میشه و باعث ناپایداری آموزش میشه (گرادیان منفجر میشه).
اینجاست که Gradient Clipping وارد میشه:
اگر گرادیان خیلی بزرگ شد، اونو قطع یا مقیاس میکنیم تا نترکونه!
این کار جلوی نوسانات شدید مدل رو میگیره.
مدلهای بزرگ حافظه زیادی میخوان. برای بهینهکردن آموزش:
-
بخشی از محاسبات با float16 (عددی با دقت کمتر ولی سریعتر)
-
و بخش حساستر با float32 (دقیقتر ولی سنگینتر) انجام میشه
این کار باعث میشه:
-
سرعت آموزش زیاد بشه
-
حافظه GPU کمتر اشغال بشه
-
اما دقت مدل قابلقبول باقی بمونه
در اغلب LLMهای جدید این تکنیک بهشدت استفاده میشه.
آموزش LLMها ممکنه هفتهها یا ماهها طول بکشه. اگه وسطش برق بره یا GPU خراب بشه؟
اینجاست که Checkpoint کمک میکنه:
مدل در بازههای منظم (مثلاً هر ۱۰۰۰ مرحله) ذخیره میشه
پس اگه مشکلی پیش اومد:
-
از آخرین Checkpoint ادامه میدیم
-
نیاز نیست از اول شروع کنیم
همچنین برای فاینتیونینگ بعدی هم از همین چکپوینتها استفاده میشه.
مدلهایی مثل GPT-4 یا PaLM دهها یا صدها میلیارد پارامتر دارن و روی یک GPU جا نمیشن.
پس آموزش اونها به صورت توزیعشده انجام میشه:
-
Data Parallelism: دادهها بین GPUها تقسیم میشن و هر GPU نسخهای از مدل داره
-
Model Parallelism: خود مدل بین چند GPU پخش میشه
-
Pipeline Parallelism: لایههای مدل به صورت زنجیرهای روی چند GPU تقسیم میشن
-
یا ترکیبی به اسم DeepSpeed یا FSDP (Fully Sharded Data Parallel)
با این تکنیکها:
-
مدلهای غولپیکر رو میتونیم آموزش بدیم
-
سرعت آموزش از چند هفته به چند روز میرسه
آموزش LLM یعنی یک مسابقهٔ نفسگیر بین:
-
دادههای حجیم (ترابایتها متن)
-
مدل بسیار بزرگ (میلیاردها وزن)
-
زمان زیاد و منابع محاسباتی بالا
اما با استفاده از بهینهسازیهای هوشمند، تکنیکهای پیشرفته مثل Mixed Precision، و توزیع بار روی هزاران GPU، آموزش این مدلها ممکن شده.
مدلهای زبانی (Language Models یا به اختصار LMها) نوعی شبکه عصبی هستند که هدف اصلیشان یادگیری توزیع احتمال دنبالهای از کلمات یا توکنهاست. بهصورت ریاضی، هدف این مدلها پیشبینی احتمال رخداد یک توکن با توجه به توکنهای قبلی است:
P(wt∣w1,w2,...,wt−1)
در این رابطه:
-
wt توکن فعلی است که باید پیشبینی شود.
-
w1,...,wt−1 توکنهای قبلی هستند که بهعنوان context استفاده میشوند.
زیرا اگر مدلی بتواند توکن بعدی را بهخوبی حدس بزند، میتواند:
-
متن تولید کند،
-
جمله را کامل کند،
-
پاسخ سؤال بدهد،
-
یا حتی کد بنویسد.
مدلهای زبانی به دو دسته اصلی تقسیم میشوند:
-
Unidirectional Language Models مثل GPT که فقط از سمت چپ به راست نگاه میکنند.
-
Bidirectional Language Models مثل BERT که متن را هم از چپ و هم از راست نگاه میکنند.
در این پروژه، ما ابتدا روی نوع اول تمرکز میکنیم چون مناسب تولید متن (Text Generation) است.
قبل از اینکه دادهای به مدل بدهیم، باید آن را به توکنهایی تبدیل کنیم که مدل بتواند آنها را درک کند.
چون مدل با کلمه یا حرف سروکار ندارد، بلکه با توکنهایی سروکار دارد که نمایشی عددی و یکنواخت دارند. به زبان ساده، هر ورودی باید به عدد (ID) تبدیل شود.
-
Word-level tokenization
مثال:"I love AI"⟶["I", "love", "AI"]
مشکل: تعداد کلمات بسیار زیاد است (vocabulary size بالا) -
Character-level tokenization
مثال:"I love"⟶["I", " ", "l", "o", "v", "e"]
مشکل: دنبالهها بسیار بلند میشوند. -
Subword-level tokenization (BPE یا SentencePiece)
مثال:"unbelievable"⟶["un", "believ", "able"]
مزیت: هم ترکیبی است و هم کمواژهتر.
ما در ادامه از روش BPE استفاده میکنیم که پایهی بیشتر LLMهاست.
اکنون باید دادههایی که در اختیار داریم را تبدیل به فرمتی کنیم که مدل بتواند آن را بخواند.
-
خواندن داده متنی (مثلاً فایل txt)
-
تبدیل آن به لیستی از توکنها
-
تقسیم آن به دنبالههای طول ثابت (مثلاً 128 توکن)
-
تولید ورودی (X) و خروجی هدف (y) برای آموزش مدل
مثال با یک جمله:
-
ورودی:
["من", "کتاب", "را", "میخوانم"] -
هدف:
["کتاب", "را", "میخوانم", "<EOS>"]
یعنی مدل باید یاد بگیرد با دیدن هر توکن، توکن بعدی را حدس بزند.
فکر میکنم کم کم باید کد زدن رو شروع کنیم، پس :
فرض کنیم فایل data.txt شامل متن زیر است:
من برنامهنویسی را دوست دارم. مدلهای زبانی بسیار جالب هستند.
برای شروع، یک توکنایزر ابتدایی کلمهمحور میسازیم (در بخش ۱۴ توکنایزر پیچیدهتر مثل BPE داریم):
text = open("data.txt", encoding="utf-8").read()
tokens = text.split() # توکنایزر ساده مبتنی بر فاصله
vocab = sorted(set(tokens))
word2idx = {word: idx for idx, word in enumerate(vocab)}
idx2word = {idx: word for word, idx in word2idx.items()}مثلاً خروجی:
tokens = ['من', 'برنامهنویسی', 'را', 'دوست', 'دارم.', 'مدلهای', 'زبانی', 'بسیار', 'جالب', 'هستند.']
word2idx = {'برنامهنویسی': 0, 'بسیار': 1, 'جالب': 2, 'دارم.': 3, 'دوست': 4, 'زبانی': 5, 'را': 6, 'من': 7, 'مدلهای': 8, 'هستند.': 9}token_ids = [word2idx[word] for word in tokens]
# مثال خروجی: [7, 0, 6, 4, 3, 8, 5, 1, 2, 9]فرض کنیم block_size = 4 (یعنی 4 توکن به عنوان ورودی → پیشبینی توکن بعدی)
block_size = 4
X = []
Y = []
for i in range(len(token_ids) - block_size):
x = token_ids[i : i + block_size]
y = token_ids[i + block_size]
X.append(x)
Y.append(y)خروجی:
X = [
[7, 0, 6, 4], # "من برنامهنویسی را دوست"
[0, 6, 4, 3], # "برنامهنویسی را دوست دارم."
[6, 4, 3, 8], # ...
[4, 3, 8, 5],
[3, 8, 5, 1],
[8, 5, 1, 2],
]
Y = [3, 8, 5, 1, 2, 9]import torch
X_tensor = torch.tensor(X, dtype=torch.long)
Y_tensor = torch.tensor(Y, dtype=torch.long)
print(X_tensor.shape) # torch.Size([6, 4])
print(Y_tensor.shape) # torch.Size([6])from torch.utils.data import Dataset, DataLoader
class SimpleTextDataset(Dataset):
def __init__(self, X, Y):
self.X = X
self.Y = Y
def __len__(self):
return len(self.X)
def __getitem__(self, idx):
return self.X[idx], self.Y[idx]
dataset = SimpleTextDataset(X_tensor, Y_tensor)
loader = DataLoader(dataset, batch_size=2, shuffle=True)for batch_x, batch_y in loader:
print("X:", batch_x)
print("Y:", batch_y)
breakالان یک دیتاست کامل آماده آموزش داریم که از متن ساده فارسی استخراج شده و به صورت batch آماده تغذیه به مدل زبانی ماست.
ساخت یک مدل ساده اما کاربردی که ورودیاش دنبالهای از توکنهاست و خروجیاش پیشبینی توکن بعدی.
در این مرحله، ما یک معماری پایه برای مدل زبانی میسازیم:
-
Embedding Layer: نگاشت هر توکن به یک بردار ویژگی
-
یک یا چند لایه ترنسفورمر (سادهشده) یا RNN (فعلاً ساده)
-
Linear layer: خروجی به اندازه واژگان برای پیشبینی توکن بعدی
برای شروع از سادهترین حالت استفاده میکنیم:
import torch
import torch.nn as nn
import torch.nn.functional as F
class TinyLanguageModel(nn.Module):
def __init__(self, vocab_size, embed_dim):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.linear = nn.Linear(embed_dim, vocab_size)
def forward(self, x):
embeds = self.embedding(x) # (batch, seq_len, embed_dim)
avg_embed = embeds.mean(dim=1) # (batch, embed_dim)
logits = self.linear(avg_embed) # (batch, vocab_size)
return logitsvocab_size = len(word2idx)
model = TinyLanguageModel(vocab_size, embed_dim=32)
sample_x, sample_y = next(iter(loader))
logits = model(sample_x)
print("logits shape:", logits.shape) # (batch_size, vocab_size)optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
loss_fn = nn.CrossEntropyLoss()
for epoch in range(100):
total_loss = 0
for batch_x, batch_y in loader:
logits = model(batch_x)
loss = loss_fn(logits, batch_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
if epoch % 10 == 0:
print(f"Epoch {epoch} | Loss: {total_loss:.4f}")def predict_next_token(input_tokens):
input_ids = torch.tensor([[word2idx[t] for t in input_tokens]], dtype=torch.long)
logits = model(input_ids)
probs = F.softmax(logits, dim=-1)
next_token_id = torch.argmax(probs, dim=-1).item()
return idx2word[next_token_id]
test_input = ['من', 'برنامهنویسی', 'را', 'دوست']
print("Next word:", predict_next_token(test_input))ما موفق شدیم یک مدل خیلی ساده اما کامل بسازیم که میتونه توکن بعدی یک دنباله را پیشبینی کنه.
پیادهسازی یک مدل زبانی ساده ولی واقعی با استفاده از Transformer Blocks که ورودی آن دنبالهای از توکنهاست و خروجیاش احتمال توکن بعدی در هر موقعیت است.
Input (Token IDs)
↓
Embedding Layer + Positional Encoding
↓
N × Transformer Block (Self-Attention + FeedForward)
↓
Linear Layer → Vocab Size
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0) # (1, max_len, d_model)
self.register_buffer('pe', pe)
def forward(self, x):
x = x + self.pe[:, :x.size(1)]
return x
class TinyTransformerLM(nn.Module):
def __init__(self, vocab_size, d_model=64, nhead=4, num_layers=2):
super().__init__()
self.token_embed = nn.Embedding(vocab_size, d_model)
self.pos_encoder = PositionalEncoding(d_model)
encoder_layer = nn.TransformerEncoderLayer(d_model=d_model, nhead=nhead, dim_feedforward=128)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
self.linear_out = nn.Linear(d_model, vocab_size)
def forward(self, x):
# x shape: (batch, seq_len)
emb = self.token_embed(x) # (batch, seq_len, d_model)
emb = self.pos_encoder(emb) # (batch, seq_len, d_model)
emb = emb.permute(1, 0, 2) # Transformer expects (seq_len, batch, d_model)
output = self.transformer(emb) # (seq_len, batch, d_model)
output = output.permute(1, 0, 2) # (batch, seq_len, d_model)
logits = self.linear_out(output) # (batch, seq_len, vocab_size)
return logitsmodel = TinyTransformerLM(vocab_size=len(word2idx), d_model=64)
sample_x, _ = next(iter(loader)) # (batch, seq_len)
logits = model(sample_x) # (batch, seq_len, vocab_size)
print("Logits shape:", logits.shape)def train_model(model, dataloader, epochs=20):
optimizer = torch.optim.AdamW(model.parameters(), lr=0.001)
loss_fn = nn.CrossEntropyLoss()
for epoch in range(epochs):
total_loss = 0
for batch_x, batch_y in dataloader:
logits = model(batch_x) # (batch, seq_len, vocab_size)
# فقط توکن آخر رو هدف قرار بدیم
final_logits = logits[:, -1, :] # (batch, vocab_size)
loss = loss_fn(final_logits, batch_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch} | Loss: {total_loss:.4f}")def predict_next_token(model, input_tokens, topk=5):
model.eval()
input_ids = torch.tensor([[word2idx[t] for t in input_tokens]], dtype=torch.long)
with torch.no_grad():
logits = model(input_ids) # (1, seq_len, vocab_size)
last_logits = logits[:, -1, :] # (1, vocab_size)
probs = F.softmax(last_logits, dim=-1)
topk_ids = torch.topk(probs, k=topk).indices[0].tolist()
return [idx2word[i] for i in topk_ids]test_input = ['من', 'کتاب', 'را']
print("Predicted next words:", predict_next_token(model, test_input))ما در این مرحله یک مدل زبانی واقعی با ترنسفورمر طراحی کردیم که میتونه توکن بعدی را پیشبینی کنه و ساختارش شبیه به مدلهای بزرگتره. حالا پایهی لازم برای ساخت یک LLM واقعی رو داریم.
ایجاد مدلی که بتونه بعد از دیدن یک دنبالهی اولیه از کلمات، توکنهای بعدی رو یکییکی پیشبینی و تولید کنه؛ درست مثل LLMهای واقعی مثل GPT.
مدلهای auto-regressive مثل GPT، خروجی خودشون رو به عنوان ورودی مرحلهی بعدی استفاده میکنن.
فرایند تولید به این شکله
Input: ["من", "کتاب", "را"]
→ مدل پیشبینی میکنه: "خواندم"
→ Input جدید میشه: ["من", "کتاب", "را", "خواندم"]
→ مدل پیشبینی میکنه: "."
→ و همینطور ادامه میده...
ما قبلاً مدلی ساختیم که میتونه خروجی برای یک توکن بعدی بده. حالا میخوایم یک تابع بسازیم که بتونه بهصورت مرحلهای متن کامل تولید کنه.
def generate_text(model, prompt_tokens, max_new_tokens=10, temperature=1.0, top_k=5):
model.eval()
tokens = [word2idx.get(t, word2idx["<unk>"]) for t in prompt_tokens]
for _ in range(max_new_tokens):
input_ids = torch.tensor([tokens], dtype=torch.long) # (1, len)
with torch.no_grad():
logits = model(input_ids) # (1, seq_len, vocab_size)
logits = logits[0, -1, :] / temperature # فقط آخرین توکن → (vocab_size,)
probs = F.softmax(logits, dim=-1)
# top-k sampling
topk_probs, topk_indices = torch.topk(probs, k=top_k)
chosen = torch.multinomial(topk_probs, num_samples=1).item()
next_token = topk_indices[chosen].item()
tokens.append(next_token)
# اگه توکن پایان باشه، تولید رو متوقف کن
if idx2word[next_token] in ['<eos>', '.', '؟']:
break
return [idx2word[tok] for tok in tokens]prompt = ['من', 'کتاب', 'را']
generated = generate_text(model, prompt, max_new_tokens=10)
print(" ".join(generated))| پارامتر | توضیح |
|---|---|
max_new_tokens |
تعداد توکنهایی که مدل باید پیشبینی کنه |
temperature |
دمای نمونهگیری؛ هرچه بالاتر، خروجی خلاقانهتر ولی بیثباتتر |
top_k |
فقط از بین k تا توکن پر احتمال نمونهگیری انجام میشه |
Prompt: من کتاب را
Output: من کتاب را خواندم و آن را بسیار دوست داشتم .
یکی از اصلیترین اهداف در آموزش مدلهای زبانی بزرگ اینه که مدل بتونه فقط با دیدن توکنهای قبلی، توکن بعدی رو پیشبینی کنه. برای مثال اگه جملهای مثل «من کتاب را» رو به مدل بدی، انتظار داری مدل بگه «خواندم».
فرمول ریاضی این کار چیه؟
فرض کن یه دنبالهی متنی داری مثل: x = [x₁, x₂, x₃, ..., xₙ]
هدف اینه که مدل بتونه احتمالات زیر رو یاد بگیره:
P(x₁) * P(x₂ | x₁) * P(x₃ | x₁,x₂) * ... * P(xₙ | x₁,...,xₙ₋₁)
یعنی مدل در هر مرحله فقط باید با توجه به توکنهای قبلی، توکن بعدی رو درست حدس بزنه.
ورودی و هدف در آموزش
برای آموزش مدل، ورودیها و هدفها باید یک توکن از هم جابهجا باشن. مثلاً:
ورودی: من کتاب را
هدف: کتاب را خواندم
مدل یاد میگیره برای هر جای جمله، چی باید بیاد بعدش.
def create_training_batch(tokens, block_size=5):
X, Y = [], []
for i in range(len(tokens) - block_size):
x_chunk = tokens[i : i + block_size]
y_chunk = tokens[i + 1 : i + block_size + 1]
X.append(x_chunk)
Y.append(y_chunk)
return torch.tensor(X), torch.tensor(Y)این تابع یه دنباله رو به ورودی (X) و خروجی هدف (Y) تبدیل میکنه.
def train_epoch(model, optimizer, loss_fn, data, batch_size=8):
model.train()
total_loss = 0
for i in range(0, len(data) - batch_size, batch_size):
batch_tokens = data[i : i + batch_size + 1]
X, Y = create_training_batch(batch_tokens)
optimizer.zero_grad()
logits = model(X) # خروجی مدل: (B, T, vocab_size)
loss = loss_fn(logits.view(-1, logits.size(-1)), Y.view(-1))
loss.backward()
optimizer.step()
total_loss += loss.item()
return total_loss / len(data)توی این حلقه، هر بار چند توکن به مدل میدیم، خروجی میگیریم، loss رو حساب میکنیم، و مدل رو یک قدم آموزش میدیم.
خروجی نهایی مدل
اگه ورودی بدیم :
من کتاب را
مدل باید بگه مثلاً:
خواندم (با احتمال ۶۸٪)
مدل در آخر خروجیهایی به شکل logits تولید میکنه (اعداد خام)، بعد با softmax تبدیلشون میکنیم به احتمال.
with torch.no_grad():
out = model(X)
probs = F.softmax(out, dim=-1)
top_probs, top_ids = torch.topk(probs, k=3, dim=-1)
print("Top predictions per token:", top_ids)به این ترتیب میتونیم ببینیم مدل داره برای هر موقعیت تو جمله، چی پیشنهاد میده.
بعد از اینکه مدلمون آموزش دید (چه بزرگ، چه ساده)، حالا وقتشه باهاش متن تولید کنیم. معمولاً ورودی (prompt) میدیم و مدل ادامهش رو پیشبینی میکنه.
import torch
import torch.nn.functional as F
def generate_text(model, tokenizer, prompt, max_new_tokens=50, temperature=1.0, top_k=40):
model.eval()
input_ids = tokenizer.encode(prompt, return_tensors="pt")
for _ in range(max_new_tokens):
logits = model(input_ids).logits[:, -1, :] / temperature
top_k_logits, top_k_indices = torch.topk(logits, top_k)
probs = F.softmax(top_k_logits, dim=-1)
next_token = top_k_indices[torch.multinomial(probs, num_samples=1)]
input_ids = torch.cat([input_ids, next_token], dim=1)
return tokenizer.decode(input_ids[0], skip_special_tokens=True)فرض کن یه مدل داریم که زبان فارسی بلده، حالا میخوایم فقط روی دیالوگهای فروشگاهی fine-tune کنیم:
# مراحل کلی:
# - آمادهسازی دیتا
# - تعریف optimizer جدید با نرخ یادگیری کمتر
# - فریز کردن برخی لایهها (اختیاری)
# - آموزش مدل دوباره با batch کوچک
# مثلا:
for param in model.transformer.h[:6].parameters(): # لایههای اولیه رو فریز کن
param.requires_grad = Falseچند روش برای بررسی کیفیت:
from torch.nn import CrossEntropyLoss
def calculate_perplexity(model, input_ids, labels):
with torch.no_grad():
outputs = model(input_ids, labels=labels)
loss = outputs.loss
return torch.exp(loss) # perplexity = e^losspip install evaluatefrom evaluate import load
bleu = load("bleu")
results = bleu.compute(predictions=["سلام چطوری؟"], references=[["سلام، حالت چطوره؟"]])
print("BLEU:", results["bleu"])| ویژگی | مدل شما | GPT-2 | Bloom |
|---|---|---|---|
| تعداد پارامتر | ~10M | 124M–1.5B | تا 176B |
| زبان | فارسی | انگلیسی | چندزبانه |
| سرعت آموزش | سریع | کندتر | خیلی کند |
| نیاز به GPU | کم | متوسط | بالا |
pip install fastapi uvicorn# main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Prompt(BaseModel):
text: str
@app.post("/generate")
def generate(prompt: Prompt):
output = generate_text(model, tokenizer, prompt.text)
return {"response": output}uvicorn main:app --reload-
داده بد یا جانبدارانه
-
پیش پردازش روی دیتا
-
مشکلات اخلاقی
-
هزینه GPU بالا
-
استفاده از دیتاستهای بیشتر و تمیزتر
-
یادگیری Transfer Learning و Adapter Layers (مثل LoRA)
-
یادگیری Distributed Training با PyTorch Lightning و DeepSpeed
-
ساخت LLM سفارشی با کمترین منابع ممکن