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

Bagging আর random forest — variance কমানো আর OOB error

Bagging and Random Forests — Variance of Averages, Bootstrap and Out-of-Bag Error, Feature Subsampling, Importance Bias and Extrapolation

Taught in Bangla, this lesson derives the variance of an average of correlated trees and the 63.2% bootstrap fraction, checks both by simulation, and builds bagging by hand against RandomForestClassifier on fictional churn data in the browser. It measures max_features and tree correlation, compares OOB with 5-fold CV, exposes impurity-importance bias with a random ID column, and shows forests failing to extrapolate in a real desktop run.

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

  • Correlated অনুমানের গড়ের variance-এর সূত্র প্রমাণ করা আর simulation দিয়ে মেলানো
  • Bootstrap নমুনায় ৬৩.২% আলাদা সারি কেন, আর একটি সারি out-of-bag হওয়ার সম্ভাবনা হাতে হিসাব করা
  • নিজে bagging লিখে একটি গভীর গাছ আর RandomForestClassifier-এর সঙ্গে তুলনা, OOB score বনাম ৫-fold CV
  • max_features, n_estimators আর min_samples_leaf-এর প্রভাব মাপা, গাছের correlation সহ
  • Impurity আর permutation importance-এর পার্থক্য, আর regression forest ও extra-trees-এর extrapolation-এর সীমা

আগের পর্ব ৩২-এ (Decision tree গভীরে — CART, impurity, pruning আর missing value) একটি গাছ কীভাবে প্রশ্ন বেছে ডেটা ভাগ করে, তা দেখেছেন। সেখানে একটি অস্বস্তিকর কথাও এসেছে: ডেটা একটু বদলালে গাছ অনেক বদলে যায়। এই পর্ব সেই দুর্বলতাকে শক্তিতে বদলায়। অনেকগুলো নড়বড়ে গাছের গড় একটি স্থির অনুমান দেয়, যদি গাছগুলো একে অন্যের নকল না হয়। AI-এর ভিত্তি কোর্সের পর্ব ৬৬-এ random forest-কে প্রথম দেখেছেন; এখানে তার ভেতরের গণিত, মাপ আর ফাঁদ। চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python, scikit-learn 1.8.0); প্রথমবার scikit-learn নামতে প্রায় দশ সেকেন্ড লাগে।

১ · একটি গাছ কেন নড়বড়ে — variance-এর সমস্যা

একটি ক্রিকেট ম্যাচের ফল আন্দাজ করতে একজন ধারাভাষ্যকারের কথা শুনলে তাঁর মেজাজ, পছন্দের দল, আগের দিনের খবর সব মিশে যায়। দশজন আলাদা বিশ্লেষকের গড় আন্দাজ সাধারণত বেশি স্থির। কিন্তু দশজনই যদি একই টিভি চ্যানেলের একই স্টুডিওতে বসে একই পরিসংখ্যান দেখেন, তবে তাঁদের ভুলও একসঙ্গে হয়; গড় নিয়ে লাভ কম। Bagging আর random forest ঠিক এই দুটি কথা নিয়ে: অনেক মতামতের গড়, আর মতামতগুলোকে যতটা সম্ভব স্বাধীন রাখা।

একটি গভীর decision tree (pruning ছাড়া) train ডেটা প্রায় মুখস্থ করে: পাতাগুলো এত ছোট যে প্রায় প্রতিটি সারি নিজের পাতা পায়। এতে bias কম, কারণ যেকোনো জটিল সীমা আঁকতে পারে; কিন্তু variance বেশি, কারণ train ডেটার কয়েকটি সারি বদলালেই মূলের কাছের একটি প্রশ্ন বদলে যায়, আর তার নিচের পুরো গাছ অন্য রকম হয়। পর্ব ০৩ আর AI-এর ভিত্তি কোর্সের পর্ব ৬১-এ bias–variance-এর ভাগ দেখেছেন: প্রত্যাশিত squared ভুল = bias² + variance + অপরিহার্য গোলমাল।

Pruning variance কমায়, কিন্তু bias বাড়িয়ে। প্রশ্ন হলো, bias প্রায় না বাড়িয়ে variance কমানো যায় কি? উত্তর গড়ে। একই ধরনের অনেক model-এর গড়ের bias প্রায় একক model-এর bias-এর সমান থাকে (গড়ের প্রত্যাশা = প্রত্যাশার গড়), কিন্তু variance কমে। কতটা কমে, সেটিই পরের অংশের সূত্র। এই ভাবনার নাম ensemble learning: অনেকগুলো base learner-এর অনুমান মিলিয়ে একটি সিদ্ধান্ত।

২ · গড়ের হিসাব — ρσ² + (1 − ρ)σ²/B

ধরুন B টি অনুমান T1, …, TB (যেমন B টি গাছের একই সারির জন্য অনুমান)। প্রত্যেকের variance σ², আর যেকোনো দুটির correlation ρ, অর্থাৎ covariance ρσ². এখানে ρ মাপে দুটি গাছের ভুল কতটা একসঙ্গে ওঠানামা করে: ০ মানে স্বাধীন, ১ মানে হুবহু এক। গড় T̄ = (1/B)∑Tb. Variance-এর নিয়ম Var(∑Tb) = ∑Var(Tb) + ∑b≠cCov(Tb, Tc) দিয়ে:

Var(T̄) = (1/B²) [ Bσ² + B(B − 1)ρσ² ] = ρσ² + (1 − ρ)σ²/B

প্রথম বন্ধনীতে B টি নিজের variance আর B(B − 1)টি ক্রমযুক্ত জোড়ার covariance. ভাগ করে সাজালে σ²/B + ρσ²(B − 1)/B, যা ওপরের রূপের সমান। Trevor Hastie, Robert Tibshirani আর Jerome Friedman-এর বই The Elements of Statistical Learning-এর random forest অধ্যায় ঠিক এই সূত্র দিয়ে শুরু হয়।

Var(T̄) = ρσ² + (1 − ρ)σ²/Bσ² = 1

            
  1. দুটি পদ। একটি গাছের variance ১ ধরা হলো। গড়ের variance-এ একটি পদ গাছের সংখ্যার ওপর নির্ভর করে না, অন্যটি B দিয়ে ভাগ হয়।
  2. ρ = ০. গাছগুলো সম্পূর্ণ স্বাধীন হলে গড়ের variance σ²/B: দশটিতে ০.১, একশোটিতে ০.০১. এটিই পরিচিত “গড় নিলে গোলমাল কমে” নিয়ম, law of large numbers-এর ভিত্তি।
  3. ρ = ০.৩. দশটি গাছে ০.৩৭, একশোটিতে ০.৩০৭. B দশ গুণ করেও লাভ মাত্র ০.০৬৩, কারণ ০.৩-এর মেঝে নড়ে না।
  4. ρ = ১. সব গাছ হুবহু এক হলে গড় নেওয়া অর্থহীন: variance ১-ই থাকে। একই ডেটায় একই নিয়মে বানানো গাছ এমনই হয়।
  5. শিক্ষা। গাছ যথেষ্ট হলে বাকি থাকে ρσ². তাই ρ কমানোর কৌশল দামি, এমনকি একক গাছ একটু খারাপ (σ² একটু বেশি) হলেও। সংখ্যাগুলো উদাহরণের জন্য ধরা; অংশ ৬-এ আসল মাপ দেখবেন।

দুটি সতর্কতা। এক, সূত্রটি squared ভুলের variance অংশের কথা বলে; bias অংশ গড়ে বদলায় না। তাই bagging উচ্চ-bias, কম-variance model-এর (যেমন একটি ছোট stump বা linear regression) জন্য প্রায় কোনো কাজের নয়। দুই, classification-এ ভোট বা সম্ভাবনার গড়ের পরে threshold আছে, তাই accuracy-র ওপর প্রভাব এই সরল সূত্রের মতো মসৃণ নয়; Leo Breiman ১৯৯৬ সালেই লক্ষ করেন, স্থির (stable) পদ্ধতিতে bagging লাভ দেয় না, কখনো সামান্য খারাপও করে। ভাবনার মূল কাঠামো তবু একই।

৩ · Bootstrap নমুনা — ৬৩.২% আর out-of-bag সারি

সূত্রে B টি আলাদা অনুমান চাই, অথচ ডেটা একটাই। Breiman-এর উত্তর: প্রতিটি গাছের জন্য একটি bootstrap নমুনা, অর্থাৎ n টি সারি থেকে ফেরত রেখে n বার টানা। এই পদ্ধতির নাম bagging, bootstrap aggregating-এর সংক্ষেপ (Leo Breiman, “Bagging Predictors”, Machine Learning, ১৯৯৬)। পর্ব ২১-এ bootstrap দিয়ে অনিশ্চয়তা মেপেছেন আর .632 অনুমানের কথা শুনেছেন; এখানে একই নমুনা model বানানোর কাঁচামাল।

একটি নির্দিষ্ট সারি একবার টানায় না ওঠার সম্ভাবনা 1 − 1/n. n বার স্বাধীন টানায় একবারও না ওঠার সম্ভাবনা (1 − 1/n)n. n বড় হলে এটি e−1 ≈ ০.৩৬৮-এর দিকে যায়, কারণ (1 + x/n)n → ex, এখানে x = −১.

P(out-of-bag) = (1 − 1/n)ⁿn = সারির সংখ্যা

            
  1. দুটি সারি। চারটি সমান সম্ভাব্য নমুনার মধ্যে শুধু BB-তে A নেই, তাই সম্ভাবনা ১/৪. নমুনায় গড়ে ৭৫% আলাদা সারি।
  2. তিন আর পাঁচ। (২/৩)³ = ৮/২৭ ≈ ০.২৯৬, আর (৪/৫)⁵ ≈ ০.৩২৮. n বাড়লে সম্ভাবনা বাড়ছে, কিন্তু ধীরে।
  3. দশ থেকে হাজার। ০.৩৪৯, ০.৩৬৬, ০.৩৬৮. একশো সারিতেই মান প্রায় স্থির।
  4. সীমা। e⁻¹ ≈ ০.৩৬৭৯. তাই প্রতিটি bootstrap নমুনায় গড়ে ৬৩.২% আলাদা সারি, বাকিগুলো পুনরাবৃত্তি। বাদ পড়া ৩৬.৮% সেই গাছের জন্য অদেখা ডেটা।
  5. পুরো forest. প্রতিটি সারি গড়ে ০.৩৬৮B গাছে বাইরে থাকে; ৬০টি গাছে প্রায় ২২টি। কোনো গাছেই বাইরে না থাকার সম্ভাবনা ০.৬৩২B: পাঁচটি গাছে ১০%, পঁচিশটিতে প্রায় লাখে এক। তাই গাছ খুব কম হলে কিছু সারির OOB অনুমান থাকে না, আর scikit-learn সতর্কবার্তা দেয়।

এই অদেখা সারিগুলো থেকেই out-of-bag (OOB) অনুমান: প্রতিটি সারি i-এর জন্য শুধু সেই গাছগুলোর গড় নিন, যাদের নমুনায় i ছিল না। তারপর সেই অনুমান আর আসল label মিলিয়ে accuracy বা ভুল। কোনো সারি নিজের অনুমানে অংশ নেয়নি, তাই এটি test-এর মতো সৎ। দাম শূন্য: আলাদা fit লাগে না।

প্রথম ব্লক দুটি দাবি simulation দিয়ে মেলায়। (ক) ভাগের একটি অংশ আর নিজস্ব অংশ মিলিয়ে ঠিক ρ correlation-এর B টি অনুমান বানিয়ে তাদের গড়ের variance-কে সূত্রের সঙ্গে তুলনা। (খ) বিভিন্ন n-এ bootstrap নমুনায় আলাদা সারির ভাগ। (ক)-তে √ρ·Z + √(1 − ρ)·Eb লেখার কারণ: Z সবার ভাগের, Eb প্রত্যেকের নিজের, দুটিই variance ১; তাহলে প্রতিটির variance ১ আর যেকোনো দুটির covariance ρ.

import numpy as np

rng = np.random.default_rng(33)
reps = 20000                                         # প্রতিটি পরিস্থিতিতে ২০,০০০ বার
print("rho    B   simulated Var(mean)   formula")
for rho in [0.0, 0.3, 0.7]:
    for B in [1, 10, 100]:
        common = rng.normal(0, 1, (reps, 1))         # সব অনুমানের ভাগের অংশ
        own = rng.normal(0, 1, (reps, B))            # প্রত্যেকের নিজের অংশ
        est = np.sqrt(rho) * common + np.sqrt(1 - rho) * own   # σ² = 1
        avg = est.mean(axis=1)                       # B টি অনুমানের গড়
        theory = rho + (1 - rho) / B
        print(f"{rho:.1f}  {B:4d}      {avg.var():.4f}          {theory:.4f}")

print("\n    n   unique share (200 samples)   1-(1-1/n)^n")
for n in [10, 100, 1000, 10000]:
    shares = [np.unique(rng.integers(0, n, n)).size / n for _ in range(200)]
    print(f"{n:5d}        {np.mean(shares):.4f}               {1 - (1 - 1 / n) ** n:.4f}")
print(f"limit 1 - 1/e = {1 - np.exp(-1):.4f}")

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

rho    B   simulated Var(mean)   formula
0.0     1      0.9880          1.0000
0.0    10      0.1012          0.1000
0.0   100      0.0099          0.0100
0.3     1      1.0160          1.0000
0.3    10      0.3716          0.3700
0.3   100      0.3029          0.3070
0.7     1      1.0010          1.0000
0.7    10      0.7309          0.7300
0.7   100      0.7122          0.7030

    n   unique share (200 samples)   1-(1-1/n)^n
   10        0.6405               0.6513
  100        0.6346               0.6340
 1000        0.6319               0.6323
10000        0.6321               0.6321
limit 1 - 1/e = 0.6321

নয়টি সারির প্রতিটিতে simulation সূত্রের সঙ্গে প্রায় হুবহু মেলে; যেটুকু পার্থক্য (যেমন ০.৩০২৯ বনাম ০.৩০৭০), তা ২০,০০০ পুনরাবৃত্তির নিজের গোলমাল। B = ১-এ সব ρ-তে variance প্রায় ১, কারণ একটি অনুমানের গড় সে নিজেই। ρ = ০-তে B দশ গুণ হলে variance দশ ভাগের এক ভাগ। কিন্তু ρ = ০.৭-এ দশ থেকে একশো অনুমানে variance ০.৭৩ থেকে মাত্র ০.৭১-এ নামে: মেঝে ০.৭. দ্বিতীয় টেবিলে আলাদা সারির ভাগ n = ১০০-তেই ০.৬৩৪, আর দশ হাজারে ০.৬৩২১, ঠিক 1 − 1/e. n = ১০-এ simulation (০.৬৪০৫) আর সূত্রের (০.৬৫১৩) ফাঁক বেশি, কারণ ছোট n-এ প্রতিটি নমুনার ভাগ অনেক নড়ে আর গড় মাত্র ২০০টির।

৪ · Bagging থেকে random forest — feature subsampling দিয়ে ρ কমানো

Bootstrap গাছগুলোকে আলাদা করে, কিন্তু পুরোপুরি নয়। ৬৩% সারি তো সব নমুনায় ভাগাভাগি। আরও বড় সমস্যা: ডেটায় একটি খুব শক্ত feature থাকলে প্রায় প্রতিটি গাছ মূলে সেই feature দিয়েই ভাগ করে, তারপর নিচের প্রশ্নগুলোও মিলে যায়। বাজারঘরের churn ডেটায় সেই feature হলো “শেষ অর্ডারের পর কত দিন”. ফলে ρ বড় থাকে, আর অংশ ২-এর মেঝে ρσ² উঁচু।

Breiman-এর ২০০১ সালের “Random Forests” প্রবন্ধ (Machine Learning, খণ্ড ৪৫) এর সমাধান দেয়: গাছ বড় করার সময় প্রতিটি node-এ p টি feature থেকে এলোমেলোভাবে m টি বেছে শুধু তাদের মধ্যে সেরা split খোঁজা। এর নাম feature subsampling, scikit-learn-এ max_features. ভাবনাটির আগের রূপ ছিল Tin Kam Ho-এর random subspace method (১৯৯৫, ১৯৯৮), যেখানে পুরো গাছ একটি feature-উপসেটে শেখে, আর Yali Amit ও Donald Geman-এর ১৯৯৭ সালের এলোমেলো গাছ। m = p হলে random forest সাধারণ bagging-ই।

Random forest: bootstrap নমুনা, এলোমেলো feature, তারপর গড়

৫টি সারি (১–৫), ৩টি গাছ; ধূসর সারি = সেই গাছের OOB

Bagging and random forest with out-of-bag rows train: সারি ১ ২ ৩ ৪ ৫ ১ ১ ৩ ৪ ৪ OOB: ২, ৫ ২ ৩ ৩ ৫ ৫ OOB: ১, ৪ ১ ২ ২ ৪ ৫ OOB: ৩ গাছ ১ গাছ ২ গাছ ৩ প্রতিটি node-এ p-এর মধ্যে এলোমেলো m টি feature সম্ভাবনার গড় বা ভোট সারি ২-এর OOB অনুমান = শুধু গাছ ১ (গাছ ২ আর ৩ সারি ২ দেখেছে)
ছবি ৩৩.১ — প্রতিটি গাছ নিজের bootstrap নমুনায় শেখে, প্রতিটি node-এ এলোমেলো কয়েকটি feature দেখে। OOB অনুমানে একটি সারির জন্য শুধু সেই গাছগুলো ভোট দেয়, যারা সারিটি দেখেনি।

কেন feature subsampling ρ কমায়, তা একটি সরল হিসাবে দেখা যায়। চলে যাওয়া গ্রাহক, অর্থাৎ churn, আন্দাজের ডেটায় p = ৮টি feature, যার একটি অনেক শক্তিশালী।

p = 8 feature, একটি শক্ত: days_sincem = max_features

            
  1. m = ৮. প্রতিটি node-এ সব feature দেখা গেলে শক্ত feature প্রায় সবসময় মূলে জেতে। Bootstrap নমুনা আলাদা হলেও গাছগুলোর গঠন কাছাকাছি, তাই তাদের ভুলও একসঙ্গে।
  2. m = ৩. মূলে শক্ত feature প্রার্থী না হওয়ার সম্ভাবনা ৩৫/৫৬, অর্থাৎ সাতটি দুর্বল feature থেকে তিনটি বাছার উপায় ভাগ আটটি থেকে তিনটির উপায়। প্রার্থী হওয়ার সম্ভাবনা ৩/৮.
  3. m = ২ আর ১. scikit-learn-এর classifier-এর ডিফল্ট "sqrt" মানে ⌊√p⌋, এখানে ২. সম্ভাবনা ১/৪; m = ১-এ ১/৮. সাধারণ নিয়ম m/p, কারণ m টি আসন আর p জন প্রার্থী।
  4. দাম। শক্ত feature না পেলে গাছ দুর্বল প্রশ্নে শুরু করে, তাই একক গাছের ভুল বাড়ে। কিন্তু forest-এর ভুলের মেঝে ρσ²; ρ কমার হার σ² বাড়ার হারের চেয়ে বেশি হলে লাভ। সেরা m তাই সাধারণত মাঝামাঝি, ডেটা দেখে বাছতে হয়।
  5. লাভ। শক্ত feature-এর আড়ালে থাকা দুর্বল কিন্তু আসল pattern-গুলো কিছু গাছে মূল জায়গা পায়। গড়ে সেগুলো যোগ হয়, আর এলোমেলো ভুলগুলো কাটাকাটি হয়। অংশ ৬-এ correlation সরাসরি মাপবেন।

৫ · হাতে-কলমে — নিজে লেখা bagging, একটি গাছ আর random forest

এই পর্বের ডেটা: বাজারঘরের কাল্পনিক গ্রাহক, seed দিয়ে বানানো, কোনো আসল দোকানের নয়। আটটি feature: ছয় মাসে অর্ডার, শেষ অর্ডারের পর দিন, গড় ঝুড়ির টাকা, অভিযোগ, অ্যাপে মিনিট, ছাড়ে কেনার ভাগ, দেরিতে ডেলিভারির ভাগ আর জেলার কোড। লক্ষ্য: গ্রাহক চলে যাবে (churn = ১) কি না। নিয়মে দুটি interaction আছে: অনেক দিন অর্ডার না করা আর বারবার দেরিতে ডেলিভারি একসঙ্গে হলে ঝুঁকি অনেক বাড়ে, আর ছোট ঝুড়ির গ্রাহক শুধু ছাড়ে কিনলে। এমন ডেটায় গাছ ভালো কাজ করে।

# bazar_churn.py — বাজারঘরের কাল্পনিক গ্রাহক churn (seed দিয়ে বানানো)
import numpy as np

COLS = ["orders_6m", "days_since", "basket_tk", "complaints",
        "app_min", "discount", "late_rate", "district"]

def make_churn(n, seed=33):
    r = np.random.default_rng(seed)
    orders = r.poisson(4, n)                         # ৬ মাসে অর্ডার
    days = r.exponential(30, n)                      # শেষ অর্ডারের পর দিন
    basket = r.lognormal(7, 0.5, n)                  # গড় ঝুড়ি (টাকা)
    compl = r.poisson(0.5, n)                        # অভিযোগ
    app = r.gamma(2, 10, n)                          # অ্যাপে মিনিট/সপ্তাহ
    disc = r.beta(2, 5, n)                           # ছাড়ে কেনার ভাগ
    late = r.beta(2, 6, n)                           # দেরিতে ডেলিভারির ভাগ
    dist = r.integers(0, 8, n)                       # জেলা (কোড)
    z = (-1.2 + 0.035 * days - 0.3 * orders + 0.7 * compl - 0.03 * app
         + 2.5 * disc * (basket < 900) + 3.0 * (late > 0.3) * (days > 25))
    y = (r.random(n) < 1 / (1 + np.exp(-z))).astype(int)
    X = np.column_stack([orders, days, basket, compl, app, disc, late, dist])
    return X, y

দ্বিতীয় ব্লক ৮০০ গ্রাহকে শেখায় আর ২,০০০ জনের একটি আলাদা, তালাবন্ধ test-এ মাপে। প্রথমে নিজে লেখা bagging: ৬০টি bootstrap নমুনা, প্রতিটিতে একটি pruning-ছাড়া DecisionTreeClassifier; প্রতিটি গাছের test সম্ভাবনা P-তে জমা হয়, আর OOB সারির সম্ভাবনা আলাদা যোগফলে। তারপর একটি একক গভীর গাছ, scikit-learn-এর RandomForestClassifier (oob_score=True), আর একই forest-এর ৫-fold cross-validation. পরের ব্লক এই ব্লকের ফল ব্যবহার করে, তাই দুটি একই namespace-এ চলে।

import numpy as np
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from bazar_churn import make_churn

X, y = make_churn(800, seed=1)                   # train: ৮০০ গ্রাহক
Xte, yte = make_churn(2000, seed=2)              # তালাবন্ধ test
n, B = len(X), 60
rng = np.random.default_rng(0)
P = np.zeros((B, len(Xte)))                      # প্রতিটি গাছের test সম্ভাবনা
p_oob, n_oob = np.zeros(n), np.zeros(n)
for b in range(B):
    idx = rng.integers(0, n, n)                  # bootstrap: ফেরত রেখে n টি
    out = np.setdiff1d(np.arange(n), idx)        # এই গাছের OOB সারি
    t = DecisionTreeClassifier(random_state=b).fit(X[idx], y[idx])
    P[b] = t.predict_proba(Xte)[:, 1]
    p_oob[out] += t.predict_proba(X[out])[:, 1]
    n_oob[out] += 1
print(f"churn rate (test) {yte.mean():.3f}, rows never OOB: {(n_oob == 0).sum()}")
tree = DecisionTreeClassifier(random_state=0).fit(X, y)
print(f"one deep tree       train {tree.score(X, y):.4f}  test {tree.score(Xte, yte):.4f}")
print(f"own bagging, B={B}  test {np.mean((P.mean(0) > 0.5) == yte):.4f}"
      f"  OOB {np.mean((p_oob / n_oob > 0.5) == y):.4f}")
rf = RandomForestClassifier(n_estimators=B, oob_score=True, random_state=0).fit(X, y)
cv = cross_val_score(RandomForestClassifier(n_estimators=B, random_state=0), X, y, cv=5)
print(f"RandomForest        test {rf.score(Xte, yte):.4f}  OOB {rf.oob_score_:.4f}")
print(f"5-fold CV (5 forests) {cv.mean():.4f} ± {cv.std():.4f}  folds", np.round(cv, 3))

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

churn rate (test) 0.337, rows never OOB: 0
one deep tree       train 1.0000  test 0.7280
own bagging, B=60  test 0.7970  OOB 0.7887
RandomForest        test 0.8130  OOB 0.7825
5-fold CV (5 forests) 0.7725 ± 0.0287  folds [0.788 0.788 0.725 0.806 0.756]

Churn-এর হার ০.৩৩৭, তাই “কেউ যাবে না” বললেই accuracy ০.৬৬৩. একক গভীর গাছ train-এ ১.০০০, test-এ ০.৭২৮: পুরো মুখস্থ, আর variance-এর দাম। নিজে লেখা bagging একই গাছ ৬০টি bootstrap নমুনায় শিখিয়ে test ০.৭৯৭-এ তোলে, প্রায় ৭ শতাংশ বিন্দু লাভ, কোনো নতুন তথ্য ছাড়াই; আর তার OOB ০.৭৮৯, test-এর কাছেই। কোনো সারি OOB-ছাড়া থাকেনি, কারণ ০.৬৩২৬০ প্রায় ১০−১২. Random forest (ডিফল্ট max_features="sqrt", অর্থাৎ ৮টির মধ্যে ২টি) আরও এগিয়ে ০.৮১৩. তার OOB ০.৭৮৩ আর ৫-fold CV ০.৭৭৩ ± ০.০২৯: OOB একটি fit-এই এসেছে, CV লেগেছে আরও পাঁচটি forest. Fold-গুলোর মধ্যেই ০.৭২৫ থেকে ০.৮০৬ ছড়ানো, যা OOB আর CV-এর পার্থক্যের চেয়ে অনেক বড়। OOB আর CV-এর সব অনুমানই test-এর চেয়ে একটু কম; ৮০০ সারির accuracy-র standard error প্রায় ০.০১৪, তাই এই পার্থক্য মূলত নমুনার ভাগ্য।

৬ · গাছের সংখ্যা, max_features আর OOB বনাম cross-validation

তৃতীয় ব্লক আগের ব্লকের ডেটা আর ৬০টি নিজের গাছ নিয়ে দুটি প্রশ্নের উত্তর মাপে। (ক) গাছের সংখ্যা বাড়লে test accuracy কীভাবে বদলায়: প্রথম k টি গাছের গড় দিয়ে, k = ১ থেকে ৬০. (খ) max_features = ১, ২, ৩, ৫, ৮-এ forest-এর test আর OOB accuracy, একটি গাছের গড় accuracy, আর গাছগুলোর test সম্ভাবনার জোড়ায় জোড়ায় গড় correlation, অর্থাৎ অংশ ২-এর ρ-এর একটি সরাসরি মাপ।

import matplotlib.pyplot as plt

ks = np.arange(1, B + 1)
bag_curve = [np.mean((P[:k].mean(0) > 0.5) == yte) for k in ks]
rows = []
print("max_features  test    OOB    tree acc  mean corr between trees")
for m in [1, 2, 3, 5, 8]:
    f = RandomForestClassifier(n_estimators=B, max_features=m, oob_score=True,
                               random_state=0).fit(X, y)
    T = np.array([e.predict_proba(Xte)[:, 1] for e in f.estimators_])
    rho = np.corrcoef(T)[np.triu_indices(B, 1)].mean()   # জোড়ায় জোড়ায় গড় corr
    one = np.mean((T > 0.5) == yte, axis=1).mean()        # একটি গাছের গড় accuracy
    rows.append((m, f.score(Xte, yte), f.oob_score_))
    print(f"{m:7d}      {rows[-1][1]:.4f}  {rows[-1][2]:.4f}  {one:.4f}    {rho:.3f}")
    if m == 3:
        rf_curve = [np.mean((T[:k].mean(0) > 0.5) == yte) for k in ks]
print("bagging test acc at B = 1, 5, 20, 60: "
      + ", ".join(f"{bag_curve[k - 1]:.3f}" for k in [1, 5, 20, 60]))

fig, (a1, a2) = plt.subplots(1, 2, figsize=(9, 3.6))
a1.plot(ks, bag_curve, label="bagging (all 8 features)")
a1.plot(ks, rf_curve, label="random forest, max_features=3")
a1.set(xlabel="number of trees", ylabel="test accuracy", title="More trees: accuracy settles")
a1.legend(fontsize=8)
ms, te, oo = zip(*rows)
a2.plot(ms, te, "o-", label="test")
a2.plot(ms, oo, "s--", label="OOB")
a2.set(xlabel="max_features", ylabel="accuracy", title="Feature subsampling")
a2.legend(fontsize=8)
fig.subplots_adjust(wspace=0.3, bottom=0.15)

আসল আউটপুট (ব্রাউজারের Python), নিচে দুটি ছবি:

max_features  test    OOB    tree acc  mean corr between trees
      1      0.8120  0.7438  0.6609    0.167
      2      0.8130  0.7825  0.6867    0.256
      3      0.8130  0.7762  0.7018    0.312
      5      0.8085  0.7825  0.7077    0.350
      8      0.7945  0.7800  0.7134    0.391
bagging test acc at B = 1, 5, 20, 60: 0.708, 0.765, 0.794, 0.797

গাছের সংখ্যা: bagging-এ একটি গাছে ০.৭০৮, পাঁচটিতে ০.৭৬৫, বিশটিতে ০.৭৯৪, ষাটটিতে ০.৭৯৭. প্রায় বিশটির পরে রেখা সমতল, আর খারাপ হয় না। বাঁ ছবিতে random forest-এর রেখা শুরুতে নিচে (তার একক গাছ দুর্বল), কিন্তু প্রায় বিশটি গাছের পরে bagging-কে ছাড়িয়ে যায়: বৈচিত্র্যের লাভ আসে গড় নেওয়ার পরে। max_features: m ১ থেকে ৮-এ একক গাছের accuracy ০.৬৬১ থেকে ০.৭১৩-এ বাড়ে, কিন্তু গাছগুলোর গড় correlation ০.১৬৭ থেকে ০.৩৯১, দ্বিগুণের বেশি। Forest-এর test তাই m = ২–৩-এ সবচেয়ে ভালো (০.৮১৩), আর m = ৮, অর্থাৎ সাধারণ bagging, সবচেয়ে কম (০.৭৯৫): অংশ ২-এর ρσ² trade-off, মাপা অবস্থায়। পার্থক্য প্রায় ২ শতাংশ বিন্দু; ২,০০০ সারির test-এ standard error প্রায় ০.০০৯, তাই এটি একটি run-এর ফল, বড় দাবি নয়। শেষ কথা OOB নিয়ে: m = ১-এ OOB ০.৭৪৪, test ০.৮১২. প্রতিটি সারির OOB অনুমান মাত্র প্রায় ২২টি গাছের, আর দুর্বল গাছের ছোট forest যথেষ্ট স্থির নয়। গাছ বাড়ালে এই ফাঁক কমে (অনুশীলন ৪)।

OOB না cross-validation?

OOB ফ্রি, কিন্তু তার সীমা জানা দরকার। (১) প্রতিটি সারির OOB অনুমান আসে মাত্র প্রায় ৩৭% গাছ থেকে; গাছ কম হলে সেই ছোট forest পুরো forest-এর চেয়ে দুর্বল, তাই OOB হতাশাবাদী, ওপরের max_features=1 সারির মতো। (২) OOB শুধু forest-এর ভেতরটা যাচাই করে। Scaling, imputation, target encoding বা feature বাছাই যদি পুরো ডেটায় আগে করা হয়, OOB সেই leakage ধরতে পারে না; CV-তে পুরো pipeline প্রতিটি fold-এ নতুন করে শেখে। (৩) সময়ের ক্রম বা একই গ্রাহকের অনেক সারি থাকলে bootstrap সারি এলোমেলোভাবে টানে, তাই OOB আশাবাদী হতে পারে; তখন time-based বা group CV লাগে। (৪) OOB দিয়ে অনেক hyperparameter বাছাই করলে সেটিও একধরনের নির্বাচন, শেষ সংখ্যা তালাবন্ধ test থেকে নিন।

Hyperparameter (scikit-learn 1.8)ডিফল্টকী করে, কখন বদলাবেন
n_estimators100বেশি গাছে ভুল স্থির হয়, overfit বাড়ে না; শুধু সময় আর memory বাড়ে। accuracy-র রেখা সমতল হলে থামুন।
max_featuresclassifier "sqrt", regressor 1.0ρ আর একক গাছের শক্তির trade-off; সবচেয়ে গুরুত্বপূর্ণ knob. Regressor-এর ডিফল্ট সব feature, অর্থাৎ bagging; p/3 বা ০.৩–০.৫ চেষ্টা করুন।
min_samples_leaf, max_depth1, Noneগাছ কত গভীর; গোলমেলে ডেটায় ৫–২০-এর মতো পাতা প্রায়ই সাহায্য করে, model-ও ছোট হয়।
max_samples, bootstrapNone (n), Trueপ্রতিটি গাছ কত সারি টানে; বড় ডেটায় ভগ্নাংশ দিলে দ্রুত আর ρ কম। bootstrap=False হলে OOB থাকে না।
oob_score, class_weightFalse, NoneOOB score চালু; class অসমান হলে "balanced_subsample" বিকল্প (পর্ব ৫০-এ বিস্তারিত)।

একটি ছোট কিন্তু গুরুত্বপূর্ণ বিবরণ: Breiman-এর মূল forest প্রতিটি গাছের majority vote নেয়, কিন্তু scikit-learn-এর docs স্পষ্ট বলে যে তার বাস্তবায়ন গাছগুলোর সম্ভাবনার গড় নেয়। তাই predict_proba হলো “কত ভাগ গাছ এই শ্রেণিতে” ধরনের সংখ্যা, যা প্রায়ই ০ আর ১-এর কাছে যেতে দ্বিধা করে; পর্ব ২৬-এ forest-এর সম্ভাবনার এই আকৃতি আর calibration দেখেছেন।

৭ · Feature importance — impurity বনাম permutation

একটি কোচিং সেন্টারে কোন শিক্ষক ভালো, তা মাপার দুটি উপায়। এক, ক্লাসে কে কত বেশি কথা বলেছেন তা গোনা। দুই, একজন শিক্ষককে এক মাস ছুটিতে পাঠিয়ে দেখা পরীক্ষার ফল কতটা পড়ে। প্রথমটি সস্তা, কিন্তু যিনি বেশি কথা বলেন তিনিই এগিয়ে থাকেন। দ্বিতীয়টি ধীর, কিন্তু আসল নির্ভরতা মাপে। Random forest-এর দুটি importance ঠিক এই দুই উপায়।

Impurity-based importance (MDI, mean decrease in impurity) প্রতিটি গাছের প্রতিটি split-এ Gini impurity কতটা কমল তা সেই node-এর সারির ভাগ দিয়ে ভার দিয়ে feature অনুযায়ী যোগ করে, তারপর সব গাছে গড় আর মোট ১-এ normalise করে; scikit-learn-এ এটি feature_importances_. সমস্যা দুটি। এটি train ডেটা থেকে হিসাব হয়, তাই মুখস্থ করার split-গুলোও “গুরুত্ব” পায়। আর যে feature-এর আলাদা মান বেশি (high cardinality), তার সম্ভাব্য split বেশি, তাই শুধু সুযোগের জোরে কোথাও না কোথাও impurity কমানোর সম্ভাবনা বেশি। Carolin Strobl ও সহকর্মীরা ২০০৭ সালে (BMC Bioinformatics) এই পক্ষপাত বিশদভাবে দেখান।

Permutation importance উল্টো পথে যায়: শেখানো model স্থির রেখে held-out ডেটায় একটি কলামের মান এলোমেলো করে দেখে score কতটা কমে। কলামটি দরকারি হলে score পড়ে, অদরকারি হলে প্রায় শূন্য। চতুর্থ ব্লক churn ডেটায় একটি ফাঁদ বসায়: প্রতিটি গ্রাহকের একটি এলোমেলো customer_id, churn-এর সঙ্গে যার কোনো সম্পর্ক নেই। তারপর দুটি forest (min_samples_leaf ১ আর ২০) দুই রকম importance দিয়ে মাপা।

import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance
from bazar_churn import make_churn, COLS

X, y = make_churn(800, seed=1)
Xte, yte = make_churn(2000, seed=2)
rng = np.random.default_rng(0)
# লাগানো ফাঁদ: প্রতিটি গ্রাহকের এলোমেলো ID, churn-এর সঙ্গে কোনো সম্পর্ক নেই
X = np.column_stack([X, rng.permutation(len(X))])
Xte = np.column_stack([Xte, rng.permutation(len(Xte))])
names = COLS + ["customer_id"]

table = {}
for leaf in [1, 20]:
    rf = RandomForestClassifier(n_estimators=60, min_samples_leaf=leaf,
                                random_state=0).fit(X, y)
    pi = permutation_importance(rf, Xte, yte, n_repeats=5, random_state=0)
    table[f"MDI_leaf{leaf}"] = rf.feature_importances_
    table[f"perm_leaf{leaf}"] = pi.importances_mean
    print(f"min_samples_leaf={leaf:2d}: test accuracy {rf.score(Xte, yte):.4f}")
pd.set_option("display.width", 90, "display.max_columns", 9)
df = pd.DataFrame(table, index=names).round(3)
print(df.sort_values("MDI_leaf1", ascending=False))
print("unique values:", ", ".join(f"{c} {len(np.unique(v))}" for c, v in zip(names, X.T)))

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

min_samples_leaf= 1: test accuracy 0.7995
min_samples_leaf=20: test accuracy 0.8110
             MDI_leaf1  perm_leaf1  MDI_leaf20  perm_leaf20
days_since       0.325       0.176       0.546        0.190
late_rate        0.153       0.058       0.169        0.068
app_min          0.112       0.010       0.082        0.006
discount         0.099      -0.004       0.040       -0.001
basket_tk        0.096       0.008       0.049        0.000
customer_id      0.092       0.002       0.040        0.000
orders_6m        0.061       0.014       0.054        0.006
district         0.036       0.000       0.009        0.000
complaints       0.026       0.003       0.011        0.000
unique values: orders_6m 12, days_since 800, basket_tk 800, complaints 5, app_min 800, discount 800, late_rate 800, district 8, customer_id 800

ফাঁদ কাজ করেছে। min_samples_leaf=1-এ এলোমেলো customer_id MDI-তে ০.০৯২ পেয়ে ষষ্ঠ, “৬ মাসে অর্ডার” (০.০৬১), জেলা (০.০৩৬) আর অভিযোগের (০.০২৬) ওপরে। অথচ test-এ তার permutation importance ০.০০২, কার্যত শূন্য। শেষ লাইন কারণ দেখায়: ID-এর ৮০০টি আলাদা মান, অভিযোগের মাত্র ৫টি আর জেলার ৮টি; গভীর গাছ ৮০০ মানের কলামে অনেক ছোট ছোট ভাগ পায়, যা train-এ impurity কমায় কিন্তু নতুন ডেটায় কিছুই বলে না। একই কারণে discount-ও MDI-তে ০.০৯৯, অথচ permutation-এ −০.০০৪. দুই পদ্ধতিই একমত যে days_since আর late_rate আসল চালক, যা ডেটা বানানোর নিয়মের সঙ্গে মেলে। পাতা বড় করলে (২০) ID-এর MDI অর্ধেকের বেশি কমে ০.০৪০, test accuracy ০.৭৯৯৫ থেকে ০.৮১১-এ ওঠে; পক্ষপাত কমে, মোছে না। মনে রাখুন MDI-এর যোগফল সবসময় ১ (আপেক্ষিক ভাগ), আর permutation importance accuracy-র একক কমতি; তাই দুই কলামের সংখ্যা সরাসরি তুলনীয় নয়, শুধু ক্রম তুলনীয়। কাজের নিয়ম: একটি এলোমেলো কলাম বসান, আর যে feature সেটির চেয়ে স্পষ্টভাবে ওপরে নয়, তার গুরুত্বের দাবি করবেন না।

Permutation importance-এরও সীমা। দুটি feature খুব correlated হলে (যেমন “৬ মাসে অর্ডার” আর “৬ মাসে খরচ”), একটি এলোমেলো করলে অন্যটি তার কাজ চালিয়ে নেয়, তাই দুটিরই importance কম দেখায়। আর এলোমেলো করা সারিগুলো বাস্তবে অসম্ভব সংমিশ্রণ তৈরি করতে পারে, যেখানে model-কে extrapolate করতে হয়। কোনো importance-ই কারণ বোঝায় না: এটি model কীসের ওপর নির্ভর করে তার মাপ, দুনিয়া কীভাবে চলে তার নয়। পর্ব ৫৬ (Model বোঝা — permutation importance, PDP আর ICE) আর পর্ব ৫৭ (একটি অনুমানের ব্যাখ্যা — SHAP আর LIME) এই প্রশ্নগুলো বিস্তারিত নেবে।

৮ · Regression forest, extra-trees আর extrapolation-এর সীমা

Regression-এ প্রতিটি পাতা তার train সারিগুলোর লক্ষ্যের গড় রাখে, আর forest সেই গড়গুলোর গড় নেয়। তাই forest-এর যেকোনো অনুমান train-এর লক্ষ্যগুলোর একটি ভারযুক্ত গড়: সবচেয়ে ছোট আর সবচেয়ে বড় train লক্ষ্যের মাঝেই থাকতে বাধ্য। শেখার পরিসরের বাইরের ইনপুটে, অর্থাৎ extrapolation-এ, শেষ পাতাগুলোর মানেই আটকে থাকে।

Extra-trees (extremely randomized trees; Pierre Geurts, Damien Ernst আর Louis Wehenkel, Machine Learning, ২০০৬) এলোমেলোতা আরও এক ধাপ বাড়ায়: প্রতিটি বাছা feature-এর threshold-ও এলোমেলোভাবে টানা হয়, তারপর সেই প্রার্থীদের মধ্যে সেরাটি। scikit-learn-এ ডিফল্টভাবে bootstrap-ও নেই (bootstrap=False), তাই OOB-ও নেই। ফল: ρ আরও কম, fit দ্রুত (সাজানো আর সব threshold খোঁজা লাগে না), কিন্তু একক গাছ দুর্বল। Extrapolation-এর সমস্যা তার-ও একই, কারণ পাতায় সেই গড়ই।

বাজারঘরের কাল্পনিক ডেলিভারি সময়: আসল নিয়ম মিনিট = ১৫ + ৩.২ × km + ৮ × বৃষ্টি + গোলমাল (sd ৪ মিনিট)। Train-এ শুধু ১–১৫ km-এর ৪০০টি অর্ডার। তারপর বাজারঘর নতুন এলাকায় ১৫–২৫ km দূরে ডেলিভারি শুরু করল। ডেস্কটপে সত্যিকারের run (Python 3.14.4, scikit-learn 1.8.0, পাতার version-এর সমান; একটি run):

~/bazarghar-ml$ python extrapolate.py
Python 3.14.4, scikit-learn 1.8.0
km (no rain):            2      5     10     14     15     18     20     25
true mean minutes:    21.4   31.0   47.0   59.8   63.0   72.6   79.0   95.0
linear               21.7   31.0   46.5   58.8   61.9   71.2   77.3   92.8
random forest        26.5   28.7   46.3   62.7   61.5   61.5   61.5   61.5
extra trees          27.3   28.6   50.2   64.5   61.3   61.3   61.3   61.3

MAE (minutes)       1-15 km   15-25 km
linear                 3.14      3.38
random forest          3.70     17.60
extra trees            3.85     16.97
max prediction of the forest anywhere: 69.2  (largest training target 77.2)

১৫ km-এর পরে forest সমতল, linear model রেখা ধরে এগোয়

বৃষ্টি নেই; ওপরের সত্যিকারের run-এর সংখ্যা থেকে আঁকা

Random forest regression cannot extrapolate beyond the training range 0510 152025 দূরত্ব (km) 204060 80100 train: ১–১৫ km linear 92.8 forest: 61.5 (সমতল) আসল 95
ছবি ৩৩.২ — Train-এর পরিসরের ভেতরে forest আর linear model দুটিই ভালো (MAE ৩.৭ আর ৩.১ মিনিট)। বাইরে forest একটি সমতল রেখা: ২৫ km-এ আসল ৯৫ মিনিট, forest বলে ৬১.৫.

সংখ্যাগুলো স্পষ্ট। ১–১৫ km-এ তিনটি model-এর MAE (mean absolute error, গড় পরম ভুল) ৩.১ থেকে ৩.৯ মিনিট। ১৫–২৫ km-এ linear model ৩.৪, forest ১৭.৬, extra-trees ১৭.০: প্রায় পাঁচ গুণ খারাপ। ১৫ km-এর পরে forest প্রতিটি দূরত্বে ৬১.৫ বলে, কারণ ১৫ km-এর পাশের পাতাগুলোর গড় সেটিই। ১০০ km আর বৃষ্টিতেও forest বলে ৬৯.২, যা train-এর সবচেয়ে বড় লক্ষ্য ৭৭.২-এর নিচে। খেয়াল করুন ২ km-এও forest ২৬.৫ বলে (আসল ২১.৪): পরিসরের কিনারায় পাতাগুলো একদিকের সারি দিয়েই ভরা, তাই কিনারাতেও পক্ষপাত।

বাস্তবে এর অর্থ: দাম বাড়ছে এমন বাজারে বিক্রির পূর্বাভাস, নতুন এলাকা, বা সময়ের সঙ্গে বাড়তে থাকা যেকোনো সংখ্যায় forest একা নিরাপদ নয়। প্রতিকার: লক্ষ্যকে আপেক্ষিক করা (যেমন মিনিট/km বা আগের মাসের তুলনায় পরিবর্তন), linear অংশ আলাদা মডেল করে forest-কে শুধু residual দেওয়া, বা পর্ব ৫২ (ML দিয়ে forecasting — lag feature, backtesting আর অনেক series একসঙ্গে)-এর মতো lag feature. আর ইনপুট train-এর পরিসরের বাইরে গেলে সতর্কবার্তা দেওয়ার একটি সরল পরীক্ষা production-এ রাখুন।

আরও গভীরে: Breiman ২০০১ সালে দেখান, forest-এর generalization ভুলের একটি ঊর্ধ্বসীমা মোটামুটি ρ̄(1 − s²)/s², যেখানে s গাছগুলোর গড় “শক্তি” (margin) আর ρ̄ তাদের গড় correlation: অংশ ২-এর একই trade-off, classification-এর ভাষায়। Forest দুটি সারির proximity (কত ভাগ গাছে একই পাতায় পড়ে) দিয়ে clustering, outlier খোঁজা আর missing মান ভরাতেও ব্যবহার হয়েছে (Breiman আর Adele Cutler-এর মূল বাস্তবায়নে)। Nicolai Meinshausen ২০০৬ সালে পাতার পুরো বণ্টন রেখে quantile regression forest দেন (পর্ব ২৪-এর quantile-এর সঙ্গে মেলে), আর Stefan Wager, Trevor Hastie ও Bradley Efron ২০১৪ সালে forest-এর অনুমানের জন্য jackknife দিয়ে confidence interval দেন।

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

  • B টি অনুমানের গড়ের variance, প্রতিটি σ², correlation ρ? ρσ² + (1 − ρ)σ²/B; B → ∞ হলে ρσ².
  • Bootstrap নমুনায় কত ভাগ আলাদা সারি? 1 − (1 − 1/n)ⁿ → 1 − 1/e ≈ ০.৬৩২; বাকি ০.৩৬৮ OOB.
  • Bagging আর random forest-এর পার্থক্য? Forest প্রতিটি split-এ শুধু m < p টি এলোমেলো feature দেখে, ফলে গাছের correlation কমে।
  • বেশি গাছে random forest overfit করে? গাছ যোগ করে নয়, ভুল একটি সীমায় স্থির হয়; তবে গভীর গাছ আর গোলমেলে ডেটায় forest overfit করতেই পারে।
  • feature_importances_ কেন ভুল পথে নিতে পারে? Train ডেটায় impurity-ভিত্তিক, high-cardinality feature-এর দিকে ঝোঁকে; held-out ডেটায় permutation importance দিয়ে মেলান।

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

AI assistant-কে “churn-এর জন্য random forest দাও, আর কোন feature সবচেয়ে গুরুত্বপূর্ণ বলো” বললে প্রায়ই সে একটি কাজ-চলা কোড দেয়, যার ফলাফলের দাবিগুলো ভুল পথে নিতে পারে। পরিচিত ভুলগুলো:

AI প্রায়ই যা করেকেন ভুলকীভাবে ধরবেন
feature_importances_-এর bar chart দেখিয়ে “X churn ঘটায়”Train-ভিত্তিক, cardinality-পক্ষপাতী, আর কারণ নয়Held-out ডেটায় permutation importance, একটি এলোমেলো কলাম বসিয়ে তুলনা
max_features="auto" বা পুরোনো argument“auto” 1.3-এ সরানো হয়েছে; এখন errorনিজের version-এর docs-এ argument মিলিয়ে নিন
Train accuracy ১.০ দেখিয়ে “চমৎকার model”গভীর গাছের forest train প্রায় মুখস্থ করেOOB বা CV, আর শেষে তালাবন্ধ test
পুরো ডেটায় scaling বা target encoding, তারপর OOB score রিপোর্টOOB শুধু forest যাচাই করে; আগের ধাপের leakage ধরে নাপুরো pipeline cross-validation-এর ভেতরে
Forest দিয়ে ভবিষ্যতের দাম বা নতুন এলাকার সময় পূর্বাভাসTrain-এর পরিসরের বাইরে সমতলইনপুটের পরিসর যাচাই, linear baseline-এর সঙ্গে তুলনা

AI-কে কোডের সঙ্গে যাচাইও চাইতে বলুন:

আমার কাছে বাজারঘরের ২০,০০০ গ্রাহকের টেবিল আছে: সংখ্যার কলাম, জেলা আর পেমেন্টের ধরন (categorical), আর churn label (scikit-learn 1.8.0). ১. প্রথমে train আর test ভাগ করো, test শেষ পর্যন্ত ছুঁয়ো না। ২. ColumnTransformer + RandomForestClassifier দিয়ে একটি Pipeline দাও; n_estimators, max_features আর min_samples_leaf নিজে লিখে দাও আর কেন, তা বলো। ৩. OOB score আর ৫-fold CV দুটিই দেখাও, আর ব্যাখ্যা করো কোনটি pipeline-এর leakage ধরবে। ৪. feature_importances_ আর held-out ডেটায় permutation_importance পাশাপাশি দাও; একটি এলোমেলো কলাম বসিয়ে দেখাও কোনটি তাকে গুরুত্ব দেয়। ৫. Logistic regression baseline একই fold-এ রাখো। ৬. যে argument ব্যবহার করছ, সেগুলো 1.8.0-এর docs-এ আছে কি না বলো; কোনো warning চাপা দিও না, আর importance-কে কারণ বলে দাবি কোরো না।

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

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

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

প্র ০১Random forest কে আবিষ্কার করেছেন: Ho, Amit ও Geman, না Breiman? একটি ধারণার কৃতিত্ব কীভাবে ভাগ হয়?

সাধারণ বইয়ে “random forest, Breiman ২০০১” লেখা থাকে, কিন্তু ইতিহাস একটু জটিল। Tin Kam Ho ১৯৯৫ সালে একটি সম্মেলনে “Random Decision Forests” নামে কাজ প্রকাশ করেন, যেখানে অনেক গাছ feature-এর এলোমেলো উপসেটে শেখে; ১৯৯৮ সালে তিনি এর বিস্তৃত রূপ “random subspace method” প্রকাশ করেন। Yali Amit আর Donald Geman ১৯৯৭ সালে হাতের লেখা চেনার কাজে প্রতিটি node-এ এলোমেলোভাবে বাছা প্রশ্ন থেকে গাছ বানানোর পদ্ধতি দেন। Thomas Dietterich ২০০০ সালের দিকে split-এর সিদ্ধান্তেই এলোমেলোতা যোগ করে তুলনা করেন।

Leo Breiman এই সূত্রগুলো একসঙ্গে করেন: নিজের ১৯৯৬ সালের bagging (bootstrap নমুনা), প্রতিটি node-এ এলোমেলো feature (Amit ও Geman-এর ধারণার কাছাকাছি), আর OOB অনুমান। ২০০১ সালের প্রবন্ধে তিনি এর একটি তাত্ত্বিক কাঠামোও দেন: গাছ বাড়ালে ভুল একটি সীমায় স্থির হয়, আর সেই সীমা গাছগুলোর শক্তি আর correlation-এর ওপর নির্ভর করে। Adele Cutler-এর সঙ্গে তিনি একটি ব্যবহারযোগ্য বাস্তবায়ন, importance আর proximity-র মতো সরঞ্জামও দেন। “Random Forests” নামটি তাঁদের trademark হিসেবেও নিবন্ধিত হয়েছিল বলে জানা যায়।

তাহলে কৃতিত্ব কার? বিজ্ঞানে প্রায়ই একটি ধারণা কয়েকজনের কাজে একসঙ্গে জন্মায়, আর যিনি সেগুলো একটি পরিষ্কার, ব্যবহারযোগ্য, তাত্ত্বিকভাবে ব্যাখ্যা করা প্যাকেজে আনেন, তাঁর নাম টিকে যায়। এটি অন্যায্যও হতে পারে; Ho-এর কাজ অনেক পাঠ্যবইয়ে শুধু পাদটীকা। আবার Breiman-এর অবদানও শুধু জোড়া লাগানো নয়: bagging আর OOB তাঁর নিজের, আর ρ ও শক্তির trade-off-এর বিশ্লেষণ পরের সব কাজের ভিত্তি।

আপনার নিজের কাজের জন্য শিক্ষা: সাহিত্য পর্যালোচনায় শুধু সবচেয়ে বিখ্যাত প্রবন্ধ নয়, তার আগের কাজও খুঁজুন আর উল্লেখ করুন। আর কোনো ইতিহাসের দাবি (“X আবিষ্কার করেছেন”) মূল প্রবন্ধ আর তারিখ দেখে লিখুন, AI assistant-এর এক লাইনের উত্তর থেকে নয়।

মূল উপলব্ধি: random forest কয়েকটি স্বাধীন ধারার মিলন: Ho-এর random subspace, Amit ও Geman-এর এলোমেলো node, আর Breiman-এর bagging, OOB ও তত্ত্ব; নাম একজনের, ধারণা অনেকের।

প্র ০২“Random forest overfit করে না” — Breiman-এর এই কথাটি কতটা সত্য? গাছ বাড়ানো আর গাছ গভীর করার পার্থক্য কী?

Breiman ২০০১ সালের প্রবন্ধে law of large numbers ব্যবহার করে দেখান, গাছের সংখ্যা অসীমের দিকে গেলে forest-এর generalization ভুল একটি নির্দিষ্ট সীমায় পৌঁছায়। তাঁর ভাষায় forest “does not overfit” যখন আরও গাছ যোগ হয়। কথাটি সত্য, কিন্তু একটি নির্দিষ্ট অর্থে: গাছের সংখ্যা বাড়ানো overfitting বাড়ায় না। অংশ ৬-এর ছবিতে দেখেছেন, প্রায় ২০টি গাছের পরে test accuracy সমতল; ৬০টিতে খারাপ হয়নি। এটি boosting থেকে আলাদা, যেখানে প্রতিটি নতুন গাছ আগের ভুল সংশোধন করে আর অনেক ধাপে সত্যিই overfit করতে পারে (পর্ব ৩৫-এ দেখবেন)।

কিন্তু forest নিজে overfit করতে পারে না, এ দাবি ভুল। যে সীমায় ভুল স্থির হয়, সেটি গাছগুলোর গভীরতা, max_features আর ডেটার গোলমালের ওপর নির্ভর করে। Mark Segal ২০০৪ সালের একটি প্রতিবেদনে regression-এ দেখান যে পাতার আকার বাড়ালে কিছু ডেটায় ভুল কমে; Hastie, Tibshirani ও Friedman-এর বইও একই কথা বলে, আর যোগ করে যে প্রভাব সাধারণত ছোট। এই পর্বের চতুর্থ ব্লকেও min_samples_leaf ১ থেকে ২০ করায় এলোমেলো ID থাকা অবস্থায় test accuracy বেড়েছে; সেখানে গভীর গাছ গোলমাল আর অদরকারি কলাম মুখস্থ করছিল।

আরেকটি সূক্ষ্মতা: forest-এর train accuracy প্রায় ১.০, আর OOB বা test অনেক কম। এই ফাঁক দেখে overfitting ভাবা সহজ, কিন্তু forest-এর জন্য এটি স্বাভাবিক, কারণ প্রতিটি গাছ নিজের নমুনা মুখস্থ করে। প্রশ্ন হলো test ভুল কমানো যায় কি না, ফাঁক নয়।

ব্যবহারিক নিয়ম: গাছের সংখ্যা “যথেষ্ট” রাখুন (রেখা সমতল হওয়া পর্যন্ত), সেখানে tuning-এর দরকার নেই। Tuning-এর বাজেট দিন max_features আর পাতার আকারে, OOB বা CV দিয়ে। আর মনে রাখুন forest-এর ডিফল্ট প্রায়ই ভালো, তাই বড় tuning-এ লাভ প্রায়ই ছোট; পর্ব ৩৮ (Tabular ডেটায় model বাছাই — tree, linear না neural, আর tuning-এর বাজেট) এ নিয়ে বিস্তারিত।

মূল উপলব্ধি: গাছ যোগ করলে forest overfit করে না, ভুল একটি সীমায় স্থির হয়; কিন্তু সেই সীমা নিজেই গভীরতা আর max_features-এর ওপর নির্ভর করে, আর সেখানে overfitting সম্ভব।

প্র ০৩জীববিজ্ঞানে random forest-এর importance দিয়ে “গুরুত্বপূর্ণ জিন” বাছাই নিয়ে কেন বিতর্ক হয়েছিল? correlated feature-এ importance কী করে?

২০০০-এর দশকে জিনের অভিব্যক্তি বা SNP ডেটায় হাজার হাজার feature আর অল্প নমুনা নিয়ে কাজ করতে random forest খুব জনপ্রিয় হয়, আর তার importance দিয়ে “রোগের সঙ্গে জড়িত জিন” বাছার চল শুরু হয়। Carolin Strobl, Anne-Laure Boulesteix, Achim Zeileis আর Torsten Hothorn ২০০৭ সালে দেখান, Gini-ভিত্তিক importance ভিন্ন সংখ্যক বিভাগ বা মানের feature-এর মধ্যে পক্ষপাতী: বেশি বিভাগের feature শুধু সুযোগের জোরে বেশি গুরুত্ব পায়। এই পর্বের চতুর্থ ব্লকের customer_id ঠিক এই ঘটনা: অর্থহীন কলাম ০.০৯২ MDI পেয়ে অভিযোগ আর জেলার ওপরে উঠেছে।

তাঁরা সমাধান হিসেবে conditional inference tree-ভিত্তিক forest আর subsampling (ফেরত না রেখে) প্রস্তাব করেন, যেখানে split বাছাই পরিসংখ্যানগত পরীক্ষার ওপর নির্ভর করে। ২০০৮ সালে Strobl ও কয়েকজন সহকর্মীর আরেকটি প্রবন্ধ correlated feature-এর সমস্যা তোলে: permutation importance একটি feature এলোমেলো করার সময় তার correlated সঙ্গীদের সঙ্গে সম্পর্কও ভেঙে দেয়, ফলে এমন সারি তৈরি হয় যা বাস্তবে নেই, আর importance ফুলে উঠতে পারে। তাঁরা “conditional permutation importance” প্রস্তাব করেন, যেখানে সঙ্গী feature-এর মান অনুযায়ী দলের ভেতরেই এলোমেলো করা হয়।

আরেকটি দিক: দুটি জিন একই তথ্য বহন করলে MDI গুরুত্ব তাদের মধ্যে ভাগ হয়ে যায়, প্রতিটির কম দেখায়; আবার permutation importance-এ একটি সরালে অন্যটি কাজ চালিয়ে নেয়। তাই “importance-এর ক্রমে প্রথম দশ” তালিকা নমুনা আর seed বদলালেই অনেক বদলে যেতে পারে। Miron Kursa আর Witold Rudnicki-এর Boruta (২০১০) প্রতিটি feature-এর এলোমেলো “ছায়া” কপি বানিয়ে তুলনা করে, ঠিক যেমন এই পর্বে এলোমেলো ID দিয়ে একটি তুলনার মেঝে বানিয়েছেন।

বাজারঘরের জন্য শিক্ষা: importance দিয়ে ব্যবসার সিদ্ধান্ত (“দেরিতে ডেলিভারি কমালে churn কমবে”) নিতে চাইলে মনে রাখুন importance model-এর নির্ভরতা মাপে, কারণ নয়। কারণ জানতে পরীক্ষা (A/B test) বা causal পদ্ধতি লাগে।

মূল উপলব্ধি: MDI cardinality-পক্ষপাতী, permutation importance correlated feature-এ বিভ্রান্ত হতে পারে; এলোমেলো তুলনার কলাম, একাধিক seed আর সতর্ক ভাষা ছাড়া importance-এর তালিকা প্রকাশ করবেন না।

প্র ০৪OOB error কি cross-validation-এর পুরো বিকল্প? কোন অবস্থায় OOB বিশ্বাস করবেন আর কোন অবস্থায় করবেন না?

Breiman ১৯৯৬ সালেই একটি প্রতিবেদনে OOB অনুমানের প্রস্তাব দেন, আর দেখান যে এটি আলাদা test set-এর মতো প্রায় নিরপেক্ষ, কিন্তু খরচ শূন্য। ১৯৯০-এর দশকের কম্পিউটারে যেখানে প্রতিটি বাড়তি fit দামি ছিল, সেখানে এটি বড় সুবিধা। আজও বড় ডেটায় forest-এর ১০টি fit (৫-fold CV, দুটি hyperparameter) ঘণ্টা নিতে পারে, তাই OOB দিয়ে দ্রুত বাছাই খুব কাজের।

কিন্তু OOB-এর তিনটি পরিচিত দুর্বলতা আছে। প্রথম, প্রতিটি সারির OOB অনুমান প্রায় ৩৭% গাছের একটি ছোট forest থেকে আসে। গাছ কম হলে সেই ছোট forest-এর ভুল বেশি, তাই OOB হতাশাবাদী; অংশ ৬-এ max_features=1-এ OOB আর test-এর বড় ফাঁক এর উদাহরণ। কিছু গবেষণায় (যেমন Silke Janitza আর Roman Hornung, ২০১৮) দেখা গেছে, বিশেষ করে অল্প নমুনা আর দুর্বল সংকেতে OOB ভুলকে বাড়িয়ে দেখাতে পারে। দ্বিতীয়, OOB শুধু forest-এর নিজের fit যাচাই করে; তার আগের সব ধাপ (imputation, encoding, feature বাছাই) যদি পুরো ডেটায় শেখা হয়, সেই leakage OOB-তে লুকিয়ে থাকে। তৃতীয়, OOB সারিগুলো এলোমেলো, তাই সময়ের ক্রম বা একই গ্রাহকের অনেক সারি থাকলে প্রায়-নকল সারি train আর OOB-তে ছড়িয়ে যায়।

তাহলে কখন বিশ্বাস করবেন? সারিগুলো মোটামুটি স্বাধীন, preprocessing নেই বা forest-এর ভেতরেই, আর গাছ যথেষ্ট (কয়েকশো), তখন OOB দ্রুত, নির্ভরযোগ্য প্রথম অনুমান, বিশেষ করে max_features বা পাতার আকারের মতো ভেতরের knob বাছতে। সময়ভিত্তিক ডেটা, গ্রাহকভিত্তিক দল, জটিল pipeline, বা অন্য model-এর সঙ্গে ন্যায্য তুলনা লাগলে একই fold-এ CV (পর্ব ২২-এর পরীক্ষা সহ)। আর দুটির মাঝে বড় পার্থক্য দেখলে তা নিজেই একটি সতর্কসংকেত: হয় গাছ কম, নয় কোথাও leakage.

মূল উপলব্ধি: OOB হলো forest-এর বিনামূল্যের ভেতরের যাচাই; স্বাধীন সারি আর যথেষ্ট গাছে ভালো, কিন্তু pipeline, সময় বা দলের গঠন থাকলে cross-validation আর তালাবন্ধ test-এর বিকল্প নয়।

প্র ০৫একটি random forest production-এ নিতে কী কী খরচ আর ঝুঁকি ভাবতে হয়? মূল্যস্ফীতির সময়ে দামের পূর্বাভাসে forest কেন বিপদে পড়তে পারে?

প্রথম খরচ আকার। Pruning ছাড়া গভীর গাছে পাতার সংখ্যা প্রায় train সারির কাছাকাছি হতে পারে; কয়েক লাখ সারি আর কয়েকশো গাছে model ফাইল সহজেই কয়েকশো MB হয়। প্রতিটি prediction সব গাছ ঘুরে আসে, তাই latency গাছ আর গভীরতার সঙ্গে বাড়ে। min_samples_leaf বাড়ানো, max_samples কমানো, বা গাছ কমানো প্রায়ই accuracy প্রায় না কমিয়ে আকার অনেক কমায়; মেপে দেখুন। scikit-learn-এর model pickle দিয়ে রাখলে version মিলিয়ে load করতে হয়, এটিও একটি রক্ষণাবেক্ষণের খরচ।

দ্বিতীয় ঝুঁকি extrapolation, অংশ ৮-এর মতো। ধরুন বাজারঘর গত দুই বছরের দাম দিয়ে একটি forest শেখাল একটি পণ্যের আগামী মাসের বিক্রয়মূল্য অনুমানে। মূল্যস্ফীতিতে দাম train-এর সবচেয়ে বড় মানের ওপরে চলে গেলে forest কখনো সেই মান বলতে পারবে না; সে সবসময় দেখা দামের মধ্যেই থাকবে, আর ভুলটি নিঃশব্দে বাড়বে। Linear model অন্তত প্রবণতা ধরে এগোয় (যদিও সেটিও ভুল হতে পারে)। প্রতিকার: লক্ষ্যকে আপেক্ষিক করা (পরিবর্তনের হার), মূল্যসূচক দিয়ে প্রকৃত মূল্যে নেওয়া, বা পর্ব ৫২-এর forecasting কৌশল।

তৃতীয় ঝুঁকি ডেটার পরিবর্তন, যাকে drift বলা হয়। নতুন জেলা কোড, নতুন courier, বা একটি কলামের একক বদলালে (টাকা থেকে হাজার টাকা) forest কোনো error দেয় না, শুধু চুপচাপ ভুল পাতায় পাঠায়। ইনপুটের পরিসর আর বণ্টনের ওপর নজরদারি লাগে; পর্ব ৬৯ (ML system-এর test আর নজরদারি — data test, model test আর drift)-এ বিস্তারিত।

চতুর্থ দিক অনিশ্চয়তা। Forest-এর গাছগুলোর অনুমানের ছড়ানো দেখে “অনিশ্চয়তা” বলা লোভনীয়, কিন্তু সেটি সঠিক interval নয়। Quantile regression forest বা পর্ব ৫৮ (Conformal prediction — নিশ্চয়তাসহ অনুমানের পরিসর) এর জন্য ভালো পথ।

মূল উপলব্ধি: forest-এর accuracy-র পাশে আকার, latency, version আর drift-এর খরচ আছে; আর সময়ের সঙ্গে বাড়তে থাকা লক্ষ্যে সে নিঃশব্দে পুরোনো পরিসরে আটকে থাকে, তাই ইনপুটের পরিসর পাহারা দিন।

অনুশীলন

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

  1. পূর্ণ numerical: একটি regression forest-এর প্রতিটি গাছের একটি সারির অনুমানের variance σ² = ৪ (মিনিট²), আর যেকোনো দুটি গাছের correlation ρ = ০.২৫. (ক) B = ৫০ গাছের গড়ের variance কত? (খ) B → ∞ হলে? (গ) গড়ের variance মেঝের চেয়ে ৫%-এর বেশি না রাখতে কমপক্ষে কত গাছ? (ঘ) n = ৫০০ সারির train-এ একটি bootstrap নমুনায় প্রত্যাশিত আলাদা সারি আর OOB সারি কত?

    (ক) ρσ² + (1 − ρ)σ²/B = ০.২৫ × ৪ + ০.৭৫ × ৪/৫০ = ১ + ০.০৬ = ১.০৬.

    (খ) দ্বিতীয় পদ শূন্য, বাকি ρσ² = ১.০০. একক গাছের ৪-এর তুলনায় চার গুণ কম, কিন্তু আর নামবে না।

    (গ) শর্ত (1 − ρ)σ²/B ≤ ০.০৫ × ρσ², অর্থাৎ ৩/B ≤ ০.০৫, তাই B ≥ ৬০.

    (ঘ) আলাদা সারি ৫০০ × [১ − (১ − ১/৫০০)৫০০] ≈ ৫০০ × ০.৬৩২৫ ≈ ৩১৬; OOB ≈ ৫০০ × ০.৩৬৭৫ ≈ ১৮৪.

  2. সমালোচনা ও নকশা: একজন বিশ্লেষক রিপোর্টে লিখেছেন: “পুরো ৫০,০০০ সারিতে target encoding করে RandomForestClassifier(n_estimators=10, oob_score=True) শিখিয়েছি; OOB ০.৯৪, তাই আলাদা test লাগেনি। feature_importances_ অনুযায়ী order_id দ্বিতীয় গুরুত্বপূর্ণ, তাই অর্ডার নম্বর churn-এর একটি চালক। Model দিয়ে আগামী বছরের গড় ঝুড়ির টাকাও অনুমান করব।” কী কী সমস্যা, আর ভালো নকশা কী?

    সমস্যা: (১) Target encoding পুরো ডেটায় শেখা, তাই লক্ষ্যের তথ্য feature-এ ঢুকেছে; OOB এই leakage ধরতে পারে না, ০.৯৪ আশাবাদী। (২) মাত্র ১০টি গাছ: প্রতিটি সারির OOB অনুমান গড়ে প্রায় ৩.৭টি গাছ থেকে, আর প্রায় ১% সারির (০.৬৩২¹⁰) কোনো OOB অনুমানই নেই। (৩) order_id high-cardinality আর অর্থহীন; MDI-এর পক্ষপাত, “চালক” দাবি ভুল। ID যদি সময়ের সঙ্গে বাড়ে, তবে এটি সময়ের leakage-এর লক্ষণও হতে পারে। (৪) আগামী বছরের টাকা train-এর পরিসরের বাইরে যেতে পারে; forest সেখানে সমতল।

    ভালো নকশা: আগে তালাবন্ধ test (সময় থাকলে সময় অনুযায়ী)। Encoding আর forest এক Pipeline-এ, ৫-fold CV দিয়ে মূল্যায়ন, কয়েকশো গাছ। ID কলাম বাদ। Held-out ডেটায় permutation importance, একটি এলোমেলো কলামের সঙ্গে তুলনা, আর importance-কে কারণ না বলা। টাকার পূর্বাভাসে আপেক্ষিক লক্ষ্য বা linear/forecasting পদ্ধতি, পাশে baseline.

  3. আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড মোটামুটি কী ছাপবে, বিশেষ করে শেষ সংখ্যাটির সীমা কত। (সংকেত: train-এর লক্ষ্য ২ থেকে ২০.)
    import numpy as np
    from sklearn.ensemble import RandomForestRegressor
    X = np.arange(1, 11).reshape(-1, 1)
    y = 2 * X.ravel()
    m = RandomForestRegressor(n_estimators=50, random_state=0).fit(X, y)
    print(m.predict([[0], [5.5], [100]]).round(1))
    [ 2.8 10.5 19.2]

    ৫.৫-এ আসল রেখা ১১, forest ১০.৫, ভেতরে ভালো। ০-তে আসল ০, forest ২.৮: বাঁ কিনারার পাতাগুলো x = ১, ২-এর মতো সারির গড়। ১০০-তে আসল ২০০, forest মাত্র ১৯.২: কোনো অনুমান train-এর সবচেয়ে বড় লক্ষ্য ২০-এর ওপরে যেতে পারে না। ২০-ও নয়, কারণ যেসব গাছের bootstrap নমুনায় x = ১০ ওঠেনি, তাদের ডান কিনারার পাতা ১৮ বা তার কম বলে। (ডেস্কটপে scikit-learn 1.8.0-এ চালিয়ে মেলানো।)

  4. কোড লিখুন: অংশ ৫-এর make_churn দিয়ে n = ৮০০ train আর ২,০০০ test নিন। RandomForestClassifier(max_features=2)-এর n_estimators = ১০, ৩০, ১০০, ৩০০-এ OOB আর test accuracy ছাপুন। দেখান যে কম গাছে OOB test-এর চেয়ে বেশি হতাশাবাদী, আর গাছ বাড়লে ফাঁক কমে। ফাঁক কি পুরোপুরি মোছে?
    import warnings
    from sklearn.ensemble import RandomForestClassifier
    from bazar_churn import make_churn
    X, y = make_churn(800, seed=1)
    Xte, yte = make_churn(2000, seed=2)
    for B in [10, 30, 100, 300]:
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")      # B = 10-এ কিছু সারির OOB নেই
            rf = RandomForestClassifier(n_estimators=B, max_features=2,
                                        oob_score=True, random_state=0).fit(X, y)
        print(f"B = {B:3d}  OOB {rf.oob_score_:.4f}  test {rf.score(Xte, yte):.4f}"
              f"  gap {rf.score(Xte, yte) - rf.oob_score_:+.4f}")

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

    B =  10  OOB 0.7125  test 0.7825  gap +0.0700
    B =  30  OOB 0.7712  test 0.8070  gap +0.0358
    B = 100  OOB 0.7913  test 0.8150  gap +0.0237
    B = 300  OOB 0.7887  test 0.8120  gap +0.0233

    ১০টি গাছে OOB ০.৭১২৫, test ০.৭৮২৫: ফাঁক ৭ শতাংশ বিন্দু, কারণ প্রতিটি সারির OOB অনুমান গড়ে মাত্র ৩–৪টি গাছের। ৩০টিতে ফাঁক ৩.৬, ১০০টিতে ২.৪. কিন্তু ১০০ থেকে ৩০০-তে আর কমে না (২.৩)। বাকি ফাঁক গাছের সংখ্যার নয়, নমুনার: ৮০০ সারির accuracy-র standard error প্রায় √(০.৮ × ০.২/৮০০) ≈ ০.০১৪, আর ৫-fold CV-ও (অংশ ৫) একই দিকে কম বলেছিল। অর্থাৎ OOB-এর হতাশাবাদ কম গাছের সমস্যা, আর বাকিটা এই train আর test-এর ভাগ্য।

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

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

ক্রমএকটি random forest classifier শেখানো আর মাপার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. প্রতিটি গাছের জন্য n টি সারি ফেরত রেখে টেনে একটি bootstrap নমুনা নেওয়া
  2. গাছের প্রতিটি node-এ p টি feature থেকে এলোমেলো m টি বাছা
  3. সেই m টির মধ্যে সেরা split দিয়ে node ভাগ করা, pruning ছাড়া গাছ বাড়ানো
  4. B টি গাছের সম্ভাবনার গড় নিয়ে নতুন সারির অনুমান
  5. প্রতিটি সারির জন্য যেসব গাছ সারিটি দেখেনি, শুধু তাদের গড় দিয়ে OOB score
  6. তালাবন্ধ test-এ একবার চূড়ান্ত accuracy মাপা

১০টি অনুমানের প্রতিটির variance ১ আর যেকোনো দুটির correlation ০.৩. তাদের গড়ের variance কত?

  • ০.১০
  • ০.৩৭
  • ০.৩০
  • ০.৭৩

n খুব বড় হলে একটি bootstrap নমুনায় (n টি সারি থেকে ফেরত রেখে n বার টানা) গড়ে কত ভাগ আলাদা সারি থাকে?

  • প্রায় ১০০%
  • প্রায় ৫০%
  • প্রায় ৬৩.২%
  • প্রায় ৩৬.৮%

Random forest-এ max_features কমালে সাধারণত কী হয়?

  • গাছগুলোর correlation কমে, কিন্তু একক গাছ দুর্বল হয়
  • একক গাছ শক্তিশালী হয় আর correlation বাড়ে
  • গাছের সংখ্যা কমে যায়
  • Bootstrap বন্ধ হয়ে OOB score পাওয়া যায় না

একটি churn ডেটায় প্রতিটি গ্রাহকের একটি এলোমেলো ID কলাম যোগ করা হলো। Random forest-এর feature_importances_ সেই ID-কে মাঝারি গুরুত্ব দেখায়, কিন্তু test ডেটায় permutation importance প্রায় শূন্য। সবচেয়ে ভালো ব্যাখ্যা কী?

  • ID আসলে churn-এর একটি লুকানো চালক
  • Permutation importance ভুল, কারণ সে ID-এর মান এলোমেলো করেছে
  • গাছের সংখ্যা কম বলে forest ভুলভাবে শিখেছে
  • Impurity importance train-ভিত্তিক আর high-cardinality কলামের দিকে পক্ষপাতী

একটি RandomForestRegressor ১–১৫ km দূরত্বের ডেলিভারিতে শেখানো; train-এ সবচেয়ে বড় সময় ৭৭ মিনিট। ২৫ km-এর অর্ডারে (আসল প্রায় ৯৫ মিনিট) সে কী বলবে?

  • প্রায় ৯৫, কারণ forest প্রবণতা শেখে
  • ১৫ km-এর কাছের অনুমানের মতো, ৭৭-এর বেশি কখনো নয়
  • ValueError, কারণ ইনপুট পরিসরের বাইরে
  • ০, কারণ এই এলাকায় কোনো train সারি নেই

কোন অবস্থায় random forest-এর OOB score-কে cross-validation-এর বদলে চূড়ান্ত মূল্যায়ন হিসেবে নেওয়া সবচেয়ে বিপজ্জনক?

  • সারিগুলো স্বাধীন, ৫০০টি গাছ, কোনো preprocessing নেই
  • শুধু max_features বাছতে OOB ব্যবহার
  • পুরো ডেটায় target encoding করার পরে forest-এর OOB score
  • বড় ডেটা, যেখানে ৫-fold CV খুব ধীর

শব্দকোষ

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

মনে রাখুন

  1. গড়ের সূত্র: Var(T̄) = ρσ² + (1 − ρ)σ²/B; গাছ বাড়ালে দ্বিতীয় পদ মোছে, ρσ² মেঝে থাকে। তাই ensemble-এর লাভ গাছের মধ্যে বৈচিত্র্যে।
  2. Bootstrap আর OOB: প্রতিটি নমুনায় ৬৩.২% আলাদা সারি, ৩৬.৮% OOB; OOB score বাড়তি fit ছাড়াই, কিন্তু কম গাছে হতাশাবাদী আর pipeline-এর leakage ধরে না।
  3. Random forest: প্রতিটি node-এ m টি এলোমেলো feature; ρ কমে, একক গাছ দুর্বল হয়। max_features সবচেয়ে গুরুত্বপূর্ণ knob, গাছের সংখ্যা শুধু “যথেষ্ট” হলেই হলো।
  4. Importance: MDI train-ভিত্তিক আর high-cardinality-পক্ষপাতী; held-out permutation importance আর একটি এলোমেলো তুলনার কলাম দিয়ে মেলান; কোনোটিই কারণ নয়।
  5. Extrapolation: regression forest আর extra-trees train-এর লক্ষ্যের পরিসরের বাইরে যেতে পারে না; নতুন পরিসরে linear baseline আর ইনপুট-পাহারা রাখুন।

পরের পর্বে: পর্ব ৩৪ (Boosting-এর ধারণা — AdaBoost আর weak learner) নতুন পথ দেখাবে। Bagging স্বাধীন গাছের গড় নিয়ে variance কমায়; boosting উল্টো পথে যায়, ছোট দুর্বল গাছ একটির পর একটি যোগ করে, প্রতিটি আগের ভুলে বেশি মন দিয়ে, আর তাতে bias কমে।

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

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