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

Overfitting, bias আর variance — regularization আর learning curve

Overfitting, Bias and Variance — Validation Curves, a Real Bias–Variance Simulation, Learning Curves, Ridge and Lasso

Taught in Bangla, this lesson explains underfitting and overfitting with polynomial fits on fictional delivery data, verifies the bias-variance decomposition with an in-browser simulation, and reads validation and learning curves. It builds ridge regression by hand, matches it to scikit-learn, shows why lasso zeroes coefficients, and covers choosing lambda on validation data, early stopping and double descent.

এই পর্বে যা শিখবেন

  • Train আর validation error দেখে underfitting আর overfitting চেনা, আর U-আকৃতির validation curve-এ “ঠিক জায়গা” খোঁজা
  • Bias–variance decomposition-এর মানে, হাতে হিসাব, আর একটি সত্যিকারের simulation দিয়ে সূত্রটি যাচাই
  • k-NN-এর k আর polynomial-এর degree দিয়ে model complexity বোঝা; learning curve দেখে ঠিক করা আরও ডেটা কাজে লাগবে কি না
  • Ridge আর lasso নিজের হাতে আর scikit-learn দিয়ে, L1 বনাম L2-এর জ্যামিতি, early stopping, আর validation-এ overfit ঠেকানোর অভ্যাস

মডিউল ১০-এর এটি শেষ পর্ব। এর আগে পর্ব ৫৫ (যন্ত্র কীভাবে শেখে)-এ generalization-এর ধারণা, পর্ব ৫৬ (Train, validation আর test — data leakage-এর ফাঁদ)-এ ডেটা ভাগ করার নিয়ম, পর্ব ৫৭-এ k-NN আর পর্ব ৫৮ (Linear regression — loss, gradient descent আর closed form)-এ MSE, normal equation আর polynomial feature এসেছে। এই পর্ব সেগুলোকে একটি প্রশ্নে জোড়া দেয়: একটি model নতুন ডেটায় কেন ভুল করে, আর সেই ভুল কীভাবে কমানো যায়? চালানো যায় এমন ব্লকগুলো পাতাতেই চলে (ব্রাউজারের Python, NumPy আর scikit-learn 1.8.0). scikit-learn প্রথমবার নামতে প্রায় ১০ সেকেন্ড লাগে; পরের run দ্রুত।

১ · দুই রকম ব্যর্থতা — underfitting আর overfitting

বাজারঘর (এই কোর্সের কাল্পনিক অনলাইন দোকান) জানতে চায় দিনের কোন সময়ে অর্ডার দিলে ডেলিভারিতে কত মিনিট লাগে। সকাল ৮টা থেকে রাত ৮টাকে আমরা ০ থেকে ১-এর একটি সংখ্যা x-এ আনি (x = ০.২৫ মানে বেলা ১১টা)। ধরুন রাস্তার ভিড়ের কারণে আসল নিয়মটি একটি ঢেউ: f(x) = 40 + 12·sin(2πx) মিনিট, বেলা ১১টায় সবচেয়ে বেশি (৫২ মিনিট), বিকেল ৫টায় সবচেয়ে কম (২৮ মিনিট)। নিয়মটি পুরোপুরি বানানো; বাস্তবে আসল নিয়ম কেউ জানে না, আর ঠিক এ কারণেই এখানে জানা নিয়ম নিলাম, যাতে model-এর ভুল আসল নিয়মের সঙ্গে মেলানো যায়। প্রতিটি ডেলিভারিতে যানজট, বৃষ্টি বা রাইডারের দেরির মতো এলোমেলো প্রভাবও আছে: গড় ০, standard deviation ৪ মিনিটের normal noise. হাতে আছে মাত্র ২০টি ডেলিভারি।

এই ২০টি বিন্দুতে আমরা বিভিন্ন degree-এর polynomial বসাব। degree d-এর polynomial মানে w0 + w1t + w2t² + … + wdtd; প্রতিটি ঘাত একটি নতুন কলাম, যাকে বলে polynomial feature, আর বাকিটা পর্ব ৫৮-এর সাধারণ least squares. (t = 2x − 1, অর্থাৎ x-কে −১ থেকে ১-এ আনা, যাতে t15-এর মতো কলাম খুব ছোট না হয়ে যায়।) Model মাপব দুই জায়গায়: যে ২০টি বিন্দুতে শিখেছে (training set), আর আলাদা ২০০টি বিন্দুতে, যা শেখার সময় দেখেনি (validation set). ভুলের মাপ MSE, মিনিটের বর্গে।

পরীক্ষার প্রস্তুতির কথা ভাবুন। একজন ছাত্র শুধু অধ্যায়ের শিরোনাম পড়ে গেছেন; সব প্রশ্নেই আধাআধি উত্তর, পুরোনো প্রশ্নেও, নতুনেও। এটি underfitting. আরেকজন গত পাঁচ বছরের প্রশ্নের উত্তর দাঁড়ি-কমাসহ মুখস্থ করেছেন; পুরোনো প্রশ্নে পুরো নম্বর, কিন্তু প্রশ্ন একটু ঘুরিয়ে দিলেই শূন্য। এটি overfitting. তৃতীয়জন ধারণাগুলো বুঝেছেন, কয়েকটি পুরোনো প্রশ্নে একটু-আধটু ভুল করেন, কিন্তু নতুন প্রশ্নেও ভালো করেন। শেখার লক্ষ্য এই তৃতীয়জন: পুরোনো খাতায় নিখুঁত হওয়া নয়, নতুন খাতায় ভালো করা, যাকে বলে generalization.

import numpy as np
import matplotlib.pyplot as plt

def f(x):                      # কাল্পনিক "আসল" নিয়ম: দিনের সময় → ডেলিভারির মিনিট
    return 40 + 12 * np.sin(2 * np.pi * x)

rng = np.random.default_rng(42)
x_tr = np.sort(rng.uniform(0, 1, 20));  y_tr = f(x_tr) + rng.normal(0, 4, 20)
x_va = rng.uniform(0, 1, 200);          y_va = f(x_va) + rng.normal(0, 4, 200)

def design(x, d):              # x-কে [-1, 1]-এ এনে 1, t, t², …, t^d কলাম
    return np.vander(2 * x - 1, d + 1, increasing=True)
def fit(x, y, d):
    return np.linalg.lstsq(design(x, d), y, rcond=None)[0]
def mse(w, x, y, d):
    return np.mean((design(x, d) @ w - y) ** 2)

degs = range(16)
tr = [mse(fit(x_tr, y_tr, d), x_tr, y_tr, d) for d in degs]
va = [mse(fit(x_tr, y_tr, d), x_va, y_va, d) for d in degs]
for d in (0, 1, 3, 5, 9, 12, 15):
    print(f"degree {d:2d}: train MSE {tr[d]:6.2f} | validation MSE {va[d]:.4g}")
print("validation-এ সেরা degree:", int(np.argmin(va)))

grid = np.linspace(0, 1, 300)
fig, axes = plt.subplots(2, 2, figsize=(8, 6), layout="constrained")
for ax, d in zip(axes.flat, (1, 3, 15)):
    ax.plot(grid, f(grid), "g--", label="true f(x)")
    ax.plot(grid, design(grid, d) @ fit(x_tr, y_tr, d), "C0", label=f"degree {d}")
    ax.scatter(x_tr, y_tr, c="k", s=12, label="train")
    ax.set_ylim(15, 65); ax.set_title(f"degree {d}"); ax.legend(fontsize=7)
ax = axes[1, 1]
ax.semilogy(degs, tr, "o-", label="train MSE"); ax.semilogy(degs, va, "s-", label="validation MSE")
ax.set_xlabel("polynomial degree"); ax.set_title("validation curve"); ax.legend(fontsize=7)
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

degree  0: train MSE  74.81 | validation MSE 89.11
degree  1: train MSE  29.11 | validation MSE 39.63
degree  3: train MSE   7.90 | validation MSE 18.35
degree  5: train MSE   7.38 | validation MSE 21.09
degree  9: train MSE   6.27 | validation MSE 22.89
degree 12: train MSE   2.90 | validation MSE 948.3
degree 15: train MSE   1.86 | validation MSE 4.23e+07
validation-এ সেরা degree: 4

প্রথম ছবিতে degree 1-এর সরলরেখা ঢেউটি ধরতেই পারে না: train MSE 29.11, validation 39.63, দুটিই বড়। এটি underfitting. degree 3 ঢেউয়ের সঙ্গে প্রায় মিশে যায়; validation MSE 18.35, noise-এর variance ৪² = ১৬-এর কাছাকাছি, যার নিচে কোনো model নামতে পারে না। degree 15-এর ছবি পাগলাটে: ২০টি বিন্দুর কাছ দিয়ে যেতে গিয়ে বিন্দুগুলোর মাঝে আর দুই প্রান্তে বিশাল দোল খায়। Train MSE সবচেয়ে কম (1.86), অথচ validation MSE চার কোটির বেশি, কারণ যেসব জায়গায় train বিন্দু নেই, সেখানে অনুমান ৫০০ বা −৫০০ মিনিটও হতে পারে। সংখ্যায় সেরা degree 4 (17.98) আর degree 3 (18.35) প্রায় সমান; ২০০টি validation বিন্দুর ছোট এলোমেলোতাতেই এই তফাত হতে পারে, তাই দুটির সরলটি, degree 3, বাছাই যুক্তিসঙ্গত।

মূল সূত্র Train error একা কখনো ভালো model-এর প্রমাণ নয়। Degree বাড়ালে train error প্রায় সবসময় কমে, কারণ নমনীয় model আগের সরল model-এর সব আকার বানাতে পারে, তার ওপর আরও। প্রশ্ন সবসময় নতুন ডেটার error নিয়ে।

২ · Model complexity আর U-আকৃতির validation curve

ওপরের চতুর্থ ছবিটির নাম validation curve: একটি hyperparameter (এখানে degree) বদলাতে বদলাতে train আর validation error. Degree হলো model complexity-এর একটি মাপ: model কত রকম আকারের curve বানাতে পারে। নিচের stepper একই run-এর সংখ্যা দিয়ে curve-টি ধাপে ধাপে পড়ে।

২০টি train বিন্দু, ২০০টি validation বিন্দু, noise-এর variance ১৬
degree
parameter-এর সংখ্যা
train MSE
validation MSE
অবস্থা
  1. Degree 0. শুধু একটি সংখ্যা, সব সময়ের জন্য গড় মিনিট। ঢেউয়ের কিছুই ধরে না: train MSE ৭৪.৮১, validation ৮৯.১১. দুটিই বিশাল, আর দুটির মধ্যে ফাঁক সামান্য।
  2. Degree 1. একটি ঢালু রেখা। দুটি error-ই প্রায় অর্ধেক হলো (২৯.১১ আর ৩৯.৬৩), কিন্তু এখনো noise-এর ১৬-এর অনেক ওপরে। Model-এর আকারই আসল নিয়মের সঙ্গে মেলে না।
  3. Degree 3. চারটি parameter. Train ৭.৯০, validation ১৮.৩৫, ১৬-এর খুব কাছে। এখানে model মোটামুটি “ঠিক জায়গা”-য় (sweet spot): আসল ঢেউ ধরে, গোলমাল তেমন ধরে না।
  4. Degree 9. Train আরও কমে ৬.২৭, কিন্তু validation উল্টো বেড়ে ২২.৮৯. বাড়তি ৬টি parameter আসল ঢেউ নয়, এই ২০টি বিন্দুর এলোমেলো noise শিখছে।
  5. Degree 12. Train ২.৯০, validation ৯৪৮.৩. দুটির ফাঁক বিশাল। বিন্দুগুলোর মাঝখানে curve বড় দোল খেতে শুরু করেছে।
  6. Degree 15. ২০টি বিন্দুর জন্য ১৬টি parameter: প্রায় প্রতিটি বিন্দুর জন্য একটি করে। Train ১.৮৬, validation ৪.২৩ × ১০৭. Model ডেটা মুখস্থ করেছে, শেখেনি। Validation curve-এর বাঁ দিক underfitting, ডান দিক overfitting, মাঝের নিচু জায়গা সেরা।

এই আকারটিকে প্রায়ই “U-আকৃতির” validation curve বলা হয়: বাঁ দিকে model খুব সরল, ডান দিকে খুব নমনীয়, তলায় ঠিক জায়গা। আমাদের curve নিখুঁত U নয়, ডান দিকে খাড়া দেয়াল; ছোট ডেটায় এমনই হয়। degree 15-এর প্রান্তের দোলাগুলোর একটি পুরোনো নামও আছে: Runge's phenomenon. Carl Runge ১৯০১ সালে দেখান, সমান দূরত্বের বিন্দু দিয়ে উঁচু degree-র polynomial টানলে প্রান্তে দোলা বাড়তেই থাকে।

সৎ সতর্কতা: এখানে validation set ২০০টি বিন্দুর, train-এর দশ গুণ, কারণ ডেটা বানানো আর সস্তা। বাস্তবে ডেটা কম থাকে, আর একবারের একটি validation ভাগ নিজেই নড়বড়ে। তখন cross-validation লাগে: ডেটা কয়েক ভাগে ভাগ করে প্রতিবার একটি ভাগে যাচাই। পর্ব ৫৬-এ এর পরিচয়, আর পূর্ণ ব্যবহার পর্ব ৬৬ (Model বাছাই — cross-validation আর hyperparameter search)-এ।

৩ · Bias–variance decomposition — ভুলের তিনটি উৎস

Underfitting আর overfitting-কে একটি সূত্রে বাঁধা যায়। ধরুন আসল নিয়ম f, আর প্রতিটি পর্যবেক্ষণ y = f(x) + ε, যেখানে ε হলো গড় শূন্য, variance σ²-এর noise. আমরা একটি training set থেকে model শিখি; তার অনুমান f̂(x). এখন মনে মনে একই পদ্ধতি অনেকগুলো আলাদা training set-এ চালান, প্রতিবার বাজারঘরের নতুন ২০টি ডেলিভারি নিয়ে। প্রতিবার f̂ একটু আলাদা হবে। কোনো একটি বিন্দু x-এ, সব সম্ভাব্য training set আর নতুন noise-এর ওপর গড় নিলে (গড়কে E দিয়ে লিখি) গড় বর্গ-ভুল তিন ভাগে ভাঙে:

E[(y − f̂(x))²] = (E[f̂(x)] − f(x))² + E[(f̂(x) − E[f̂(x)])²] + σ²

গড় ভুল = bias² + variance + অপরিবর্তনীয় noise

  • Bias: সব training set-এর গড় অনুমান আসল মান থেকে কত দূরে। Model-এর ধরন নিজেই যদি আসল আকার বানাতে না পারে (সরলরেখা দিয়ে ঢেউ), তবে যত ডেটাই দিন, গড়টি ভুল জায়গায় থাকবে। এই bias পর্ব ৭২-এর সামাজিক অর্থের bias (কোনো দলের প্রতি অন্যায় ঝোঁক) নয়; একই শব্দ, আলাদা ধারণা।
  • Variance: Training set বদলালে অনুমান নিজের গড়ের চারপাশে কতটা নড়ে। নমনীয় model প্রতিটি ডেটাসেটের noise-এর পেছনে ছোটে, তাই প্রতিবার আলাদা curve দেয়।
  • Irreducible error: σ², ডেটার নিজের এলোমেলোতা। আমাদের উদাহরণে ৪² = ১৬. কোনো model-ই এর নিচে নামতে পারে না, কারণ একই সময়ের দুটি অর্ডারেও আলাদা মিনিট লাগে।

কেন ঠিক এই তিনটি? y − f̂ = (f − E[f̂]) + (E[f̂] − f̂) + ε লিখে বর্গ করুন। ε নতুন পর্যবেক্ষণের noise, training set থেকে স্বাধীন আর গড় শূন্য, তাই তার সঙ্গে গুণফলের গড় শূন্য। (E[f̂] − f̂)-এর গড়ও সংজ্ঞা অনুযায়ী শূন্য, তাই মাঝের গুণফলও যায়। থাকে তিনটি বর্গ। সূত্রটি বর্গ-ভুলের জন্য হুবহু সত্য; accuracy-র মতো অন্য মাপে এমন পরিষ্কার ভাঙন হয় না, শুধু ধারণাটি একই রকম থাকে। Variance-এর পরিসংখ্যানিক সংজ্ঞা আর গড়ের হিসাব দেখুন পর্ব ৩৭-এ।

নিচের stepper একটি বিন্দুতে, x = ০.২৫ (বেলা ১১টা, আসল মান ঠিক ৫২ মিনিট), পাঁচটি আলাদা training set থেকে শেখা model-এর অনুমান দেখায়। সংখ্যাগুলো ডেস্কটপের একটি সত্যিকারের run থেকে (NumPy 2.5.4, প্রতি সেটে ২০টি নতুন ডেলিভারি), এক দশমিকে গোল করা; বাকি হিসাব হাতে মেলানো যায়।

x = 0.25, আসল f = 52৫টি training set

            
  1. লক্ষ্য ঠিক করা। বেলা ১১টায় আসল গড় ডেলিভারি ৫২ মিনিট। প্রতিটি নতুন ডেলিভারিতে এর সঙ্গে noise যোগ হয়, variance ১৬. আমরা জানতে চাই model-এর অনুমান গড়ে কোথায় পড়ে আর কতটা নড়ে।
  2. Degree 1-এর পাঁচটি অনুমান। পাঁচটি সেট, পাঁচটি সরলরেখা, পাঁচটি অনুমান: সবগুলো ৪৫ থেকে ৪৬-এর মধ্যে। খুব স্থির, কিন্তু সবগুলোই ৫২ থেকে অনেক নিচে।
  3. Degree 1-এর bias. গড় ৪৫.৯, আসল মানের চেয়ে ৬.১ মিনিট কম। bias² = ৩৭.২১. সরলরেখা ঢেউয়ের চূড়ায় পৌঁছাতেই পারে না; পাঁচটি সেট কেন, পাঁচ হাজার সেটেও এই ভুল থাকবে।
  4. Degree 1-এর variance. গড় থেকে দূরত্বের বর্গের গড় মাত্র ০.১৩৬. সরল model ডেটাসেট বদলালেও প্রায় একই উত্তর দেয়: উচ্চ bias, নিম্ন variance.
  5. Degree 15-এর পাঁচটি অনুমান। ৩৭.২ থেকে ৬৪.৪, ছড়ানো অনেক বেশি। কোনো সেটে curve এই বিন্দুতে নিচে দোল খেয়েছে, কোনোটিতে ওপরে।
  6. Degree 15-এর bias আর variance. গড় ৫১.৫২, আসল মানের খুব কাছে: bias² মাত্র ০.২৩. কিন্তু variance ৭৪.৮৪. গড় ভুল আনুমানিক ৯১.০৭, degree 1-এর ৫৩.৩৫-এর চেয়েও খারাপ: নিম্ন bias, উচ্চ variance. মাত্র ৫টি সেটের হিসাব নিজেই নড়বড়ে; পরের অংশে ২০০টি সেট আর ৪০টি বিন্দু নিয়ে মাপব।

দুজন তীরন্দাজের কথা ভাবুন। প্রথমজনের পাঁচটি তীর একে অপরের গা ঘেঁষে লাগে, কিন্তু লক্ষ্যের বাঁ দিকে, কারণ তাঁর ধনুকের নিশানা বাঁকা: উচ্চ bias, নিম্ন variance. দ্বিতীয়জনের তীর লক্ষ্যের চারদিকে ছড়ানো, গড় করলে ঠিক মাঝখানে, কিন্তু কোনোটিই কাছে নয়: নিম্ন bias, উচ্চ variance.

৪ · Simulation দিয়ে সূত্র যাচাই — ২০০টি নতুন training set

সূত্রটি বিশ্বাস না করে মেপে দেখা যাক। আসল f জানা, তাই আমরা যত খুশি নতুন training set বানাতে পারি, যা বাস্তবে অসম্ভব। প্রতিটি degree-র জন্য: ২০০ বার নতুন ২০টি ডেলিভারি বানাই, model শিখি, আর ৪০টি নির্দিষ্ট সময়ে অনুমান রাখি। তারপর প্রতিটি সময়ে ২০০টি অনুমানের গড় থেকে bias², ছড়ানো থেকে variance, আর সব সময়ের গড়। পাশাপাশি সরাসরি ভুলও মাপি: প্রতিটি model-কে নতুন noise-সহ test label দিয়ে। সূত্র ঠিক হলে bias² + variance + ১৬ আর মাপা ভুল প্রায় সমান হবে।

একটি ছোট সিদ্ধান্ত: প্রতিটি training set-এর x পুরো এলোমেলো নয়, প্রতি ১/২০ ঘরে একটি এলোমেলো বিন্দু। পুরো এলোমেলো x-এ মাঝে মাঝে বড় ফাঁক পড়ত আর উঁচু degree সেখানে লাফ দিত; সূত্র তখনো মিলত, কিন্তু ছবি পড়া কঠিন হতো। মোট প্রায় ৩,২০০টি fit, তাই ব্লকটি চলতে এক-দুই সেকেন্ড লাগতে পারে।

import numpy as np
import matplotlib.pyplot as plt

f = lambda x: 40 + 12 * np.sin(2 * np.pi * x)        # জানা "আসল" নিয়ম
design = lambda x, d: np.vander(2 * x - 1, d + 1, increasing=True)
rng = np.random.default_rng(42)
SIGMA, N, R = 4.0, 20, 200            # noise-এর sd, প্রতি সেটে বিন্দু, কতবার নতুন সেট
x0 = np.linspace(0.05, 0.95, 40)      # যে বিন্দুগুলোতে bias আর variance মাপব
degs = range(16)
rows = []
for d in degs:
    preds, errs = np.empty((R, x0.size)), np.empty(R)
    for r in range(R):                # প্রতিবার সম্পূর্ণ নতুন training set
        x = (np.arange(N) + rng.uniform(0, 1, N)) / N   # প্রতি 1/N ঘরে একটি এলোমেলো x
        y = f(x) + rng.normal(0, SIGMA, N)
        w = np.linalg.lstsq(design(x, d), y, rcond=None)[0]
        preds[r] = design(x0, d) @ w
        y0 = f(x0) + rng.normal(0, SIGMA, x0.size)   # নতুন test label
        errs[r] = np.mean((y0 - preds[r]) ** 2)
    bias2 = np.mean((preds.mean(axis=0) - f(x0)) ** 2)
    var = np.mean(preds.var(axis=0))
    rows.append((bias2, var, errs.mean()))
    if d in (0, 1, 3, 6, 9, 12, 15):
        print(f"deg {d:2d}: bias² {bias2:6.2f} var {var:6.2f} | "
              f"bias²+var+σ² {bias2 + var + SIGMA**2:6.2f} | measured {errs.mean():6.2f}")

b2, v, e = np.array(rows).T
plt.figure(figsize=(6, 3.6), layout="constrained")
plt.semilogy(degs, b2, "o-", label="bias²"); plt.semilogy(degs, v, "s-", label="variance")
plt.semilogy(degs, b2 + v + SIGMA**2, "k--", label="bias² + variance + noise")
plt.semilogy(degs, e, "^", c="C3", label="measured test MSE")
plt.axhline(SIGMA**2, c="gray", lw=0.8); plt.xlabel("polynomial degree")
plt.ylabel("error (log scale)"); plt.legend(fontsize=8); plt.title("bias-variance trade-off")
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

deg  0: bias²  77.79 var   0.75 | bias²+var+σ²  94.54 | measured  96.16
deg  1: bias²  22.84 var   1.35 | bias²+var+σ²  40.19 | measured  40.07
deg  3: bias²   0.53 var   2.89 | bias²+var+σ²  19.42 | measured  19.21
deg  6: bias²   0.04 var   4.84 | bias²+var+σ²  20.88 | measured  21.51
deg  9: bias²   0.02 var   8.27 | bias²+var+σ²  24.29 | measured  24.25
deg 12: bias²   0.13 var  19.82 | bias²+var+σ²  35.95 | measured  35.60
deg 15: bias²   1.33 var 497.14 | bias²+var+σ² 514.47 | measured 517.71

প্রথম কথা, সূত্রটি মেলে: প্রতিটি সারিতে bias² + variance + ১৬ আর সরাসরি মাপা ভুল প্রায় সমান (৪০.১৯ বনাম ৪০.০৭, ৫১৪.৪৭ বনাম ৫১৭.৭১)। যেটুকু তফাত, তা ২০০টি সেটের সীমিত নমুনার এলোমেলোতা। দ্বিতীয় কথা, দুটি ভাগ উল্টো দিকে চলে। bias² degree 0-তে ৭৭.৭৯, degree 1-এ ২২.৮৪, আর degree 3-এ হঠাৎ ০.৫৩: তিন ঘাতের polynomial এই ঢেউটি প্রায় ধরতে পারে। Variance ০.৭৫ থেকে ধীরে বেড়ে degree 12-এ ১৯.৮২, আর degree 15-এ ৪৯৭. ছবিতে মোট ভুলের কালো রেখা degree 3–5-এর কাছে সবচেয়ে নিচে, ধূসর noise-রেখার একটু ওপরে। উঁচু degree-তে bias² আবার সামান্য বাড়ে (১.৩৩), কারণ বিশাল দোলাগুলোর গড়ও পুরো কাটাকাটি হয় না। এই ছবিটিই bias–variance trade-off: complexity বাড়ালে একটি কমে, অন্যটি বাড়ে, আর মোট ভুলের তলা কোথাও মাঝখানে।

৫ · দ্বিতীয় উদাহরণ — k-NN-এর k

Trade-off শুধু polynomial-এর নয়। পর্ব ৫৭ (k-Nearest Neighbors — NumPy দিয়ে নিজে লেখা)-এ k-NN নতুন বিন্দুর সবচেয়ে কাছের k-টি উদাহরণের গড় (regression-এ) বা ভোট (classification-এ) নেয়। এখানে complexity-র দিক উল্টো: k ছোট মানে model বেশি নমনীয়। k = 1 হলে অনুমান হলো ঠিক সবচেয়ে কাছের একটি ডেলিভারির মিনিট, সেই ডেলিভারির noise-সহ। k বড় হলে অনেকের গড়, তাই মসৃণ, কিন্তু দূরের বিন্দুও ঢুকে পড়ে আর ঢেউয়ের চূড়া চ্যাপ্টা হয়ে যায়। একই simulation k-NN দিয়ে চালালাম (প্রতি সেটে ৪০টি বিন্দু), তারপর scikit-learn-এর validation_curve দিয়ে ২০০টি বিন্দুতে ৫-fold যাচাই। মূল অংশ:

for k in (1, 3, 7, 15, 30):
    for r in range(R):        # 200টি নতুন training set, প্রতিটিতে 40টি বিন্দু
        m = KNeighborsRegressor(n_neighbors=k).fit(x.reshape(-1, 1), y)
        preds[r] = m.predict(x0.reshape(-1, 1))
    ...
tr, va = validation_curve(KNeighborsRegressor(), X, y,
                          param_name="n_neighbors", param_range=ks,
                          cv=KFold(5, shuffle=True, random_state=42),
                          scoring="neg_mean_squared_error")

সত্যিকারের run (ডেস্কটপ, Python 3.14.4, NumPy 2.5.4, scikit-learn 1.8.0):

~/bazarghar-ai$ python knn_bv.py
k-NN regression, 200 training sets of 40 points
k= 1: bias²   0.05  variance  16.47  total  32.52
k= 3: bias²   0.10  variance   6.17  total  22.27
k= 7: bias²   0.72  variance   3.70  total  20.41
k=15: bias²   5.15  variance   3.28  total  24.43
k=30: bias²  44.62  variance   4.57  total  65.19

validation_curve, 200 points, 5-fold
k= 1: train MSE   0.00 | validation MSE  30.65
k= 3: train MSE  10.40 | validation MSE  20.58
k= 5: train MSE  12.43 | validation MSE  17.58
k= 9: train MSE  13.25 | validation MSE  16.34
k=15: train MSE  14.65 | validation MSE  15.99
k=25: train MSE  15.84 | validation MSE  16.84
k=40: train MSE  18.96 | validation MSE  19.85
k=70: train MSE  24.83 | validation MSE  25.14

k = 1-এ variance ১৬.৪৭, প্রায় noise-এর σ²-এর সমান, কারণ অনুমানটি একটি মাত্র noisy পর্যবেক্ষণ। bias তখন প্রায় শূন্য। k বাড়লে variance কমে (k স্বাধীন noise-এর গড় নিলে variance মোটামুটি σ²/k), কিন্তু k = ৩০-এ (৪০টির ৩০টি!) bias² ৪৪.৬২-এ লাফায়: প্রায় পুরো দিনের গড় নেওয়া হচ্ছে। k = ৩০-এ variance একটু বেড়েছে, কারণ কোন ৩০টি বিন্দু ঢুকবে তা x-এর এলোমেলো অবস্থানের ওপর অনেক নির্ভর করে। validation_curve-এ k = 1-এর train MSE হুবহু 0.00: প্রতিটি train বিন্দুর সবচেয়ে কাছের প্রতিবেশী সে নিজেই। এই শূন্য দেখে খুশি হওয়ার কিছু নেই; validation-এ ৩০.৬৫. সেরা জায়গা k ≈ ৯–১৫, validation MSE ১৬-এর একেবারে কাছে।

দুই model, এক নিয়ম: polynomial-এ degree বাড়ালে, k-NN-এ k কমালে complexity বাড়ে। দুই ক্ষেত্রেই train error কমে, validation error প্রথমে কমে তারপর বাড়ে। Hyperparameter-এর নাম আর দিক আলাদা, কিন্তু ছবি একই। Decision tree-র গভীরতা বা Naive Bayes-এর smoothing-এর α (পর্ব ৫১)-ও এমন নিয়ন্ত্রক।

৬ · Learning curve — আরও ডেটা কখন কাজে লাগে

Validation curve-এ ডেটা স্থির রেখে complexity বদলাই। Learning curve-এ উল্টো: model স্থির, training ডেটার পরিমাণ বদলাই, আর প্রতিবার train আর validation error মাপি। এই ছবি একটি দামি প্রশ্নের উত্তর দেয়: বাজারঘর কি আরও হাজারখানেক ডেলিভারির ডেটা জোগাড় করবে, নাকি model বদলাবে? নিচের ব্লকে দুটি model: degree 1 (সরল) আর degree 12 (নমনীয়), ৪০০টি বিন্দুর ডেটায় ৫-fold cross-validation, scikit-learn-এর learning_curve দিয়ে।

import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import learning_curve, KFold

rng = np.random.default_rng(42)
x = rng.uniform(0, 1, 400)
y = 40 + 12 * np.sin(2 * np.pi * x) + rng.normal(0, 4, 400)   # noise variance 16
X = (2 * x - 1).reshape(-1, 1)

models = {"degree 1 (simple)": 1, "degree 12 (flexible)": 12}
sizes = [15, 20, 30, 50, 80, 140, 220, 320]
cv = KFold(n_splits=5, shuffle=True, random_state=42)
fig, axes = plt.subplots(1, 2, figsize=(9, 3.4), sharey=True, layout="constrained")
for ax, (name, d) in zip(axes, models.items()):
    model = make_pipeline(PolynomialFeatures(d, include_bias=False),
                          StandardScaler(), LinearRegression())
    n, tr, va = learning_curve(model, X, y, train_sizes=sizes, cv=cv,
                               scoring="neg_mean_squared_error")
    tr, va = -tr.mean(axis=1), -va.mean(axis=1)            # চিহ্ন উল্টে MSE
    print(name)
    for k in (0, 2, 4, 7):
        print(f"  n={n[k]:3d}: train MSE {tr[k]:7.2f} | validation MSE {va[k]:.4g}")
    ax.plot(n, tr, "o-", label="train MSE"); ax.plot(n, va, "s-", label="validation MSE")
    ax.axhline(16, c="gray", ls=":", label="noise level")
    ax.set_ylim(0, 60); ax.set_title(name)
    ax.set_xlabel("training set size"); ax.legend(fontsize=7)
axes[0].set_ylabel("MSE")
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

degree 1 (simple)
  n= 15: train MSE   40.27 | validation MSE 47.07
  n= 30: train MSE   40.54 | validation MSE 47.55
  n= 80: train MSE   41.40 | validation MSE 46.65
  n=320: train MSE   45.71 | validation MSE 45.86
degree 12 (flexible)
  n= 15: train MSE    0.64 | validation MSE 6.935e+08
  n= 30: train MSE    8.99 | validation MSE 832.6
  n= 80: train MSE   11.98 | validation MSE 21.63
  n=320: train MSE   15.52 | validation MSE 16.7

scikit-learn “বড় মানই ভালো” নিয়মে score দেয়, তাই MSE আসে ঋণাত্মক রূপে (neg_mean_squared_error); চিহ্ন উল্টে নিয়েছি। বাঁ ছবি, উচ্চ bias: ১৫টি বিন্দুতেই দুটি curve প্রায় ৪০–৪৭-এ, আর ৩২০টিতে ৪৫.৭১ আর ৪৫.৮৬-এ মিলে গেছে। মিলেছে, কিন্তু noise-এর ১৬-এর অনেক ওপরে। ডেটা ২০ গুণ করেও লাভ প্রায় শূন্য; সরলরেখা ঢেউ ধরতে পারে না, সমস্যা ডেটার নয়, model-এর। ডান ছবি, উচ্চ variance: ১৫টি বিন্দুতে train MSE ০.৬৪, validation প্রায় ৬৯ কোটি (ছবির বাইরে)। ডেটা বাড়ার সঙ্গে ফাঁক দ্রুত বন্ধ হয়: ৮০টিতে ২১.৬৩, ৩২০টিতে ১৬.৭. এখানে আরও ডেটা সবচেয়ে ভালো ওষুধ। ছোট ভাগে গড় validation MSE একটি-দুটি বিস্ফোরিত fold-এর টানে বিশাল দেখায়; এটিও উচ্চ variance-এর লক্ষণ।

ছবিতে যা দেখছেনমানেআরও ডেটায় লাভ?
দুটি curve কাছাকাছি, কিন্তু দুটিই আপনার লক্ষ্যের বা noise-এর অনেক ওপরেউচ্চ bias (underfitting)প্রায় না; বেশি নমনীয় model বা ভালো feature লাগবে
Train error খুব কম, validation অনেক বেশি, ডেটা বাড়লে ফাঁক কমছেউচ্চ variance (overfitting)হ্যাঁ; সঙ্গে regularization বা সরল model
দুটি curve কাছাকাছি আর লক্ষ্যের কাছেভালো অবস্থাঅল্প; noise-এর নিচে কেউ নামতে পারে না
Validation curve শেষ বিন্দুতেও নামছেডেটা এখনো কমসম্ভবত হ্যাঁ; ঢাল দেখে আন্দাজ

৭ · Regularization ১ — Ridge (L2), নিজের হাতে

Degree বা k বদলানো complexity কমানোর মোটা উপায়: পুরো একটি ঘাত বাদ বা রাখা। Regularization একটি সূক্ষ্ম উপায়: model-এর আকার রেখে দিই, কিন্তু loss-এর সঙ্গে একটি শাস্তি যোগ করি, যাতে বড় coefficient “দামি” হয়। Ridge regression-এ শাস্তি coefficient-এর বর্গের যোগফল, অর্থাৎ L2 norm-এর বর্গ:

J(w) = ∑i (yi − xiᵀw − b)² + λ ∑j wj²

প্রথম অংশ সাধারণ বর্গ-ভুলের যোগফল (MSE-এর n গুণ), দ্বিতীয় অংশ শাস্তি। λ (lambda) ≥ ০ একটি hyperparameter: λ = ০ মানে সাধারণ least squares, λ খুব বড় মানে সব w প্রায় শূন্য। b হলো intercept, যাকে সাধারণত শাস্তি দেওয়া হয় না; না হলে শুধু y-এর একক বদলালেই (মিনিট থেকে সেকেন্ড) model বদলে যেত। পর্ব ৫৮-এর মতো gradient শূন্য বসালে সমাধান আসে এক লাইনে (X আর y আগে থেকে গড় বাদ দেওয়া, যাতে b আলাদা হয়ে যায়):

w = (XᵀX + λI)−1 Xᵀy,   b = ȳ − x̄ᵀw

I হলো identity matrix, Xᵀ হলো X-এর transpose. সূত্রটি normal equation-এর মতোই, শুধু XᵀX-এর কর্ণে λ যোগ। এর একটি বাড়তি সুফল আছে: দুটি feature প্রায় একই হলে XᵀX প্রায় singular হয়ে যায় (inverse অস্থির, পর্ব ১৭-এর condition number বিশাল); কর্ণে λ যোগ করলে matrix-টি সবসময় invertible আর স্থির থাকে। এটি একটি closed-form সমাধান। Matrix-এর গুণ আর np.linalg.solve দেখুন পর্ব ১৭-এ।

দুই feature, center করা ডেটাw = (A + λI)⁻¹ Xᵀy

            
  1. প্রস্তুতি। দুটি feature, ডেটা আগে থেকে center করা, তাই intercept আলাদা। XᵀX-এর কর্ণের বাইরের ৬ বলে দুই feature-এর মধ্যে মিল আছে। 2×2 matrix-এর inverse: [[a, b], [c, d]]−1 = (1/det)·[[d, −b], [−c, a]].
  2. λ = ০, সাধারণ least squares. w = [২, ০]: সব ওজন প্রথম feature-এ, দ্বিতীয়টি শূন্য। দৈর্ঘ্য ‖w‖ = ২.
  3. λ = ২. কর্ণ ১০ থেকে ১২. w1 নেমে ১.৫৫৬, আর w2 শূন্য থেকে উঠে ০.২২২! Ridge মিলওয়ালা feature-দের মধ্যে ওজন ভাগ করে দিতে পছন্দ করে, কারণ [১.৫৬, ০.২২]-এর বর্গের যোগফল [২, ০]-এর চেয়ে কম। মোট দৈর্ঘ্য কমে ১.৫৭১.
  4. λ = ১০. w = [০.৯০১, ০.৩৩০], দৈর্ঘ্য ০.৯৬০. প্রতিটি coefficient একা একা সবসময় কমে না, কিন্তু পুরো vector-এর দৈর্ঘ্য প্রতিবার কমছে।
  5. λ = ১০০. শাস্তিই প্রধান: w = [০.১৭৬, ০.০৯৯], দৈর্ঘ্য ০.২০২. λ → ∞ হলে w → ০, model শুধু গড় y বলবে, অর্থাৎ degree 0-এর মতো উচ্চ bias. λ হলো bias আর variance-এর মধ্যে একটি মসৃণ নব (knob).

শূন্যের দিকে এই ছোট হয়ে আসাকে বলে shrinkage. ছোট coefficient মানে input একটু বদলালে অনুমান কম লাফায়, অর্থাৎ variance কমে; দাম হিসেবে একটু bias বাড়ে। বাস্তবে একটি নিয়ম মানতেই হয়: ridge-এর আগে feature-গুলো একই মাপে আনুন (standardization). না হলে কিলোমিটারে মাপা দূরত্ব আর গ্রামে মাপা ওজন আলাদা পরিমাণ শাস্তি পাবে, শুধু এককের কারণে। পর্ব ৩৩-এ দেখেছেন scaling কীভাবে দূরত্ব বদলায়; শাস্তির ক্ষেত্রেও একই কথা। এই scaling শুধু train ডেটায় fit করতে হয় (পর্ব ৫৬)।

৮ · Regularization ২ — Lasso (L1), জ্যামিতি আর λ বাছাই

Lasso (Robert Tibshirani, ১৯৯৬) শাস্তি হিসেবে বর্গের বদলে পরম মানের যোগফল নেয়, অর্থাৎ L1 norm: λ∑|wj|. ছোট বদল, বড় ফল: lasso অনেক coefficient ঠিক শূন্য করে দেয়। ফলে model নিজে থেকেই কিছু feature বাদ দেয়, যাকে বলে feature selection, আর এমন model-কে বলে sparse (sparsity). কেন ঠিক শূন্য? জ্যামিতিতে দেখা সবচেয়ে সহজ। শাস্তিওয়ালা সমস্যাকে এভাবেও লেখা যায়: “শাস্তির মাপ একটি সীমার মধ্যে রেখে বর্গ-ভুল যত কম সম্ভব”. দুই coefficient-এ L2-এর সীমা একটি বৃত্ত, L1-এর সীমা একটি হীরা (কোণ অক্ষের ওপর)। বর্গ-ভুলের সমান-মানের রেখাগুলো ডিম্বাকার, কেন্দ্রে সাধারণ least squares-এর উত্তর ŵ. ডিম্বাকার রেখা বাড়তে বাড়তে যেখানে প্রথম সীমাটিকে ছোঁয়, সেটিই সমাধান।

L2 বৃত্ত বনাম L1 হীরা: কেন lasso coefficient ঠিক শূন্য করে

Constraint regions for ridge (circle) and lasso (diamond) with elliptical loss contours; schematic

Ridge circle versus lasso diamond with loss contours L2 (ridge): বৃত্ত L1 (lasso): হীরা w₁ →w₂ w₁ →w₂ ŵŵ w₁, w₂ দুটিই ≠ 0 কোণে ছোঁয়া: w₂ = 0

বৃত্তের কোনো কোণ নেই, তাই ডিম্বাকার রেখা সাধারণত এমন বিন্দুতে ছোঁয় যেখানে দুটি coefficient-ই অশূন্য: ridge ছোট করে, শূন্য করে না। হীরার কোণগুলো অক্ষের ওপর আর বাইরের দিকে খোঁচা, তাই অনেক দিক থেকে আসা ডিম্বাকার রেখা প্রথমে কোণেই ঠেকে, যেখানে একটি coefficient ঠিক শূন্য। অনেক মাত্রায় হীরার কোণ আর ধার আরও বেশি, তাই অনেক coefficient একসঙ্গে শূন্য হয়। (ছবিটি ধারণাগত; ডিম্বাকার রেখাগুলো হিসাব করে এমনভাবে আঁকা যে সত্যিই সীমাকে ছোঁয়।)

এবার সব একসঙ্গে, ১৩টি feature-এর একটি কাল্পনিক ডেটায়: বাজারঘরের ৬০টি ডেলিভারি, যেখানে শুধু দূরত্ব, ওজন আর যানজট আসলে কাজের (ওজন ৬, ৩ আর −২), আর বাকি ১০টি কলাম পুরো এলোমেলো সংখ্যা। ব্লকটি (১) নিজের লেখা ridge-কে scikit-learn-এর Ridge-এর সঙ্গে মেলায়, (২) λ বদলে coefficient-এর পথ আঁকে, আর (৩) Lasso-কে তিনটি শক্তিতে চালায়।

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge, Lasso

rng = np.random.default_rng(42)
n = 60                                   # বাজারঘরের ৬০টি কাল্পনিক ডেলিভারি
X = rng.normal(0, 1, (n, 13))            # সব কলাম আগেই standardize করা ধরে নিন
true_w = np.array([6.0, 3.0, -2.0] + [0.0] * 10)   # শুধু প্রথম ৩টি কাজের
y = 35 + X @ true_w + rng.normal(0, 3, n)

def ridge_scratch(X, y, lam):            # intercept-এ শাস্তি নেই: আগে center করি
    xm, ym = X.mean(axis=0), y.mean()
    Xc, yc = X - xm, y - ym
    w = np.linalg.solve(Xc.T @ Xc + lam * np.eye(X.shape[1]), Xc.T @ yc)
    return w, ym - xm @ w

w0 = ridge_scratch(X, y, 0.0)[0]         # λ = 0 মানে সাধারণ least squares
print("λ=0: প্রথম ৩টি", np.round(w0[:3], 2), "| অকাজের ১০টির সর্বোচ্চ |w|:", round(np.abs(w0[3:]).max(), 2))
w, b = ridge_scratch(X, y, 10.0)
sk = Ridge(alpha=10.0).fit(X, y)
print("scratch w[:3]:", np.round(w[:3], 3), " b:", round(b, 3))
print("sklearn w[:3]:", np.round(sk.coef_[:3], 3), " b:", round(sk.intercept_, 3))
print("সর্বোচ্চ পার্থক্য:", np.abs(w - sk.coef_).max())

lams = np.logspace(-2, 4, 40)
path = np.array([ridge_scratch(X, y, l)[0] for l in lams])
for a in (0.1, 0.5, 1.0):
    las = Lasso(alpha=a).fit(X, y)
    print(f"Lasso alpha={a}: ঠিক 0 হয়েছে {np.sum(las.coef_ == 0)}/13; প্রথম ৩টি",
          np.round(las.coef_[:3], 2), "| অশূন্য:", np.flatnonzero(las.coef_))
plt.figure(figsize=(6, 3.6), layout="constrained")
plt.semilogx(lams, path[:, :3], lw=2); plt.semilogx(lams, path[:, 3:], c="gray", lw=0.7)
plt.legend(["w0 distance", "w1 weight", "w2 traffic", "10 useless"], fontsize=7)
plt.xlabel("lambda (log scale)"); plt.ylabel("coefficient"); plt.title("ridge shrinkage path")
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

λ=0: প্রথম ৩টি [ 6.78  2.65 -3.3 ] | অকাজের ১০টির সর্বোচ্চ |w|: 1.03
scratch w[:3]: [ 5.784  2.489 -2.74 ]  b: 34.835
sklearn w[:3]: [ 5.784  2.489 -2.74 ]  b: 34.835
সর্বোচ্চ পার্থক্য: 8.881784197001252e-16
Lasso alpha=0.1: ঠিক 0 হয়েছে 2/13; প্রথম ৩টি [ 6.67  2.56 -3.2 ] | অশূন্য: [ 0  1  2  4  5  6  7  8 10 11 12]
Lasso alpha=0.5: ঠিক 0 হয়েছে 9/13; প্রথম ৩টি [ 6.27  2.37 -2.64] | অশূন্য: [0 1 2 5]
Lasso alpha=1.0: ঠিক 0 হয়েছে 10/13; প্রথম ৩টি [ 5.81  2.09 -2.13] | অশূন্য: [0 1 2]

λ = ০-তে সাধারণ least squares অকাজের কলামগুলোকেও ১.০৩ পর্যন্ত ওজন দিয়েছে: ৬০টি বিন্দুতে ১৩টি parameter, তাই এলোমেলো কলামের সঙ্গেও কাকতালীয় মিল পাওয়া যায়। এটি variance. নিজের লেখা ridge আর scikit-learn প্রায় 10−15 পর্যন্ত মেলে, অর্থাৎ float-এর গোলের ভুলটুকু ছাড়া হুবহু। দুটি জিনিস খেয়াল করার মতো: scikit-learn-এ λ-এর নাম alpha, আর Ridge নিজেই center করে intercept-কে শাস্তির বাইরে রাখে, ঠিক আমাদের ridge_scratch-এর মতো। ছবিতে λ বাড়ার সঙ্গে সব coefficient ধীরে শূন্যের দিকে নামে, কিন্তু কোনোটিই ঠিক শূন্য হয় না। Lasso-তে ছবি অন্য: alpha = ০.৫-এ ১৩টির ৯টি ঠিক 0, alpha = ১-এ অকাজের ১০টির ১০টিই 0, আর তিনটি আসল feature টিকে আছে (যদিও কিছুটা ছোট: যানজটের −৩.৩ থেকে −২.১৩)। একটি সূক্ষ্ম তফাত: scikit-learn-এর Lasso বর্গ-ভুলকে ১/(২n) দিয়ে গুণ করে, Ridge করে না; তাই দুই model-এর alpha-র মান সরাসরি তুলনীয় নয়।

Elastic net (Hui Zou আর Trevor Hastie, ২০০৫): L1 আর L2 দুটি শাস্তি একসঙ্গে, scikit-learn-এ ElasticNet(alpha, l1_ratio). Lasso-র একটি দুর্বলতা হলো প্রায় একই রকম কয়েকটি feature থাকলে সে প্রায় খেয়ালখুশিমতো একটি রাখে, বাকিগুলো শূন্য করে, আর ডেটা একটু বদলালে অন্যটি রাখে। L2 অংশ যোগ করলে মিলওয়ালা feature-গুলো একসঙ্গে থাকতে চায়, ঠিক stepper-এ ridge যেমন ওজন ভাগ করেছিল।

λ বাছাই হবে validation-এ, test-এ নয়

λ একটি hyperparameter, তাই degree আর k-এর মতো এটিও train ডেটা দিয়ে বাছা যায় না (train-এ λ = ০ সবসময় জেতে)। পর্ব ৫৬-এর নিয়ম: train-এ শিখুন, validation-এ বাছুন, আর test-এ শেষে একবার মাপুন। নিচে degree 12-এর polynomial-এ ridge, ৩০টি train বিন্দু, ১০০টি validation, ২০০০টি test. সত্যিকারের run (ডেস্কটপ, একই version):

~/bazarghar-ai$ python choose_lambda.py
lambda=1e-06   train    6.86 | validation    44.34
lambda=0.0001  train    7.79 | validation    21.97
lambda=0.001   train    7.84 | validation    21.93
lambda=0.01    train    7.99 | validation    23.66
lambda=0.1     train    8.86 | validation    25.26
lambda=1       train   13.30 | validation    23.40
lambda=10      train   26.88 | validation    37.22
lambda=100     train   54.67 | validation    59.03
validation-এ সেরা lambda = 0.001; test MSE (একবার) = 18.51
lambda=1e-6-এর test MSE = 77.59

Train MSE λ-এর সঙ্গে কেবল বাড়ে, তাই train দিয়ে বাছলে ভুল হতো। Validation-এ সেরা λ = ০.০০১, আর সেই একটি model-এর test MSE ১৮.৫১; প্রায় শাস্তিহীন model-এর (λ = 10−6) test MSE ৭৭.৫৯. এখানে degree 12 বদলাইনি, শুধু শাস্তি যোগ করেছি, তাতেই overfitting প্রায় সেরে গেছে। (Polynomial feature-গুলো আগে StandardScaler দিয়ে একই মাপে আনা।) বাস্তব কাজে validation-এর বদলে cross-validation, আর scikit-learn-এ RidgeCV বা GridSearchCV; পর্ব ৬৬-এ।

Regularization-এর আরও রূপ: early stopping, dropout, augmentation

Early stopping শাস্তির সূত্র ছাড়াই regularization. পর্ব ৩৪-এর gradient descent শূন্য থেকে শুরু করে ধাপে ধাপে coefficient বড় করে; প্রতিটি epoch-এর পর validation loss দেখুন, আর তা বেশ কিছুক্ষণ না কমলে থামুন, সেরা অবস্থার ওজন রেখে। কম ধাপ মানে coefficient ছোট থাকে, অনেকটা ridge-এর মতো; linear least squares-এ এই মিল গাণিতিকভাবেও দেখানো যায়, যদিও দুটি হুবহু এক নয়। পরের মডিউলের neural network-এ আরও দুটি পরিচিত কৌশল আসবে, এখানে শুধু নাম: dropout (training-এর প্রতি ধাপে কিছু neuron এলোমেলোভাবে বন্ধ রাখা) আর data augmentation (ছবি একটু ঘুরিয়ে বা কেটে নতুন উদাহরণ বানানো, অর্থ না বদলে)। দুটির লক্ষ্যই এক: model যেন train ডেটার খুঁটিনাটি মুখস্থ না করে।

আরও গভীরে: Regularization-কে পর্ব ৫০-এর MAP হিসেবে পড়া যায়। Noise normal ধরলে বর্গ-ভুল কমানো মানে likelihood বাড়ানো (MLE). এখন প্রতিটি w-এর ওপর গড় শূন্যের একটি normal (Gaussian) prior বসালে −log prior হয় w²-এর সমানুপাতিক, তাই MAP = ridge, আর λ = σ²/τ² (noise-এর variance ভাগ prior-এর variance). Laplace distribution-এর prior বসালে −log prior হয় |w|-এর সমানুপাতিক, আর MAP = lasso; Laplace-এর শূন্যে তীক্ষ্ণ চূড়াই coefficient-কে শূন্যে টানে। একটি সূক্ষ্মতা: lasso-র শূন্য আসে MAP-এর সর্বোচ্চ বিন্দু থেকে; পুরো Bayesian posterior-এর গড় নিলে coefficient সাধারণত ঠিক শূন্য হয় না।

৯ · Validation-এ overfit, double descent আর রোগনির্ণয়ের তালিকা

Validation set-ও ব্যবহারে ক্ষয় হয়। প্রতিবার আপনি একটি নতুন degree, λ বা feature-এর সমাহার চেষ্টা করে validation score দেখে সিদ্ধান্ত নেন, তখন সেই score-এর ভেতরের এলোমেলোতাও আপনার সিদ্ধান্তে ঢোকে। হাজারবার চেষ্টা করলে কোনো একটি ভাগ্যক্রমে ভালো দেখাবেই। একটি চরম পরীক্ষা: label পুরো এলোমেলো (০ বা ১, কোনো feature-এর সঙ্গে সম্পর্ক নেই), ৫০টি validation সারি, আর ৫০০টি এলোমেলো linear “model”. সত্যিকারের run (একই script-এর শেষ অংশ):

500 random models: best validation accuracy 0.70, same model on fresh test 0.496

যেখানে কিছুই শেখার নেই, সেখানে বাছাই করা “সেরা” model validation-এ ৭০% দেখায়, আর ২০,০০০টি নতুন সারিতে মুদ্রা টসের মতো ৪৯.৬%. বাস্তবে বিপদটি এত স্পষ্ট নয়, কিন্তু একই রকম: অনেক চেষ্টার পর validation score আশাবাদী হয়ে যায়। তাই test set আলাদা রাখা, বাছাইয়ে কখনো ব্যবহার না করা, cross-validation-এ গড়ের পাশে ছড়ানোও দেখা, আর কতগুলো চেষ্টা করেছেন তা লিখে রাখা দরকার।

Double descent — সাবধানে একটি নোট

“Complexity বাড়ালে একসময় test error বাড়েই”, এই U-ছবি সব পরিস্থিতিতে পুরো গল্প নয়। Mikhail Belkin, Daniel Hsu, Siyuan Ma আর Soumik Mandal ২০১৯ সালে (PNAS) দেখান, কিছু model-এ parameter-এর সংখ্যা বাড়াতে থাকলে test error প্রথমে U-এর মতো চলে, ঠিক ডেটা মুখস্থ করার সীমার কাছে (parameter প্রায় ডেটার সংখ্যার সমান) চূড়ায় ওঠে, তারপর আরও অনেক বড় model-এ আবার নামে। এর নাম দেন double descent. আমাদের degree 15-এর বিস্ফোরণ ঠিক সেই চূড়ার কাছের ঘটনা: ২০টি বিন্দুতে ১৬টি parameter. পরে বড় neural network-এও এমন ছবি দেখা গেছে। তবে এটি কখন কতটা ঘটে তা model, ডেটা আর regularization-এর ওপর নির্ভর করে; গবেষণা এখনো চলছে। এর মানে “যত বড় তত ভালো” নয়, আর ছোট ডেটায় সাধারণ model-এর জন্য এই পর্বের U-ছবিই প্রায় সবসময় প্রাসঙ্গিক।

রোগনির্ণয়ের তালিকা

ডাক্তার যেমন লক্ষণ দেখে আগে সম্ভাব্য কারণ ভাবেন, তারপর পরীক্ষা দেন, model-এর ক্ষেত্রেও তাই। সংখ্যাগুলো শুধু তুলনার জন্য; আপনার ডেটায় “বেশি” আর “কম” ঠিক হবে একটি সরল baseline আর noise-এর আন্দাজ দেখে।

লক্ষণসম্ভাব্য কারণযা চেষ্টা করবেন
Train আর validation দুটিই খারাপ, কাছাকাছিউচ্চ biasবেশি নমনীয় model, নতুন feature, regularization কমানো (λ ছোট, k ছোট)
Train খুব ভালো, validation অনেক খারাপউচ্চ varianceআরও ডেটা, regularization বাড়ানো, সরল model, feature কমানো, early stopping
Train-এ প্রায় নিখুঁত (যেমন MSE 0, accuracy ১০০%)মুখস্থ, বা data leakageLeakage খুঁজুন (পর্ব ৫৬), তারপর variance-এর ওষুধ
Validation ভালো, test হঠাৎ খারাপValidation-এ অতিরিক্ত বাছাই, বা ভাগের পার্থক্যCross-validation, কম চেষ্টা, নতুন একটি test ভাগ
Fold থেকে fold-এ score খুব নড়েডেটা কম, উচ্চ varianceআরও ডেটা, সরল model, ছড়ানোসহ রিপোর্ট
Learning curve-এ validation এখনো নামছেডেটা যথেষ্ট নয়আরও ডেটা জোগাড়, অথবা augmentation

Stuart Geman, Elie Bienenstock আর René Doursat-এর ১৯৯২ সালের প্রবন্ধ “Neural Networks and the Bias/Variance Dilemma” (Neural Computation) এই বিনিময়কে machine learning-এ পরিচিত করে তোলে, যদিও decomposition-এর ধারণা পরিসংখ্যানে আগে থেকেই ছিল। Ridge-এর নাম Arthur Hoerl আর Robert Kennard-এর ১৯৭০ সালের Technometrics-এর প্রবন্ধ থেকে; একই গাণিতিক কৌশল ভুল-প্রবণ (ill-posed) সমস্যার জন্য Andrey Tikhonov-এর নামে Tikhonov regularization নামেও পরিচিত। Lasso ১৯৯৬ সালে Tibshirani-র, আর elastic net ২০০৫ সালে Zou ও Hastie-র।

১০ · AI-এর যুগে — AI assistant-এর model আর তার সংখ্যা যাচাই

“আমার ডেটায় একটা ভালো model বানিয়ে দাও” বললে AI assistant দ্রুত কোড দেয়, প্রায়ই একটি চমৎকার সংখ্যাসহ। Overfitting-এর প্রশ্নে তার ভুলগুলো নিয়মিত, আর চোখে পড়ে না, কারণ কোড চলে।

AI যা প্রায়ই করেকীভাবে ধরবেন
Training set-এর R² বা accuracy রিপোর্ট করে, “৯৯% নির্ভুল” বলেআলাদা validation আর test-এর সংখ্যা চান, train-এর পাশে
Test set দেখে λ, degree বা k বাছে, তারপর সেই test score-ই রিপোর্ট করেবাছাই কোন ডেটায় হলো, কোড পড়ে নিশ্চিত হন
পুরো ডেটায় StandardScaler বা PolynomialFeatures fit করে, তারপর ভাগ করেআগে ভাগ, তারপর Pipeline-এ শুধু train-এ fit
Scaling ছাড়াই Ridge বা Lasso চালায়Feature-এর একক দেখুন; শাস্তির আগে standardize
Ridge আর Lasso-র alpha একই মাপের ভাবে, বা normalize=True-এর মতো সরিয়ে ফেলা parameter দেয়নিজের version-এর অফিসিয়াল docs-এ objective আর parameter মিলিয়ে নিন
Lasso-র শূন্য-না-হওয়া feature-কে “কারণ” বলে ব্যাখ্যা করেকয়েকটি seed বা bootstrap-এ বাছাই স্থির কি না দেখুন; মিলওয়ালা feature-এ সাবধান
Convergence warning লুকিয়ে ফেলে (warnings.filterwarnings("ignore"))সব warning পড়ুন; max_iter বা scaling ঠিক করুন

নির্দিষ্ট করে চান, আর এমন আউটপুট চান যা যাচাই করা যায়। আসল গ্রাহকের ডেটা পেস্ট করবেন না; কলামের নাম আর কয়েকটি বানানো সারিই যথেষ্ট।

আমার একটি pandas DataFrame আছে: কয়েকটি সংখ্যার feature কলাম আর লক্ষ্য কলাম delivery_minutes. Python 3.14, NumPy 2 আর scikit-learn 1.8 দিয়ে কোড লেখো যা: ১. প্রথমেই train_test_split(random_state=42) দিয়ে test আলাদা করবে, আর test শুধু একদম শেষে একবার ব্যবহার করবে। ২. Pipeline-এ StandardScaler আর Ridge রাখবে, আর alpha বাছাই করবে শুধু train ডেটায় 5-fold cross-validation দিয়ে (GridSearchCV বা RidgeCV). ৩. প্রতিটি alpha-র train আর validation MSE একটি টেবিলে ছাপাবে, আর learning_curve দিয়ে train ও validation MSE বনাম training size-এর একটি matplotlib ছবি দেবে (লেবেল ইংরেজিতে)। ৪. একটি DummyRegressor baseline-এর MSE পাশে দেখাবে। ৫. কোনো warning চাপা দেবে না, deprecated parameter ব্যবহার করবে না, আর ব্যাখ্যা করবে Ridge-এর alpha আর Lasso-র alpha কেন সরাসরি তুলনীয় নয়।

AI-এর দেওয়া model হাতে পেলে — যাচাইয়ের চেকলিস্ট

পরীক্ষা ও ইন্টারভিউতে:

  • Bias–variance decomposition লিখুন। E[(y − f̂)²] = bias² + variance + σ²; σ² noise, কোনো model কমাতে পারে না।
  • Train error ২, validation error ৪০: কী সমস্যা, কী করবেন? উচ্চ variance (overfitting); আরও ডেটা, regularization, সরল model.
  • একটি feature, ∑x² = ১০, ∑xy = ৩০ (center করা), λ = ৫: ridge-এর w? ৩০/(১০ + ৫) = ২; সাধারণ least squares-এ ৩০/১০ = ৩.
  • Lasso আর ridge-এর মূল পার্থক্য? L1 অনেক coefficient ঠিক শূন্য করে (feature selection), L2 ছোট করে কিন্তু সাধারণত শূন্য করে না।
  • k-NN-এ k = 1-এর bias আর variance? Bias কম, variance বেশি; train error শূন্য, কারণ প্রতিটি বিন্দুর প্রতিবেশী সে নিজেই।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১বিশাল neural network-এ ডেটার চেয়ে অনেক বেশি parameter থাকে, অথচ তারা প্রায়ই নতুন ডেটায় ভালো করে। এতে কি bias–variance trade-off মিথ্যা হয়ে গেল?

Trade-off-এর পরিচিত ছবিটি জনপ্রিয় হয় Stuart Geman, Elie Bienenstock আর René Doursat-এর ১৯৯২ সালের প্রবন্ধে। তাঁদের বার্তা ছিল সাবধানতার: খুব নমনীয় model, যেমন বড় neural network, অল্প ডেটায় variance-এ ডুবে যাবে, তাই কিছু অনুমান (bias) মেনে নিতেই হবে। পরের দুই দশক এই পরামর্শই পাঠ্যবইয়ে ছিল: parameter কম রাখুন, না হলে regularization দিন।

২০১০-এর দশকে deep learning এই ছবিকে চ্যালেঞ্জ করে। ২০১৭ সালে Chiyuan Zhang ও সহকর্মীরা (ICLR) দেখান, সাধারণ image network ছবির label পুরো এলোমেলো করে দিলেও training ডেটা নিখুঁতভাবে মুখস্থ করতে পারে; অর্থাৎ এদের ক্ষমতা মুখস্থের জন্য যথেষ্ট, তবু আসল label-এ এরা ভালো generalize করে। প্রথাগত “parameter গুনে complexity মাপা” তাই এখানে যথেষ্ট নয়। ২০১৯ সালে Belkin, Hsu, Ma আর Mandal double descent-এর ছবি দেন: মুখস্থের সীমা পার হওয়ার পর আরও বড় model-এ test error আবার নামতে পারে। একই বছর Preetum Nakkiran ও সহকর্মীরা বড় network-এও এমন ছবি দেখান।

তবে এতে decomposition মিথ্যা হয় না: বর্গ-ভুলের জন্য bias² + variance + σ² একটি গাণিতিক অভেদ, যেকোনো model-এ সত্য। বদলায় শুধু ধারণাটি যে “parameter বাড়ালে variance বাড়েই”. বড় network-এ training-এর পদ্ধতি নিজেই (gradient descent, early stopping, weight decay, augmentation) একরকম অদৃশ্য regularization দেয়, যাকে প্রায়ই implicit regularization বলা হয়; এর পূর্ণ তত্ত্ব এখনো গবেষণার বিষয়। আর ছোট ডেটায় সাধারণ model-এ, যা বাজারঘরের মতো বেশিরভাগ ব্যবসায়িক কাজে দেখা যায়, U-ছবিটি এখনো খুব ভালো পথপ্রদর্শক।

মূল উপলব্ধি: Decomposition একটি অভেদ, তাই সবসময় সত্য; কিন্তু “complexity” কী দিয়ে মাপবেন, তা model আর training-এর পদ্ধতির ওপর নির্ভর করে, আর আধুনিক বড় model-এ parameter গোনা সেই মাপ নয়।

প্র ০২ইচ্ছা করে একটু ভুল (bias) মেনে নেওয়া কেন ভালো হতে পারে? Ridge-এর জন্মের ইতিহাস এ বিষয়ে কী বলে?

পরিসংখ্যানে দীর্ঘদিন একটি আদর্শ ছিল unbiased estimator: গড়ে ঠিক উত্তর দেয় এমন অনুমান। সাধারণ least squares-এর coefficient (অনুমানগুলো ঠিক থাকলে) unbiased, আর Gauss–Markov উপপাদ্য অনুযায়ী linear unbiased অনুমানের মধ্যে এর variance সবচেয়ে কম। কিন্তু “unbiased-দের মধ্যে সেরা” মানে “সবার মধ্যে সেরা” নয়। Feature-গুলো পরস্পর খুব মিললে (multicollinearity) XᵀX প্রায় singular হয়, আর least squares-এর coefficient বিশাল আর অস্থির হয়: একটি সারি বদলালেই চিহ্ন উল্টে যায়।

Arthur Hoerl আর Robert Kennard ১৯৭০ সালে Technometrics-এ ঠিক এই সমস্যার জন্য ridge প্রস্তাব করেন। তাঁরা দেখান, XᵀX-এর কর্ণে একটি ছোট ধনাত্মক সংখ্যা যোগ করলে এমন একটি পরিসর সবসময় থাকে যেখানে মোট গড় বর্গ-ভুল least squares-এর চেয়ে কম, যদিও অনুমান তখন biased. নামটি এসেছে Hoerl-এর আগের “ridge analysis” কাজ থেকে বলে প্রায়ই বলা হয়। একই কৌশল অন্য পথে এসেছিল Andrey Tikhonov-এর ill-posed সমস্যার কাজ থেকে, যেখানে ছোট মাপের ভুলে উত্তর বিশাল বদলায়।

আরও নাটকীয় উদাহরণ Charles Stein-এর: ১৯৫৬ সালের কাজে আর ১৯৬১ সালে William James-এর সঙ্গে দেখান, তিন বা তার বেশি গড় একসঙ্গে অনুমান করলে প্রতিটির নিজের নমুনা-গড়ের চেয়ে সবগুলোকে একটু একসঙ্গে টেনে ছোট করা অনুমান মোট বর্গ-ভুলে ভালো করে। পরিসংখ্যানবিদদের কাছে এটি তখন বিস্ময়কর ছিল। এই পর্বের stepper-এও একই কথা: degree 1-এর পাঁচটি স্থির কিন্তু ভুল অনুমান degree 15-এর ছড়ানো কিন্তু গড়ে ঠিক অনুমানের চেয়ে কম মোট ভুল করেছিল (৫৩.৩৫ বনাম ৯১.০৭)।

মূল উপলব্ধি: লক্ষ্য গড়ে ঠিক হওয়া নয়, নতুন ডেটায় মোট ভুল কম রাখা; একটু bias দিয়ে অনেক variance কিনতে পারলে সেটিই ভালো দর।

প্র ০৩Lasso যে feature-গুলো রেখে দেয়, সেগুলোকে কি “আসল কারণ” বলা যায়? Feature selection-এর ব্যাখ্যায় কী সাবধানতা দরকার?

Robert Tibshirani ১৯৯৬ সালে Journal of the Royal Statistical Society (Series B)-তে lasso প্রস্তাব করেন; নামটি least absolute shrinkage and selection operator-এর সংক্ষেপ। আকর্ষণ পরিষ্কার: শাস্তি একটি, অথচ model ছোট হয়, আর অল্প কয়েকটি অশূন্য coefficient পড়ে ব্যাখ্যা দেওয়া সহজ। জিন-ডেটার মতো ক্ষেত্রে, যেখানে feature হাজারো আর নমুনা কয়েকশো, এটি খুব জনপ্রিয় হয়।

কিন্তু “lasso রেখেছে” আর “এটিই কারণ” এক কথা নয়। প্রথমত, lasso অনুমানের জন্য বাছে, কারণ খোঁজার জন্য নয়: একটি feature যদি আসল কারণের সঙ্গে শুধু মেলে (যেমন বাজারঘরে “অর্ডারের সময়” আর “রাস্তার ভিড়”), lasso যেকোনোটি রাখতে পারে। দ্বিতীয়ত, মিলওয়ালা feature-দের মধ্যে lasso-র বাছাই অস্থির; ডেটা একটু বদলালে অন্যটি আসে। Hui Zou আর Trevor Hastie ২০০৫ সালে elastic net প্রস্তাব করেন অনেকটা এই কারণেই, আর Nicolai Meinshausen ও Peter Bühlmann ২০১০ সালে stability selection-এ দেখান, অনেকবার আংশিক নমুনায় lasso চালিয়ে কোন feature বারবার টেকে তা দেখা বেশি ভরসার। তৃতীয়ত, এই পর্বের run-এও দেখা গেছে: alpha = ০.১-এ ১১টি feature টিকেছিল, যার ৮টি পুরো এলোমেলো; alpha-র মানই ঠিক করে কতগুলো থাকবে, আর alpha নিজে একটি বাছাই।

আর lasso coefficient-কে ছোট করে (যানজট −৩.৩ থেকে −২.১৩, দূরত্ব ৬.৭৮ থেকে ৫.৮১), তাই তার মান দিয়ে প্রভাবের আকার বলা যায় না; প্রায়ই lasso দিয়ে বাছাই করে তারপর সেই feature-গুলোতে সাধারণ regression চালানো হয়, যদিও সেটিরও নিজস্ব পরিসংখ্যানিক সমস্যা আছে। কারণ-ফলের প্রশ্নের জন্য পরীক্ষা (যেমন A/B test) বা causal inference-এর পদ্ধতি লাগে।

মূল উপলব্ধি: Lasso একটি ভালো অনুমানকারী আর ব্যবহারিক feature ছাঁকনি, কিন্তু তার বাছাই কারণের প্রমাণ নয়; স্থিরতা যাচাই আর ক্ষেত্রের জ্ঞান ছাড়া “এই feature-ই আসল” বলা ঝুঁকিপূর্ণ।

প্র ০৪একই benchmark বা প্রতিযোগিতার test set-এ হাজারো গবেষক আর দল বছরের পর বছর model মাপলে কী হয়? Validation আর test-এর “ক্ষয়” কীভাবে ঠেকানো যায়?

এই পর্বের শেষ পরীক্ষায় এলোমেলো label-এ ৫০০টি model চেষ্টা করে “সেরা”-টি validation-এ ৭০% পেয়েছিল, অথচ নতুন ডেটায় ৪৯.৬%. পুরো একটি গবেষণা-সমাজও একই কাজ করতে পারে, শুধু ধীরে: সবাই একই public test set-এ মাপে, যে ধারণা সেখানে ভালো করে তা প্রকাশিত হয়, আর পরের কাজ তার ওপর দাঁড়ায়। প্রতিটি দল সৎ হলেও সম্মিলিত বাছাই test set-এর এলোমেলো বৈশিষ্ট্যের সঙ্গে খাপ খেতে পারে।

এটি কতটা ঘটেছে, তা মাপার একটি পরিচিত চেষ্টা Benjamin Recht ও সহকর্মীদের ২০১৯ সালের কাজ (ICML): তাঁরা ImageNet আর CIFAR-10-এর মূল পদ্ধতি অনুসরণ করে নতুন test set বানান। বহু model-এর accuracy নতুন set-এ উল্লেখযোগ্যভাবে কমে, তবে model-দের ক্রম মোটামুটি একই থাকে। তাঁদের ব্যাখ্যায় পতনের বড় অংশ ডেটা সংগ্রহের সূক্ষ্ম পার্থক্য থেকে, শুধু বারবার ব্যবহারের overfitting থেকে নয়; অর্থাৎ ছবিটি জটিল। Kaggle-এর মতো প্রতিযোগিতায় public leaderboard-এর বাইরে একটি private অংশ রাখা হয়, আর শেষে দেখা যায় public-এ ওপরে থাকা কিছু দল private-এ নিচে নেমে গেছে; এটি এই সমস্যার পরিচিত লক্ষণ বলে ধরা হয়।

প্রতিরক্ষার উপায়: test set একবারের জন্য রাখা; বাছাই cross-validation-এ; কতগুলো চেষ্টা হলো তা লিখে রাখা; সম্ভব হলে সময়ের পরের নতুন ডেটায় আবার মাপা। তত্ত্বের দিক থেকে Cynthia Dwork ও সহকর্মীরা ২০১৫ সালে (Science) “reusable holdout” প্রস্তাব করেন, যেখানে holdout-এর উত্তর একটু noise মিশিয়ে আর সীমিতভাবে দেওয়া হয়, যাতে বারবার প্রশ্ন করেও তার এলোমেলোতা মুখস্থ না হয়।

মূল উপলব্ধি: প্রতিটি দেখা মানে একটু খরচ; validation আর test set-কে একটি সীমিত সম্পদ ভাবুন, আর গুরুত্বপূর্ণ দাবির আগে একেবারে নতুন ডেটায় যাচাই করুন।

প্র ০৫বাজারঘরের হাতে সীমিত টাকা: আরও ডেটা জোগাড় করবে, নাকি বেশি জটিল model আর বেশি engineer? Learning curve এই সিদ্ধান্তে কীভাবে সাহায্য করে?

২০০১ সালে Michele Banko আর Eric Brill (ACL) একটি সরল ভাষার কাজে (দুটি বিভ্রান্তিকর শব্দের মধ্যে ঠিকটি বাছা) কয়েকটি পদ্ধতিকে দশ লাখ থেকে শত কোটি শব্দ পর্যন্ত ডেটায় চালিয়ে learning curve আঁকেন। তাঁদের ছবিতে ডেটা বাড়ানোর লাভ পদ্ধতি বদলানোর লাভের চেয়ে অনেক বড় দেখা যায়, আর ছোট ডেটায় দুর্বল পদ্ধতি বড় ডেটায় অনেক সময় এগিয়ে যায়। ২০০৯ সালে Alon Halevy, Peter Norvig আর Fernando Pereira-র প্রবন্ধ “The Unreasonable Effectiveness of Data” এই মনোভাবকে জনপ্রিয় করে। কিন্তু এগুলো নির্দিষ্ট ধরনের কাজের অভিজ্ঞতা, সর্বজনীন নিয়ম নয়।

এই পর্বের দুটি learning curve দেখায় কেন। Degree 1-এর model-এ ১৫ থেকে ৩২০টি বিন্দু, প্রায় ২০ গুণ ডেটা, অথচ validation MSE ৪৭ থেকে মাত্র ৪৫.৯-এ: টাকা নষ্ট। Degree 12-এ ৩০ থেকে ৩২০টি বিন্দুতে ৮৩২ থেকে ১৬.৭: টাকার সবচেয়ে ভালো ব্যবহার। অর্থাৎ “আরও ডেটা” সিদ্ধান্তটি model-এর অবস্থার ওপর নির্ভর করে, আর learning curve সেই অবস্থা দেখায়। বাজারঘরের জন্য বাস্তব পথ: বর্তমান ডেটার ২০%, ৪০%, …, ১০০% দিয়ে learning curve আঁকা। Validation error শেষেও নামতে থাকলে ডেটায় বিনিয়োগ; দুটি curve মিলে গিয়ে উঁচুতে থেমে থাকলে নতুন feature (বৃষ্টির খবর, এলাকার যানজটের মাপ) বা বেশি নমনীয় model.

আরেকটি দিক: সব ডেটা সমান নয়। ডেটা বাড়ানোর খরচ শুধু টাকা নয়, গ্রাহকের গোপনীয়তাও; প্রয়োজনের বেশি ব্যক্তিগত তথ্য জমানো ঠিক নয় (পর্ব ৭২)। অনেক সময় বেশি ডেটার চেয়ে ভালো ডেটা, যেমন ভুল label ঠিক করা বা যে এলাকা থেকে ডেটা কম সেখান থেকে নমুনা, বেশি লাভ দেয়। আর Bangladesh-এর মতো জায়গায়, যেখানে কিছু এলাকার ডিজিটাল ডেটা কম হতে পারে, শুধু মোট ডেটা বাড়ালে সেই এলাকাগুলোর ভুল হয়তো কমবে না।

মূল উপলব্ধি: “আরও ডেটা না আরও জটিল model” প্রশ্নের উত্তর অনুমানে নয়, learning curve-এ; উচ্চ variance-এ ডেটা কাজে লাগে, উচ্চ bias-এ লাগে ভালো feature বা নমনীয়তা।

অনুশীলন

হাতে হিসাব, কোড, সমালোচনা আর আউটপুট অনুমান; প্রতিটির উত্তর নিচে লুকানো আছে।

  1. হাতে bias আর variance (হিসাব): একটি বিন্দুতে আসল মান f(x) = ২০. একই পদ্ধতি চারটি আলাদা training set-এ চালিয়ে অনুমান পাওয়া গেছে ১৮, ২২, ২৪, ২০. Noise-এর variance σ² = ৯. (ক) bias², variance আর আনুমানিক গড় বর্গ-ভুল বের করুন। (খ) আরেকটি পদ্ধতির অনুমান ১৭, ১৭, ১৮, ১৮: কোনটি ভালো? (গ) একটি feature, center করা ডেটায় ∑x² = ২০, ∑xy = ৬০: λ = ০ আর λ = ১০-এ ridge-এর w কত?
    • (ক) গড় = (১৮ + ২২ + ২৪ + ২০) ÷ ৪ = ৮৪ ÷ ৪ = ২১. bias = ২১ − ২০ = ১, bias² = ১. গড় থেকে দূরত্ব −৩, ১, ৩, −১; বর্গ ৯, ১, ৯, ১; variance = ২০ ÷ ৪ = ৫. গড় ভুল ≈ ১ + ৫ + ৯ = ১৫.
    • (খ) গড় ১৭.৫, bias² = (১৭.৫ − ২০)² = ৬.২৫; দূরত্ব −০.৫, −০.৫, ০.৫, ০.৫, variance = ০.২৫. গড় ভুল ≈ ৬.২৫ + ০.২৫ + ৯ = ১৫.৫. প্রায় সমান, প্রথমটি সামান্য ভালো; দ্বিতীয়টি স্থির কিন্তু biased. মাত্র চারটি অনুমানে এই তফাত নির্ভরযোগ্য নয়।
    • (গ) w = ∑xy ÷ (∑x² + λ). λ = ০: ৬০ ÷ ২০ = ৩. λ = ১০: ৬০ ÷ ৩০ = ২. শাস্তি coefficient-কে এক-তৃতীয়াংশ ছোট করেছে।
  2. Degree 15-কে ridge দিয়ে বাঁচানো (কোড): অংশ ১-এর poly_degrees.py-এ degree 15 রেখে fit-এর বদলে একটি fit_ridge(x, y, d, lam) লিখুন, যা (XᵀX + λP)w = Xᵀy সমাধান করে, যেখানে P হলো identity matrix, শুধু প্রথম কর্ণের ঘরটি (intercept-এর কলাম) ০. λ = ০, ০.০০১, ০.০১, ০.১, ১ আর ১০-এ train আর validation MSE ছাপান। কোন λ বাছবেন, আর কেন test-এ নয়?
    def fit_ridge(x, y, d, lam):
        A = design(x, d)
        P = lam * np.eye(d + 1); P[0, 0] = 0          # intercept-এ শাস্তি নেই
        return np.linalg.solve(A.T @ A + P, A.T @ y)
    
    for lam in (0.0, 1e-3, 1e-2, 1e-1, 1.0, 10.0):
        w = fit_ridge(x_tr, y_tr, 15, lam)
        tr = np.mean((design(x_tr, 15) @ w - y_tr) ** 2)
        va = np.mean((design(x_va, 15) @ w - y_va) ** 2)
        print(f"lambda={lam:g}: train {tr:.2f} | validation {va:.4g}")

    সত্যিকারের run (ডেস্কটপ, Python 3.14.4, NumPy 2.5.4; একই seed-এর ডেটা):

    lambda=0: train 1.86 | validation 4.235e+07
    lambda=0.001: train 6.82 | validation 21.03
    lambda=0.01: train 7.28 | validation 24.21
    lambda=0.1: train 8.16 | validation 24.59
    lambda=1: train 17.16 | validation 29.08
    lambda=10: train 44.85 | validation 56.9

    λ = ০.০০১ বাছবেন: validation MSE চার কোটি থেকে ২১.০৩-এ নেমেছে, যদিও degree এখনো 15. এত ছোট λ-ও কাজ করে, কারণ degree 15-এর বিশাল coefficient-গুলোর ওপর শাস্তি অনেক বড়। λ বাড়ালে train আর validation দুটিই বাড়ে: উচ্চ bias-এর দিকে যাত্রা। λ test-এ বাছলে test score আর নিরপেক্ষ থাকে না; সেটি তখন বাছাইয়ের অংশ। খেয়াল করুন, এখানে feature standardize করা হয়নি; t আগে থেকেই −১ থেকে ১-এ, তবু t15-এর মাপ t-এর চেয়ে ছোট, তাই শাস্তি কলামভেদে আলাদা প্রভাব ফেলে।

  3. সমালোচনা ও নতুন নকশা: একজন সহকর্মী লিখেছেন: “আমি ডেলিভারি-সময়ের জন্য ৪০টি feature দিয়ে degree 3-এর polynomial বানিয়েছি। পুরো ডেটায় StandardScaler fit করে ভাগ করেছি। ৫০টি আলাদা alpha-তে Lasso চালিয়ে test set-এ যেটি সেরা, সেটি নিয়েছি: test R² ০.৯৭. Lasso যে ৬টি feature রেখেছে, সেগুলোই ডেলিভারি দেরির কারণ, আর বাকিগুলোর ডেটা সংগ্রহ বন্ধ করে দেব।” কী কী ভুল, আর আপনি কীভাবে সাজাবেন?
    • ভাগের আগে scaling: test-এর গড় আর standard deviation train-এ ঢুকেছে; এটি data leakage. আগে ভাগ, তারপর Pipeline-এ শুধু train-এ fit.
    • Test-এ alpha বাছাই: ৫০টি চেষ্টার সেরাটি test-এ বেছে সেই test score রিপোর্ট করা আশাবাদী; test বাছাইয়ের অংশ হয়ে গেছে। alpha বাছা হবে cross-validation-এ (যেমন LassoCV), test শেষে একবার।
    • ৪০টি feature-এর degree 3: সব জোড়া আর তিনের গুণফল মিলে কয়েক হাজার কলাম (১২,৩৪০টি, bias-কলাম ছাড়া); ডেটা কম হলে variance বিশাল, আর ব্যাখ্যা প্রায় অসম্ভব। আগে সরল linear model আর baseline দিয়ে শুরু।
    • “কারণ” দাবি: lasso অনুমানের জন্য বাছে; মিলওয়ালা feature-এর মধ্যে বাছাই অস্থির, আর কারণ প্রমাণ করে না। কয়েকটি seed বা bootstrap-এ বাছাই স্থির কি না দেখতে হবে।
    • ডেটা সংগ্রহ বন্ধ: একটি ছোট নমুনার lasso-র ভিত্তিতে ডেটা ফেলে দিলে ভবিষ্যতের model আর ভুল যাচাইয়ের পথ বন্ধ হয়; সিদ্ধান্তটি ক্ষেত্রের জ্ঞান আর বারবার যাচাইয়ের পর।

    নতুন নকশা: ভাগ সবার আগে; Pipeline(StandardScaler, LassoCV বা ElasticNetCV) শুধু train-এ; DummyRegressor আর সাধারণ linear regression-এর baseline; learning curve দেখে bias না variance; test-এর সংখ্যা শেষে একবার, ছড়ানোসহ; feature বাছাইয়ের স্থিরতা অনেক নমুনায়।

  4. আউটপুট অনুমান: নিচের কোড কী ছাপবে? প্রথম দুই লাইনে intercept নেই কেন, তা কি সমস্যা?
    import numpy as np
    X = np.array([[1.0], [2.0], [3.0]])
    y = np.array([2.0, 4.0, 6.0])
    for lam in (0, 14):
        w = np.linalg.solve(X.T @ X + lam * np.eye(1), X.T @ y)
        print(lam, w)
    coef = np.array([0.0, 1.2, 0.0, -0.3])
    print(np.sum(coef == 0), np.abs(coef).sum(), (coef ** 2).sum())

    সত্যিকারের run (ডেস্কটপ, Python 3.14.4, NumPy 2.5.4):

    0 [2.]
    14 [1.]
    2 1.5 1.53

    XᵀX = ১ + ৪ + ৯ = ১৪, Xᵀy = ২ + ৮ + ১৮ = ২৮. λ = ০: w = ২৮ ÷ ১৪ = ২ (ডেটা ঠিক y = 2x). λ = ১৪: w = ২৮ ÷ ২৮ = ১, অর্থাৎ λ যখন XᵀX-এর সমান, coefficient ঠিক অর্ধেক। শেষ লাইনে দুটি coefficient ঠিক শূন্য (lasso যেমন বানায়), L1 = ১.২ + ০.৩ = ১.৫, আর L2-এর বর্গ = ১.৪৪ + ০.০৯ = ১.৫৩. এখানে model-এ intercept রাখিনি, কারণ ডেটা মূলবিন্দু দিয়েই যায়; সাধারণ ডেটায় intercept ছাড়া ridge চালালে শাস্তি পুরো রেখাকে মূলবিন্দুর দিকে টানত, যা ভুল। তাই আগে center করা বা Ridge(fit_intercept=True), যা default.

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

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি regression model-এর overfitting বা underfitting খুঁজে সারানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Seed দিয়ে ডেটা train, validation আর test-এ ভাগ করা, test সরিয়ে রাখা
  2. একটি সরল baseline আর একটি প্রথম model শুধু train-এ শেখানো
  3. Train আর validation error পাশাপাশি তুলনা করা
  4. Learning curve দেখে ঠিক করা সমস্যা bias না variance
  5. ওষুধ বাছা আর λ বা complexity validation-এ ঠিক করা
  6. বেছে নেওয়া একটি model test set-এ একবার মাপা

একটি model-এর train MSE ২.১, আর validation MSE ৪৮.৫; একটি সরল baseline-এর validation MSE ৫০. সবচেয়ে সম্ভাব্য অবস্থা আর যুক্তিসঙ্গত পদক্ষেপ কোনটি?

  • Underfitting; আরও নমনীয় model নেওয়া
  • Model ঠিক আছে; test set-এ মেপে প্রকাশ করা
  • Overfitting (উচ্চ variance); regularization বাড়ানো বা আরও ডেটা
  • শুধু irreducible noise; কিছুই করার নেই

একটি বিন্দুতে একটি model-এর bias² = ৯, variance = ৪, আর noise-এর variance σ² = ১৬. বর্গ-ভুলের bias–variance decomposition অনুযায়ী সেখানে নতুন পর্যবেক্ষণের প্রত্যাশিত বর্গ-ভুল কত?

  • ১৩
  • ২৯
  • ৮১
  • ২৫

k-NN regression-এ k = 1 থেকে k-কে ধীরে ধীরে training ডেটার প্রায় সব বিন্দুর সমান করলে bias আর variance সাধারণত কীভাবে বদলায়?

  • Variance কমে, bias বাড়ে
  • Variance বাড়ে, bias কমে
  • দুটিই কমে
  • কোনোটিই বদলায় না, k শুধু গতির ব্যাপার

একটি learning curve-এ training size ৫০ থেকে ৫০০০ পর্যন্ত বাড়ালে train আর validation MSE দুটিই প্রায় ৪০-এ এসে মিলে যায়, অথচ ব্যবসার লক্ষ্য MSE ১৫-এর নিচে, আর noise-এর আন্দাজ প্রায় ১০. সবচেয়ে যুক্তিসঙ্গত সিদ্ধান্ত কোনটি?

  • আরও দশ গুণ ডেটা জোগাড় করা
  • Regularization বাড়ানো
  • Model ঠিক আছে, কারণ train আর validation সমান
  • উচ্চ bias; বেশি নমনীয় model বা নতুন feature চেষ্টা করা

Linear regression-এ ১০০টি feature-এর মধ্যে অল্প কয়েকটি কাজের বলে সন্দেহ, আর আপনি চান model নিজেই বাকি coefficient ঠিক শূন্য করে দিক। কোন regularization সবচেয়ে উপযুক্ত, আর কেন?

  • Ridge (L2), কারণ বর্গের শাস্তি বড় coefficient বেশি কমায়
  • Lasso (L1), কারণ পরম মানের শাস্তি অনেক coefficient ঠিক শূন্যে নামায়
  • Early stopping, কারণ কম ধাপে coefficient শূন্য থাকে
  • Intercept-এ শাস্তি দেওয়া

একজন analyst ২০টি আলাদা λ চেষ্টা করে প্রতিটির test MSE দেখলেন, আর সবচেয়ে কম test MSE-এর λ বেছে সেই সংখ্যাটিই রিপোর্ট করলেন। এতে মূল সমস্যা কী?

  • ২০টি λ খুব কম, আরও বেশি চেষ্টা করা উচিত ছিল
  • Test set-এ MSE হিসাব করা যায় না, শুধু accuracy
  • Test set বাছাইয়ে ব্যবহার হওয়ায় রিপোর্ট করা MSE আশাবাদী
  • Ridge-এ λ বাছাই করা উচিত নয়, সবসময় λ = ১ রাখতে হয়

শব্দকোষ

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

মনে রাখুন

  1. দুই ব্যর্থতা: underfitting-এ train আর validation দুটিই খারাপ; overfitting-এ train ভালো, validation খারাপ। Degree 1, 3, 15-এ validation MSE 39.6, 18.4 আর চার কোটির বেশি। Validation curve-এর তলায় ঠিক জায়গা।
  2. Decomposition: E[(y − f̂)²] = bias² + variance + σ². ২০০টি training set-এর simulation-এ যোগফল মাপা ভুলের সঙ্গে মেলে; complexity বাড়লে bias কমে, variance বাড়ে। k-NN-এ ছোট k মানে উচ্চ variance.
  3. Learning curve: উঁচুতে মিলে থামা মানে উচ্চ bias, আরও ডেটায় লাভ নেই; বড় ফাঁক যা ডেটায় কমে, মানে উচ্চ variance, আরও ডেটা কাজে লাগে।
  4. Ridge আর lasso: ridge = (XᵀX + λI)−1Xᵀy, ছোট করে কিন্তু শূন্য করে না; lasso অনেক coefficient ঠিক শূন্য করে। আগে standardize; intercept-এ শাস্তি নেই; scikit-learn-এ λ-এর নাম alpha, আর Lasso-র objective-এ ১/(২n) আছে। Prior হিসেবে: L2 ↔ Gaussian, L1 ↔ Laplace.
  5. সৎ বাছাই: λ, degree, k বাছা হয় validation বা cross-validation-এ; test শেষে একবার। বারবার বাছাইয়ে validation-ও overfit হয়; double descent আছে, তবে “যত বড় তত ভালো” প্রমাণ করে না।

পরের পর্বে: মডিউল ১০ এখানে শেষ। পর্ব ৬২ (Exploratory data analysis — প্রশ্ন, পরিসংখ্যান আর chart) দিয়ে শুরু হবে মডিউল ১১, scikit-learn আর পুরো কাজের ধারা: model বানানোর আগে ডেটাকে প্রশ্ন করা, অদ্ভুত মান আর ধাঁচ খোঁজা, যাতে এই পর্বের রোগনির্ণয় শুরু হয় ডেটা চিনে।

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

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