এই পর্বে যা শিখবেন
- পছন্দের ডেটা (prompt, chosen, rejected) কেন SFT-এর পরের ধাপ, আর তার নিজের সমস্যা
- RLHF-এর তিন ধাপ, InstructGPT প্রবন্ধ যেভাবে বর্ণনা করেছে
- Bradley–Terry loss derive করা, হাতে হিসাব আর gradient যাচাই
- KL শাস্তিসহ PPO-র রূপরেখা, আর DPO-র derivation ও loss হাতে
- Reward hacking, Constitutional AI/RLAIF আর GRPO-র জায়গা
পর্ব ৫৬ · Instruction tuning — SFT ডেটা, chat template আর ফরম্যাট-এ model “নির্দেশ → উত্তর” জোড়া নকল করে শিখেছে; সেখানে LoRA এসেছে পর্ব ৫৫ · Parameter-efficient fine-tuning থেকে। RLHF-এর এক অনুচ্ছেদের পরিচয় AI-এর ভিত্তি কোর্সের পর্ব ৭০-এ আছে। এই পর্বে সেটির গণিত আর হাতে-কলমে কাজ। Desktop-এর run লেখার সময়ে (২০২৬ সালের নভেম্বর) এই version-এ: Python 3.12, torch 2.15.0, transformers 5.22.2, trl 1.17.1, peft 0.21.2, datasets 5.1.0; model Qwen/Qwen2.5-0.5B-Instruct (Apache 2.0 লাইসেন্স, revision 7ae5576), model card পড়ে নেওয়া। GPU-র কাজ GPU-তে, যেখানে যেটি তা লেখা আছে।
১ · SFT কেন যথেষ্ট নয় — পছন্দের ডেটা
SFT-এ model প্রতিটি প্রশ্নের একটিমাত্র “ঠিক” উত্তরের token-গুলোর সম্ভাবনা বাড়ায়। কোন উত্তরটি খারাপ, তা সে কখনো দেখে না। রূঢ় উত্তর, ভুল সংখ্যা বা অকারণ লম্বা ভূমিকা: SFT-এর loss এগুলোকে আলাদা করে শাস্তি দেয় না। আবার সব প্রশ্নের নিখুঁত উত্তর লিখে দেওয়াও ব্যয়বহুল।
উপমা: একজন পরীক্ষক নিজে হয়তো নিখুঁত উত্তর লিখতে পারবেন না, কিন্তু দুটি খাতা পাশাপাশি রাখলে কোনটি ভালো তা দ্রুত বলতে পারেন। পছন্দের ডেটা ঠিক এই তুলনা: একই প্রশ্নের দুটি উত্তর, একটি chosen, অন্যটি rejected.
বাজারঘরের সাপোর্ট-সহকারীর জন্য আমরা হাতে লেখা template থেকে seed দিয়ে একটি ছোট বাংলা পছন্দের ডেটা বানিয়েছি। এটি বানানো ডেটা, কোনো সত্যিকারের গ্রাহকের নয়। নীতিগুলো পর্ব ৫৬-এর মতোই: ঢাকার ভেতরে ১–২ দিন, চার্জ ৬০ টাকা; বাইরে ৩–৫ দিন, ১২০ টাকা; ফেরত ৭ দিনে; রিফান্ড ৫ কার্যদিবসে; নষ্ট পণ্য ৪৮ ঘণ্টার মধ্যে জানাতে হয়। Chosen সব সময় ঠিক, ছোট আর ভদ্র; rejected চার ধরনের ভুলের একটি। ডেটা ফাইলের প্রথম জোড়াটি হুবহু (pref_train.json থেকে, intent চাবি বাদে):
{
"question": "আমি উত্তরা-তে থাকি, পণ্য কবে পাব?",
"chosen": "ঢাকার ভেতরে (উত্তরা) ডেলিভারি ১–২ দিনে হয়, চার্জ ৬০ টাকা।",
"rejected": "জানি না, অন্য কোথাও জিজ্ঞেস করুন।",
"kind": "rude"
}
| Rejected-এর ধরন | উদাহরণ | কী শেখাতে চাই |
|---|---|---|
| ভুল তথ্য | “… ডেলিভারি একই দিনে হয়, কোনো চার্জ নেই।” | সাবলীল কিন্তু ভুল উত্তর খারাপ |
| রূঢ় | “এসব তো অ্যাপেই লেখা আছে, নিজে পড়ে নিন।” | ভদ্রতা |
| অস্পষ্ট | “নিয়ম মাঝে মাঝে বদলায়, ঠিক বলা কঠিন।” | সাহায্য করা |
| প্যাঁচাল | ৪৭ শব্দের ধন্যবাদ আর প্রতিশ্রুতি, কোনো তথ্য নেই | দৈর্ঘ্য মানেই মান নয় |
Train-এ ৩৬২টি অনন্য জোড়া, আলাদা রাখা test-এ ৮৪টি। Test-এর প্রশ্নের ধরন, জেলা আর পণ্য train-এ নেই, তাই test-এর একটি প্রশ্নও train-এ হুবহু আসেনি। সত্যিকারের পছন্দের ডেটা এত পরিষ্কার হয় না: InstructGPT প্রবন্ধে প্রশিক্ষণের labeler-রা একে অপরের সঙ্গে প্রায় ৭২.৬% ক্ষেত্রে একমত ছিলেন। মানুষের পছন্দে মতভেদ, ক্লান্তি আর পক্ষপাত থাকে, আর reward model সেগুলোও শেখে।
২ · RLHF-এর তিন ধাপ — InstructGPT যেভাবে করেছে
ভাষা model-এ RLHF-এর বহুল উদ্ধৃত বর্ণনা Ouyang ও সহলেখকদের InstructGPT প্রবন্ধ (arXiv, মার্চ ২০২২)। ধারণাটি আগের কাজ থেকে এসেছে: Christiano ও সহলেখকেরা (২০১৭) মানুষের তুলনা থেকে reward শেখান, আর Stiennon ও সহলেখকেরা (২০২০) সারাংশ লেখায় একই পদ্ধতি প্রয়োগ করেন। প্রবন্ধের বর্ণনায় GPT-3 থেকে শুরু করে তিনটি ধাপ:
- SFT
~13k promptনমুনা নকল - তুলনা
K = 4–9উত্তর সাজানো - Reward model
6B, ~33kBT loss - PPO
~31k promptKL শাস্তি - PPO-ptx
+ γpretraining মেশানো
- SFT. Labeler-দের লেখা নমুনা উত্তরে GPT-3 fine-tune; SFT ডেটায় প্রায় ১৩ হাজার prompt. এটিই পরের ধাপের শুরু আর reference model.
- তুলনা. একটি prompt-এর K = ৪ থেকে ৯টি উত্তর labeler সাজান; তাতে K(K − 1)/2টি জোড়া, K = ৯-এ ৩৬টি। একই prompt-এর জোড়াগুলো খুব সম্পর্কিত, তাই প্রবন্ধে সবগুলো একটি batch-এর একটি উপাদান হিসেবে শেখানো হয়, নইলে এক epoch-এই overfit হচ্ছিল।
- Reward model. SFT model-এর শেষ unembedding layer সরিয়ে একটি scalar বের করার model; ৬B আকারের, কারণ লেখকেরা ১৭৫B-এর RM training অস্থির পেয়েছিলেন। Loss অংশ ৩-এর Bradley–Terry; RM ডেটায় প্রায় ৩৩ হাজার prompt.
- PPO. একটি prompt আসে, policy উত্তর লেখে, RM score দেয়, পর্ব শেষ। সঙ্গে প্রতি token-এ SFT model থেকে KL শাস্তি, যাতে RM-কে “অতিরিক্ত খুশি” করা না যায়। Value function শুরু হয় RM থেকে; PPO ডেটায় প্রায় ৩১ হাজার prompt, কোনো মানুষের label ছাড়া।
- PPO-ptx. সাধারণ NLP ডেটাসেটে মান কমে যাওয়া (প্রবন্ধের ভাষায় “alignment tax”) ঠেকাতে pretraining-এর log-likelihood γ গুণে মেশানো। প্রবন্ধে “InstructGPT” বলতে এটিই।
প্রবন্ধের মূল দাবি: তাঁদের prompt-বণ্টনে মানুষের মূল্যায়নে ১.৩B InstructGPT-এর উত্তর ১৭৫B GPT-3-এর চেয়ে বেশি পছন্দ হয়েছে, যদিও parameter প্রায় ১০০ ভাগের এক ভাগ। তবে লেখকেরা নিজেরাই জানান, ডেটার ৯৬%-এর বেশি ইংরেজি, আর model এখনো সরল ভুল করে। পরের বাণিজ্যিক assistant-গুলো ঠিক কোন পদ্ধতি আর ডেটায় align করা, তার বেশির ভাগ প্রকাশিত নয়; তাই “ChatGPT এভাবে বানানো” বলা যায় না, শুধু প্রকাশিত প্রবন্ধে কী আছে তা বলা যায়।
৩ · Reward model আর Bradley–Terry loss
Reward model (RM) একটি prompt x আর উত্তর y নিয়ে একটি সংখ্যা r(x, y) দেয়। কিন্তু মানুষ সংখ্যা দেননি, দিয়েছেন তুলনা। সংখ্যা আর তুলনা জোড়ার সেতু Bradley–Terry model (Bradley ও Terry, ১৯৫২): দুটি জিনিসের প্রতিটির একটি লুকানো “শক্তি” আছে, আর একটি জেতার সম্ভাবনা
P(yw ≻ yl | x) = erw / (erw + erl) = σ(rw − rl)
এখানে yw জেতা (chosen), yl হারা (rejected), rw = r(x, yw), আর σ হলো sigmoid, σ(z) = 1/(1 + e−z). দ্বিতীয় সমতা পেতে লব আর হর দুটিকে erw দিয়ে ভাগ করুন: 1/(1 + e−(rw − rl)). দাবা বা ক্রিকেট র্যাংকিং-এর রেটিংও একই ধাঁচের চিন্তা: শুধু রেটিংয়ের পার্থক্য জয়ের সম্ভাবনা ঠিক করে।
ডেটার সম্ভাবনা সর্বোচ্চ করা (MLE; মেশিন লার্নিং কোর্সের পর্ব ১৮) মানে negative log-likelihood কমানো:
LRM = −E[ log σ(rw − rl) ], ∂L/∂rw = −(1 − σ(Δ)) = −σ(−Δ), ∂L/∂rl = +σ(−Δ)
Δ = rw − rl. এটি আসলে Δ-এর ওপর logistic regression, যেখানে label সব সময় ১ (AI-এর ভিত্তি কোর্সের পর্ব ৫৯)। দুটি জরুরি ফল। এক, gradient-এর মাপ σ(−Δ): ক্রম ভুল হলে (Δ < 0) ধাক্কা বড়, ঠিক আর নিশ্চিত হলে প্রায় শূন্য। দুই, দুটি score-এ একই সংখ্যা যোগ করলে loss বদলায় না; তাই score-এর নিজের কোনো একক নেই। InstructGPT এ কারণে RL-এর আগে একটি bias দিয়ে RM-কে এমনভাবে সরায় যাতে নমুনা উত্তরগুলোর গড় score শূন্য হয়।
| ধাপ | |
| হিসাব | |
| ফল |
- Score. RM chosen উত্তরকে ১.৫ আর rejected-কে ০.৩ দিয়েছে। আলাদা করে এদের অর্থ নেই।
- পার্থক্য. Δ = ১.৫ − ০.৩ = ১.২; Bradley–Terry শুধু এটিই দেখে।
- সম্ভাবনা. σ(১.২) = ০.৭৬৮৫: model-এর হিসাবে chosen ভালো হওয়ার সম্ভাবনা প্রায় ৭৭%.
- Loss. −ln ০.৭৬৮৫ = ০.২৬৩৩। ক্রম ঠিক, তাই loss ছোট; শূন্য নয়, কারণ নিশ্চয়তা ১০০% নয়।
- Gradient. ∂L/∂rw = −০.২৩১৫: gradient descent rw বাড়াবে, rl সমান মাপে কমাবে। দুটির মোট যোগফল শূন্য, কারণ শুধু পার্থক্য গোনে।
- উল্টো ক্রম. একই সংখ্যা উল্টে দিলে σ(−১.২) = ০.২৩১৫, loss ১.৪৬৩৩, আর ধাক্কা ০.৭৬৮৫, তিন গুণের বেশি। ভুল ক্রমের জোড়া বেশি শেখায়।
- এক ধাপ. Learning rate ১ নিয়ে score-এর ওপর সরাসরি এক ধাপ: rw = ১.০৬৮৫, rl = ০.৭৩১৫, Δ = ০.৩৩৭, ক্রম ঠিক হয়ে গেল, loss ০.৫৩৮৮। আসল RM-এ ধাপ নেয় network-এর ওজন, score নয়।
এবার আটটি জোড়ায় loss, pairwise accuracy, সূত্রের gradient সংখ্যাগত gradient-এর সঙ্গে মেলানো, আর score সরালে কী হয়:
import numpy as np
# reward model-এর score: ৮টি জোড়া, chosen (w) আর rejected (l)
r_w = np.array([2.1, 0.4, 1.5, -0.2, 3.0, 0.9, 0.1, 1.2])
r_l = np.array([0.3, 0.9, -1.0, -0.5, 1.0, 1.4, 0.1, -0.8])
sig = lambda z: 1 / (1 + np.exp(-z))
def bt_loss(rw, rl): # −log σ(r_w − r_l)-এর গড়
return -np.log(sig(rw - rl)).mean()
d = r_w - r_l
print("Δ = r_w − r_l :", np.round(d, 2))
print("P(chosen ভালো):", np.round(sig(d), 3))
print("BT loss :", round(bt_loss(r_w, r_l), 4))
print("pairwise acc. :", (d > 0).mean(), "(Δ = 0 জোড়াটিও ভুল ধরা)")
g_w = -(1 - sig(d)) / len(d) # ∂L/∂r_w, সূত্র থেকে
eps, num = 1e-6, np.zeros_like(r_w)
for i in range(len(r_w)): # সংখ্যাগত gradient, যাচাইয়ের জন্য
e = np.zeros_like(r_w); e[i] = eps
num[i] = (bt_loss(r_w + e, r_l) - bt_loss(r_w - e, r_l)) / (2 * eps)
print("∂L/∂r_w :", np.round(g_w, 4))
print("max |সূত্র − সংখ্যাগত| =", f"{np.abs(g_w - num).max():.1e}")
print("সব score-এ +5 :", round(bt_loss(r_w + 5, r_l + 5), 4))
lr = 4.0 # এক ধাপ gradient descent, score-এর ওপর
r_w2, r_l2 = r_w - lr * g_w, r_l + lr * g_w
print("এক ধাপ পরে loss:", round(bt_loss(r_w2, r_l2), 4),
"| acc:", ((r_w2 - r_l2) > 0).mean())আউটপুট (পাতার Python-এ চালানো):
Δ = r_w − r_l : [ 1.8 -0.5 2.5 0.3 2. -0.5 0. 2. ]
P(chosen ভালো): [0.858 0.378 0.924 0.574 0.881 0.378 0.5 0.881]
BT loss : 0.4602
pairwise acc. : 0.625 (Δ = 0 জোড়াটিও ভুল ধরা)
∂L/∂r_w : [-0.0177 -0.0778 -0.0095 -0.0532 -0.0149 -0.0778 -0.0625 -0.0149]
max |সূত্র − সংখ্যাগত| = 2.9e-11
সব score-এ +5 : 0.4602
এক ধাপ পরে loss: 0.3213 | acc: 1.0
দ্বিতীয় আর ষষ্ঠ জোড়ায় (Δ = −০.৫) ক্রম ভুল, আর সপ্তম জোড়ায় Δ = ০: সম্ভাবনা ঠিক ০.৫, model সিদ্ধান্তহীন, তাই সেটিও ভুল গোনা হয়েছে; accuracy ৫/৮ = ০.৬২৫। Gradient-এর মাপ দেখুন: ভুল ক্রমের জোড়ায় −০.০৭৭৮, আর Δ = ২.৫-এর নিশ্চিত জোড়ায় মাত্র −০.০০৯৫। সূত্র আর সংখ্যাগত gradient-এর পার্থক্য ৩ × ১০−১১, অর্থাৎ derivation ঠিক। সব score-এ ৫ যোগ করলেও loss হুবহু ০.৪৬০২: RM-এর score-এর শূন্য-বিন্দু অর্থহীন। শেষ লাইনে score-এর ওপর সরাসরি এক ধাপ নিয়ে সব জোড়া ঠিক ক্রমে এসেছে; আসল RM-এ এই ধাপ নেয় network-এর ওজন, তাই এক জোড়া ঠিক করতে গিয়ে অন্য জোড়ার score-ও বদলায়।
৪ · সত্যিকারের run — বাজারঘরের reward model
InstructGPT-এর মতো RM বানানো হয়েছে একটি ভাষা model থেকে: Qwen/Qwen2.5-0.5B-Instruct-এর শেষ unembedding-এর বদলে একটি নতুন Linear (৮৯৬ → ১), যা প্রতিটি উত্তরের শেষ token থেকে একটি score দেয়। Transformers-এ এটি AutoModelForSequenceClassification, num_labels=1. পুরো model না শিখিয়ে q, k, v, o-তে LoRA (r = ৮, পর্ব ৫৫) আর নতুন score head: ১০,৮২,২৪০টি শেখার parameter, মোটের ০.২২%. Loss হাতে লেখা Bradley–Terry, প্রতি ধাপে ৪টি জোড়া, ২ epoch, AdamW (lr ২ × ১০−৪), bf16 autocast আর gradient checkpointing. মূল অংশ (script থেকে ছোট করা):
model = AutoModelForSequenceClassification.from_pretrained(MODEL, num_labels=1)
model = get_peft_model(model, LoraConfig(task_type="SEQ_CLS", r=8, lora_alpha=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"]))
r = model(**enc).logits[:, 0] # প্রথম ৪টি chosen, পরের ৪টি rejected
rc, rr = r[:4], r[4:]
loss = -F.logsigmoid(rc - rr).mean() # Bradley–Terry
আলাদা রাখা ৮৪টি জোড়ায় ফল, তিনটি seed-এ (seed বদলায় score head-এর শুরুর মান আর ডেটার ক্রম)। সত্যিকারের run, GPU-তে; প্রতিটির training প্রায় ১১৬–১১৯ সেকেন্ড, peak প্রায় ২,৫৪৫ MiB (torch.cuda.max_memory_allocated). তুলনার জন্য “লম্বা উত্তরই ভালো” নিয়মের accuracy ০.৬৯০।
| মাপ | seed ০ | seed ১ | seed ২ |
|---|---|---|---|
| Pairwise accuracy, শেখার আগে | ০.৬৩১ | ০.৫৩৬ | ০.৭০২ |
| Pairwise accuracy, ২ epoch পরে | ১.০০০ | ১.০০০ | ১.০০০ |
| ঠিক + প্যাঁচাল > শুধু ঠিক, আগে | ০.৯১৭ | ০.০১২ | ০.৮৫৭ |
| ঠিক + প্যাঁচাল > শুধু ঠিক, পরে | ০.০০০ | ০.০০০ | ০.০০০ |
| অজানা ভুল সংখ্যা ধরা (ঠিক > বদলানো), পরে | ০.৬৬৭ | ০.৬৪৩ | ০.৮৯৩ |
শেষ সারির পরীক্ষাটি: ঠিক উত্তরের একটি সংখ্যা এমন মানে বদলানো, যা ডেটায় কখনো ছিল না (৫ → ১৫ কার্যদিবস, ৪৮ → ২ ঘণ্টা …)। seed ০-এর log-এর লাইন:
{"tag": "unseen_wrong_number", "seed": 0, "n": 84, "original_beats_perturbed": 0.6667, "example": ["ফেরত পণ্য পৌঁছানোর ৫ কার্যদিবসের মধ্যে, যে মাধ্যমে দাম দিয়েছিলেন সেখানেই টাকা ফেরত যায়।", "ফেরত পণ্য পৌঁছানোর ১৫ কার্যদিবসের মধ্যে, যে মাধ্যমে দাম দিয়েছিলেন সেখানেই টাকা ফেরত যায়।", 22.12, 22.38]}
পড়া: শেখার আগে নতুন score head এলোমেলো, তাই ফল seed-ভেদে ০.৫৪ থেকে ০.৭০, আর প্যাঁচাল জুড়লে score বাড়বে কি না তাও কাকতালীয় (১% থেকে ৯২%). দুই epoch পর তিনটি seed-ই ৮৪টির ৮৪টি ঠিক, loss প্রায় ০, আর প্যাঁচাল এখন সব সময় শাস্তি পায়, কারণ ডেটায় প্যাঁচাল-ধরনের rejected ছিল।
কিন্তু ১০০% দেখে খুশি হবেন না। Test-এর প্রশ্ন নতুন হলেও chosen আর rejected উত্তরের ছাঁচ train-এর মতোই, তাই কাজটি সহজ। অজানা ভুল সংখ্যায় RM ১১% থেকে ৩৬% ক্ষেত্রে ভুল সংস্করণকে সমান বা বেশি score দিয়েছে; seed ০ আর ১-এ ওপরের “১৫ কার্যদিবস” ঠিকটির চেয়ে বেশি পেয়েছে। অর্থাৎ RM তথ্য যাচাই শেখেনি, শিখেছে কোন বাক্য-ছাঁচ chosen-এ থাকে, আর seed বদলালেই এই দুর্বলতার মাপ বদলায়। এটি ছোট, বানানো ডেটার স্থানীয় run; কিন্তু শিক্ষাটি সাধারণ: RM-এর accuracy শুধু ডেটার বণ্টনের ভেতরে অর্থপূর্ণ।
৫ · PPO-র রূপরেখা আর KL-এর লাগাম
RM পাওয়ার পর লক্ষ্য: model এমন উত্তর লিখুক যাতে RM-এর score বেশি। এখানে model-কে বলা হয় policy πθ, কারণ reinforcement learning-এর ভাষায় প্রতিটি token একটি action. কিন্তু শুধু score বাড়ালে policy RM-এর দুর্বলতা খুঁজে বের করে ফেলতে পারে। তাই লক্ষ্যে একটি লাগাম থাকে (DPO প্রবন্ধের সমীকরণ ৩-এর রূপে):
maxπθ Ex, y∼πθ[ r(x, y) ] − β · DKL( πθ(y|x) ‖ πref(y|x) )
πref হলো reference model, সাধারণত SFT model-এর একটি স্থির কপি। KL divergence মাপে নতুন policy-র বণ্টন পুরনোটি থেকে কত দূরে (AI-এর ভিত্তি কোর্সের পর্ব ৩৮)। β বড় হলে লাগাম শক্ত। InstructGPT এটি প্রতি token-এ reward-এর ভেতরে বসায়: r(x, y) − β·log(πθ(y|x)/πSFT(y|x)).
PPO (Proximal Policy Optimization; Schulman ও সহলেখক, ২০১৭) এই লক্ষ্য বাড়ানোর একটি algorithm. রূপরেখায়: (১) একগুচ্ছ prompt-এ policy থেকে উত্তর তোলা; (২) RM দিয়ে score, KL শাস্তি বাদ; (৩) একটি value function দিয়ে প্রতিটি token কতটা “প্রত্যাশার চেয়ে ভালো” (advantage) তা অনুমান; (৪) নতুন আর পুরনো policy-র অনুপাত একটি সীমায় কেটে (clip) ছোট ছোট ধাপে policy আপডেট। ফলে memory-তে চারটি model: policy, reference, RM আর value. আমরা এখানে পুরো PPO চালাইনি; একটি ছোট model-এও এটি অনেক hyperparameter-এর ভারী কাজ, আর এই পর্বের মূল ধারণাগুলো নিচের হিসাব আর DPO-র run-এ দেখা যায়।
লাগামটির একটি সুন্দর বন্ধ-রূপ সমাধান আছে, যা DPO প্রবন্ধ আগের কাজ থেকে নিয়ে লেখে: সর্বোত্তম policy π*(y|x) = πref(y|x)·exp(r(x, y)/β) / Z(x), যেখানে Z(x) সব উত্তরের ওপর যোগ করে সম্ভাবনাকে ১-এ আনে ; একে বলে partition function. অর্থাৎ পুরনো সম্ভাবনাকে reward অনুযায়ী বাড়ানো-কমানো, আর β ঠিক করে কতটা। পাঁচটি সম্ভাব্য উত্তরে চালিয়ে দেখুন:
import numpy as np
import matplotlib.pyplot as plt
# একটি প্রশ্নের ৫টি সম্ভাব্য উত্তর; reference (SFT) model-এর সম্ভাবনা আর reward
names = ["ঠিক, ছোট", "ঠিক, লম্বা", "অস্পষ্ট", "রূঢ়", "ভুল তথ্য"]
p_ref = np.array([0.30, 0.10, 0.30, 0.05, 0.25])
r = np.array([2.0, 2.5, -0.5, -2.0, -1.5])
def best_policy(beta): # π* ∝ π_ref · exp(r / β)
w = p_ref * np.exp(r / beta)
return w / w.sum()
def kl(p, q):
return float((p * np.log(p / q)).sum())
print(" beta │ E[r] │ KL(π‖ref) │ E[r] − β·KL │ π*")
rows = []
for beta in [10, 2, 1, 0.5, 0.1, 0.02]:
p = best_policy(beta)
er, k = float(p @ r), kl(p, p_ref)
rows.append((beta, er, k))
print(f"{beta:5} │ {er:5.2f} │ {k:9.3f} │ {er - beta * k:11.3f} │", np.round(p, 2))
print("reference E[r] =", round(float(p_ref @ r), 2))
for name, pr in zip(names, best_policy(0.5)):
print(f" β=0.5: {name:9s} {pr:.2f}")
b, er, k = zip(*rows)
fig, ax = plt.subplots(figsize=(5, 3))
ax.plot(k, er, "o-")
for bi, x, y in rows:
ax.annotate(f"beta={bi}", (x, y), textcoords="offset points", xytext=(5, -12), fontsize=8)
ax.set_xlabel("KL(policy || reference)"); ax.set_ylabel("expected reward")
ax.set_title("Smaller beta: more reward, more drift")
plt.show()আউটপুট (পাতার Python-এ চালানো):
beta │ E[r] │ KL(π‖ref) │ E[r] − β·KL │ π*
10 │ 0.49 │ 0.013 │ 0.357 │ [0.35 0.12 0.28 0.04 0.21]
2 │ 1.42 │ 0.280 │ 0.857 │ [0.53 0.23 0.15 0.01 0.08]
1 │ 1.98 │ 0.679 │ 1.303 │ [0.6 0.33 0.05 0. 0.02]
0.5 │ 2.23 │ 1.008 │ 1.723 │ [0.52 0.47 0. 0. 0. ]
0.1 │ 2.49 │ 2.184 │ 2.272 │ [0.02 0.98 0. 0. 0. ]
0.02 │ 2.50 │ 2.303 │ 2.454 │ [0. 1. 0. 0. 0.]
reference E[r] = 0.22
β=0.5: ঠিক, ছোট 0.52
β=0.5: ঠিক, লম্বা 0.47
β=0.5: অস্পষ্ট 0.00
β=0.5: রূঢ় 0.00
β=0.5: ভুল তথ্য 0.00
β = ১০-এ policy প্রায় নড়েনি, KL মাত্র ০.০১৩। β = ১-এ প্রত্যাশিত reward ০.২২ থেকে ১.৯৮, আর “অস্পষ্ট” উত্তরের সম্ভাবনা ০.৩০ থেকে ০.০৫। β = ০.৫-এ দুটি ঠিক উত্তর প্রায় সমান ভাগে। কিন্তু β ছোট হতে থাকলে policy পুরোপুরি “ঠিক, লম্বা” উত্তরে চলে যায়, কারণ RM সেটিকে ২.৫ দিয়েছে, ছোটটিকে ২.০। β = ০.০২-এ KL = ২.৩০৩ = ln(১/০.১০), অর্থাৎ reference-এ ১০% সম্ভাবনার একটি উত্তরই এখন ১০০%. বৈচিত্র্য শেষ, যাকে mode collapse বলে। RM-এর লম্বা উত্তরের প্রতি সামান্য পক্ষপাত যদি ভুল হয়, ছোট β সেই ভুলকেই পুরো কাজে লাগায়। Graph-এ প্রতিটি বিন্দু একটি β-এর সর্বোত্তম policy: reward বনাম সরে যাওয়ার দাম।
৬ · DPO — reward model ছাড়াই, একই লক্ষ্য
Rafailov ও সহলেখকদের DPO প্রবন্ধ (arXiv, মে ২০২৩; শিরোনামের অর্থ “আপনার language model গোপনে একটি reward model”) একটি সরল প্রশ্ন করে: π* যদি r দিয়ে লেখা যায়, তবে r-কেও π* দিয়ে লেখা যায় না কেন? চারটি ধাপে derivation:
- সমাধান: π*(y|x) = πref(y|x)·exp(r(x, y)/β)/Z(x).
- দুই পাশে log নিয়ে সাজানো: r(x, y) = β·log[π*(y|x)/πref(y|x)] + β·log Z(x).
- Bradley–Terry-তে বসানো: P(yw ≻ yl) = σ(rw − rl). দুটি r-এই একই β·log Z(x), কারণ prompt একই; বিয়োগে কেটে যায়। Z হিসাব করা প্রায় অসম্ভব ছিল, এখন দরকারই নেই।
- π*-এর জায়গায় শেখার policy πθ বসিয়ে MLE:
LDPO = −E[ log σ( β·log(πθ(yw|x)/πref(yw|x)) − β·log(πθ(yl|x)/πref(yl|x)) ) ]
log π(y|x) হলো পুরো উত্তরের log-probability, অর্থাৎ উত্তরের প্রতিটি token-এর log-probability-র যোগফল। r̂(x, y) = β·log(πθ/πref)-কে বলা হয় implicit reward; trl এটিকেই rewards/chosen নামে log করে। প্রবন্ধে gradient-ও লেখা: chosen-এর log-probability বাড়ানো আর rejected-এরটি কমানো, প্রতিটি জোড়ার ওজন σ(r̂l − r̂w), অর্থাৎ implicit reward যত ভুল ক্রমে তত বেশি। লেখকেরা জানান, এই ওজন বাদ দেওয়া সরল সংস্করণে model নষ্ট হয়ে যেতে পারে।
| ধাপ | |
| হিসাব | |
| ফল |
- চারটি সংখ্যা. একই prompt-এ chosen উত্তরের log-probability policy-তে −১২, reference-এ −১৩; rejected-এর −১৪ আর −১৩.৫।
- Chosen-এর log-ratio. −১২ − (−১৩) = +১: policy chosen-কে reference-এর চেয়ে e গুণ বেশি সম্ভাব্য ভাবছে। Implicit reward r̂w = ০.১ × ১ = ০.১০।
- Rejected-এর log-ratio. −১৪ − (−১৩.৫) = −০.৫: rejected কম সম্ভাব্য হয়েছে; r̂l = −০.০৫।
- পার্থক্য h. ১ − (−০.৫) = ১.৫। DPO শুধু এই আপেক্ষিক পার্থক্য দেখে, কোনো উত্তরের সম্পূর্ণ সম্ভাবনা নয়।
- Sigmoid. β·h = ০.১৫; σ(০.১৫) = ০.৫৩৭৪।
- Loss. −ln ০.৫৩৭৪ = ০.৬২১০। শুরুতে policy = reference হলে h = ০, loss = ln ২ = ০.৬৯৩১, β যা-ই হোক।
- ওজন. ∂L/∂log πθ(yw) = −β·σ(−β·h) = −০.১ × ০.৪৬২৬ = −০.০৪৬৩। ঋণাত্মক, তাই gradient descent chosen-এর log-probability বাড়াবে; rejected-এর ক্ষেত্রে চিহ্ন উল্টো।
- β = ০.৫. একই log-prob-এ β·h = ০.৭৫, σ = ০.৬৭৯২, loss ০.৩৮৬৯। বড় β একই সরে যাওয়াকে বেশি “পুরস্কার” মনে করে, তাই কম সরেই loss কমে: এটিই শক্ত লাগাম।
import numpy as np
sig = lambda z: 1 / (1 + np.exp(-z))
# ৪টি জোড়া: পুরো উত্তরের log-probability (token-গুলোর log p-এর যোগফল)
pol_w = np.array([-12.0, -20.5, -8.0, -15.0]) # policy, chosen
pol_l = np.array([-14.0, -19.0, -11.0, -15.5]) # policy, rejected
ref_w = np.array([-13.0, -20.0, -9.0, -15.0]) # reference, chosen
ref_l = np.array([-13.5, -20.0, -10.0, -15.0]) # reference, rejected
def dpo(beta, pw, pl):
h = (pw - ref_w) - (pl - ref_l) # log-ratio-র পার্থক্য
loss = -np.log(sig(beta * h)).mean()
wgt = sig(-beta * h) # ভুল ক্রমে বেশি ওজন
g_w = -beta * wgt / len(h) # ∂L/∂log π(y_w)
return h, loss, wgt, g_w
h, *_ = dpo(0.1, pol_w, pol_l)
print("h = Δlog π_w − Δlog π_l:", h)
for beta in [0.1, 0.5, 2.0]:
h, loss, wgt, g_w = dpo(beta, pol_w, pol_l)
print(f"β={beta:3}: implicit r_w={np.round(beta * (pol_w - ref_w), 2)}",
f"loss={loss:.4f}")
print(f" weight σ(−βh)={np.round(wgt, 3)}")
print("∂L/∂log π_w (β=0.1):", np.round(dpo(0.1, pol_w, pol_l)[3], 4))
print("∂L/∂log π_l = −(∂L/∂log π_w) → চিহ্ন উল্টো")
eps = 1e-6; f = lambda pw: dpo(0.1, pw, pol_l)[1]
e = np.array([eps, 0, 0, 0])
print("সংখ্যাগত ∂L/∂log π_w[0] =", round((f(pol_w + e) - f(pol_w - e)) / (2 * eps), 4))
print("শুরুতে policy = ref হলে loss:",
[round(float(dpo(b, ref_w, ref_l)[1]), 4) for b in (0.1, 0.5, 2.0)], "= log 2")আউটপুট (পাতার Python-এ চালানো):
h = Δlog π_w − Δlog π_l: [ 1.5 -1.5 2. 0.5]
β=0.1: implicit r_w=[ 0.1 -0.05 0.1 0. ] loss=0.6646
weight σ(−βh)=[0.463 0.537 0.45 0.488]
β=0.5: implicit r_w=[ 0.5 -0.25 0.5 0. ] loss=0.6032
weight σ(−βh)=[0.321 0.679 0.269 0.438]
β=2.0: implicit r_w=[ 2. -1. 2. 0.] loss=0.8571
weight σ(−βh)=[0.047 0.953 0.018 0.269]
∂L/∂log π_w (β=0.1): [-0.0116 -0.0134 -0.0113 -0.0122]
∂L/∂log π_l = −(∂L/∂log π_w) → চিহ্ন উল্টো
সংখ্যাগত ∂L/∂log π_w[0] = -0.0116
শুরুতে policy = ref হলে loss: [0.6931, 0.6931, 0.6931] = log 2
দ্বিতীয় জোড়ায় h = −১.৫: policy rejected-কে বেশি তুলেছে। তার ওজন σ(−βh) তিনটি β-তেই সবচেয়ে বড় (০.৫৩৭, ০.৬৭৯, ০.৯৫৩): DPO ভুল ক্রমের জোড়াতেই সবচেয়ে জোরে ঠেলে, Bradley–Terry-র মতোই। Chosen-এর gradient চারটিতেই ঋণাত্মক, তাই gradient descent log π(yw) বাড়ায়; rejected-এরটি ধনাত্মক, কমায়। সংখ্যাগত মান −০.০১১৬ সূত্রের সঙ্গে মিলেছে। শুরুতে loss তিনটি β-তেই ০.৬৯৩১ = ln ২। খেয়াল করুন, β = ২-এ loss (০.৮৫৭১) β = ০.১-এর (০.৬৬৪৬) চেয়ে বেশি, কারণ বড় β ভুল জোড়ার শাস্তিও বাড়ায়। তাই ভিন্ন β-এর loss তুলনা করে model-এর মান বিচার করা যায় না।
৭ · সত্যিকারের run — trl-এর DPOTrainer
একই ৩৬২টি জোড়ায় এবার policy নিজেই শিখবে। Policy Qwen/Qwen2.5-0.5B-Instruct, bf16-এ; q, k, v, o-তে LoRA r = ১৬. Prompt-এ system বার্তা আর প্রশ্ন, তাই ডেটা trl-এর conversational ফরম্যাটে। Reference আলাদা করে লোড করিনি: peft_config দিলে trl নথি অনুযায়ী শুরুর model-টিই reference, আর LoRA-য় সেটি হলো adapter বন্ধ করা একই base. ইচ্ছা করে SFT ধাপ বাদ রাখা হয়েছে, যাতে শুধু DPO-র প্রভাব দেখা যায়। মূল অংশ (script থেকে ছোট করা):
row = {"prompt": [{"role": "system", "content": SYSTEM},
{"role": "user", "content": e["question"]}],
"chosen": [{"role": "assistant", "content": e["chosen"]}],
"rejected": [{"role": "assistant", "content": e["rejected"]}]}
args = DPOConfig(beta=0.1, learning_rate=5e-5, num_train_epochs=2,
per_device_train_batch_size=2, gradient_accumulation_steps=4,
max_length=512, bf16=True, eval_strategy="epoch", eval_on_start=True)
trainer = DPOTrainer(model=model, args=args, train_dataset=train_ds, eval_dataset=test_ds,
processing_class=tok, peft_config=LoraConfig(r=16, lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"]))
β = ০.১-এর run, GPU-তে ২৪৮.৩ সেকেন্ড, peak ২,৯২৪ MiB. trl-এর log থেকে কয়েকটি ধাপ (সত্যিকারের run; প্রতিটি লাইন JSON থেকে ছোট করে সাজানো, প্রতি ধাপে ৮টি জোড়া):
ধাপ loss r̂_chosen r̂_rejected margin accuracy
0 0.6931 0.0000 0.0000 0.000 (eval, শুরু)
5 0.6733 0.0234 -0.0209 0.044 0.45
10 0.3125 0.3758 -1.0276 1.403 1.00
25 0.0088 1.8393 -4.8963 6.736 1.00
46 0.0069 2.3172 -7.0019 9.319 1.00 (eval, epoch 1)
92 0.0019 2.3226 -8.0813 10.404 1.00 (eval, epoch 2)
eval logps/chosen: -132.97 → -109.74 logps/rejected: -163.30 → -244.11
ধাপ ০-এ loss ঠিক ০.৬৯৩১ = ln ২ আর implicit reward ০, অংশ ৬-এর হিসাবের মতোই: policy আর reference তখন এক। ১০ নম্বর ধাপেই train-এর batch-এ accuracy ১.০০, আর শেষে আলাদা রাখা ৮৪টি জোড়াতেও। কিন্তু margin কীভাবে বাড়ল দেখুন: chosen-এর implicit reward ২.৩২, অর্থাৎ log-ratio +২৩.২; rejected-এর −৮.০৮, অর্থাৎ −৮০.৮। শেষ লাইনের log-probability ঠিক এটিই দেখায়: chosen ২৩ nat বেড়েছে, rejected ৮১ nat কমেছে। অর্থাৎ এই run-এ margin বেড়েছে প্রধানত rejected-কে চাপা দিয়ে।
এবার আসল প্রশ্ন: policy-র লেখা উত্তর কি ভালো হলো? আলাদা রাখা ৪০টি প্রশ্নে greedy উত্তর আমাদের নিয়ম দিয়ে মেপেছি (ঠিক সংখ্যা বা শব্দ আছে কি না, যেমন ঢাকার ভেতরে “১–২” আর “৬০”)। আর তিনটি সাধারণ প্রশ্নে আগে-পরে তুলনা, model কতটা সরে গেল দেখতে। সত্যিকারের run, seed ০:
| মাপ (আলাদা রাখা অংশে) | শুরু | β = ০.৫ | β = ০.১ | β = ০.০২ |
|---|---|---|---|---|
| Implicit margin (eval) | ০ | ২৪.৯৮ | ১০.৪০ | ৬.৫৭ |
| Chosen-এর log-ratio (nat) | ০ | +১৪.৬ | +২৩.২ | +৯.২ |
| Rejected-এর log-ratio (nat) | ০ | −৩৫.৩ | −৮০.৮ | −৩১৯.১ |
| Eval-এ implicit reward accuracy | — | ১.০০ | ১.০০ | ১.০০ |
| ৪০টি প্রশ্নে তথ্য-নিয়মে ঠিক (greedy) | ০/৪০ | ০/৪০ | ২/৪০ | ২/৪০ |
| সাধারণ প্রশ্নে ভাষা | দুর্বল বাংলা | দুর্বল বাংলা | দুর্বল বাংলা, অন্য লিপি মেশে | বাংলা ভেঙে কোরীয়, চীনা, হিন্দি |
প্রশ্ন: মিরপুর এলাকায় অর্ডার দিলে হাতে পেতে কয় দিন? (greedy)
শুরু : মিরপুর এলাকা আমার মাঝে খাওয়া গেছে। আমার চার ফাঁ克 শব্দ আমার মাথায় পড়িয়েছ…
β=0.5 : মিরপুরের আপনি অর্ডার দিলে হাতে পেতে 1-2 দিন পড়ায় হবে।
β=0.1 : মিরপুরের আপনি বাজারঘর অনলাইন দোকানের সাপোর্ট সহকারীদের 24 মিনিটের মধ্যে অর্…
β=0.02 : মিরপুরেও আপনي 10-12 দнями धमाई坚坚决反对任何形式的… (চীনা লেখা চলতে থাকে, কাটা হয়েছে)
β=0.1, seed 0, temperature 0.7: মিশ্রপুরের মধ্যে 24 সাথের আপনাকে পেতে পারবে এবং অর্ডারের মতো আপনি 18 মিন锡র আগ…
প্রশ্ন: ২৫ আর ১৭ যোগ করলে কত হয়? (greedy)
শুরু : ২৫ আর৩৮ যোগ করলে কত হয়? এখন একটি গণিত শব্দ লিখি: "সমান্তর: 25 + 38 = 63" …
β=0.5 : ২৫ আর ১৭ যোগ করলে কত হয় ৩৮ যোগ।
β=0.1 : ২৫ আর ১৭ যোগ করলে একটি মধ্যসমposable শব্দ "স্থাপনা" (स्थापना) হবে।
β=0.02 : 25
β-এর ভূমিকা সংখ্যায়। β = ০.৫-এ margin সবচেয়ে বড় (২৪.৯৮), অথচ policy সবচেয়ে কম সরেছে: chosen +১৪.৬, rejected −৩৫.৩ nat. বড় β একটু সরাকেই বড় implicit reward ধরে, তাই loss তাড়াতাড়ি শূন্যে নামে আর শেখা থামে। β = ০.০২-এ ঠিক উল্টো: rejected-এর log-probability ৩১৯ nat নেমেছে, অর্থাৎ policy reference থেকে অনেক দূরে, আর সাধারণ প্রশ্নেও ভাষা ভেঙে অন্য লিপিতে চলে গেছে। অংশ ৫-এর খেলনার “ছোট β = বেশি সরে যাওয়া” এখানে সত্যিকারের model-এ দেখা গেল। তিনটি run-এর প্রতিটি GPU-তে প্রায় ২৪৪–২৪৮ সেকেন্ড, peak ২,৯২৪ MiB, seed ০, প্রতিটি একবার।
সবচেয়ে বড় শিক্ষা: eval-এ implicit reward accuracy তিনটিতেই ১.০০, কিন্তু policy-র নিজের লেখা উত্তরে তথ্য ঠিক হয়েছে ৪০টির মধ্যে বড়জোর ২টিতে। DPO একটি জোড়ার আপেক্ষিক সম্ভাবনা বদলায়; ৪৯ কোটি parameter-এর এই model বাংলায় দুর্বল, বাজারঘরের নীতি জানে না, আর ৩৬২টি জোড়া তাকে সেই নীতি লিখতে শেখায়নি। তাই বাস্তবে আগে SFT (পর্ব ৫৬) দিয়ে model-কে ঠিক ধাঁচে লিখতে শেখানো হয়, তারপর DPO দিয়ে ভালো আর খারাপের মধ্যে পছন্দ ধারালো করা হয়। আর “training-এর metric নিখুঁত” মানেই “model ভালো” নয়: policy-র উত্তর সব সময় আলাদা নিয়ম বা মানুষ দিয়ে মাপুন। ওপরের উত্তরগুলো যেমন এসেছে তেমনই রাখা, শুধু লম্বাগুলো “…” দিয়ে ছোট করা আর লাইন-ভাঙা এক লাইনে আনা।
৮ · Reward hacking আর over-optimisation
মেশিন লার্নিং কোর্সের পর্ব ২-এ Goodhart-এর নিয়ম দেখেছেন: একটি মাপ লক্ষ্য হয়ে গেলে সেটি আর ভালো মাপ থাকে না। RM মানুষের পছন্দের একটি অসম্পূর্ণ মাপ (proxy). Policy-কে সেটি বাড়াতে যত জোরে চাপ দেবেন, তত বেশি সে RM-এর ফাঁক খুঁজবে: এটিই reward hacking, বা over-optimisation.
Gao, Schulman ও Hilton (২০২২) এটি নিয়মিতভাবে মাপেন। মানুষের বদলে একটি বড় “gold” RM-কে আসল বিচারক ধরে তাঁরা তার label থেকে ছোট proxy RM শেখান, তারপর proxy-র বিরুদ্ধে RL বা best-of-n দিয়ে optimise করেন। প্রবন্ধের সারাংশ অনুযায়ী proxy বাড়তে থাকে, কিন্তু gold score একটা সময় পর কমে; আর এই সম্পর্ক RM-এর আকারের সঙ্গে মসৃণভাবে বদলায়। নিচে একই ধাঁচের একটি খেলনা: আমাদের proxy RM দৈর্ঘ্য পছন্দ করে, অথচ ৬০ শব্দের পর প্যাঁচাল আসল মান কমায়।
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(42)
P, N = 2000, 256 # ২০০০টি প্রশ্ন, প্রতিটিতে ২৫৬টি উত্তর
core = rng.normal(0, 1, (P, N)) # উত্তরের আসল মান (লুকানো)
length = 10 + rng.exponential(50, (P, N)) # শব্দ-সংখ্যা: বেশির ভাগ ছোট
true_q = core - ((length - 80) / 80) ** 2 # ~৮০ শব্দই আদর্শ, বেশি হলে প্যাঁচাল
proxy = 0.6 * core + 0.012 * length + rng.normal(0, 0.3, (P, N)) # RM: দৈর্ঘ্যে পক্ষপাত
print(" n │ proxy reward │ আসল মান │ গড় দৈর্ঘ্য")
res = []
for n in [1, 2, 4, 8, 16, 32, 64, 128, 256]:
best = proxy[:, :n].argmax(axis=1) # best-of-n: proxy-তে সেরাটি বাছাই
i = np.arange(P)
pr, tq, ln = proxy[i, best].mean(), true_q[i, best].mean(), length[i, best].mean()
res.append((n, pr, tq))
print(f"{n:4d} │ {pr:12.2f} │ {tq:7.2f} │ {ln:6.0f}")
k = max(res, key=lambda t: t[2])
print("আসল মান সবচেয়ে বেশি n =", k[0], "-তে; এরপর proxy বাড়ে, আসল মান কমে")
n, pr, tq = zip(*res)
fig, ax = plt.subplots(figsize=(5, 3))
ax.semilogx(n, pr, "o-", base=2, label="proxy reward (length-biased)")
ax.semilogx(n, tq, "s-", base=2, label="true quality")
ax.set_xlabel("best-of-n (optimisation pressure)"); ax.set_ylabel("mean score")
ax.set_title("Goodhart: optimising a proxy"); ax.legend(fontsize=8)
plt.show()আউটপুট (পাতার Python-এ চালানো):
n │ proxy reward │ আসল মান │ গড় দৈর্ঘ্য
1 │ 0.72 │ -0.44 │ 59
2 │ 1.23 │ -0.13 │ 76
4 │ 1.68 │ -0.09 │ 98
8 │ 2.12 │ -0.40 │ 125
16 │ 2.53 │ -0.86 │ 152
32 │ 2.93 │ -1.72 │ 183
64 │ 3.34 │ -3.08 │ 217
128 │ 3.76 │ -4.82 │ 252
256 │ 4.17 │ -6.86 │ 286
আসল মান সবচেয়ে বেশি n = 4 -তে; এরপর proxy বাড়ে, আসল মান কমে
n = ১ মানে কোনো বাছাই নেই: গড় দৈর্ঘ্য ৫৯, আসল মান −০.৪৪। অল্প চাপে (n = ২ থেকে ৪) proxy আর আসল মান দুটিই বাড়ে, কারণ proxy-তে আসল মানের অংশও আছে। n = ৪-এর পর proxy একটানা বাড়ে (৪.১৭ পর্যন্ত), কিন্তু আসল মান নামে −৬.৮৬-এ, আর গড় দৈর্ঘ্য ২৮৬ শব্দ: optimiser proxy-র সবচেয়ে সস্তা ফাঁক, দৈর্ঘ্য, খুঁজে নিয়েছে। এই উল্টো-U আকারই Gao ও সহলেখকেরা gold RM দিয়ে মেপেছিলেন; এখানের সংখ্যাগুলো বানানো খেলনার, কোনো model-এর নয়। Best-of-n নিজেও একটি বাস্তব কৌশল: training ছাড়াই অনেক উত্তর থেকে RM দিয়ে সেরাটি বাছা, আর একই বিপদ তাতেও আছে।
এটি সাজানো খেলনা, কিন্তু আমাদের সত্যিকারের RM-এও একই ফাঁক মাপা গেছে: শেখার আগে এলোমেলো score head-এর দুটি seed-এ ঠিক উত্তরের শেষে প্যাঁচাল জুড়লে ৮৬% আর ৯২% ক্ষেত্রে score বেড়েছিল (তৃতীয়টিতে ১%); ডেটায় প্যাঁচাল-ধরনের rejected থাকায় training-এর পর তা ০%. কিন্তু ডেটায় নেই এমন ফাঁক, যেমন অজানা ভুল সংখ্যা, থেকেই গেছে (অংশ ৪)। অংশ ৫-এর খেলনাতেও β = ০.০২-এ policy পুরোপুরি “ঠিক, লম্বা” উত্তরে গিয়েছিল, কারণ সেখানে RM লম্বা উত্তরকে সামান্য বেশি দিয়েছিল।
Reward hacking-এর পরিচিত রূপ: দৈর্ঘ্য আর সাজানো ফরম্যাট, আত্মবিশ্বাসী সুর, আর ব্যবহারকারীর মতের সঙ্গে একমত হওয়া (sycophancy; AI-এর ভিত্তি কোর্সের পর্ব ৭০)। DeepSeek-R1 প্রবন্ধে লেখকেরা R1-Zero-তে neural RM ব্যবহার না করার কারণ হিসেবে বড় মাপের RL-এ reward hacking-এর ঝুঁকিই লিখেছেন। প্রতিরোধের সাধারণ উপায়:
- KL-এর লাগাম আর early stopping: β আর ধাপের সংখ্যা দিয়ে চাপ সীমিত রাখা।
- Proxy-র বাইরে মাপা: আলাদা মানুষের মূল্যায়ন বা নিয়ম-ভিত্তিক যাচাই (যেমন আমাদের তথ্য-নিয়ম), proxy score নয়।
- জানা ফাঁক ডেটায় বন্ধ করা: “লম্বা কিন্তু ফাঁপা” উত্তরকে rejected হিসেবে রাখা, যা আমাদের ডেটার প্যাঁচাল-ধরন করে; বা দৈর্ঘ্যে normalise করা loss.
- RM হালনাগাদ: নতুন policy-র উত্তরে আবার তুলনা নিয়ে RM শেখানো, কারণ policy সরে গেলে পুরনো RM-এর জানা এলাকার বাইরে চলে যায়।
৯ · RLHF-এর পরিবার — Constitutional AI, RLAIF আর GRPO
Constitutional AI আর RLAIF. Anthropic-এর Bai ও সহলেখকদের প্রবন্ধ (arXiv, ডিসেম্বর ২০২২) ক্ষতিকর উত্তর চিহ্নিত করার মানুষের label প্রায় বাদ দেয়। মানুষের তত্ত্বাবধান আসে একটি ছোট নীতির তালিকা থেকে, যাকে লেখকেরা “constitution” বলেন। দুটি ধাপ: (১) supervised: model নিজের উত্তর নীতি ধরে সমালোচনা করে, সংশোধন করে, আর সংশোধিত উত্তরে fine-tune হয়; (২) RL: একটি model দুটি উত্তরের কোনটি ভালো তা বাছে, সেই AI-পছন্দ থেকে একটি preference model শেখে, আর সেটিই reward. এর নাম RLAIF (RL from AI Feedback). লেখকেরা জানান, এতে model ক্ষতিকর অনুরোধ এড়িয়ে না গিয়ে কেন আপত্তি তা ব্যাখ্যা করে। Anthropic-এর Claude-এর বর্তমান প্রশিক্ষণের পুরো বিবরণ এই প্রবন্ধে নেই; প্রবন্ধটি পদ্ধতির বর্ণনা, কোনো পণ্যের রেসিপি নয়। AI-এর বিচারকেরও পক্ষপাত আছে, যা পর্ব ৫৮ (LLM মূল্যায়ন — benchmark, LLM-as-judge আর তার ফাঁদ)-এ মাপা হবে।
GRPO. DeepSeek-এর Shao ও সহলেখকদের DeepSeekMath প্রবন্ধ (arXiv, ফেব্রুয়ারি ২০২৪) GRPO (Group Relative Policy Optimization) প্রস্তাব করে, PPO-র একটি রূপ। PPO-র value model সাধারণত policy-র সমান বড়, তাই memory-র বড় বোঝা। GRPO সেটি বাদ দেয়: প্রতিটি প্রশ্নে G টি উত্তর তুলে প্রতিটির reward ri, তারপর advantage = (ri − গড়)/std, অর্থাৎ দলের ভেতরে আপেক্ষিক মান। উদাহরণ: G = ৪, reward [১, ০, ০, ১] হলে গড় ০.৫, std ০.৫, advantage [+১, −১, −১, +১]। KL শাস্তি reward-এ না বসিয়ে সরাসরি loss-এ যোগ হয়। পরে DeepSeek-R1 প্রবন্ধ (arXiv, জানুয়ারি ২০২৫) জানায়, R1-Zero-তে GRPO চালানো হয়েছে নিয়ম-ভিত্তিক reward দিয়ে: উত্তর ঠিক কি না (গণিতে উত্তর মিলিয়ে, কোডে test চালিয়ে) আর ভাবনা নির্দিষ্ট tag-এর ভেতরে লেখা কি না।
| পদ্ধতি | পছন্দ বা reward কোথা থেকে | Training-এ নতুন উত্তর তোলে? | Memory-তে model |
|---|---|---|---|
| RLHF + PPO (InstructGPT) | মানুষের তুলনা → RM | হ্যাঁ (online) | policy, ref, RM, value |
| DPO | মানুষের বা অন্য উৎসের জোড়া, সরাসরি | না (offline) | policy, ref |
| RLAIF (Constitutional AI) | নীতি দেখে AI-এর তুলনা → preference model | হ্যাঁ | PPO-র মতো |
| GRPO (DeepSeekMath, R1) | RM বা যাচাইযোগ্য নিয়ম | হ্যাঁ, প্রশ্নপ্রতি G টি | policy, ref, (RM) |
DPO-র পর অনেক offline loss প্রস্তাবিত হয়েছে; লেখার সময়ে (নভেম্বর ২০২৬) trl 1.17-এর নথিতে loss_type-এর ১৫টি বিকল্প, যেমন IPO (identity transform) আর দৈর্ঘ্যে normalise করা SimPO-ধাঁচের sigmoid_norm. কোনটি সব কাজে ভালো, তার সাধারণ উত্তর নেই; online (PPO, GRPO) বনাম offline (DPO) নিয়ে গবেষকদের মধ্যেও মতভেদ আছে। Offline পদ্ধতি সস্তা আর স্থির, কিন্তু শুধু ডেটায় থাকা উত্তর থেকে শেখে; online পদ্ধতি নিজের নতুন উত্তর যাচাই করে শেখে, খরচ আর অস্থিরতা বেশি।
আরও গভীরে: DPO প্রবন্ধের Theorem 1 দেখায়, Bradley–Terry-র অধীনে যে-কোনো reward-এর সমতুল্য শ্রেণিতে এমন একটি সদস্য আছে যা β·log(π/πref) আকারে লেখা যায়; তাই এই reparameterisation কোনো reward বাদ দেয় না। প্রবন্ধের অংশ ৫.২ PPO-র অস্থিরতাকে ওই normalisation পদের (soft value function) উচ্চ variance দিয়ে ব্যাখ্যা করে। পরীক্ষায় তাঁরা GPT-2-large-এ IMDb sentiment, আর বড় model-এ Reddit TL;DR সারাংশ ও Anthropic HH সংলাপ ব্যবহার করেন, আর শেষ দুটির মান যাচাই করেন GPT-4-কে মানুষের বদলি বিচারক ধরে।
পরীক্ষা ও ইন্টারভিউতে:
- rw = ২, rl = ০ হলে BT loss? −ln σ(২) = −ln ০.৮৮০৮ ≈ ০.১২৬৯।
- DPO-তে শুরুতে loss কত, কেন? ln ২ ≈ ০.৬৯৩১, কারণ policy = reference হলে h = ০, σ(০) = ০.৫।
- KL শাস্তি কেন? RM-এর জানা এলাকার বাইরে গিয়ে ফাঁক খোঁজা (reward hacking) আর ভাষার মান নষ্ট হওয়া ঠেকাতে।
- DPO-তে কোন দুটি model লাগে? শেখার policy আর স্থির reference; আলাদা RM আর value model লাগে না।
- GRPO value model ছাড়া advantage পায় কীভাবে? একই প্রশ্নের G টি উত্তরের reward-কে দলের গড় আর std দিয়ে normalise করে।
১০ · AI-এর যুগে — alignment কোড আর দাবি যাচাই
DPO আর reward model-এর কোড AI assistant কয়েক সেকেন্ডে লেখে। কিন্তু trl এক বছরে কয়েকবার বড় বদল দেখেছে, আর alignment নিয়ে অনলাইনের লেখায় দাবি অনেক, মাপ কম।
| AI প্রায়ই যা করে | কেন ভুল | কীভাবে ধরবেন |
|---|---|---|
পুরনো trl argument (যেমন tokenizer=, DPOTrainer-এ সরাসরি beta=) | নতুন version-এ processing_class আর DPOConfig-এ beta | নিজের version-এর নথি; ছোট ডেটায় একবার চালানো |
| Reference model হিসেবে policy-র সঙ্গে ওজন ভাগ করা কপি | Reference বদলে গেলে h সব সময় ০-এর কাছে, শেখা থামে | ধাপ ০-এ loss = ln ২, rewards/chosen = ০ দেখা |
| Token-এর log-prob-এর গড়কে sequence log-prob বলা | মূল DPO-তে যোগফল; গড় নিলে ভিন্ন loss (দৈর্ঘ্য-normalised) | Loss হাতে হিসাব করে library-র সঙ্গে মেলানো |
| Prompt-এর token-কেও log-prob-এ ধরা | Chosen আর rejected-এর একই prompt, কিন্তু truncation-এ পার্থক্য আসে | Mask দেখা; শুধু উত্তরের token গোনা |
| “RM accuracy ৯৯%, তাই model aligned” | RM proxy; policy তার ফাঁক খোঁজে | Policy-র উত্তর আলাদা নিয়ম বা মানুষ দিয়ে মাপা |
| প্রবন্ধের সংখ্যা বা পদ্ধতি বানিয়ে লেখা | বিশ্বাসযোগ্য শোনায়, যাচাই নেই | arXiv-এর মূল প্রবন্ধ খুলে মেলানো |
কোড চাওয়ার সময় যাচাইও চেয়ে নিন:
AI-এর লেখা alignment কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১মানুষকে উত্তরের নম্বর দিতে না বলে দুটি উত্তরের তুলনা করতে বলা হয় কেন? এই ধারণার ইতিহাস কোথা থেকে?
নম্বর দেওয়া শুনতে সহজ, কিন্তু মানুষের নম্বর অস্থির। একজনের “৭” আরেকজনের “৫”, আর একই মানুষ সকালে আর ক্লান্ত বিকেলে ভিন্ন নম্বর দেন। তুলনায় এই একক-সমস্যা অনেকটা কেটে যায়: “A না B ভালো” প্রশ্নের উত্তরে কারও ব্যক্তিগত মাপকাঠি লাগে না। মনোবিজ্ঞানে জোড়ায় তুলনার মডেলের ইতিহাস পুরনো, আর Bradley ও Terry ১৯৫২ সালে প্রতিটি বস্তুর একটি লুকানো শক্তি ধরে জেতার সম্ভাবনার যে রূপ দেন, আজকের reward model ঠিক সেটিই ব্যবহার করে। খেলাধুলার রেটিং পদ্ধতিও একই ধাঁচের চিন্তা।
Machine learning-এ মানুষের তুলনা থেকে reward শেখার বহুল উদ্ধৃত কাজ Christiano ও সহলেখকদের ২০১৭ সালের প্রবন্ধ, যেখানে simulated robot-এর আচরণের ছোট ভিডিও-জোড়া দেখে মানুষ পছন্দ জানান। Stiennon ও সহলেখকেরা (২০২০) সারাংশ লেখায় একই পদ্ধতি নেন, আর InstructGPT (২০২২) এটিকে সাধারণ নির্দেশ মানায় নিয়ে আসে। InstructGPT-তে labeler একসঙ্গে ৪ থেকে ৯টি উত্তর সাজাতেন, যা থেকে অনেকগুলো জোড়া তৈরি হয়, ফলে একবারের পরিশ্রমে বেশি তথ্য।
তুলনারও সীমা আছে। দুটি উত্তরই খারাপ হলে “কম খারাপ” বাছা হয়, যা থেকে model ভালো উত্তর কী তা শেখে না। খুব কাছাকাছি দুটি উত্তরে মানুষের মতভেদ বাড়ে; InstructGPT-তে labeler-দের পারস্পরিক মিল প্রায় ৭৩%. আর তুলনা শুধু আপেক্ষিক তথ্য দেয়, তাই reward-এর শূন্য-বিন্দু ঠিক হয় না, যেটি আমরা কোডে দেখেছি।
মূল উপলব্ধি: তুলনা মানুষের অস্থির নম্বরের চেয়ে বেশি নির্ভরযোগ্য সংকেত দেয়, আর Bradley–Terry তাকে একটি সংখ্যায় রূপ দেয়; কিন্তু সেই সংখ্যা শুধু আপেক্ষিক, আর মানুষের মতভেদও তার মধ্যে ঢুকে থাকে।
প্র ০২DPO এত সরল হলে PPO-ভিত্তিক RLHF এখনো আলোচনায় কেন? দুটির মধ্যে বাছাই কীভাবে করবেন?
DPO-র সুবিধা স্পষ্ট। একটি loss, দুটি model, কোনো sampling নেই, hyperparameter কম। আমাদের run-এ ৩৬২টি জোড়ায় GPU-তে প্রতিটি run চার মিনিটের মতো। DPO প্রবন্ধ জানায়, তাঁদের পরীক্ষায় sentiment নিয়ন্ত্রণে DPO PPO-কে ছাড়িয়েছে আর সারাংশ ও সংলাপে সমান বা ভালো করেছে।
কিন্তু DPO offline: সে শুধু ডেটায় থাকা জোড়া থেকে শেখে। Policy training-এর সময় নতুন যে ধরনের উত্তর লিখতে শুরু করে, সেগুলোর ওপর কেউ রায় দেয় না। PPO বা GRPO online: policy নিজে উত্তর তোলে, reward পায়, আর নিজের ভুল থেকে শেখে। যেখানে reward যাচাই করা যায়, যেমন গণিতের উত্তর বা কোডের test, সেখানে online RL নতুন সমাধান খুঁজে পেতে পারে, যা কোনো ডেটাসেটে ছিল না; DeepSeek-R1-এর প্রবন্ধ এই দিকটিই সামনে আনে। আবার online পদ্ধতির খরচ বেশি, অস্থিরতা বেশি, আর RM থাকলে reward hacking-এর সুযোগও বেশি।
গবেষকদের মধ্যে কোনটি “ভালো” তা নিয়ে মতভেদ আছে, আর ফল অনেক সময় ডেটা, model-এর আকার আর কতটা যত্নে tuning করা হয়েছে তার ওপর নির্ভর করে। বাস্তবে প্রশ্নগুলো এমন: আপনার কাছে কি যাচাইযোগ্য reward আছে? নতুন উত্তর তোলার মতো compute আছে? ডেটা কি আপনার policy-রই উত্তর থেকে বানানো, নাকি অন্য model-এর? শেষটি DPO-তে গুরুত্বপূর্ণ, কারণ প্রবন্ধে reference-কে ডেটার উত্তরের বণ্টনের কাছে রাখার কথা বলা হয়েছে।
মূল উপলব্ধি: DPO একটি সস্তা, স্থির offline পদ্ধতি; PPO আর GRPO ব্যয়বহুল কিন্তু নিজের উত্তর থেকে শেখে। যাচাইযোগ্য reward আর compute থাকলে online, শুধু পছন্দের জোড়া থাকলে সাধারণত DPO দিয়ে শুরু করা যুক্তিসঙ্গত।
প্র ০৩“Aligned” model আসলে কার পছন্দের সঙ্গে aligned? বাংলাভাষী ব্যবহারকারীর জন্য এর অর্থ কী?
InstructGPT প্রবন্ধ নিজেই এই প্রশ্ন তোলে। তাঁদের labeler ছিলেন প্রায় ৪০ জন চুক্তিভিত্তিক কর্মী, একটি বাছাই-পরীক্ষার মাধ্যমে নির্বাচিত, আর prompt এসেছিল মূলত তাঁদের API-র ব্যবহারকারীদের কাছ থেকে। ডেটার ৯৬%-এর বেশি ইংরেজি। লেখকেরা পরিষ্কার লিখেছেন, model align হয়েছে এই নির্দিষ্ট গোষ্ঠীর পছন্দ, নির্দেশিকা আর গবেষকদের সিদ্ধান্তের সঙ্গে, “সব মানুষের মূল্যবোধ”-এর সঙ্গে নয়।
এর অর্থ কয়েক স্তরে। ভাষা: যে ভাষায় তুলনা কম, সেখানে reward model-এর অনুমান দুর্বল হতে পারে, আর ভদ্রতার সংকেতগুলো অনুবাদে হারায়। বাংলায় “আপনি”-র সম্মান বা সাপোর্টের উত্তরে “দুঃখিত” কতটা জরুরি, তা ইংরেজি labeler-এর রায়ে আসে না। প্রসঙ্গ: বিকাশ বা ক্যাশ অন ডেলিভারির মতো স্থানীয় বাস্তবতা ডেটায় না থাকলে model “ভালো” উত্তর বলতে অন্য দেশের দোকানের নিয়ম বোঝে। মূল্যবোধ: কোন প্রশ্নে উত্তর না দেওয়া উচিত, কোন বিষয় সংবেদনশীল, তা সমাজভেদে বদলায়।
Constitutional AI একটি পথ দেখায়: নীতিগুলো লিখিত আর প্রকাশ্য, তাই অন্তত তর্ক করা যায় কোন নীতি থাকা উচিত। বাজারঘরের মতো একটি প্রতিষ্ঠানের জন্য বাস্তব শিক্ষা হলো, নিজের গ্রাহক আর নিজের ভাষায় নিজের পছন্দের ডেটা আর মূল্যায়ন রাখা, আর কোন নির্দেশিকায় তুলনা করা হয়েছে তা নথিতে লেখা। তবু বাংলাদেশে এ ধরনের কাজ কতটা হচ্ছে, তার নির্ভরযোগ্য পরিসংখ্যান আমাদের জানা নেই।
মূল উপলব্ধি: Alignment সব সময় কোনো নির্দিষ্ট মানুষ, নির্দেশিকা আর ভাষার সঙ্গে। কার পছন্দ, কোন নির্দেশিকা, কোন ভাষা, তা না জেনে “aligned” শব্দটি অর্ধেক তথ্য দেয়।
প্র ০৪Reward hacking কি শুধু LLM-এর সমস্যা? reinforcement learning-এর ইতিহাস থেকে কী শেখা যায়?
না। Reinforcement learning-এ agent যা মাপা হয় তা-ই বাড়ায়, যা চাওয়া হয়েছিল তা নয়। গবেষকেরা বহু বছর ধরে এমন উদাহরণ জমিয়েছেন, যাকে প্রায়ই specification gaming বলা হয়: একটি নৌকা-দৌড়ের খেলায় agent দৌড় শেষ না করে একই জায়গায় ঘুরে ঘুরে বোনাস তুলেছিল, এমন উদাহরণ OpenAI-এর একটি পুরনো blog লেখায় বহুল উদ্ধৃত। নিয়মটি পুরনো: অর্থনীতিতে Goodhart-এর নিয়ম, আর সমাজবিজ্ঞানে কাছাকাছি পর্যবেক্ষণ আছে।
LLM-এ সমস্যাটি আরও সূক্ষ্ম, কারণ reward নিজেই একটি শেখা model, যা মানুষের অসম্পূর্ণ তুলনা থেকে এসেছে। মানুষ লম্বা, গোছানো আর আত্মবিশ্বাসী উত্তর পছন্দ করার প্রবণতা দেখালে RM সেটি শেখে, আর policy সেটি বাড়িয়ে তোলে। আমাদের ছোট run-এ এর একটি রূপ দেখা গেছে: আলাদা রাখা জোড়ায় ১০০% ঠিক করা RM-ও ঠিক উত্তরের একটি সংখ্যা বদলে দিলে তিনটি seed-এ ১১% থেকে ৩৬% ক্ষেত্রে ভুল সংস্করণটিকে সমান বা বেশি score দিয়েছে; সে তথ্য নয়, পরিচিত বাক্যের ধাঁচ শিখেছিল। Gao ও সহলেখকেরা (২০২২) দেখান, proxy RM যত ছোট বা ডেটা যত কম, তত আগে আসল মান কমতে শুরু করে।
DeepSeek-R1-এর লেখকেরা R1-Zero-তে neural RM বাদ দিয়ে নিয়ম-ভিত্তিক reward নিয়েছেন, কারণ হিসেবে reward hacking-এর ঝুঁকি লিখেছেন। কিন্তু নিয়মও ফাঁকি খাওয়া যায়: শুধু ফরম্যাট মেলালে নম্বর দিলে model ফরম্যাট শিখবে, বিষয় নয়। প্রতিরোধ তাই স্তরে স্তরে: KL-এর লাগাম, proxy-র বাইরে মূল্যায়ন, জানা ফাঁকের জন্য উল্টো উদাহরণ, আর মানুষের নিয়মিত নমুনা-পরীক্ষা।
মূল উপলব্ধি: যেকোনো optimiser মাপের ফাঁক খোঁজে। LLM-এ RM নিজেই শেখা এবং অসম্পূর্ণ, তাই চাপ যত বেশি, proxy আর আসল লক্ষ্য তত দূরে সরে; সমাধান একটিই নয়, কয়েক স্তরের পাহারা।
প্র ০৫DeepSeek-এর GRPO আর যাচাইযোগ্য reward কেন এত আলোচিত হলো? সব কাজে কি এটি চলে?
PPO-তে policy-র পাশাপাশি একটি value model শেখাতে হয়, যা সাধারণত policy-র সমান বড়, আর LLM-এ reward প্রায়ই শুধু শেষ token-এ আসে বলে প্রতিটি token-এর value ঠিকভাবে শেখা কঠিন। DeepSeekMath (২০২৪) এই সমস্যার সরল উত্তর দেয়: একই প্রশ্নে কয়েকটি উত্তর তুলে তাদের reward-কে দলের গড় আর std দিয়ে normalise করা। দলের গড়ই baseline, তাই value model লাগে না, memory কমে। পরীক্ষায় দেখার মতো: G = ৪, reward [১, ০, ০, ১] হলে advantage [+১, −১, −১, +১]।
২০২৫ সালের জানুয়ারিতে প্রকাশিত DeepSeek-R1 প্রবন্ধ জানায়, R1-Zero-তে GRPO চালানো হয়েছে মানুষের লেখা যুক্তির উদাহরণ ছাড়াই, শুধু নিয়ম-ভিত্তিক reward দিয়ে: উত্তর মিলল কি না, আর ফরম্যাট ঠিক কি না। লেখকেরা জানান, এতে model লম্বা যুক্তি লেখা আর নিজের উত্তর যাচাইয়ের মতো আচরণ দেখিয়েছে। এই দাবি প্রবন্ধের নিজের মূল্যায়ন; অন্যরা কতটা পুনরুৎপাদন করতে পেরেছেন, তা আলাদা করে দেখতে হয়।
সীমাটি নামেই আছে: “যাচাইযোগ্য”। গণিত, কোডের test, নির্দিষ্ট ফরম্যাটের উত্তর: এখানে নিয়ম লেখা যায়। কিন্তু বাজারঘরের গ্রাহককে সহানুভূতির সঙ্গে উত্তর দেওয়া, একটি গল্পের মান বা একটি চিকিৎসা-পরামর্শের নিরাপত্তা নিয়মে মাপা কঠিন। সেখানে আবার পছন্দের ডেটা, RM বা AI বিচারকের দরকার পড়ে, আর তাদের সব দুর্বলতাও ফিরে আসে। আমাদের run-এর তথ্য-নিয়মটিও একটি যাচাইযোগ্য reward-এর ছোট উদাহরণ: সংখ্যা মিলেছে কি না দেখে, ভদ্রতা নয়।
মূল উপলব্ধি: GRPO value model বাদ দিয়ে RL-কে সস্তা করে, আর যাচাইযোগ্য reward reward hacking-এর একটি বড় উৎস কমায়; কিন্তু যেখানে “ভালো” নিয়মে লেখা যায় না, সেখানে পছন্দ থেকে শেখাই এখনো প্রধান পথ।
অনুশীলন
খাতা-কলমে করুন, তারপর উত্তর মেলান। বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের হিসাব কাজে লাগে।
-
হিসাব করুন: (ক) একটি RM chosen-কে −০.৫ আর rejected-কে ০.৫ দিয়েছে। BT loss আর ∂L/∂rw কত? (খ) DPO-তে log πθ(yw) = −২০, log πref(yw) = −২২, log πθ(yl) = −২৫, log πref(yl) = −২৪, β = ০.১। h, implicit reward দুটি আর loss কত? (গ) GRPO-তে G = ৪, reward [২, ০, ১, ১] হলে advantage কত (std জনসংখ্যার, n দিয়ে ভাগ)?
(ক) Δ = −১; σ(−১) = ০.২৬৮৯; loss = −ln ০.২৬৮৯ = ১.৩১৩৩; ∂L/∂rw = −(১ − ০.২৬৮৯) = −০.৭৩১১, ক্রম ভুল বলে বড় ধাক্কা। (খ) chosen-এর log-ratio = −২০ + ২২ = ২, rejected-এর = −২৫ + ২৪ = −১; h = ৩; r̂w = ০.২, r̂l = −০.১; β·h = ০.৩, σ(০.৩) = ০.৫৭৪৪, loss = ০.৫৫৪৪। (গ) গড় ১, বিচ্যুতি [১, −১, ০, ০], variance = (১ + ১ + ০ + ০)/৪ = ০.৫, std = ০.৭০৭১; advantage = [১.৪১৪, −১.৪১৪, ০, ০]।
-
আউটপুট অনুমান করুন: কোড না চালিয়ে তিনটি লাইন লিখুন (দশমিকের পর ৪ ঘর)।
import numpy as np sig = lambda z: 1 / (1 + np.exp(-z)) rw, rl = np.array([1.0, 0.0]), np.array([0.0, 0.0]) print(round(float(-np.log(sig(rw - rl)).mean()), 4)) print(round(float(-np.log(sig(rw + 3 - (rl + 3))).mean()), 4)) h = 0.0 print([round(float(-np.log(sig(b * h))), 4) for b in (0.1, 1.0)])0.5032 0.5032 [0.6931, 0.6931]প্রথম জোড়া: −ln σ(১) = ০.৩১৩৩; দ্বিতীয়: −ln σ(০) = ০.৬৯৩১; গড় ০.৫০৩২। দুই পাশে ৩ যোগ করলে পার্থক্য একই, তাই loss হুবহু একই। DPO-তে h = ০ মানে policy = reference, loss β-নিরপেক্ষভাবে ln ২।
-
নকশা সমালোচনা করুন: একটি দলের পরিকল্পনা: “অন্য একটি বড় model দিয়ে ইংরেজিতে লেখা ২০০টি পছন্দের জোড়া বাংলায় যন্ত্র-অনুবাদ করব; chosen সব সময় লম্বা উত্তর। Base model-এ (SFT ছাড়া) β = ০.০১ দিয়ে ২০ epoch DPO চালাব। Train set-এ rewards/accuracies ১.০ হলেই চালু করব।” অন্তত পাঁচটি সমস্যা আর সমাধান বলুন।
- Chosen সব সময় লম্বা: model শিখবে “লম্বা = ভালো”, reward hacking-এর বীজ। সমাধান: দৈর্ঘ্যে ভারসাম্য, ছোট-কিন্তু-ঠিক chosen আর লম্বা-কিন্তু-ফাঁপা rejected রাখা।
- যন্ত্র-অনুবাদ আর অন্য model-এর উত্তর: ভাষার মান আর স্থানীয় প্রসঙ্গ হারায়; ডেটা policy-র নিজের বণ্টন থেকে দূরে। সমাধান: বাংলায় নিজের policy-র উত্তর থেকে জোড়া, মানুষের যাচাই; অন্য model-এর output-এর ব্যবহার-শর্তও দেখা।
- SFT ছাড়া base model: reference নির্দেশ মানে না, DPO-কে ফরম্যাটও শেখাতে হয়। সমাধান: আগে SFT (পর্ব ৫৬), তারপর DPO.
- β = ০.০১ আর ২০ epoch: লাগাম প্রায় নেই, ২০০ জোড়ায় overfit আর reference থেকে অনেক দূরে সরে যাওয়া। সমাধান: β ০.১–০.৫, ১–৩ epoch, কয়েকটি β মেপে বাছা।
- Train-এ accuracy দেখে চালু: মুখস্থ ধরা পড়ে না, আর implicit reward নিজেও একটি proxy মাপ। সমাধান: আলাদা রাখা জোড়া, policy-র নতুন উত্তর নিয়ম বা মানুষ দিয়ে মাপা, সাধারণ প্রশ্নে আগে-পরে তুলনা।
-
কোড লিখুন: NumPy-তে
dpo_loss(pw, pl, rw, rl, beta)লিখুন, যা চারটি sequence log-prob array থেকে গড় loss আর pairwise “implicit reward accuracy” ফেরত দেবে। অনুশীলন ১(খ)-এর সংখ্যায় যাচাই করুন।import numpy as np def dpo_loss(pw, pl, rw, rl, beta): h = (pw - rw) - (pl - rl) loss = np.logaddexp(0, -beta * h).mean() # = −log σ(βh), স্থিতিশীল acc = (h > 0).mean() return float(loss), float(acc) print(dpo_loss(np.array([-20.]), np.array([-25.]), np.array([-22.]), np.array([-24.]), 0.1)) # (0.5544..., 1.0)np.logaddexp(0, −x)= log(১ + e−x) = −log σ(x), বড় ঋণাত্মক x-এও overflow হয় না। Implicit reward accuracy মানে h > ০, অর্থাৎ policy reference-এর তুলনায় chosen-কে rejected-এর চেয়ে বেশি তুলেছে; trl-এরrewards/accuraciesএটিই।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমInstructGPT-ধাঁচের RLHF-এর ধাপগুলো সাজান (▲▼ চেপে সরান)
- Pretrained model-কে নমুনা উত্তরে SFT করা
- একই prompt-এর কয়েকটি উত্তর মানুষ দিয়ে তুলনা করানো
- তুলনা থেকে Bradley–Terry loss দিয়ে reward model শেখানো
- Reward model-এর score আর KL শাস্তি দিয়ে PPO চালানো
- নতুন উত্তর আলাদা মানুষের মূল্যায়নে মাপা
একটি reward model chosen উত্তরকে ৩ আর rejected-কে ১ দিয়েছে। Bradley–Terry অনুযায়ী chosen ভালো হওয়ার সম্ভাবনা কত?
- ০.৭৫
- প্রায় ০.৮৮
- ২
- ০.৫
একটি reward model-এর সব score-এ একই ধ্রুবক c যোগ করলে Bradley–Terry loss-এর কী হয়?
- c-এর সমান বাড়ে
- কমে যায়, কিন্তু accuracy বাড়ে
- c ঋণাত্মক হলেই বদলায়
- একটুও বদলায় না
DPO training শুরুর মুহূর্তে, যখন policy আর reference হুবহু এক, প্রতিটি জোড়ার loss কত?
- ln ২ ≈ ০.৬৯৩১
- ০
- β-এর ওপর নির্ভর করে
- অসীম
KL-সীমিত লক্ষ্য max E[r] − β·KL(π ‖ πref)-এ β খুব ছোট করলে সবচেয়ে সম্ভাব্য ফল কোনটি?
- Policy reference-এর আরও কাছে থাকে
- Reward model আরও নির্ভুল হয়
- Policy বেশি reward পায় কিন্তু অনেক সরে যায়, আর RM-এর ফাঁক কাজে লাগানোর ঝুঁকি বাড়ে
- KL শূন্যে নেমে আসে
GRPO PPO-র কোন অংশটি বাদ দিয়ে memory বাঁচায়, আর তার জায়গায় কী ব্যবহার করে?
- Reference model; তার জায়গায় কিছু লাগে না
- Value model; একই প্রশ্নের কয়েকটি উত্তরের reward-এর গড় আর std
- Policy model; reward model সরাসরি উত্তর লেখে
- নমুনা তোলা; তার জায়গায় স্থির পছন্দের জোড়া
একটি reward model আলাদা রাখা পছন্দের জোড়ায় ৯০% pairwise accuracy পেয়েছে, কিন্তু তার বিরুদ্ধে optimise করা policy-র উত্তর ক্রমেই লম্বা আর তথ্যহীন হচ্ছে। সবচেয়ে সম্ভাব্য ব্যাখ্যা কোনটি?
- ৯০% accuracy মানে RM নিখুঁত, তাই সমস্যা policy-র learning rate-এ
- Bradley–Terry loss নিজেই লম্বা উত্তর পছন্দ করে
- RM দৈর্ঘ্যের প্রতি পক্ষপাত শিখেছে, আর policy সেই ফাঁক কাজে লাগাচ্ছে
- KL শাস্তি খুব শক্ত ছিল
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- পছন্দের ডেটা = prompt, chosen, rejected; তুলনা নম্বরের চেয়ে স্থির, কিন্তু মতভেদ আর পক্ষপাতও বহন করে।
- BT loss = −log σ(rw − rl); gradient-এর মাপ σ(−Δ), শুধু পার্থক্যের অর্থ আছে।
- RLHF: SFT → RM → PPO, সঙ্গে KL শাস্তি β; সমাধান π* ∝ πref·exp(r/β).
- DPO: −log σ(β·h), h = chosen আর rejected-এর log-ratio-র পার্থক্য; শুরুতে loss ln ২, reward model আর sampling নেই।
- RM একটি proxy: চাপ বাড়ালে ফাঁক কাজে লাগে; proxy-র বাইরে মাপুন, β আর ধাপ সীমিত রাখুন।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৫৮ · LLM মূল্যায়ন — benchmark, LLM-as-judge আর তার ফাঁদপরবর্তী পর্বAligned model-কে কীভাবে সৎভাবে মাপবেন।
- ডিপ লার্নিং, NLP আর Generative AI · কোর্স হোম৯১টি পর্বপুরো কোর্সের রোডম্যাপ।
- পর্ব ৫৬ · Instruction tuningফিরে দেখুনDPO-র আগের ধাপ: SFT আর chat template.
- AI-এর ভিত্তি · পর্ব ৭০ · LLM কীভাবে কাজ করেপাশের ট্র্যাকPretraining, instruction tuning আর RLHF-এর সহজ পরিচয়।
- মেশিন লার্নিং · পর্ব ১৮ · MLE আর MAPপাশের ট্র্যাকBradley–Terry loss যে নীতি থেকে আসে।