এই পর্বে যা পাবেন
- Perceptron update, দুই স্তরের network-এ হাতে backprop, softmax আর cross-entropy-র gradient, parameter আর memory গোনা
- BatchNorm ও LayerNorm, AdamW-এর ধাপ, warmup + cosine schedule, dropout আর gradient clipping
- Convolution-এর output shape, receptive field, IoU, LSTM-এর এক ধাপ, attention, multi-head shape, positional encoding, BERT/GPT-এর parameter আর KV cache
- BPE merge, perplexity, BLEU ও chrF, temperature আর top-p, compute-এর হিসাব; LoRA, int8 quantization, token আর খরচ
- Cosine, BM25, recall@k, MRR, nDCG, PQ-এর memory; Gaussian KL, GAN-এর সেরা discriminator, ᾱt আর SNR, CFG, Fréchet distance
- TTFT, TPOT, throughput, Little-এর সূত্র, ১০ লাখ token-এ GPU-সেকেন্ড আর CLIP-এর logit scale
১ · পাতার নিয়ম, ভাগের মানচিত্র আর খাতার কৌশল
মডিউল ১৪-এ স্বাগতম। দর্জি শুধু “জামাটা ঢিলা” বলেন না; বুকের মাপ কত ইঞ্চি, কতটা কাটতে হবে, হাতে লিখে দেখান। Deep learning-এও “KV cache অনেক জায়গা খায়” বলা যথেষ্ট নয়; কত GiB, কোন সূত্রে, কোন সংখ্যা বদলালে কতটা কমে, হাতে দেখাতে পারা দরকার। বিশ্ববিদ্যালয়ের Deep Learning, NLP বা Generative AI কোর্সের পরীক্ষায়, চাকরির লিখিত পরীক্ষায় আর ML বা LLM engineer ইন্টারভিউতে এ ধরনের ছোট হিসাব প্রায়ই আসে। মডিউলের পরের পর্বগুলো অন্য দিক দেখবে: পর্ব ৮৮ (MCQ প্রশ্নব্যাংক), পর্ব ৮৯ (লিখিত ও মৌখিক প্রশ্ন), পর্ব ৯০ (Deep learning coding ইন্টারভিউ) আর পর্ব ৯১ (ধারণা ও system design ইন্টারভিউ)।
কোন ভাগে কী, কোন পর্ব ঝালাবেন
| ভাগ | মডিউল ও কী আছে | মূল পর্ব |
|---|---|---|
| ২ | ১–২: perceptron, backprop, softmax gradient, parameter, memory | ০২–০৪, ০৭, ০৯ |
| ৩ | ৩: BatchNorm, LayerNorm, AdamW, schedule, dropout, clipping | ১৩–১৭ |
| ৪ | ৪: conv shape, receptive field, IoU, depthwise separable | ২০, ২২, ২৫ |
| ৫ | ৫–৬: LSTM, attention, multi-head, positional encoding, parameter, KV cache | ২৯, ৩৩–৩৮, ৪৮, ৪৯ |
| ৬ | ৭–৮: BPE, perplexity, BLEU, chrF, temperature, top-p, compute | ৪০, ৪৫, ৪৬, ৫০, ৫১ |
| ৭ | ৯–১০: LoRA, quantization, memory, token আর খরচ | ৫৫, ৫৯, ৬৫ |
| ৮ | ১১: cosine, BM25, recall@k, MRR, nDCG, PQ | ৬৭, ৬৮, ৭০ |
| ৯ | ১২: KL ও ELBO, GAN, diffusion, CFG, Fréchet distance | ৭৬, ৭৭, ৭৯–৮১ |
| ১০ | ১৩: TTFT, TPOT, throughput, Little-এর সূত্র, খরচ, CLIP | ৮২, ৮৩, ৮৫ |
এই পাতার নিয়ম — প্রশ্নে অন্য কিছু না বললে
| বিষয় | নিয়ম |
|---|---|
| log | ln (একক nats); bits চাইলে log2; nDCG-এর ছাড়ে log2 |
| Parameter | bias-সহ: Linear(a, b)-এ a·b + b; LayerNorm(d)-এ ২d |
| Memory | KiB = ২10, MiB = ২20, GiB = ২30 byte; MB, GB মানে ১০6, ১০9; float32 ৪ byte, fp16/bf16 ২ byte |
| Shape | PyTorch-এর ক্রম: ছবি (B, C, H, W), sequence (B, T, d) |
| ধাপ | “এক SGD ধাপ” মানে θ ← θ − η × gradient; η (eta) হলো learning rate |
| দশমিক | মাঝের ধাপে ৪–৫ ঘর, শেষ উত্তর প্রশ্ন যত ঘর চায় |
১. দেওয়া তথ্য এক লাইনে। “L = ২৪, d = ২০৪৮, T = ৪০৯৬, fp16” লিখে নিলে পরের ধাপে ভুল সংখ্যা বসে না।
২. Shape লিখে চলুন। প্রতিটি গুণের পাশে (B, T, d) × (d, d) = (B, T, d) লিখলে অর্ধেক ভুল আগেই ধরা পড়ে।
৩. সূত্র আগে, মান পরে। হিসাবে ভুল হলেও সূত্র দেখে পরীক্ষক যুক্তি বোঝেন।
৪. এক লাইনের যাচাই। Softmax-এর যোগ ১, p − y-এর যোগ ০, GPT-2-এর সূত্রে ১২,৪৪,৩৯,৮০৮, dropout-এর পরে গড় একই, AdamW-এর প্রথম ধাপ প্রায় lr.
৫. অর্থ এক লাইনে। “৬ GiB” লিখেই থামবেন না; বলুন, “GQA-তে ৪টি KV head রাখলে ১.৫ GiB, তাই একই GPU-তে চার গুণ অনুরোধ।”
২ · মডিউল ১–২ — perceptron, backprop, softmax আর parameter গোনা
ঝালিয়ে নিন: পর্ব ০২ · Neuron আর perceptron, পর্ব ০৩ · NumPy-তে MLP, পর্ব ০৪ · Backpropagation হাতে-কলমে, পর্ব ০৭ · PyTorch tensor, পর্ব ০৯ · nn.Module.
Perceptron (±১ label, শেষ ঘরে bias): y·(w̃·x̃) ≤ ০ হলে w̃ ← w̃ + y·x̃. Backprop: ½(ŷ − y)2-এর local gradient ŷ − y; ReLU-তে z ≤ ০ হলে gradient ০. Softmax + cross-entropy: ∂L/∂z = p − y; label smoothing-এ লক্ষ্য q = (১ − ε)y + ε/K. Linear(a, b): a·b + b. He init: std = √(২/fan_in).
২.১(সহজ) Perceptron-এ w̃ = (০.৫, −১, ০), শেষ ঘরটি bias-এর। নিয়ম: y·(w̃·x̃) ≤ ০ হলে w̃ ← w̃ + y·x̃ (η = ১)। পরপর দুটি উদাহরণ আসে: x̃ = (২, ১, ১), y = +১; তারপর x̃ = (১, ৩, ১), y = −১. (ক) প্রতিটিতে margin m আর নতুন w̃? (খ) প্রথমটিতে m = ০ হলেও update কেন? (ঝালিয়ে নিন: পর্ব ০২)
(a) ex 1: w.x = 1 - 1 + 0 = 0 ; m = +1 x 0 = 0 <= 0 -> update
w = (0.5, -1, 0) + (2, 1, 1) = (2.5, 0, 1)
ex 2: w.x = 2.5 + 0 + 1 = 3.5 ; m = -1 x 3.5 = -3.5 -> update
w = (2.5, 0, 1) - (1, 3, 1) = (1.5, -3, 0)
(b) "<=" counts a point on the boundary as a mistake
সীমানায় থাকা বিন্দুকেও ভুল না ধরলে শূন্য weight থেকে শুরু করা perceptron কখনো নড়ত না, কারণ শুরুতে সব m = ০। শূন্য থেকে শুরু করলে η-এর মান সিদ্ধান্ত বদলায় না; প্রতিটি w̃ শুধু η গুণ হয়।
২.২-এর network — দুই input, দুই ReLU hidden unit, একটি linear output
Numbers on the arrows are weights; values inside the circles come from the forward pass
২.২(মাঝারি) x = (১, ২)। Hidden unit h1: weight (০.৫, −০.২৫), bias ০.১; h2: weight (−১, ০.২৫), bias ০.১; দুটিতেই ReLU. Output ŷ = ২h1 − ১h2 + ০.৫ (linear). লক্ষ্য y = ১.৫, loss L = ½(ŷ − y)2. (ক) Forward pass: z, h, ŷ, L? (খ) সব weight আর bias-এর gradient? (গ) η = ০.০৫-এ এক SGD ধাপের পরে নতুন ŷ আর L? (ঘ) η = ০.১ হলে কী হয়? (ঝালিয়ে নিন: পর্ব ০৩, ০৪)
(a) z1 = 0.5 - 0.5 + 0.1 = 0.1 -> h1 = 0.1
z2 = -1 + 0.5 + 0.1 = -0.4 -> h2 = 0
yhat = 2(0.1) - 0 + 0.5 = 0.7 ; L = 0.5(0.7 - 1.5)^2 = 0.32
(b) d = yhat - y = -0.8
dL/dv = d x h = (-0.08, 0) ; dL/dc = -0.8
dL/dh = d x v = (-1.6, 0.8) ; ReLU gate -> dL/dz = (-1.6, 0)
dL/dW1 row1 = -1.6 x (1, 2) = (-1.6, -3.2) ; row2 = (0, 0)
dL/db1 = (-1.6, 0)
(c) W1 row1 = (0.58, -0.09), b1 = (0.18, 0.1), v = (2.004, -1), c = 0.54
z1 = 0.58 - 0.18 + 0.18 = 0.58 ; yhat = 2.004 x 0.58 + 0.54 = 1.7023
L = 0.5(0.2023)^2 = 0.0205
(d) eta = 0.1 -> yhat = 2.7085, L = 0.7302 (overshoot, loss rises)
h2-এর dL/dh = ০.৮ হলেও ReLU-র দরজা বন্ধ, তাই তার weight-এ gradient শূন্য: এটিই “dead ReLU”-র ছোট রূপ। (ঘ)-তে ভুলের দিক ঠিক ছিল, পা বড়: ŷ-কে ০.৮ বাড়ানো দরকার ছিল, ধাপটি বাড়াল প্রায় ২.০. নিচের stepper ধাপগুলো দেখায়।
z1 = 0.5 - 0.5 + 0.1 = 0.1
z2 = -1 + 0.5 + 0.1 = -0.4
h = (0.1, 0)
yhat = 0.7, L = 0.32
d = 0.7 - 1.5 = -0.8
dv = d x h = (-0.08, 0)
dc = -0.8
dh = d x v = (-1.6, 0.8)
ReLU: z2 < 0 -> 0
dz = (-1.6, 0)
dW1 row1 = (-1.6, -3.2)
W1 row1 = (0.58, -0.09)
b1 = (0.18, 0.1)
v = (2.004, -1), c = 0.54
yhat 1.7023, L 0.0205
- সামনে চলা। h1 = ০.১ টিকে থাকে, h2-এর z = −০.৪ বলে ReLU শূন্য দেয়। ŷ = ০.৭, loss ০.৩২.
- শেষ থেকে শুরু। ŷ − y = −০.৮; ঋণাত্মক মানে ŷ বাড়ালে loss কমবে। Output weight-এর gradient d × h.
- Hidden-এ ভাগ। d × v = (−১.৬, ০.৮), কিন্তু ReLU-র দরজা বন্ধ থাকায় h2-এর অংশ শূন্য। W1-এর প্রথম সারির gradient −১.৬ × x.
- এক ধাপ। η = ০.০৫-এ সব parameter gradient-এর উল্টো দিকে সরে; ŷ = ১.৭০২৩, loss ০.৩২ থেকে ০.০২০৫.
২.৩(মাঝারি) তিন শ্রেণির logits z = (২, ১, ০.১), সঠিক শ্রেণি প্রথমটি। (ক) Softmax p, cross-entropy L আর ∂L/∂z? (খ) সঠিক শ্রেণি তৃতীয়টি হলে L কত? (গ) Label smoothing ε = ০.১ দিলে লক্ষ্য q, loss আর gradient? (ঝালিয়ে নিন: পর্ব ০৪, ১৩)
(a) subtract max: (0, -1, -1.9) ; e^ = (1, 0.3679, 0.1496) ; sum 1.5175
p = (0.6590, 0.2424, 0.0986)
L = -ln 0.6590 = 0.4170
dL/dz = p - y = (-0.3410, 0.2424, 0.0986) (sums to 0)
(b) L = -ln 0.0986 = 2.3170
(c) q = (1 - 0.1 + 0.1/3, 0.1/3, 0.1/3) = (0.9333, 0.0333, 0.0333)
L = -sum q ln p = 0.5137 ; grad = p - q = (-0.2743, 0.2091, 0.0652)
Gradient-এর প্রতিটি ঘর −১ থেকে ১-এর মধ্যে, আর যোগফল শূন্য: একটি logit বাড়ানো মানে বাকিদের থেকে ভাগ কেড়ে নেওয়া। Label smoothing-এ smoothed loss কখনো শূন্যে নামে না; তাই training log-এ loss একটি মেঝেতে থামলে ভয়ের কিছু নেই।
২.৪(সহজ) একটি MLP: ৭৮৪ → ২৫৬ → ১২৮ → ১০, সব স্তরে bias. (ক) প্রতিটি স্তর আর মোট parameter? (খ) float32-এ weight-এর memory? (গ) AdamW-এ training-এর সময় weight, gradient আর দুটি moment, সব float32: কত byte? (ঘ) প্রথম স্তরে He init-এর std? (ঝালিয়ে নিন: পর্ব ০৩, ০৯, ১৪)
(a) 784 x 256 + 256 = 200,960
256 x 128 + 128 = 32,896
128 x 10 + 10 = 1,290 total 235,146
(b) 235,146 x 4 = 940,584 bytes = 0.94 MB
(c) 4 copies x 4 bytes = 16 bytes/param -> 3,762,336 bytes = 3.76 MB
(d) sqrt(2/784) = 0.0505
৮৫%-এর বেশি parameter প্রথম স্তরেই, কারণ input চওড়া। (গ)-র “parameter প্রতি ১৬ byte” বড় model-এ খুব কাজের: ১৫০ কোটির model পুরো fine-tune করতে শুধু এই চার কপিতেই প্রায় ২৪ GB, activation বাদে।
২.৫(সহজ) (ক) Shape (৩২, ১, ৫) আর (৪, ১)-এর tensor যোগ করলে broadcasting-এর পরে shape কী? (খ) ছবির একটি batch (৬৪, ৩, ২২৪, ২২৪): float32-এ কত MiB, fp16-এ কত? (ঝালিয়ে নিন: পর্ব ০৭)
(a) align right: (32, 1, 5) and (1, 4, 1) -> (32, 4, 5)
(b) 64 x 3 x 224 x 224 = 9,633,792 values
float32: x 4 / 2^20 = 36.75 MiB ; fp16: 18.375 MiB
Broadcasting ডান দিক থেকে মেলায়; যে অক্ষে ১, সেটি প্রসারিত হয়। (ক)-র মতো অজান্তে বড় shape তৈরি হলে loss ঠিকঠাক ছাপে, কিন্তু ভুল জিনিস গড় করে: পর্ব ১৮-এর পরিচিত নীরব ভুল।
import numpy as np
np.set_printoptions(precision=4, suppress=True)
w = np.array([0.5, -1, 0.0]) # 2.1
for x, y in [((2, 1, 1), 1), ((1, 3, 1), -1)]:
x = np.array(x, float); m = y * w @ x
if m <= 0: w = w + y * x
print("2.1 m", m, "-> w", w)
x = np.array([1., 2]); W1 = np.array([[0.5, -0.25], [-1, 0.25]]) # 2.2
b1, v, c, y = np.array([0.1, 0.1]), np.array([2., -1]), 0.5, 1.5
z = W1 @ x + b1; h = np.maximum(z, 0); yh = v @ h + c; d = yh - y
dz = d * v * (z > 0); dW1 = np.outer(dz, x)
print("2.2 z", z, "yhat", round(yh, 4), "L", round(0.5 * d**2, 4), "dv", d * h, "dz", dz, "dW1", dW1.tolist())
for eta in (0.05, 0.1):
W, b, vn, cn = W1 - eta * dW1, b1 - eta * dz, v - eta * d * h, c - eta * d
y2 = vn @ np.maximum(W @ x + b, 0) + cn
print(f" eta {eta}: yhat {y2:.4f} L {0.5 * (y2 - y)**2:.4f}")
zl = np.array([2., 1, 0.1]); p = np.exp(zl - zl.max()); p /= p.sum() # 2.3
q = np.array([0.9 + 0.1 / 3, 0.1 / 3, 0.1 / 3])
print("2.3 p", p, "L", round(-np.log(p[0]), 4), "grad", p - [1, 0, 0], "L(class 3)", round(-np.log(p[2]), 4))
print(" smooth L", round(-(q * np.log(p)).sum(), 4), "grad", p - q)
dims = [784, 256, 128, 10]; n = [a * b + b for a, b in zip(dims, dims[1:])] # 2.4
print("2.4", n, "total", sum(n), "fp32", 4 * sum(n), "train", 16 * sum(n), "He std", round(np.sqrt(2 / 784), 4))
print("2.5", np.broadcast_shapes((32, 1, 5), (4, 1)), "MiB", 64 * 3 * 224 * 224 * 4 / 2**20, 64 * 3 * 224 * 224 * 2 / 2**20)প্রথম দুই লাইনে perceptron-এর w [2.5 0. 1. ] আর [ 1.5 -3. 0. ]. Backprop-এ yhat 0.7 L 0.32, dz [-1.6 0. ], dW1 [[-1.6, -3.2], [0.0, 0.0]]; η = ০.০৫-এ yhat 1.7023 L 0.0205, η = ০.১-এ L 0.7302. Softmax p [0.659 0.2424 0.0986], L 0.417, তৃতীয় শ্রেণিতে 2.317, smoothing-এ 0.5137. MLP-এ total 235146, training-এ 3762336 byte, He std 0.0505; শেষে (32, 4, 5), 36.75 আর 18.375 MiB.
৩ · মডিউল ৩ — normalization, optimizer, schedule আর regularization
ঝালিয়ে নিন: পর্ব ১৩ · Deep learning-এর loss, পর্ব ১৪ · Initialization আর normalization, পর্ব ১৫ · Optimizer আর schedule, পর্ব ১৬ · Regularization, পর্ব ১৭ · Vanishing আর exploding gradient.
BatchNorm: প্রতিটি feature-এ batch-এর গড় আর variance (n দিয়ে ভাগ), x̂ = (x − μ)/√(σ2 + ε), y = γx̂ + β; running_var-এ unbiased (n − ১) variance, momentum ০.১. LayerNorm: একই কাজ প্রতিটি সারির feature জুড়ে। AdamW: আগে θ ← θ(১ − ηλ), তারপর Adam-এর ধাপ η·m̂/(√v̂ + ε)। Warmup: ηt = ηmax(t + ১)/W; তারপর cosine: ½ηmax(১ + cos(π(t − W)/(T − W))). Inverted dropout: টিকে থাকা মান ১/(১ − p) গুণ। Clipping: ‖g‖ > c হলে g ← g·c/‖g‖.
৩.১(মাঝারি) একটি feature-এর batch x = (১, ২, ৪, ৯), BatchNorm-এ γ = ২, β = ০.৫, ε উপেক্ষা করুন। (ক) μ, σ2, x̂ আর y? (খ) শুরুতে running_mean = ০, running_var = ১, momentum ০.১: এক batch পরে দুটির মান? (গ) Eval mode-এ x = ৬-এর output? (ঝালিয়ে নিন: পর্ব ১৪)
(a) mu = 16/4 = 4 ; dev = (-3, -2, 0, 5) ; var = (9+4+0+25)/4 = 9.5
xhat = dev / 3.0822 = (-0.9733, -0.6489, 0, 1.6222)
y = 2 xhat + 0.5 = (-1.4467, -0.7978, 0.5, 3.7444)
(b) running_mean = 0.9 x 0 + 0.1 x 4 = 0.4
unbiased var = 38/3 = 12.6667 ; running_var = 0.9 x 1 + 0.1 x 12.6667 = 2.1667
(c) eval: 2 (6 - 0.4)/sqrt(2.1667) + 0.5 = 8.1089
Training-এ batch-এর নিজের পরিসংখ্যান, eval-এ জমানো running পরিসংখ্যান: একই x-এ দুই mode-এ ভিন্ন output. মাত্র এক batch পরে running মান এখনো শুরুর মানের কাছে, তাই এখানে eval output অনেক বড়। model.eval() ভুলে গেলে বা খুব অল্প ধাপ train করলে এই অমিলই ধরা পড়ে।
৩.২(মাঝারি) X-এর দুটি সারি (দুটি উদাহরণ) (১, ২, ৬) আর (৩, ৬, ২)। γ = ১, β = ০, ε উপেক্ষা। (ক) BatchNorm (প্রতিটি কলাম জুড়ে) করলে কী আসে? (খ) LayerNorm (প্রতিটি সারি জুড়ে)? (গ) (ক)-র ফল থেকে batch আকার ২-এর BatchNorm নিয়ে কী শিখলেন? (ঝালিয়ে নিন: পর্ব ১৪, ৩৫)
(a) col means (2, 4, 4), col vars (1, 4, 4)
BN = [[-1, -1, 1], [1, 1, -1]]
(b) row 1: mean 3, var 14/3 = 4.6667 -> (-0.9258, -0.4629, 1.3887)
row 2: mean 3.6667, var 2.8889 -> (-0.3922, 1.3728, -0.9806)
(c) with 2 rows every column becomes exactly -1 or +1
দুটি সারি থাকলে BatchNorm শুধু বলে “কোনটি বড়”, মানের তথ্য মুছে যায়; ছোট batch-এ BatchNorm তাই অস্থির। LayerNorm প্রতিটি উদাহরণ নিজে normalize করে, batch-এর ওপর নির্ভর করে না; এ কারণেই Transformer আর sequence model-এ LayerNorm চলে।
৩.৩(মাঝারি) একটি weight θ = ১.০, AdamW: lr = ০.০১, β1 = ০.৯, β2 = ০.৯৯৯, ε = 1e-8, weight decay λ = ০.০১ (decoupled: আগে θ ← θ(১ − lr·λ))। প্রথম ধাপে gradient g = ০.৫, দ্বিতীয় ধাপে g = −০.৫. (ক) দুই ধাপে m, v, m̂, v̂, ধাপের মাপ আর θ? (খ) প্রথম ধাপে g = ৫০ হলে ধাপের মাপ? (ঝালিয়ে নিন: পর্ব ১৫)
(a) t=1: theta = 1 x (1 - 0.0001) = 0.9999
m = 0.05 ; v = 0.00025 ; mhat = 0.05/0.1 = 0.5 ; vhat = 0.00025/0.001 = 0.25
step = 0.01 x 0.5/0.5 = 0.01 -> theta = 0.9899
t=2: theta = 0.9899 x 0.9999 = 0.98980
m = 0.045 - 0.05 = -0.005 ; v = 0.00049975
mhat = -0.005/0.19 = -0.02632 ; vhat = 0.00049975/0.001999 = 0.25
step = 0.01 x (-0.02632)/0.5 = -0.000526 -> theta = 0.990327
(b) mhat/sqrt(vhat) = 50/50 = 1 -> step = 0.01 (same as before)
Bias correction-এর কারণে প্রথম ধাপের মাপ gradient-এর আকার যা-ই হোক, প্রায় lr: Adam মাপে না, দিক আর ধারাবাহিকতা দেখে। দ্বিতীয় ধাপে gradient উল্টো হওয়ায় momentum প্রায় কাটাকাটি, ধাপ ছোট। Adam-এর derivation মেশিন লার্নিং কোর্সের পর্ব ১৪-এ।
৩.৪(সহজ) Warmup + cosine: মোট T = ১০০০ ধাপ, ηmax = ৩ × ১০−৪, warmup W = ১০০, ηmin = ০, t গোনা ০ থেকে। (ক) t = ০, ৪৯, ৫৫০, ৭৭৫-এ learning rate? (খ) Step decay: η = ০.১, প্রতি ৩০ epoch-এ ১০ ভাগের ১; epoch ৬৫-এ η? (ঝালিয়ে নিন: পর্ব ১৫)
(a) t=0: 3e-4 x 1/100 = 3e-6
t=49: 3e-4 x 50/100 = 1.5e-4
t=550: f = 450/900 = 0.5 -> 0.5 x 3e-4 x (1 + cos(pi/2)) = 1.5e-4
t=775: f = 0.75 -> 0.5 x 3e-4 x (1 + cos(0.75 pi)) = 0.5 x 3e-4 x 0.2929 = 4.39e-5
(b) floor(65/30) = 2 -> 0.1 x 0.1^2 = 0.001
Cosine-এর অর্ধেক পথে (f = ০.৫) lr ঠিক অর্ধেক, তিন-চতুর্থাংশে প্রায় ১৫%. Warmup শুরুতে Adam-এর অস্থির v̂-কে সময় দেয়।
৩.৫(সহজ) (ক) Activation h = (১, ২, ০, ৪, ০.৫), inverted dropout p = ০.৫, এবারের mask (১, ০, ১, ১, ০): train-এ output কী? Eval-এ? (খ) p = ০.২ হলে গুণক? (গ) Gradient g = (৩, ৪, ১২), max_norm = ৫-এ clip করলে? (ঘ) Sigmoid-এর derivative সর্বোচ্চ ০.২৫; ১০টি স্তরে এই সর্বোচ্চ মানের গুণফল? (ঝালিয়ে নিন: পর্ব ১৬, ১৭)
(a) train: h x mask / (1 - 0.5) = (2, 0, 0, 8, 0) ; eval: h unchanged
(b) 1/(1 - 0.2) = 1.25
(c) ||g|| = sqrt(9 + 16 + 144) = 13 > 5 -> g x 5/13 = (1.1538, 1.5385, 4.6154)
(d) 0.25^10 = 9.54e-7
Dropout-এ প্রত্যাশিত মান একই থাকে, তাই eval-এ কিছু গুণ করতে হয় না। Clipping দিক রাখে, শুধু দৈর্ঘ্য ছোট করে। (ঘ) দেখায় কেন গভীর sigmoid network-এ প্রথম স্তর প্রায় শেখে না: vanishing gradient; ReLU আর residual connection এর প্রতিকার।
import numpy as np
np.set_printoptions(precision=4, suppress=True)
x = np.array([1., 2, 4, 9]); mu, var = x.mean(), x.var() # 3.1
xh = (x - mu) / np.sqrt(var + 1e-5); rm, rv = 0.1 * mu, 0.9 + 0.1 * x.var(ddof=1)
print("3.1 mean", mu, "var", var, "xhat", xh, "y", 2 * xh + 0.5)
print(" running", rm, round(rv, 4), "eval y(6)", round(2 * (6 - rm) / np.sqrt(rv + 1e-5) + 0.5, 4))
X = np.array([[1., 2, 6], [3, 6, 2]]) # 3.2
nrm = lambda A, ax: (A - A.mean(ax, keepdims=True)) / np.sqrt(A.var(ax, keepdims=True) + 1e-5)
print("3.2 BN", nrm(X, 0).round(4).tolist(), "\n LN", nrm(X, 1).round(4).tolist())
th, m, v = 1.0, 0.0, 0.0 # 3.3
for t, g in [(1, 0.5), (2, -0.5)]:
th *= 1 - 0.01 * 0.01
m = 0.9 * m + 0.1 * g; v = 0.999 * v + 0.001 * g * g
step = 0.01 * (m / (1 - 0.9**t)) / (np.sqrt(v / (1 - 0.999**t)) + 1e-8); th -= step
print(f"3.3 t={t}: m {m:.4f} v {v:.8f} step {step:.6f} theta {th:.6f}")
T, peak, W = 1000, 3e-4, 100 # 3.4
lr = lambda t: peak * (t + 1) / W if t < W else 0.5 * peak * (1 + np.cos(np.pi * (t - W) / (T - W)))
print("3.4", " ".join(f"t={t}:{lr(t):.3e}" for t in (0, 49, 550, 775)), "| step decay", round(0.1 * 0.1 ** (65 // 30), 6))
h = np.array([1., 2, 0, 4, 0.5]); mask = np.array([1, 0, 1, 1, 0]) # 3.5
g = np.array([3., 4, 12]); gn = np.linalg.norm(g)
print("3.5 dropout", h * mask / 0.5, "| norm", gn, "clipped", g * min(1, 5 / gn), "| 0.25^10", 0.25**10)BatchNorm-এ var 9.5, y [-1.4467 -0.7978 0.5 3.7444], running মান 0.4 2.1667 আর eval-এ 8.1089. দুই সারির BatchNorm-এ সব ঘর ±1.0, LayerNorm-এ [-0.9258, -0.4629, 1.3887] ও [-0.3922, 1.3728, -0.9806]. AdamW-এর দুই ধাপে theta 0.989900 আর 0.990327. Schedule-এ 3.000e-06, 1.500e-04, 1.500e-04, 4.393e-05, step decay 0.001. শেষ লাইনে [2. 0. 0. 8. 0.], norm 13.0, [1.1538 1.5385 4.6154] আর 9.5367431640625e-07.
৪ · মডিউল ৪ — convolution-এর shape, receptive field আর IoU
ঝালিয়ে নিন: পর্ব ২০ · Convolution, পর্ব ২১ · CNN-এর স্থাপত্য, পর্ব ২২ · ResNet আর আধুনিক CNN, পর্ব ২৫ · Detection আর segmentation.
Output = ⌊(n + ২p − d(k − ১) − ১)/s⌋ + ১, যেখানে k kernel, s stride, p padding, d dilation. Conv-এর parameter: Cout·Cin·kH·kW + Cout. Receptive field (শুরু r = ১, j = ১): r ← r + (k − ১)·j, j ← j·s. IoU = ছেদ ÷ (A + B − ছেদ)। Depthwise separable-এর খরচ standard-এর ১/Cout + ১/k2 ভাগ।
৪.১(সহজ) Input (৩, ২২৪, ২২৪)। প্রথমে ৬৪টি ৭ × ৭ filter, stride ২, padding ৩; তারপর ৩ × ৩ max pool, stride ২, padding ১; তারপর ৩ × ৩ conv, stride ১, dilation ২, padding ২. (ক) প্রতিটির পরে H = W কত? (খ) প্রথম conv-এর parameter? (গ) প্রথম conv-এ গুণ-যোগ (MAC)? (ঘ) ৩২ × ৩২ ছবিতে ৫ × ৫ kernel, stride ১, padding ০-এ output? (ঝালিয়ে নিন: পর্ব ২০)
(a) conv: floor((224 + 6 - 6 - 1)/2) + 1 = 111 + 1 = 112
pool: floor((112 + 2 - 2 - 1)/2) + 1 = 55 + 1 = 56
dilated: effective k = 2(3 - 1) + 1 = 5 ; floor((56 + 4 - 4 - 1)/1) + 1 = 56
(b) 64 x 3 x 7 x 7 + 64 = 9,472
(c) 64 x 112 x 112 x (3 x 49) = 118,013,952 MAC
(d) floor((32 - 4 - 1)/1) + 1 = 28
Parameter মাত্র ৯,৪৭২, অথচ গুণ-যোগ প্রায় ১১.৮ কোটি: conv-এর weight কম, কিন্তু প্রতিটি অবস্থানে আবার ব্যবহার হয়। তাই CNN-এ memory আর compute-এর হিসাব আলাদা করে করতে হয়।
৪.২(মাঝারি) স্তরের ক্রম: conv ৩ × ৩ (s = ১), conv ৩ × ৩ (s = ১), pool ২ × ২ (s = ২), conv ৩ × ৩ (s = ১), conv ৩ × ৩ (s = ২)। (ক) প্রতিটি স্তরের পরে receptive field r আর jump j? (খ) ৩২ channel-এ (input আর output দুটিই) দুটি ৩ × ৩ conv বনাম একটি ৫ × ৫ conv-এর parameter (bias-সহ)? (ঝালিয়ে নিন: পর্ব ২০, ২১)
(a) start r = 1, j = 1
conv3 s1: r = 1 + 2x1 = 3, j = 1
conv3 s1: r = 3 + 2x1 = 5, j = 1
pool2 s2: r = 5 + 1x1 = 6, j = 2
conv3 s1: r = 6 + 2x2 = 10, j = 2
conv3 s2: r = 10 + 2x2 = 14, j = 4
(b) two 3x3: 2 x (9 x 32 x 32 + 32) = 18,496 ; one 5x5: 25 x 32 x 32 + 32 = 25,632
Stride-এর পরে প্রতিটি স্তর আগের চেয়ে বেশি যোগ করে, কারণ যোগটি j দিয়ে গুণ হয়। দুটি ৩ × ৩ একই ৫ × ৫ এলাকা দেখে, প্রায় ২৮% কম weight-এ, মাঝে বাড়তি একটি ReLU-সহ: VGG-র নকশার যুক্তি।
৪.৩(মাঝারি) (ক) Box A = (০, ০, ৪, ৪) আর B = (২, ২, ৬, ৬), রূপ (x1, y1, x2, y2). IoU কত? NMS-এর সীমা ০.৫ হলে দুটিই থাকবে? (খ) ৬৪ থেকে ১২৮ channel, ৩ × ৩ kernel: প্রতি output pixel-এ standard conv আর depthwise separable conv-এর MAC, আর অনুপাত? (গ) ১ × ১ conv, ২৫৬ থেকে ৬৪ channel: parameter? (ঝালিয়ে নিন: পর্ব ২২, ২৫)
(a) overlap (2,2)-(4,4): 2 x 2 = 4 ; union 16 + 16 - 4 = 28
IoU = 4/28 = 0.1429 < 0.5 -> NMS keeps both
(b) standard: 9 x 64 x 128 = 73,728
depthwise 9 x 64 = 576 + pointwise 64 x 128 = 8,192 -> 8,768
ratio 8,768/73,728 = 0.1189 = 1/128 + 1/9
(c) 256 x 64 + 64 = 16,448
অর্ধেক পাশাপাশি সরা দুটি box-এর IoU মাত্র ০.১৪: IoU কঠোর মাপ। Depthwise separable প্রায় ৮ গুণ সস্তা, MobileNet ঠিক এ কারণে ফোনে চলে; ১ × ১ conv channel কমিয়ে খরচ নামায়।
out = lambda n, k, s=1, p=0, d=1: (n + 2 * p - d * (k - 1) - 1) // s + 1
a = out(224, 7, 2, 3); b = out(a, 3, 2, 1) # 4.1
print("4.1 conv", a, "pool", b, "dilated", out(b, 3, 1, 2, 2), "| params", 64 * 3 * 7 * 7 + 64,
"| MAC", 64 * a * a * 3 * 7 * 7, "| 32 with k5:", out(32, 5))
r, j = 1, 1 # 4.2
for k, s in [(3, 1), (3, 1), (2, 2), (3, 1), (3, 2)]:
r += (k - 1) * j; j *= s
print(f"4.2 k={k} s={s}: r={r} j={j}")
print(" two 3x3:", 2 * (9 * 32 * 32 + 32), "one 5x5:", 25 * 32 * 32 + 32)
A, B = (0, 0, 4, 4), (2, 2, 6, 6) # 4.3
iw = max(0, min(A[2], B[2]) - max(A[0], B[0])); ih = max(0, min(A[3], B[3]) - max(A[1], B[1]))
area = lambda q: (q[2] - q[0]) * (q[3] - q[1]); inter = iw * ih
print("4.3 IoU", round(inter / (area(A) + area(B) - inter), 4))
C, Co, k = 64, 128, 3; std, sep = k * k * C * Co, k * k * C + C * Co
print(" MAC std", std, "dw-sep", sep, "ratio", round(sep / std, 4), "=", round(1 / Co + 1 / k**2, 4), "| 1x1", 256 * 64 + 64)Shape 112, 56, 56; params 9472, MAC 118013952, আর ৩২-এ 28. Receptive field-এর পাঁচ লাইন r=3, 5, 6, 10, 14, শেষ j=4; weight 18496 বনাম 25632. IoU 0.1429, MAC 73728 বনাম 8768, অনুপাত দুই পথেই 0.1189, আর ১ × ১ conv-এ 16448.
৫ · মডিউল ৫–৬ — LSTM, attention, Transformer-এর parameter আর KV cache
ঝালিয়ে নিন: পর্ব ২৯ · LSTM আর GRU, পর্ব ৩৩ · Self-attention, পর্ব ৩৪ · Positional encoding, পর্ব ৩৫ · Transformer block, পর্ব ৩৭ · দক্ষ attention, পর্ব ৩৮ · Vision Transformer, পর্ব ৪৮ · BERT পরিবার, পর্ব ৪৯ · GPT.
LSTM: ct = f ⊙ ct−1 + i ⊙ c̃, ht = o ⊙ tanh(ct); parameter (বইয়ের রূপ) ৪nh(nx + nh + ১)। Attention: softmax(QKᵀ/√dk)V. Multi-head: dk = d/h; parameter 4(d2 + d), h-এর ওপর নির্ভর করে না। Sinusoidal PE: PE(t, ২n) = sin(ωnt), PE(t, ২n + ১) = cos(ωnt), ωn = ১/১০০০০2n/d. Decoder block (FFN ৪d): 4(d2 + d) + (8d2 + 5d) + 4d ≈ 12d2. KV cache = ২ · L · B · T · Hkv · dhead · bytes.
৫.১(মাঝারি) একটি unit-এর LSTM, এক ধাপ। Gate-গুলোর pre-activation: forget ২.০, input −১.০, candidate ০.৫, output ০.০; আগের ct−1 = ১.৫. (ক) f, i, c̃, o, ct, ht? (খ) f এই মানে ২০ ধাপ স্থির থাকলে c ধরে gradient-এর গুণক? (গ) nx = ৫০, nh = ১২৮: LSTM-এর parameter বইয়ের সূত্রে, PyTorch-এ (দুটি bias), আর GRU-র (বইয়ের সূত্রে)? (ঝালিয়ে নিন: পর্ব ২৯)
(a) f = sigmoid(2) = 0.8808 ; i = sigmoid(-1) = 0.2689
g = tanh(0.5) = 0.4621 ; o = sigmoid(0) = 0.5
c = 0.8808 x 1.5 + 0.2689 x 0.4621 = 1.3212 + 0.1243 = 1.4455
h = 0.5 x tanh(1.4455) = 0.5 x 0.8948 = 0.4474
(b) dc_t/dc_(t-1) = f -> 0.8808^20 = 0.0790
(c) book: 4 x 128 x (50 + 128 + 1) = 91,648
PyTorch: 4 x 128 x (50 + 128 + 2) = 92,160
GRU book: 3 x 128 x 179 = 68,736
Cell-এর পথে শুধু f দিয়ে গুণ, কোনো weight matrix নেই; f কাছাকাছি ১ রাখলে স্মৃতি টেকে। Forget-gate bias ধনাত্মক দিয়ে শুরু করার কারণ এটিই। দুটি bias থাকায় PyTorch-এর সংখ্যা বইয়ের চেয়ে ৪nh = ৫১২ বেশি।
৫.২(মাঝারি) dk = ৪. Query q = (১, ০, ১, ০); key k1 = (১, ১, ০, ০), k2 = (০, ০, ১, ১), k3 = (১, ০, ১, ০); value v1 = (১, ০), v2 = (০, ১), v3 = (১, ১)। (ক) Score, scale-এর পরে score, attention weight আর output? (খ) √dk দিয়ে ভাগ না করলে weight? (গ) Causal mask-এ query দ্বিতীয় অবস্থানে থাকলে (শুধু k1, k2 দেখে) output? (ঝালিয়ে নিন: পর্ব ৩২, ৩৩)
(a) q.k = (1, 1, 2) ; / sqrt(4) = (0.5, 0.5, 1.0)
e^ = (1.6487, 1.6487, 2.7183) ; sum 6.0157
weights = (0.2741, 0.2741, 0.4519)
out = 0.2741 (1,0) + 0.2741 (0,1) + 0.4519 (1,1) = (0.7259, 0.7259)
(b) softmax(1, 1, 2) = (0.2119, 0.2119, 0.5761) -> sharper
(c) softmax(0.5, 0.5) = (0.5, 0.5) -> out = (0.5, 0.5)
Scale ছাড়া weight বেশি ধারালো; বড় dk-তে dot product-এর variance প্রায় dk, তখন softmax প্রায় one-hot হয়ে gradient মরে যায়। Mask-এ ভবিষ্যতের key-র score −∞, তাই তাদের weight ঠিক শূন্য।
q = (1, 0, 1, 0)
k1 -> 1, k2 -> 1, k3 -> 2
scores = (1, 1, 2)
sqrt(4) = 2
(0.5, 0.5, 1.0)
e^ = 1.6487, 1.6487,
2.7183 ; sum 6.0157
w = (.2741, .2741, .4519)
.2741(1,0) + .2741(0,1)
+ .4519(1,1)
out = (0.7259, 0.7259)
- মিল মাপা। q আর প্রতিটি key-র dot product: k3 দুটি ঘরে মেলে, তাই score ২.
- Scale. √৪ = ২ দিয়ে ভাগ; dk বড় হলে এই ভাগ softmax-কে চরম হতে দেয় না।
- ওজন। Softmax-এ যোগফল ১; k3 পায় ০.৪৫১৯, বাকি দুটি ০.২৭৪১ করে।
- মেশানো। Value-গুলোর ভারযুক্ত যোগ: output (০.৭২৫৯, ০.৭২৫৯)।
৫.৩(সহজ) B = ৮, T = ১২৮, d = ৫১২, h = ৮ head. (ক) dk আর head-এ ভাগ করার পরে Q-এর shape? (খ) Attention score tensor-এর shape আর float32-এ memory (এক স্তরে)? (গ) Multi-head attention-এর parameter (bias-সহ)? h = ১৬ হলে? (ঘ) ২২৪ × ২২৪ ছবি, patch ১৬: ViT-এ token কয়টি ([CLS]-সহ), আর একটি patch-এ কয়টি সংখ্যা? (ঝালিয়ে নিন: পর্ব ৩৩, ৩৮)
(a) d_k = 512/8 = 64 ; Q: (B, T, d) -> (8, 128, 8, 64) -> transpose (8, 8, 128, 64)
(b) scores (8, 8, 128, 128) = 1,048,576 values x 4 = 4 MiB
(c) 4 (512^2 + 512) = 1,050,624 ; h = 16: same
(d) (224/16)^2 = 196 patches + 1 = 197 tokens ; 16 x 16 x 3 = 768 numbers
Head বাড়ালে parameter বদলায় না, শুধু d ভাগ হয়। কিন্তু score-এর memory T2-এর সঙ্গে বাড়ে: T দ্বিগুণ হলে ৪ গুণ। FlashAttention-এর ধারণা এই পুরো matrix memory-তে না রেখে টুকরো টুকরো হিসাব করা।
৫.৪(সহজ) Sinusoidal positional encoding, d = ৪. (ক) ω0, ω1? (খ) অবস্থান t = ৩-এর PE vector? (গ) দ্বিতীয় জোড়ার wavelength? (ঝালিয়ে নিন: পর্ব ৩৪)
(a) omega_0 = 1/10000^0 = 1 ; omega_1 = 1/10000^(2/4) = 0.01
(b) PE(3) = (sin 3, cos 3, sin 0.03, cos 0.03) = (0.1411, -0.9900, 0.0300, 0.9996)
(c) 2 pi / 0.01 = 628.3 positions
প্রথম জোড়া ঘড়ির সেকেন্ডের কাঁটার মতো দ্রুত ঘোরে, দ্বিতীয়টি প্রায় নড়ে না; দুই গতির কাঁটা মিলে প্রতিটি অবস্থান আলাদা চেনা যায়। শুধু sin নিলে sin(a) = sin(π − a)-র কারণে দুটি অবস্থান গুলিয়ে যেত।
৫.৫(কঠিন) Bias-সহ, LayerNorm-এ ২d, FFN ৪d. (ক) GPT-ধাঁচের decoder: V = ৩২,০০০, context ৫১২ (শেখা position table), d = ২৫৬, L = ৪, block-এ দুটি LayerNorm, শেষে একটি LayerNorm, LM head tied: মোট parameter? Tie না করলে? (খ) BERT-ধাঁচের encoder: V = ৩০,৫২২, position ৫১২, segment ২, embedding-এর পরে LayerNorm, d = ৫১২, L = ৪, FFN ২০৪৮, pooler (Linear d → d): embedding, এক layer আর মোট? (ঝালিয়ে নিন: পর্ব ৩৫, ৪৮, ৪৯)
(a) token 32,000 x 256 = 8,192,000 ; position 512 x 256 = 131,072
attn (256 x 768 + 768) + (256 x 256 + 256) = 263,168
FFN (256 x 1024 + 1024) + (1024 x 256 + 256) = 525,568 ; 2 LN = 1,024
block = 789,760 ; 4 blocks = 3,159,040 ; final LN 512
total = 8,192,000 + 131,072 + 3,159,040 + 512 = 11,482,624
untied: + 8,192,000 = 19,674,624
(b) embedding 30,522 x 512 + 512 x 512 + 2 x 512 + 1,024 = 15,891,456
layer: attn 4(512^2 + 512) + LN 1,024 = 1,051,648
FFN (512 x 2048 + 2048) + (2048 x 512 + 512) + LN 1,024 = 2,100,736
-> 3,152,384 per layer
total = 15,891,456 + 4 x 3,152,384 + 262,656 = 28,763,648
ছোট model-এ token table-ই সবচেয়ে বড় খাত: (ক)-তে ৭১%. একই সূত্রে V = ৫০,২৫৭, d = ৭৬৮, L = ১২, context ১০২৪ বসালে আসে ১২,৪৪,৩৯,৮০৮, যা পর্ব ৪৯-এ আসল GPT-2 checkpoint-এর গোনার সঙ্গে হুবহু মিলেছিল; আর (খ)-র সূত্রে d = ৭৬৮, L = ১২ দিলে BERT-base-এর ১০,৯৪,৮২,২৪০ (প্রবন্ধের “110M”). চেকার দুটিই মেলায়।
৫.৬(মাঝারি) একটি decoder: L = ২৪, d = ২০৪৮, ১৬টি query head, fp16. (ক) T = ৪০৯৬ token, B = ৮ অনুরোধে KV cache কত GiB? (খ) প্রতি token-এ কত KiB? (গ) GQA-তে K, V-এর head ১৬ থেকে ৪ হলে? (ঘ) একটি config: ২৮ স্তর, ২টি KV head, head_dim ১২৮, bf16: প্রতি token-এ কত byte? (ঝালিয়ে নিন: পর্ব ৩৭, ৮৩)
(a) 2 x 24 x 8 x 4096 x 2048 x 2 = 6,442,450,944 bytes = 6 GiB
(b) 2 x 24 x 2048 x 2 = 196,608 bytes = 192 KiB per token
(c) H_kv x d_head = 4 x 128 = 512 instead of 2048 -> 1.5 GiB
(d) 2 x 28 x 2 x 128 x 2 = 28,672 bytes = 28 KiB
(ঘ) হুবহু পর্ব ৮৩-এ পড়া Qwen2.5-1.5B-এর config-এর হিসাব। KV cache weight নয়, অনুরোধ আর context-এর সঙ্গে বাড়ে; long context আর বড় batch-এ GPU memory সাধারণত এখানেই ফুরায়, আর GQA সরাসরি এটিই কমায়।
import numpy as np
np.set_printoptions(precision=4, suppress=True)
sig = lambda a: 1 / (1 + np.exp(-a))
f, i, g, o = sig(2.0), sig(-1.0), np.tanh(0.5), sig(0.0) # 5.1
c = f * 1.5 + i * g; h = o * np.tanh(c)
print(f"5.1 f {f:.4f} i {i:.4f} g {g:.4f} o {o:.4f} | c {c:.4f} h {h:.4f} | f^20 {f**20:.4f}")
nx, nh = 50, 128
print(" LSTM book", 4 * nh * (nx + nh + 1), "torch", 4 * nh * (nx + nh + 2), "| GRU book", 3 * nh * (nx + nh + 1))
q = np.array([1., 0, 1, 0]); K = np.array([[1., 1, 0, 0], [0, 0, 1, 1], [1, 0, 1, 0]]) # 5.2
V = np.array([[1., 0], [0, 1], [1, 1]])
def soft(s): e = np.exp(s - s.max()); return e / e.sum()
s = K @ q; a = soft(s / np.sqrt(4))
print("5.2 scores", s, "weights", a, "out", a @ V)
print(" no scale", soft(s), "| causal out", soft(s[:2] / 2) @ V[:2])
B, T, d, H = 8, 128, 512, 8 # 5.3
print("5.3 d_k", d // H, "| Q", (B, H, T, d // H), "| scores", (B, H, T, T), B * H * T * T * 4 / 2**20, "MiB")
print(" params", 4 * (d * d + d), "| ViT tokens", (224 // 16)**2 + 1, "patch", 16 * 16 * 3)
w = 1 / 10000 ** (np.arange(2) * 2 / 4) # 5.4
pe = np.array([np.sin(3 * w[0]), np.cos(3 * w[0]), np.sin(3 * w[1]), np.cos(3 * w[1])])
print("5.4 omega", w, "PE(3)", pe, "| wavelength", round(2 * np.pi / w[1], 1))LSTM-এ c 1.4455 h 0.4474, f^20 0.0790, parameter 91648, 92160 আর GRU 68736. Attention-এ weights [0.2741 0.2741 0.4519], out [0.7259 0.7259], scale ছাড়া [0.2119 0.2119 0.5761], causal-এ [0.5 0.5]. Shape (8, 8, 128, 64), score 4.0 MiB, parameter 1050624, ViT-এ 197 token আর 768 সংখ্যা। PE(৩) [ 0.1411 -0.99 0.03 0.9996], wavelength 628.3.
def decoder(V, ctx, d, L, tied=True):
attn = (d * 3 * d + 3 * d) + (d * d + d); mlp = (d * 4 * d + 4 * d) + (4 * d * d + d)
block = attn + mlp + 4 * d
return V * d + ctx * d + L * block + 2 * d + (0 if tied else V * d), block
def encoder(V, d, L, ffn, P=512, S=2):
emb = V * d + P * d + S * d + 2 * d
layer = 4 * (d * d + d) + 2 * d + (d * ffn + ffn) + (ffn * d + d) + 2 * d
return emb, layer, emb + L * layer + d * d + d
print("5.5 decoder (total, block)", decoder(32000, 512, 256, 4), "untied", decoder(32000, 512, 256, 4, False)[0])
print(" encoder (emb, layer, total)", encoder(30522, 512, 4, 2048))
print(" GPT-2 check", decoder(50257, 1024, 768, 12)[0] == 124_439_808,
"| BERT-base check", encoder(30522, 768, 12, 3072)[2] == 109_482_240)
kv = 2 * 24 * 8 * 4096 * 2048 * 2 # 5.6
print("5.6 KV GiB", kv / 2**30, "| per token KiB", 2 * 24 * 2048 * 2 / 1024,
"| GQA 4 heads GiB", 2 * 24 * 8 * 4096 * 4 * 128 * 2 / 2**30, "| 28-layer config bytes", 2 * 28 * 2 * 128 * 2)Decoder-এ (11482624, 789760), tie ছাড়া 19674624; encoder-এ (15891456, 3152384, 28763648). একই ফাংশন GPT-2 আর BERT-base-এর সংখ্যা মেলায়: True, True. KV cache 6.0 GiB, token-প্রতি 192.0 KiB, GQA-তে 1.5 GiB, আর ২৮ স্তরের config-এ 28672 byte.
৬ · মডিউল ৭–৮ — BPE, perplexity, BLEU ও chrF, decoding আর compute
ঝালিয়ে নিন: পর্ব ৪০ · Tokenization, পর্ব ৪৫ · NLP মূল্যায়ন, পর্ব ৪৬ · Language modeling, পর্ব ৫০ · Decoding, পর্ব ৫১ · Scaling.
BPE: সবচেয়ে বেশি পাশাপাশি আসা জোড়া জোড়া দেওয়া, আবার গোনা; encode-এ merge-গুলো শেখার ক্রমে। Perplexity = eNLL = ২bits; bits = nats ÷ ln ২. BLEU = BP × exp((১/N)∑ln pn), BP = e1 − r/c (c < r হলে)। chrFβ = (১ + β2)PR/(β2P + R), β = ২, P আর R অক্ষর-n-gram জুড়ে গড়। Temperature: softmax(z/T). Top-p: সাজানো সম্ভাবনার যোগ p ছোঁয়া পর্যন্ত সবচেয়ে ছোট সেট, তারপর আবার normalize. Compute C ≈ ৬ND.
৬.১(মাঝারি) শব্দ আর গণনা: “bar” ৬, “bark” ৩, “car” ৪, “card” ২; শুরুতে প্রতিটি অক্ষর একটি টুকরো, শব্দের সীমা পেরিয়ে জোড়া নয়। (ক) তিনটি BPE merge কী কী, প্রতিটিতে জোড়ার গণনা? (খ) শেষে চারটি শব্দের token? (গ) নতুন শব্দ “cark”-এর encode? (ঘ) Vocabulary-র আকার? (ঝালিয়ে নিন: পর্ব ৪০)
(a) round 1: b a 9, a r 15, r k 3, c a 6, r d 2 -> merge a+r (15)
round 2: b ar 9, ar k 3, c ar 6, ar d 2 -> merge b+ar (9)
round 3: bar k 3, c ar 6, ar d 2 -> merge c+ar (6)
(b) bar = [bar] ; bark = [bar, k] ; car = [car] ; card = [car, d]
(c) c a r k -> c ar k -> (b+ar: none) -> car k = [car, k]
(d) base {a, b, c, d, k, r} = 6 + 3 merges = 9
“cark” training-এ ছিল না, তবু অচেনা token ছাড়া ভাঙা গেল: BPE-র মূল সুবিধা। বাংলায় code point থেকে শুরু করলে merge কখনো কার-চিহ্নকে ব্যঞ্জন থেকে আলাদা রাখে; পর্ব ৪০ এটি দেখিয়েছে।
৬.২(সহজ) একটি language model চারটি test token-কে সম্ভাবনা দিল ০.৫, ০.২৫, ০.৮, ০.১. (ক) গড় NLL (nats), perplexity, bits প্রতি token? (খ) এই চার token মিলে ১০টি অক্ষর হলে bits প্রতি অক্ষর? (গ) V = ৫০,০০০-এর সমান-সম্ভাবনার model-এর perplexity? (ঝালিয়ে নিন: পর্ব ৪৬)
(a) -ln: 0.6931, 1.3863, 0.2231, 2.3026 ; sum 4.6052 ; NLL = 1.1513 nats
PPL = e^1.1513 = 3.1623 ; bits = 1.1513 / 0.6931 = 1.6610 ; 2^1.661 = 3.1623
(b) total bits 6.6439 / 10 chars = 0.6644 bits per char
(c) uniform: PPL = V = 50,000
Log-এর base বদলালে perplexity বদলায় না। কিন্তু ভিন্ন tokenizer-এর model-এর token-প্রতি perplexity তুলনা চলে না; তখন অক্ষর বা byte-প্রতি bits-এ নামিয়ে তুলনা করতে হয়।
৬.৩(মাঝারি) Reference: “দাম আজ অনেক কমেছে বাজারে”, candidate: “আজ দাম অনেক কমেছে” (শব্দে ভাগ)। (ক) p1, p2, BP আর BLEU-2 (সমান ওজন)? (খ) Candidate “দাম দাম দাম দাম” হলে clipped p1? (গ) শব্দ “বাজারে” (candidate) বনাম “বাজারের” (reference): শব্দ-স্তরে মিল কত, আর code point-এ N = ২, β = ২-এর chrF? (ঝালিয়ে নিন: পর্ব ৪৫)
(a) unigrams: 4/4 match -> p1 = 1
cand bigrams: (aj dam), (dam onek), (onek komeche) ; only the last is in ref -> p2 = 1/3
BP = e^(1 - 5/4) = e^-0.25 = 0.7788
BLEU-2 = 0.7788 x sqrt(1 x 1/3) = 0.7788 x 0.5774 = 0.4496
(b) "dam" appears once in ref -> clipped count 1 -> p1 = 1/4
(c) word level: 0 match
1-grams: cand 6 code points, ref 7, match 6 -> P1 = 1, R1 = 6/7 = 0.8571
2-grams: cand 5, ref 6, match 5 -> P2 = 1, R2 = 5/6 = 0.8333
P = 1, R = 0.8452 ; F2 = 5PR/(4P + R) = 4.2262/4.8452 = 87.22
(ক)-তে শব্দের ক্রম বদলেই bigram মেলেনি, আর ছোট candidate-কে BP শাস্তি দিল। (গ) বাংলার জন্য মূল কথা: বিভক্তি বদলালে শব্দভিত্তিক BLEU-এর চোখে সম্পূর্ণ ভুল, chrF-এর চোখে প্রায় ঠিক। (কোডের ভেতরে বাংলা শব্দগুলো ইংরেজি হরফে লেখা।)
৬.৪(মাঝারি) পাঁচ token-এর logits (২, ১, ০.৫, ০, −১)। (ক) T = ১, ০.৫ আর ২-এ সম্ভাবনা? (খ) T = ১-এ top-p, p = ০.৮: কয়টি token থাকে, আর নতুন সম্ভাবনা? (গ) Top-k, k = ২? (ঝালিয়ে নিন: পর্ব ৫০)
(a) T=1: (0.5630, 0.2071, 0.1256, 0.0762, 0.0280)
T=0.5: (0.8292, 0.1122, 0.0413, 0.0152, 0.0021)
T=2: (0.3745, 0.2272, 0.1769, 0.1378, 0.0836)
(b) cumsum 0.5630, 0.7701, 0.8958 -> 0.8 reached at 3 tokens
renorm: (0.5630, 0.2071, 0.1256)/0.8958 = (0.6285, 0.2312, 0.1402)
(c) (0.5630, 0.2071)/0.7701 = (0.7311, 0.2689)
T ছোট হলে বণ্টন ধারালো (T → ০ মানে greedy), বড় হলে সমতল। Top-p লেজ কাটে, কিন্তু কতগুলো থাকবে তা বণ্টনের আকার ঠিক করে: আত্মবিশ্বাসী ধাপে কম, দোটানার ধাপে বেশি। Top-k-তে সংখ্যা সবসময় স্থির।
৬.৫(সহজ) একটি model-এ N = ১০০ কোটি (১০9) parameter. (ক) Chinchilla-র মোটামুটি নিয়মে (প্রতি parameter-এ প্রায় ২০ token) কত token? (খ) C ≈ ৬ND-তে training compute? (গ) ধরুন যন্ত্রগুলো মিলে কার্যকর গতি ৪০ TFLOP/s (৪ × ১০13): কত দিন? (ঝালিয়ে নিন: পর্ব ৫১)
(a) D = 20 x 1e9 = 2e10 tokens
(b) C = 6 x 1e9 x 2e10 = 1.2e20 FLOP
(c) 1.2e20 / 4e13 = 3.0e6 s = 34.7 days
“৬” আসে forward-এ প্রতি parameter-এ প্রায় ২ FLOP আর backward-এ প্রায় ৪ থেকে। ২০-এর অনুপাত একটি নির্দিষ্ট গবেষণার (২০২২) মাপা ফল, প্রাকৃতিক নিয়ম নয়; ডেটা ফুরিয়ে গেলে বা inference-এর খরচ ধরলে বাস্তবে অন্য অনুপাত বাছা হয়।
import numpy as np
from collections import Counter
corpus = {"bar": 6, "bark": 3, "car": 4, "card": 2}; toks = {w: list(w) for w in corpus} # 6.1
def apply(t, pair):
out, i = [], 0
while i < len(t):
if i + 1 < len(t) and (t[i], t[i + 1]) == pair: out.append(t[i] + t[i + 1]); i += 2
else: out.append(t[i]); i += 1
return out
merges, enc = [], list("cark")
for _ in range(3):
pc = Counter()
for w, f in corpus.items(): pc += Counter({p: f * c for p, c in Counter(zip(toks[w], toks[w][1:])).items()})
best = max(pc, key=pc.get); merges.append(best); toks = {w: apply(t, best) for w, t in toks.items()}
enc = apply(enc, best); print("6.1 merge", best, pc[best])
print(" tokens", toks, "| cark", enc)
p = np.array([0.5, 0.25, 0.8, 0.1]); nll = -np.log(p).mean() # 6.2
print(f"6.2 NLL {nll:.4f} PPL {np.exp(nll):.4f} bits/token {nll / np.log(2):.4f} bits/char {-np.log2(p).sum() / 10:.4f}")
ref, cand = "dam aj onek komeche bajare".split(), "aj dam onek komeche".split() # 6.3
ng = lambda t, n: Counter(tuple(t[i:i + n]) for i in range(len(t) - n + 1))
ps = [sum((ng(cand, n) & ng(ref, n)).values()) / (len(cand) - n + 1) for n in (1, 2)]
bp = np.exp(1 - len(ref) / len(cand)); print("6.3 p", ps, f"BP {bp:.4f} BLEU-2 {bp * np.sqrt(ps[0] * ps[1]):.4f}")
cg = lambda s, n: Counter(s[i:i + n] for i in range(len(s) - n + 1)); c, r = "বাজারে", "বাজারের"
m = [sum((cg(c, n) & cg(r, n)).values()) for n in (1, 2)]
P, R = np.mean([m[0] / 6, m[1] / 5]), np.mean([m[0] / 7, m[1] / 6])
print(f" chrF P {P:.4f} R {R:.4f} F {100 * 5 * P * R / (4 * P + R):.2f}")
z = np.array([2., 1, 0.5, 0, -1]); soft = lambda s: np.exp(s - s.max()) / np.exp(s - s.max()).sum() # 6.4
for T in (1, 0.5, 2): print(f"6.4 T={T}:", np.round(soft(z / T), 4))
pr = soft(z); keep = np.searchsorted(np.cumsum(pr), 0.8) + 1
print(" keep", keep, np.round(pr[:keep] / pr[:keep].sum(), 4), "| top-2", np.round(pr[:2] / pr[:2].sum(), 4)); print(f"6.5 C = {6 * 1e9 * 20e9:.2e} FLOP, {6 * 1e9 * 20e9 / 4e13 / 86400:.1f} days")তিনটি merge ('a', 'r') 15, ('b', 'ar') 9, ('c', 'ar') 6; “cark” হয় ['car', 'k']. PPL 3.1623, bits/token 1.6610, bits/char 0.6644. BP 0.7788 BLEU-2 0.4496, chrF 87.22. Temperature-এর তিন সারি খাতার সঙ্গে মেলে; top-p-তে keep 3 [0.6285 0.2312 0.1402], top-2-তে [0.7311 0.2689]. শেষে 1.20e+20 FLOP, 34.7 days.
৭ · মডিউল ৯–১০ — LoRA, quantization, memory আর token-এর হিসাব
ঝালিয়ে নিন: পর্ব ৫৫ · LoRA আর QLoRA, পর্ব ৫৯ · Quantization আর distillation, পর্ব ৬৫ · Token, খরচ আর latency, পর্ব ৫২ · LLM-এর ভেতরে.
LoRA: W (d × k) স্থির, শেখে B (d × r) আর A (r × k): r(d + k) parameter; output-এ (α/r)BA. Quantization: q = clip(round(x/s) + z), x̂ = (q − z)s; symmetric int8-এ s = max|x|/১২৭, z = ০; asymmetric uint8-এ s = (max − min)/২৫৫, z = round(−min/s); ত্রুটি ≤ s/২. মাসের token = অনুরোধ × অনুরোধপ্রতি token. Latency ≈ TTFT + (N − ১)/r.
৭.১(মাঝারি) একটি decoder-এ L = ২৪, প্রতিটি attention projection ২০৪৮ × ২০৪৮, LoRA r = ১৬, α = ৩২. (ক) শুধু q আর v-তে LoRA: নতুন parameter? (খ) q, k, v, o চারটিতে? মূল চারটি matrix-এর কত শতাংশ? (গ) GQA-তে v_proj ২০৪৮ → ২৫৬ হলে এক layer-এ v-এর LoRA parameter? (ঘ) Scaling গুণক? (ঝালিয়ে নিন: পর্ব ৫৫)
(a) per matrix r(d + k) = 16 x 4096 = 65,536 ; x 2 x 24 = 3,145,728
(b) x 4 x 24 = 6,291,456 ; full 4 x 2048^2 x 24 = 402,653,184 -> 1.5625%
(c) 16 x (2048 + 256) = 36,864
(d) alpha/r = 32/16 = 2
LoRA-র parameter d·k নয়, d + k-এর সঙ্গে বাড়ে; তাই চওড়া matrix-এ লাভ বেশি। B শূন্য দিয়ে শুরু হয় বলে প্রথম ধাপে model হুবহু আগের মতো, আর merge করলে inference-এ বাড়তি খরচ নেই।
৭.২(কঠিন) x = (−০.৭৩৪, ০, ০.৪১৫, ১.৯০৫)। (ক) Symmetric int8: s, q, x̂, ত্রুটি, আর ত্রুটি s/২-এর মধ্যে কি না? (খ) Asymmetric uint8: s, z, q আর সর্বোচ্চ ত্রুটি? (গ) শেষ মানটি ১৯.০৫ (outlier) হলে symmetric-এ বাকি মানগুলোর কী হয়? (ঝালিয়ে নিন: পর্ব ৫৯)
(a) s = 1.905/127 = 0.015 ; x/s = (-48.93, 0, 27.67, 127)
q = (-49, 0, 28, 127) ; xhat = (-0.735, 0, 0.42, 1.905)
err = (-0.001, 0, 0.005, 0) ; max 0.005 <= s/2 = 0.0075
(b) s = 2.639/255 = 0.010349 ; z = round(70.92) = 71
q = (0, 71, 111, 255) ; xhat = (-0.7348, 0, 0.4140, 1.9042)
max err 0.00104
(c) s = 19.05/127 = 0.15 ; q = (-5, 0, 3, 127)
xhat = (-0.75, 0, 0.45, 19.05) ; err up to 0.035 (7x larger)
Asymmetric পুরো ২৫৬টি ঘর কাজে লাগায়, তাই ত্রুটি কম; z-এর কারণে আসল ০ হুবহু একটি পূর্ণসংখ্যায় পড়ে। একটি outlier গোটা tensor-এর scale ফুলিয়ে ছোট মানগুলোকে মোটা দানায় ফেলে; per-channel quantization আর outlier আলাদা রাখার কৌশল এই সমস্যার জবাব।
৭.৩(সহজ) ১৫০ কোটি (১.৫ × ১০9) parameter-এর model. (ক) শুধু weight রাখতে fp32, bf16, int8 আর ৪-bit-এ কত GB? (খ) পুরো fine-tuning-এ parameter প্রতি প্রায় ১৬ byte ধরলে (weight, gradient, দুটি Adam moment)? (ঝালিয়ে নিন: পর্ব ৫৫, ৫৯)
(a) fp32 6 GB ; bf16 3 GB ; int8 1.5 GB ; 4-bit 0.75 GB
(b) 1.5e9 x 16 = 24 GB (activations and KV cache extra)
Inference-এ ৪-bit model একটি সাধারণ laptop-এ ঢোকে, কিন্তু পুরো fine-tuning-এ বড় GPU লাগে। QLoRA-র ধারণা: base ৪-bit-এ স্থির, শুধু ছোট LoRA-র weight আর moment শেখা।
৭.৪(মাঝারি) একটি chatbot-এর context window ৮১৯২ token. প্রতি অনুরোধে system prompt ১২০০, RAG-এর ৫টি টুকরো × ৪০০, আগের কথোপকথন ১৫০০, প্রশ্ন ১০০ token; output-এর জন্য ১০২৪ সংরক্ষিত, গড়ে ৩০০ output token; ধরুন মাসে ৩,০০,০০০ অনুরোধ। (ক) Input token আর ফাঁকা জায়গা? (খ) মাসে মোট input আর output token, আর input-এর ভাগ? (গ) প্রতিবার হুবহু একই system prompt মাসে কত token, মোট input-এর কত ভাগ (prompt caching এই অংশেই কাজ করে)? (ঘ) Prefill ২০০ token/s আর decode ১০ token/s-এর CPU-তে মোট latency? (ঝালিয়ে নিন: পর্ব ৬৫)
(a) 1200 + 2000 + 1500 + 100 = 4800 ; + 1024 = 5824 ; headroom 8192 - 5824 = 2368
(b) input 4800 x 300,000 = 1,440,000,000 ; output 300 x 300,000 = 90,000,000
input share 4800 / 5100 = 94.1%
(c) 1200 x 300,000 = 360,000,000 tokens = 25% of all input
(d) TTFT = 4800/200 = 24 s ; + 299/10 = 29.9 s -> 53.9 s
Token-এর ৯৪% input-এ, আর তার এক-চতুর্থাংশ প্রতিবার একই system prompt; তাই প্রথম লাভ আসে বারবার যাওয়া অংশ ছোট বা cache করা থেকে। (ঘ)-এর গতি পর্ব ৬৫-এর একটি ছোট model-এর এক CPU run-এর মাপের কাছাকাছি ধরা; GPU-তে সংখ্যা অনেক ছোট। একই অর্থের বাংলা লেখায় tokenizer-ভেদে token বেশি লাগে, তাই বাংলা bot-এ এই বাজেট আগে ফুরায়।
import numpy as np
np.set_printoptions(precision=4, suppress=True)
r, d, L = 16, 2048, 24 # 7.1
qv, all4, full4 = 2 * r * (d + d) * L, 4 * r * (d + d) * L, 4 * d * d * L
print("7.1 q+v", qv, "| all four", all4, "of", full4, f"= {all4 / full4:.4%}", "| GQA v", r * (d + 256), "| alpha/r", 32 / r)
x = np.array([-0.734, 0.0, 0.415, 1.905]) # 7.2
s = np.abs(x).max() / 127; q = np.round(x / s); xh = q * s
print(f"7.2 sym s {s:.4f}", "q", q, "err", xh - x, f"max {np.abs(xh - x).max():.4f} vs s/2 {s / 2:.4f}")
sa = (x.max() - x.min()) / 255; z = int(np.round(-x.min() / sa)); qa = np.clip(np.round(x / sa) + z, 0, 255)
print(f" asym s {sa:.6f} z {z}", "q", qa, "xhat", (qa - z) * sa, f"max err {np.abs((qa - z) * sa - x).max():.5f}")
x2 = np.array([-0.734, 0.0, 0.415, 19.05]); s2 = 19.05 / 127; q2 = np.round(x2 / s2)
print(f" outlier s {s2:.4f}", "q", q2, "err", q2 * s2 - x2)
print("7.3 GB", {k: 1.5e9 * b / 1e9 for k, b in [("fp32", 4), ("bf16", 2), ("int8", 1), ("4-bit", 0.5), ("train", 16)]})
tin = 1200 + 5 * 400 + 1500 + 100 # 7.4
print("7.4 input", tin, "reserved", tin + 1024, "headroom", 8192 - tin - 1024)
print(f" month in {tin * 300_000:,} out {300 * 300_000:,} share {tin / (tin + 300):.1%} | system {1200 * 300_000:,} = {1200 / tin:.0%} | latency {tin / 200 + 299 / 10:.1f} s")LoRA-তে 3145728, 6291456, 1.5625%, GQA-র v-তে 36864. Symmetric-এ q [-49. 0. 28. 127.], সর্বোচ্চ ত্রুটি 0.0050 (সীমা 0.0075); asymmetric-এ z 71, q [ 0. 71. 111. 255.], ত্রুটি 0.00104; outlier-এ ত্রুটি 0.035 পর্যন্ত। Memory 6.0, 3.0, 1.5, 0.75 আর training-এ 24.0 GB. Token-এ 4800, headroom 2368, মাসে input 1,440,000,000 আর output 90,000,000 token, input-এর ভাগ 94.1%, system prompt 360,000,000 = 25%, latency 53.9 s.
৮ · মডিউল ১১ — retrieval-এর হিসাব: cosine, BM25, ranking metric আর PQ
ঝালিয়ে নিন: পর্ব ৬৭ · Sentence embedding, পর্ব ৬৮ · Vector index, পর্ব ৬৯ · RAG, পর্ব ৭০ · RAG মূল্যায়ন.
Cosine = a·b/(‖a‖‖b‖)। BM25: ∑ IDF(w) · tf(k1 + ১)/(tf + k1(১ − b + b|d|/avgdl)), IDF = ln(১ + (N − df + ০.৫)/(df + ০.৫)), k1 = ১.৫, b = ০.৭৫. Recall@k = প্রথম k-তে পাওয়া প্রাসঙ্গিক ÷ মোট প্রাসঙ্গিক; MRR = গড় ১/(প্রথম প্রাসঙ্গিকের rank); nDCG@k = DCG/IDCG, rank i-এর ছাড় ১/log2(i + ১)। PQ: প্রতি vector m byte (nbits = ৮)।
৮.১(সহজ) প্রশ্নের vector q = (১, ২, ২); নথি d1 = (২, ১, ২), d2 = (০, ৩, ৪), d3 = (৯, ০, ৩)। (ক) Dot product আর cosine-এ ক্রম? (খ) সব vector আগে normalize করলে dot product কী দেয়? (ঝালিয়ে নিন: পর্ব ৬৭)
(a) ||q|| = 3 ; dot: d1 8, d2 14, d3 15 -> order d3, d2, d1
norms 3, 5, 9.4868 ; cos: 8/9 = 0.8889, 14/15 = 0.9333, 15/28.46 = 0.5270
-> order d2, d1, d3
(b) unit vectors: dot = cosine (0.8889, 0.9333, 0.5270)
Dot product-এ লম্বা vector (d3) শুধু দৈর্ঘ্যের জোরে প্রথম হয়, দিক কম মিললেও। তাই embedding normalize করে inner-product index ব্যবহার করা হয়: তখন দ্রুত dot product-ই cosine.
৮.২(মাঝারি) চারটি নথি, দৈর্ঘ্য ৬, ১২, ৮, ১০ শব্দ। প্রশ্ন “চাল দাম”; “চাল” আছে ১টি নথিতে (df = ১), “দাম” ৩টিতে (df = ৩)। নথি ১ (দৈর্ঘ্য ৬): চাল ১ বার, দাম ১ বার; নথি ২ (দৈর্ঘ্য ১২): দাম ৩ বার, চাল নেই। k1 = ১.৫, b = ০.৭৫. (ক) দুই শব্দের IDF? (খ) দুই নথির BM25? (গ) নথি ২-এ দাম ১ বার থাকলে? (ঝালিয়ে নিন: পর্ব ৬৭)
(a) avgdl = 36/4 = 9
IDF(chal) = ln(1 + 3.5/1.5) = ln 3.3333 = 1.2040
IDF(dam) = ln(1 + 1.5/3.5) = ln 1.4286 = 0.3567
(b) doc1 (|d|/avgdl = 0.6667): norm = 1.5(0.25 + 0.5) = 1.125
tf part = 1 x 2.5/(1 + 1.125) = 1.1765
score = (1.2040 + 0.3567) x 1.1765 = 1.4164 + 0.4196 = 1.8361
doc2 (|d|/avgdl = 1.3333): norm = 1.5(0.25 + 1) = 1.875
tf part = 3 x 2.5/(3 + 1.875) = 1.5385 ; score = 0.3567 x 1.5385 = 0.5487
(c) 1 x 2.5/2.875 = 0.8696 ; score 0.3102
বিরল শব্দ “চাল” বেশি ওজন পায়; আর tf তিন গুণ হলেও score দ্বিগুণও হয়নি (০.৩১০ থেকে ০.৫৪৯): k1-এর saturation. লম্বা নথি b-এর কারণে একটু শাস্তি পায়।
৮.৩(মাঝারি) তিনটি প্রশ্নের প্রথম ৫টি ফল (১ = প্রাসঙ্গিক): প্রশ্ন ১: (০, ১, ০, ০, ১), মোট প্রাসঙ্গিক ২; প্রশ্ন ২: (১, ০, ০, ০, ০), মোট ১; প্রশ্ন ৩: (০, ০, ০, ০, ০), মোট ১. (ক) প্রতিটির recall@5 আর গড়? (খ) MRR? (গ) nDCG@5 (IDCG সব প্রাসঙ্গিক নথি ওপরে রেখে)? (ঘ) প্রশ্ন ১-এর recall@1? (ঝালিয়ে নিন: পর্ব ৭০)
(a) 2/2 = 1, 1/1 = 1, 0/1 = 0 -> mean 0.6667
(b) RR = 1/2, 1/1, 0 -> MRR = 0.5
(c) q1: DCG = 1/log2(3) + 1/log2(6) = 0.6309 + 0.3869 = 1.0178
IDCG = 1 + 0.6309 = 1.6309 -> 0.6241
q2: 1 ; q3: 0 -> mean 0.5414
(d) 0/2 = 0
প্রশ্ন ১-এ recall@5 নিখুঁত, তবু nDCG ০.৬২: ঠিক নথি এসেছে, কিন্তু নিচে। RAG-এ শুধু প্রথম কয়েকটি টুকরো prompt-এ যায়, তাই অবস্থানও গুরুত্বপূর্ণ। প্রশ্ন ৩-এর মতো শূন্য retrieval-এ generator যত ভালোই হোক, সঠিক উৎসভিত্তিক উত্তর পাওয়া কঠিন।
৮.৪(মাঝারি) ১০ লাখ (১০6) vector, d = ৭৬৮. (ক) Flat float32 index-এর memory (GB)? int8 কপি? (খ) PQ, m = ৯৬, nbits = ৮: code-এর memory, codebook-এর byte আর সংকোচনের অনুপাত? (গ) IVF, nlist = ১০০০, nprobe = ১০: সমান ভাগ ধরলে প্রতি প্রশ্নে কতগুলো vector-এর দূরত্ব হিসাব হয় (centroid বাদে)? (ঝালিয়ে নিন: পর্ব ৬৮)
(a) 1e6 x 768 x 4 = 3.072 GB ; int8 768 MB
(b) codes 1e6 x 96 bytes = 96 MB
codebooks 96 x 256 centroids x 8 dims x 4 bytes = 786,432 bytes
ratio 3072 / 96 = 32x
(c) 10 of 1000 cells -> about 1% -> 10,000 vectors (+1000 centroids)
IVF সময় বাঁচায়, memory নয়; PQ memory বাঁচায়, কিছু নির্ভুলতার দামে। বড় মাপে তাই দুটি জোড়া লাগে (IVFPQ). দাম সবসময় recall@k দিয়ে মাপতে হয়, Flat-এর ফলকে সত্য ধরে।
import numpy as np
q = np.array([1., 2, 2]); D = np.array([[2., 1, 2], [0, 3, 4], [9, 0, 3]]) # 8.1
dot = D @ q; nD = np.linalg.norm(D, axis=1)
print("8.1 dot", dot, "cos", np.round(dot / (nD * 3), 4), "| unit dot", np.round((D / nD[:, None]) @ (q / 3), 4))
N, avgdl, k1, b = 4, 9.0, 1.5, 0.75 # 8.2
idf = lambda df: np.log((N - df + 0.5) / (df + 0.5) + 1)
tfp = lambda tf, dl: tf * (k1 + 1) / (tf + k1 * (1 - b + b * dl / avgdl))
d1 = (idf(1) + idf(3)) * tfp(1, 6); d2 = idf(3) * tfp(3, 12)
print(f"8.2 idf {idf(1):.4f} {idf(3):.4f} | doc1 {d1:.4f} doc2 {d2:.4f} | doc2 tf=1 {idf(3) * tfp(1, 12):.4f}")
runs = [([0, 1, 0, 0, 1], 2), ([1, 0, 0, 0, 0], 1), ([0, 0, 0, 0, 0], 1)] # 8.3
disc = 1 / np.log2(np.arange(2, 7)); R, RR, ND = [], [], []
for rel, n in runs:
rel = np.array(rel); R.append(rel.sum() / n)
RR.append(1 / (np.argmax(rel) + 1) if rel.any() else 0.0); ND.append((rel * disc).sum() / disc[:n].sum())
print("8.3 recall@5", np.round(R, 4), "MRR", np.mean(RR), "nDCG@5", np.round(ND, 4), "mean", round(np.mean(ND), 4))
N, d, m = 1_000_000, 768, 96 # 8.4
print("8.4 flat GB", N * d * 4 / 1e9, "| int8 MB", N * d / 1e6, "| PQ MB", N * m / 1e6,
"| codebooks", m * 256 * (d // m) * 4, "| ratio", d * 4 // m, "| IVF scans", N * 10 // 1000)Dot [ 8. 14. 15.] আর cosine [0.8889 0.9333 0.527 ]; normalize করা vector-এর dot হুবহু cosine. BM25-এ doc1 1.8361 doc2 0.5487, tf = ১-এ 0.3102. recall@5 [1. 1. 0.], MRR 0.5, nDCG@5 [0.6241 1. 0. ], গড় 0.5414. PQ-তে 96.0 MB, codebook 786432 byte, অনুপাত 32, IVF-এ 10000টি দূরত্ব।
৯ · মডিউল ১২ — generative model: KL, GAN, diffusion, CFG আর FID
ঝালিয়ে নিন: পর্ব ৭৬ · VAE, পর্ব ৭৭ · GAN, পর্ব ৭৯ · Diffusion, পর্ব ৮০ · Latent diffusion আর guidance, পর্ব ৮১ · Generative model-এর মূল্যায়ন.
KL(N(μ, σ2) ‖ N(০, ১)) প্রতি মাত্রায় ½(μ2 + σ2 − ১ − ln σ2). ELBO = E[log p(x|z)] − KL; reparameterization z = μ + σε. D*(x) = pdata/(pdata + pg); তখন V = −ln ৪ + ২·JS. ᾱt = ∏(১ − βs), xt = √ᾱtx0 + √(১ − ᾱt)ε, SNR = ᾱ/(১ − ᾱ)। CFG: ε̃ = εu + w(εc − εu). Fréchet distance = ‖μ1 − μ2‖2 + Tr(Σ1 + Σ2 − ২(Σ1Σ2)½)।
৯.১(মাঝারি) একটি VAE-এর encoder দুই মাত্রায় μ = (০.৫, −১.০), σ = (১.০, ০.৫) দিল; prior N(০, I). Reconstruction পদ E[log p(x|z)] = −৪৫.২. (ক) প্রতি মাত্রার আর মোট KL? (খ) ELBO আর loss (−ELBO)? (গ) β-VAE, β = ৪-এ loss? (ঘ) ε = (০.২, −১.০) হলে z? (ঝালিয়ে নিন: পর্ব ৭৬)
(a) dim 1: 0.5(0.25 + 1 - 1 - ln 1) = 0.125
dim 2: 0.5(1 + 0.25 - 1 - ln 0.25) = 0.5(0.25 + 1.3863) = 0.8181
total KL = 0.9431
(b) ELBO = -45.2 - 0.9431 = -46.1431 ; loss = 46.1431
(c) 45.2 + 4 x 0.9431 = 48.9726
(d) z = (0.5 + 1.0 x 0.2, -1.0 + 0.5 x (-1.0)) = (0.7, -1.5)
KL বলে encoder prior থেকে কতটা সরে আছে; দ্বিতীয় মাত্রা দুই দিকেই সরেছে (গড় −১, σ ছোট), তাই বেশি দাম। z = μ + σε লেখায় এলোমেলো অংশ ε-এ সরে, আর μ, σ-তে gradient যেতে পারে।
৯.২(মাঝারি) (ক) একটি বিন্দুতে pdata = ০.৬, pg = ০.২: সেরা discriminator-এর মান? (খ) তিনটি বিন্দুর ওপর pdata = (০.৫, ০.৫, ০), pg = (০.২৫, ০.২৫, ০.৫)। D*, V(D*, G) আর JS divergence; V = −ln ৪ + ২JS মেলে কি? (গ) pg = pdata হলে D* আর V? (ঝালিয়ে নিন: পর্ব ৭৭)
(a) D* = 0.6/(0.6 + 0.2) = 0.75
(b) D* = (2/3, 2/3, 0)
V = 0.5 ln(2/3) x 2 + 0.25 ln(1/3) x 2 + 0.5 ln 1 = -0.4055 - 0.5493 = -0.9548
M = (0.375, 0.375, 0.25)
KL(p_data||M) = ln(0.5/0.375) = 0.2877 ; KL(p_g||M) = 0.5 ln(2/3) + 0.5 ln 2 = 0.1438
JS = 0.5(0.2877 + 0.1438) = 0.2158 ; -1.3863 + 2 x 0.2158 = -0.9548 (match)
(c) D* = 0.5 everywhere ; V = -ln 4 = -1.3863
Generator-এর লক্ষ্য V-কে −ln ৪-এ নামানো, অর্থাৎ JS শূন্য। তৃতীয় বিন্দুতে pdata = ০ বলে D* = ০: discriminator নিশ্চিতভাবে জাল চেনে; বণ্টন দুটি না মিললে এমন নিশ্চিত discriminator-ই GAN-এর gradient-সমস্যার মূলে।
৯.৩(মাঝারি) খেলনা diffusion, T = ৪, β = (০.১, ০.২, ০.৩, ০.৪)। (ক) প্রতিটি t-তে ᾱt আর SNR? (খ) x0 = ১.০, ε = −০.৫ হলে x3? (গ) DDPM-এর linear schedule (β ০.০০০১ থেকে ০.০২, T = ১০০০)-এ SNR প্রায় ১ কোথায়? (ঝালিয়ে নিন: পর্ব ৭৯)
(a) alpha = (0.9, 0.8, 0.7, 0.6)
abar = 0.9, 0.72, 0.504, 0.3024
SNR = 9, 2.5714, 1.0161, 0.4335
(b) x3 = sqrt(0.504) x 1 + sqrt(0.496) x (-0.5) = 0.7099 - 0.3521 = 0.3578
(c) computed by the checker: t = 259 gives SNR = 1.0010
ᾱ গুণফল, তাই দ্রুত নামে; SNR ১-এর নিচে মানে ছবির চেয়ে noise বেশি। (খ) দেখায় কেন training সস্তা: t-তম noisy ছবির জন্য t ধাপ চালাতে হয় না, একটি সূত্রই যথেষ্ট। Linear schedule-এ ১০০০ ধাপের প্রায় চার ভাগের এক ভাগেই SNR ১-এ নামে, বাকি ধাপের বেশিরভাগ প্রায় খাঁটি noise-এ কাটে; cosine schedule এই কারণে প্রস্তাবিত।
৯.৪(সহজ) শর্তহীন noise অনুমান εu = (০.১, −০.৪), prompt-সহ εc = (০.৩, −০.২)। (ক) w = ০, ১, ৩, ৭.৫-এ ε̃? (খ) xt = (০.৬, −০.৪), ᾱt = ০.৫, w = ৩: x̂0 = (xt − √(১ − ᾱ)ε̃)/√ᾱ কত? (ঝালিয়ে নিন: পর্ব ৮০)
(a) diff = eps_c - eps_u = (0.2, 0.2)
w=0: (0.1, -0.4) ; w=1: (0.3, -0.2) ; w=3: (0.7, 0.2) ; w=7.5: (1.6, 1.1)
(b) (0.6 - 0.7071 x 0.7, -0.4 - 0.7071 x 0.2) / 0.7071 = (0.1485, -0.7657)
এই রূপে w = ১ মানে সাধারণ শর্তসহ অনুমান, w > ১ মানে prompt-এর দিকে বাড়তি ঠেলা। কিছু প্রবন্ধ (১ + w)εc − wεu লেখে; তখন একই সংখ্যার অর্থ এক কম, তাই খাতায় কোন রূপ ধরেছেন লিখুন। বড় w মিল বাড়ায়, বৈচিত্র্য কমায়।
৯.৫(কঠিন) (ক) এক মাত্রায় N(০, ১) আর N(০.৫, ২2)-এর Fréchet distance? (খ) দুই মাত্রায় μ1 = (০, ০), Σ1 = diag(১, ৪); μ2 = (১, ০), Σ2 = diag(৪, ১)? (গ) একই গড়, Σ1-এর সারি (২, ১), (১, ২) আর Σ2 = I? (ঝালিয়ে নিন: পর্ব ৮১)
(a) (mu1 - mu2)^2 + (sigma1 - sigma2)^2 = 0.25 + 1 = 1.25
(b) ||dmu||^2 = 1 ; per dim: 1 + 4 - 2 sqrt(4) = 1, 4 + 1 - 2 sqrt(4) = 1
FD = 1 + 2 = 3
(c) eigenvalues of S1 S2 = S1: 3, 1
Tr(S1 + I) = 6 ; 2(sqrt3 + 1) = 5.4641 ; FD = 0.5359
(খ)-তে দুই সেটের মোট variance একই (trace ৫), তবু আকার আলাদা বলে দূরত্ব আছে। FID এই সূত্রই চালায় Inception-এর feature-এ; নমুনা কম হলে Σ-এর অনুমান নড়ে আর FID পক্ষপাতী হয়, তাই একই নমুনা-সংখ্যায় তুলনা করতে হয়।
import numpy as np
np.set_printoptions(precision=4, suppress=True)
mu, sg = np.array([0.5, -1.0]), np.array([1.0, 0.5]) # 9.1
kl = 0.5 * (mu**2 + sg**2 - 1 - np.log(sg**2))
print("9.1 KL", kl, f"total {kl.sum():.4f} ELBO {-45.2 - kl.sum():.4f} beta4 {45.2 + 4 * kl.sum():.4f}", "z", mu + sg * np.array([0.2, -1.0]))
pd, pg = np.array([0.5, 0.5, 0.0]), np.array([0.25, 0.25, 0.5]) # 9.2
D = pd / (pd + pg); M = (pd + pg) / 2
kl_ = lambda a, b: sum(ai * np.log(ai / bi) for ai, bi in zip(a, b) if ai > 0)
V = sum(p * np.log(t) for p, t in zip(pd, D) if p > 0) + sum(p * np.log(1 - t) for p, t in zip(pg, D) if p > 0)
js = 0.5 * kl_(pd, M) + 0.5 * kl_(pg, M)
print("9.2 D*", D, f"V {V:.4f} JS {js:.4f} -ln4+2JS {-np.log(4) + 2 * js:.4f} | D*(0.6,0.2) {0.6 / 0.8:.2f}")
ab = np.cumprod(1 - np.array([0.1, 0.2, 0.3, 0.4])) # 9.3
print("9.3 abar", ab, "SNR", ab / (1 - ab), f"x3 {np.sqrt(ab[2]) - 0.5 * np.sqrt(1 - ab[2]):.4f}")
abl = np.cumprod(1 - np.linspace(1e-4, 0.02, 1000)); snr = abl / (1 - abl)
print(f" linear: SNR(t=258) {snr[257]:.4f} SNR(t=259) {snr[258]:.4f} SNR(t=260) {snr[259]:.4f}")
eu, ec = np.array([0.1, -0.4]), np.array([0.3, -0.2]) # 9.4
print("9.4", " | ".join(f"w={w}: {eu + w * (ec - eu)}" for w in (0, 1, 3, 7.5)))
et = eu + 3 * (ec - eu); print(" x0hat", (np.array([0.6, -0.4]) - np.sqrt(0.5) * et) / np.sqrt(0.5))
def fd(m1, S1, m2, S2): # 9.5
ev = np.clip(np.linalg.eigvals(S1 @ S2).real, 0, None)
return round(float(np.sum((m1 - m2)**2) + np.trace(S1 + S2) - 2 * np.sqrt(ev).sum()), 4)
print("9.5 1-D", fd(np.zeros(1), np.eye(1), np.full(1, 0.5), 4 * np.eye(1)),
"| 2-D", fd(np.zeros(2), np.diag([1., 4]), np.array([1., 0]), np.diag([4., 1])),
"| full", fd(np.zeros(2), np.array([[2., 1], [1, 2]]), np.zeros(2), np.eye(2)))KL [0.125 0.8181], মোট 0.9431, ELBO -46.1431, β = ৪-এ 48.9726, z [ 0.7 -1.5]. GAN-এ V -0.9548, JS 0.2158, আর −ln ৪ + ২JS-ও -0.9548. Diffusion-এ SNR [9. 2.5714 1.0161 0.4335], x3 0.3578; linear schedule-এ SNR t = ২৫৯-এ 1.0010, t = ২৬০-এ প্রথম ১-এর নিচে 0.9905. CFG-র চারটি মান (w=3: [0.7 0.2], w=7.5: [1.6 1.1]) আর x0hat [ 0.1485 -0.7657]. Fréchet distance 1.25, 3.0, 0.5359.
১০ · মডিউল ১৩ — serving: TTFT, TPOT, throughput, Little-এর সূত্র আর CLIP
ঝালিয়ে নিন: পর্ব ৮২ · Multimodal model, পর্ব ৮৩ · LLM serving, পর্ব ৮৪ · Model deploy, পর্ব ৮৫ · GenAI system design.
TTFT: অনুরোধ থেকে প্রথম token (সারি + prefill). TPOT: পরের প্রতিটি token-এর গড় সময়। E2E = TTFT + (N − ১) × TPOT. Throughput = সব অনুরোধের মোট token ÷ সময়। Little-এর সূত্র: L = λW (গড়ে চলমান অনুরোধ = আগমনের হার × গড় সময়)। Goodput = SLO মানা অনুরোধ ÷ সময়। CLIP: logit = s · cosine, s = logit scale.
১০.১(সহজ) একটি অনুরোধে TTFT = ০.৪ s, TPOT = ২৫ ms, output N = ২০০ token. (ক) E2E আর ব্যবহারকারীর token/s? (খ) ১৬টি অনুরোধ একসঙ্গে batch-এ চললে TPOT বেড়ে ৩০ ms (TTFT একই ধরুন): প্রতিজনের E2E, মোট throughput আর প্রতিজনের token/s? (ঝালিয়ে নিন: পর্ব ৮৩)
(a) E2E = 0.4 + 199 x 0.025 = 5.375 s ; 200/5.375 = 37.2 tok/s
(b) E2E = 0.4 + 199 x 0.030 = 6.37 s
aggregate = 16 x 200 / 6.37 = 502.4 tok/s ; per user 31.4 tok/s
Batch-এ প্রতিজন একটু ধীর (৩৭ থেকে ৩১ token/s), কিন্তু যন্ত্রের মোট উৎপাদন প্রায় ১৩.৫ গুণ: decode ধাপে GPU মূলত memory থেকে weight পড়ে, একবার পড়ে ১৬ জনকে সেবা দেওয়া প্রায় একই খরচ। এ কারণেই continuous batching serving-এর মূল কৌশল।
১০.২(মাঝারি) গড়ে প্রতি সেকেন্ডে ৩টি অনুরোধ আসে, গড় E2E ৬ s. (ক) গড়ে কতগুলো অনুরোধ একসঙ্গে চলমান? (খ) Server-এ ১৬টি slot থাকলে কী হবে? (গ) ১০০ সেকেন্ডে ১০০০টি অনুরোধের ৮৭০টি SLO (TTFT ≤ ১ s, TPOT ≤ ৫০ ms) মানলে goodput? (ঝালিয়ে নিন: পর্ব ৮৩, ৮৫)
(a) L = lambda x W = 3 x 6 = 18 requests in flight
(b) 18 > 16 -> queue grows, TTFT keeps rising ; need >= 18 slots plus headroom
(c) goodput = 870 / 100 = 8.7 req/s (raw throughput 10 req/s)
Little-এর সূত্র কোনো বণ্টন ধরে না, শুধু স্থির অবস্থা; তাই capacity-র প্রথম অনুমানে এটি সবচেয়ে সহজ হাতিয়ার। গড়ের পাশে p95 দেখতেই হয়: গড় ১৮ মানে ভিড়ের মুহূর্তে আরও বেশি।
১০.৩(সহজ) ধরুন একটি GPU মোট ৫০০ token/s দেয়। (ক) ১০ লাখ token তৈরি করতে কত GPU-সেকেন্ড আর GPU-ঘণ্টা? (খ) Batch বাড়িয়ে throughput ১০০০ token/s হলে? (গ) ৩০০ output token-এর একটি উত্তরে গড়ে কত GPU-সেকেন্ড (৫০০ token/s-এ)? (ঝালিয়ে নিন: পর্ব ৮৩)
(a) 1e6 / 500 = 2000 GPU-s = 0.5556 GPU-h per 1M tokens
(b) 1e6 / 1000 = 1000 GPU-s = 0.2778 GPU-h per 1M tokens
(c) 300 / 500 = 0.6 GPU-s per answer
নিজের GPU-তে token-প্রতি GPU-সময় পুরোপুরি throughput-এর ওপর নির্ভর করে: throughput দ্বিগুণ মানে ১০ লাখ token-এ অর্ধেক GPU-ঘণ্টা। অলস GPU-ও সময় খায়, তাই ব্যবহার কম হলে GPU-ঘণ্টার বড় অংশ নষ্ট হয়।
১০.৪(মাঝারি) CLIP-এ একটি ছবির normalize করা vector-এর সঙ্গে তিনটি caption-এর cosine ০.৩০, ০.২৫, ০.১০. (ক) Logit scale s = ১০০ হলে softmax সম্ভাবনা? (খ) s = ১ হলে? (গ) কী শেখা গেল? (ঝালিয়ে নিন: পর্ব ৮২)
(a) logits (30, 25, 10) -> e^0, e^-5, e^-20 -> (0.9933, 0.0067, 0.0000)
(b) logits (0.30, 0.25, 0.10) -> (0.3610, 0.3434, 0.2956)
(c) cosine gaps of 0.05 become decisive only after the scale
CLIP-এর cosine সাধারণত ছোট পরিসরে থাকে; s (১/temperature) সেই ছোট ব্যবধানকে নিশ্চিত সিদ্ধান্তে বদলায়। তাই সম্ভাবনা ০.৯৯ দেখে “model নিশ্চিত জানে” ধরা ভুল; এটি scale-এর ফল, calibration নয়।
import numpy as np
ttft, tpot, N = 0.4, 0.025, 200 # 10.1
e2e = ttft + (N - 1) * tpot; e16 = ttft + (N - 1) * 0.030
print(f"10.1 E2E {e2e:.3f} s, {N / e2e:.1f} tok/s | B=16: E2E {e16:.2f} s, aggregate {16 * N / e16:.1f}, per user {N / e16:.1f}")
lam, W = 3, 6.0 # 10.2
print(f"10.2 L = {lam * W:.0f} in flight | goodput {870 / 100:.1f} req/s")
for tps in (500, 1000): # 10.3
print(f"10.3 {tps} tok/s: {1e6 / tps:.0f} GPU-s = {1e6 / tps / 3600:.4f} GPU-h per 1M tokens | answer {300 / tps:.1f} GPU-s")
s = np.array([0.30, 0.25, 0.10]) # 10.4
for scale in (100, 1):
e = np.exp(scale * s - (scale * s).max()); print(f"10.4 scale {scale}:", np.round(e / e.sum(), 4))E2E 5.375 s, 37.2 tok/s; batch-এ 6.37 s, মোট 502.4, প্রতিজন 31.4 token/s. L = 18, goodput 8.7 req/s. ১০ লাখ token-এ 2000 GPU-s = 0.5556 GPU-h আর 1000 GPU-s = 0.2778 GPU-h, একটি উত্তরে 0.6 আর 0.3 GPU-s. CLIP-এ s = ১০০-এ [0.9933 0.0067 0. ], s = ১-এ [0.361 0.3434 0.2956].
সাধারণ ভুল: একক না লিখে সংখ্যা লেখা। MiB না MB, fp16 না fp32, bias আছে কি না, t ০ থেকে না ১ থেকে, CFG-র কোন রূপ: প্রতিটিতে দুটি চালু রূপ আছে, আর উত্তর বদলায়। খাতার শুরুতে এক লাইনে নিজের ধরা রূপ লিখলে ভিন্ন রূপের সঠিক উত্তরও পরীক্ষক মেলাতে পারেন।
শেষ মুহূর্তের সূত্র-তালিকা আর সাধারণ ভুল
পরীক্ষার আগে এই এক টেবিলই যথেষ্ট; তৃতীয় কলামের ভুলগুলো মনে রাখাই বেশি কাজের।
| বিষয় | সূত্র | সাধারণ ভুল | পর্ব |
|---|---|---|---|
| Backprop, softmax | ReLU-তে z ≤ ০ হলে ০; ∂L/∂z = p − y | বন্ধ ReLU-তে gradient পাঠানো; batch-গড়ে ১/B বাদ | ০৪ |
| Parameter | a·b + b; LN ২d; block ≈ ১২d2 | bias বা tied head দুবার গোনা; head-সংখ্যা দিয়ে গুণ | ০৯, ৩৫, ৪৯ |
| BatchNorm | train-এ n দিয়ে ভাগ, running-এ n − ১ | eval-এ batch-এর পরিসংখ্যান ব্যবহার | ১৪ |
| AdamW, schedule | m̂ = m/(১ − βt); cosine ½η(১ + cos) | bias correction বাদ; warmup-এ t ০ না ১ গুলিয়ে ফেলা | ১৫ |
| Dropout, clipping | ১/(১ − p); g·c/‖g‖ | eval-এও mask বা গুণ | ১৬, ১৭ |
| Conv, RF | ⌊(n + ২p − d(k − ১) − ১)/s⌋ + ১; r += (k − ১)j | dilation বাদ; j দিয়ে গুণ ভুলে যাওয়া | ২০ |
| Attention | softmax(QKᵀ/√dk)V; memory ∝ T2 | √d দিয়ে ভাগ (dk নয়); কলামে softmax | ৩৩ |
| KV cache | ২·L·B·T·Hkv·dh·bytes | K আর V-এর ২ বাদ; query head দিয়ে গোনা | ৩৭, ৮৩ |
| Perplexity, BLEU, chrF | eNLL; BP·exp(গড় ln p); (১ + β2)PR/(β2P + R) | ভিন্ন tokenizer-এর PPL তুলনা; clipping বাদ | ৪৫, ৪৬ |
| Decoding | softmax(z/T); top-p-র সবচেয়ে ছোট সেট | top-p-তে p ছোঁয়ার আগেই থামা; renormalize বাদ | ৫০ |
| LoRA, quantization | r(d + k); s = max|x|/১২৭, ত্রুটি ≤ s/২ | r·d·k লেখা; GQA-র ছোট v_proj ভুলে যাওয়া | ৫৫, ৫৯ |
| Token, GPU-সময় | অনুরোধ × token; ১০6 ÷ throughput = GPU-সেকেন্ড | output-এর সংরক্ষিত জায়গা context থেকে বাদ না দেওয়া | ৬৫ |
| BM25, nDCG | IDF·tf(k1 + ১)/(…); DCG/IDCG | IDCG-এর সংজ্ঞা না লেখা; tf-কে রৈখিক ভাবা | ৬৭, ৭০ |
| KL, GAN | ½(μ2 + σ2 − ১ − ln σ2); pd/(pd + pg) | σ আর σ2 গুলিয়ে ফেলা | ৭৬, ৭৭ |
| Diffusion, CFG, FD | ᾱ = ∏(১ − β); εu + w(εc − εu); ‖Δμ‖2 + Tr(…) | ᾱ-কে যোগে হিসাব; CFG-র রূপ না লেখা | ৭৯–৮১ |
| Serving | TTFT + (N − ১)TPOT; L = λW | গড় দিয়ে SLO; batch-এ শুধু মোট throughput দেখা | ৮৩ |
২০ মিনিটের মিনি-সেট — ঘড়ি ধরে করুন
আটটি ছোট প্রশ্ন, প্রতিটিতে আড়াই মিনিট। Calculator চলবে, চেকার নয়। ৭–৮ ঠিক হলে প্রস্তুত, ৫–৬ হলে সংশ্লিষ্ট ভাগ আবার, ৪-এর কম হলে ভাগ ২ থেকে আবার শুরু করুন।
- Input ৬৪ × ৬৪, kernel ৩, stride ২, padding ১: output-এর পাশ কত?
- Linear(১০২৪, ৪০৯৬)-এ bias-সহ parameter কত?
- dk = ১৬; একটি score ৮. Scale-এর পরে মান কত?
- L = ১২, d = ৭৬৮, T = ১০২৪, B = ১, fp16: KV cache কত MiB?
- গড় NLL ০.৬৯৩১ nats: perplexity কত?
- LoRA r = ৮, matrix ৪০৯৬ × ৪০৯৬: নতুন parameter কত?
- β1 = ০.১, β2 = ০.২: ᾱ2 কত?
- λ = ৫ অনুরোধ/s, W = ২ s: গড়ে কয়টি চলমান?
উত্তরমিনি-সেটের উত্তর আর এক লাইনের কারণ (নিজে করার পরে খুলুন)
1. floor((64 + 2 - 2 - 1)/2) + 1 = 31 + 1 = 32
2. 1024 x 4096 + 4096 = 4,198,400
3. 8 / sqrt(16) = 2
4. 2 x 12 x 1024 x 768 x 2 = 37,748,736 bytes = 36 MiB
5. e^0.6931 = 2
6. 8 x (4096 + 4096) = 65,536
7. 0.9 x 0.8 = 0.72
8. L = 5 x 2 = 10
সবচেয়ে বেশি ভুল হয় ৪ আর ৭-এ: KV cache-এ K আর V-এর জন্য ২ গুণ ভুলে ১৮ MiB লেখা, আর ᾱ-কে ১ − ০.১ − ০.২ = ০.৭ লেখা (গুণফলের বদলে যোগ)।
AI-এর যুগে — AI-এর হিসাব, shape আর কোড যাচাই
AI সহকারীকে KV cache বা LoRA-র হিসাব দিলে পরিষ্কার, আত্মবিশ্বাসী উত্তর আসে। তাতে প্রায়ই একক চুপচাপ বদলে যায় (MiB না MB, fp16 না fp32), shape-এর অক্ষ গুলিয়ে যায়, বা পুরোনো library-র argument আসে। ভুলগুলো চেনা:
| AI-এর ভুল | পরিণাম | কীভাবে ধরবেন |
|---|---|---|
| পুরোনো বা বানানো API: transformers, peft বা trl-এর বাদ পড়া argument, সরানো torch function | TypeError, সতর্কবার্তা বা চুপচাপ অন্য আচরণ | নিজের version-এর মূল docs, তারপর ছোট batch-এ চালানো |
| Shape, dim বা dtype গুলিয়ে ফেলা (view বনাম permute, ভুল অক্ষে softmax) | কোড চলে, ফল ভুল | প্রতিটি ধাপে shape ছাপা; হাতে করা ছোট উদাহরণে মেলানো |
model.eval(), torch.no_grad() বা zero_grad() ভুলে যাওয়া | dropout আর BatchNorm-এ ভুল মাপ, gradient জমে যাওয়া | এক batch overfit করা; train আর eval-এ একই input-এর output তুলনা |
| Test ডেটা দিয়ে tuning, বা একটি ভাগ্যবান run-কে ফল বলা | ফোলানো score | locked test set, কয়েকটি seed, সরল baseline-এর পাশে |
| বিশ্বাসযোগ্য কিন্তু ভুল derivation বা বানানো উদ্ধৃতি | খাতায় শূন্য, রিপোর্টে মিথ্যা দাবি | ছোট সংখ্যায় নিজে আবার derive; প্রবন্ধ নিজে খুলে দেখা |
| LLM-এর আচরণকে “বোঝা” বা “জানা” বলে বর্ণনা | ক্ষমতা নিয়ে অতিরঞ্জিত সিদ্ধান্ত | মাপা metric আর ব্যর্থতার উদাহরণ দিয়ে কথা বলা |
AI-কে উত্তরদাতা নয়, যাচাইকারী হিসেবে ব্যবহার করুন। গ্রাহকের তথ্য, API key বা প্রতিষ্ঠানের গোপন config prompt-এ দেবেন না; সংখ্যাগুলো নিজের বানানো উদাহরণে বদলে নিন।
যাচাইয়ের চেকলিস্ট
নিজেকে যাচাই করুন
প্রতিটি প্রশ্নে খাতায় হিসাব করে তবে উত্তর বাছুন। সব তথ্য প্রশ্নের ভেতরেই আছে।
ক্রমDDPM-এর একটি training ধাপ সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- ডেটা থেকে একটি পরিষ্কার ছবি x0 নেওয়া
- এলোমেলোভাবে একটি ধাপ t আর noise ε বাছা
- xt = √ᾱtx0 + √(১ − ᾱt)ε বানানো
- Network দিয়ে xt আর t থেকে noise অনুমান করা
- অনুমান আর আসল ε-এর MSE থেকে gradient নিয়ে weight বদলানো
(সহজ) ৩২ × ৩২ ছবিতে ৫ × ৫ kernel, stride ১, padding ০. Output-এর পাশ কত?
- ২৮
- ২৭
- ৩২
- ৩০
(সহজ) ১০২৪ × ১০২৪ matrix-এ LoRA, r = ৮. নতুন parameter কত?
- ১০,৪৮,৫৭৬
- ৮,১৯২
- ১৬,৩৮৪
- ৮৩,৮৮,৬০৮
(মাঝারি) L = ১২, d = ৭৬৮, ১২টি head, T = ২০৪৮, B = ১, fp16. KV cache কত MiB?
- ৩৬ MiB
- ৭২ MiB
- ১৪৪ MiB
- ৮৬৪ MiB
(সহজ) Test লেখায় একটি language model-এর গড় NLL ২ nats. Perplexity কত?
- ৪.০০
- ২.০০
- ০.১৩৫
- ৭.৩৮৯
(মাঝারি) সাজানো সম্ভাবনা (০.৫, ০.৩, ০.১৫, ০.০৫), top-p = ০.৯. কয়টি token থেকে নমুনা নেওয়া হয়?
- ৩
- ২
- ৪
- ১
(সহজ) Inverted dropout, p = ০.২৫. Train mode-এ টিকে থাকা মান কত দিয়ে গুণ হয়?
- ০.৭৫
- ৪.০০
- ১.৩৩৩
- ১.০০
(সহজ) Attention-এ dk = ৬৪. QKᵀ-কে কত দিয়ে ভাগ করা হয়?
- ৬৪
- ৮
- ৪০৯৬
- head-এর সংখ্যা
(মাঝারি) একটি বিন্দুতে pdata = ০.৩, pg = ০.১. সেরা discriminator-এর মান?
- ০.২৫
- ০.৫০
- ৩.০০
- ০.৭৫
(সহজ) Diffusion-এ β1 = β2 = ০.১. ᾱ2 কত?
- ০.৮১
- ০.৮০
- ০.০১
- ০.৯০
(সহজ) প্রতি সেকেন্ডে গড়ে ৫টি অনুরোধ, প্রতিটির গড় E2E ৪ সেকেন্ড। গড়ে কয়টি অনুরোধ একসঙ্গে চলমান?
- ১.২৫
- ২০
- ০.৮
- ৯
(মাঝারি) Adam (bias correction-সহ), lr = ০.০০১. প্রথম ধাপে একটি weight-এর gradient ১০০. ধাপের মাপ প্রায় কত?
- ০.১
- ১০০
- ০.০০১
- ০.০০০০১
(সহজ) Linear(৫১২, ২৫৬), bias-সহ: parameter কত?
- ১,৩১,০৭২
- ৭৬৮
- ১,৩১,৫৮৪
- ১,৩১,৩২৮
(মাঝারি) একটি candidate অনুবাদ ৪ শব্দের, reference ৫ শব্দের। BLEU-এর brevity penalty কত?
- ০.৭৭৮৮
- ০.৮০০০
- ১.০০০০
- ১.২২১৪
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব শব্দ এখানে একসাথে:
মনে রাখুন
- একক আর রূপ আগে: MiB না MB, fp16 না fp32, bias আছে কি না, t ০ না ১ থেকে, CFG-র কোন রূপ। অর্ধেক মতভেদ এখানেই।
- Shape লিখে চলুন: attention, multi-head আর KV cache-এর প্রতিটি হিসাব আসলে shape-এর গুণফল।
- কী h-এর ওপর নির্ভর করে, কী করে না: attention-এর parameter আর KV cache-এর মাপ head-সংখ্যায় বদলায় না; GQA-র KV head বদলায়।
- এক লাইনের যাচাই: softmax-এর যোগ ১, p − y-এর যোগ ০, GPT-2-এর সূত্রে ১২,৪৪,৩৯,৮০৮, dropout-এর পরে গড় একই, V = −ln ৪ + ২JS.
- চেকার দিয়ে মেলান: আগে হাতে, তারপর কোড; কোড আগে চালালে ভুল ধাপটি শেখা হয় না।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৮৮ · MCQ প্রশ্নব্যাংক — Deep Learning, NLP আর Generative AIপরবর্তী পর্বসব মডিউলের MCQ, প্রতিটি বিকল্পের ব্যাখ্যাসহ।
- ডিপ লার্নিং, NLP আর Generative AI · কোর্স হোম৯১টি পর্বপরের পর্ব ৮৮, MCQ প্রশ্নব্যাংক, আর পুরো রোডম্যাপ এখান থেকে।
- পর্ব ০৪ · Backpropagation হাতে-কলমেআবার পড়ুনChain rule থেকে পুরো gradient, softmax-এর P − Y-সহ।
- পর্ব ৩৩ · Self-attention আর multi-headআবার পড়ুনMatrix দিয়ে হাতে attention আর √dk-এর যুক্তি।
- পর্ব ৩৭ · দক্ষ attentionআবার পড়ুনKV cache-এর সূত্র, GQA আর long context.
- পর্ব ৫৫ · LoRA, QLoRA আর adapterআবার পড়ুনযেকোনো model-এর LoRA parameter হাতে গোনা।
- পর্ব ৭৯ · Diffusion modelআবার পড়ুনNoise schedule, ᾱt আর SNR.
- পর্ব ৮৩ · LLM servingআবার পড়ুনTTFT, TPOT, batching আর Little-এর সূত্রে যাচাই।
- DSA · কোর্স হোমপাশের ট্র্যাকComplexity আর algorithm-এর হাতে-করা trace.