تغییرات اخیر

در اینجا اطلاعیه‌ها، نسخه‌ها و تغییرات جدید لیارا فهرست می‌شوند.

ترنسفورمر چیست؟ راهنمای جامع برای یادگیری سریع

خلاصه کنید:

openaigeminiperplexity

ترنسفورمر یک نوع معماری شبکه عصبی است که برای پردازش داده‌های ترتیبی طراحی شده، اما برخلاف مدل‌های سنتی مانند RNN و LSTM، نیازی به پردازش داده‌ها به ترتیب زمانی ندارد و می‌تواند بخش‌های مختلف داده را به‌طور همزمان تحلیل کند. این ویژگی باعث می‌شود ترنسفورمرها سریع‌تر آموزش ببینند و وابستگی‌های طولانی مدت در داده‌ها را بهتر یاد بگیرند. ترنسفورمرها برای اولین بار توسط Vaswani و همکاران در مقاله‌ی مشهور «Attention Is All You Need» معرفی شدند و سریعا تحولی بزرگ در حوزه‌ی پردازش زبان طبیعی (NLP) ایجاد کردند.

در ابتدا، ترنسفورمرها برای پردازش زبان طبیعی توسعه یافتند و جایگزین سیستم‌های سنتی مبتنی بر RNN و LSTM شدند. آن‌ها با استفاده از مکانیزم توجه (Attention) توانستند روابط بلندمدت بین کلمات را به شکل موثتری مدل کنند و با قابلیت آموزش موازی، سرعت و دقت مدل‌ها را به شکل چشم‌گیری افزایش دهند.

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

آنچه در این مقاله می‌خوانید:

  • نکات کلیدی ترنسفورمرها
  • پیش نیازها
  • ترنسفورمر چیست؟
  • معماری ترنسفورمر چگونه است؟
  • چرا ازGPU برای آموزش ترنسفورمرها استفاده کنیم؟
  • نحوه آموزش یک مدل ترنسفورمر
  • جمع بندی
  • سوالات متداول
ترنسفورمر چیست

نکات کلیدی ترنسفورمرها

در این بخش مهم‌ترین نکاتی که در رابطه با ترنسفورمرها در جریان بوده را توضیح خواهیم داد.

  • ترنسفورمرها که برای اولین بار در سال ۲۰۱۷ توسط Vaswani و همکاران معرفی شدند، لایه‌های بازگشتی (RNN) و کانولوشنی (CNN) را با یک مکانیزم کاملا مبتنی بر Attention جایگزین کردند.
  • به لطف مکانیزم Self-Attention ترنسفورمرها انقلابی در هوش مصنوعی به‌خصوص در حوزه‌های پردازش زبان طبیعی (NLP) و بینایی کامپیوتری ایجاد کرده‌اند.
  • اطلاعات ترتیب توالی (Sequence Order) به مدل تزریق می‌شود، بنابراین ترنسفورمرها می‌توانند داده‌ها را بدون نیاز به بازگشت زمانی پردازش کنند.
  • چندین لایه Attention به‌صورت موازی اجرا می‌شوند که امکان یادگیری روابط متنوع بین توکن‌ها را فراهم می‌کند.
  • Ecoder ورودی‌ها را پردازش می‌کند و Decoder خروجی را تولید می‌کند، در حالی که لایه‌های Attention ارتباط بین این دو را برقرار می‌کنند.
  • ترنسفورمرها وابستگی‌های ترتیبی RNNها را حذف کرده‌اند، بنابراین مدل با GPU بسیار سریع‌تر و کارآمدتر انجام می‌شود.
  • استفاده از شتاب‌دهنده‌های GPU برای ترنسفورمرها ضروری است، زیرا زمان محاسبات را به شکل قابل توجهی کاهش می‌دهد.
  • تنظیم اندازه Batch به بهینه‌سازی استفاده از حافظه و سرعت همگرایی مدل کمک می‌کند.

پیش نیازها

قبل از شروع این آموزش، مطمئن شوید که:

  • دانش پایه‌ای از برنامه‌نویسی پایتون دارید.
  • با مفاهیم یادگیری عمیق مانند شبکه‌های عصبی، مکانیزم توجه (Attention) و Embeddings آشنا هستید.
  • دسترسی به GPU برای آموزش سریع‌تر مدل‌ها را دارید.

ترنسفورمر چیست؟

قبل از اینکه وارد معماری ترنسفورمرها شویم، ابتدا بیایید با Word Embeddings آشنا شویم و دلیل اهمیت آن‌ها را درک کنیم.

Word Embeddings هر توکن (کلمه یا زیرکلمه) را به یک بردار عددی با طول ثابت تبدیل می‌کند تا مدل بتواند آن‌ها را پردازش کند. چرا؟ زیرا شبکه‌های عصبی با اعداد کار می‌کنند ،نه متن خام. این بردارها طوری طراحی می‌شوند که کلمات مشابه در فضای برداری به یکدیگر نزدیک باشند، مثلا بردار کلمات «king» و «queen» به هم نزدیک است.

به زبان ساده، Word Embeddings روشی است برای نمایش کلمات به صورت بردارهای چگال در یک فضای چندبعدی (مثلاً ۳۰۰ بعد، ۵۱۲ بعد یا ۱۰۲۴ بعد) که در آن:

  • کلمات مشابه در کنار هم قرار دارند.
  • کلمات نامشابه از هم دور هستند.
  • هندسه فضای برداری روابط معنایی و دستوری بین کلمات را بازتاب می‌دهد.

نمونه‌ای از Word Embeddings

مثال زیر را در نظر بگیرید:

  • “king” → [0.25, -0.88, 0.13, …]
  • “queen” → [0.24, -0.80, 0.11, …]
  • “apple” → [-0.72, 0.13, 0.55, …]

در این حالت، بردارهای «king» و «queen» به یکدیگر نزدیک‌تر هستند تا فاصله هر کدام با «apple». این یعنی کلمات مشابه از نظر معنایی، در فضای برداری هم به هم نزدیک می‌شوند.

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

در معماری ترنسفورمر، مرحله‌ی Embeddings اولین قدم اصلی است و پایه‌ای‌ترین بخش فرآیند یادگیری را تشکیل می‌دهد.

از Word Embedding به Contextual Embedding

بیایید کلمه‌ی bank” را مثال بزنیم. اگر از static embedding استفاده کنیم، این کلمه همیشه همان بردار ثابت را دارد، چه منظور از آن ساحل رودخانه باشد و چه بانک مالی. بنابراین، در چنین مواردی، بردار ثابت کارایی ندارد.

اینجاست که Contextual Embedding (بردارهای پویا) وارد مدل ترنسفورمر می‌شوند. این بردارها بسته به کلمات اطراف تغییر می‌کنند:

  • "I sat by the bank of the river" → بردار به سمت معنی طبیعی یا محیطی تغییر می‌کند.
  • "I deposited money at the bank" → بردار به سمت معنی مالی تغییر می‌کند.

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

رمزگذاری موقعیت (Positional Encoding)، چگونه نحوه ترتیب کلمات را می‌فهمد

ترنسفورمرها توکن‌ها را به‌صورت موازی پردازش می‌کنند، بنابراین برخلاف RNNها که پردازش ترتیبی دارند، باید اطلاعات موقعیت هر توکن به مدل داده شود. برای این کار، یک بردار خاص هر موقعیت به Word Embedding اضافه می‌شود تا مدل بفهمد هر توکن در کجای جمله قرار دارد. با این روش، مدل می‌تواند تشخیص دهد: «این سومین توکن است» در مقابل «هفتمین توکن»، که روی معنی جمله تأثیر می‌گذارد (مثلاً جایگاه فاعل و فعل).

آموزش موازی‌ سازی داده‌ها (Data Parallelism) در یادگیری عمیق را در مقاله زیر بخوانید.
موازی‌ سازی داده‌ها در یادگیری عمیق

معماری ترنسفورمر چگونه است؟

در شکل پائین، دو بخش اصلی وجود دارد: یک Encoder Stack (چپ) و یک Decoder Stack (راست). Encoder توکن‌های ورودی را به بردارهای زمینه‌ای غنی تبدیل می‌کند و Decoder Stack توکن‌های خروجی را به‌صورت خودکار تولید می‌کند. هر کدام از این بخش‌ها با تکرار همان لایه به تعداد N بار (مثلاً ۶، ۱۲، ۲۴…) ساخته می‌شوند تا مدل عمیق‌تر شود.

input tokens → input embedding + positional encoding → encoder layers (self-attention + FFN) → encoder outputs decoder receives shifted output embeddings + pos encoding → masked self-attention → encoder–decoder attention → FFN → linear → softmax → token probabilities.
معماری ترنسفورمر چگونه است؟

بردار ورودی (Input Embedding) و رمزگذاری جایگاه کلمات در ترنسفورمر

در یک ترنسفورمر، Input Embedding روشی است که کلمات یا توکن‌های متن شما را به اعداد تبدیل می‌کند تا مدل بتواند آن‌ها را درک کند. می‌توان آن را مانند ترجمه‌ی زبان به یک کد مخفی دانست که مدل آن را می‌فهمد.

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

دو روش رایج:

  • سینوسی (ثابت)
  • آموزش‌دیده (Trainable): Embeddingهای موقعیتی که قابل آموزش هستند.

مجموع Token Embedding + Positional Encoding به عنوان ورودی اولین لایه Encoder استفاده می‌شود.

لایه رمزگذار یا Encoder (بلوک تکرارشونده در ترنسفورمر)

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

  • بخش اول: مکانیزم توجه چندسری (Multi-Head Self-Attention)
    در این قسمت، هر توکن (کلمه یا بخش از متن) می‌تواند به تمام توکن‌های دیگر نگاه کند و تشخیص دهد کدام‌یک برای درک معنای خودش مهم‌تر هستند. به زبان ساده، این مکانیزم کمک می‌کند مدل بفهمد چه ارتباطی بین بخش‌های مختلف جمله وجود دارد — مثلاً «چه کسی چه کاری انجام داد و برای چه کسی». این ویژگی باعث می‌شود مدل، معنای کلی جمله را به‌صورت عمیق و وابسته به بافت (context) درک کند.
  • بخش دوم: شبکه پیش‌خور موضعی (Position-wise Feed-Forward Network)
    در این مرحله، هر توکن به‌صورت جداگانه پردازش می‌شود تا ویژگی‌هایی که در مرحله توجه یاد گرفته، تقویت و به شکل بهتری بازنمایی شود. این بخش در واقع مثل یک لایه‌ی پالایش (refinement) عمل می‌کند که خروجی دقیق‌تری برای هر توکن تولید می‌کند.

در اطراف هر دو بخش دو مولفه مهم وجود دارد:

  • اتصال باقیمانده (Residual Connection) که با اضافه‌کردن ورودی اولیه به خروجی نهایی، از گم‌شدن اطلاعات جلوگیری می‌کند.
  • نرمال‌سازی لایه‌ای (Layer Normalization) که باعث پایداری و سرعت بیشتر در فرایند آموزش می‌شود.

با چیدن چندین لایه رمزگذار مشابه روی هم، ترنسفورمر می‌تواند بازنمایی‌هایی بسیار غنی، دقیق و وابسته به بافت از متن بسازد.

هر لایه رمزگشا (Decoder Layer) در واقع یک بلوک هوشمند است که چندین بار روی هم تکرار می‌شود و مرحله‌به‌مرحله متن را می‌سازد. این لایه سه بخش کلیدی دارد که کنار هم، قدرت خارق‌العاده‌ی مدل‌های زبانی مثل GPT را شکل می‌دهند.

لایه رمزگشا یا Decoder

اگر رمزگذار مغز تحلیلی ترنسفورمر باشد، رمزگشا (Decoder) نقش زبان و بیان آن را دارد؛ یعنی همان بخشی که مدل از دل درک ورودی، خروجی تولید می‌کند.

هر لایه رمزگشا در واقع یک بلوک هوشمند است که چندین بار روی هم تکرار می‌شود و مرحله‌به‌مرحله متن را می‌سازد. این لایه سه بخش کلیدی دارد که کنار هم، قدرت خارق‌العاده‌ی مدل‌های زبانی مثل GPT را شکل می‌دهند.

۱. توجه مرحله‌ای و هدفمند

در این بخش وقتی مدل می‌خواهد یک کلمه تولید کند، فقط می‌تواند به کلمات قبلی و خودش نگاه کند و خبری از آینده ندارد. این محدودیت با یک ماسک نگاه به جلو (Look-Ahead Mask) اعمال می‌شود تا متن به‌صورت گام‌به‌گام و منطقی ساخته شود. تصور کنید در حال نوشتن یک داستان هستید و نمی‌دانید جمله بعدی چیست! دقیقا همین کار را مدل انجام می‌دهد تا خروجی طبیعی تولید شود.

۲. توجه هدفمند بین ورودی و خروجی

وقتی مدل می‌خواهد یک کلمه جدید تولید کند، می‌پرسد:

«برای نوشتن این کلمه، کدام بخش از جمله ورودی واقعاً مهم است؟»

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

به بیان ساده‌تر: این بخش کمک می‌کند وقتی مدل مثلا در حال ترجمه است، بداند کلمه‌ای که قرار است بنویسد مربوط به کدام بخش از جمله ورودی است. اینجا همان نقطه‌ای است که ارتباط بین ورودی و خروجی شکل می‌گیرد، مغز مدل، تمرکز می‌کند، توجه می‌کند و تصمیم می‌گیرد.

۳. شبکه پیش‌خور موضعی

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

لایه رمزگشا

نرمال سازی لایه‌ای و پایداری در ترنسفورمر

در معماری ترنسفورمر، دو تکنیک مهم به‌صورت هماهنگ کار می‌کنند تا فرآیند آموزش مدل پایدار، سریع و قابل اعتماد بماند:

اتصال باقیمانده (Residual Connection) و نرمال‌سازی لایه‌ای (Layer Normalization).

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

بعد از این مرحله، LayerNorm وارد عمل می‌شود تا خروجی را تنظیم کند و مقادیر را در محدوده‌ای پایدار نگه دارد. این کار باعث می‌شود یادگیری مدل نرم‌تر، سریع‌تر و قابل کنترل‌تر انجام شود. در نسخه اولیه ترنسفورمر، ترتیب کار به‌صورت Add → LayerNorm بود (که به آن post-norm می‌گویند)، یعنی نرمال‌سازی بعد از اضافه شدن ورودی انجام می‌شد.

اما در مدل‌های جدیدتر، معمولاً از pre-norm استفاده می‌شود، جایی که ابتدا نرمال‌سازی انجام می‌شود و سپس داده وارد زیر‌لایه و جمع می‌شود. این روش باعث می‌شود مدل‌های خیلی عمیق پایدارتر آموزش ببینند و از نوسانات شدید در گرادیان جلوگیری شود.

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

خروجی نهایی و تابع خطا

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

در هنگام آموزش، معمولاً از روش Teacher Forcing استفاده می‌شود. یعنی به جای اینکه مدل از خروجی پیش‌بینی‌شده‌ی خودش در گام قبل استفاده کند، توکن واقعی مرحله‌ی قبل را به آن می‌دهیم. سپس با استفاده از تابع خطای Cross-Entropy، اختلاف بین توزیع پیش‌بینی‌شده و توکن واقعی بعدی محاسبه می‌شود تا مدل یاد بگیرد چطور پیش‌بینی بهتری انجام بدهد.

RAG یا Fine-tuning؟ انتخاب مناسب برای مدل‌ های هوش مصنوعی
RAG یا Fine-tuning

چرا ازGPU برای آموزش ترنسفورمرها استفاده کنیم؟

آموزش مدل‌های ترنسفورمر، چه برای پردازش زبان طبیعی (NLP)، چه برای بینایی کامپیوتر (Computer Vision) یا مدل‌های چندوجهی (Multimodal) به توان پردازشی بسیار زیادی نیاز دارد.

ترنسفورمرها از ساختارهایی مثل توجه چندسری (Multi-Head Attention)، ضرب ماتریسی‌های عظیم و لایه‌های عمیق شبکه عصبی استفاده می‌کنند که باید میلیون‌ها تا میلیاردها پارامتر را به‌صورت هم‌زمان پردازش کنند. اگر بخواهید این کار را روی CPU انجام دهید، ممکن است روزها یا حتی هفته‌ها طول بکشد! این یعنی فرایند توسعه و آزمایش مدل‌ها به‌شدت کند و فرسایشی می‌شود.

اما اینجاست که GPUها وارد می‌شوند. کارت‌های گرافیک یا واحدهای پردازش گرافیکی (Graphics Processing Units) برای محاسبات موازی با توان بالا طراحی شده‌اند. برخلاف CPU که بیشتر برای پردازش ترتیبی ساخته شده، GPU هزاران هسته‌ی کوچک دارد که می‌توانند ده‌ها یا صدها عملیات را به‌صورت هم‌زمان انجام دهند.

نتیجه آن، مدلی که شاید روی CPU چند هفته طول بکشد، روی GPU در چند ساعت یا چند روز آموزش می‌بیند! این یعنی آزمایش سریع‌تر، توسعه چابک‌تر و امکان آموزش مدل‌های بزرگ‌تر بدون اتلاف زمان. تا چند سال پیش، دسترسی به GPU برای پروژه‌های یادگیری عمیق کار ساده‌ای نبود، اما حالا پلتفرم‌هایی مثل GPU Droplets این محدودیت را از میان برداشته‌اند. با این سرویس‌ها می‌توانید در عرض چند دقیقه یک محیط قدرتمند برای آموزش مدل‌های هوش مصنوعی و یادگیری ماشین (AI/ML) راه‌اندازی کنید و فقط به اندازه‌ی مصرف خودتان هزینه بپردازید، بدون نیاز به دردسرهای مربوط به زیرساخت سخت‌افزاری.

نحوه آموزش یک مدل ترنسفورمر

بیایید با هم یک مدل سبک ترنسفورمر (Transformer) برای طبقه‌بندی متون بسازیم. به‌عنوان نمونه از دیتاست معروف Kaggle – “Disaster Tweets” استفاده می‌کنیم. این مجموعه‌داده با عنوان “Real or Not? NLP with Disaster Tweets” به‌راحتی در وب‌سایت Kaggle قابل پیدا کردن است.

در این دیتاست، یک ستون متنی (tweet text) و یک برچسب دودویی (۰ یا ۱) وجود دارد که مشخص می‌کند آیا توییت مربوط به یک حادثه واقعی است یا نه. البته اگر دیتاست مشابهی دارید، می‌توانید همان را جایگزین کنید، فقط کافی است ساختار فایل CSV مشابه باشد.

قبل از اجرای کد چه باید کرد؟

۱. فایل train.csv را از صفحه‌ی دیتاست در Kaggle دانلود کنید.
۲. آن را در پوشه‌ی کاری (Working Directory) پروژه‌تان قرار دهید.
۳. سپس اسکریپت را اجرا کنید (یا در Jupyter Notebook کپی و اجرا کنید).

این اسکریپت به‌صورت خودکار داده‌ها را به دو بخش Train و Validation تقسیم می‌کند، پس نیازی نیست خودتان دستی این کار را انجام دهید.

نکته: در این پیاده‌سازی از کتابخانه‌ی re (Regular Expressions) پایتون برای توکن‌سازی (Tokenization) استفاده می‌کنیم تا فرآیند ساده و سبک باقی بماند. همچنین این پروژه فقط به حداقل وابستگی‌ها (Minimal Dependencies) نیاز دارد، بنابراین خیلی سریع و بدون نصب کتابخانه‌های سنگین اجرا می‌شود.

کمترین پیش‌نیازها

pip install torch pandas scikit-learn numpy

کد آموزش ترنسفورمر

import re
import math
import random
import time
import os
import numpy as np
import pandas as pd
from collections import Counter
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader

# ====== Config ======
DATA_PATH = "./train.csv"  # Kaggle "Real or Not? NLP with Disaster Tweets"
MAX_LEN = 50
MIN_FREQ = 2
BATCH_SIZE = 64
EMBED_DIM = 128
FF_DIM = 256
N_HEADS = 4
N_LAYERS = 2
DROPOUT = 0.1
LR = 3e-4
EPOCHS = 5
SEED = 42
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)

# ====== Load Dataset ======
df = pd.read_csv(DATA_PATH)[["text", "target"]].dropna()
train_df, val_df = train_test_split(df, test_size=0.15, stratify=df["target"], random_state=SEED)

# ====== Tokenizer & Vocab ======
def simple_tokenizer(text):
    text = text.lower()
    return re.findall(r"\b\w+\b", text)

counter = Counter()
for text in train_df["text"]:
    counter.update(simple_tokenizer(text))

# Special tokens
PAD_TOKEN = "<pad>"
UNK_TOKEN = "<unk>"
BOS_TOKEN = "<bos>"
EOS_TOKEN = "<eos>"

itos = [PAD_TOKEN, UNK_TOKEN, BOS_TOKEN, EOS_TOKEN] + [w for w, c in counter.items() if c >= MIN_FREQ]
stoi = {tok: i for i, tok in enumerate(itos)}

PAD_IDX = stoi[PAD_TOKEN]
BOS_IDX = stoi[BOS_TOKEN]
EOS_IDX = stoi[EOS_TOKEN]

def text_to_ids(text):
    tokens = [BOS_TOKEN] + simple_tokenizer(text)[:MAX_LEN-2] + [EOS_TOKEN]
    ids = [stoi.get(tok, stoi[UNK_TOKEN]) for tok in tokens]
    ids = ids + [PAD_IDX] * (MAX_LEN - len(ids)) if len(ids) < MAX_LEN else ids[:MAX_LEN]
    return ids

# ====== Dataset Class ======
class TextDataset(Dataset):
    def __init__(self, df):
        self.texts = df["text"].tolist()
        self.labels = df["target"].astype(int).tolist()
    def __len__(self):
        return len(self.texts)
    def __getitem__(self, idx):
        ids = torch.tensor(text_to_ids(self.texts[idx]), dtype=torch.long)
        label = torch.tensor(self.labels[idx], dtype=torch.long)
        return ids, label

train_ds = TextDataset(train_df)
val_ds = TextDataset(val_df)
train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)
val_loader = DataLoader(val_ds, batch_size=BATCH_SIZE)

# ====== Positional Encoding ======
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, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        self.register_buffer("pe", pe.unsqueeze(0))
    def forward(self, x):
        return x + self.pe[:, :x.size(1), :]

# ====== Model ======
class TransformerClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim, num_heads, ff_dim, num_layers, num_classes, pad_idx, dropout=0.1):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=pad_idx)
        self.pos_encoding = PositionalEncoding(embed_dim)
        encoder_layer = nn.TransformerEncoderLayer(d_model=embed_dim, nhead=num_heads, dim_feedforward=ff_dim, dropout=dropout, batch_first=True)
        self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
        self.fc = nn.Linear(embed_dim, num_classes)
    def forward(self, ids):
        mask = (ids == PAD_IDX)
        x = self.embedding(ids)
        x = self.pos_encoding(x)
        x = self.encoder(x, src_key_padding_mask=mask)
        x = x[:, 0, :]  # take BOS token
        return self.fc(x)

model = TransformerClassifier(len(itos), EMBED_DIM, N_HEADS, FF_DIM, N_LAYERS, 2, PAD_IDX, DROPOUT).to(DEVICE)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=LR)

# ====== Training Loop ======
for epoch in range(1, EPOCHS+1):
    model.train()
    train_loss = 0
    for ids, labels in train_loader:
        ids, labels = ids.to(DEVICE), labels.to(DEVICE)
        optimizer.zero_grad()
        output = model(ids)
        loss = criterion(output, labels)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()
    
    # Validation
    model.eval()
    val_loss, preds_all, labels_all = 0, [], []
    with torch.no_grad():
        for ids, labels in val_loader:
            ids, labels = ids.to(DEVICE), labels.to(DEVICE)
            output = model(ids)
            loss = criterion(output, labels)
            val_loss += loss.item()
            preds_all.extend(torch.argmax(output, dim=1).cpu().numpy())
            labels_all.extend(labels.cpu().numpy())
    
    acc = accuracy_score(labels_all, preds_all)
    f1 = f1_score(labels_all, preds_all, average="macro")
    print(f"Epoch {epoch}: Train Loss={train_loss/len(train_loader):.4f}, Val Loss={val_loss/len(val_loader):.4f}, Acc={acc:.4f}, F1={f1:.4f}")

print("Training complete.")

# ====== Predict for a few random samples ======
model.eval()
sample_indices = random.sample(range(len(val_df)), 5)
for idx in sample_indices:
    text = val_df.iloc[idx]["text"]
    true_label = val_df.iloc[idx]["target"]
    ids = torch.tensor(text_to_ids(text), dtype=torch.long).unsqueeze(0).to(DEVICE)
    with torch.no_grad():
        pred = torch.argmax(model(ids), dim=1).item()
    print(f"Text: {text[:80]}...")  # print first 80 chars
    print(f"True: {true_label}, Pred: {pred}")
    print("-" * 50)

# ====== Save all validation predictions ======
all_preds = []
model.eval()
with torch.no_grad():
    for text in val_df["text"]:
        ids = torch.tensor(text_to_ids(text), dtype=torch.long).unsqueeze(0).to(DEVICE)
        pred = torch.argmax(model(ids), dim=1).item()
        all_preds.append(pred)

val_df_with_preds = val_df.copy()
val_df_with_preds["predicted"] = all_preds
val_df_with_preds.to_csv("validation_predictions.csv", index=False)
print("Saved validation predictions to validation_predictions.csv")

نکات کلیدی برای نوشتن کد آموزش ترنسفورمر

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

۱. انتخاب دیتاست (Dataset Choice)

اگر تازه شروع کرده‌اید، بهتر است از دیتاست‌های سبک و قابل دانلود آسان استفاده کنید، مثل نمونه‌های موجود در Kaggle یا Hugging Face Datasets. این کار کمک می‌کند از مشکلات ناشی از تداخل وابستگی‌ها (dependency conflicts) جلوگیری شود.

۲. توکن‌سازی (Tokenization)

برای تبدیل متن به توکن‌های عددی مناسب مدل، از یک Tokenizer استفاده کنید. کتابخانه‌ی transformers نمونه‌های آماده‌ای ارائه می‌دهد که این کار را بسیار راحت می‌کند.

۳. انتخاب مدل (Model Selection)

برای شروع، می‌توانید از مدل‌های ترنسفورمر کوچک و پیش‌آموزش‌دیده مثل distilbert یا distilbert-base-uncased استفاده کنید. این انتخاب باعث سرعت بالاتر آموزش و کاهش نیاز به منابع سخت‌افزاری می‌شود.

۴. آماده‌سازی DataLoader

با استفاده از DataLoader داده‌ها را به‌صورت بچ و تصادفی (batch & shuffle) برای آموزش و ارزیابی آماده کنید. این کار باعث افزایش کارایی و سرعت آموزش می‌شود.

۵. حلقه‌ی آموزش (Training Loop)

در هر مرحله‌ی آموزش، این موارد را انجام دهید:

  • Forward Pass: عبور داده‌ها از مدل
  • محاسبه Loss: مثل Cross-Entropy
  • Backward Pass: محاسبه گرادیان‌ها
  • Optimizer Step: به‌روزرسانی پارامترها

۶. استفاده از GPU (GPU Utilization)

برای سرعت بالاتر آموزش، مدل و داده‌ها را به GPU منتقل کنید (.to(device)). این کار باعث کاهش قابل توجه زمان آموزش می‌شود.

۷. توقف زودهنگام (Early Stopping)

برای جلوگیری از overfitting، مکانیزمی مثل Early Stopping را پیاده کنید تا وقتی Loss روی داده‌های اعتبارسنجی بهبود نمی‌یابد، آموزش متوقف شود.

۸. لاگ‌گیری و ردیابی (Logging)

برای مشاهده‌ی پیشرفت آموزش و تحلیل نتایج از ابزارهایی مانند:

  • tqdm برای نوار پیشرفت (Progress Bar)
  • wandb یا TensorBoard برای ردیابی دقیق تجربیات و مقایسه مدل‌ها
  • استفاده کنید.

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

کسب و کار خود را با دسترسی به API هوش مصنوعی ارتقاء دهید. 
✅ ارائه توکن رایگان ✅سازگاری با OpenAI SDK ✅ دسترسی به ۲۰ مدل زبانی بزرگ
خرید سرویس هوش مصنوعی

جمع بندی

در این راهنما از لیارا، با مفاهیم اصلی مدل ترنسفورمر آشنا شدیم، ساختار مدل را بررسی کردیم و برخی از مفاهیم کلیدی معماری را فهمیدیم. همچنین مراحل آموزش مدل از صفر را مرور کردیم، از پیش‌پردازش دیتاست گرفته تا تعریف معماری مدل و انجام پیش‌بینی (Inference) با مدل. هرچند برای آموزش و نمایش مفاهیم، از یک دیتاست نسبتاً کوچک و قابل مدیریت استفاده کردیم، اما همین اصول را می‌توان در کارهای بزرگ‌مقیاس و پروژه‌های صنعتی نیز اعمال کرد. در محیط‌های تولید (Production)، توانایی مقیاس‌دهی آموزش به شکل بهینه اهمیت زیادی پیدا می‌کند و اینجاست که راهکارهای ابری با GPU می‌توانند به طور چشمگیری سرعت و بهره‌وری فرایند آموزش را افزایش دهند.

سوالات متداول

ترنسفورمرها چیستند و چرا اینقدر محبوب هستند؟

ترنسفورمرها معماری‌های یادگیری عمیق هستند که از مکانیزم خودتوجهی (Self-Attention) برای پردازش داده‌ها استفاده می‌کنند. برخلاف RNNها که داده‌ها را به‌صورت ترتیبی پردازش می‌کنند، ترنسفورمرها می‌توانند تمام ورودی را به‌صورت موازی پردازش کنند. این ویژگی باعث می‌شود که ترنسفورمرها بسیار کارآمد و مقیاس‌پذیر باشند و در زمینه‌هایی مثل ترجمه ماشینی، خلاصه‌سازی متن و طبقه‌بندی تصاویر پیشرفت‌های چشمگیری ایجاد کنند.

چرا باید ترنسفورمرها را روی GPU آموزش دهیم؟

ترنسفورمرها شامل ضرب ماتریسی‌های بزرگ و محاسبات توجه (Attention) هستند که بسیار سنگین هستند.
GPUها برای پردازش موازی طراحی شده‌اند و می‌توانند سرعت آموزش را به شکل چشمگیری افزایش دهند.
بدون GPU، آموزش ممکن است روزها یا هفته‌ها طول بکشد؛ اما با GPU، همان کار می‌تواند در چند ساعت انجام شود.

آموزش با دقت ترکیبی (Mixed Precision Training) چیست و چه کمکی می‌کند؟

در این روش، از ترکیب اعداد 16 بیتی و 32 بیتی در آموزش استفاده می‌شود. مزیت آن این است که حافظه کمتری مصرف می‌شود و سرعت محاسبات افزایش می‌یابد، بدون اینکه دقت مدل به شکل قابل توجهی کاهش یابد. به‌ویژه روی GPUهایی که دارای Tensor Cores و بهینه‌سازی برای FP16 هستند، این روش بسیار مؤثر است.

چگونه سایز بچ (Batch Size) مناسب برای آموزش ترنسفورمر را انتخاب کنم؟

سایز بچ بر سرعت آموزش، همگرایی مدل و مصرف حافظه تأثیر می‌گذارد:

  • بچ کوچک: مصرف حافظه کمتر، اما همگرایی کمی ناپایدار و پرنوسان.
  • بچ بزرگ: همگرایی پایدارتر، اما نیازمند حافظه بیشتر.

معمولاً بهترین روش، آزمایش با چند اندازه مختلف بر اساس سخت‌افزار شماست تا تعادلی مناسب پیدا شود.

آیا می‌توانم ترنسفورمر را از صفر آموزش دهم یا بهتر است از مدل‌های پیش‌آموزش‌دیده استفاده کنم؟

آموزش از صفر حجم زیادی از منابع و داده‌های بزرگ می‌طلبد. اکثر متخصصان، از مدل‌های پیش‌آموزش‌دیده (Pre-trained Transformers) استفاده کرده و آن‌ها را روی دیتاست خود فاین‌تیون (Fine-tune) می‌کنند. کتابخانه‌هایی مثل Hugging Face Transformers این فرایند را بسیار ساده و قابل دسترس کرده‌اند.

به اشتراک بگذارید