Skip to content

MrYazdan/LLM

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 

Repository files navigation

۱. مقدمه

مدل‌های زبانی بزرگ (LLM: Large Language Models) در چند سال اخیر به عنوان یکی از پیشرفته‌ترین و تاثیرگذارترین فناوری‌های هوش مصنوعی مطرح شده‌اند. این مدل‌ها با تحلیل میلیاردها کلمه از متون موجود، توانایی درک زبان طبیعی، تولید متن، ترجمه، خلاصه‌سازی، کدنویسی، پاسخ به سوالات و انجام بسیاری از وظایف پیچیده زبانی را پیدا کرده‌اند.

با پیشرفت سخت‌افزارهای محاسباتی و در دسترس بودن داده‌های عظیم، امکان آموزش شبکه‌های عصبی بسیار بزرگ فراهم شد. شرکت‌هایی مانند OpenAI، Google و Meta با ارائه مدل‌هایی مانند GPT، BERT و LLaMA قدرت بی‌سابقه‌ای به مدل‌های زبانی بخشیده‌اند. این مدل‌ها نه تنها از لحاظ فنی پیچیده هستند، بلکه پیامدهای فلسفی، اجتماعی، حقوقی و اقتصادی نیز به همراه دارند.

هدف این ارائه، بررسی جامع از ابتدا تا انتهای مسیر تکامل LLMها است. در ابتدا، تاریخچه‌ای از پردازش زبان طبیعی و مدل‌های زبانی سنتی ارائه می‌شود. سپس ساختارهای کلیدی مانند Transformer، آشنایی با معماری مدل‌ها و روند آموزش بررسی خواهد شد. در ادامه با کاربردهای مدل‌های زبانی در زمینه‌های مختلف، چالش‌ها و محدودیت‌های آن‌ها و در نهایت آینده این حوزه آشنا خواهیم شد.

در انتهای ، یک مدل زبان ساده با پایتون پیاده‌سازی خواهد شد تا درک عملی‌تری از نحوه کار این مدل‌ها حاصل شود. همچنین مروری بر معروف‌ترین LLMهای موجود و ویژگی‌های آن‌ها انجام خواهد گرفت.

این تحقیق برای دانشجویان، پژوهشگران و علاقه‌مندان به هوش مصنوعی و پردازش زبان طبیعی می‌تواند نقطه آغازی برای یادگیری عمیق‌تر باشد.


۲. تاریخچه پردازش زبان طبیعی (NLP)

پردازش زبان طبیعی (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 وارد کردند.


۴. آغاز عصر یادگیری عمیق در NLP

ورود یادگیری عمیق (Deep Learning) به حوزه NLP، نقطه عطف بزرگی بود. شبکه‌های عصبی توانستند بدون نیاز به مهندسی ویژگی، خودشان از داده‌ها یاد بگیرند و الگوهای زبانی را کشف کنند. اولین گام‌ها با استفاده از شبکه‌های عصبی بازگشتی (RNN) برداشته شد که مناسب مدل‌سازی توالی بودند.

شبکه‌های LSTM (Long Short-Term Memory) و GRU (Gated Recurrent Unit) به منظور رفع مشکل ناپدید شدن گرادیان در شبکه‌های RNN معرفی شدند و عملکرد بهتری در نگهداری حافظه برای توالی‌های طولانی داشتند. این مدل‌ها در ترجمه ماشینی، تحلیل احساسات، و تولید متن به کار گرفته شدند.

در همین زمان، مدل‌های توزیعی کلمات مانند Word2Vec و GloVe معرفی شدند که هر کلمه را به صورت یک بردار در فضای برداری نمایش می‌دادند. این نمایش برداری کمک بزرگی به درک معنایی زبان توسط ماشین کرد.

با این حال، مدل‌های بازگشتی دارای محدودیت‌هایی مانند عدم قابلیت پردازش موازی و ضعف در درک روابط طولانی‌مدت بودند. این مسائل باعث شد تا نیاز به معماری‌های جدیدی احساس شود.

این نیاز با معرفی مدل Transformer در سال ۲۰۱۷ توسط گوگل پاسخ داده شد، که در بخش بعدی به طور مفصل به آن می‌پردازیم.


۵. معرفی معماری Transformer و انقلاب در NLP

معماری 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 تبدیل شد و تقریباً تمامی مدل‌های موفق بعدی بر پایه آن طراحی شدند.


۶. مراحل آموزش مدل‌های زبانی بزرگ (LLM)

فرآیند آموزش مدل‌های زبانی بزرگ معمولاً در چند مرحله‌ی اساسی انجام می‌شود که هر کدام نقش مهمی در کیفیت نهایی مدل ایفا می‌کنند:

۶.۱. پیش‌پردازش داده‌ها

قبل از آموزش، داده‌های خام نیاز به پاک‌سازی، حذف نویز، نرمال‌سازی، جداسازی جملات و توکن‌سازی دارند. همچنین در این مرحله داده‌های حساس یا تکراری نیز حذف می‌شوند.

۶.۲. پیش‌آموزش (Pretraining)

در این مرحله، مدل روی حجم عظیمی از متون بدون برچسب آموزش می‌بیند تا ساختار زبان، قواعد دستوری، روابط معنایی و اطلاعات عمومی را فراگیرد. معمولاً از روش‌های self-supervised مانند پیش‌بینی کلمه بعدی (Causal Language Modeling) یا پرکردن ماسک (Masked Language Modeling) استفاده می‌شود.

۶.۳. ریزتنظیم (Fine-tuning)

پس از پیش‌آموزش، مدل برای وظایف خاص (مانند ترجمه، خلاصه‌سازی یا پاسخ‌گویی) روی داده‌های هدفمند آموزش داده می‌شود. این مرحله با داده‌های کوچک‌تر و برچسب‌دار انجام می‌شود و باعث می‌شود مدل بهتر با کاربرد مشخص سازگار شود.

۶.۴. تنظیمات تقویتی (Reinforcement Learning from Human Feedback - RLHF)

برای برخی مدل‌ها مانند ChatGPT، مرحله‌ای تحت عنوان RLHF به کار می‌رود که در آن با کمک بازخورد انسان، مدل برای پاسخ‌های اخلاقی‌تر، سودمندتر و طبیعی‌تر بهینه می‌شود.

۶.۵. ارزیابی و تنظیم نهایی

در پایان، مدل با استفاده از مجموعه‌ای از معیارها ارزیابی می‌شود (که در بخش‌های بعدی خواهیم گفت). در صورت نیاز، تنظیمات نهایی یا pruning برای کاهش حجم و افزایش کارایی انجام می‌گیرد.

این مراحل به شدت نیازمند منابع محاسباتی بالا و مدیریت دقیق داده هستند. اجرای موفق این چرخه تفاوت بین یک LLM معمولی و یک مدل قدرتمند مانند GPT-4 یا Gemini را رقم می‌زند.


۷. انواع داده‌های آموزشی در مدل‌های زبانی بزرگ

موفقیت مدل‌های زبانی بزرگ تا حد زیادی به کیفیت و تنوع داده‌های آموزشی آن‌ها بستگی دارد. این داده‌ها نه تنها باید شامل حجم عظیمی از متون باشند، بلکه باید از لحاظ نوع، موضوع و سبک نیز گوناگون باشند.

۷.۱. متون وب (Web Crawl)

بیشتر LLMها از داده‌هایی که با خزنده‌های وب جمع‌آوری شده‌اند استفاده می‌کنند. این متون شامل صفحات ویکی‌پدیا، وبلاگ‌ها، فروم‌ها، اخبار و پست‌های شبکه‌های اجتماعی هستند. مزیت آن‌ها حجم زیاد و پوشش موضوعی گسترده است، اما ممکن است حاوی نویز، اطلاعات نادرست یا محتوای غیراخلاقی باشند.

۷.۲. کتاب‌ها و مقالات علمی

کتاب‌ها به دلیل داشتن ساختار منظم و زبان غنی، داده‌های ارزشمندی محسوب می‌شوند. مقالات علمی نیز دانش تخصصی را در اختیار مدل قرار می‌دهند. برای مثال، پروژه‌هایی مانند Pile یا C4 مجموعه‌هایی از این نوع متون را در خود دارند.

۷.۳. گفتگوها و مکالمات انسانی

برای آموزش مدل‌های مکالمه‌محور (مانند ChatGPT)، مجموعه داده‌های گفتگویی شامل مکالمات انسان-انسان یا انسان-ماشین ضروری هستند. این داده‌ها به مدل کمک می‌کنند تا سبک طبیعی صحبت کردن و نوبت‌گیری در گفتگو را بیاموزد.

۷.۴. داده‌های کدنویسی و ریاضی

مدل‌هایی مانند Codex و AlphaCode از مخازن کد مانند GitHub برای آموزش استفاده کرده‌اند. همچنین مجموعه‌هایی از سوالات ریاضی یا اثبات‌های منطقی نیز برای مدل‌سازی استدلال و حل مسئله به‌کار گرفته می‌شوند.

۷.۵. فیلتر و غربال‌گری داده‌ها

برای جلوگیری از یادگیری اطلاعات نادرست، تبعیض‌آمیز یا غیراخلاقی، فرآیندهایی برای فیلتر کردن داده‌ها به کار می‌رود. همچنین برخی شرکت‌ها مانند OpenAI و Anthropic از تیم‌های انسانی برای مرور داده‌ها و حذف محتوای مضر استفاده می‌کنند.

۷.۶. داده‌های چندزبانه (Multilingual Data)

بسیاری از LLMها، به ویژه مدل‌های جهانی مانند mBERT یا XLM-R، برای پشتیبانی از زبان‌های مختلف دنیا نیازمند داده‌هایی به زبان‌های گوناگون هستند. این داده‌ها شامل ترجمه‌های موازی، وب‌سایت‌های چندزبانه (مانند ویکی‌پدیای زبان‌های مختلف)، مقالات خبری و متون رسمی دولت‌ها هستند. چالش اصلی در داده‌های چندزبانه، نابرابری حجم و کیفیت بین زبان‌هاست؛ مثلاً زبان‌هایی مانند انگلیسی و چینی حجم بالایی دارند، اما زبان‌هایی مثل فارسی، پشتو یا زبان‌های آفریقایی داده‌های محدودی دارند.

۷.۷. داده‌های ساختاریافته و نیمه‌ساختاریافته

مدل‌های زبانی فقط با متن خام آموزش نمی‌بینند. داده‌هایی مانند جداول، نمودارها، کدهای JSON، ساختارهای XML و فایل‌های Markdown، ساختار اطلاعاتی ارزشمندی دارند. استفاده از این داده‌ها به مدل کمک می‌کند تا با ساختارهای اطلاعاتی دنیای واقعی بهتر آشنا شود. برای مثال، برخی مدل‌ها می‌توانند جداول HTML را درک کرده و تبدیل به متن یا بالعکس کنند.

۷.۸. داده‌های خاص دامنه (Domain-Specific)

برای استفاده تخصصی در حوزه‌هایی مانند پزشکی، حقوق، مهندسی یا امور مالی، مدل‌ها باید بر داده‌های همان حوزه‌ها آموزش ببینند. مثلاً Med-PaLM از متون پزشکی مانند مقالات PubMed یا مکالمات پزشک-بیمار استفاده کرده است. استفاده از داده‌های تخصصی باعث افزایش دقت و توان مدل در وظایف آن حوزه می‌شود، اما ممکن است باعث اُفت عملکرد در وظایف عمومی شود، که به آن overfitting دامنه‌ای می‌گویند.

۷.۹. داده‌های انسانی (Human Feedback)

برای مدل‌هایی که قرار است با انسان تعامل داشته باشند (مانند چت‌بات‌ها یا دستیارهای هوشمند)، جمع‌آوری داده‌های انسانی اهمیت ویژه‌ای دارد. این داده‌ها می‌توانند شامل:

  • رتبه‌بندی پاسخ‌های مدل توسط انسان

  • اصلاح خروجی‌های مدل توسط انسان

  • نمونه‌هایی از پاسخ‌های ایده‌آل باشند

این داده‌ها در مرحله RLHF (Reinforcement Learning with Human Feedback) به کار می‌روند تا مدل نه‌فقط زبان، بلکه نیت و سلیقه کاربران را بهتر درک کند.

۷.۱۰. داده‌های مصنوعی (Synthetic Data)

در برخی مواقع، به‌جای جمع‌آوری داده واقعی، از مدل‌های قبلی برای تولید داده استفاده می‌شود. مثلاً با مدل‌های ساده‌تر می‌توان سوال و جواب ساخت و به مدل جدید آموزش داد. این روش به کاهش هزینه جمع‌آوری داده و افزایش تنوع کمک می‌کند، اما خطر تقویت خطاهای مدل‌های قدیمی وجود دارد (error amplification).


۸. توکن‌سازی (Tokenization) در LLMها

توکن‌سازی، یکی از مراحل حیاتی قبل از آموزش مدل زبانی است. این فرآیند به مدل کمک می‌کند تا متن خام را به قطعاتی قابل فهم تبدیل کند. این قطعات می‌توانند کلمات، زیرکلمات یا کاراکترها باشند، اما در اکثر مدل‌های مدرن از توکن‌های زیرکلمه‌ای استفاده می‌شود.

۸.۱. چرا زیرکلمه (Subword)؟

استفاده از زیرکلمه‌ها مزایای متعددی دارد:

  • جلوگیری از افزایش بیش‌ازحد اندازه واژگان (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 را به چند تکه بشکند.

۸.۳. مسائل فرهنگی و زبانی در توکن‌سازی

توکن‌سازهای عمومی اغلب برای زبان انگلیسی بهینه‌سازی شده‌اند. این موضوع برای زبان‌هایی مانند فارسی، عربی، اردو یا ترکی که ساختار صرفی پیچیده و الفبای متفاوت دارند، مشکل‌ساز می‌شود. مثلاً عبارت «می‌نویسم» ممکن است به چند توکن جدا تبدیل شود که یادگیری معنای آن را سخت‌تر می‌کند.

مثال: «می‌نویسم»

این کلمه از چند جزء تشکیل شده:

  • «می» → پیشوند زمان حال استمراری

  • «نویس» → ریشه‌ی فعل

  • «م» → شناسه‌ی اول شخص مفرد

حالت‌های ممکن در توکن‌سازی (بسته به نوع tokenizer):
روش توکن‌سازی خروجی توکن‌ها برای "می‌نویسم" توضیح
BPE می, نویس, م با یادگیری پرتکرارترین ترکیب‌ها، سه زیرواژه رایج را حفظ می‌کند.
WordPiece می, ##نویس, ##م نشانه‌ی ## یعنی این بخش‌ها ادامه کلمه هستند.
Unigram می‌نویس, م یا می, نویسم چندین احتمال را بررسی می‌کند، سپس بهترین را برمی‌گزیند.
Tiktoken می‌, نوی, سم یا حتی کامل‌تر توکن‌بندی بسیار خاص و سریع برای سازگاری با GPT.
چرا این موضوع مهمه؟

اگر مدل نتواند «می‌نویسم» را به شکل مناسبی تجزیه کند، ممکن است:

  • مفهوم زمان حال استمراری را درک نکند (اگر «می» حذف شود)

  • ضمیر را از دست بدهد (اگر «م» حذف شود)

  • یا ریشه فعل را گم کند (اگر «نویس» اشتباه شکسته شود)

۸.۴. نگاشت توکن‌ها به شناسه (ID Mapping)

پس از توکن‌سازی، هر توکن به یک عدد صحیح (Token ID) تبدیل می‌شود تا توسط مدل پردازش شود. این نگاشت به‌صورت دیکشنری انجام می‌شود و مدل فقط با اعداد کار می‌کند، نه کلمات.


۹. ساختار کلی معماری مدل‌های زبانی بزرگ

مدل‌های زبانی بزرگ تقریباً همگی بر پایه معماری Transformer ساخته شده‌اند که در سال ۲۰۱۷ توسط Google معرفی شد. این معماری انقلابی، باعث شد تا مدل‌ها بدون نیاز به ساختارهای سلسله‌مراتبی مانند RNN یا LSTM، بتوانند وابستگی‌های طولانی در متن را یاد بگیرند.

۹.۱. اجزای اصلی ترنسفورمر:

  • Embedding Layer: تبدیل توکن IDها به بردارهای قابل یادگیری

  • Multi-Head Self-Attention: یادگیری رابطه بین هر توکن با دیگر توکن‌ها

  • Feed Forward Network (FFN): اعمال نگاشت‌های غیرخطی برای افزایش قدرت مدل

  • Layer Normalization و Residual Connections: برای پایدارسازی آموزش

فرض کنید مدل ما جمله‌ ی زیر رو پردازش کنه:

امروز هوا خیلی خوبه.

هدف مدل اینه که بفهمه معنای جمله چیه و مثلاً پیش‌بینی کنه که توکن بعدی چیه یا جواب سوالی بده.

بیایم ببینیم چه اتفاقی می‌افته:

مرحله ۱: Embedding Layer
  • جمله به توکن‌ها تبدیل می‌شه (مثلاً با BPE یا WordPiece):

    ["امروز", "هوا", "خیلی", "خوبه", "."]

  • هر توکن به یک عدد نگاشت می‌شه:

    [587, 905, 342, 881, 3]

  • سپس هر عدد به یک بردار واقعی (مثلاً با 768 بعد) تبدیل می‌شه.
    این بردارها قابل یادگیری هستن و می‌تونن نماینده معنای توکن باشن.

مرحله ۲: اضافه‌کردن Positional Encoding
  • چون ترنسفورمر ترتیب توکن‌ها رو نمی‌فهمه، به هر بردار، اطلاعاتی درباره موقعیت اون توکن هم اضافه می‌شه. مثلاً توکن اول، دوم، ... ششم.

  • نتیجه: بردارهایی که هم معنا و هم جایگاه رو در خودشون دارن.

مرحله ۳: Multi-Head Self-Attention
  • مدل نگاه می‌کنه هر توکن با کدوم توکن‌های دیگه باید ارتباط برقرار کنه.

  • مثلاً:

    • «خوبه» به «هوا» توجه می‌کنه تا بفهمه «چی خوبه؟»

    • «امروز» به «هوا» توجه می‌کنه برای ساخت زمینه زمانی

  • این اتفاق با محاسبه‌ی ماتریس Attention می‌افته، برای تمام جفت‌های توکن.

  • در "multi-head"، این توجه در چند فضای معنایی مختلف انجام می‌شه (مثلاً یک head برای دستور زبان، یکی برای زمان، یکی برای جنسیت و ...)

مرحله ۴: Feed Forward Network (FFN)
  • برای هر توکن، یک شبکه‌ی کوچک دو لایه‌ای (MLP) داریم که بردارش رو به شکلی غیرخطی تغییر می‌ده.

  • انگار یه پردازش محلی برای هر توکن انجام میدیم.

  • هدف: افزایش ظرفیت مدل برای درک الگوهای پیچیده.

مرحله ۵: Residual Connection و Layer Normalization
  • خروجی attention و FFN با ورودی قبلی جمع می‌شن (Residual)، بعد نرمال‌سازی می‌شن.

  • کمک می‌کنه که مدل در عمق بالا هم دچار ناپایداری نشه و گرادیان‌ها بهتر منتقل شن.

این کل بلاک (Attention + FFN + Normalization) معمولاً n بار تکرار می‌شه (مثلاً 12 یا 96 بار بسته به اندازه مدل).
در انتها:

توکن‌ها به بردارهایی تبدیل می‌شن که می‌تونن برای کارهایی مثل:

  • پیش‌بینی کلمه بعدی

  • پاسخ به سؤال

  • تولید متن

مورد استفاده قرار بگیرن.

مثال دقیق‌تر در عملکرد Attention:

مثلاً مدل بفهمه که در جمله:

«امروز هوا خیلی خوبه.»

توکن «خوبه» باید به «هوا» توجه کنه تا معنای جمله درست درک بشه. این «توجه» همون attention هست که مدل خودش یاد می‌گیره، بدون اینکه ما براش مشخص کنیم.

۹.۲. مدل‌های دیکودر-محور (Decoder-only)

مدل‌هایی مانند GPT، فقط از قسمت Decoder ترنسفورمر استفاده می‌کنند. این مدل‌ها در تولید متن بسیار مؤثرند و آموزش آن‌ها به صورت خودپیش‌بینی (causal language modeling) انجام می‌شود.

۹.۳. مدل‌های Encoder-only

مدل‌هایی مانند BERT فقط از Encoder استفاده می‌کنند. این مدل‌ها برای درک معنا و استخراج ویژگی‌ها مناسب هستند و اغلب در وظایف دسته‌بندی یا پرسش‌پاسخ به کار می‌روند.

۹.۴. مدل‌های Encoder-Decoder

مدل‌هایی مانند T5 یا BART از هر دو بخش Encoder و Decoder استفاده می‌کنند و برای وظایف تبدیل متن به متن (مانند ترجمه، خلاصه‌سازی یا اصلاح گرامر) مناسب هستند.


۱۰. پیش‌پردازش و آماده‌سازی داده‌ها قبل از آموزش

آماده‌سازی داده‌ها یکی از حساس‌ترین مراحل آموزش LLM است. این مرحله شامل پاک‌سازی، فیلتر، نرمال‌سازی و ساخت فرمت نهایی برای تغذیه به مدل است.

مراحل مهم:
  • پاک‌سازی نویز: حذف HTMLهای اضافی، تبلیغات، ایمیل‌ها و داده‌های تکراری

  • نرمال‌سازی: یکسان‌سازی فاصله‌ها، علائم نگارشی، یونیکد و حذف نویسه‌های شکسته

  • حذف داده‌های سمی یا مغرضانه: مانند نژادپرستی، توهین، اطلاعات نادرست

  • ساخت توکن‌ها و تقسیم‌بندی طولی: محدود کردن طول هر نمونه به حداکثر توکن مجاز مدل (مثلاً 2048)

  • شافل‌کردن: برای جلوگیری از هم‌وابستگی موضوعی در مینی‌بچ‌ها

فرض کنید جمله‌ی زیر یکی از ورودی‌های خام ماست:

"سلام!!! چطوری؟ من ۳ بار ایمیلت رو فرستادم ولی جواب ندادی :("

مراحل پیش‌پردازش، به ترتیب:
۱. نرمال‌سازی نویسه‌ها (Normalization)
  • «۳» به «سه» تبدیل می‌شه یا حداقل با فرمت عددی قابل فهم مدل ذخیره می‌شه.

  • نویسه‌های عربی مثل «ي» و «ك» به فارسی «ی» و «ک» تبدیل می‌شن (اگه وجود داشته باشن).

  • مثال:

    "سلام!!! چطوری؟ من سه بار ایمیلت رو فرستادم ولی جواب ندادی :("

۲. حذف نویزها (Noise Removal)
  • نشانه‌های نگارشی تکراری مثل «!!!»، فاصله‌های زیاد، یا صورتک‌ها حذف یا جایگزین می‌شن.

  • مثال:

    "سلام! چطوری؟ من سه بار ایمیلت رو فرستادم ولی جواب ندادی"

۳. تبدیل حروف به قالب یکسان (Casing)
  • برای زبان فارسی تفاوت بزرگی نداره، ولی در انگلیسی حروف بزرگ به کوچک تبدیل می‌شن.
۴. حذف stopwords (در برخی کاربردها)
  • در برخی مدل‌ها، واژه‌هایی مثل «من»، «رو»، «ولی» حذف می‌شن (اگر هدف، تحلیل معنا نباشه).
۵. Tokenization (توکن‌سازی)
  • جمله به بخش‌های کوچکتر (توکن‌ها) تقسیم می‌شه.
    مثال خروجی:

    ["سلام", "!", "چطوری", "؟", "من", "سه", "بار", "ایمیلت", "رو", "فرستادم", "ولی", "جواب", "ندادی"]

۶. نمایش عددی (Numericalization)
  • هر توکن با استفاده از واژه‌نامه (Vocabulary) به یک عدد نگاشت می‌شه:

    [342, 23, 912, 51, 10, 104, 207, 591, 11, 803, 55, 401, 721]

۷. پدینگ یا برش (Padding / Truncation)
  • اگر طول جمله کمتر از مقدار مورد نظر باشه، صفر اضافه می‌شه؛ اگر بیشتر باشه، انتهای جمله بریده می‌شه.
۸. ساخت ماسک توجه (Attention Mask)
  • مشخص می‌کنه کدوم توکن‌ها معنی‌دار هستن و کدوم فقط پدینگن. مثلاً:

    Attention mask: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

نتیجه:

بعد از این فرآیندها، جمله‌ی فارسی ما آماده تغذیه به مدل می‌شه، به‌طوری‌که مدل دقیقاً می‌فهمه چه چیزی گفته شده، کجا مکث وجود داشته، و کدوم توکن‌ها اهمیت دارن.


۱۱. نحوه تنظیم و کنترل حجم مدل (Model Size)

مدل‌های زبانی بزرگ معمولاً با اندازه‌های مختلفی عرضه می‌شوند: کوچک، متوسط، بزرگ و غول‌پیکر (مانند GPT-3 با 175 میلیارد پارامتر). تصمیم درباره اندازه مدل بستگی به منابع محاسباتی، کاربرد هدف و محدودیت‌های فنی دارد.

نکات مهم:
  • افزایش پارامتر لزوماً به معنای افزایش کیفیت نیست

  • مدل‌های بزرگ‌تر به داده، GPU و زمان آموزش بیشتری نیاز دارند

  • ممکن است مدل کوچک با آموزش بهتر از مدل بزرگ آموزش‌ندیده عملکرد بهتری داشته باشد

  • مدل‌های کوچک‌تر برای اجرا روی موبایل یا لبه مناسب‌ترند (مانند LLaMA 2 7B)


۱۲. آموزش مدل (Training) و مفاهیم پایه آن

آموزش مدل شامل تغذیه داده‌ها به شبکه عصبی و به‌روزرسانی وزن‌ها با استفاده از الگوریتم‌های بهینه‌سازی است. در LLMها، این فرایند ممکن است هفته‌ها یا ماه‌ها طول بکشد.

مراحل اصلی:
  • Loss Function: اغلب Cross-Entropy برای پیش‌بینی توکن بعدی

  • Optimization: استفاده از Adam یا AdamW

  • Gradient Clipping: برای کنترل انفجار گرادیان

  • Mixed Precision Training: برای کاهش حافظه و افزایش سرعت

  • Checkpointing: ذخیره مرحله‌ای مدل برای بازیابی یا ادامه آموزش

  • Distributed Training: استفاده از چند GPU یا حتی چندین سرور برای آموزش موازی

آموزش LLM یعنی یاد دادن به مدل که:

  • درک کنه توی متن‌ها چه الگوهایی وجود داره

  • و بتونه توکن بعدی رو با دقت بالا پیش‌بینی کنه

مثل اینه که هزاران کتاب، مکالمه، پست وبلاگی و کد رو به مدل بدیم و بگیم:

«هر بار فقط یکی از کلمه‌ها رو حذف می‌کنیم، تو حدس بزن چی بوده!»

۱۲.۱. Loss Function – «معیار خطا»

برای اینکه بفهمیم مدل چقدر خوب عمل کرده، از تابع خطا یا Loss Function استفاده می‌کنیم.

در LLMها، معمولاً از Cross-Entropy Loss استفاده می‌کنیم.
این تابع بررسی می‌کنه که آیا احتمال داده‌شده توسط مدل برای توکن درست، بالا بوده یا نه.

مثال: اگر مدل جمله «من امروز به…» رو دید و باید «مدرسه» رو پیش‌بینی کنه:

  • اگه احتمال «مدرسه» رو بده 0.8 → Loss پایین

  • اگه بده 0.01 → Loss بالا

هدف آموزش: کم کردن میانگین Loss روی تمام داده‌ها

۱۲.۲. Optimization – «بهینه‌سازی وزن‌ها»

حالا که فهمیدیم مدل چقدر اشتباه کرده (Loss)، باید کاری کنیم که دفعه بعد اشتباه کمتری کنه.

برای اینکار از الگوریتم‌های بهینه‌سازی استفاده می‌کنیم. معروف‌ترین‌ها:

  • Adam: نسخه‌ی پیشرفته SGD که با نرخ یادگیری تطبیقی کار می‌کنه.

  • AdamW: مثل Adam هست، ولی وزن‌ها رو به‌صورت خاصی regularize می‌کنه (وزن‌ها رو خیلی بزرگ نکنه).

مدل با این الگوریتم، وزن‌ها (parameters) رو قدم به قدم طوری تغییر می‌ده که Loss کمتر بشه.

۱۲.۳. Gradient Clipping – «مهار گرادیان»

گاهی در مدل‌های بزرگ، هنگام backpropagation، مقدار گرادیان خیلی بزرگ می‌شه و باعث ناپایداری آموزش می‌شه (گرادیان منفجر می‌شه).

اینجاست که Gradient Clipping وارد می‌شه:

اگر گرادیان خیلی بزرگ شد، اونو قطع یا مقیاس می‌کنیم تا نترکونه!

این کار جلوی نوسانات شدید مدل رو می‌گیره.

۱۲.۴. Mixed Precision Training – «استفاده هم‌زمان از عددهای ۱۶ و ۳۲ بیتی»

مدل‌های بزرگ حافظه زیادی می‌خوان. برای بهینه‌کردن آموزش:

  • بخشی از محاسبات با float16 (عددی با دقت کمتر ولی سریع‌تر)

  • و بخش حساس‌تر با float32 (دقیق‌تر ولی سنگین‌تر) انجام می‌شه

این کار باعث می‌شه:

  • سرعت آموزش زیاد بشه

  • حافظه GPU کمتر اشغال بشه

  • اما دقت مدل قابل‌قبول باقی بمونه

در اغلب LLMهای جدید این تکنیک به‌شدت استفاده می‌شه.

۱۲.۵. Checkpointing – «ذخیره‌سازی مرحله‌ای مدل»

آموزش LLMها ممکنه هفته‌ها یا ماه‌ها طول بکشه. اگه وسطش برق بره یا GPU خراب بشه؟

اینجاست که Checkpoint کمک می‌کنه:

مدل در بازه‌های منظم (مثلاً هر ۱۰۰۰ مرحله) ذخیره می‌شه

پس اگه مشکلی پیش اومد:

  • از آخرین Checkpoint ادامه می‌دیم

  • نیاز نیست از اول شروع کنیم

همچنین برای فاین‌تیونینگ بعدی هم از همین چک‌پوینت‌ها استفاده می‌شه.

۱۲.۶. Distributed Training – «آموزش توزیع‌شده روی چند GPU یا سرور»

مدل‌هایی مثل 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) تبدیل شود.

۱۴.۲. روش‌های مختلف توکن‌سازی

  1. Word-level tokenization
    مثال: "I love AI"["I", "love", "AI"]
    مشکل: تعداد کلمات بسیار زیاد است (vocabulary size بالا)

  2. Character-level tokenization
    مثال: "I love"["I", " ", "l", "o", "v", "e"]
    مشکل: دنباله‌ها بسیار بلند می‌شوند.

  3. Subword-level tokenization (BPE یا SentencePiece)
    مثال: "unbelievable"["un", "believ", "able"]
    مزیت: هم ترکیبی است و هم کم‌واژه‌تر.

ما در ادامه از روش BPE استفاده می‌کنیم که پایه‌ی بیشتر LLMهاست.


۱۵. ساخت دیتاست قابل آموزش (Dataset)

اکنون باید داده‌هایی که در اختیار داریم را تبدیل به فرمتی کنیم که مدل بتواند آن را بخواند.

۱۵.۱. مراحل کلی:

  1. خواندن داده متنی (مثلاً فایل txt)

  2. تبدیل آن به لیستی از توکن‌ها

  3. تقسیم آن به دنباله‌های طول ثابت (مثلاً 128 توکن)

  4. تولید ورودی (X) و خروجی هدف (y) برای آموزش مدل

مثال با یک جمله:

  • ورودی: ["من", "کتاب", "را", "می‌خوانم"]

  • هدف: ["کتاب", "را", "می‌خوانم", "<EOS>"]

یعنی مدل باید یاد بگیرد با دیدن هر توکن، توکن بعدی را حدس بزند.

فکر میکنم کم کم باید کد زدن رو شروع کنیم، پس :

مرحله 1: نمونه فایل متنی

فرض کنیم فایل data.txt شامل متن زیر است:

من برنامه‌نویسی را دوست دارم. مدل‌های زبانی بسیار جالب هستند.

مرحله 2: تعریف توکنایزر ساده

برای شروع، یک توکنایزر ابتدایی کلمه‌محور می‌سازیم (در بخش ۱۴ توکنایزر پیچیده‌تر مثل 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}
مرحله 3: تبدیل کل متن به توکن‌های عددی
token_ids = [word2idx[word] for word in tokens]
# مثال خروجی: [7, 0, 6, 4, 3, 8, 5, 1, 2, 9]
مرحله 4: ایجاد جفت‌های ورودی/خروجی با sliding window

فرض کنیم 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]
مرحله 5: تبدیل به Tensor
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])
مرحله 6: ساخت کلاس Dataset و DataLoader
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)
مرحله 7: تست کردن دیتالودر
for batch_x, batch_y in loader:
    print("X:", batch_x)
    print("Y:", batch_y)
    break
نتیجه نهایی:

الان یک دیتاست کامل آماده آموزش داریم که از متن ساده فارسی استخراج شده و به صورت batch آماده تغذیه به مدل زبانی ماست.


۱۶. ساخت مدل زبانی ساده با PyTorch

ساخت یک مدل ساده اما کاربردی که ورودی‌اش دنباله‌ای از توکن‌هاست و خروجی‌اش پیش‌بینی توکن بعدی.

معماری مدل پیشنهادی

در این مرحله، ما یک معماری پایه برای مدل زبانی می‌سازیم:

  1. Embedding Layer: نگاشت هر توکن به یک بردار ویژگی

  2. یک یا چند لایه ترنسفورمر (ساده‌شده) یا RNN (فعلاً ساده)

  3. Linear layer: خروجی به اندازه واژگان برای پیش‌بینی توکن بعدی

برای شروع از ساده‌ترین حالت استفاده می‌کنیم:

نسخه ساده مدل با Embedding + Average + Linear
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 logits
تست اولیه مدل
vocab_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 در PyTorch

هدف این بخش:

پیاده‌سازی یک مدل زبانی ساده ولی واقعی با استفاده از Transformer Blocks که ورودی آن دنباله‌ای از توکن‌هاست و خروجی‌اش احتمال توکن بعدی در هر موقعیت است.

ساختار کلی مدل ما:
Input (Token IDs)
   ↓
Embedding Layer + Positional Encoding
   ↓
N × Transformer Block (Self-Attention + FeedForward)
   ↓
Linear Layer → Vocab Size
مرحله ۱: کلاس کامل مدل Transformer
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 logits
مرحله ۲: تست خروجی مدل
model = 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)
مرحله ۳: آموزش مدل (با cross-entropy در هر پوزیشن)
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 واقعی رو داریم.


۱۸. تولید متن (Text Generation) به صورت Auto-Regressive

ایجاد مدلی که بتونه بعد از دیدن یک دنباله‌ی اولیه از کلمات، توکن‌های بعدی رو یکی‌یکی پیش‌بینی و تولید کنه؛ درست مثل LLMهای واقعی مثل GPT.

مفهوم Auto-Regressive

مدل‌های 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: من کتاب را خواندم و آن را بسیار دوست داشتم .

۱۹. پیش‌بینی توکن بعدی (Next Token Prediction)

یکی از اصلی‌ترین اهداف در آموزش مدل‌های زبانی بزرگ اینه که مدل بتونه فقط با دیدن توکن‌های قبلی، توکن بعدی رو پیش‌بینی کنه. برای مثال اگه جمله‌ای مثل «من کتاب را» رو به مدل بدی، انتظار داری مدل بگه «خواندم».

فرمول ریاضی این کار چیه؟

فرض کن یه دنباله‌ی متنی داری مثل: x = [x₁, x₂, x₃, ..., xₙ]

هدف اینه که مدل بتونه احتمالات زیر رو یاد بگیره:

P(x₁) * P(x₂ | x₁) * P(x₃ | x₁,x₂) * ... * P(xₙ | x₁,...,xₙ₋₁)

یعنی مدل در هر مرحله فقط باید با توجه به توکن‌های قبلی، توکن بعدی رو درست حدس بزنه.

ورودی و هدف در آموزش

برای آموزش مدل، ورودی‌ها و هدف‌ها باید یک توکن از هم جا‌به‌جا باشن. مثلاً:

ورودی: من کتاب را
هدف: کتاب را خواندم

مدل یاد می‌گیره برای هر جای جمله، چی باید بیاد بعدش.

تابع آماده‌سازی batch از توکن‌ها
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) تبدیل می‌کنه.

تابع آموزش برای یک epoch
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 تبدیلشون می‌کنیم به احتمال.

مشاهده احتمال‌ها بعد از 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)

به این ترتیب می‌تونیم ببینیم مدل داره برای هر موقعیت تو جمله، چی پیشنهاد میده.


مرحله ۲۰: استفاده عملی از مدل آموزش‌دیده + بهبود و ارزیابی

۲۰.۱. تولید متن (Text Generation / Inference)

بعد از اینکه مدلمون آموزش دید (چه بزرگ، چه ساده)، حالا وقتشه باهاش متن تولید کنیم. معمولاً ورودی (prompt) می‌دیم و مدل ادامه‌ش رو پیش‌بینی می‌کنه.

مثال کد: تولید متن با Sampling و Top-k و Temperature
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-Tuning روی داده‌های جدید

فرض کن یه مدل داریم که زبان فارسی بلده، حالا می‌خوایم فقط روی دیالوگ‌های فروشگاهی fine-tune کنیم:

# مراحل کلی:
# - آماده‌سازی دیتا
# - تعریف optimizer جدید با نرخ یادگیری کمتر
# - فریز کردن برخی لایه‌ها (اختیاری)
# - آموزش مدل دوباره با batch کوچک

# مثلا:
for param in model.transformer.h[:6].parameters():  # لایه‌های اولیه رو فریز کن
    param.requires_grad = False
۲۰.۳. ارزیابی عملکرد مدل

چند روش برای بررسی کیفیت:

A. Perplexity:
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^loss
B. BLEU / ROUGE
pip install evaluate
from evaluate import load

bleu = load("bleu")
results = bleu.compute(predictions=["سلام چطوری؟"], references=[["سلام، حالت چطوره؟"]])
print("BLEU:", results["bleu"])

مرحله ۲۱: مقایسه، کاربرد واقعی، جمع‌بندی و مسیر آینده

۲۱.۱. مقایسه با مدل‌های معروف
ویژگی مدل شما GPT-2 Bloom
تعداد پارامتر ~10M 124M–1.5B تا 176B
زبان فارسی انگلیسی چندزبانه
سرعت آموزش سریع کندتر خیلی کند
نیاز به GPU کم متوسط بالا
۲۱.۲. استفاده واقعی از مدل (API ساده)
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 سفارشی با کمترین منابع ممکن

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks