এই পর্বে যা শিখবেন
- চারটি অর্ডারে squared loss-এর একটি boosting ধাপ হাতে করা, আর নিজে লেখা regression boosting-কে
GradientBoostingRegressor-এর সঙ্গে হুবহু মেলানো - Gradient boosting-কে functional gradient descent হিসেবে বোঝা, আর যেকোনো loss থেকে pseudo-residual বের করা
- Log loss-এ y − p আর Newton পাতার মান হাতে হিসাব করা, তারপর নিজে লেখা classifier মেলানো
- learning_rate, গাছের সংখ্যা, subsample আর max_depth-এর প্রভাব
staged_predict-এর বক্ররেখায় দেখা - Early stopping চালানো, আর outlier-ভরা ডেটায় squared, absolute, Huber ও quantile loss বেছে নেওয়া
এই মডিউল চলছে গাছ আর ensemble নিয়ে। পর্ব ৩২ (Decision tree গভীরে — CART, impurity, pruning আর missing value) একটি গাছ কীভাবে ভাগ বাছে তা দেখায়, পর্ব ৩৩ (Bagging আর random forest — variance কমানো আর OOB error) অনেক গভীর গাছের গড় নেয়, আর পর্ব ৩৪ (Boosting-এর ধারণা — AdaBoost আর weak learner) ভুল করা সারির ওজন বাড়িয়ে একটির পর একটি গাছ শেখায়। এই পর্ব boosting-এর সবচেয়ে প্রচলিত রূপ নিয়ে। AI-এর ভিত্তি কোর্সের পর্ব ৬৬-এ এক নজর দেখেছেন: ছোট গাছ, একটির পর একটি, প্রতিটি আগের ভুল শোধরায়। এখানে সেই “ভুল শোধরানো”-র গণিত, হাতে আর কোডে। চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python, scikit-learn 1.8.0); প্রথমবার scikit-learn নামতে প্রায় দশ সেকেন্ড লাগে।
১ · ভুল থেকে শেখা — boosting-এর মূল ভাবনা
দর্জি জামা বানান। প্রথমে মাপের গড় আন্দাজে একটি কাঠামো। তারপর ট্রায়াল: কাঁধে ঢিলা, হাতা লম্বা। তিনি পুরো জামা নতুন করে বানান না; শুধু যেখানে ভুল, সেখানে একটু কাটেন বা সেলাই করেন। আবার ট্রায়াল, আবার ছোট সংশোধন। আর অভিজ্ঞ দর্জি একবারে বেশি কাটেন না, কারণ বেশি কাটা কাপড় ফেরত আনা যায় না। Gradient boosting ঠিক এই দর্জি: একটি সরল শুরু, তারপর বারবার শুধু বাকি ভুলের ওপর ছোট সংশোধন।
Boosting-এর model একটি যোগফল:
FM(x) = F0 + ν·h1(x) + ν·h2(x) + … + ν·hM(x)
এখানে F0 একটি ধ্রুবক শুরু (squared loss-এ train-এর গড়), প্রতিটি hm একটি ছোট regression tree, M গাছের সংখ্যা, আর ν (নিউ, scikit-learn-এ learning_rate) একটি ছোট সংখ্যা, যেমন ০.১. গাছগুলো একা দুর্বল, তাই এদের বলে weak learner. সবচেয়ে ছোট গাছ, একটি মাত্র প্রশ্নের, হলো decision stump.
প্রতিটি নতুন গাছ শেখে আগের যোগফলের ভুল, অর্থাৎ residual r = y − Fm−1(x). Random forest-এর সঙ্গে পার্থক্যটি মৌলিক: সেখানে গাছগুলো একে অপরকে চেনে না, স্বাধীনভাবে শেখে আর গড় হয়, ফলে মূলত variance কমে। Boosting-এ প্রতিটি গাছ আগেরগুলোর ওপর নির্ভর করে, ফলে মূলত bias কমে (পর্ব ০৩-এর bias–variance-এর ভাষায়)। তাই random forest-এ গভীর গাছ, boosting-এ অগভীর গাছ।
নিচে চারটি কাল্পনিক অর্ডারে পুরো একটি ধাপ। x হলো গুদাম থেকে দূরত্ব (কিমি), y হলো পৌঁছাতে কত ঘণ্টা লেগেছে। Squared loss, stump, ν = ০.১.
- শুরু। Squared loss-এ সেরা ধ্রুবক অনুমান হলো গড়, ২০.৫ ঘণ্টা। সবার জন্য একই সংখ্যা, তাই MSE বড়: ৯৮.৭৫.
- Residual. প্রতিটি অর্ডারের ভুল y − F0. কাছের অর্ডারে অনুমান বেশি (ঋণাত্মক r), দূরের D-তে অনেক কম (r = ১৫.৫)। এই সংখ্যাগুলোই loss ½(y − F)²-এর ঋণাত্মক gradient.
- ভাগ বাছাই। Stump residual-কে target ধরে শেখে। তিনটি সম্ভাব্য ভাগের মধ্যে squared error সবচেয়ে বেশি কমায় km < ১৬, যা D-কে আলাদা করে: লাভ ৩২০.৩.
- পাতার মান। প্রতিটি পাতা তার residual-এর গড় অনুমান করে: বাঁয়ে −৫.১৬৬৭, ডানে ১৫.৫. এটি একটি ছোট গাছ যা “কোথায় কতটা ভুল” শিখেছে।
- ছোট ধাপ। গাছের অনুমানের মাত্র ১০% যোগ হয়। MSE ৯৮.৭৫ থেকে ৮৩.৫৩. নতুন residual পরের গাছের target; এভাবে চলতে থাকে।
- ν = ১ হলে। এক গাছেই MSE ১৮.৬৭, অনেক দ্রুত। তবু ছোট ν কেন, তা অংশ ৬-এ। scikit-learn-এর প্রথম ধাপের অনুমান আমাদের হাতের হিসাবের সঙ্গে হুবহু মেলে (ডেস্কটপে যাচাই করা)।
২ · নিজে লেখা regression boosting — ১, ৫ আর ৫০টি গাছ
এবার একই ধারণা ৮০টি অর্ডারে। ডেটা কাল্পনিক, seed দিয়ে বানানো: পৌঁছানোর সময় দূরত্বের সঙ্গে বাঁকা হারে বাড়ে, আর ১৮ কিমির পরে হঠাৎ প্রায় দশ ঘণ্টা বেশি (ধরুন, শহরের বাইরের courier হাবে একটি বাড়তি থামা)। সঙ্গে প্রায় ৩ ঘণ্টার এলোমেলো গোলমাল। কোড scikit-learn-এর DecisionTreeRegressor-কে weak learner হিসেবে নেয়, কিন্তু boosting-এর লুপ নিজে লেখা। শেষে একই সেটিংয়ে GradientBoostingRegressor-এর অনুমানের সঙ্গে তুলনা।
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import GradientBoostingRegressor
rng = np.random.default_rng(35)
km = np.sort(rng.uniform(1, 30, 80)) # দূরত্ব (কিমি)
hours = 6 + 9 * np.log1p(km) + 10 * (km > 18) + rng.normal(0, 3, 80)
X, grid = km.reshape(-1, 1), np.linspace(1, 30, 300).reshape(-1, 1)
nu, M = 0.1, 50 # shrinkage, গাছের সংখ্যা
F = np.full(80, hours.mean()) # F0 = গড়
Fg = np.full(300, hours.mean())
print(f"F0 = mean = {hours.mean():.2f} h, MSE {hours.var():.2f}")
snaps = {}
for m in range(1, M + 1):
r = hours - F # residual = −gradient
tree = DecisionTreeRegressor(max_depth=2).fit(X, r)
F += nu * tree.predict(X)
Fg += nu * tree.predict(grid)
if m in (1, 5, 50):
snaps[m] = Fg.copy()
print(f"trees {m:2d}: train MSE {np.mean((hours - F) ** 2):6.2f}")
gbr = GradientBoostingRegressor(n_estimators=M, learning_rate=nu,
max_depth=2, random_state=0).fit(X, hours)
print("max |own - sklearn| on grid:", np.abs(Fg - gbr.predict(grid)).max())
fig, ax = plt.subplots(figsize=(7, 3.8))
ax.scatter(km, hours, s=12, c="gray", label="orders")
for m, ls in zip(snaps, [":", "--", "-"]):
ax.plot(grid, snaps[m], ls, lw=2, label=f"after {m} trees")
ax.set(xlabel="distance (km)", ylabel="delivery time (hours)",
title="Gradient boosting fit, learning_rate = 0.1")
ax.legend(fontsize=8)আসল আউটপুট (ব্রাউজারের Python), নিচে ছবি:
F0 = mean = 32.20 h, MSE 121.13
trees 1: train MSE 100.63
trees 5: train MSE 49.75
trees 50: train MSE 5.55
max |own - sklearn| on grid: 0.0
শুরুতে সবার অনুমান ৩২.২০ ঘণ্টা, MSE ১২১.১৩ (এটি y-এর variance-ই)। এক গাছে MSE মাত্র ১০০.৬৩: ν = ০.১ বলে প্রথম গাছ তার শেখার দশ ভাগের এক ভাগই যোগ করে। পাঁচ গাছে ৪৯.৭৫, পঞ্চাশে ৫.৫৫. ছবিতে এক গাছের রেখা প্রায় সমতল, একটি ছোট সিঁড়ি; পাঁচ গাছে সিঁড়ি দেখা যায় কিন্তু ঢাল তখনো কম; পঞ্চাশ গাছে রেখা বাঁকা বৃদ্ধি আর ১৮ কিমির লাফ দুটিই ধরেছে। প্রতিটি গাছ সিঁড়ির মতো (ধাপে ধাপে ধ্রুবক), তাই যোগফলও সিঁড়ি, শুধু অনেক ছোট ছোট ধাপে।
দুটি সতর্কতা। এক, গোলমালের variance প্রায় ৯ (sd ৩), অথচ train MSE ৫.৫৫; অর্থাৎ ৫০ গাছ ইতিমধ্যে কিছু গোলমালও মুখস্থ করছে। কতটি গাছ ঠিক, তা train নয়, আলাদা validation ডেটা বলবে (অংশ ৬)। দুই, নিজে লেখা লুপ আর scikit-learn-এর পার্থক্য হুবহু ০.০: একই শুরু (গড়), একই residual, একই গাছ, একই ν. scikit-learn-এর গাছ ডিফল্টে criterion="friedman_mse" নেয়, আমাদের গাছ সাধারণ squared error; এখানে দুটি একই ভাগ বেছেছে।
৩ · Residual কেন gradient — function space-এ gradient descent
পর্ব ১৩-এ gradient descent parameter θ-কে সরাত: θ ← θ − η∇L(θ). এখন একটু অন্যভাবে ভাবুন। n টি train সারিতে model-এর অনুমানগুলো একটি তালিকা: F = (F(x1), …, F(xn)). মোট loss L = ∑ ℓ(yi, F(xi)) এই n টি সংখ্যার ফাংশন। তাহলে অনুমানগুলোকেই সরাসরি gradient-এর উল্টো দিকে সরানো যায়:
F(xi) ← F(xi) − ν · ∂ℓ(yi, F)/∂F = F(xi) + ν · ri
এই ri = −∂ℓ/∂F হলো pseudo-residual. Squared loss ℓ = ½(y − F)² হলে ∂ℓ/∂F = −(y − F), তাই r = y − F, সাধারণ residual. এই কারণেই অংশ ১-এর “ভুল শেখা” আসলে gradient descent. একে বলে functional gradient descent: ধাপ নেওয়া হয় parameter-এ নয়, function-এর মানে।
কিন্তু শুধু train বিন্দুতে মান সরালে নতুন অর্ডারের জন্য কিছু শেখা হয় না। তাই gradient-এর তালিকাটিকে একটি গাছ দিয়ে আন্দাজ করা হয়: গাছ ri-কে xi থেকে অনুমান করতে শেখে, ফলে যেকোনো নতুন x-এও “কোন দিকে সরতে হবে” তা বলতে পারে। গাছটি gradient-এর একটি মসৃণ, সাধারণীকরণযোগ্য কপি।
দুই অর্ডারের অনুমান-তলে gradient boosting
অক্ষ দুটি: অর্ডার A-র অনুমান আর অর্ডার D-র অনুমান
সাধারণ algorithm (Friedman ২০০১)
- শুরু: F0 = argminc ∑ ℓ(yi, c), সেরা একটি ধ্রুবক (squared-এ গড়, absolute-এ median, log loss-এ log-odds).
- m = ১ থেকে M পর্যন্ত: প্রতিটি সারিতে pseudo-residual ri = −∂ℓ(yi, F)/∂F, F = Fm−1(xi)-এ।
- (xi, ri)-এ squared error দিয়ে একটি ছোট regression tree শেখানো; এটি পাতা R1, …, RJ দেয়।
- প্রতিটি পাতায় line search: γj = argminγ ∑i∈Rj ℓ(yi, Fm−1(xi) + γ).
- Fm(x) = Fm−1(x) + ν·γj(x), যেখানে j(x) হলো x যে পাতায় পড়ে।
ধাপ ৩ সবসময় squared error দিয়ে গাছ বানায়, loss যা-ই হোক; loss ঢোকে শুধু ধাপ ২ (কোন দিকে) আর ধাপ ৪ (কতটা)। এটাই পদ্ধতিটির শক্তি: যেকোনো differentiable loss-এর জন্য একই যন্ত্র। নিচের টেবিলে পর্ব ০২ আর পর্ব ২৪-এর চেনা loss-গুলোর pseudo-residual (e = y − F):
| Loss (scikit-learn নাম) | ℓ(y, F) | pseudo-residual | F0 আর পাতার মান |
|---|---|---|---|
squared_error | ½e² | e | গড় |
absolute_error | |e| | sign(e) | median |
huber (Huber) | |e| ≤ δ হলে ½e², নইলে δ(|e| − ½δ) | |e| ≤ δ হলে e, নইলে δ·sign(e) | median থেকে শুরু, পাতায় Huber-এর আনুমানিক ধাপ |
quantile (pinball, α) | e ≥ ০ হলে αe, নইলে (α − ১)e | e > ০ হলে α, নইলে α − ১ | α-quantile |
log_loss (classification) | −[y log p + (১ − y) log(১ − p)], p = σ(F) | y − p | log-odds; পাতায় Newton ধাপ |
Absolute loss-এ pseudo-residual শুধু ±১: ভুল কত বড়, তা দিক খোঁজায় গোনা হয় না, তাই outlier একা গাছকে টেনে নিতে পারে না। কিন্তু এক ধাপ কতটা, তা পাতার median ঠিক করে। Huber দুটির মাঝামাঝি: ছোট ভুলে squared-এর মতো, বড় ভুলে absolute-এর মতো। scikit-learn প্রতিটি ধাপে δ ঠিক করে চলতি residual-এর alpha-quantile (ডিফল্ট ০.৯) থেকে।
৪ · Classification — log-odds, y − p আর Newton ধাপ
বাজারঘর জানতে চায়, কোন গ্রাহক আর ফিরবে না (churn = ১)। এখানে F সম্ভাবনা নয়, log-odds: F = log(p / (১ − p)), আর p = σ(F) = ১ / (১ + e−F), যেখানে σ হলো sigmoid. কেন log-odds? কারণ F যেকোনো বাস্তব সংখ্যা হতে পারে, তাই গাছের অনুমান যোগ করলেও p সবসময় ০ থেকে ১-এর মধ্যে থাকে। AI-এর ভিত্তি কোর্সের পর্ব ৫৯-এ logistic regression-এ একই কৌশল দেখেছেন; সেখানে F ছিল একটি সরলরেখা, এখানে গাছের যোগফল।
Log loss ℓ = −[y log p + (১ − y) log(১ − p)]-এর F অনুযায়ী অন্তরজ খুব সুন্দর: ∂ℓ/∂F = p − y. তাই pseudo-residual r = y − p: যে গ্রাহক চলে গেছে (y = ১) অথচ model বলছে p = ০.৩, তার r = ০.৭; যে থেকে গেছে অথচ p = ০.৮, তার r = −০.৮.
গাছ r শেখার পর পাতার মান কী হবে? r-এর গড় নিলে একক ভুল হয়: r সম্ভাবনার এককে, কিন্তু যোগ হবে log-odds-এ। ধাপ ৪-এর line search-এর বন্ধ সূত্র নেই, তাই একটি Newton ধাপ নেওয়া হয় (পর্ব ১৫): gradient-কে দ্বিতীয় অন্তরজ (Hessian) দিয়ে ভাগ। Log loss-এর দ্বিতীয় অন্তরজ ∂²ℓ/∂F² = p(১ − p). তাই প্রতিটি পাতা j-তে:
γj = ∑i∈Rj (yi − pi) / ∑i∈Rj pi(১ − pi)
এই সূত্র Friedman ২০০১-এ আছে, আর scikit-learn-এর GradientBoostingClassifier ঠিক এটিই করে (তার source code-এর মন্তব্যে Friedman, Hastie আর Tibshirani-র ২০০০ সালের কাজের উল্লেখ)। চার গ্রাহকে হাতে করে দেখা যাক।
- শুরু। চারজনের তিনজন চলে গেছে, p̄ = ০.৭৫. শুরুর F0 সেই হারের log-odds, log ৩ ≈ ১.০৯৮৬. সবার p = ০.৭৫.
- Pseudo-residual. P থেকে গেছে (y = ০) অথচ p = ০.৭৫, তাই r = −০.৭৫. বাকিরা চলে গেছে, প্রত্যেকের r = ০.২৫. সবার p(১ − p) = ০.১৮৭৫.
- ভাগ। Stump r-এর ওপর squared error দিয়ে ভাগ খোঁজে: “শেষ অর্ডারের পর ৬.৫ দিনের কম” P-কে আলাদা করে, লাভ ০.৭৫০, সবচেয়ে বেশি।
- Newton পাতা। বাঁ পাতায় −০.৭৫ / ০.১৮৭৫ = −৪, ডানে ০.৭৫ / ০.৫৬২৫ = ১.৩৩৩৩. এগুলো log-odds-এর এককে ধাপ। r-এর গড় (−০.৭৫, ০.২৫) নিলে ধাপ অনেক ছোট আর ভুল এককে হতো।
- হালনাগাদ। ν = ০.১ দিয়ে যোগ: P-এর p ০.৬৬৭৯-এ নামল, বাকিদের ০.৭৭৪২-এ উঠল। Log loss ০.৫৬২৩ থেকে ০.৪৬৭৫. scikit-learn-এর এক গাছের model-এর পাতার মান হুবহু −৪ আর ১.৩৩৩৩ (ডেস্কটপে যাচাই করা)।
৫ · নিজে লেখা classifier — GradientBoostingClassifier-এর সঙ্গে মেলানো
৩০০ জন কাল্পনিক গ্রাহক, দুটি feature: শেষ অর্ডারের পর কত দিন, আর গত বছরে কতটি অর্ডার। Churn-এর আসল নিয়মে একটি interaction আছে: যাদের অর্ডার খুব কম, তাদের ক্ষেত্রে দিন বাড়লে ঝুঁকি আরও দ্রুত বাড়ে। লুপটি ঠিক অংশ ৪-এর stepper: p, r = y − p, r-এ গাছ, প্রতিটি পাতায় Newton মান, ν দিয়ে যোগ। গাছের পাতা খুঁজতে tree.apply(X), যা প্রতিটি সারির পাতার নম্বর দেয়।
import numpy as np
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import log_loss
rng = np.random.default_rng(7)
n = 300
days = rng.uniform(0, 90, n) # শেষ অর্ডারের পর কত দিন
orders = rng.poisson(4, n).astype(float) # গত বছরে অর্ডার
logit = -2 + 0.05 * days - 0.4 * orders + 0.02 * days * (orders < 2)
y = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int) # churn
X = np.column_stack([days, orders])
nu, M = 0.1, 30
F = np.full(n, np.log(y.mean() / (1 - y.mean()))) # F0 = log-odds
print(f"churn rate {y.mean():.3f}, F0 = {F[0]:.4f}")
for m in range(M):
p = 1 / (1 + np.exp(-F))
r = y - p # pseudo-residual
tree = DecisionTreeRegressor(max_depth=2).fit(X, r)
leaf = tree.apply(X)
step = np.zeros(n)
for L in np.unique(leaf): # প্রতিটি পাতায় Newton ধাপ
i = leaf == L
step[i] = r[i].sum() / (p[i] * (1 - p[i])).sum()
F += nu * step
if m == 0:
print("tree 1 leaf values:", np.round(np.unique(step), 4))
gbc = GradientBoostingClassifier(n_estimators=M, learning_rate=nu,
max_depth=2, random_state=0).fit(X, y)
print("max |own F - sklearn F|:", np.abs(F - gbc.decision_function(X)).max())
p = 1 / (1 + np.exp(-F))
print(f"train log loss: start {log_loss(y, np.full(n, y.mean())):.4f}"
f" own {log_loss(y, p):.4f}"
f" sklearn {log_loss(y, gbc.predict_proba(X)[:, 1]):.4f}")আসল আউটপুট (ব্রাউজারের Python):
churn rate 0.300, F0 = -0.8473
tree 1 leaf values: [-1.1565 -0.4762 0.5877 1.9902]
max |own F - sklearn F|: 4.440892098500626e-16
train log loss: start 0.6109 own 0.4306 sklearn 0.4306
৩০% গ্রাহক churn করেছে, তাই F0 = log(০.৩/০.৭) = −০.৮৪৭৩. প্রথম গাছের গভীরতা ২, চারটি পাতা, আর Newton মানগুলো −১.১৬ থেকে ১.৯৯: log-odds-এর এককে বেশ বড় ধাপ, কিন্তু ν = ০.১ সেগুলোর দশ ভাগের এক ভাগ নেয়। ৩০ গাছ পরে নিজে লেখা F আর decision_function-এর সবচেয়ে বড় পার্থক্য প্রায় ৪.৪ × ১০−১৬, অর্থাৎ শুধু floating-point-এর শেষ অঙ্কের তফাত। Train log loss ০.৬১০৯ (শুধু হার জানা model) থেকে ০.৪৩০৬. এটি train-এর মাপ; সত্যিকারের মান যাচাই আলাদা ডেটায়, আর boosting-এর সম্ভাবনা কতটা calibrated তা পর্ব ২৬-এর reliability diagram দিয়ে দেখতে হয়।
Multiclass-এ কী হয়? K টি শ্রেণি হলে প্রতিটি ধাপে K টি গাছ, প্রতিটি শ্রেণির Fk-এর জন্য একটি, আর softmax (পর্ব ২৫)। তাই ১০ শ্রেণি আর ২০০ ধাপ মানে ২,০০০ গাছ। scikit-learn-এর estimators_-এর আকার (n_estimators, K) এটিই দেখায়; দুই শ্রেণিতে K-এর জায়গায় একটি গাছ।
৬ · Shrinkage আর গাছের সংখ্যা — staged_predict দিয়ে দেখা
ν = ১ হলে অংশ ১-এর উদাহরণে এক গাছেই MSE ৯৮.৭৫ থেকে ১৮.৬৭. তাহলে ০.১ কেন? Friedman ২০০১-এর পরীক্ষায় ছোট ν (০.১ বা তার কম) প্রায় সবসময় ভালো test ফল দিয়েছিল, আর তিনি এর নাম দেন shrinkage. যুক্তিটি পর্ব ১৭-এর early stopping-এর মতো: ছোট ধাপে পথটি মসৃণ হয়, প্রতিটি গাছ একা বেশি কিছু ঠিক করে না, তাই গোলমাল মুখস্থ হতে বেশি সময় লাগে আর কোথায় থামবেন তা সূক্ষ্মভাবে বাছা যায়। দামটি সরল হিসাব:
- সরল মডেল। ধরে নিন প্রতিটি গাছ চলতি residual হুবহু শেখে (বাস্তবে আংশিক শেখে, তবু প্রবণতা একই)। তখন প্রতিটি ধাপে বাকি ভুল (১ − ν) গুণ হয়।
- ν = ১. এক গাছেই train-এর ভুল শূন্য, আসল সংকেত আর গোলমাল দুটোই একসঙ্গে মুখস্থ। থামার জায়গা বাছার কোনো সুযোগ নেই।
- ν = ০.৫. প্রতিবার অর্ধেক: চার গাছে ভুল ৬.২৫%, দশ শতাংশের নিচে।
- ν = ০.১. ০.৯২২ ≈ ০.০৯৮, তাই ২২টি গাছ লাগে। পথে অনেক মধ্যবর্তী model, যেকোনো একটিতে থামা যায়।
- ν = ০.০১. ৫০ গাছ পরেও ৬০% ভুল বাকি; ২৩০টি গাছ লাগে। খুব ছোট ν মানে অনেক বেশি সময়, লাভ প্রায়ই সামান্য।
- নিয়ম। ছোট ν-তে প্রয়োজনীয় গাছ প্রায় ২.৩/ν. তাই learning_rate আর n_estimators একসঙ্গে বাছতে হয়, আলাদা নয়; ν অর্ধেক করলে গাছ দ্বিগুণ করুন।
এবার আসল ডেটায়। ৯০০টি কাল্পনিক অর্ডার, চারটি feature: দূরত্ব, ওজন, বৃষ্টি হয়েছিল কি না, আর ঠিকানা ঢাকার ভেতরে কি না। নিয়মে একটি interaction আছে: বৃষ্টি শুধু ঢাকার বাইরে বেশি দেরি করায়। প্রথম ৬০০টি train, বাকি ৩০০টি validation; চূড়ান্ত test আলাদা তালাবন্ধ থাকবে বলে ধরে নিচ্ছি। staged_predict প্রতিটি গাছের পরের অনুমান একে একে দেয়, তাই একবার fit করেই ১ থেকে ৩০০ গাছের পুরো বক্ররেখা পাওয়া যায়। একই ব্লক পরের অংশের subsample আর max_depth-ও মাপে।
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import GradientBoostingRegressor as GBR
rng = np.random.default_rng(35)
n = 900
km, kg = rng.uniform(1, 30, n), rng.gamma(2, 1.5, n) # দূরত্ব, ওজন
rain, dhaka = rng.integers(0, 2, n), rng.integers(0, 2, n) # বৃষ্টি, ঢাকার ভেতরে
y = (6 + 9 * np.log1p(km) + 1.5 * kg + 6 * rain * (1 - dhaka)
+ 4 * rain * km / 30 + rng.normal(0, 3, n))
X = np.column_stack([km, kg, rain, dhaka])
Xtr, ytr, Xva, yva = X[:600], y[:600], X[600:], y[600:]
def curve(**kw):
m = GBR(n_estimators=300, random_state=0, **kw).fit(Xtr, ytr)
tr = [np.mean((ytr - p) ** 2) for p in m.staged_predict(Xtr)]
va = [np.mean((yva - p) ** 2) for p in m.staged_predict(Xva)]
return np.array(tr), np.array(va)
fig, ax = plt.subplots(figsize=(7, 3.8))
print("learning_rate best trees valid MSE (depth 3)")
for lr in [1.0, 0.3, 0.1, 0.03]:
tr, va = curve(learning_rate=lr, max_depth=3)
print(f"{lr:12.2f} {va.argmin() + 1:11d} {va.min():10.2f}")
ax.plot(va, label=f"valid, lr={lr}")
if lr == 0.1:
ax.plot(tr, "k:", label="train, lr=0.1")
ax.set(xlabel="number of trees", ylabel="MSE (hours^2)", ylim=(0, 30),
title="staged_predict: train vs validation error")
ax.legend(fontsize=8)
print("\nlr=0.1 variants best trees valid MSE")
for name, kw in [("depth 1 (stumps)", dict(max_depth=1)), ("depth 6", dict(max_depth=6)),
("depth 3, subsample 0.5", dict(max_depth=3, subsample=0.5))]:
tr, va = curve(learning_rate=0.1, **kw)
print(f"{name:22s} {va.argmin() + 1:10d} {va.min():10.2f}")আসল আউটপুট (ব্রাউজারের Python, একটি seed), নিচে ছবি:
learning_rate best trees valid MSE (depth 3)
1.00 9 15.02
0.30 14 11.08
0.10 53 11.20
0.03 191 11.27
lr=0.1 variants best trees valid MSE
depth 1 (stumps) 161 12.11
depth 6 22 12.74
depth 3, subsample 0.5 45 10.67
Learning rate: ν = ১-এ সেরা validation MSE ১৫.০২, মাত্র ৯ গাছে, আর তারপর দ্রুত খারাপ হয়। ν = ০.৩, ০.১ আর ০.০৩-এ সেরা প্রায় সমান (১১.০৮, ১১.২০, ১১.২৭), কিন্তু গাছ লাগে ১৪, ৫৩ আর ১৯১: stepper-এর “ν ছোট মানে বেশি গাছ” নিয়ম। গোলমালের variance ৯, তাই ১১-এর কাছে আসা মানে model প্রায় সব সংকেত ধরেছে। লক্ষ করুন, এই ডেটা আর এই seed-এ সবচেয়ে ছোট ν সেরা হয়নি; ছোট ν-এর সুবিধা হলো স্থিরতা, প্রতিবার জয় নয়।
ছবি: কালো বিন্দুরেখা (train, ν = ০.১) একটানা নামছে, প্রায় শূন্যের দিকে। কিন্তু validation-এর রেখা একটি তলায় পৌঁছে আবার ধীরে উঠছে: সেখানেই overfitting শুরু। ν = ১-এর রেখা খুব তাড়াতাড়ি তলা ছুঁয়ে উঁচুতে উঠে যায়; ν = ০.০৩-এর রেখা ধীরে নামে আর ৩০০ গাছেও প্রায় সমতল, তাই ভুল সংখ্যায় থামলেও ক্ষতি কম।
৭ · Subsample আর গাছের গভীরতা
Subsample. Friedman ২০০২ সালে (Computational Statistics & Data Analysis) একটি ছোট বদল প্রস্তাব করেন: প্রতিটি গাছ শেখার সময় train-এর একটি এলোমেলো অংশ নিন, প্রতিস্থাপন ছাড়া, যেমন অর্ধেক। নাম stochastic gradient boosting. দুটি লাভ: গাছগুলো একটু ভিন্ন হয় (bagging-এর মতো variance কমে), আর প্রতিটি গাছ অর্ধেক ডেটায় শেখে বলে দ্রুত। উপরের run-এ subsample=0.5 validation MSE ১১.২০ থেকে ১০.৬৭-এ নামিয়েছে, ৪৫ গাছে। একটি seed-এর ফল, তাই নিশ্চিত হতে কয়েকটি seed বা CV fold দেখতে হয়। subsample < ১ হলে বাদ পড়া সারি দিয়ে scikit-learn oob_improvement_ হিসাব করে, পর্ব ২১-এর out-of-bag ভাবনার মতো; তবে scikit-learn-এর নিজের উদাহরণেও এই আন্দাজ CV-এর চেয়ে কম নির্ভরযোগ্য দেখায়, তাই থামার জায়গা বাছতে validation বা CV-ই ভালো।
গভীরতা মানে interaction-এর মাত্রা। Stump (গভীরতা ১) একবারে একটি feature দেখে, তাই stump-এর যোগফল F(x) = f1(km) + f2(kg) + f3(rain) + f4(dhaka) আকারের একটি additive model: “বৃষ্টির প্রভাব ঠিকানার ওপর নির্ভর করে” এমন কিছু সে ধরতে পারে না। গভীরতা ২-এর গাছের একটি পথে দুটি feature থাকতে পারে, তাই দুই-feature-এর interaction; গভীরতা d মানে সর্বোচ্চ d টি feature-এর interaction. উপরের run-এ stump ১৬১ গাছেও ১২.১১-এ আটকে: এই ডেটার বৃষ্টি × ঢাকার বাইরে interaction সে ধরতে পারে না। আবার গভীরতা ৬ খুব দ্রুত (২২ গাছে) গোলমাল শেখে, সেরা ১২.৭৪. মাঝের গভীরতা ৩ সবচেয়ে ভালো। Hastie, Tibshirani আর Friedman-এর বই The Elements of Statistical Learning অভিজ্ঞতা থেকে ৪ থেকে ৮টি পাতার গাছের কথা বলে; scikit-learn-এর ডিফল্ট max_depth=3 (সর্বোচ্চ ৮টি পাতা) সেই সীমার মধ্যে।
bias কমায়
বেশি গাছ (একটি সীমা পর্যন্ত), বড় ν, গভীর গাছ। Validation-এর ভুল বেশি আর train-এর ভুলও বেশি হলে এদিকে যান।
variance কমায়
ছোট ν আর early stopping, subsample ০.৫–০.৮, min_samples_leaf বড়, অগভীর গাছ, max_features < সব feature. Train আর validation-এর ফাঁক বড় হলে এদিকে যান।
৮ · Early stopping আর loss বাছাই — outlier-ভরা ডেলিভারি
Early stopping. n_iter_no_change=10 দিলে scikit-learn train থেকে validation_fraction (ডিফল্ট ০.১) অংশ আলাদা রাখে, প্রতিটি গাছের পরে সেই অংশে loss মাপে, আর পরপর ১০টি ধাপে অন্তত tol (ডিফল্ট ১০−৪) উন্নতি না হলে থামে। কতটি গাছ আসলে বানানো হলো, তা থাকে n_estimators_-এ। এটি পর্ব ১৭-এর early stopping, শুধু গাছের সংখ্যায়।
Outlier. বাজারঘরের প্রায় ৬% পার্সেল (কাল্পনিক) পথে হারিয়ে যায়, পরে খুঁজে পেয়ে পৌঁছাতে ৪০ থেকে ১২০ ঘণ্টা বেশি লাগে। ঠিকঠাক অর্ডারের সময় অনুমান করতে চাইলে এই সারিগুলো train-এ বিপদ। নিচের ব্লক train-এর y-তে এই outlier রাখে, কিন্তু test-এ মাপে outlier ছাড়া আসল সময়ের সঙ্গে: আমরা জানতে চাই, outlier কোন loss-কে কতটা টেনে নেয়। শেষে quantile loss দিয়ে ১০% আর ৯০% সীমা।
import numpy as np
from sklearn.ensemble import GradientBoostingRegressor as GBR
from sklearn.metrics import mean_absolute_error as mae
rng = np.random.default_rng(24)
n = 800
km, kg = rng.uniform(1, 30, n), rng.gamma(2, 1.5, n)
y = 6 + 9 * np.log1p(km) + 1.5 * kg + rng.normal(0, 3, n)
lost = rng.random(n) < 0.06 # ৬% পার্সেল হারিয়ে পরে পৌঁছায়
y_obs = y + lost * rng.uniform(40, 120, n) # train-এ outlier
X = np.column_stack([km, kg])
tr, te = slice(0, 600), slice(600, n)
print(f"outliers in train: {lost[tr].sum()} of 600")
for name, target in [("clean y", y), ("y with outliers", y_obs)]:
es = GBR(n_estimators=2000, learning_rate=0.1, max_depth=3, random_state=0,
n_iter_no_change=10, validation_fraction=0.2).fit(X[tr], target[tr])
print(f"early stopping on {name:15s}: {es.n_estimators_} of 2000 trees")
print("loss MAE vs clean y median error")
for loss, kw in [("squared_error", {}), ("absolute_error", {}),
("huber", dict(alpha=0.9))]:
m = GBR(loss=loss, n_estimators=150, max_depth=3, learning_rate=0.1,
random_state=0, **kw).fit(X[tr], y_obs[tr])
err = m.predict(X[te]) - y[te]
print(f"{loss:15s} {mae(y[te], m.predict(X[te])):8.2f} {np.median(err):14.2f}")
q = {a: GBR(loss="quantile", alpha=a, n_estimators=150, max_depth=3,
random_state=0).fit(X[tr], y[tr]).predict(X[te]) for a in (0.1, 0.9)}
inside = np.mean((y[te] >= q[0.1]) & (y[te] <= q[0.9]))
print(f"quantile 0.1-0.9 band: {inside:.1%} of clean test inside, "
f"mean width {np.mean(q[0.9] - q[0.1]):.1f} h")আসল আউটপুট (ব্রাউজারের Python, একটি seed):
outliers in train: 42 of 600
early stopping on clean y : 42 of 2000 trees
early stopping on y with outliers: 13 of 2000 trees
loss MAE vs clean y median error
squared_error 5.99 3.06
absolute_error 2.57 0.80
huber 3.79 0.60
quantile 0.1-0.9 band: 71.5% of clean test inside, mean width 7.2 h
Early stopping: পরিষ্কার y-তে ২,০০০-এর মধ্যে মাত্র ৪২টি গাছ বানিয়ে থেমেছে, বাকি সময় বেঁচে গেল। Outlier-ভরা y-তে থেমেছে ১৩-তে: validation অংশেও কয়েকটি হারানো পার্সেল আছে, তাদের বিশাল squared ভুল validation loss-কে এলোমেলো করে দেয়, তাই “উন্নতি নেই” খুব তাড়াতাড়ি ঘোষণা হয়। শিক্ষা: early stopping-এর validation loss একই loss দিয়ে মাপা হয়, তাই loss ভুল হলে থামার জায়গাও ভুল।
Loss: squared loss outlier-এর দিকে টেনে প্রায় সব অনুমান বাড়িয়ে দেয়: median ভুল +৩.০৬ ঘণ্টা, MAE ৫.৯৯. Absolute loss-এ MAE ২.৫৭, প্রায় অর্ধেকের কম; Huber মাঝখানে, ৩.৭৯, কারণ alpha=0.9-তে δ হলো residual-এর ৯০তম percentile, আর ৭% outlier-এর ডেটায় সেই সীমা বড় ভুলের কিছুটা ভেতরে নেয়। alpha কমালে Huber absolute-এর দিকে যায়।
Quantile: ১০% আর ৯০% quantile-এর মাঝে আসলে ৮০% অর্ডার থাকার কথা, কিন্তু test-এ আছে ৭১.৫%. প্রতিটি সীমা train-এ নিজের quantile শিখেছে, তবু নতুন ডেটায় পরিসর সংকীর্ণ: ১৫০টি গাছ train-এর প্রান্তের বিন্দুগুলো আংশিক মুখস্থ করেছে। তাই quantile boosting-এর পরিসর কোনো নিশ্চয়তা দেয় না; নিশ্চয়তা চাইলে পর্ব ৫৮ (Conformal prediction — নিশ্চয়তাসহ অনুমানের পরিসর)। কম গাছ বা বড় min_samples_leaf নিলে coverage সাধারণত কাছে আসে; validation-এ মেপে নিন।
৯ · ব্যবহারিক রেসিপি আর এর পরের ধাপ
| Hyperparameter (ডিফল্ট) | কী করে | শুরুর পরামর্শ |
|---|---|---|
learning_rate (0.1) | প্রতিটি গাছের অবদান ν | ০.০৫–০.১; গাছের সংখ্যার সঙ্গে একসঙ্গে |
n_estimators (100) | সর্বোচ্চ গাছ | বড় রাখুন, early stopping দিয়ে থামান |
max_depth (3) | interaction-এর মাত্রা | ২–৬, CV দিয়ে |
subsample (1.0) | প্রতিটি গাছের সারির ভাগ | ০.৫–০.৮ |
min_samples_leaf (1) | পাতায় সর্বনিম্ন সারি | বড় করলে পাতার মান স্থির |
loss | কোন ভুল কতটা দামি | ব্যবসার খরচ দেখে; outlier-এ absolute বা huber |
n_iter_no_change (None) | early stopping চালু | ১০–২০, সঙ্গে validation_fraction |
scikit-learn-এর GradientBoostingRegressor/Classifier শেখানোর জন্য স্বচ্ছ, কিন্তু প্রতিটি ভাগের জন্য প্রতিটি feature-এর সব মান সাজায়, তাই হাজার দশেকের বেশি সারিতে ধীর। scikit-learn নিজেই সেখানে HistGradientBoostingRegressor/Classifier সুপারিশ করে, যা মানগুলো ২৫৫টি পর্যন্ত ঝুড়িতে (bin) ভাগ করে আর missing মান নিজেই সামলায়। সেটি, আর gradient-এর পাশে Hessian ব্যবহার করে XGBoost, LightGBM আর CatBoost, পর্ব ৩৬ (XGBoost, LightGBM আর CatBoost — পার্থক্য আর হাতে-কলমে)-এ। অংশ ৪-এর Newton পাতা সেখানে প্রতিটি loss-এর সাধারণ নিয়ম হয়ে যায়।
আরও গভীরে: Jerome Friedman-এর “Greedy Function Approximation: A Gradient Boosting Machine” (Annals of Statistics, ২০০১; ১৯৯৯ সালের technical report থেকে) সাধারণ algorithm, least-absolute-deviation আর Huber সংস্করণ, shrinkage আর partial dependence plot একসঙ্গে দেয়। প্রায় একই সময়ে Llew Mason, Jonathan Baxter, Peter Bartlett আর Marcus Frean (NIPS ১৯৯৯) boosting-কে function space-এ gradient descent হিসেবে ব্যাখ্যা করেন, আর Leo Breiman-এর আগের কাজেও এই দৃষ্টির সূত্র ছিল বলে ধরা হয়। Peter Bühlmann আর Bin Yu (JASA ২০০৩) squared loss-এর boosting (L2Boosting) বিশ্লেষণ করে দেখান, গাছের সংখ্যাই এখানে regularization-এর মূল নব, যা early stopping-এর তাত্ত্বিক ভিত্তি।
পরীক্ষা ও ইন্টারভিউতে:
- Squared loss-এ pseudo-residual কী? y − F, সাধারণ residual, কারণ ½(y − F)²-এর F অনুযায়ী অন্তরজ −(y − F).
- Log loss-এ? y − p, যেখানে p = σ(F); পাতার মান Newton ধাপে ∑(y − p)/∑p(১ − p).
- Bagging আর boosting-এর মূল পার্থক্য? Bagging স্বাধীন গভীর গাছের গড়, মূলত variance কমায়; boosting ক্রমিক অগভীর গাছের যোগফল, মূলত bias কমায়।
- learning_rate ০.১ থেকে ০.০৫ করলে? একই মানের জন্য প্রায় দ্বিগুণ গাছ লাগে; প্রায়ই সামান্য ভালো বা সমান validation ফল।
- max_depth = ১ মানে কী? প্রতিটি গাছ একটি feature দেখে, তাই পুরো model additive; কোনো interaction ধরে না।
১০ · AI-এর যুগে — boosting কোড যাচাই
AI assistant boosting-এর কোড খুব দ্রুত লেখে, আর প্রায়ই চলেও। সমস্যা লুকিয়ে থাকে hyperparameter, ভাগ আর মাপে। পরিচিত ভুলগুলো:
| AI প্রায়ই যা করে | কেন ভুল | কীভাবে ধরবেন |
|---|---|---|
loss="ls", "lad" বা "deviance" লেখে | পুরোনো নাম, এখনকার version-এ সরানো; এখন squared_error, absolute_error, log_loss | নিজের version-এর docs খুলে প্রতিটি argument মেলান |
Test set দিয়ে n_estimators বা early stopping বাছে | Test আর নিরপেক্ষ থাকে না, ফল আশাবাদী | Train-এর ভেতরে validation বা CV; test শুধু শেষে একবার |
| Train-এর score বা একটি split দেখিয়ে “৯৯% accuracy” | Boosting train-এর ভুল প্রায় শূন্যে নিতে পারে | কয়েকটি seed বা fold, সরল baseline-এর পাশে |
learning_rate=1.0 আর হাজার গাছ, বা খুব গভীর গাছ | দ্রুত overfitting | staged_predict দিয়ে validation-এর বক্ররেখা দেখুন |
Classifier-এর predict_proba-কে সরাসরি ঝুঁকির সম্ভাবনা বলে | Boosting-এর সম্ভাবনা প্রায়ই অতি-আত্মবিশ্বাসী হতে পারে | আলাদা ভাগে reliability diagram, দরকারে calibration |
| Pseudo-residual বা Newton ধাপের ভুল সূত্র আত্মবিশ্বাসের সঙ্গে | দেখতে ঠিক, অথচ চিহ্ন বা একক ভুল | চারটি সারিতে হাতে হিসাব, তারপর scikit-learn-এর সঙ্গে মেলানো |
AI-কে শুধু কোড নয়, যাচাইয়ের পরিকল্পনাও চাইতে বলুন:
AI-এর দেওয়া boosting কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১“দুর্বল learner-দের জোড়া দিয়ে কি শক্ত learner বানানো যায়?” একটি তাত্ত্বিক প্রশ্ন কীভাবে আজকের সবচেয়ে ব্যবহৃত tabular পদ্ধতিতে পৌঁছাল?
গল্পের শুরু computational learning theory-তে। Michael Kearns আর Leslie Valiant ১৯৮০-এর দশকের শেষে প্রশ্ন তোলেন: যে learner অনুমানের চেয়ে সামান্য ভালো, তাকে কি কোনোভাবে যত খুশি নির্ভুল learner-এ বদলানো যায়? Robert Schapire ১৯৯০ সালে (Machine Learning জার্নালে) দেখান, যায়: তত্ত্বে “দুর্বল” আর “শক্ত” শেখার ক্ষমতা সমান। কিন্তু তাঁর প্রথম পদ্ধতি ব্যবহারিক ছিল না।
Yoav Freund আর Schapire ১৯৯৫ সালে AdaBoost প্রস্তাব করেন (জার্নাল সংস্করণ ১৯৯৭), যা পর্ব ৩৪-এর বিষয়: ভুল করা সারির ওজন বাড়িয়ে পরের learner শেখানো। এটি অবাক করার মতো ভালো কাজ করল, অথচ কেন করে তা নিয়ে বিতর্ক ছিল। ২০০০ সালে Jerome Friedman, Trevor Hastie আর Robert Tibshirani (Annals of Statistics) দেখান, AdaBoost আসলে exponential loss-এ একটি forward stagewise additive model; অর্থাৎ পরিসংখ্যানের ভাষায় loss কমানোর একটি ধাপে ধাপে পদ্ধতি। এই দৃষ্টি থেকেই Friedman-এর প্রশ্ন: exponential loss কেন, যেকোনো loss নয় কেন? তাঁর ২০০১ সালের gradient boosting সেই সাধারণীকরণ।
প্রায় একই সময়ে Mason, Baxter, Bartlett আর Frean একই ধারণায় পৌঁছান function space-এ gradient descent হিসেবে। পরের দশকে এই কাঠামোর ওপর দ্রুত বাস্তবায়ন আসে: XGBoost (Tianqi Chen আর Carlos Guestrin-এর KDD ২০১৬ প্রবন্ধ), LightGBM, CatBoost; সেগুলো পর্ব ৩৬-এ।
শিক্ষাটি হলো তত্ত্ব আর প্রয়োগের আদান-প্রদান। একটি বিমূর্ত প্রশ্ন একটি অ্যালগরিদম দিল; সেই অ্যালগরিদমের ব্যাখ্যা খুঁজতে গিয়ে পরিসংখ্যান তাকে loss কমানোর ভাষায় পড়ল; আর সেই ভাষা নতুন loss, নতুন সমস্যা আর দ্রুত software-এর দরজা খুলল।
মূল উপলব্ধি: gradient boosting একটি তাত্ত্বিক প্রশ্ন, একটি সফল কৌশল আর তার পরিসংখ্যানগত ব্যাখ্যার মিলনে জন্মেছে; “ভুল শোধরানো” আর “loss-এর gradient ধরে হাঁটা” একই কথার দুই রূপ।
প্র ০২AdaBoost নিয়ে একসময় বলা হতো এটি প্রায় overfit করে না। অথচ এই পর্বে validation-এর ভুল আবার বেড়েছে। কোনটি সত্য, আর কেন?
১৯৯০-এর দশকের শেষে অনেক পরীক্ষায় দেখা গিয়েছিল, AdaBoost-এ train-এর ভুল শূন্য হওয়ার পরেও আরও গাছ যোগ করলে test-এর ভুল কখনো কমতেই থাকে। Schapire, Freund, Bartlett আর Lee ১৯৯৮ সালে margin দিয়ে এর একটি ব্যাখ্যা দেন: গাছ যোগ করলে train সারিগুলোর margin বাড়ে, আর বড় margin ভালো সাধারণীকরণের সঙ্গে যুক্ত। এ থেকে একটি জনপ্রিয় ধারণা ছড়ায় যে boosting “overfit করে না”।
পরে বোঝা গেল ছবিটি অত সরল নয়। গোলমালভরা label-এ AdaBoost খারাপভাবে overfit করতে পারে, কারণ exponential loss ভুল label-এর সারিকে বিশাল ওজন দেয়; Thomas Dietterich-সহ কয়েকজন এমন পরীক্ষা প্রকাশ করেন। Friedman, Hastie আর Tibshirani-ও দেখান যে যথেষ্ট দীর্ঘ চালালে boosting শেষে overfit করে। Margin-এর ব্যাখ্যা নিয়েও বিতর্ক চলেছে; Breiman ১৯৯৯ সালে এমন উদাহরণ দেন যেখানে margin বাড়লেও test খারাপ হয়।
Gradient boosting-এর ক্ষেত্রে এখনকার সাধারণ মত হলো: গাছের সংখ্যা একটি regularization-এর নব। Bühlmann আর Yu-এর L2Boosting বিশ্লেষণ, আর Tong Zhang ও Bin Yu-এর (২০০৫) early stopping-সহ consistency-র ফল এই দিকেই নির্দেশ করে: ঠিক সময়ে থামলে ভালো, অনন্তকাল চালালে নয়। Shrinkage overfitting-কে ধীর করে, বন্ধ করে না। এই পর্বের অংশ ৬-এর বক্ররেখাগুলো ঠিক এটি দেখায়: ν যত ছোট, তলা তত চওড়া আর ওঠা তত ধীর, কিন্তু ওঠা আছে।
ব্যবহারিক দিক থেকে উত্তরটি সহজ: থামার জায়গা সবসময় validation বা CV দিয়ে বাছুন, আর ভুল label বেশি থাকলে log loss বা Huber-এর মতো কম কঠোর loss, subsample আর বড় min_samples_leaf দিয়ে model-কে সংযত রাখুন।
মূল উপলব্ধি: boosting overfit করে, শুধু ধীরে; গাছের সংখ্যা আর ν একসঙ্গে একটি regularization, আর থামার জায়গা ডেটা দিয়ে মাপতে হয়।
প্র ০৩Tabular ডেটার প্রতিযোগিতা আর benchmark-এ গাছভিত্তিক boosting প্রায়ই শীর্ষে থাকে। এই দাবি কতটা সাধারণ, আর boosting-এর দুর্বলতা কোথায়?
XGBoost-এর প্রবন্ধে Chen আর Guestrin জানান, ২০১৫ সালে Kaggle-এর ব্লগে প্রকাশিত ২৯টি বিজয়ী সমাধানের মধ্যে ১৭টিতে XGBoost ব্যবহৃত হয়েছিল। এটি একটি নির্দিষ্ট সময় আর প্ল্যাটফর্মের গণনা, সাধারণ সূত্র নয়। পরে Léo Grinsztajn, Edouard Oyallon আর Gaël Varoquaux (NeurIPS ২০২২, datasets and benchmarks track) মাঝারি আকারের অনেক tabular ডেটাসেটে তুলনা করে দেখান, গাছভিত্তিক model তখনকার deep learning পদ্ধতিগুলোর চেয়ে সাধারণত ভালো ছিল, আর কারণ হিসেবে অপ্রাসঙ্গিক feature আর অমসৃণ লক্ষ্যের প্রতি গাছের সহনশীলতার কথা বলেন। Ravid Shwartz-Ziv আর Amitai Armon (২০২২) একই রকম ইঙ্গিত দেন। এই ক্ষেত্র দ্রুত বদলাচ্ছে, তাই পর্ব ৩৮ (Tabular ডেটায় model বাছাই)-এ সৎ তুলনার পদ্ধতিটিই আসল শিক্ষা, কোনো একটি রায় নয়।
দুর্বলতাগুলোও এই পর্বেই দেখা যায়। এক, extrapolation: গাছের যোগফল সিঁড়ির মতো, তাই train-এর বাইরে (৩০ কিমির পরে) অনুমান সমতল হয়ে যায়; দূরত্ব বাড়লে সময় বাড়ছে এমন সরল নিয়মও সে সীমার বাইরে টানতে পারে না। দুই, মসৃণতা: সত্যিকারের নিয়ম মসৃণ আর প্রায়-রৈখিক হলে linear model অল্প ডেটায় প্রায়ই সমান বা ভালো। তিন, সম্ভাবনা: log loss শেখালেও boosting-এর সম্ভাবনা ঠিকমতো calibrated থাকবে, তার নিশ্চয়তা নেই (পর্ব ২৬)। চার, ব্যাখ্যা: শত শত গাছের যোগফল সরাসরি পড়া যায় না।
আর আছে ব্যবসায়িক সীমা: “দূরত্ব বাড়লে সময় কখনো কমবে না” এমন monotone নিয়ম সাধারণ boosting নিজে থেকে মানে না; histogram-ভিত্তিক বাস্তবায়নে monotonic_cst দিয়ে তা চাপানো যায় (পর্ব ৩৬)।
মূল উপলব্ধি: boosting tabular ডেটায় একটি শক্ত প্রথম প্রার্থী, কিন্তু extrapolation, calibration আর ব্যাখ্যার দুর্বলতা জেনে, একই fold-এ সরল baseline-এর সঙ্গে সৎ তুলনা করেই বাছতে হয়।
প্র ০৪বাজারঘর গ্রাহককে “এত ঘণ্টার মধ্যে পৌঁছাবে” কথা দিতে চায়। গড়, median না quantile — কোন loss দিয়ে model বানাবেন, আর কীভাবে জানবেন কথাটি রাখা যাচ্ছে?
প্রশ্নটি আসলে ব্যবসার খরচের প্রশ্ন, যা পর্ব ০২-এ দেখেছেন: loss হলো ভুলের দাম। Squared loss গড় অনুমান করে; গড় outlier-এ সংবেদনশীল, আর এই পর্বের ডেটায় হারানো পার্সেল সবার অনুমান তিন ঘণ্টা বাড়িয়ে দিয়েছে। Absolute loss median অনুমান করে, যা “অর্ধেক অর্ডার এর আগে পৌঁছায়”। কিন্তু গ্রাহককে median বললে অর্ধেক গ্রাহক দেরি দেখবেন; প্রতিশ্রুতির জন্য এটি খারাপ।
প্রতিশ্রুতির জন্য দরকার একটি উঁচু quantile, যেমন ৯০%: “দশটির মধ্যে নয়টি অর্ডার এর মধ্যে”। এর loss pinball loss (পর্ব ২৪), আর boosting-এ loss="quantile", alpha=0.9. Pinball loss অসম: দেরিতে (y অনুমানের ওপরে) দাম α, আগে পৌঁছালে দাম ১ − α. তাই α বাড়ালে model ইচ্ছে করে বড় সংখ্যা বলে। α কত হবে, তা নির্ভর করে দেরির দাম (অভিযোগ, ফেরত) আর বেশি সময় বলার দামের (কম অর্ডার) ওপর; এটি ব্যবসার সিদ্ধান্ত, model-এর নয়। Huber loss-এর জনক Peter Huber (১৯৬৪) এই ভাবনারই আরেক দিক দেখিয়েছিলেন: ছোট ভুলে দক্ষ, বড় ভুলে শক্ত।
কথা রাখা যাচ্ছে কি না, তা আলাদা ডেটায় coverage মেপে জানতে হয়। এই পর্বের run-এ ১০–৯০% সীমা ৮০%-এর বদলে মাত্র ৭১.৫% অর্ডার ধরেছে। এমন হলে দুটি পথ: model সংযত করা (কম গাছ, বড় পাতা), অথবা conformal prediction (পর্ব ৫৮), যা একটি calibration ভাগ দিয়ে সীমাকে এমনভাবে চওড়া করে যে কিছু শর্তে নির্দিষ্ট coverage-এর নিশ্চয়তা থাকে। আর জেলা বা মৌসুম অনুযায়ী আলাদা coverage দেখুন; গড়ে ৯০% মানে প্রতিটি জেলায় ৯০% নয়।
মূল উপলব্ধি: loss বাছাই একটি ব্যবসায়িক সিদ্ধান্ত: গড়ের জন্য squared, outlier-এ শক্ত median-এর জন্য absolute বা Huber, প্রতিশ্রুতির জন্য quantile; আর প্রতিশ্রুতি রাখা যাচ্ছে কি না তা আলাদা ডেটায় মাপতে হয়।
প্র ০৫শত শত গাছের যোগফল কীভাবে ব্যাখ্যা করবেন? Friedman নিজে এর কী উত্তর দিয়েছিলেন, আর আজকের পদ্ধতিগুলোর সীমা কী?
Friedman-এর ২০০১ সালের একই প্রবন্ধে gradient boosting-এর পাশাপাশি দুটি ব্যাখ্যার যন্ত্র ছিল। একটি হলো feature-এর আপেক্ষিক গুরুত্ব: প্রতিটি ভাগে squared error কতটা কমল, সব গাছে যোগ করে। অন্যটি partial dependence plot: একটি feature-এর মান বদলে, বাকিগুলোর ওপর গড় নিয়ে, model-এর গড় অনুমান কীভাবে বদলায়। বাজারঘরের ক্ষেত্রে “দূরত্ব ১০ থেকে ২০ কিমি হলে গড়ে কত ঘণ্টা বাড়ে” এমন প্রশ্নের উত্তর এই ছবি দেয়।
দুটিরই সীমা আছে। Impurity-ভিত্তিক গুরুত্ব অনেক আলাদা মানের feature-এর দিকে পক্ষপাতী, যা পর্ব ৩৩ (Bagging আর random forest)-এ একটি এলোমেলো ID কলাম দিয়ে দেখেছেন; permutation importance সেখানে বেশি নির্ভরযোগ্য। Partial dependence গড় নেয়, তাই interaction লুকিয়ে ফেলতে পারে: বৃষ্টির প্রভাব ঢাকার ভেতরে শূন্য আর বাইরে বড় হলে গড় ছবিটি মাঝারি একটি প্রভাব দেখাবে, যা কোথাও সত্য নয়। ICE বক্ররেখা প্রতিটি সারির আলাদা রেখা এঁকে এটি ধরে। এগুলো পর্ব ৫৬ (Model বোঝা — permutation importance, PDP আর ICE)-এ বিস্তারিত।
একটি নির্দিষ্ট অনুমান ব্যাখ্যার জন্য আজ বহুল ব্যবহৃত SHAP; Scott Lundberg ও সহকর্মীরা গাছের জন্য দ্রুত TreeSHAP অ্যালগরিদম দেন (প্রকাশিত ২০২০, Nature Machine Intelligence). কিন্তু সবচেয়ে বড় সতর্কতা সব পদ্ধতির জন্য একই: এগুলো model কী শিখেছে তা বলে, পৃথিবী কীভাবে চলে তা নয়। “বৃষ্টির SHAP মান বড়” মানে model বৃষ্টির ওপর নির্ভর করছে, বৃষ্টি দেরির কারণ এমন প্রমাণ নয়; সংশ্লিষ্ট feature থাকলে নির্ভরতা তাদের মধ্যে ভাগ হয়ে যেতে পারে। কারণ খুঁজতে পর্ব ৬৪ (Causal inference-এর পরিচয়)। SHAP নিয়ে বিস্তারিত পর্ব ৫৭-এ।
মূল উপলব্ধি: gradient boosting-এর জন্মের সঙ্গেই তার ব্যাখ্যার যন্ত্র এসেছিল; আজ permutation importance, PDP, ICE আর SHAP model-কে বোঝায়, কিন্তু কোনোটিই model-এর নির্ভরতাকে কার্যকারণে বদলায় না।
অনুশীলন
হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। চাইলে অংশ ২, ৫ আর ৮-এর ব্লক বদলে যাচাই করুন।
-
পূর্ণ numerical: (ক) চারটি অর্ডার: x = ১, ২, ৩, ৪ কিমি, y = ৪, ৬, ১০, ১৬ ঘণ্টা। Squared loss, stump, ν = ০.৫. F0, residual, সেরা ভাগ, পাতার মান, F1 আর MSE-এর পরিবর্তন বের করুন। (খ) Log loss-এ একটি পাতায় দুজন গ্রাহক: একজনের y = ০, p = ০.৮; অন্যজনের y = ১, p = ০.৬. পাতার Newton মান কত?
(ক) F0 = (৪ + ৬ + ১০ + ১৬)/৪ = ৯. Residual: −৫, −৩, ১, ৭. MSE = (২৫ + ৯ + ১ + ৪৯)/৪ = ২১.
ভাগের লাভ nL·nR/n × (r̄L − r̄R)²: x < ১.৫ → ১·৩/৪ × (−৫ − ১.৬৬৭)² = ৩৩.৩; x < ২.৫ → ২·২/৪ × (−৪ − ৪)² = ৬৪; x < ৩.৫ → ৩·১/৪ × (−২.৩৩৩ − ৭)² = ৬৫.৩, সেরা।
পাতা: বাঁয়ে (−৫ − ৩ + ১)/৩ = −২.৩৩৩, ডানে ৭. F1 = ৯ + ০.৫ × পাতা: ৭.৮৩৩, ৭.৮৩৩, ৭.৮৩৩, ১২.৫. নতুন residual −৩.৮৩৩, −১.৮৩৩, ২.১৬৭, ৩.৫; MSE = (১৪.৬৯ + ৩.৩৬ + ৪.৬৯ + ১২.২৫)/৪ = ৮.৭৫, ২১ থেকে। (ডেস্কটপে
GradientBoostingRegressor(n_estimators=1, learning_rate=0.5, max_depth=1)একই ভাগ ৩.৫ আর একই অনুমান দিয়েছে।)(খ) r = y − p: −০.৮ আর ০.৪, যোগফল −০.৪. h = p(১ − p): ০.১৬ আর ০.২৪, যোগফল ০.৪০. γ = −০.৪/০.৪০ = −১.০ (log-odds-এর এককে)।
-
সমালোচনা ও নকশা: একজন সহকর্মী (বা AI) বাজারঘরের churn model-এর জন্য লিখেছেন: “
GradientBoostingClassifier(loss="deviance", learning_rate=1.0, n_estimators=3000, max_depth=10). Test set-এ প্রতি ১০০ গাছ পরে AUC দেখে সবচেয়ে ভালো জায়গায় থামব। Train AUC ০.৯৯৯, তাই model তৈরি।predict_probaদিয়ে সরাসরি ছাড়ের বাজেট ঠিক করব।” সমস্যা কী, আর ভালো নকশা কী?সমস্যা: (১)
loss="deviance"পুরোনো নাম; এখনকার version-এ"log_loss". (২) ν = ১.০ আর গভীরতা ১০: প্রতিটি গাছ প্রায় সব বাকি ভুল এক লাফে শেখে, আর দশ-feature-এর interaction; দ্রুত overfitting. (৩) Test দিয়ে থামার জায়গা বাছলে test আর নিরপেক্ষ নয়; রিপোর্ট করা AUC আশাবাদী। (৪) Train AUC ০.৯৯৯ boosting-এ প্রায় অর্থহীন। (৫) Boosting-এর সম্ভাবনা calibration যাচাই ছাড়া বাজেটে ব্যবহার বিপজ্জনক।ভালো নকশা: train/validation/test ভাগ (বা train-এ CV),
loss="log_loss",learning_rate০.০৫–০.১,max_depth২–৪,subsample০.৮, বড়n_estimatorsআরn_iter_no_changeদিয়ে early stopping বা validation-এরstaged_predictবক্ররেখা; logistic regression baseline একই fold-এ; reliability diagram আর দরকারে calibration; শেষে test-এ একবার। ডেটা বড় হলেHistGradientBoostingClassifier. -
আউটপুট অনুমান করুন: চালানোর আগে লিখুন নিচের কোড কী ছাপবে। (সংকেত: অংশ ১-এর চার অর্ডার, কিন্তু ν = ০.৫ আর দুটি ধাপ।)
import numpy as np from sklearn.ensemble import GradientBoostingRegressor X = np.array([[2.0], [5.0], [12.0], [20.0]]) y = np.array([10.0, 14.0, 22.0, 36.0]) gbr = GradientBoostingRegressor(n_estimators=2, learning_rate=0.5, max_depth=1).fit(X, y) for p in gbr.staged_predict(X): print(np.round(p, 2))[17.92 17.92 17.92 28.25] [14.96 14.96 20.88 31.21]প্রথম ধাপ অংশ ১-এর মতোই: ভাগ km < ১৬, পাতা −৫.১৬৬৭ আর ১৫.৫; ν = ০.৫ বলে ২০.৫ − ২.৫৮৩ = ১৭.৯২ আর ২০.৫ + ৭.৭৫ = ২৮.২৫. নতুন residual: −৭.৯২, −৩.৯২, ৪.০৮, ৭.৭৫. দ্বিতীয় stump-এর সেরা ভাগ এখন km < ৮.৫ (A, B বনাম C, D): পাতা −৫.৯২ আর ৫.৯২, অর্ধেক যোগ করে ১৪.৯৬, ১৪.৯৬, ২০.৮৮, ৩১.২১. লক্ষ করুন, দ্বিতীয় গাছ প্রথমটির চেয়ে অন্য প্রশ্ন বেছেছে, কারণ residual বদলে গেছে। (ডেস্কটপে scikit-learn 1.9.1-এ চালিয়ে মেলানো।)
-
কোড লিখুন: অংশ ২-এর লুপ বদলে
loss="absolute_error"-এর gradient boosting নিজে লিখুন: F0 = median, pseudo-residual sign(y − F), গাছ sign-এর ওপর, আর প্রতিটি পাতার মান সেই পাতার residual-এর median. ভারী লেজের গোলমালের ২০০টি অর্ডারেGradientBoostingRegressor(loss="absolute_error")-এর সঙ্গে মেলান।import numpy as np from sklearn.tree import DecisionTreeRegressor from sklearn.ensemble import GradientBoostingRegressor rng = np.random.default_rng(1) km = rng.uniform(1, 30, 200) hrs = 6 + 9 * np.log1p(km) + 3 * rng.standard_t(2, 200) # ভারী লেজের গোলমাল X = km.reshape(-1, 1) def lad_boost(X, y, nu=0.1, M=40, leaf_median=np.median): F = np.full(len(y), np.median(y)) # F0 = median for _ in range(M): r = y - F tree = DecisionTreeRegressor(max_depth=2).fit(X, np.sign(r)) # −gradient leaf = tree.apply(X) for L in np.unique(leaf): # line search: পাতার median i = leaf == L F[i] += nu * leaf_median(r[i]) return F lower = lambda a: np.percentile(a, 50, method="inverted_cdf") sk = GradientBoostingRegressor(loss="absolute_error", n_estimators=40, learning_rate=0.1, max_depth=2, random_state=0).fit(X, hrs).predict(X) for name, med in [("np.median", np.median), ("lower median", lower)]: F = lad_boost(X, hrs, leaf_median=med) print(f"{name:12s} train MAE {np.mean(np.abs(hrs - F)):.4f}" f" max |own - sklearn| {np.abs(F - sk).max():.2e}") print(f"sklearn train MAE {np.mean(np.abs(hrs - sk)):.4f}")ডেস্কটপে সত্যিকারের run (Python 3.14.4, scikit-learn 1.9.1, NumPy 2.5.4):
np.median train MAE 3.1988 max |own - sklearn| 1.28e+00 lower median train MAE 3.1872 max |own - sklearn| 0.00e+00 sklearn train MAE 3.1872সাধারণ
np.medianদিলে MAE প্রায় সমান, কিন্তু অনুমান কোথাও ১.২৮ ঘণ্টা পর্যন্ত আলাদা। কারণ জোড় সংখ্যার সারির পাতায়np.medianমাঝের দুটির গড় নেয়, আর scikit-learn-এর ভেতরের weighted percentile নিচেরটি নেয়। “নিচের median” (method="inverted_cdf") দিলে পার্থক্য হুবহু শূন্য। শিক্ষা: “একই algorithm” মেলাতে গেলে এমন ছোট সংজ্ঞাও মেলাতে হয়, আর এই ভেতরের খুঁটিনাটি version বদলালে বদলাতে পারে।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমGradient boosting-এর সাধারণ algorithm-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- সেরা ধ্রুবক F0 দিয়ে শুরু করা (যেমন গড় বা log-odds)
- প্রতিটি সারিতে pseudo-residual −∂ℓ/∂F হিসাব করা
- Pseudo-residual-এর ওপর একটি ছোট regression tree শেখানো
- প্রতিটি পাতায় loss কমানো মান বের করা (line search বা Newton ধাপ)
- ν দিয়ে ছোট করে গাছের অবদান F-এ যোগ করা
- Validation-এর ভুল আর না কমলে থামা
Squared loss-এ gradient boosting: চারটি সারির y = ১০, ১৪, ২২, ৩৬ আর F0 = ২০.৫. প্রথম গাছ কোন সংখ্যাগুলো শেখে?
- ১০, ১৪, ২২, ৩৬
- ১০.৫, ৬.৫, −১.৫, −১৫.৫
- −১০.৫, −৬.৫, ১.৫, ১৫.৫
- −১, −১, ১, ১
Binary classification-এ log loss নিয়ে gradient boosting চলছে। একটি গ্রাহকের y = ১ আর চলতি p = ০.২. তার pseudo-residual কত?
- −০.৮
- ০.৮
- ০.১৬
- ৫.০
Log loss-এর boosting-এ একটি পাতায় তিনটি সারি: y − p = ০.৫, ০.৫, −০.২ আর p(১ − p) = ০.২৫, ০.২৫, ০.১৬. scikit-learn-এর GradientBoostingClassifier এই পাতার মান কত রাখে (ν দিয়ে গুণের আগে)?
- ০.২৬৭
- ০.৮
- ০.৯২
- ≈ ১.২১
একটি gradient boosting model learning_rate = ০.১-এ প্রায় ২০০টি গাছে validation-এ সেরা। learning_rate ০.০৫ করলে মোটামুটি কী আশা করা যুক্তিসঙ্গত?
- প্রায় ৪০০টি গাছ লাগবে, validation ফল সমান বা সামান্য ভালো
- প্রায় ১০০টি গাছেই একই ফল
- ২০০টি গাছেই একই ফল, কারণ গাছের সংখ্যা ν-এর ওপর নির্ভর করে না
- ছোট ν-এ model আর কখনো overfit করবে না
ডেলিভারির সময়ে বৃষ্টির প্রভাব শুধু ঢাকার বাইরে দেখা যায় (একটি interaction). শুধু decision stump (max_depth = ১) দিয়ে gradient boosting করলে কী হবে?
- যথেষ্ট গাছ দিলে interaction-ও পুরো ধরবে
- গড় অনুমানের চেয়ে ভালো কিছুই শিখবে না
- Model additive হবে, তাই বৃষ্টি আর ঠিকানার interaction ধরতে পারবে না
- Stump-এর কারণে খুব দ্রুত overfit করবে
৬% অর্ডারের পৌঁছানোর সময় ৪০–১২০ ঘণ্টা বেশি (হারানো পার্সেল), আর বাকিদের সাধারণ সময়ের ভালো অনুমান দরকার। GradientBoostingRegressor-এ কোন loss সবচেয়ে যুক্তিসঙ্গত শুরু?
- squared_error, কারণ এটি ডিফল্ট
- absolute_error, অথবা ছোট alpha-সহ huber
- quantile, alpha = ০.৯৯
- log_loss
scikit-learn-এর GradientBoostingRegressor-এ n_iter_no_change = ১০ আর validation_fraction = ০.২ দিলে কী ঘটে?
- Train-এর ২০% আলাদা রেখে সেখানে loss দেখে, ১০ ধাপ উন্নতি না হলে থামে
- Test set-এর ২০% দিয়ে থামার জায়গা বাছে
- ঠিক ১০টি গাছ বানায়, প্রতিটি ২০% ডেটায়
- প্রতিটি গাছ train-এর এলোমেলো ২০% সারিতে শেখে
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Model: FM = F0 + ν∑hm; প্রতিটি ছোট গাছ চলতি pseudo-residual −∂ℓ/∂F শেখে। এটি অনুমানের ওপর gradient descent (functional gradient descent).
- Pseudo-residual: squared-এ y − F, absolute-এ sign(y − F), quantile-এ α বা α − ১, log loss-এ y − p; গাছ সবসময় squared error দিয়ে বানানো, loss ঢোকে দিক আর পাতার মানে।
- Classification: F = log-odds, শুরু log(p̄/(১ − p̄)), পাতার মান Newton ধাপ ∑(y − p)/∑p(১ − p).
- Regularization: ছোট ν মানে প্রায় ২.৩/ν গুণ বেশি গাছ; গাছের সংখ্যা validation বা early stopping দিয়ে; subsample আর অগভীর গাছ variance কমায়; গভীরতা d মানে d-মাত্রার interaction.
- Loss বাছাই: outlier-এ absolute বা Huber, প্রতিশ্রুতিতে quantile; early stopping-এর loss-ও সেই অনুযায়ী, আর quantile সীমার coverage আলাদা ডেটায় মাপতে হয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৩৬ · XGBoost, LightGBM আর CatBoost — পার্থক্য আর হাতে-কলমেপরবর্তী পর্বNewton boosting, histogram-এ ভাগ খোঁজা আর তিন জনপ্রিয় লাইব্রেরির তুলনা।
- মেশিন লার্নিং · কোর্স হোম৭৭টি পর্ব১৪টি মডিউলের পুরো রোডম্যাপ; পর্ব ৩৬ এখান থেকে খুলবেন।
- পর্ব ১৩ · Gradient descent-এর পরিবারএই কোর্সLearning rate আর ধাপের ধারণা, যা এখানে function-এর মানে প্রয়োগ হলো।
- পর্ব ১৫ · Second-order পদ্ধতিএই কোর্সNewton ধাপ আর Hessian, log loss-এর পাতার মানের ভিত্তি।
- পর্ব ২৪ · Robust আর quantile regressionএই কোর্সHuber আর pinball loss, outlier আর অনুমানের পরিসর।
- AI-এর ভিত্তি · পর্ব ৬৬ · Model selection আর CVআগের ভিত্তিএকই fold-এ model তুলনা আর hyperparameter খোঁজা।
- DSA কোর্সসম্পর্কিত কোর্সগাছের গঠন আর সাজানোর খরচ, যা গাছ শেখার গতির পেছনে।