সরাসরি পর্বে যান
পর্ব ৮৭ · ৯১-এর মধ্যে · মডিউল ১৪

Numerical সমস্যার অনুশীলন — deep learning-এর গণিত আর হিসাব, পূর্ণ সমাধানসহ

Deep Learning Numericals — Backprop, Normalisation, Optimisers, CNN Shapes, Attention, Tokenisation, LLM Adaptation, Retrieval, Generative Models and Serving with Full Solutions

Taught in Bangla, this practice page gathers deep learning calculations into 41 fully worked problems across nine sections following the course modules: backprop and softmax, optimizers, convolution shapes and receptive fields, LSTM, attention and KV cache, BPE and perplexity, LoRA and quantization memory, retrieval metrics and PQ, KL, GAN, diffusion and FID, and serving with Little's law. Ten in-page Python checkers with NumPy verify answers, plus a formula sheet and a timed mini-set.

এই পর্বে যা পাবেন

  • 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৮২, ৮৩, ৮৫

এই পাতার নিয়ম — প্রশ্নে অন্য কিছু না বললে

বিষয়নিয়ম
logln (একক nats); bits চাইলে log2; nDCG-এর ছাড়ে log2
Parameterbias-সহ: Linear(a, b)-এ a·b + b; LayerNorm(d)-এ ২d
MemoryKiB = ২10, MiB = ২20, GiB = ২30 byte; MB, GB মানে ১০6, ১০9; float32 ৪ byte, fp16/bf16 ২ byte
ShapePyTorch-এর ক্রম: ছবি (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 গোনা

সূত্রের কার্ড

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

Two-layer ReLU network for problem 2.2 with forward values x1=1x2=2 h1h2ŷ 0.100.7 0.5−1−0.250.25 2−1 z1=0.1, b=0.1z2=−0.4 → ReLU 0c=0.5, y=1.5
ছবি ৮৭.১ — h2-এর z ঋণাত্মক, তাই ReLU তাকে বন্ধ করে (ধূসর ড্যাশ)। Backward pass-এ এই unit দিয়ে কোনো gradient যায় না, তার weight-গুলো এই ধাপে বদলায় না।
২.২(মাঝারি) 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 ধাপগুলো দেখায়।

ধাপ ১Forward
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
ধাপ ২Output-এর gradient
d  = 0.7 - 1.5 = -0.8
dv = d x h = (-0.08, 0)
dc = -0.8
ধাপ ৩Hidden-এ ফেরা
dh = d x v = (-1.6, 0.8)
ReLU: z2 < 0 -> 0
dz = (-1.6, 0)
dW1 row1 = (-1.6, -3.2)
ধাপ ৪Update, η = 0.05
W1 row1 = (0.58, -0.09)
b1 = (0.18, 0.1)
v = (2.004, -1), c = 0.54
yhat 1.7023, L 0.0205
  1. সামনে চলা। h1 = ০.১ টিকে থাকে, h2-এর z = −০.৪ বলে ReLU শূন্য দেয়। ŷ = ০.৭, loss ০.৩২.
  2. শেষ থেকে শুরু। ŷ − y = −০.৮; ঋণাত্মক মানে ŷ বাড়ালে loss কমবে। Output weight-এর gradient d × h.
  3. Hidden-এ ভাগ। d × v = (−১.৬, ০.৮), কিন্তু ReLU-র দরজা বন্ধ থাকায় h2-এর অংশ শূন্য। W1-এর প্রথম সারির gradient −১.৬ × x.
  4. এক ধাপ। η = ০.০৫-এ সব 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

সূত্রের কার্ড

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

সূত্রের কার্ড

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: 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 ঠিক শূন্য।

ধাপ ১Score = q·k
q = (1, 0, 1, 0)
k1 -> 1, k2 -> 1, k3 -> 2
scores = (1, 1, 2)
ধাপ ২÷ √dk
sqrt(4) = 2
(0.5, 0.5, 1.0)
ধাপ ৩Softmax
e^ = 1.6487, 1.6487,
     2.7183 ; sum 6.0157
w = (.2741, .2741, .4519)
ধাপ ৪V-এর মিশ্রণ
.2741(1,0) + .2741(0,1)
  + .4519(1,1)
out = (0.7259, 0.7259)
  1. মিল মাপা। q আর প্রতিটি key-র dot product: k3 দুটি ঘরে মেলে, তাই score ২.
  2. Scale. √৪ = ২ দিয়ে ভাগ; dk বড় হলে এই ভাগ softmax-কে চরম হতে দেয় না।
  3. ওজন। Softmax-এ যোগফল ১; k3 পায় ০.৪৫১৯, বাকি দুটি ০.২৭৪১ করে।
  4. মেশানো। 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

সূত্রের কার্ড

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: 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

সূত্রের কার্ড

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

সূত্রের কার্ড

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

সূত্রের কার্ড

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, softmaxReLU-তে z ≤ ০ হলে ০; ∂L/∂z = p − yবন্ধ ReLU-তে gradient পাঠানো; batch-গড়ে ১/B বাদ০৪
Parametera·b + b; LN ২d; block ≈ ১২d2bias বা tied head দুবার গোনা; head-সংখ্যা দিয়ে গুণ০৯, ৩৫, ৪৯
BatchNormtrain-এ n দিয়ে ভাগ, running-এ n − ১eval-এ batch-এর পরিসংখ্যান ব্যবহার১৪
AdamW, schedulem̂ = 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 − ১)jdilation বাদ; j দিয়ে গুণ ভুলে যাওয়া২০
Attentionsoftmax(QKᵀ/√dk)V; memory ∝ T2√d দিয়ে ভাগ (dk নয়); কলামে softmax৩৩
KV cache২·L·B·T·Hkv·dh·bytesK আর V-এর ২ বাদ; query head দিয়ে গোনা৩৭, ৮৩
Perplexity, BLEU, chrFeNLL; BP·exp(গড় ln p); (১ + β2)PR/(β2P + R)ভিন্ন tokenizer-এর PPL তুলনা; clipping বাদ৪৫, ৪৬
Decodingsoftmax(z/T); top-p-র সবচেয়ে ছোট সেটtop-p-তে p ছোঁয়ার আগেই থামা; renormalize বাদ৫০
LoRA, quantizationr(d + k); s = max|x|/১২৭, ত্রুটি ≤ s/২r·d·k লেখা; GQA-র ছোট v_proj ভুলে যাওয়া৫৫, ৫৯
Token, GPU-সময়অনুরোধ × token; ১০6 ÷ throughput = GPU-সেকেন্ডoutput-এর সংরক্ষিত জায়গা context থেকে বাদ না দেওয়া৬৫
BM25, nDCGIDF·tf(k1 + ১)/(…); DCG/IDCGIDCG-এর সংজ্ঞা না লেখা; tf-কে রৈখিক ভাবা৬৭, ৭০
KL, GAN½(μ2 + σ2 − ১ − ln σ2); pd/(pd + pg)σ আর σ2 গুলিয়ে ফেলা৭৬, ৭৭
Diffusion, CFG, FDᾱ = ∏(১ − β); εu + w(εc − εu); ‖Δμ‖2 + Tr(…)ᾱ-কে যোগে হিসাব; CFG-র রূপ না লেখা৭৯–৮১
ServingTTFT + (N − ১)TPOT; L = λWগড় দিয়ে SLO; batch-এ শুধু মোট throughput দেখা৮৩

২০ মিনিটের মিনি-সেট — ঘড়ি ধরে করুন

আটটি ছোট প্রশ্ন, প্রতিটিতে আড়াই মিনিট। Calculator চলবে, চেকার নয়। ৭–৮ ঠিক হলে প্রস্তুত, ৫–৬ হলে সংশ্লিষ্ট ভাগ আবার, ৪-এর কম হলে ভাগ ২ থেকে আবার শুরু করুন।

  1. Input ৬৪ × ৬৪, kernel ৩, stride ২, padding ১: output-এর পাশ কত?
  2. Linear(১০২৪, ৪০৯৬)-এ bias-সহ parameter কত?
  3. dk = ১৬; একটি score ৮. Scale-এর পরে মান কত?
  4. L = ১২, d = ৭৬৮, T = ১০২৪, B = ১, fp16: KV cache কত MiB?
  5. গড় NLL ০.৬৯৩১ nats: perplexity কত?
  6. LoRA r = ৮, matrix ৪০৯৬ × ৪০৯৬: নতুন parameter কত?
  7. β1 = ০.১, β2 = ০.২: ᾱ2 কত?
  8. λ = ৫ অনুরোধ/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 functionTypeError, সতর্কবার্তা বা চুপচাপ অন্য আচরণনিজের 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-কে ফল বলাফোলানো scorelocked test set, কয়েকটি seed, সরল baseline-এর পাশে
বিশ্বাসযোগ্য কিন্তু ভুল derivation বা বানানো উদ্ধৃতিখাতায় শূন্য, রিপোর্টে মিথ্যা দাবিছোট সংখ্যায় নিজে আবার derive; প্রবন্ধ নিজে খুলে দেখা
LLM-এর আচরণকে “বোঝা” বা “জানা” বলে বর্ণনাক্ষমতা নিয়ে অতিরঞ্জিত সিদ্ধান্তমাপা metric আর ব্যর্থতার উদাহরণ দিয়ে কথা বলা

AI-কে উত্তরদাতা নয়, যাচাইকারী হিসেবে ব্যবহার করুন। গ্রাহকের তথ্য, API key বা প্রতিষ্ঠানের গোপন config prompt-এ দেবেন না; সংখ্যাগুলো নিজের বানানো উদাহরণে বদলে নিন।

আমি Deep Learning-এর numerical অনুশীলন করছি। নিচে প্রশ্ন, আমার ধরা একক ও রূপ আর হাতে করা সমাধান দিলাম। প্রশ্ন: [যেমন: KV cache-এর memory / LoRA parameter / BM25 score] আমার ধরা রূপ: [যেমন: fp16 = 2 byte, MiB = 2^20; Linear-এ bias আছে; CFG ε_u + w(ε_c − ε_u)] আমার সমাধান: [ধাপগুলো পেস্ট করুন] ১. পুরো উত্তর আগে দিও না। আমার সমাধানের প্রথম যে ধাপে ভুল আছে, শুধু সেটি দেখাও। ২. আমার ধরা একক আর রূপ বদলাবে না; কিছু অস্পষ্ট হলে আগে জিজ্ঞেস করো। ৩. প্রতিটি গুণের পাশে tensor-এর shape লেখো। ৪. যাচাইয়ের জন্য ২০ লাইনের কম NumPy কোড দাও; PyTorch দিলে torch 2.x-এর বর্তমান API, বানানো argument নয়। ৫. একই ধরনের একটি নতুন, ফাঁদওয়ালা প্রশ্ন দাও; উত্তর লুকিয়ে রাখো। ৬. কোনো library-র default বা কোনো model-এর সংখ্যা বললে লিখে দাও যে docs বা model card-এ যাচাই করতে হবে। না জানলে “জানি না” বলো।

যাচাইয়ের চেকলিস্ট

নিজেকে যাচাই করুন

প্রতিটি প্রশ্নে খাতায় হিসাব করে তবে উত্তর বাছুন। সব তথ্য প্রশ্নের ভেতরেই আছে।

ক্রমDDPM-এর একটি training ধাপ সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. ডেটা থেকে একটি পরিষ্কার ছবি x0 নেওয়া
  2. এলোমেলোভাবে একটি ধাপ t আর noise ε বাছা
  3. xt = √ᾱtx0 + √(১ − ᾱt)ε বানানো
  4. Network দিয়ে xt আর t থেকে noise অনুমান করা
  5. অনুমান আর আসল ε-এর 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 কত?

  • ০.৭৭৮৮
  • ০.৮০০০
  • ১.০০০০
  • ১.২২১৪

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব শব্দ এখানে একসাথে:

মনে রাখুন

  1. একক আর রূপ আগে: MiB না MB, fp16 না fp32, bias আছে কি না, t ০ না ১ থেকে, CFG-র কোন রূপ। অর্ধেক মতভেদ এখানেই।
  2. Shape লিখে চলুন: attention, multi-head আর KV cache-এর প্রতিটি হিসাব আসলে shape-এর গুণফল।
  3. কী h-এর ওপর নির্ভর করে, কী করে না: attention-এর parameter আর KV cache-এর মাপ head-সংখ্যায় বদলায় না; GQA-র KV head বদলায়।
  4. এক লাইনের যাচাই: softmax-এর যোগ ১, p − y-এর যোগ ০, GPT-2-এর সূত্রে ১২,৪৪,৩৯,৮০৮, dropout-এর পরে গড় একই, V = −ln ৪ + ২JS.
  5. চেকার দিয়ে মেলান: আগে হাতে, তারপর কোড; কোড আগে চালালে ভুল ধাপটি শেখা হয় না।

পরের পর্বে: পর্ব ৮৮-এ পুরো কোর্সের MCQ প্রশ্নব্যাংক — Deep Learning, NLP আর Generative AI, ১৩টি মডিউল ধরে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান