আজকের বৃহৎ ভাষা মডেল (LLM) গুলো মানুষের মতো করে বাক্য তৈরি করতে পারে, কোড লিখতে পারে, এমনকি জটিল প্রশ্নের উত্তরও দিতে পারে—এই সবই সম্ভব হয়েছে একটি যুগান্তকারী স্থাপত্যের কারণে: ট্রান্সফরমার। আগের রিকারেন্ট নিউরাল নেটওয়ার্ক (RNN) বা LSTM-ভিত্তিক মডেলগুলো ক্রমান্বয়ে শব্দ প্রক্রিয়া করায় দীর্ঘ নির্ভরতা ও ধীর প্রশিক্ষণের সমস্যা ছিল। ট্রান্সফরমার আর্কিটেকচার সেই সীমাবদ্ধতা ভেঙে সমান্তরাল গণনা ও স্ব-মনোযোগ (self-attention) ব্যবস্থার মাধ্যমে বাক্যের প্রতিটি শব্দের সাথে অন্য সব শব্দের সম্পর্ক একসাথে বিশ্লেষণ করে। এই টিউটোরিয়ালে আমরা ট্রান্সফরমারের মূল উপাদান, স্ব-মনোযোগের কার্যপ্রণালী, এবং একটি LLM-এর জন্য ডিকোডার-অনলি ট্রান্সফরমার কীভাবে কাজ করে তা সরল বাংলায় বুঝব।
ট্রান্সফরমার আর্কিটেকচার কী?
ট্রান্সফরমার হলো একটি নিউরাল নেটওয়ার্ক ডিজাইন যা ২০১৭ সালে "Attention Is All You Need" পেপারে প্রথম প্রকাশিত হয়। এটি কোনো রিকারেন্ট বা কনভলিউশনাল স্তর ছাড়াই খাঁটি অ্যাটেনশন মেকানিজমের উপর দাঁড়িয়ে আছে, যার ফলে পুরো সিকোয়েন্স একত্রে প্রক্রিয়াকরণ সম্ভব। সহজভাবে বললে, ট্রান্সফরমার একটি বাক্যের সব টোকেনকে সমান্তরালে বিশ্লেষণ করে এবং প্রতিটি টোকেনের প্রাসঙ্গিক অর্থ বুঝতে বাকি টোকেনগুলোর সাথে তুলনা করে।
একটি সম্পূর্ণ ট্রান্সফরমারে এনকোডার ও ডিকোডার নামে দুটি অংশ থাকলেও, বেশিরভাগ আধুনিক LLM (যেমন GPT সিরিজ) শুধুমাত্র ডিকোডার অংশ ব্যবহার করে। ডিকোডার বারবার একই ব্লক স্ট্যাক করে গভীর নেটওয়ার্ক তৈরি করে, যেখানে প্রতিটি ব্লকের ভেতরে থাকে মাল্টি-হেড সেলফ-অ্যাটেনশন ও ফিডফরোয়ার্ড নেটওয়ার্ক—যাদের চারপাশে রেসিডুয়াল কানেকশন ও লেয়ার নর্মালাইজেশন যোগ করা হয়।
স্ব-মনোযোগ (Self-Attention): ট্রান্সফরমারের প্রাণশক্তি
স্ব-মনোযোগ মেকানিজম প্রতিটি টোকেনের জন্য ডায়নামিকভাবে অন্য সব টোকেনের গুরুত্ব নির্ধারণ করে। ধরা যাক বাক্যটি "বিড়ালটি মাছ খায় কারণ সে ক্ষুধার্ত"। 'সে' শব্দটি বোঝাতে 'বিড়ালটি'র সাথে সম্পর্ক দরকার—স্ব-মনোযোগ এই সম্পর্ককে সংখ্যায় প্রকাশ করে।
প্রযুক্তিগতভাবে, প্রতিটি ইনপুট টোকেন থেকে তিনটি ভেক্টর বের করা হয়: Query (প্রশ্ন), Key (চাবি), এবং Value (মান)। একটি টোকেনের Query ভেক্টর দিয়ে অন্যদের Key-র সাথে ডট প্রোডাক্ট করে স্কোর বের করা হয়, যা পরে স্কেলিং ও সফটম্যাক্সের মাধ্যমে অ্যাটেনশন ওয়েটে রূপান্তরিত হয়। সেই ওয়েট দিয়ে Value ভেক্টরগুলোর গড় করলে আউটপুট পাওয়া যায়—এটাই স্কেলড ডট-প্রোডাক্ট অ্যাটেনশন।
import torch
import torch.nn.functional as F
def scaled_dot_product_attention(query, key, value, mask=None):
d_k = query.size(-1)
scores = torch.matmul(query, key.transpose(-2, -1)) / (d_k ** 0.5)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attention_weights = F.softmax(scores, dim=-1)
return torch.matmul(attention_weights, value)
উপরের কোডটি একটি সরল স্ব-মনোযোগ ফাংশন দেখায়। এখানে d_k দিয়ে ভাগ করা (√d_k) ডট প্রোডাক্টের মান অতি বড় হয়ে যাওয়া রোধ করে, আর মাস্কিং ভবিষ্যতের টোকেন আড়াল করে অটোরিগ্রেসিভ মডেলে ব্যবহার করা হয়। পুরো প্রক্রিয়াটি ম্যাট্রিক্স অপারেশন হওয়ায় GPU-তে অত্যন্ত দ্রুত চলে।
ট্রান্সফরমার ব্লকের অন্যান্য উপাদান
একটি সম্পূর্ণ ট্রান্সফরমার ব্লকে শুধু অ্যাটেনশনই যথেষ্ট নয়; এর সাথে জরুরি কিছু সংযোজন মডেলকে স্থিতিশীল ও শক্তিশালী করে তোলে:
- মাল্টি-হেড অ্যাটেনশন: একই Query, Key, Value কে একাধিক ভিন্ন প্রোজেকশনে ভাগ করে আলাদা আলাদা 'হেড'-এ স্ব-মনোযোগ চালানো হয়, যাতে মডেল বিভিন্ন ধরনের সম্পর্ক (সিন্ট্যাকটিক, সিম্যান্টিক) ধরতে পারে। হেডগুলোর আউটপুট কনক্যাটেনেট করে আবার প্রোজেক্ট করা হয়।
- ফিডফরোয়ার্ড নেটওয়ার্ক: অ্যাটেনশনের পর টোকেন-ভিত্তিক একটি ছোট নিউরাল নেটওয়ার্ক বসে, দুটি রৈখিক স্তর ও একটি অ্যাক্টিভেশন (সাধারণত GELU) সহ। এটি প্রতিটি পজিশনে স্বাধীনভাবে একই ওয়েট প্রয়োগ করে নন-লিনিয়ারিটি যোগ করে।
- লেয়ার নর্মালাইজেশন ও রেসিডুয়াল কানেকশন: অ্যাটেনশন ও ফিডফরোয়ার্ড স্তরের আগে ও পরে লেয়ার নর্ম বসানো হয়, এবং প্রতিটি সাব-লেয়ারের ইনপুট আউটপুটের সাথে যোগ করা হয় (x + Sublayer(x))। এই রেসিডুয়াল কানেকশন গভীর নেটওয়ার্কে গ্রেডিয়েন্ট প্রবাহ বজায় রাখে।
- ড্রপআউট: প্রশিক্ষণকালে ওভারফিটিং কমাতে অ্যাটেনশন ওয়েট ও ফিডফরোয়ার্ড আউটপুটে নিয়মিত ড্রপআউট প্রয়োগ করা হয়।
এই উপাদানগুলো একসাথে একটি ব্লক তৈরি করে। কয়েক ডজন (বা শতাধিক) এমন ব্লক স্ট্যাক করে GPT-3 বা LLaMA-র মতো বড় মডেল গড়ে ওঠে, যেখানে প্রতিটি ব্লক আগের ব্লকের আউটপুটকে আরও কার্যকরী রিপ্রেজেন্টেশনে রূপান্তর করে।
টোকেন এম্বেডিং ও পজিশনাল এনকোডিং
ট্রান্সফরমার শব্দ বা সাব-ওয়ার্ড টোকেন নিয়ে কাজ করে, কিন্তু সেগুলোকে প্রথমেই একটি ঘন ভেক্টরে রূপান্তর করতে হয়। টোকেন এম্বেডিং ম্যাট্রিক্স প্রতিটি টোকেন আইডির বিপরীতে একটি ভেক্টর প্রদান করে, যা মডেলের সাথে প্রশিক্ষিত হয়। তবে ট্রান্সফরমারের নিজস্ব কোনো ক্রমিক বোধ নেই—"আমি ভাত খাই" আর "ভাত খাই আমি" এর টোকেনগুলোর অর্থ আলাদা হলেও তাকে বলে দিতে হবে টোকেনের অবস্থান কোথায়। পজিশনাল এনকোডিং সেই অবস্থানের তথ্য ইনপুট ভেক্টরের সাথে যোগ করে দেয়।
প্রাথমিক পেপারে সাইন ও কোসাইন ফাংশন দিয়ে নির্দিষ্ট ফ্রিকোয়েন্সির অবস্থান এনকোডিং প্রস্তাব করা হয়েছিল। আধুনিক LLM-এ শেখা যোগ্য পজিশনাল এম্বেডিং বা রোটারি পজিশনাল এম্বেডিং (RoPE) ব্যবহার করা হয়, যা আপেক্ষিক অবস্থানের তথ্য আরও ভালোভাবে ক্যাপচার করে। এই ধাপ ছাড়া ট্রান্সফরমার বাক্যের শুরু-শেষ বুঝতে পারত না, ফলে অর্থহীন আউটপুট দিত।
সংক্ষেপে একটি LLM-অভিমুখী ডিকোডার-অনলি ট্রান্সফরমার
বেশিরভাগ আধুনিক LLM একটি অটোরিগ্রেসিভ ডিকোডার-অনলি আর্কিটেকচার অনুসরণ করে। এতে পুরো সিকোয়েন্স একবারে দেখে, কিন্তু ভবিষ্যতের টোকেন মাস্ক করা থাকে (causal masking) যাতে প্রতিটি টোকেন কেবল আগের টোকেনগুলোর উপর নির্ভর করে পরবর্তী টোকেন প্রেডিক্ট করে। এর সামগ্রিক ধাপ এমন:
- ইনপুট টোকেনগুলিকে টোকেন এম্বেডিং ও পজিশনাল এনকোডিং দিয়ে একটি ভেক্টর সিরিজে রূপান্তর করা।
- প্রতিটি ট্রান্সফরমার ব্লকের ভিতরে মাল্টি-হেড causal self-attention প্রয়োগ করে বর্তমান টোকেন পর্যন্ত তথ্য একত্র করা।
- ফিডফরোয়ার্ড নেটওয়ার্কের মাধ্যমে নন-লিনিয়ার ট্রান্সফর্মেশন।
- ব্লকগুলোর স্ট্যাক থেকে শেষ লেয়ারের প্রতিটি পজিশনের আউটপুট ভেক্টরকে একটি লিনিয়ার লেয়ার ও সফটম্যাক্সের মধ্যে পাঠিয়ে শব্দভান্ডারের উপর সম্ভাবনা বের করা।
- থামার শর্ত পূরণ না হওয়া পর্যন্ত জেনারেটেড টোকেনকে আবার ইনপুট হিসেবে যোগ করে পরবর্তী টোকেন উৎপন্ন করা।
এই সরল অথচ গভীর স্থাপত্যই কোটি কোটি প্যারামিটার সহ বড় ভাষা মডেলগুলোকে কম্পিউটেশনাল দক্ষতা বজায় রেখে ইন্টারনেট-স্তরের ডেটা শিখতে সক্ষম করে তুলেছে। ট্রান্সফরমারের মেকানিজম বুঝলে, একটি LLM-এর আউটপুটের পেছনের কারণগুলোও অনেক বেশি স্বচ্ছ হয়।