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

Regularization-এর তত্ত্ব — L1/L2-এর জ্যামিতি, MAP আর early stopping

Regularization Theory — Ridge as SVD Shrinkage, Lasso and Soft-Thresholding, L1/L2 Geometry, MAP Priors, Early Stopping and Elastic Net

Taught in Bangla, this lesson explains ridge through SVD shrink factors and effective degrees of freedom, derives lasso's soft-thresholding and coordinate descent and matches them to scikit-learn, and links penalties to constraints and to Gaussian and Laplace priors. It compares early stopping with ridge numerically and uses elastic net, cross-validation and bootstrap to show unstable lasso selection, all with in-browser runs.

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

  • কেন least squares অনেক সময় ill-posed, আর regularization ঠিক কোন অস্থিরতা সারায়
  • Ridge-এর সমাধান SVD দিয়ে লেখা, shrink factor আর effective degrees of freedom হিসাব করা
  • Subgradient থেকে soft-thresholding আর lasso-র coordinate descent নিজে derive করে লেখা, scikit-learn-এর সঙ্গে মিলিয়ে
  • Constrained আর penalised রূপের সম্পর্ক, L1 বলের কোণ, আর MAP হিসেবে Gaussian ও Laplace prior
  • Early stopping-কে ridge-এর সঙ্গে সংখ্যায় তুলনা, আর elastic net ও cross-validation দিয়ে λ বাছাই

AI-এর ভিত্তি কোর্সের পর্ব ৬১-এ ridge নিজের হাতে লিখে scikit-learn-এর সঙ্গে মিলিয়েছেন, lasso-কে coefficient ঠিক শূন্য করতে দেখেছেন, আর L1 হীরা বনাম L2 বৃত্তের ছবি আর “regularization মানে prior” কথাটি এক অনুচ্ছেদে পেয়েছেন। এই পর্ব সেখান থেকে এক ধাপ ভেতরে যায়: ঠিক কোন দিকে কতটা ছোট হয়, শূন্য কোন সমীকরণ থেকে আসে, আর শাস্তি ছাড়াই (শুধু আগে থামিয়ে) একই ফল কেন আসে? হাতিয়ার আগের মডিউলের: পর্ব ০৯-এর SVD আর পর্ব ১১-এর gradient. চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python, NumPy আর scikit-learn); প্রথমবার প্যাকেজ নামতে কয়েক সেকেন্ড লাগে, scikit-learn-এ প্রায় দশ সেকেন্ড।

১ · কেন regularize — ill-posed সমস্যা আর variance

চায়ের দোকানের খাতায় রোজকার লাভ লেখা আছে, আর মালিক জানতে চান কোন পণ্য কত লাভ আনে। মুশকিল হলো চা আর বিস্কুট প্রায় সবসময় একসঙ্গে বিক্রি হয়। তাই “চা কাপপ্রতি ৫ টাকা, বিস্কুট ১ টাকা” আর “চা ২৫ টাকা, বিস্কুট −১৯ টাকা” দুটি ব্যাখ্যাই খাতার সঙ্গে প্রায় সমান মেলে। খাতা নিজে এই দুটিকে আলাদা করতে পারে না; সামান্য একটি ভুল লেখা বদলালেই উত্তর এক দিক থেকে আরেক দিকে লাফায়। একজন অভিজ্ঞ হিসাবরক্ষক তখন বলেন, “সমান মিললে ছোট আর সাদামাটা ব্যাখ্যাটি নিন।” Regularization ঠিক এই কথাটিকে গণিতে লেখে।

গণিতবিদ Jacques Hadamard বিশ শতকের শুরুতে একটি সমস্যাকে well-posed বলেছিলেন যদি তিনটি শর্ত মানে: সমাধান আছে, সমাধান একটিই, আর ডেটা সামান্য বদলালে সমাধানও সামান্য বদলায়। ML-এর least squares প্রায়ই দ্বিতীয় বা তৃতীয় শর্তে আটকায়। Feature-এর সংখ্যা p যদি সারির সংখ্যা n-এর চেয়ে বেশি হয়, অসংখ্য w একই ভুল দেয় (একক সমাধান নেই)। আর দুটি feature প্রায় একই হলে (multicollinearity) সমাধান একটি থাকলেও তা ডেটার noise-এ লাফায়। পর্ব ০৯-এর ভাষায় এর উৎস ছোট singular value.

সংখ্যায় দেখা যাক। মডেল y = Xw + ε, যেখানে ε-এর প্রতিটি ঘর স্বাধীন, গড় ০ আর variance σε². Least squares-এর উত্তর ŵ = (XᵀX)−1Xᵀy, আর তার covariance:

Cov(ŵ) = σε² (XᵀX)−1 = σε² ∑j vjvjᵀ / σj²

এখানে σj হলো X-এর j-তম singular value আর vj তার ডান singular vector (X = UΣVᵀ). অর্থ: vj দিকে ŵ-এর variance σε²/σj². একটি σj যদি ০.০১ হয়, ওই দিকে noise ১০,০০০ গুণ ফুলে ওঠে। এটিই variance, আর overfitting-এর গাণিতিক চেহারা। Regularization এই ফোলা আটকায়, বিনিময়ে একটু bias নেয়। পর্ব ০৩-এর ভাষায় এটি hypothesis-এর ক্লাসকে ছোট রাখা, অর্থাৎ structural risk minimization-এর একটি রূপ।

রূপকী যোগ বা বদল হয়ফলএই পাতায়
L2 শাস্তি (ridge)λ‖w‖₂²সব দিকে মসৃণ ছাঁটাই, শূন্য নয়অংশ ২–৩
L1 শাস্তি (lasso)λ‖w‖₁ছাঁটাই আর ঠিক শূন্য (sparsity)অংশ ৪–৫
Prior (Bayesian)w-এর ওপর বণ্টনশাস্তি = −log priorঅংশ ৬
Implicit (early stopping)কিছু যোগ হয় না, আগে থামাধাপের সংখ্যা λ-এর কাজ করেঅংশ ৭

এই ধারণার শিকড় দুই জায়গায়। পদার্থবিদ্যা আর প্রকৌশলের inverse problem-এ Andrey Tikhonov ১৯৪০ থেকে ১৯৬০-এর দশকে অস্থির সমীকরণকে স্থির করার এই কৌশল গড়ে তোলেন, তাই একে Tikhonov regularization-ও বলে। পরিসংখ্যানে Arthur Hoerl আর Robert Kennard ১৯৭০ সালে Technometrics-এ ridge regression প্রস্তাব করেন, প্রায়-collinear feature-এর অস্থির coefficient সারাতে।

২ · Ridge-কে SVD-র চোখে — প্রতিটি দিকে আলাদা ছাঁটাই

Ridge কমায় ‖y − Xw‖² + λ‖w‖², আর পর্ব ১১-এ দেখেছেন gradient শূন্য বসালে (XᵀX + λI)w = Xᵀy. ধরুন X আগে থেকে center করা (তাই intercept আলাদা), আর তার thin SVD X = UΣVᵀ: U-এর কলাম uj (দৈর্ঘ্য n), V-এর কলাম vj (দৈর্ঘ্য p), আর σ1 ≥ σ2 ≥ … ≥ ০. তাহলে XᵀX = VΣ²Vᵀ, আর XᵀX + λI = V(Σ² + λI)Vᵀ: λ যোগ করলে eigenvector বদলায় না, শুধু প্রতিটি eigenvalue σj² থেকে σj² + λ হয়। Inverse নিয়ে গুণ করলে:

wλ = ∑j vj · [σj/(σj² + λ)] · ujᵀy    ŷ = ∑j uj · [σj²/(σj² + λ)] · ujᵀy

λ = ০ হলে বন্ধনীর গুণক ১, আর পাই সাধারণ least squares: প্রতিটি দিকে θ̂j = ujᵀy/σj. Ridge সেই একই সংখ্যাকে গুণ করে shrink factor fj = σj²/(σj² + λ) দিয়ে, যা সবসময় ০ আর ১-এর মধ্যে। σj² ≫ λ হলে fj ≈ ১ (প্রায় অক্ষত); σj² ≪ λ হলে fj ≈ ০ (প্রায় মুছে যায়)। এই কারণেই ridge-কে বলা হয় shrinkage পদ্ধতি, আর পর্ব ০৯-এর truncated pseudo-inverse-এর সঙ্গে তুলনাটিও স্পষ্ট: সেখানে গুণক হয় ১ নয় ০ (কড়া ছাঁটাই), এখানে মসৃণ।

কেন ছোট σ-র দিকটিই কাটা উচিত? কারণ uj একক দৈর্ঘ্যের, তাই ujᵀε-এর noise প্রতিটি দিকে সমান, σε. ভাগ করার সময় ছোট σj সেই noise-কে ফোলায়। নিচের stepper দুটি দিক নিয়ে হিসাবটি দেখায়।

u₁ᵀy = 6, u₂ᵀy = 0.4; প্রতিটি uⱼᵀy-এর noise-এর sd = 0.3 (u একক দৈর্ঘ্যের বলে সমান)
ধাপ
দিক ১ (σ = 3)
দিক ২ (σ = 0.5)
মন্তব্য
  1. প্রস্তুতি। দুটি SVD দিক: দিক ১-এ ডেটা অনেক ছড়ানো (σ1 = ৩), দিক ২-এ খুব কম (σ2 = ০.৫). y-এর অংশ যথাক্রমে ৬ আর ০.৪. প্রতিটি ujᵀy-তে noise-এর standard deviation ০.৩.
  2. Least squares. θj = ujᵀy/σj: ২.০ আর ০.৮. Noise-ও একই ভাগে যায়: দিক ১-এ ০.৩/৩ = ০.১, দিক ২-এ ০.৩/০.৫ = ০.৬. দ্বিতীয় দিকের coefficient-এর অনিশ্চয়তা (০.৬) প্রায় তার মানের (০.৮) সমান।
  3. λ = ১-এর shrink factor. f1 = ৯/১০ = ০.৯, f2 = ০.২৫/১.২৫ = ০.২. একই λ দুই দিকে খুব আলাদা আচরণ করে, কারণ তুলনাটি σj²-এর সঙ্গে।
  4. নতুন coefficient. দিক ১: ১.৮ (মাত্র ১০% কম), noise-এর sd ০.০৯. দিক ২: ০.১৬, sd ০.১২; অর্থাৎ অনিশ্চয়তা পাঁচ ভাগের এক ভাগ। দাম হলো bias: আসল মান যদি ০.৮-এর কাছে হতো, এখন আমরা গড়ে অনেক কম বলছি।
  5. λ = ১০. f1 = ৯/১৯ = ০.৪৭৪, f2 = ০.০২৪. এবার শক্তিশালী দিকটিও অর্ধেক হয়ে গেছে (θ = ০.৯৪৭): বেশি λ মানে বেশি bias, যা আসল সংকেতও কাটে।
  6. Effective df. গুণকগুলোর যোগফল: λ = ১-এ ০.৯ + ০.২ = ১.১, λ = ১০-এ ০.৪৭৪ + ০.০২৪ = ০.৪৯৮. দুটি parameter-এর model “কার্যত” এক বা আধখানা parameter-এর মতো আচরণ করছে। পরের অংশে এই সংখ্যাটিই।

৩ · Effective degrees of freedom — λ দিয়ে model-এর আকার মাপা

Ridge-এর ŷ = Hλy, যেখানে Hλ = X(XᵀX + λI)−1Xᵀ হলো ridge-এর hat matrix. Least squares-এ H একটি projection, আর তার trace হুবহু p, অর্থাৎ parameter-এর সংখ্যা। Ridge-এর জন্য একই সংজ্ঞা নিলে:

df(λ) = tr(Hλ) = ∑j σj²/(σj² + λ)

λ = ০-তে df = p (X-এর rank পূর্ণ হলে), আর λ → ∞-এ df → ০. মাঝে এটি একটি ভগ্নাংশ, যেমন ২.৪৭: “এই model প্রায় আড়াইটি parameter-এর সমান নমনীয়”. সংজ্ঞাটির একটি গভীর রূপও আছে (Bradley Efron-এর কাজে জনপ্রিয়): df = ∑i Cov(ŷi, yi)/σε², অর্থাৎ প্রতিটি yi নিজের অনুমান ŷi-কে কতটা টানে। Linear smoother-এ এটি trace-এর সমান। Lasso-র ক্ষেত্রে Zou, Hastie আর Tibshirani ২০০৭ সালে দেখান যে কিছু শর্তে অশূন্য coefficient-এর সংখ্যাই df-এর একটি unbiased অনুমান। এই সংখ্যা λ-এর তুলনা সহজ করে (বিভিন্ন ডেটায় λ-এর মান তুলনীয় নয়, df তুলনীয়), আর generalized cross-validation বা Cp-এর মতো সূত্রে বসে।

নিচের ব্লক বাজারঘরের ৪০ জন কাল্পনিক গ্রাহকের ডেটায় তিনটি কাজ করে। “দেখা পাতা” কলামটি ইচ্ছা করে “app-এ ঢোকা”-র প্রায় নকল, যাতে একটি singular value খুব ছোট হয়। (১) SVD সূত্রে ridge বানিয়ে scikit-learn-এর Ridge-এর সঙ্গে মেলায়, (২) df আর hat matrix-এর trace মেলায়, (৩) λ বদলে df-এর বক্ররেখা আঁকে।

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge
np.set_printoptions(precision=3, suppress=True)

rng = np.random.default_rng(42)          # কাল্পনিক বাজারঘর: ৪০ জন গ্রাহক
n = 40
visits = rng.normal(10, 3, n)                      # মাসে app-এ ঢোকা
pages = 2.5 * visits + rng.normal(0, 0.5, n)       # দেখা পাতা: visits-এর প্রায় নকল
days = rng.integers(1, 60, n).astype(float)        # শেষ অর্ডারের পর দিন
basket = rng.normal(9, 2.5, n)                     # গড় ঝুড়ি, শত টাকা
X = np.column_stack([visits, pages, days, basket])
y = 200 + 30 * visits + 4 * pages - 6 * days + 40 * basket + rng.normal(0, 60, n)
Xs = (X - X.mean(0)) / X.std(0)                    # standardize: সব কলাম একই মাপে
yc = y - y.mean()

U, s, Vt = np.linalg.svd(Xs, full_matrices=False)
lam = 10.0; f = s**2 / (s**2 + lam)               # প্রতিটি SVD দিকের shrink factor
w_svd = Vt.T @ ((s / (s**2 + lam)) * (U.T @ yc))   # = V diag(σ/(σ²+λ)) Uᵀy
w_sk = Ridge(alpha=lam).fit(Xs, y).coef_
print("singular values σ :", s)
print("shrink σ²/(σ²+λ)  :", f)
print("ridge via SVD     :", w_svd)
print("sklearn Ridge     :", w_sk, " max diff", f"{np.abs(w_svd - w_sk).max():.1e}")

lams = np.logspace(-2, 4, 60)
df = [np.sum(s**2 / (s**2 + l)) for l in lams]
H = Xs @ np.linalg.solve(Xs.T @ Xs + lam * np.eye(4), Xs.T)   # hat matrix, λ = 10
print(f"df(10) = {np.sum(f):.3f}, trace(H) = {np.trace(H):.3f}")
for l in (0.1, 10, 100, 1000):
    print(f"λ = {l:<6} df = {np.sum(s**2 / (s**2 + l)):.2f}")
plt.figure(figsize=(6, 3.4), layout="constrained")
plt.semilogx(lams, df, lw=2)
plt.xlabel("lambda (log scale)"); plt.ylabel("effective df")
plt.title("ridge: effective degrees of freedom"); plt.grid(alpha=0.3); plt.show()

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

singular values σ : [9.187 6.733 5.496 0.254]
shrink σ²/(σ²+λ)  : [0.894 0.819 0.751 0.006]
ridge via SVD     : [ 39.862  42.37  -72.267  84.321]
sklearn Ridge     : [ 39.862  42.37  -72.267  84.321]  max diff 8.0e-13
df(10) = 2.471, trace(H) = 2.471
λ = 0.1    df = 3.39
λ = 10     df = 2.47
λ = 100    df = 1.00
λ = 1000   df = 0.15

চারটি singular value-এর শেষটি মাত্র ০.২৫৪, বাকিগুলো ৫.৫ থেকে ৯.২: “দেখা পাতা” আর “app-এ ঢোকা” প্রায় একই হওয়ায় তাদের পার্থক্যের দিকে ডেটা প্রায় ছড়ায়নি। λ = ১০-এ shrink factor ০.৮৯৪, ০.৮১৯, ০.৭৫১ আর ০.০০৬: তিনটি ভালো দিক ১০ থেকে ২৫ শতাংশ ছোট হয়েছে, আর নকল কলামের দিকটি কার্যত মুছে গেছে, ঠিক অংশ ২-এর stepper-এর মতো। SVD সূত্রে বানানো coefficient আর scikit-learn-এর Ridge-এর পার্থক্য ৮.০ × ১০−১৩, অর্থাৎ float-এর গোলের ভুলটুকু ছাড়া হুবহু এক (X আগেই center করা, আর Ridge intercept-কে শাস্তির বাইরে রাখে)। লক্ষ করুন visits আর pages প্রায় সমান ভাগ পেয়েছে (৩৯.৮৬ আর ৪২.৩৭): ridge নকল কলামদের মধ্যে ওজন ভাগ করে দেয়, কারণ সমান ভাগেই ‖w‖² সবচেয়ে ছোট। df(১০) = ২.৪৭১, আর hat matrix-এর trace-ও ২.৪৭১: দুটি সংজ্ঞা মেলে। তালিকায় λ = ০.১-এই df ৪ থেকে ৩.৩৯-এ নেমেছে, কারণ ছোট দিকটির σ² = ০.০৬৫, যা ০.১-এর চেয়েও ছোট; বাকি তিনটি দিক তখনো প্রায় অক্ষত। λ = ১০০-এ df ঠিক ১.০০, আর ১০০০-এ ০.১৫. ছবির বক্ররেখাটি তাই দুই ধাপে নামে: প্রথমে খুব ছোট λ-এ নকল দিকটি হারায়, তারপর ১০ থেকে কয়েকশর মধ্যে বাকি তিনটি একসঙ্গে। (সংখ্যাগুলো এই কাল্পনিক ডেটার একটি run; অন্য ডেটায় অন্য হবে।)

৪ · L1, subgradient আর soft-thresholding — lasso-র coordinate update

Lasso-র শাস্তি λ‖w‖₁ = λ∑|wj|, অর্থাৎ L1 norm. সমস্যা: |w|-এর w = ০-তে কোনো derivative নেই, কোণা আছে। তাই gradient শূন্য বসানোর পুরোনো কৌশল খাটে না। এর সমাধান subgradient: একটি convex function-এর কোনো বিন্দুতে যে সব ঢালের রেখা পুরো function-এর নিচে থাকে, তাদের ঢালের সেট। |w|-এর জন্য:

∂|w| = {+1} যদি w > 0,   {−1} যদি w < 0,   [−1, +1] যদি w = 0

আর convex function-এর সর্বনিম্ন বিন্দুর শর্ত: ০ সেই বিন্দুর subgradient সেটের মধ্যে আছে। এবার সবচেয়ে ছোট lasso: এক চলক, minw ½(w − z)² + λ|w|. এখানে z হলো শাস্তি ছাড়া যা উত্তর হতো। শর্ত লিখলে: ০ ∈ w − z + λ·∂│w│. তিনটি ক্ষেত্র আলাদা করে দেখলে উত্তর আসে soft-thresholding:

w* = S(z, λ) = sign(z) · max(|z| − λ, 0)

min ½(w − z)² + λ|w|z = 1.8

            
  1. তিনটি ক্ষেত্র। w ধনাত্মক হলে subgradient +১, ঋণাত্মক হলে −১, আর ঠিক শূন্যে [−১, ১]-এর যেকোনো সংখ্যা। প্রতিটি ক্ষেত্রে সমাধান বের করে দেখতে হয় সেটি নিজের ধরে নেওয়া শর্ত মানে কি না।
  2. λ = ০.৫. w = ১.৮ − ০.৫ = ১.৩, ধনাত্মক, তাই বৈধ। Soft-thresholding মানে “λ পরিমাণ সরিয়ে নেওয়া”: বড় coefficient থেকেও একই ০.৫ কাটা যায়।
  3. λ = ১.৫. w = ০.৩. মান মূলের মাত্র ১৭%, কিন্তু এখনো অশূন্য।
  4. λ = ২.৫. z − λ = −০.৭ ঋণাত্মক, অথচ ক্ষেত্র ১ ধরেছিল w > ০; বাতিল। ক্ষেত্র ২-এ w = ৪.৩, তাও বাতিল। ক্ষেত্র ৩: |১.৮| ≤ ২.৫, তাই w = ০ ঠিক শূন্য। এটিই lasso-র sparsity-র উৎস।
  5. Ridge-এর সঙ্গে তুলনা। একই সমস্যায় L2 শাস্তি দিলে w = z/(১ + λ): ১.২, ০.৭২, ০.৫১৪. অনুপাতে ছোট হয়, কিন্তু কোনো সসীম λ-এ শূন্য হয় না, কারণ w²-এর ঢাল শূন্যের কাছে নিজেই শূন্য।

তিনটি নিয়ম এক ছবিতে: least squares, ridge আর lasso

Orthonormal design: each estimate as a function of the least-squares value ŵ, with λ = 1

Least squares, ridge and lasso estimates as functions of the least-squares value −λ+λ −22 ŵ (least squares) → w ridge: ŵ/(1+λ) OLS: w = ŵ lasso: S(ŵ, λ) শূন্য-অঞ্চল
ছবি ৪.১ — Feature-গুলো পরস্পর লম্ব আর একক দৈর্ঘ্যের হলে (orthonormal design) প্রতিটি coefficient আলাদাভাবে সমাধান হয়। Ridge সব মানকে একই অনুপাতে ছোট করে (নীল), lasso |ŵ| ≤ λ অঞ্চলের সবকিছু ঠিক শূন্যে পাঠায় আর বাকিদের থেকে λ বাদ দেয় (সবুজ)। Best subset বা L0 শাস্তি দিলে আসে “hard thresholding”: ছোট হলে শূন্য, বড় হলে একটুও না কমানো।

অনেক feature-এ: coordinate descent

Feature অনেক আর পরস্পর সম্পর্কিত হলে সব coefficient একসঙ্গে closed form-এ মেলে না। কিন্তু একটি বাদে বাকি সব স্থির রাখলে সমস্যাটি ঠিক উপরের এক-চলকের রূপ নেয়। scikit-learn-এর Lasso যা কমায় (ডকুমেন্টেশন অনুযায়ী), তা দিয়েই লিখি:

L(w) = (1/2n)‖y − Xw‖² + α‖w‖₁

wj বাদে বাকিদের residual r(j) = y − ∑k≠j xkwk. তাহলে wj-এর অংশ (1/2n)‖r(j) − xjwj‖² + α|wj|. ρj = xjᵀr(j)/n আর zj = xjᵀxj/n লিখে বর্গ পূর্ণ করলে, subgradient শর্ত থেকে আসে:

wj ← S(ρj, α) / zj

Standardize করা কলামে zj = ১. প্রতিটি j-তে এই update পালাক্রমে চালানো, যতক্ষণ না কোনো coefficient আর নড়ে: এটিই coordinate descent. Lasso-র loss convex, আর শাস্তিটি প্রতিটি coordinate-এ আলাদা হয়ে যায় (separable), তাই এই পদ্ধতি সর্বনিম্নে পৌঁছায় (Paul Tseng-এর ২০০১ সালের ফলাফল এই ধরনের সমস্যার জন্য প্রচলিত তত্ত্ব)। আরেকটি সহজ ফল: w = ০-তে subgradient শর্ত হলো প্রতিটি j-এ |xjᵀy|/n ≤ α. তাই αmax = maxj |xjᵀy|/n-এর ওপরে সব coefficient শূন্য; regularization path এখান থেকেই শুরু হয়।

নিচের ব্লক বাজারঘরের ৫০টি কাল্পনিক ডেলিভারিতে (৮টি feature, শুধু প্রথম তিনটি কাজের) এই update নিজে লেখে, scikit-learn-এর সঙ্গে মেলায়, তারপর αmax থেকে নিচে ৪০টি α-তে পুরো path আঁকে। প্রতিটি α-র সমাধান আগের α-র উত্তর থেকে শুরু হয়; একে warm start বলে, আর path দ্রুত হওয়ার মূল কৌশল এটিই।

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import Lasso
np.set_printoptions(precision=3, suppress=True)

rng = np.random.default_rng(7)           # বাজারঘরের ৫০টি কাল্পনিক ডেলিভারি, ৮টি feature
n, p = 50, 8                              # শুধু প্রথম ৩টি (দূরত্ব, ওজন, যানজট) কাজের
X = rng.normal(size=(n, p)); X = (X - X.mean(0)) / X.std(0)
y = X @ np.array([3.0, -2.0, 1.5, 0, 0, 0, 0, 0]) + rng.normal(0, 1.5, n); y -= y.mean()

def soft(z, a): return np.sign(z) * np.maximum(np.abs(z) - a, 0.0)   # S(z, a)

def lasso_cd(X, y, a, w=None, tol=1e-10):    # (1/2n)‖y − Xw‖² + a‖w‖₁
    w = np.zeros(p) if w is None else w.copy()
    z, r = (X**2).sum(0) / n, y - X @ w
    for sweep in range(1000):
        w_old = w.copy()
        for j in range(p):
            r += X[:, j] * w[j]                    # j বাদে বাকিদের residual
            w[j] = soft(X[:, j] @ r / n, a) / z[j]
            r -= X[:, j] * w[j]
        if np.abs(w - w_old).max() < tol: return w
    return w

w_cd = lasso_cd(X, y, 0.5)
w_sk = Lasso(alpha=0.5, tol=1e-12, max_iter=100000).fit(X, y).coef_
print("CD     :", w_cd + 0.0); print("sklearn:", w_sk + 0.0, " max diff", f"{np.abs(w_cd - w_sk).max():.1e}")
a_max = np.abs(X.T @ y).max() / n
alphas = a_max * np.logspace(0.01, -3, 40); w, path = None, []
for a in alphas:
    w = lasso_cd(X, y, a, w); path.append(w)       # warm start: আগের উত্তর থেকে শুরু
path = np.array(path)
print(f"alpha_max = {a_max:.3f}; non-zeros on path:", [int((r != 0).sum()) for r in path[::5]])
plt.figure(figsize=(6, 3.4), layout="constrained"); plt.semilogx(alphas, path); plt.gca().invert_xaxis()
plt.xlabel("alpha (log scale, decreasing)"); plt.ylabel("coefficient"); plt.title("lasso path by coordinate descent"); plt.show()

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

CD     : [ 2.132 -1.532  1.043  0.     0.     0.     0.     0.   ]
sklearn: [ 2.132 -1.532  1.043  0.     0.     0.     0.     0.   ]  max diff 1.9e-12
alpha_max = 2.515; non-zeros on path: [0, 3, 3, 4, 6, 6, 7, 7]

নিজের লেখা coordinate descent আর scikit-learn-এর Lasso-র পার্থক্য ১.৯ × ১০−১২: একই objective, একই উত্তর। (+ 0.0 যোগ করা হয়েছে, যাতে −০.০ “-0.” হিসেবে না ছাপে; soft-thresholding ঋণাত্মক z-কে শূন্য করলে float-এ −০.০ আসে।) α = ০.৫-এ পাঁচটি অকাজের feature ঠিক শূন্য, আর তিনটি আসল feature টিকে আছে, কিন্তু ছোট হয়ে: ২.১৩২, −১.৫৩২ আর ১.০৪৩, যেখানে ডেটা বানানো হয়েছিল ৩, −২ আর ১.৫ দিয়ে। প্রায় α পরিমাণ কাটা পড়েছে (path-এর একেবারে ছোট α-তে এদের মান প্রায় ২.৬১, −২.১৭ আর ১.৫৯), তবে ঠিক ০.৫ নয়, কারণ কলামগুলো পুরো লম্ব নয়। αmax = ২.৫১৫, যা প্রথম কলামের |xjᵀy|/n; path শুরু হয়েছে এর সামান্য ওপর থেকে, আর সেখানে অশূন্যের সংখ্যা ০, অংশ ৪-এর সূত্রের মতোই। প্রতি পঞ্চম α-তে অশূন্যের সংখ্যা ০, ৩, ৩, ৪, ৬, ৬, ৭, ৭: আসল তিনটি আগে ঢোকে, অকাজেরগুলো ঢোকে কেবল অনেক ছোট α-তে, আর একটি αmax/১০০০-এর কাছেও শূন্য থাকে। ছবিতে বাঁ থেকে ডানে α কমছে; প্রতিটি রেখা শূন্য থেকে বেরিয়ে প্রায় সরলভাবে বাড়ে। Lasso-র path আসলে টুকরো টুকরো সরলরেখা (α-এর সাপেক্ষে), log স্কেলের কারণে বাঁকা দেখায়।

৫ · জ্যামিতি — শাস্তির রূপ বনাম সীমার রূপ, আর L1 বলের কোণ

একই lasso দুইভাবে লেখা যায়। শাস্তির রূপ: min ‖y − Xw‖² + λ‖w‖₁. সীমার রূপ: min ‖y − Xw‖², শর্ত ‖w‖₁ ≤ t. প্রতিটি λ > ০-এর জন্য একটি t আছে (সেই λ-এর সমাধানের ‖w‖₁ নিজেই), যেখানে দুই সমস্যার উত্তর এক; ridge-এর জন্যও একই কথা। সংযোগটি হলো Lagrange multiplier: সীমার রূপের KKT শর্ত লিখলে যে multiplier আসে, সেটিই λ. এই শর্তগুলোর পুরো তত্ত্ব পর্ব ১৬ (Constraint-সহ optimization — Lagrange multiplier আর KKT)-এর বিষয়; এখানে শুধু ফল লাগবে। দুটি সতর্কতা: t আর λ-এর সম্পর্ক ডেটার ওপর নির্ভর করে, আর lasso-য় একই সমাধান অনেক সময় এক সারি λ-এর জন্য থাকে না, কারণ path টুকরো টুকরো সরলরেখা।

সীমার রূপে জ্যামিতি পরিষ্কার। AI-এর ভিত্তি কোর্সের পর্ব ৬১-এর হীরা আর বৃত্তের ছবিটি মনে করুন। এবার সেই ছবির পেছনের হিসাবটি সংখ্যায়, সবচেয়ে সরল ক্ষেত্রে: X-এর কলাম orthonormal, তাই loss-এর সমান-মানের রেখা বৃত্তাকার আর কেন্দ্র ŵ.

min ‖w − ŵ‖² শর্তসহ; loss-রেখা ŵ-কে কেন্দ্র করে বৃত্ত (orthonormal X)
ধাপ
হিসাব
ফল
  1. শুরু। শাস্তি ছাড়া উত্তর ŵ = (২, ০.৩), যার L1 দৈর্ঘ্য ২.৩. সীমা t = ১.৫, তাই উত্তরকে হীরার গায়ে কোথাও থাকতে হবে: ভেতরে থাকলে আরও কাছে যাওয়া যেত।
  2. ধারে চেষ্টা। প্রথম চতুর্ভাগে হীরার ধার w1 + w2 = ১.৫. ŵ থেকে এই রেখায় লম্ব ফেললে পাই (১.৬, −০.১): কিন্তু w2 ঋণাত্মক, অর্থাৎ বিন্দুটি এই ধারের অংশ নয়।
  3. কোণ। তাই নিকটতম বৈধ বিন্দু ধারের শেষ মাথা, কোণ (১.৫, ০). w2 ঠিক শূন্য, যদিও ŵ2 = ০.৩ শূন্য ছিল না।
  4. একই সীমায় L2. বৃত্ত ‖w‖2 ≤ ১.৫-এ নিকটতম বিন্দু ŵ-এর দিকেই: ১.৫ · (২, ০.৩)/২.০২২ = (১.৪৮৩, ০.২২৩). বৃত্তের কোনো কোণ নেই, তাই কোনো ঘর শূন্য হয় না।
  5. কোণের “ধরার এলাকা”. যেকোনো ŵ যার ŵ1 − ১.৫ ≥ |ŵ2|, তার উত্তরই একই কোণ। এটি একটি পুরো কোণাকৃতি এলাকা, তাই এলোমেলো ŵ-এর একটি বড় অংশ শূন্যে পৌঁছায়। বৃত্তে এই এলাকা শুধু একটি রেখা (ŵ2 = ০ হুবহু), যার সম্ভাবনা কার্যত শূন্য। বহু মাত্রায় হীরার কোণ আর নিচু-মাত্রার ধার আরও অনেক, তাই অনেক ঘর একসঙ্গে শূন্য হয়।
  6. শাস্তির রূপে মিলিয়ে দেখা। ½‖w − ŵ‖² + μ‖w‖₁-এর উত্তর S(ŵ, μ). μ = ০.৫-এ S((২, ০.৩), ০.৫) = (১.৫, ০), যার L1 দৈর্ঘ্য ঠিক ১.৫. অর্থাৎ t = ১.৫-এর সীমা আর μ = ০.৫-এর শাস্তি একই উত্তর দেয়; subgradient শর্তেও মেলে: w1-এ ১.৫ − ২ + ০.৫ = ০, আর w2-এ |০ − ০.৩| ≤ ০.৫.

তাহলে L0 (অশূন্য coefficient গোনা) সরাসরি শাস্তি দিলে কী হয়? সেটি best subset selection, যা সবচেয়ে sparse, কিন্তু non-convex আর সাধারণভাবে combinatorial: p = ৪০ হলে প্রায় ১.১ লাখ কোটি উপসেট। |w|q (q < ১)-ও বেশি sparse, কিন্তু non-convex. L1 হলো সবচেয়ে “sparse-প্রেমী” convex শাস্তি: q = ১-এর নিচে নামলেই convexity হারায়। কেন convexity এত দামি, সেটি পর্ব ১২ (Convexity — কেন কিছু loss সহজ আর কিছু কঠিন)-এর বিষয়।

৬ · Regularization মানে MAP — Gaussian আর Laplace prior

AI-এর ভিত্তি কোর্সের পর্ব ৫০-এ Bayes-এর নিয়ম আর পর্ব ৬১-এর “আরও গভীরে” বাক্সে এই সংযোগের এক অনুচ্ছেদের সারাংশ পেয়েছেন। এবার পুরো হিসাব। মডেল: y = Xw + ε, ε-এর ঘরগুলো স্বাধীন N(০, σ²). তাহলে likelihood p(y | w) ∝ exp(−‖y − Xw‖²/(2σ²)). এখন w-এর প্রতিটি ঘরে একটি prior বসাই। Bayes-এর নিয়মে posterior ∝ likelihood × prior, আর posterior যেখানে সর্বোচ্চ, সেটিই MAP. লগ নিয়ে চিহ্ন উল্টালে, “সর্বোচ্চ posterior” মানে “সর্বনিম্ন −log posterior”:

−log p(w | y) = ‖y − Xw‖²/(2σ²) − log p(w) + ধ্রুবক

Gaussian prior → ridge

wj ~ N(০, τ²) হলে −log p(w) = ‖w‖²/(2τ²) + ধ্রুবক. 2σ² দিয়ে গুণ করলে: ‖y − Xw‖² + (σ²/τ²)‖w‖². অর্থাৎ λ = σ²/τ². Prior যত সরু (τ ছোট, “w ছোট হওয়ার কথা” বিশ্বাস দৃঢ়), বা noise যত বেশি, λ তত বড়।

Laplace prior → lasso

wj-এর ঘনত্ব ∝ exp(−|wj|/b) হলে (Laplace distribution) −log p(w) = ‖w‖₁/b + ধ্রুবক. 2σ² দিয়ে গুণ করলে ‖y − Xw‖² + (2σ²/b)‖w‖₁, অর্থাৎ λ = 2σ²/b. Laplace-এর শূন্যে তীক্ষ্ণ চূড়াই সেই কোণা, যা MAP-কে শূন্যে আটকায়।

এই দৃষ্টিভঙ্গি কয়েকটি ব্যবহারিক নিয়মকে যুক্তি দেয়। Standardization: সব wj-এ একই prior বসানো যুক্তিসঙ্গত কেবল যখন feature-গুলো একই মাপে; না হলে কিলোমিটার আর মিটারের coefficient-এর একই prior অর্থহীন। Intercept-এ শাস্তি নেই: intercept-এর prior সমতল (প্রায় অসীম τ), কারণ গড় স্তর নিয়ে আমাদের কোনো পূর্ব ধারণা নেই। λ-এর অর্থ: noise আর prior-এর অনুপাত; ডেটা যত বাড়ে, likelihood-এর পদ (n-টি বর্গের যোগফল) তত বড় হয়, আর একই λ-এর প্রভাব আপেক্ষিকভাবে কমে।

MAP একটি বিন্দু, পুরো posterior নয়। Lasso-র ঠিক শূন্য আসে posterior-এর সর্বোচ্চ বিন্দু থেকে। Laplace prior-এর পুরো posterior-এর গড় নিলে (Park আর Casella-র ২০০৮ সালের “Bayesian lasso”) coefficient সাধারণত ঠিক শূন্য হয় না। অর্থাৎ “lasso শূন্য করেছে, তাই এই feature-এর প্রভাব নেই”, এই দাবি Bayesian দৃষ্টিতেও দুর্বল। সত্যিকারের sparse বিশ্বাস লিখতে গবেষকেরা spike-and-slab বা horseshoe-এর মতো prior ব্যবহার করেন। MLE আর MAP-এর সাধারণ তত্ত্ব আসবে পর্ব ১৮ (Maximum likelihood আর MAP — model fit করার মূলনীতি)-এ, আর পুরো posterior পর্ব ৪৫ (Bayesian linear regression)-এ।

৭ · Early stopping — শাস্তি ছাড়াই regularization

AI-এর ভিত্তি কোর্সের পর্ব ৩৪-এর gradient descent চালান ½‖y − Xw‖²-এ, শুরু w0 = ০ থেকে, ধাপের মাপ η: wt+1 = wt + ηXᵀ(y − Xwt). কোনো শাস্তি নেই, তবু আগে থামালে w ছোট থাকে। কতটা ছোট? SVD-র ভাষায় প্রতিটি দিক আলাদা চলে। vj দিকের অংশ θj(t) আর least squares-এর মান θ̂j-এর পার্থক্য প্রতি ধাপে (1 − ησj²) গুণ হয়, আর শুরুতে পার্থক্য −θ̂j. তাই:

θj(t) = [1 − (1 − ησj²)t] · θ̂j    বনাম ridge: [σj²/(σj² + λ)] · θ̂j

দুটিই একটি filter: বড় σ-র দিক আগে শেখা হয়, ছোট σ-র দিক দেরিতে। মিলটি সংখ্যায় দেখতে λ = 1/(ηt) বসান। ησj²t ছোট হলে দুটি গুণকই প্রায় ησj²t; বড় হলে দুটিই প্রায় ১. মাঝখানে পার্থক্য আছে: x = ησ²t লিখলে GD-র গুণক প্রায় 1 − e−x, ridge-এর x/(1 + x); x = ১-এ ০.৬৩ বনাম ০.৫. তাই early stopping মোটামুটি ridge, হুবহু নয়; ধাপের সংখ্যা t উল্টো λ-এর মতো কাজ করে। Early stopping এক ধরনের implicit regularization: loss-এ কিছু যোগ না করেও optimizer-এর আচরণ থেকে regularization আসে। শর্ত মনে রাখুন: η < 2/σ1² না হলে |1 − ησ²| > ১ হয়ে GD ছিটকে যায়; ধাপের মাপের পুরো আলোচনা পর্ব ১৩ (Gradient descent-এর পরিবার)-এ।

import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(3)            # কাল্পনিক ৪০টি সারি, ৬টি standardized feature
n, p = 40, 6
X = rng.normal(size=(n, p)) @ np.diag([3, 2, 1.5, 1, 0.5, 0.2])   # দিকভেদে আলাদা σ
X -= X.mean(0)
y = X @ np.array([1.0, -1.0, 0.8, 0.5, 0.0, 0.0]) + rng.normal(0, 1, n); y -= y.mean()
U, s, Vt = np.linalg.svd(X, full_matrices=False)
eta = 0.9 / s[0]**2                        # ধাপের মাপ: η < 2/σ_max² হলে স্থির
w_ols = np.linalg.lstsq(X, y, rcond=None)[0]

def ridge(lam):                            # ‖y − Xw‖² + λ‖w‖²
    return np.linalg.solve(X.T @ X + lam * np.eye(p), X.T @ y)

w, gd = np.zeros(p), []
for t in range(1, 2001):                   # (1/2)‖y − Xw‖²-এ gradient descent, শুরু 0 থেকে
    w = w + eta * X.T @ (y - X @ w); gd.append(w.copy())
print(" t   λ=1/(ηt)   ‖w_t‖   ‖w_ridge‖   ‖w_t − w_ridge‖/‖w_ols‖")
for t in (5, 20, 100, 500, 2000):
    lam = 1 / (eta * t); wr = ridge(lam)
    print(f"{t:4d} {lam:9.2f} {np.linalg.norm(gd[t-1]):7.3f} {np.linalg.norm(wr):9.3f}"
          f" {np.linalg.norm(gd[t-1] - wr) / np.linalg.norm(w_ols):12.3f}")
print("σ:", np.round(s, 2)); t = 20; print("filter GD   :", np.round(1 - (1 - eta * s**2)**t, 3))
print("filter ridge:", np.round(s**2 / (s**2 + 1 / (eta * t)), 3))
gd = np.array(gd); rp = np.array([ridge(1 / (eta * t)) for t in range(1, 2001)])
fig, ax = plt.subplots(1, 2, figsize=(7, 3.2), sharey=True, layout="constrained")
ax[0].plot(np.linalg.norm(gd, axis=1), gd); ax[0].set_title("gradient descent, t = 1..2000")
ax[1].plot(np.linalg.norm(rp, axis=1), rp); ax[1].set_title("ridge, lambda = 1/(eta t)")
for a in ax: a.set_xlabel("norm of w")
ax[0].set_ylabel("coefficient"); plt.show()

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

 t   λ=1/(ηt)   ‖w_t‖   ‖w_ridge‖   ‖w_t − w_ridge‖/‖w_ols‖
   5     76.20   1.553     1.237        0.195
  20     19.05   1.648     1.510        0.098
 100      3.81   1.653     1.620        0.025
 500      0.76   1.655     1.647        0.012
2000      0.19   1.657     1.654        0.011
σ: [18.52 12.57  9.61  7.21  3.11  0.98]
filter GD   : [1.    1.    0.996 0.947 0.403 0.049]
filter ridge: [0.947 0.892 0.829 0.732 0.337 0.048]

সারণির শেষ কলাম দেখায়, মেলানো λ = 1/(ηt)-তে GD আর ridge-এর পার্থক্য least squares-এর দৈর্ঘ্যের তুলনায় কত: t = ৫-এ ০.১৯৫ (প্রায় ২০%), t = ২০-এ ০.০৯৮, t = ১০০-এ ০.০২৫, আর ৫০০ থেকে ২০০০-এ প্রায় ০.০১১. অর্থাৎ পুরো পথে দুটি কাছাকাছি চলে, শুরুতে মিল দুর্বল। প্রতিটি সারিতে ‖wt‖ ridge-এর চেয়ে একটু বড়, কারণ 1 − (1 − x/t)t ≈ 1 − e−x সবসময় x/(1 + x)-এর চেয়ে বড়। t = ২০-এর filter দুটি পাশাপাশি দেখুন: সবচেয়ে বড় দুটি σ (১৮.৫২ আর ১২.৫৭)-এর দিকে GD ইতিমধ্যেই পুরো শিখে ফেলেছে (১.০), ridge সেখানে ০.৯৪৭ আর ০.৮৯২; কিন্তু সবচেয়ে ছোট σ (০.৯৮)-এর দিকে দুটিই প্রায় সমান (০.০৪৯ বনাম ০.০৪৮). ঠিক এই ছোট দিকগুলোতেই noise সবচেয়ে বেশি ফোলে, তাই regularization-এর কাজের জায়গায় মিলটি সবচেয়ে ভালো। t = ২০০০-এও পার্থক্য ০.০১১-এ থেমে আছে, কারণ ছোট দিকে ησ²t প্রায় ৫, যেখানে দুটি গুণক (প্রায় ০.৯৯৪ বনাম ০.৮৩) আলাদা। ছবির দুটি প্যানেলে x-অক্ষ ‖w‖, আর ছয়টি coefficient-এর পথ প্রায় একই আকৃতির: শূন্য থেকে শুরু, বড় coefficient আগে বাড়ে। এটি একটি কাল্পনিক ডেটার একটি run; মিলের মাত্রা ডেটা আর η-এর ওপর নির্ভর করে।

বাস্তবে t বাছা হয় validation দিয়ে: প্রতি epoch-এর পর validation loss দেখুন, কয়েক epoch (patience) উন্নতি না হলে থামুন, আর সেরা অবস্থার ওজন ফেরত নিন। একটি বাড়তি সুবিধা: একটিমাত্র training-এ পুরো “path” পাওয়া যায়, প্রতিটি λ-এর জন্য আলাদা fit লাগে না। Neural network-এ এটি সবচেয়ে প্রচলিত regularization-গুলোর একটি; সেখানে loss non-convex, তাই উপরের হুবহু সূত্র খাটে না, শুধু ধারণাটি। এর বিস্তার পরে Deep Learning কোর্সে।

৮ · Elastic net, একসঙ্গে চলা feature আর cross-validation দিয়ে λ বাছাই

Lasso-র দুটি পরিচিত দুর্বলতা। প্রথম, p > n হলে সে বড়জোর n-টি feature বেছে নিতে পারে। দ্বিতীয়, প্রায় একই কয়েকটি feature থাকলে সে প্রায় খেয়ালখুশিমতো একটি রাখে, বাকিগুলো শূন্য করে, আর ডেটা একটু বদলালে অন্যটি রাখে। Hui Zou আর Trevor Hastie ২০০৫ সালে এর প্রতিকার হিসেবে elastic net প্রস্তাব করেন: দুটি শাস্তি একসঙ্গে। scikit-learn-এর ElasticNet (ডকুমেন্টেশন অনুযায়ী) কমায়:

(1/2n)‖y − Xw‖² + α·ρ·‖w‖₁ + ½·α·(1 − ρ)·‖w‖₂²

এখানে ρ হলো l1_ratio: ρ = ১ মানে lasso, ρ = ০ মানে ridge. L2 অংশটি loss-কে strictly convex করে, তাই সমাধান একটিই থাকে; আর Zou ও Hastie দেখান যে দুটি feature-এর সম্পর্ক ১-এর কাছে গেলে তাদের coefficient-ও কাছাকাছি হয়, যাকে তাঁরা grouping effect বলেছেন। L1 অংশ sparsity রাখে। নিচের ব্লকে তিনটি কলাম (app-এ ঢোকা, দেখা পাতা, কাটানো মিনিট) একটি অদৃশ্য “আগ্রহ”-এর তিনটি মাপ, পরস্পর সম্পর্ক প্রায় ০.৯৯; বাকি পাঁচটি অকাজের। λ (আর elastic net-এ ρ) বাছা হয় পাঁচ-fold cross-validation-এ, তারপর একই কাজ পাঁচটি bootstrap নমুনায়, কে টিকে থাকে দেখতে। max_iter=10000 দেওয়া হয়েছে যাতে solver পুরোপুরি মিলে যায় আর কোনো ConvergenceWarning না আসে।

import numpy as np
from sklearn.linear_model import LassoCV, ElasticNetCV
from sklearn.model_selection import KFold
np.set_printoptions(precision=2, suppress=True)

rng = np.random.default_rng(11)         # কাল্পনিক বাজারঘর: ৮০ জন গ্রাহকের মাসিক কেনাকাটা
n = 80
engage = rng.normal(size=n)                        # অদৃশ্য "আগ্রহ", তিনটি মাপে ধরা পড়ে
group = np.column_stack([engage + rng.normal(0, 0.1, n) for _ in range(3)])  # visits, pages, minutes
noise = rng.normal(size=(n, 5))                    # ৫টি অকাজের feature
X = np.column_stack([group, noise]); X = (X - X.mean(0)) / X.std(0)
y = 3 * engage + rng.normal(0, 1, n)
print("corr inside group:", np.round(np.corrcoef(group.T)[np.triu_indices(3, 1)], 2))

cv = KFold(5, shuffle=True, random_state=42)
las = LassoCV(cv=cv, max_iter=10000).fit(X, y)
enet = ElasticNetCV(l1_ratio=[0.1, 0.3, 0.5, 0.7, 0.9], cv=cv, max_iter=10000).fit(X, y)
print(f"LassoCV      alpha={las.alpha_:.3f}             coef:", las.coef_ + 0.0)
print(f"ElasticNetCV alpha={enet.alpha_:.3f} l1_ratio={enet.l1_ratio_}  coef:", enet.coef_ + 0.0)
print(f"CV MSE  lasso {las.mse_path_.mean(1).min():.3f}   enet {enet.mse_path_.mean(2).min():.3f}")

for seed in range(5):                   # একই সমস্যা, ৫টি bootstrap নমুনা: কে টিকে থাকে?
    i = np.random.default_rng(seed).integers(0, n, n)
    a = LassoCV(cv=cv, max_iter=10000).fit(X[i], y[i]).coef_[:3]
    b = ElasticNetCV(l1_ratio=0.5, cv=cv, max_iter=10000).fit(X[i], y[i]).coef_[:3]
    print(f"bootstrap {seed}: lasso group {a + 0.0}   enet(0.5) group {b + 0.0}")

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

corr inside group: [0.99 0.99 0.99]
LassoCV      alpha=0.159             coef: [ 1.23  0.19  0.85  0.   -0.08  0.    0.    0.  ]
ElasticNetCV alpha=0.190 l1_ratio=0.9  coef: [ 0.9   0.54  0.8   0.   -0.07  0.    0.    0.  ]
CV MSE  lasso 1.229   enet 1.208
bootstrap 0: lasso group [0.   0.   2.42]   enet(0.5) group [0.65 0.64 1.1 ]
bootstrap 1: lasso group [1.19 0.22 0.96]   enet(0.5) group [0.84 0.64 0.84]
bootstrap 2: lasso group [0.66 0.   1.6 ]   enet(0.5) group [0.78 0.46 0.98]
bootstrap 3: lasso group [0.39 0.   1.9 ]   enet(0.5) group [0.72 0.59 0.98]
bootstrap 4: lasso group [0.72 0.   1.62]   enet(0.5) group [0.77 0.56 1.02]

দলের তিনটি কলামের পরস্পর সম্পর্ক ০.৯৯. পুরো ডেটায় LassoCV α = ০.১৫৯ বেছেছে আর দলকে দিয়েছে (১.২৩, ০.১৯, ০.৮৫): মাঝেরটি প্রায় বাদ। ElasticNetCV পাঁচটি l1_ratio-র মধ্যে ০.৯ বেছেছে (প্রায় lasso), তবু তার ভাগ সমান-ঘেঁষা (০.৯, ০.৫৪, ০.৮). দুটিই অকাজের পাঁচটির চারটি ঠিক শূন্য করেছে, আর CV ভুল প্রায় সমান: ১.২২৯ বনাম ১.২০৮. অর্থাৎ অনুমানের মানে দুটি প্রায় এক; তফাত বাছাইয়ের স্থিরতায়। Bootstrap-এর সারিগুলো সেটিই দেখায়: নমুনা ০-তে lasso প্রথম দুটি কলাম ঠিক শূন্য করে সব ওজন (২.৪২) তৃতীয়টিকে দিয়েছে; নমুনা ২, ৩ আর ৪-এ দ্বিতীয়টি শূন্য; নমুনা ১-এ তিনটিই আছে। একই সমস্যা, সামান্য ভিন্ন ডেটা, আর “কোন feature গুরুত্বপূর্ণ” প্রশ্নের উত্তর বদলে যাচ্ছে। Elastic net (l1_ratio = ০.৫) প্রতিটি নমুনায় তিনটিকেই রেখেছে, প্রতিটিকে ০.৪৬ থেকে ১.২৭-এর মধ্যে। দুই পদ্ধতিতেই দলের মোট ওজন প্রায় একই (যেমন নমুনা ০-তে ২.৪২ বনাম ২.৩৯): মোট প্রভাব স্থির, ভাগাভাগিটাই অস্থির। ব্লকটি কয়েকশ ছোট fit চালায়, তাই ব্রাউজারে এক-দুই সেকেন্ড লাগতে পারে।

λ বাছাইয়ের নিয়ম

  • পুরো path, warm start: LassoCV আর ElasticNetCV নিজেরাই αmax থেকে αmax/১০০০ পর্যন্ত (default eps=1e-3) ১০০টি α-র grid বানায় আর প্রতিটি fold-এ warm start দিয়ে path চালায়। ডকুমেন্টেশন l1_ratio-এর জন্য ১-এর কাছের মানগুলো (যেমন ০.১, ০.৫, ০.৭, ০.৯, ০.৯৫, ০.৯৯, ১) বেশি চেষ্টা করার পরামর্শ দেয়।
  • One-standard-error rule: সর্বনিম্ন CV ভুলের α না নিয়ে, তার এক standard error-এর মধ্যে থাকা সবচেয়ে বড় α নেওয়া (সরলতম model). Breiman ও সহকর্মীদের CART বই (১৯৮৪) থেকে আসা এই নিয়ম প্রায় একই নির্ভুলতায় বেশি sparse model দেয়; তবে এটি একটি প্রথা, উপপাদ্য নয়।
  • Leakage এড়ানো: standardization আর feature বাছাই প্রতিটি fold-এর ভেতরে, pipeline দিয়ে (AI-এর ভিত্তি কোর্সের পর্ব ৬৪). CV দিয়ে বাছা α-র score আশাবাদী; সৎ অনুমান চাইলে nested CV বা তালাবন্ধ test set (পর্ব ৬৬).
  • Path পড়া: basis function আর interaction-সহ বড় linear model-এ regularization path কীভাবে পড়বেন, সেটি পর্ব ২৩ (Linear model গভীরে — basis function, interaction আর regularization path)-এর বিষয়।

আরও গভীরে: Lasso-র পুরো path-কে টুকরো টুকরো সরলরেখা হিসেবে হুবহু বের করার পদ্ধতি LARS (Efron, Hastie, Johnstone আর Tibshirani, ২০০৪, Annals of Statistics); আর coordinate descent-এর জনপ্রিয়তা আসে Friedman, Hastie আর Tibshirani-র ২০১০ সালের glmnet প্রবন্ধ (Journal of Statistical Software) থেকে, যদিও একই ধাঁচের “shooting” পদ্ধতি Wenjiang Fu ১৯৯৮ সালে লিখেছিলেন। Lasso কখন ঠিক feature-গুলোই বাছে, তার একটি শর্ত “irrepresentable condition” (Zhao আর Yu, ২০০৬); একসঙ্গে চলা feature-এ এই শর্ত সহজেই ভাঙে। পাঠ্যবই হিসেবে Hastie, Tibshirani আর Wainwright-এর Statistical Learning with Sparsity (২০১৫) আর Hastie, Tibshirani ও Friedman-এর The Elements of Statistical Learning-এর তৃতীয় অধ্যায় এই পর্বের প্রায় সব সূত্র ধারণ করে।

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

  • Ridge কেন collinearity-তে সাহায্য করে? XᵀX-এর প্রতিটি eigenvalue-তে λ যোগ হয়; ছোট σ-র দিকের গুণক σ²/(σ² + λ) প্রায় ০, তাই variance আর condition number কমে।
  • Lasso কেন ঠিক শূন্য দেয়, ridge কেন নয়? |w|-এর শূন্যে subgradient [−১, ১]; |z| ≤ λ হলে S(z, λ) = ০. w²-এর ঢাল শূন্যে ০, তাই ridge শুধু z/(১ + λ)-তে ছোট করে।
  • Ridge আর lasso কোন prior-এর MAP? যথাক্রমে শূন্য-গড়ের Gaussian (λ = σ²/τ²) আর Laplace (λ = 2σ²/b).
  • Effective degrees of freedom কীভাবে বের করবেন? ∑ σj²/(σj² + λ) = tr(Hλ); λ = ০-তে p, λ → ∞-এ ০.
  • Elastic net কখন? একসঙ্গে চলা feature-এর দল বা p > n হলে, যখন sparsity আর স্থিরতা দুটিই চাই।

৯ · AI-এর যুগে — regularization-এর কোড আর দাবি যাচাই

Regularization-এর কোড ছোট, কিন্তু ভুলগুলো নিঃশব্দ: ভুল objective-এর সংজ্ঞা, ভুল জায়গায় standardization, বা test set দিয়ে λ বাছাই। কোড চলে, সংখ্যাও আসে, শুধু সেই সংখ্যা যা বলছে তা সত্য নয়। AI assistant এই জায়গাগুলোতেই প্রায়ই আত্মবিশ্বাসী ভুল করে।

AI প্রায়ই যা করেকেন ভুলকীভাবে ধরবেন
Ridge-এর alpha আর Lasso-র alpha একই স্কেলে তুলনা করেRidge-এর loss-এ 1/(2n) নেই, Lasso-তে আছে; সংখ্যা তুলনীয় নয়নিজের version-এর docs-এ দুটি objective পড়ুন; ছোট ডেটায় closed form বা CD দিয়ে মেলান
পুরো ডেটায় standardize বা lasso দিয়ে feature বেছে তারপর CV চালায়Validation fold-এর তথ্য train-এ ঢোকে; score আশাবাদীসব ধাপ একটি Pipeline-এ, আর CV সেই pipeline-এর ওপর
Test set-এ সেরা λ বেছে সেই score-ই রিপোর্ট করেTest set আর নিরপেক্ষ নেইλ বাছাই CV-তে; test একবার, শেষে
“Lasso শূন্য করেছে, তাই feature-টির প্রভাব নেই” বলেএকসঙ্গে চলা feature-এ বাছাই অস্থির; শূন্য মানে কারণগত অনুপস্থিতি নয়Bootstrap বা ভিন্ন fold-এ বাছাই কতবার টেকে দেখুন; elastic net-এর সঙ্গে তুলনা করুন
ConvergenceWarning দেখে warnings.filterwarnings("ignore") দেয়Solver না মিললে coefficient ভুল হতে পারেFeature standardize করুন, max_iter বাড়ান, বা α বড় করুন; warning-এর কারণ সারান
“Early stopping আর ridge হুবহু এক” বলেশুধু আনুমানিক মিল, তাও linear least squares-এSVD-র filter factor দুটি পাশাপাশি হিসাব করুন

AI-কে শুধু কোড নয়, objective, অনুমান আর যাচাইও চাইতে বলুন:

আমার কাছে ৮০টি সারি আর ৩০টি feature-এর একটি regression ডেটা আছে, কয়েকটি feature পরস্পর খুব সম্পর্কিত। ১. scikit-learn-এর Ridge, Lasso আর ElasticNet ঠিক কোন objective কমায়, ডকুমেন্টেশনের সূত্রসহ লেখো (n দিয়ে ভাগ আছে কি না সহ)। ২. Standardization আর ElasticNetCV একটি Pipeline-এ রেখে কোড দাও, cv-তে shuffle আর random_state সহ; কোনো warning চাপা দিও না। ৩. বাছা alpha আর l1_ratio ছাপো, আর one-standard-error rule দিয়ে আরেকটি alpha বের করো। ৪. ২০টি bootstrap নমুনায় প্রতিটি feature কতবার অশূন্য হয়, তার একটি তালিকা দাও। ৫. কোন দাবিগুলো এই ফল থেকে করা যায় না (যেমন কারণগত প্রভাব), সেটিও স্পষ্ট করে বলো। ৬. তোমার দেওয়া কোনো parameter আমার scikit-learn version-এ বাদ পড়েছে কি না, কীভাবে যাচাই করব বলো।

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

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

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

প্র ০১ইচ্ছা করে bias যোগ করা একসময় পরিসংখ্যানে বিতর্কিত ছিল। Shrinkage কেন শেষ পর্যন্ত মূলধারায় এল?

বিশ শতকের মাঝামাঝি পর্যন্ত পরিসংখ্যানের একটি প্রধান আদর্শ ছিল unbiased estimator: গড়ে সঠিক উত্তর দেয় এমন অনুমান। Gauss–Markov উপপাদ্য বলে, linear unbiased estimator-দের মধ্যে least squares-এর variance সবচেয়ে কম। তাহলে কেউ ইচ্ছা করে bias যোগ করবে কেন? প্রথম বড় ধাক্কা আসে Charles Stein-এর ১৯৫৬ সালের ফলাফল থেকে, আর ১৯৬১ সালে William James ও Stein-এর একটি সুনির্দিষ্ট estimator থেকে: তিন বা তার বেশি গড় একসঙ্গে অনুমান করলে, প্রতিটি নমুনা-গড়কে একটি সাধারণ বিন্দুর দিকে একটু টেনে আনলে মোট বর্গ-ভুল সবসময় কমে, আসল গড় যা-ই হোক। একে Stein-এর paradox বলা হয়, কারণ তিনটি সম্পূর্ণ আলাদা বিষয়ের গড়কেও (ধরুন চালের দাম, ক্রিকেটের গড় রান আর বৃষ্টিপাত) একসঙ্গে টানলে লাভ হয়।

মূল শিক্ষা: মোট ভুল = bias² + variance, আর variance অনেক বড় হলে সামান্য bias দিয়ে তা বড় পরিমাণে কমানো লাভজনক। Hoerl আর Kennard ১৯৭০ সালে ridge-এর জন্য একই ধরনের যুক্তি দেন: যেকোনো ডেটায় এমন একটি λ > ০ আছে, যেখানে ridge-এর প্রত্যাশিত বর্গ-ভুল least squares-এর চেয়ে কম। সমস্যা হলো সেই λ আসল w-এর ওপর নির্ভর করে, যা আমরা জানি না। এই ফাঁকটি পূরণ করে cross-validation (১৯৭০-এর দশকে Stone আর Geisser-এর কাজে তত্ত্বগত রূপ পায়) আর generalized cross-validation (Golub, Heath আর Wahba, ১৯৭৯)।

কম্পিউটার সস্তা হওয়ার সঙ্গে সঙ্গে আরেকটি পরিবর্তন আসে: p বড় আর n তুলনায় ছোট ডেটা (জিনের মাপ, টেক্সটের শব্দ, ক্লিকের ইতিহাস) সাধারণ হয়ে ওঠে। সেখানে least squares-এর একক সমাধানই থাকে না, তাই regularization ঐচ্ছিক নয়, অপরিহার্য। এই পর্বের অংশ ২-এর stepper-এ দেখেছেন, ছোট σ-র দিকে noise ছয় গুণ ফুলে ওঠে; সেটি কাটার জন্য সামান্য bias দেওয়াই যুক্তিসঙ্গত।

তবু বিতর্ক পুরো শেষ হয়নি। Coefficient-এর অর্থ ব্যাখ্যা করতে হলে (যেমন নীতিনির্ধারণে) shrinkage-এর bias সমস্যা তৈরি করে, আর regularize করা coefficient-এর confidence interval সাধারণ সূত্রে বৈধ নয়।

মূল উপলব্ধি: unbiased হওয়া লক্ষ্য নয়, নতুন ডেটায় কম ভুলই লক্ষ্য; কিন্তু ব্যাখ্যার কাজে shrinkage-এর দাম মনে রাখতে হয়।

প্র ০২Lasso দিয়ে বাছা feature-কে কি “গুরুত্বপূর্ণ feature” বলে রিপোর্ট করা যায়?

সতর্কতার সঙ্গে, আর অনেক সময় না। Robert Tibshirani ১৯৯৬ সালে lasso প্রস্তাব করেন, আর একই সময়ে signal processing-এ Chen, Donoho আর Saunders-এর basis pursuit একই L1 ধারণা ব্যবহার করে। ২০০৬ সালের দিকে Candès, Romberg ও Tao এবং Donoho-র compressed sensing-এর কাজ দেখায়, কিছু শর্তে L1 দিয়ে অল্প মাপ থেকেই একটি sparse সংকেত হুবহু উদ্ধার করা যায়। এই সাফল্য L1-কে অত্যন্ত জনপ্রিয় করে, কিন্তু শর্তগুলো প্রায়ই ভুলে যাওয়া হয়।

শর্তের মূল কথা: সত্যিকারের কাজের feature-গুলো বাকিদের সঙ্গে খুব বেশি সম্পর্কিত হলে চলবে না। Zhao আর Yu ২০০৬ সালে এর একটি রূপ দেন, “irrepresentable condition”. বাস্তবের ডেটায় একসঙ্গে চলা feature প্রায় সবসময় থাকে: “মাসিক খরচ” আর “অর্ডারের সংখ্যা”, “দূরত্ব” আর “ডেলিভারির খরচ”. অংশ ৮-এর run-এ একটি bootstrap নমুনায় lasso তিনটি প্রায়-একই কলামের দুটিকে ঠিক শূন্য করেছে আর তৃতীয়টিকে ২.৪২ দিয়েছে; অন্য নমুনায় অন্য রকম। একজন বিশ্লেষক যদি শুধু একটি run দেখে বলেন “শুধু কাটানো মিনিট গুরুত্বপূর্ণ”, সেটি ডেটার নয়, দৈবের গল্প।

আরও দুটি সমস্যা। প্রথম, lasso-র অশূন্য coefficient-গুলোও শূন্যের দিকে টানা (অংশ ৪-এর run-এ আসল ৩ থেকে ২.১৩); এজন্য relaxed lasso (Meinshausen, ২০০৭) বাছা feature-এ আবার কম শাস্তিতে fit করে। দ্বিতীয়, বাছাইয়ের পর একই ডেটায় p-value বা confidence interval হিসাব করলে তা অতিরিক্ত আশাবাদী, কারণ বাছাই নিজেই ডেটা দেখেছে; এই “post-selection inference” আলাদা গবেষণার বিষয়।

ভালো অভ্যাস: বাছাইয়ের স্থিরতা মাপুন। Meinshausen আর Bühlmann-এর ২০১০ সালের stability selection অনেক subsample-এ lasso চালিয়ে দেখে কোন feature কত শতাংশ সময়ে বাছাই হয়। একসঙ্গে চলা দলকে দল হিসেবে রিপোর্ট করুন (“আগ্রহের তিনটি মাপ”), আর কারণগত দাবি করবেন না; সে কাজের জন্য পর্ব ৬৪ (Causal inference-এর পরিচয়) আলাদা কাঠামো দেয়।

মূল উপলব্ধি: lasso ভালো অনুমানের হাতিয়ার; feature-এর গুরুত্ব বা কারণ বলতে হলে স্থিরতা আর আলাদা প্রমাণ দরকার।

প্র ০৩λ বাছাইয়ে Bayesian আর frequentist দৃষ্টিভঙ্গির মধ্যে মতভেদ কোথায়, আর ব্যবহারিক কাজে কোনটি নেবেন?

অংশ ৬-এ দেখেছেন, ridge-এর λ = σ²/τ²: noise-এর variance ভাগ prior-এর variance. Bayesian দৃষ্টিতে τ একটি বিশ্বাস, যা ডেটা দেখার আগে ঠিক করা যায়, বা ডেটা থেকেই শেখা যায়। দ্বিতীয় পথকে বলে empirical Bayes বা evidence maximization: marginal likelihood p(y | τ, σ), অর্থাৎ w-কে সমাকলন করে বাদ দিলে যা থাকে, তা সর্বোচ্চ করে τ আর σ বাছা। scikit-learn-এর BayesianRidge এই পথেই চলে (David MacKay-এর ১৯৯০-এর দশকের কাজের ধারায়)। আরও পূর্ণ Bayesian পথ হলো τ-এর ওপরও prior বসিয়ে সবকিছু একসঙ্গে posterior-এ নেওয়া।

Frequentist দৃষ্টিতে λ শুধু একটি tuning knob, যার মানদণ্ড নতুন ডেটায় অনুমানের ভুল। তাই cross-validation দিয়ে সরাসরি সেই ভুল মেপে λ বাছা হয়। CV কোনো মডেল-অনুমানের ওপর ভর করে না; noise Gaussian না হলেও কাজ করে। বিনিময়ে এটি অনেকবার fit চালায়, আর ছোট ডেটায় CV-র অনুমান নিজেই noisy.

মতভেদের জায়গা মূলত তিনটি। প্রথম, অনুমান না অনিশ্চয়তা: Bayesian পথ শুধু λ নয়, coefficient আর অনুমানের অনিশ্চয়তাও দেয়, যা CV দেয় না। দ্বিতীয়, ভুল model: noise বা prior-এর অনুমান ভুল হলে evidence-ভিত্তিক বাছাই ভুল দিকে যেতে পারে, যেখানে CV সরাসরি লক্ষ্যটি মাপে। তৃতীয়, দর্শন: অনেক frequentist পরিসংখ্যানবিদ prior-কে বিষয়ভিত্তিক বলে সন্দেহ করেন, আর অনেক Bayesian বলেন CV-ও লুকানো অনুমান নেয় (যেমন fold-গুলো স্বাধীন)। গবেষণায় দেখা গেছে দুটি পথ অনেক সময় কাছাকাছি λ দেয়, কিন্তু সবসময় নয়।

ব্যবহারিক পরামর্শ: অনুমানের নির্ভুলতাই মূল লক্ষ্য হলে CV (বা RidgeCV-এর দ্রুত leave-one-out) নিন; অনিশ্চয়তার পরিসর লাগলে Bayesian পথ বা পর্ব ৫৮ (Conformal prediction)-এর মতো পদ্ধতি দেখুন। ছোট ডেটায় দুটি চালিয়ে মিলিয়ে নেওয়াও খারাপ নয়।

মূল উপলব্ধি: একই λ-কে কেউ বিশ্বাসের মাপ, কেউ tuning knob বলে দেখেন; কোন প্রশ্নের উত্তর চাইছেন, সেটিই ঠিক করে কোন পথ।

প্র ০৪বড় neural network-এ শাস্তি প্রায় না দিলেও overfitting কম হয় কেন? Implicit regularization নিয়ে আমরা কী জানি আর কী জানি না?

Linear least squares-এ উত্তরটি পরিষ্কার, আর এই পর্বে নিজে দেখেছেন। শূন্য থেকে শুরু করা gradient descent প্রতিটি SVD দিক আলাদা গতিতে শেখে: বড় σ-র দিক আগে, ছোট σ-র দিক দেরিতে। আগে থামলে ফল ridge-এর কাছাকাছি (অংশ ৭-এর run-এ t = ১০০-এ পার্থক্য least squares-এর দৈর্ঘ্যের ২.৫%). আরও একটি ফল: p > n হলে, অর্থাৎ অসংখ্য w সমান ভালো ফিট দেয়, শূন্য থেকে শুরু করা GD শেষ পর্যন্ত minimum-norm সমাধানে পৌঁছায়, যা পর্ব ০৯-এর pseudo-inverse-এর উত্তর। অর্থাৎ শাস্তি ছাড়াই optimizer নিজে একটি “ছোট” সমাধান বেছে নেয়। Alnur Ali, Zico Kolter আর Ryan Tibshirani ২০১৯ সালে দেখান যে least squares-এ continuous-time gradient flow-এর ঝুঁকি কিছু শর্তে ridge-এর ঝুঁকির একটি ছোট ধ্রুবক গুণের মধ্যে থাকে।

Neural network-এ ছবিটি অনেক জটিল। Zhang ও সহকর্মীদের ২০১৭ সালের একটি পরিচিত পরীক্ষায় দেখা যায়, বড় network এলোমেলো label-ও পুরো মুখস্থ করতে পারে, তবু আসল label-এ ভালো generalize করে। অর্থাৎ শুধু parameter-এর সংখ্যা বা hypothesis class-এর আকার দিয়ে ব্যাখ্যা হয় না (পর্ব ০৩-এর VC-এর যুক্তি এখানে দুর্বল)। প্রস্তাবিত ব্যাখ্যাগুলোর মধ্যে আছে SGD-এর noise “চওড়া” minimum পছন্দ করে, initialization ও architecture-এর নিজস্ব ঝোঁক, আর double descent-এর মতো ঘটনা। এদের কোনোটিই সর্বজনস্বীকৃত পূর্ণ তত্ত্ব নয়; গবেষণা চলছে, আর অনেক দাবি নির্দিষ্ট পরীক্ষার বাইরে টেকেনি।

ব্যবহারিক দিক থেকে deep learning-এ regularization সাধারণত অনেক রূপের মিশ্রণ: weight decay (L2-এর মতো; Adam-এর সঙ্গে এর সূক্ষ্ম পার্থক্য পর্ব ১৪-এ আসছে), early stopping, data augmentation, dropout. কোনটি কতটা কাজ করছে, তা আলাদা করে মাপা কঠিন, আর validation curve-ই শেষ বিচারক।

এই পর্বের linear উদাহরণের মূল্য তাই দুটি: এটি একটি জায়গা যেখানে implicit regularization হুবহু হিসাব করা যায়, আর এটি সতর্ক করে যে “optimizer কীভাবে চলে” তাও model-এর অংশ।

মূল উপলব্ধি: linear model-এ early stopping আর GD-এর minimum-norm ঝোঁক প্রমাণিত; বড় network-এ সেগুলো যুক্তিসঙ্গত অনুমান, পূর্ণ ব্যাখ্যা এখনো খোলা প্রশ্ন।

প্র ০৫Ridge, lasso আর elastic net-এর মধ্যে বাস্তব কাজে কীভাবে বাছবেন, আর regularization কখন যথেষ্ট নয়?

শুরুর প্রশ্ন: লক্ষ্য কি শুধু অনুমান, নাকি sparse ও পড়া যায় এমন model? শুধু অনুমান হলে, আর অনেক ছোট ছোট প্রভাবের feature থাকলে (যেমন অনেক জনমিতিক তথ্য, প্রতিটির সামান্য প্রভাব), ridge প্রায়ই সবচেয়ে ভালো আর সবচেয়ে স্থির। তত্ত্বেও এর যুক্তি আছে: সংকেত অনেক দিকে ছড়ানো হলে সবগুলোকে একটু ছোট করাই ভালো, কাউকে শূন্য করা নয়। Lasso ভালো কাজ করে যখন সত্যিই অল্প কয়েকটি feature-এর বড় প্রভাব আর বাকিরা প্রায় শূন্য, আর feature-গুলো খুব বেশি সম্পর্কিত নয়। Elastic net মাঝামাঝি: একসঙ্গে চলা দল থাকলে, বা p অনেক বড় হলে। অংশ ৮-এর run-এ CV elastic net-এর জন্য l1_ratio = ০.৯ বেছেছে, আর দুই model-এর CV ভুল প্রায় সমান (১.২২৯ বনাম ১.২০৮); অনুমানে তফাত কম, বাছাইয়ের স্থিরতায় তফাত বেশি।

বাছাইয়ের সবচেয়ে সৎ পথ হলো তিনটিকেই একই CV কাঠামোয় চালানো (ElasticNetCV-তে l1_ratio-র তালিকায় ০-এর কাছের মান থেকে ১ পর্যন্ত), কয়েকটি seed বা fold-এ তুলনা, আর একটি সরল baseline-এর সঙ্গে মেলানো। ১-standard-error নিয়মে সরলতর model বেছে নেওয়া বিবেচনা করুন, বিশেষ করে model যদি মানুষকে ব্যাখ্যা করতে হয়।

কখন regularization যথেষ্ট নয়? প্রথম, model-এর রূপই ভুল হলে: আসল সম্পর্ক non-linear বা interaction-নির্ভর হলে যত ভালো λ-ই নিন, linear model-এর bias থেকে যায়; তখন basis function (পর্ব ২৩) বা tree-ভিত্তিক model দরকার। দ্বিতীয়, ডেটার সমস্যায়: leakage, ভুল label বা distribution shift regularization সারায় না। তৃতীয়, outlier: বর্গ-ভুল একটি বড় ভুলের সারিকে অনেক ওজন দেয়, শাস্তি তা বদলায় না; তখন পর্ব ২৪ (Robust আর quantile regression)-এর loss লাগে। চতুর্থ, ব্যাখ্যার প্রশ্ন: shrink করা coefficient কারণগত প্রভাবের অনুমান নয়।

শেষে একটি ব্যবহারিক কথা: standardization ছাড়া ridge বা lasso চালানো প্রায় সবসময় ভুল, কারণ শাস্তি এককের ওপর নির্ভর করে; আর one-hot কলামগুলোর ক্ষেত্রে কীভাবে scale করবেন, তা নিয়ে প্রচলিত মতভেদ আছে, তাই সিদ্ধান্তটি লিখে রাখুন।

মূল উপলব্ধি: শাস্তির ধরন বাছা হয় সংকেতের আকৃতি আর লক্ষ্য দেখে, আর তা CV-তে যাচাই হয়; model-এর ভুল রূপ বা খারাপ ডেটা কোনো শাস্তি সারায় না।

অনুশীলন

হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। চাইলে অংশ ৩ আর ৪-এর ব্লক বদলে যাচাই করুন।

  1. পূর্ণ numerical: (ক) Orthonormal design-এ least squares-এর তিনটি coefficient ŵ = (২.৪, −০.৭, ১.১). λ = ১-এ lasso (½‖w − ŵ‖² + λ‖w‖₁) আর ridge (½‖w − ŵ‖² + (λ/2)‖w‖²)-এর উত্তর বের করুন। (খ) একটি center করা X-এর σ² = (১৬, ৪, ১). λ = ৪-এ ridge-এর shrink factor আর effective degrees of freedom কত?

    (ক) Lasso: S(২.৪, ১) = ১.৪; S(−০.৭, ১) = ০, কারণ |−০.৭| ≤ ১; S(১.১, ১) = ০.১. উত্তর (১.৪, ০, ০.১). Ridge: ŵ/(১ + ১) = (১.২, −০.৩৫, ০.৫৫), কোনোটি শূন্য নয়। লক্ষ করুন, lasso বড় coefficient-কে ridge-এর চেয়ে কম ছোট করেছে (১.৪ বনাম ১.২), কিন্তু ছোটগুলোকে মুছে দিয়েছে। (খ) f = σ²/(σ² + λ): ১৬/২০ = ০.৮, ৪/৮ = ০.৫, ১/৫ = ০.২. df = ০.৮ + ০.৫ + ০.২ = ১.৫, অর্থাৎ তিন parameter-এর model দেড়টির মতো নমনীয়।

  2. সমালোচনা ও নকশা: বাজারঘরের একজন বিশ্লেষক (বা একটি AI assistant) এই পদ্ধতিতে কাজ করেছেন: পুরো ডেটায় StandardScaler চালানো, পুরো ডেটায় LassoCV দিয়ে feature বাছা, বাছা ৬টি feature নিয়ে train/test ভাগ, তারপর test set-এ পাঁচটি alpha চেষ্টা করে সেরাটির test R² রিপোর্ট করা, আর লেখা “বাকি feature-গুলোর বিক্রিতে কোনো প্রভাব নেই”. কী কী সমস্যা, আর সঠিক নকশা কী?

    সমস্যা: (১) Scaler আর feature বাছাই পুরো ডেটায়, তাই test-এর তথ্য train-এ ঢুকেছে (leakage). (২) Test set-এ alpha বাছা মানে test আর নিরপেক্ষ নয়; রিপোর্ট করা R² আশাবাদী। (৩) Lasso-র শূন্য coefficient থেকে “প্রভাব নেই” বলা ভুল: একসঙ্গে চলা feature-এর একটি বাদ পড়তে পারে শুধু তার সঙ্গী থাকার কারণে, আর এটি কারণগত দাবিও নয়। (৪) একটিমাত্র ভাগ, কোনো ছড়ানো বা baseline নেই। সঠিক নকশা: শুরুতেই test set আলাদা করে তালাবন্ধ; train-এ Pipeline([StandardScaler(), ElasticNetCV(...)]) বা LassoCV, cv-তে shuffle আর random_state; alpha আর l1_ratio CV-তে বাছা (প্রয়োজনে one-standard-error নিয়মে); OLS আর গড়-অনুমানের baseline-এর সঙ্গে তুলনা, কয়েকটি seed-এ; feature বাছাইয়ের স্থিরতা bootstrap-এ মাপা; শেষে test set-এ একবার মাপা; আর রিপোর্টে “এই feature-গুলো অনুমানে সাহায্য করে, কারণগত প্রভাব যাচাই করা হয়নি” লেখা।

  3. আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড কী ছাপবে।
    import numpy as np
    def soft(z, a): return np.sign(z) * np.maximum(np.abs(z) - a, 0.0)
    z = np.array([3.0, -0.5, -2.0, 1.0])
    print(soft(z, 1.0))
    print(z / (1 + 1.0))
    [ 2. -0. -1.  0.]
    [ 1.5  -0.25 -1.    0.5 ]

    প্রথম লাইন soft-thresholding: |৩| − ১ = ২; |−০.৫| < ১ তাই শূন্য; −২ থেকে −১; |১| − ১ = ০. দ্বিতীয় ঘরে “-0.” ছাপা হয়, কারণ sign(−০.৫) = −১ আর −১ × ০.০ = −০.০; IEEE float-এ ঋণাত্মক শূন্য আলাদা করে ছাপা হয়, যদিও −০.০ == ০.০ সত্য। চতুর্থ ঘরটি দেখায় |z| = λ হলেও উত্তর শূন্য। দ্বিতীয় লাইন ridge-এর orthonormal রূপ, z/(১ + λ): সব ঘর অর্ধেক, কোনোটি শূন্য নয়। NumPy কলাম মেলাতে বাড়তি ফাঁকা জায়গা দেয়।

  4. Early stopping-এর হিসাব: শূন্য থেকে শুরু করা gradient descent-এ একটি দিকের জন্য ησ² = ০.১, আরেকটির জন্য ησ² = ০.০১. t = ১০ ধাপ পরে প্রতিটির GD গুণক 1 − (1 − ησ²)t বের করুন, তারপর λ = 1/(ηt) ধরে ridge-এর গুণক σ²/(σ² + λ) বের করুন। কোন দিকে মিল বেশি, আর কেন?

    λ = 1/(ηt) বসালে ridge-এর গুণক = ησ²t/(১ + ησ²t). দিক ১ (ησ²t = ১): GD 1 − ০.৯১০ = ১ − ০.৩৪৮৭ = ০.৬৫১; ridge ১/২ = ০.৫. দিক ২ (ησ²t = ০.১): GD 1 − ০.৯৯১০ = ০.০৯৬; ridge ০.১/১.১ = ০.০৯১. দিক ২-এ মিল অনেক বেশি, কারণ ησ²t ছোট হলে দুটিই প্রায় ησ²t; x = ησ²t এক-এর কাছে এলে 1 − e−x আর x/(1 + x) আলাদা হয়ে যায়। এজন্যই “early stopping ≈ ridge” কথাটি আনুমানিক, আর GD বড় σ-র দিক ridge-এর চেয়ে দ্রুত পুরো শিখে ফেলে।

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

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

ক্রমLasso-র coordinate descent-এ একটি coefficient wj হালনাগাদের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. বাকি সব coefficient স্থির রেখে j বাদে residual r(j) = r + xjwj বের করা
  2. ρj = xjᵀr(j)/n হিসাব করা
  3. Soft-thresholding: wj ← S(ρj, α)/zj
  4. নতুন wj দিয়ে residual হালনাগাদ: r = r(j) − xjwj
  5. সব j শেষে সবচেয়ে বড় বদল সহনসীমার নিচে কি না দেখা

একটি center করা X-এর singular value σ = (৪, ১). Ridge-এ λ = ১ হলে দুটি SVD দিকের shrink factor σ²/(σ² + λ) কত?

  • ০.৮ আর ০.৫
  • ০.৯৪১ আর ০.৫
  • ০.০৫৯ আর ০.৫
  • দুটিই ০.৫

½(w − z)² + λ|w| কমানো হচ্ছে, যেখানে z = −১.২ আর λ = ০.৫. সর্বনিম্ন বিন্দু w* কত?

  • ০.৭
  • ০
  • −০.৭
  • −০.৮

Linear regression-এ noise N(০, σ²) আর প্রতিটি coefficient-এ স্বাধীন prior N(০, τ²) ধরা হলো। MAP অনুমান কোন সমস্যার সমাধান?

  • Ridge, λ = σ²/τ²
  • Lasso, λ = σ²/τ²
  • Ridge, λ = τ²/σ²
  • শাস্তিহীন least squares

scikit-learn-এর Lasso কমায় (1/2n)‖y − Xw‖² + α‖w‖₁. কোন α-র ওপরে সব coefficient ঠিক শূন্য হয় (center করা X আর y)?

  • α > ১, সব ডেটায়
  • α > ‖y‖²/n
  • α > maxj |xjᵀy|
  • α > maxj |xjᵀy|/n

শূন্য থেকে শুরু করে least squares-এ gradient descent (ধাপ η) t ধাপ পরে থামানো হলো। Ridge-এর কোন λ-এর সঙ্গে এটি মোটামুটি মেলে, আর মিল কতটা?

  • λ ≈ ηt, হুবহু মেলে
  • কোনো মিল নেই, early stopping ভিন্ন জাতের
  • λ ≈ 1/(ηt), আনুমানিক মিল
  • λ = ০, যেকোনো t-এ

তিনটি feature-এর পরস্পর সম্পর্ক প্রায় ০.৯৯, আর তিনটিই একই অদৃশ্য কারণ থেকে আসে। Lasso আর elastic net-এর সাধারণ আচরণ কোনটি?

  • Lasso তিনটিকে সমান ভাগ দেয়, elastic net একটিকে বাছে
  • Lasso প্রায়ই একটি বা দুটি রেখে বাকিদের শূন্য করে এবং বাছাই অস্থির; elastic net দলটিকে কাছাকাছি মানে একসঙ্গে রাখে
  • দুটিই সব feature অশূন্য রাখে, কারণ সম্পর্ক বেশি
  • Standardize করলে দুটির আচরণ হুবহু এক হয়ে যায়

শব্দকোষ

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

মনে রাখুন

  1. কেন: ছোট singular value-র দিকে least squares-এর variance σε²/σj²; regularization সামান্য bias দিয়ে এই ফোলা আটকায়।
  2. Ridge: wλ = ∑ vj·σj/(σj² + λ)·ujᵀy; shrink factor σj²/(σj² + λ), আর effective df = তাদের যোগফল = tr(Hλ).
  3. Lasso: subgradient থেকে S(z, λ) = sign(z)·max(|z| − λ, ০); coordinate descent-এ wj ← S(ρj, α)/zj; αmax = max|xjᵀy|/n-এর ওপরে সব শূন্য।
  4. জ্যামিতি আর prior: শাস্তির রূপ আর সীমার রূপ এক, λ = Lagrange multiplier; L1 বলের কোণ শূন্য দেয়। Gaussian prior → ridge (λ = σ²/τ²), Laplace prior → lasso.
  5. Early stopping আর বাছাই: GD-এর গুণক 1 − (1 − ησ²)t ≈ ridge, λ ≈ 1/(ηt), আনুমানিক; একসঙ্গে চলা feature-এ elastic net; λ বাছাই pipeline-এর ভেতরে CV-তে, test শেষে একবার।

পরের পর্বে: এই পর্বে শাস্তিকে prior হিসেবে পড়েছি। পর্ব ১৮ (Maximum likelihood আর MAP — model fit করার মূলনীতি) দিয়ে নতুন মডিউল শুরু: likelihood কী, MLE কেন least squares আর log loss দেয়, আর MAP কখন MLE-এর চেয়ে ভালো।

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

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