Part 03 · Deep Learning

RNNs and the Attention Mechanism

RNNLSTMSequence modellingAttention

Text একটা ক্রমে আসে। RNN কীভাবে শব্দে শব্দে একটা memory বয়ে নেয়, long sentence এ কেন সেই memory মুছে যায়, আর attention কীভাবে প্রতিটা শব্দকে সরাসরি অন্য যেকোনো শব্দের দিকে তাকাতে দেয়।

⏱ 1-2 ঘণ্টা · Station 20 / 92

জিনিসটা কী, কেন দরকার

"The cat sat on the mat because it was tired." "it" মানে কী বুঝতে চার শব্দ পেছনে তাকাতে হয়, "cat" এ। একটা আইনি চুক্তিতে "it" যেটাকে বোঝায় সেটা হয়তো তিন paragraph আগে। ভাষা একটা sequence, আর অর্থ নির্ভর করে ক্রমের উপর, আর এমন শব্দের উপর যেগুলো অনেক দূরে থাকতে পারে।

সাধারণ network বা CNN একটা নির্দিষ্ট সাইজের input নেয়, "আগে" বলে কিছু বোঝে না। প্রথম সমাধান ছিল recurrent neural network (RNN): একবারে একটা শব্দ পড়ো আর একটা memory সামনে নিয়ে যাও। কাজ করত, কিন্তু লম্বা দূরত্বে memory মিলিয়ে যেত। Attention এটা ঠিক করল প্রতিটা শব্দকে অন্য প্রতিটা শব্দের দিকে সরাসরি তাকাতে দিয়ে, আর পুরো Part 4, মানে transformer আর LLM, দাঁড়িয়ে আছে এই একটা ধারণার উপর।

ভেতরে কীভাবে চলে

RNN। একটা hidden state h রাখো, একটা vector যেটা এখন পর্যন্ত পড়া সবকিছুর সারাংশ। প্রতিটা নতুন শব্দ x এর জন্য:

h_new = tanh(W_x · x + W_h · h_old + b)

প্রতিটা step এ একই weight, তাই যেকোনো দৈর্ঘ্যের বাক্য সামলায়। শেষের h (বা প্রতিটা h) একটা classifier এ যায় বা পরের শব্দ predict করে।

Memory কেন মিলিয়ে যায়। 50 নম্বর শব্দে state এর উপর 1 নম্বর শব্দের প্রভাবকে W_h দিয়ে 49 টা গুণ আর 49 টা tanh slope পার হতে হয়, তাই সেটা exponential হারে ছোট হয় (বা ফেটে যায়): আবার সেই vanishing gradient, এবার সময় বরাবর। Network শিখতেই পারে না যে "it" 40 শব্দ আগের কিছুকে বোঝায়, কারণ training signal কখনো অতদূর পৌঁছায় না।

LSTM আর GRU যোগ করে gate: ছোট শেখা switch যেগুলো ঠিক করে কী রাখবে, কী ভুলবে, কী বের করবে, সাথে একটা আলাদা "cell" যেটা দিয়ে তথ্য প্রায় অপরিবর্তিত পার হতে পারে। এগুলো অনেক বেশি সময় মনে রাখে (শত শত step), আর 2017 এর আশেপাশে পর্যন্ত translation আর speech এর standard ছিল। অন্য সীমাটা থেকেই যায়: 49 নম্বর শব্দ শেষ না হলে 50 নম্বর process করা যায় না, তাই একটা বাক্যের উপর training parallel এ চলতে পারে না।

Attention। একটা chain ধরে চিরকুট পাঠানোর বদলে, প্রতিটা শব্দ অন্য প্রতিটা শব্দকে সরাসরি জিজ্ঞেস করে:

  1. এখনকার শব্দের একটা query vector থাকে ("আমি কী খুঁজছি?"); প্রতিটা শব্দের একটা key ("আমার ভেতরে কী আছে?")।
  2. প্রতিটা শব্দের জন্য score = query · key (Part 1 এর dot product), √dimension দিয়ে scale করা।
  3. Softmax score গুলোকে weight বানায়, যোগ করলে 1।
  4. Output হলো শব্দগুলোর value vector এর weight দেওয়া মিশ্রণ।

1 নম্বর শব্দ এখন 50 নম্বর থেকে 49 step না, এক step দূরে। আর সব position একসাথে হিসাব হয়, matrix multiply হিসেবে, যেটা GPU র খুব পছন্দ। Part 4 পুরো version টা বানায় (শেখা Q, K, V সহ self-attention); এখানে ধারণাটুকুই যথেষ্ট।

আগে অনুমান করো

একটা RNN 1,000 শব্দের একটা document পড়ে। শেষ বাক্য নিয়ে answer দিতে গিয়ে প্রথম বাক্যের একটা detail ব্যবহার করা তার জন্য কঠিন কেন?

ছবিতে দেখো

RNN: one state passed alongthecatsatbecauseitwastiredhby 'it', the hidden state holds only a faint trace of 'cat'attention: 'it' looks at every word directlythecatsatbecauseitwastiredweights: cat 0.34, it 0.32, everything else under 0.10
উপরে: একটা RNN "the cat sat because it was tired" একটা একটা শব্দ করে পড়ে, একটাই hidden state সামনে নিয়ে যায়; "it" এ পৌঁছাতে পৌঁছাতে "cat" এর হালকা একটা ছাপই বাকি থাকে। নিচে: attention এ "it" প্রতিটা শব্দের সাথে সরাসরি যুক্ত; সবচেয়ে মোটা যোগটা "cat" এ (weight 0.34)।

Code এ RNN আর attention

PyTorch এ দুইটা মাপ। প্রথমে, 2 থেকে 50 শব্দের বাক্যে একটা আসল RNN এর শেষ state এখনো প্রথম শব্দের উপর কতটা নির্ভর করে (gradient দিয়ে মাপা)। তারপর, হাতে বানানো ছোট word vector দিয়ে 7 শব্দের একটা বাক্যে "it" থেকে attention।

import torch

torch.manual_seed(0)

# 1) An RNN reads one word at a time and keeps a hidden state h: h = tanh(W_x·x + W_h·h).
rnn = torch.nn.RNN(input_size=8, hidden_size=16, batch_first=True)

def first_word_influence(length):
    x = torch.randn(1, length, 8, requires_grad=True)
    _, h_last = rnn(x)
    h_last.sum().backward()
    return x.grad[0, 0].norm().item()       # how much the final state depends on word 1

print("how much the last hidden state still depends on the FIRST word")
for n in (2, 5, 10, 20, 50):
    print(f"  sentence of {n:>2} words: {first_word_influence(n):.1e}")

# 2) Attention: each word scores every other word, softmax turns scores into weights,
#    and the output is the weighted mix. No chain to travel along: word 1 is one step away.
words = ["the", "cat", "sat", "because", "it", "was", "tired"]
# Hand-made 3-number vectors: [animal-ness, action-ness, filler-ness]
vec = {"the": [0, 0, 1], "cat": [1, 0, 0.1], "sat": [0, 1, 0], "because": [0, 0, 1],
       "it": [0.9, 0, 0.3], "was": [0, 0.3, 0.8], "tired": [0.3, 0.7, 0]}
K = torch.tensor([vec[w] for w in words])
q = torch.tensor(vec["it"])                  # the word asking "who am I about?"
scores = K @ q / (3 ** 0.5)                   # dot product, scaled by sqrt(dimension)
weights = torch.softmax(scores * 4, dim=0)    # sharpen a little so the pattern is easy to see
print("\nattention from 'it' to every word:")
for w, a in sorted(zip(words, weights.tolist()), key=lambda p: -p[1]):
    print(f"  {w:<8} {a:.2f} {'#' * round(a * 40)}")
mix = weights @ K
print("output for 'it' =", [round(v, 2) for v in mix.tolist()], " <- mostly the cat's vector")

Output:

how much the last hidden state still depends on the FIRST word
  sentence of  2 words: 4.9e-01
  sentence of  5 words: 5.1e-02
  sentence of 10 words: 1.6e-03
  sentence of 20 words: 1.4e-07
  sentence of 50 words: 2.9e-17

attention from 'it' to every word:
  cat      0.34 ##############
  it       0.32 #############
  the      0.08 ###
  because  0.08 ###
  tired    0.07 ###
  was      0.07 ###
  sat      0.04 ##
output for 'it' = [0.65, 0.11, 0.34]  <- mostly the cat's vector

কী দেখলাম:

  • RNN এর memory exponential হারে মিলিয়ে যায়: প্রথম শব্দের প্রভাব 2 শব্দ পরে 0.49, 10 এর পরে 0.0016, 20 এর পরে 1.4 × 10⁻⁷, আর 50 এর পরে 3 × 10⁻¹⁷। মোটামুটি 20 শব্দের পরে প্রথম শব্দটা কার্যত নেই। এত ছোট signal কোনো training ই ঠিক করতে পারে না।
  • Attention "cat" খুঁজে পায়: "it" থেকে weight "cat" এ 0.34 আর নিজের উপর 0.32, বাকি সব শব্দ 0.10 এর নিচে। দূরত্ব কোনো ব্যাপার ছিল না; "cat" জিতেছে কারণ তার vector "it" যা খুঁজছে তার সাথে মেলে।
  • "it" এর output [0.65, 0.11, 0.34], cat এর vector এর কাছাকাছি। "it" যে শব্দকে বোঝায় তার অর্থ নিজের ভেতরে টেনে নিয়েছে। Transformer layer প্রতিটা শব্দের জন্য ঠিক এটাই করে, বারবার।
  • এখানে vector গুলো হাতে বানানো। আসল model এ query, key আর value শেখা হয় (Part 4)।

Production এ যা মাথায় রাখবে

  • Attention এর খরচ n²। প্রতিটা শব্দ অন্য প্রতিটা শব্দকে score করে, তাই 10 গুণ লম্বা text এ attention ধাপে খরচ 100 গুণ। এজন্যই LLM এর context window এর সীমা আছে, আর long-context এর কৌশল গুলো (Part 4, Part 9) আছে।
  • RNN মরেনি। প্রতি step এ সস্তা আর memory স্থির, তাই ছোট on-device model এ এখনো চলে (keyboard, wake-word ধরা), আর নতুন state-space model (Mamba আর এরকম) লম্বা sequence এর জন্য ধারণাটা ফিরিয়ে আনছে।
  • Attention weight ব্যাখ্যা না। বড় weight দেখায় একটা layer এ তথ্য কোথায় গেছে, model কেন কোনো সিদ্ধান্ত নিল সেটা না। User কে attention map "কারণ" হিসেবে দেখিও না।
  • ক্রম যোগ করে দিতে হয়। Attention নিজে বাক্যকে শব্দের থলে হিসেবে দেখে: শব্দগুলো এলোমেলো করলেও weight বদলায় না। Transformer positional encoding (Part 4) যোগ করে ক্রম ফিরিয়ে আনে।

এক কথায়

RNN শব্দ ধরে ধরে একটা hidden state বয়ে নেয়, তাই লম্বা sequence এ আগের তথ্য exponential হারে মিলিয়ে যায়; attention প্রতিটা শব্দকে অন্য প্রতিটা শব্দ সরাসরি score করে weight দেওয়া মিশ্রণ নিতে দেয়, যেটা দূরত্ব সামলায় আর parallel এ চলে, তবে খরচ দৈর্ঘ্যের বর্গে বাড়ে।

নিজে করে দেখো

Attention এর example এ বাক্যটা বদলে করো "the dog chased the cat because it was hungry", "dog" কে vector [1, 0, 0] আর "cat" কে [1, 0, 0.1] দাও, আর "it" থেকে weight গুলো দেখো। এই হাতে বানানো vector গুলো কেন ঠিক করতে পারে না "it" কোন প্রাণী, আর একটা আসল model এর কী লাগবে?

Answer

"dog" আর "cat" এর vector প্রায় একই, তাই "it" দুইটাকে প্রায় সমান weight দেয়: প্রাণী-ভাবের একটা vector এ এমন কিছু নেই যেটা বলে কোন প্রাণীটা ক্ষুধার্ত। আসল model বিশাল পরিমাণ text থেকে query আর key শেখে, তাই "it" এর query এমন জিনিস ধরতে পারে যেমন "আগের clause এর subject" বা "যার ক্ষুধা লাগতে পারে", আর কয়েকটা attention head (Part 4) একসাথে আলাদা আলাদা সূত্র খুঁজতে পারে। তারপরও এরকম বাক্য কঠিনই থাকে; language model এর একটা ক্লাসিক test।

Station stamp

Chapter শেষ করো

পড়া শেষ হলে Complete চাপো, progress save হবে। XP শুধু Pro তে — Pro থাকলে এই chapter এ +10 XP পেতে।

Pro নিয়ে XP পাও →