এই পর্বে যা শিখবেন
- Bootstrap-এর plug-in মূলনীতি, তার চারটি শর্ত, আর ৬৩.২%-এর হিসাব
- ১০০০টি bootstrap মান থেকে percentile, basic আর BCa interval হাতে বের করা, আর jackknife-এর সঙ্গে সম্পর্ক
scipy.stats.bootstrapদিয়ে interval বানিয়ে simulation-এ তার coverage যাচাই করা- Logistic regression-এর coefficient-এর bootstrap SE বনাম statsmodels-এর analytic SE, আর শুধু metric বনাম পুরো pipeline
- Autocorrelated দৈনিক বিক্রিতে block bootstrap, আর sample maximum ও ভারী লেজে bootstrap কেন ব্যর্থ হয়
Bootstrap আপনার কাছে নতুন নয়। AI-এর ভিত্তি কোর্সের পর্ব ৩০-এ seaborn-এর error bar-এর পেছনে ছয়টি ডেলিভারির bootstrap হাতে করেছেন, পর্ব ৩৭-এ দেখেছেন বাঁকা ডেটায় t-interval-এর “৯৫%” আসলে প্রায় ৮৯%, আর পর্ব ৬০-এ F1-এর bootstrap CI বানিয়েছেন। এই কোর্সের পর্ব ০৪-এ একটি test set-এর AUC-র bootstrap interval ছিল। এবার গভীরে: bootstrap কেন কাজ করে, কোন interval কখন বিশ্বাসযোগ্য, ঠিক কী resample করবেন, আর কখন এটি আত্মবিশ্বাসের সঙ্গে ভুল বলে? চারটি চালানো যায় এমন ব্লক পাতাতেই চলে (ব্রাউজারের Python-এ NumPy, SciPy, statsmodels আর scikit-learn); প্রথমবার প্যাকেজ নামতে কয়েক সেকেন্ড লাগে, scikit-learn আর statsmodels-এ প্রায় দশ সেকেন্ড।
১ · Bootstrap-এর মূলনীতি — নমুনাকেই জনসংখ্যা ধরা
পাইকারি বাজারে চালের এক বস্তা কেনার আগে ব্যবসায়ী এক মুঠো চাল হাতে নিয়ে দেখেন কত ভাগ ভাঙা দানা। প্রশ্ন হলো, এই এক মুঠোর হিসাব কতটা নির্ভরযোগ্য? আরও বস্তা খোলার সুযোগ নেই। কৌশল: হাতের মুঠোর চালকেই “ছোট বস্তা” ধরে তা থেকে বারবার মুঠো তুলে (প্রতিবার দানা ফেরত রেখে) দেখা, ভাঙা দানার ভাগ মুঠোভেদে কতটা ওঠানামা করে। এই ওঠানামা আসল বস্তা থেকে নতুন মুঠো নিলে যতটা হতো, তার একটি অনুমান। কৌশলটি কাজ করে কেবল যদি মুঠোটি বস্তার একটি ভালো প্রতিচ্ছবি হয়।
একটু আনুষ্ঠানিক ভাষায়। জনসংখ্যার বণ্টন F অজানা, আর আমরা চাই একটি পরিমাণ θ = t(F), যেমন ডেলিভারির সময়ের median. হাতে n টি স্বাধীন পর্যবেক্ষণ x1, …, xn, আর তা থেকে অনুমান θ̂ = s(x). প্রশ্ন: নতুন নমুনা নিলে θ̂ কতটা নড়ত? এই নড়াচড়ার বণ্টনের নাম sampling distribution, আর তার standard deviation হলো standard error. গড়ের জন্য সূত্র আছে (s/√n), কিন্তু median, AUC বা পুরো pipeline-এর score-এর জন্য সহজ সূত্র নেই।
Bradley Efron ১৯৭৯ সালে Annals of Statistics-এ যে উত্তর প্রকাশ করেন, তার মূল কথা plug-in principle: অজানা F-এর জায়গায় বসান F̂, অর্থাৎ নমুনার empirical distribution, যেখানে প্রতিটি পর্যবেক্ষণের সম্ভাবনা 1/n. F̂ থেকে n টি মান টানা মানে নমুনা থেকে ফেরত রেখে n টি মান টানা; এর নাম bootstrap resample x*. তার ওপর একই পরিসংখ্যান θ̂* = s(x*). B বার করলে θ̂*-এর ছড়ানো θ̂-এর sampling distribution-এর অনুমান। এটি একটি Monte Carlo হিসাব।
দুই জগৎ: আসল জগতে যা করা যায় না, bootstrap জগতে তা নকল করা
Real world vs bootstrap world: the sample plays the role of the population
চারটি শর্ত মনে রাখুন। (১) Resample-এর একক স্বাধীন: সারিগুলো i.i.d. হতে হবে, অথবা যা স্বাধীন (যেমন গ্রাহক, দিন-এর টুকরো) সেটিকেই resample করতে হবে; অংশ ৬ দেখাবে না মানলে কী হয়। (২) নমুনা প্রতিনিধিত্বমূলক: যা নমুনায় নেই (একটি বিরল ১৫ দিনের দেরি), তা কোনো resample-এ আসবে না। (৩) পরিসংখ্যান মসৃণ: গড়, median, correlation, coefficient মোটামুটি ঠিক আছে; সর্বোচ্চ মানের মতো প্রান্তের পরিসংখ্যানে ব্যর্থ (অংশ ৭)। (৪) n যথেষ্ট: ফলটি n → ∞-এর তত্ত্ব, ছোট n-এ interval সাধারণত সরু।
দুই স্তরের ভুলও আলাদা রাখুন। B বাড়ালে কমে শুধু Monte Carlo ভুল, অর্থাৎ θ̂*-এর বণ্টন কতটা নিখুঁতভাবে আঁকা হলো। কিন্তু F̂ আর F-এর পার্থক্য থেকে আসা ভুল B দিয়ে কমে না, কমে শুধু n বাড়লে। নিচের stepper আটটি ডেলিভারির একটি resample ধাপে ধাপে দেখায়।
- মূল নমুনা। বাজারঘরের আটটি কাল্পনিক ডেলিভারি, দিনে। মাঝের দুটি মান ৩.০ আর ৩.৫, তাই median θ̂ = ৩.২৫. গড় ৪.০, কারণ ৯.৫ দিনের একটি দেরি গড়কে ডানে টানে।
- Index টানা। ০ থেকে ৭-এর মধ্যে ৮টি index, প্রতিটি আলাদাভাবে আর সমান সম্ভাবনায়। একই index আবার আসতে পারে, কারণ টানার পর “ফেরত রাখা” হয়েছে।
- মান বসানো। Index ৫ আর ৬ দুবার করে এসেছে, index ১ আর ৭ একবারও আসেনি। তাই এই resample-এ ৯.৫ দিনের দেরিটি নেই; ৮টির মধ্যে ৬টি আলাদা সারি।
- একই পরিসংখ্যান আবার। সাজালে মাঝের দুটি ৩.৫ আর ৪.০, তাই θ̂* = ৩.৭৫. এটি মূল median থেকে +০.৫০ দূরে। Bootstrap-এর পুরো কাজ হলো এই দূরত্ব হাজারবার মাপা।
- আরও resample আর ৬৩.২%. পরের দুটির median ২.৭৫ আর ২.০০. একটি সারি একটি resample-এ একবারও না আসার সম্ভাবনা (১ − ১/n)n, যা n বড় হলে e−১ ≈ ০.৩৬৮-এর দিকে যায়। তাই গড়ে প্রায় ৬৩.২% আলাদা সারি থাকে; n = ৮-এ ৫.২৫টি, অর্থাৎ ৬৫.৬%. এই সংখ্যাটি অংশ ৫-এ .632 অনুমানের নাম দেবে।
২ · Percentile, basic আর BCa — ১০০০টি সংখ্যা থেকে interval
B টি θ̂* হাতে এলে confidence interval বানানোর কয়েকটি পথ আছে। সবচেয়ে প্রচলিত তিনটি লিখি, ৯৫% (α = ০.০৫) ধরে। qp মানে θ̂*-গুলোর p-তম percentile.
Percentile
[q0.025, q0.975]. Bootstrap মানের মাঝের ৯৫% সরাসরি। সহজ, আর log-এর মতো রূপান্তরে স্থির; কিন্তু θ̂ biased হলে ভুল দিকে সরে থাকে।
Basic (reverse percentile)
[2θ̂ − q0.975, 2θ̂ − q0.025]. যুক্তি: θ̂* − θ̂ ছড়ায় θ̂ − θ-এর মতো, তাই interval θ̂-এর চারপাশে উল্টে যায়, আর ডেটার বাইরেও যেতে পারে।
BCa (bias-corrected and accelerated)
Percentile-ই, তবে ২.৫ আর ৯৭.৫-এর জায়গায় সংশোধিত দুটি percentile নেয়। Efron, ১৯৮৭. দুটি সংখ্যা লাগে: bias-correction z0 আর acceleration a.
BCa-র সূত্র দেখতে ভারী, কিন্তু অংশগুলো সরল। Φ হলো standard normal-এর CDF আর zα/2 = Φ−1(০.০২৫) = −১.৯৬.
z0 = Φ−1(θ̂* < θ̂ এমন ভাগ) α1 = Φ( z0 + (z0 + zα/2) / (1 − a(z0 + zα/2)) )
উপরের প্রান্তের α2-ও একই সূত্রে, zα/2-এর জায়গায় +১.৯৬ বসিয়ে। Interval হলো [qα1, qα2]. z0 মাপে bootstrap মানের মাঝখান θ̂ থেকে কতটা সরে আছে (median bias): ঠিক অর্ধেক θ̂*, θ̂-এর নিচে হলে z0 = ০. a মাপে θ̂-এর standard error θ-এর সঙ্গে কত দ্রুত বদলায় (skew-এর মতো প্রভাব); সাধারণত jackknife থেকে বের করা হয়:
a = ∑ (θ̄(·) − θ̂(i))³ / ( 6 [∑ (θ̄(·) − θ̂(i))²]3/2 )
এখানে θ̂(i) হলো i-তম সারি বাদ দিয়ে বাকি n − 1 টিতে হিসাব করা পরিসংখ্যান, আর θ̄(·) তাদের গড়। z0 = ০ আর a = ০ হলে α1 = Φ(−১.৯৬) = ০.০২৫, অর্থাৎ BCa আর percentile হুবহু এক। নিচের stepper আগের আটটি ডেলিভারির ১০০০টি bootstrap median থেকে তিনটি interval বের করে।
| ধাপ | |
| হিসাব | |
| অর্থ |
- ১০০০টি median. প্রতিটি আলাদা resample-এর median. আটটি মানের median মাত্র কয়েকটি মানই নিতে পারে, তাই ১০০০টির মধ্যে আলাদা মান মাত্র ২০টি। এদের standard deviation ০.৮৯৭, অর্থাৎ median-এর bootstrap SE প্রায় ০.৯০ দিন।
- সাজানো। Percentile বের করতে প্রথমে ছোট থেকে বড় সাজাতে হয়। s[k] মানে সাজানো তালিকার k-তম ঘর, ০ থেকে গোনা।
- ২.৫ percentile. NumPy-র default নিয়মে অবস্থান ০.০২৫ × (১০০০ − ১) = ২৪.৯৭৫, অর্থাৎ s[২৪] আর s[২৫]-এর মাঝে। দুটিই ২.০, তাই নিচের প্রান্ত ২.০.
- ৯৭.৫ percentile. অবস্থান ৯৭৪.০২৫; s[৯৭৪] আর s[৯৭৫] দুটিই ৬.০. অন্য interpolation নিয়মে প্রান্ত সামান্য বদলাতে পারে, বিশেষ করে মান কম আলাদা হলে।
- Percentile interval [২.০, ৬.০]. θ̂ = ৩.২৫ থেকে নিচে ১.২৫ আর ওপরে ২.৭৫: interval অসমান, কারণ ডেলিভারির সময়ের লেজ ডানে। ± SE ধরনের সমান interval এই তথ্য হারাত।
- Basic আর BCa. Basic: ২ × ৩.২৫ − ৬.০ = ০.৫ থেকে ৬.৫ − ২.০ = ৪.৫. অসমানতা উল্টে গেছে, আর ০.৫ দিন আটটি ডেলিভারির সবচেয়ে ছোট মান ১.৫-এরও নিচে। BCa: θ̂*-এর ৪৩.৪% θ̂-এর নিচে আর ১৪.৭% সমান; সমানগুলোর অর্ধেক ধরে ভাগ ০.৫০৭৫, তাই z0 = Φ−1(০.৫০৭৫) ≈ ০.০২. Jackknife median-গুলো শুধু ৩.০ আর ৩.৫ (প্রতিটি চারবার), প্রতিসম, তাই a = ০. ফল প্রায় percentile-এর সমান, আর SciPy-ও এই ডেটায় [২.০, ৬.০] দেয়।
Jackknife — bootstrap-এর আগের পদ্ধতি
উপরের θ̂(i)-গুলোই jackknife: প্রতিবার একটি সারি বাদ দিয়ে n বার হিসাব (Quenouille আর Tukey-র পদ্ধতি; ইতিহাস প্র ০১-এ)। SE-র সূত্র:
SEjack = √( (n − 1)/n · ∑ (θ̂(i) − θ̄(·))² )
গড়ের ক্ষেত্রে এটি হুবহু s/√n দেয়: আটটি ডেলিভারিতে দুটিই ০.৯২৬. খরচ ঠিক n বার হিসাব, কোনো এলোমেলোতা নেই। দুর্বলতা অমসৃণ পরিসংখ্যানে। Median-এর ক্ষেত্রে একটি সারি বাদ দিলে median মাত্র দুটি মানের একটি হয় (এখানে ৩.০ বা ৩.৫), তাই jackknife SE median-এর জন্য অসংগত (inconsistent) বলে পরিচিত। এজন্যই median-এর BCa-তে a প্রায়ই ০ আসে, আর BCa তখন percentile-এর চেয়ে বেশি কিছু দেয় না; অংশ ৩-এর run এটি দেখাবে।
SciPy-তে: scipy.stats.bootstrap
SciPy ১.৭ (২০২১) থেকে এটি আছে। প্রধান argument: data হলো নমুনাগুলোর একটি tuple, তাই একটি নমুনা হলেও (x,); statistic একটি function, যার axis argument থাকলে SciPy একসঙ্গে অনেক resample হিসাব করে (vectorized); n_resamples (default ৯৯৯৯); method হলো "percentile", "basic" বা "BCa" (default BCa); confidence_level; আর এলোমেলোতার জন্য rng. পুরোনো কোডে rng-এর জায়গায় random_state দেখবেন। ফলাফলে confidence_interval, bootstrap_distribution আর standard_error. একটি কাজের কৌশল: bootstrap_result= দিয়ে আগের run-এর bootstrap মানগুলো আবার ব্যবহার করা যায়, আর n_resamples=0 দিলে নতুন resample না টেনে শুধু অন্য পদ্ধতির interval হিসাব হয়।
৩ · কোন interval সত্যিই ৯৫%? — coverage দিয়ে যাচাই
“৯৫% confidence interval” একটি পদ্ধতির প্রতিশ্রুতি: বহুবার নতুন ডেটায় একই পদ্ধতি চালালে প্রায় ৯৫% interval আসল θ ধরবে। এই ভাগের নাম coverage probability. বাস্তবে θ অজানা, কিন্তু simulation-এ আমরা নিজেরাই F বেছে নিই, তাই θ জানা। নিয়ম: (১) জানা F থেকে অনেকগুলো নতুন ডেটাসেট, (২) প্রতিটিতে interval, (৩) কতগুলো θ ধরল তা গোনা। S টি ডেটাসেটে গোনা ভাগের নিজস্ব Monte Carlo ভুল আছে: S = ৩০০ হলে sd ≈ √(০.৯৫ × ০.০৫ / ৩০০) ≈ ০.০১৩, তাই ০.৯৩ আর ০.৯৫-এর পার্থক্য প্রায় এই ভুলের সীমায়।
নিচের ব্লকে ডেলিভারির সময় lognormal: log নিলে normal, μ = log ৩ আর σ = ০.৯. এর আসল median ঠিক ৩.০ দিন আর আসল গড় 3eσ²/2 ≈ ৪.৫০ দিন; গড় median-এর অনেক ওপরে, কারণ লেজ ডানে. প্রথমে একটি মাসের ২৫টি ডেলিভারিতে দুই পরিসংখ্যানের interval, তারপর ৩০০টি নতুন মাসে coverage. BCa-র জন্য নতুন resample না টেনে bootstrap_result দিয়ে একই ৯৯৯টি মান আবার ব্যবহার করা হয়েছে, তাই দুই পদ্ধতির তুলনা ন্যায্য আর সময়ও অর্ধেক।
import numpy as np
from scipy import stats
rng = np.random.default_rng(42) # কাল্পনিক বাজারঘর: ডেলিভারির সময় (দিন)
mu, sig, n = np.log(3.0), 0.9, 25 # lognormal: ডানদিকে লম্বা লেজ
truth = {"median": 3.0, "mean": 3.0 * np.exp(sig**2 / 2)}
fn = {"median": np.median, "mean": np.mean}
x = rng.lognormal(mu, sig, n) # একটি মাসের ২৫টি ডেলিভারি
for s in fn:
r = stats.bootstrap((x,), fn[s], n_resamples=999, method="percentile", rng=1)
rb = stats.bootstrap((x,), fn[s], n_resamples=0, method="BCa", bootstrap_result=r)
print(f"{s:6s} = {fn[s](x):.2f} SE {r.standard_error:.2f} percentile",
np.round(r.confidence_interval, 2), " BCa", np.round(rb.confidence_interval, 2))
S = 300 # coverage: আসল মান জানা, ৩০০টি নতুন মাস
hit = {(s, m): 0 for s in fn for m in ("percentile", "BCa")}
high = dict.fromkeys(hit, 0) # আসল মান interval-এর ওপরে পড়ল কতবার
for k in range(S):
d = rng.lognormal(mu, sig, n)
for s in fn:
r = stats.bootstrap((d,), fn[s], n_resamples=999, method="percentile", rng=k)
for m in ("percentile", "BCa"):
if m == "BCa":
r = stats.bootstrap((d,), fn[s], n_resamples=0, method="BCa",
bootstrap_result=r)
lo, hi = r.confidence_interval
hit[s, m] += lo <= truth[s] <= hi
high[s, m] += truth[s] > hi
print(f"\ntrue median {truth['median']:.2f}, true mean {truth['mean']:.2f}; n = {n}, {S} datasets")
for key in hit:
print(f"{key[0]:6s} {key[1]:10s} coverage {hit[key] / S:.3f} missed-high {high[key]} missed-low {S - hit[key] - high[key]}")আসল আউটপুট (ব্রাউজারের Python):
median = 2.95 SE 0.59 percentile [2.04 4.57] BCa [2.04 4.57]
mean = 3.69 SE 0.48 percentile [2.84 4.64] BCa [2.9 4.71]
true median 3.00, true mean 4.50; n = 25, 300 datasets
median percentile coverage 0.937 missed-high 10 missed-low 9
median BCa coverage 0.930 missed-high 10 missed-low 11
mean percentile coverage 0.870 missed-high 35 missed-low 4
mean BCa coverage 0.870 missed-high 27 missed-low 12
প্রথম মাস: median ২.৯৫, interval [২.০৪, ৪.৫৭], BCa-তেও হুবহু একই, অংশ ২-এর jackknife-যুক্তির মতো। গড়ের ক্ষেত্রে BCa দুই প্রান্তই একটু ডানে সরিয়েছে ([২.৮৪, ৪.৬৪] থেকে [২.৯, ৪.৭১]), কারণ গড়ের sampling distribution-এর লেজ ডানে।
Coverage: median-এ দুই পদ্ধতিই ৯৩–৯৪%, ৯৫%-এর Monte Carlo ভুলের সীমার কাছেই, আর ভুলগুলো দুই দিকে প্রায় সমান (১০ আর ৯). গড়ে দুই পদ্ধতিই মাত্র ৮৭%: “৯৫%” বলা interval আসলে প্রতি আটটির একটিতে ভুল। আরও গুরুত্বপূর্ণ হলো ভুলের দিক: percentile-এর ৩৯টি ভুলের ৩৫টিতে আসল গড় interval-এর ওপরে. কারণ ২৫টি ডেলিভারিতে প্রায়ই লেজের বড় মানগুলো থাকে না, তাই নমুনা-গড় আর তার bootstrap দুটিই নিচে থাকে। BCa ভুলগুলোকে দুই দিকে কিছুটা সমান করেছে (২৭ আর ১২), কিন্তু মোট coverage বাড়াতে পারেনি। অন্য seed-এ সংখ্যা এক-দুই শতাংশ বদলাবে।
শিক্ষা: BCa তাত্ত্বিকভাবে percentile-এর চেয়ে নির্ভুল (n বড় হলে এর coverage-ভুল দ্রুত কমে, যাকে second-order accuracy বলা হয়), কিন্তু ছোট n-এ বাঁকা ডেটার গড়ে কোনো পদ্ধতিই নমুনায় অনুপস্থিত লেজ ফিরিয়ে আনতে পারে না। অংশ ৭-এর ডেস্কটপ run-এ একই lognormal-এ n = ১০০-এ coverage ৯২–৯৩% আর n = ৪০০-এ ৯৩–৯৪% হয়েছে।
৪ · Coefficient-এর অনিশ্চয়তা — analytic SE বনাম bootstrap
AI-এর ভিত্তি কোর্সের পর্ব ৫৯-এ logistic regression-এর coefficient-কে log-odds হিসেবে পড়েছেন। statsmodels-এর Logit প্রতিটি coefficient-এর পাশে একটি standard error দেয়। এটি আসে negative log-likelihood-এর Hessian-এর inverse থেকে (observed information), যার তত্ত্ব পর্ব ১৮ (Maximum likelihood আর MAP — model fit করার মূলনীতি)-এর বিষয়। এই SE থেকে Wald interval: coefficient ± ১.৯৬ × SE. সূত্রটি বড় n-এর তত্ত্ব আর model ঠিক থাকার অনুমানের ওপর দাঁড়ায়।
Regression-এ bootstrap দুইভাবে করা যায়। সারি resample (pairs bootstrap): (x, y) জোড়া একসঙ্গে টেনে প্রতিবার নতুন fit; model-এর রূপ ভুল হলেও কাজ করে। Residual resample: fit করা model রেখে residual এলোমেলো করে নতুন y; linear regression-এ চলে, কিন্তু model ঠিক হওয়ার ওপর নির্ভর করে। Logistic-এ তাই সারি resample. নিচের ব্লক একটি কাল্পনিক ডেটায় (অর্ডার ফেরত আসবে কি না; ছাড়, ডেলিভারির দিন, প্রথম অর্ডার কি না) দুই আকারে, n = ৪০০ আর ৮০, দুই SE পাশাপাশি রাখে।
import numpy as np
import statsmodels.api as sm
from sklearn.linear_model import LogisticRegression
np.set_printoptions(precision=3, suppress=True)
def make(n, seed=7): # কাল্পনিক বাজারঘর: অর্ডার ফেরত আসবে কি না
r = np.random.default_rng(seed)
disc = r.uniform(0, 40, n) # ছাড়, শতাংশ
days = r.poisson(3, n).astype(float) # ডেলিভারির দিন
first = r.binomial(1, 0.3, n).astype(float) # প্রথম অর্ডার?
z = -3.0 + 0.04 * disc + 0.25 * days + 0.8 * first
return np.column_stack([disc, days, first]), r.binomial(1, 1 / (1 + np.exp(-z)))
lr = LogisticRegression(C=1e10, max_iter=1000) # কার্যত শাস্তিহীন fit
for n in (400, 80):
X, y = make(n)
fit = sm.Logit(y, sm.add_constant(X)).fit(disp=0)
boot = np.empty((200, 4)); r = np.random.default_rng(0)
for b in range(200): # প্রতিবার সারি ফেরত রেখে টেনে নতুন করে fit
i = r.integers(0, n, n)
m = lr.fit(X[i], y[i])
boot[b] = np.r_[m.intercept_, m.coef_[0]]
print(f"n = {n}, returns = {y.sum()} [const, disc, days, first]")
print(" coef ", fit.params)
print(" SE analytic ", fit.bse)
print(" SE bootstrap", boot.std(0, ddof=1))
q = np.percentile(boot[:, 2], [2.5, 97.5])
print(" days: Wald CI", fit.conf_int()[2], " percentile CI", q)
print(" largest |days coef| in bootstrap", np.abs(boot[:, 2]).max().round(2))আসল আউটপুট (ব্রাউজারের Python):
n = 400, returns = 118 [const, disc, days, first]
coef [-2.92 0.038 0.251 1.203]
SE analytic [0.374 0.01 0.073 0.24 ]
SE bootstrap [0.36 0.01 0.07 0.248]
days: Wald CI [0.108 0.395] percentile CI [0.122 0.406]
largest |days coef| in bootstrap 0.54
n = 80, returns = 15 [const, disc, days, first]
coef [-4.205 0.009 0.6 1.289]
SE analytic [1.228 0.029 0.21 0.683]
SE bootstrap [25.262 0.21 3.478 7.343]
days: Wald CI [0.189 1.011] percentile CI [0.045 1.549]
largest |days coef| in bootstrap 49.57
n = ৪০০ (১১৮টি ফেরত): দুই SE প্রায় মেলে (days ০.০৭৩ বনাম ০.০৭০, first ০.২৪০ বনাম ০.২৪৮), আর Wald [০.১০৮, ০.৩৯৫] ও percentile [০.১২২, ০.৪০৬] কাছাকাছি। Model ঠিক আর ডেটা যথেষ্ট হলে দুই পথ একই কথা বলে। n = ৮০ (মাত্র ১৫টি ফেরত): days-এর bootstrap SE ৩.৪৭৮, analytic-এর প্রায় ১৭ গুণ। কারণ ২০০টি resample-এর একটিতে coefficient ৪৯.৫৭: সেখানে ফেরত-অর্ডারগুলো প্রায় পুরো আলাদা করা যায় (separation-এর কাছাকাছি), তাই শাস্তিহীন fit-এর ওজন বিশাল। ডেস্কটপে একই কোডে ওই একটি resample বাদ দিয়ে হিসাব করলে sd ০.৩৬২. একটি চরম মান SD-কে নষ্ট করে, percentile-কে কম; তাই percentile interval [০.০৪৫, ১.৫৪৯] বেশি অর্থবহ, যা Wald [০.১৮৯, ১.০১১]-এর চেয়ে চওড়া আর ডানে লম্বা। শিক্ষা: কম event-এ bootstrap-এর SD নয়, বণ্টনটি দেখুন; আর তখন analytic SE-ও আশাবাদী হতে পারে।
Regularize করা model-এর bootstrap interval সাবধানে পড়ুন। Ridge বা lasso-র coefficient ইচ্ছা করে শূন্যের দিকে টানা (পর্ব ১৭), তাই bootstrap-এর ছড়ানো ঠিক থাকলেও interval-এর কেন্দ্র biased, আর “৯৫%” প্রতিশ্রুতি খাটে না। Lasso-র ক্ষেত্রে কোনো coefficient অনেক resample-এ ঠিক শূন্য হয়, তাই তার bootstrap বণ্টন শূন্যে একটি স্তূপ আর বাকিটা ছড়ানো; সেখানে percentile interval-এর চেয়ে “কত ভাগ resample-এ feature-টি বাছা হলো” বেশি অর্থবহ।
৫ · শুধু metric, না পুরো pipeline? — out-of-bag আর .632
Model-এর একটি AUC-র interval দিতে চাইলে প্রথম প্রশ্ন: কোন অনিশ্চয়তা? দুটি আলাদা প্রশ্ন আছে। (ক) “এই fit করা model-টি নতুন গ্রাহকদের ওপর কত AUC দেবে?” এখানে model স্থির, শুধু test-এর গ্রাহকেরা দৈবে এসেছেন; তাই test-এর সারি resample যথেষ্ট, যা পর্ব ০৪-এ করেছেন। (খ) “এই training পদ্ধতি (একই আকারের নতুন train ডেটায় একই pipeline) কত AUC দেবে?” এখানে train ডেটাও দৈবে এসেছে, আর model নিজেও বদলাত। তখন preprocessing থেকে fit পর্যন্ত পুরো pipeline প্রতিটি resample-এ নতুন করে চালাতে হয়। “পদ্ধতি A ভালো” দাবি (খ) ধরনের; একটি নির্দিষ্ট model ছাড়ার সিদ্ধান্ত (ক).
পুরো ডেটা থেকে resample নিয়ে fit করলে একটি স্বাভাবিক test set-ও পাওয়া যায়: যে প্রায় ৩৬.৮% সারি সেই resample-এ ওঠেনি, তারা out-of-bag (OOB). OOB-তে মাপা ভুল সাধারণত একটু হতাশাবাদী, কারণ model শিখেছে মাত্র প্রায় ৬৩.২% আলাদা সারি থেকে। অন্যদিকে একই ডেটায় fit করে একই ডেটায় মাপা apparent error আশাবাদী। Efron ১৯৮৩ সালে দুটি মিশিয়ে প্রস্তাব করেন:
Err.632 = 0.368 · Errapparent + 0.632 · ErrOOB
Model খুব বেশি overfit করলে (apparent ভুল প্রায় শূন্য) এটিও আশাবাদী হয়ে পড়ে, তাই Efron আর Tibshirani ১৯৯৭ সালে overfitting-এর মাত্রা দেখে ওজন বাড়ানো .632+ রূপ দেন। Random forest-এর OOB score এই ধারণারই আরেক প্রয়োগ, যা পর্ব ৩৩ (Bagging আর random forest)-এ আসবে। নিচের ব্লকে ২০টি feature (কাজের মাত্র ৩টি), train ১৫০ আর test ৩৫০টি অর্ডার: তিন পথে AUC-র অনিশ্চয়তা। এখানে মূল সংখ্যাটি AUC, তাই .632 সূত্রে “ভুল” হিসেবে 1 − AUC বসানো হয়েছে; Efron-এর মূল সূত্র ভুলের হারের জন্য।
import numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
rng = np.random.default_rng(3) # কাল্পনিক বাজারঘর: ৫০০টি অর্ডার, ২০টি feature
n = 500
X = rng.normal(size=(n, 20)) # কাজের feature মাত্র ৩টি, বাকি ১৭টি noise
z = -1 + X @ np.r_[0.9, -0.6, 0.4, np.zeros(17)]
y = rng.binomial(1, 1 / (1 + np.exp(-z)))
tr, te = np.arange(150), np.arange(150, n) # train 150, test 350
pipe = make_pipeline(StandardScaler(), LogisticRegression(C=10.0))
p = pipe.fit(X[tr], y[tr]).predict_proba(X[te])[:, 1]
print("single split AUC", round(roc_auc_score(y[te], p), 3))
r, B = np.random.default_rng(0), 200
a = [roc_auc_score(y[te][i], p[i]) for i in r.integers(0, 350, (B, 350))]
w = [] # (খ) পুরো pipeline: train-ও resample, আবার fit
for b in range(B):
i, j = r.choice(tr, 150), r.choice(te, 350)
w.append(roc_auc_score(y[j], pipe.fit(X[i], y[i]).predict_proba(X[j])[:, 1]))
for name, v in (("(a) test rows only ", a), ("(b) whole pipeline ", w)):
print(name, "sd", round(np.std(v, ddof=1), 4), "95%", np.round(np.percentile(v, [2.5, 97.5]), 3))
oob = [] # out-of-bag: যে সারি resample-এ ওঠেনি, তাতেই মাপা
for b in range(B):
i = r.integers(0, n, n); o = np.setdiff1d(np.arange(n), i)
oob.append(roc_auc_score(y[o], pipe.fit(X[i], y[i]).predict_proba(X[o])[:, 1]))
app = roc_auc_score(y, pipe.fit(X, y).predict_proba(X)[:, 1]) # একই ডেটায় fit আর মাপ
e632 = 0.368 * (1 - app) + 0.632 * (1 - np.mean(oob)) # ভুল = 1 − AUC ধরে
print("apparent", round(app, 3), " OOB mean", round(np.mean(oob), 3),
" .632", round(1 - e632, 3), " last OOB share", round(len(o) / n, 3))আসল আউটপুট (ব্রাউজারের Python):
single split AUC 0.76
(a) test rows only sd 0.0249 95% [0.702 0.804]
(b) whole pipeline sd 0.0355 95% [0.647 0.786]
apparent 0.81 OOB mean 0.758 .632 0.777 last OOB share 0.35
(a) শুধু test-সারি: sd ০.০২৪৯, interval [০.৭০২, ০.৮০৪], একক split-এর ০.৭৬-কে ঘিরে। (b) পুরো pipeline: sd ০.০৩৫৫, প্রায় ১.৪ গুণ। বাড়তি ছড়ানো আসে model-এর বদল থেকে: ১৫০টি সারিতে ২০টি coefficient, আর প্রতিটি resample-এ noise feature-গুলোর ওজন আলাদা। Interval-এর কেন্দ্রও নেমেছে ([০.৬৪৭, ০.৭৮৬]), কারণ bootstrap train-এ গড়ে মাত্র প্রায় ৯৫টি আলাদা সারি থাকে; এই হতাশাবাদ resampling-এর নিজের, পদ্ধতির নয়। শেষ লাইন: একই ডেটায় fit আর মাপা apparent AUC ০.৮১ (আশাবাদী), OOB-এর গড় ০.৭৫৮ (হতাশাবাদী), আর .632 মিশ্রণ ০.৭৭৭. শেষ resample-এ ৩৫% সারি OOB ছিল, তত্ত্বের ৩৬.৮%-এর কাছে। সব সংখ্যা এই কাল্পনিক ডেটার একটি run.
পুরো ডেটা resample করে তারপর train/test ভাগ করবেন না। একই সারি resample-এ দুবার উঠলে একটি কপি train-এ আর আরেকটি test-এ পড়তে পারে, আর model মুখস্থ করা সারিতেই পরীক্ষা দেয়। এটি নিঃশব্দ leakage. উপরের ব্লকে train আর test আলাদা পুল থেকে টানা, আর OOB-তে test হলো যে সারি resample-এ একবারও ওঠেনি।
৬ · সময়ের ক্রমে ডেটা — block bootstrap
বর্ষায় আজ বৃষ্টি হলে কালও হওয়ার সম্ভাবনা বেশি। আবহাওয়ার খাতার দিনগুলো কেটে তাসের মতো এলোমেলো করে নতুন “মাস” বানালে টানা সাত দিনের বৃষ্টি বা টানা খরা প্রায় আসবে না, তাই “মাসে বৃষ্টির দিন কত ওঠানামা করে” প্রশ্নের উত্তর খুব ছোট আসবে। সপ্তাহের টুকরো হিসেবে এলোমেলো করলে টানা দিনের ছন্দ টুকরোর ভেতরে টিকে থাকে।
বাজারঘরের দৈনিক বিক্রিতে একই ঘটনা: একটি ভালো দিনের পর সাধারণত আরেকটি ভালো দিন আসে (ক্যাম্পেইন, ঈদের আগের সপ্তাহ, আবহাওয়া)। পরপর দিনের সম্পর্কের মাপ autocorrelation; lag ১-এ এটি xt আর xt+1-এর correlation. সবচেয়ে সরল model AR(1): xt − μ = φ(xt−1 − μ) + εt, যেখানে |φ| < ১ আর ε স্বাধীন noise. এমন ধারায় n দিনের গড়ের variance প্রায়
Var(x̄) ≈ (σx² / n) · (1 + φ)/(1 − φ)
φ = ০.৭ হলে গুণক ১.৭/০.৩ ≈ ৫.৬৭, অর্থাৎ standard error স্বাধীন ধরার চেয়ে প্রায় √৫.৬৭ ≈ ২.৩৮ গুণ। সাধারণ i.i.d. bootstrap দিনগুলোকে স্বাধীন ধরে এলোমেলো করে, তাই ঠিক এই গুণকটি হারায়। সমাধান block bootstrap: টানা L দিনের টুকরো (block) ফেরত রেখে টেনে জোড়া লাগানো। Hans Künsch ১৯৮৯ সালে Annals of Statistics-এ চলমান (moving) block bootstrap বিশ্লেষণ করেন; যেকোনো দিন থেকে শুরু হওয়া সব সম্ভাব্য block থেকে টানা হয়। নিচের stepper ১২ দিনের একটি ছোট ধারায় দেখায় block কেন ছন্দ রাখে।
| ধাপ | |
| ধারা | |
| lag-1 r |
- মূল ধারা। বিক্রি ধীরে ওঠে-নামে: ৫০ থেকে ৫৭, নেমে ৪৪, আবার উঠে ৬০. পরপর দুই দিনের correlation ০.৭১১: আজ বেশি হলে কালও বেশি হওয়ার ঝোঁক।
- i.i.d. resample. seed 1 দিয়ে ১২টি দিন এলোমেলো টানা। ৪৫-এর পরে ৪৪, তারপর হঠাৎ ৫৮: পাশাপাশি মানের সম্পর্ক ভেঙে গেছে, lag-1 correlation মাত্র ০.০৬৭. এমন নকল ধারার গড় স্বাধীন দিনের গড়ের মতো কম নড়ে।
- Block resample. L = ৩ দিনের ৪টি block, শুরুর দিন ২, ৮, ২ আর ৪ (একই block দুবার আসতে পারে)। প্রতিটি block-এর ভেতরে আসল ক্রম আছে: ৫৭ ৫৫ ৪৯, ৫৩ ৫৮ ৬০. এই একটি resample-এর lag-1 correlation ০.৭৩৮.
- জোড়া গোনা। ১২ দিনের ধারায় ১১টি পাশাপাশি জোড়া। Block-এ ৮টি জোড়া block-এর ভেতরে, অর্থাৎ আসল প্রতিবেশী; শুধু ৩টি জোড়া block-এর সীমানায় কৃত্রিম। i.i.d.-তে কাকতালে মাত্র ৩টি জোড়া আসল প্রতিবেশী (৫→৬, ০→১, ২→৩)।
- অনেকবারের গড়। ৫০০০টি resample-এ lag-1 correlation-এর গড়: i.i.d. −০.০৯৩ (প্রায় শূন্য, ছোট ধারার পক্ষপাতসহ), block ০.৩৮৫. Block পুরো ০.৭১১ রাখে না, কারণ সীমানার জোড়া আর মাত্র ১২ দিন; L বড় করলে বেশি রাখে, কিন্তু আলাদা block কমে যায়। L বাছাই এই টানাপোড়েন।
এবার বড় পরিসরে: ৩৬০ দিনের AR(1) বিক্রি, φ = ০.৭, আসল দীর্ঘমেয়াদি গড় ৫০০. প্রশ্ন: বছরের গড় বিক্রির ৯৫% interval. সত্যিকারের ছড়ানো আমরা জানি, কারণ ৩০০০টি নতুন বছর simulate করা যায়। তারপর ২০০টি বছরে দুই পদ্ধতির coverage.
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(11) # কাল্পনিক বাজারঘর: ৩৬০ দিনের দৈনিক বিক্রি
n, phi, L, B = 360, 0.7, 15, 400 # AR(1): আজকের ধাক্কার ৭০% কাল পর্যন্ত থাকে
def series(k):
e = rng.normal(0, 20, (k, n)); s = np.zeros((k, n))
s[:, 0] = e[:, 0] / np.sqrt(1 - phi**2)
for t in range(1, n): s[:, t] = phi * s[:, t - 1] + e[:, t]
return 500 + s # আসল দীর্ঘমেয়াদি গড় 500
def naive(x, r): # i.i.d. bootstrap: দিনগুলো এলোমেলো
return x[r.integers(0, n, (B, n))].mean(1)
def block(x, r): # moving block: L দিনের টানা টুকরো জোড়া
st = r.integers(0, n - L + 1, (B, n // L))
return x[(st[:, :, None] + np.arange(L)).reshape(B, -1)].mean(1)
true_means = series(3000).mean(1) # ৩০০০টি নতুন বছর: গড়ের আসল ছড়ানো
x = series(1)[0]; r = np.random.default_rng(0)
bn, bb = naive(x, r), block(x, r)
print("lag-1 autocorrelation", round(np.corrcoef(x[:-1], x[1:])[0, 1], 2))
print("sd of the yearly mean: true", round(true_means.std(), 2),
" naive", round(bn.std(), 2), " block", round(bb.std(), 2))
cov = {"naive": 0, "block": 0}; S = 200
for d in series(S): # coverage: ২০০টি বছর, প্রতিটিতে দুই পদ্ধতি
for name, f in (("naive", naive), ("block", block)):
lo, hi = np.percentile(f(d, r), [2.5, 97.5])
cov[name] += lo <= 500 <= hi
print("95% interval coverage: naive", cov["naive"] / S, " block", cov["block"] / S)
plt.figure(figsize=(6, 3.2), layout="constrained")
for v, lab in ((true_means - 500, "true (3000 new years)"),
(bn - bn.mean(), "naive bootstrap"), (bb - bb.mean(), "block bootstrap")):
plt.hist(v, bins=40, density=True, histtype="step", lw=2, label=lab)
plt.xlabel("yearly mean minus its centre"); plt.legend(fontsize=8)
plt.title("Spread of the mean: autocorrelated daily sales"); plt.show()আসল আউটপুট (ব্রাউজারের Python):
lag-1 autocorrelation 0.71
sd of the yearly mean: true 3.43 naive 1.77 block 3.62
95% interval coverage: naive 0.605 block 0.9
Lag-1 autocorrelation ০.৭১, φ = ০.৭-এর কাছে। বছরের গড়ের আসল sd (৩০০০টি নতুন বছর থেকে) ৩.৪৩; সূত্রও প্রায় একই দেয়: σx = ২০/√(১ − ০.৪৯) ≈ ২৮, তাই (২৮/√৩৬০) × ২.৩৮ ≈ ৩.৫১. Naive bootstrap দেয় ১.৭৭, প্রায় অর্ধেক; block দেয় ৩.৬২. Coverage-এ পার্থক্য আরও স্পষ্ট: naive “৯৫%” interval মাত্র ৬০.৫% বছরে আসল গড় ধরে, block ৯০%. Block-ও ৯৫%-এ পৌঁছায়নি, কারণ ৩৬০ দিনে মাত্র ২৪টি block, আর প্রতিটি সীমানায় নির্ভরতা কাটা পড়ে। ছবিতে naive-এর বক্ররেখা স্পষ্টভাবে সরু।
Block bootstrap-এর কয়েকটি রূপ আছে। Circular block ধারার শেষ আর শুরু জুড়ে দেয়। Stationary bootstrap (Dimitris Politis আর Joseph Romano, ১৯৯৪) block-এর দৈর্ঘ্যও এলোমেলো রাখে (geometric বণ্টনে), ফলে নকল ধারাটিও stationary থাকে। L কত? তাত্ত্বিক ফলাফল অনুযায়ী variance অনুমানের জন্য ভালো L মোটামুটি n1/3-এর হারে বাড়ে (n = ৩৬০-এ প্রায় ৭), তবে ধ্রুবক গুণটি autocorrelation কত দীর্ঘ তার ওপর নির্ভর করে; φ = ০.৭-এ তাই ১৫ নেওয়া হয়েছে। Forecast-এর backtesting আলাদা বিষয়, পর্ব ৫২ (ML দিয়ে forecasting)-এ।
একই যুক্তি সময় ছাড়া অন্য নির্ভরতাতেও খাটে। একজন গ্রাহকের দশটি অর্ডার পরস্পর স্বাধীন নয়; অর্ডার resample করলে interval সরু হয়। তখন resample-এর একক হবে গ্রাহক: গ্রাহকদের ফেরত রেখে টেনে তাদের সব অর্ডার একসঙ্গে নেওয়া। একে cluster bootstrap বলে। নিয়মটি এক বাক্যে: ডেটা যে এককে স্বাধীনভাবে এসেছে, সেই এককেই resample করুন।
৭ · কখন bootstrap ব্যর্থ হয় — sample maximum, ভারী লেজ আর ছোট n
Bootstrap-এর তত্ত্ব বলে, পরিসংখ্যানটি যদি F-এর একটি “মসৃণ” function হয় আর তার sampling distribution একটি সুস্থির সীমায় (সাধারণত normal) যায়, তাহলে bootstrap n বাড়লে ঠিক উত্তরের দিকে যায়। দুটি পরিচিত ক্ষেত্রে এই শর্ত ভাঙে। প্রান্তের পরিসংখ্যান: Uniform(০, θ) থেকে সর্বোচ্চ মান দিয়ে θ অনুমান করলে θ̂ = max(x) সবসময় θ-এর নিচে। Resample-এর max কখনো নমুনার max-কে ছাড়াতে পারে না, আর প্রায় ৬৩.২% resample-এ নমুনার max ঠিক আবার ওঠে, কারণ সেই সারিটি টানা না হওয়ার সম্ভাবনা প্রায় ০.৩৬৮. ফলে bootstrap বণ্টনের বড় অংশ একটি বিন্দুতে জমা, অথচ আসল বণ্টন মসৃণ। Peter Bickel আর David Freedman ১৯৮১ সালে এই ধরনের অসংগতির উদাহরণ দেন। ভারী লেজ: ভারী লেজের বণ্টনে variance অসীম হলে গড়ের bootstrap সঠিক সীমায় পৌঁছায় না; Krishna Athreya ১৯৮৭ সালে এটি দেখান। Pareto বণ্টনে (ঘনত্ব ∝ x−(α+1), x ≥ ১) α = ১.৫ হলে গড় আছে (α/(α − ১) = ৩), কিন্তু variance অসীম।
এই দুটি দেখাতে অনেক ডেটাসেট আর প্রতিটিতে ৯৯৯টি resample লাগে, যা ব্রাউজারে ধীর। তাই একটি ডেস্কটপে সত্যিকারের run (Python 3.13.5, NumPy 2.5.4, লেখার সময়ের SciPy 1.18.1; প্রতিটি সারি S = ২০০০টি নতুন ডেটাসেট, একটি ল্যাপটপে মোট প্রায় ৫০ সেকেন্ড, একটি run). নিচে কোডের মূল অংশ, তারপর পুরো আউটপুট:
import numpy as np
from scipy import stats
rng = np.random.default_rng(2026)
S, B = 2000, 999 # S টি নতুন ডেটাসেট, প্রতিটিতে B টি resample
def coverage(draw, stat, truth, n, method):
hit = 0
for s in range(S):
x = draw(n)
lo, hi = stats.bootstrap((x,), stat, n_resamples=B, method=method,
rng=s).confidence_interval
hit += lo <= truth <= hi
return hit / S
theta = 10.0 # ১) Uniform(0, θ)-এর সর্বোচ্চ মান
unif = lambda n: rng.uniform(0, theta, n)
# … n = 20 আর 100-এ percentile আর basic-এর coverage, আর একটি নমুনায়
# কত ভাগ resample-এর max নমুনার max-এর সমান
pareto = lambda n: rng.pareto(1.5, n) + 1.0 # ২) Pareto α = 1.5, আসল গড় 3
logn = lambda n: rng.lognormal(np.log(3.0), 0.9, n) # ৩) তুলনা: lognormal-এর গড়
# … প্রতিটির জন্য coverage(...) ছাপা~/bazarghar-ml$ python failure_cases.py
python 3.13.5 | numpy 2.5.4 | scipy 1.18.1
max, uniform(0,10), n=20 percentile 0.0 basic 0.886
max, uniform(0,10), n=100 percentile 0.0 basic 0.8875
share of resamples whose max equals the sample max: 0.625 (theory 1-(1-1/n)^n = 0.634 )
mean, Pareto(a=1.5), n=50 percentile 0.6665 BCa 0.7475
mean, Pareto(a=1.5), n=500 percentile 0.7515 BCa 0.819
mean, lognormal(0.9), n=25 percentile 0.8795 BCa 0.89
mean, lognormal(0.9), n=100 percentile 0.92 BCa 0.929
mean, lognormal(0.9), n=400 percentile 0.9425 BCa 0.9315
Maximum: percentile interval-এর coverage ঠিক ০. কারণটি যুক্তিতেই দেখা যায়: interval-এর ওপরের প্রান্ত কোনো resample-এর max, যা নমুনার max-এর বেশি হতে পারে না, আর নমুনার max সবসময় θ = ১০-এর নিচে। ২০০০টির একটিও θ ধরেনি। Basic interval উল্টে θ̂-এর ওপরে যায়, তাই ৮৯%-এর কাছে ধরে, কিন্তু n = ১০০-এও ৯৫%-এ পৌঁছায় না: এখানে n বাড়ালে সমস্যা মেটে না। একটি নমুনায় ৬২.৫% resample-এর max নমুনার max-এর সমান, তত্ত্বের ৬৩.৪%-এর কাছে।
Pareto-র গড়: n = ৫০-এ percentile মাত্র ৬৭%, BCa ৭৫%. দশ গুণ ডেটা (n = ৫০০) নিলেও ৭৫% আর ৮২%. এর কারণ: আসল গড়ের একটি বড় অংশ আসে বিরল বিশাল মান থেকে, যা বেশিরভাগ নমুনায় নেই, আর নমুনায় থাকলে একটিই resample-এর গড়কে ঝাঁকায়। Lognormal-এর গড় (সব moment সসীম) তুলনায় স্বাভাবিক আচরণ করে: n = ২৫-এ প্রায় ৮৮–৮৯%, n = ৪০০-এ ৯৩–৯৪%, অংশ ৩-এর ব্রাউজার run-এর সঙ্গে সামঞ্জস্যপূর্ণ। (২০০০টি ডেটাসেটে Monte Carlo sd প্রায় ০.০০৫.)
তাহলে কী করবেন? প্রান্তের পরিসংখ্যানে: m-out-of-n bootstrap, অর্থাৎ n-এর চেয়ে অনেক ছোট m টি সারি টেনে আর পরে ফল স্কেল করা (Bickel, Götze আর van Zwet, ১৯৯৭), অথবা extreme value theory-র parametric model. ভারী লেজে: প্রশ্নটিই বদলানো ভালো: গড়ের জায়গায় median বা trimmed mean, log-স্কেলে বিশ্লেষণ, অথবা লেজের জন্য আলাদা parametric model; আর রিপোর্টে লেখা যে গড়ের interval অনির্ভরযোগ্য। খুব ছোট n-এ interval প্রায় সবসময় সরু; exact বা parametric পদ্ধতি ভালো।
Bootstrap নতুন ডেটা বানায় না। এটি শুধু দৈব নমুনা-বাছাইয়ের অনিশ্চয়তা মাপে। নমুনা biased হলে (শুধু ঢাকার গ্রাহক), label ভুল হলে, leakage থাকলে বা ভবিষ্যতের ডেটা অন্য রকম হলে (পর্ব ৫৯), interval সুন্দর দেখালেও ভুল জায়গায় বসে থাকে।
৮ · কাজের নিয়ম — কী resample, কোন interval, কীভাবে রিপোর্ট
| পরিস্থিতি | Resample-এর একক | পরামর্শ |
|---|---|---|
| একটি fit করা model-এর test metric | Test-এর সারি | Percentile বা BCa; model স্থির, প্রশ্ন (ক) |
| একটি training পদ্ধতির মান | Train-এর সারি (আর test-এর) | প্রতিবার পুরো pipeline আবার fit; বা repeated CV |
| Coefficient-এর অনিশ্চয়তা | সারি (x, y) জোড়া | Analytic SE-র সঙ্গে মেলান; কম event-এ সাবধান |
| দুই model-এর তুলনা | একই resample-এ দুটিই | পার্থক্যের interval (paired); পূর্ণ পরীক্ষা পর্ব ২২-এ |
| সময়ের ধারা | টানা দিনের block | Moving, circular বা stationary bootstrap |
| একজনের অনেক সারি | গ্রাহক, দোকান, রোগী | Cluster bootstrap |
| Max, min, ভারী লেজের গড়, n খুব ছোট | — | Bootstrap-এ ভরসা নয়; m-out-of-n, robust পরিসংখ্যান বা parametric model |
B কত? SE-র জন্য কয়েকশ যথেষ্ট; ৯৫% interval-এর প্রান্ত লেজ থেকে আসে, তাই কমপক্ষে ১০০০, আর প্রকাশের জন্য কয়েক হাজার (SciPy-র default ৯৯৯৯)। রিপোর্টে কী লিখবেন: resample-এর একক, B, পদ্ধতি (percentile/BCa), কী স্থির আর কী আবার fit করা হয়েছে, আর seed. যেমন: “AUC ০.৭৬, ৯৫% bootstrap percentile interval [০.৭০, ০.৮০]; ২০০০টি test-সারি resample, model স্থির।”
Bootstrap বনাম permutation test: bootstrap একটি পরিমাণের interval দেয়; permutation test “কোনো পার্থক্য নেই” ধরে label এলোমেলো করে p-value দেয়; তার পুরো আলোচনা AI-এর ভিত্তি কোর্সের পর্ব ৩৭-এ। দুটি model-এর পরিসংখ্যানিক তুলনার পূর্ণ কাঠামো পর্ব ২২ (Model তুলনার পরিসংখ্যান — paired test, McNemar আর একাধিক তুলনা)-এর বিষয়।
আরও গভীরে: Bootstrap-এর মূল প্রবন্ধ Efron-এর “Bootstrap methods: another look at the jackknife” (Annals of Statistics, ১৯৭৯)। বিভিন্ন interval-এর তুলনামূলক তত্ত্ব DiCiccio আর Efron-এর “Bootstrap confidence intervals” (Statistical Science, ১৯৯৬)-এ, আর BCa ও bootstrap-t-এর second-order accuracy-র গাণিতিক ভিত্তি Peter Hall-এর The Bootstrap and Edgeworth Expansion (১৯৯২)-এ। পাঠ্যবই হিসেবে Efron ও Tibshirani-র An Introduction to the Bootstrap (১৯৯৩) আর Davison ও Hinkley-র Bootstrap Methods and their Application (১৯৯৭)। Donald Rubin-এর ১৯৮১ সালের Bayesian bootstrap প্রতিটি সারির ওজন Dirichlet বণ্টন থেকে টানে; এটি bootstrap-কে একটি সরল posterior হিসেবে পড়ার পথ দেখায়।
পরীক্ষা ও ইন্টারভিউতে:
- n টি সারির একটি resample-এ একটি নির্দিষ্ট সারি না থাকার সম্ভাবনা? (১ − ১/n)n → e−১ ≈ ০.৩৬৮; তাই গড়ে প্রায় ৬৩.২% আলাদা সারি।
- Percentile আর basic interval-এর সূত্র? [q০.০২৫, q০.৯৭৫] আর [2θ̂ − q০.৯৭৫, 2θ̂ − q০.০২৫].
- BCa-র দুটি সংশোধন কী? z0 (θ̂*-এর কত ভাগ θ̂-এর নিচে, median bias) আর a (jackknife থেকে acceleration, skew-এর মতো)। দুটি শূন্য হলে BCa = percentile.
- সময়ের ধারায় সাধারণ bootstrap কেন ভুল? দিনগুলো স্বাধীন ধরে autocorrelation ভেঙে দেয়, তাই SE ছোট আসে; টানা দিনের block resample করতে হয়।
- Bootstrap কোথায় ব্যর্থ? Sample max/min, অসীম variance-এর গড়, খুব ছোট n, আর নির্ভরশীল ডেটায় ভুল একক; এছাড়া biased নমুনা কোনো resampling সারায় না।
৯ · AI-এর যুগে — bootstrap কোড আর interval-এর দাবি যাচাই
Bootstrap-এর কোড দশ লাইনের, আর AI assistant তা আত্মবিশ্বাসের সঙ্গে দেয়। কোড চলে, সুন্দর interval আসে; প্রশ্ন হলো interval-টি কোন অনিশ্চয়তার, আর তার “৯৫%” সত্যি কি না।
| AI প্রায়ই যা করে | কেন ভুল | কীভাবে ধরবেন |
|---|---|---|
Time series বা একজন গ্রাহকের অনেক অর্ডারে সাধারণ np.random.choice bootstrap | নির্ভরশীল সারি স্বাধীন ধরা; interval অনেক সরু | ডেটা কোন এককে স্বাধীন, জিজ্ঞেস করুন; block বা cluster bootstrap |
| পুরো ডেটা resample করে তারপর train/test ভাগ | একই সারির কপি দুই দিকে; leakage | Train আর test আলাদা পুল থেকে, বা OOB ব্যবহার |
| Test set-এ tuning করে সেই test-এরই bootstrap CI দেয় | Interval বাছাইয়ের আশাবাদ ঢাকে না | Tuning CV-তে; তালাবন্ধ test শেষে একবার |
পুরোনো argument (random_state) বা না-থাকা method নাম দেয় | Version বদলালে error বা ভুল আচরণ | নিজের SciPy version-এর docs পড়ুন; ছোট উদাহরণে চালান |
| “BCa সবসময় সঠিক ৯৫%” বলে, বা max ও ভারী লেজের গড়েও CI দেয় | B শুধু Monte Carlo ভুল কমায়; ছোট n, ভারী লেজ বা max সারায় না | জানা বণ্টনে coverage simulation; robust পরিসংখ্যান |
AI-কে শুধু কোড নয়, প্রশ্ন, একক আর যাচাইও চাইতে বলুন:
AI-এর দেওয়া bootstrap কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Bootstrap-এর ধারণা এত সরল, তবু ১৯৭৯ সালের আগে কেন প্রচলিত হয়নি? Jackknife থেকে bootstrap-এ যাত্রাটি কী শেখায়?
বিশ শতকের প্রথমার্ধে পরিসংখ্যানের অনিশ্চয়তা মাপা হতো সূত্র দিয়ে: normal বণ্টন ধরে নিয়ে গড়ের SE, t-বণ্টন, বা বড় n-এর অনুমান, যেমন delta method দিয়ে। প্রতিটি নতুন পরিসংখ্যানের জন্য আলাদা গাণিতিক বিশ্লেষণ লাগত। এই প্রেক্ষাপটে Maurice Quenouille ১৯৪০-এর দশকের শেষে আর ১৯৫৬ সালে একটি করে সারি বাদ দিয়ে bias কমানোর কৌশল দেন, আর John Tukey ১৯৫৮ সালে একে standard error অনুমানে লাগিয়ে নাম দেন jackknife. মাত্র n বার হিসাব লাগত বলে এটি যান্ত্রিক ক্যালকুলেটরের যুগেও সম্ভব ছিল।
Bradley Efron ১৯৭৯ সালের প্রবন্ধের শিরোনামেই লেখেন “another look at the jackknife”. তাঁর অন্তর্দৃষ্টি ছিল, jackknife আসলে F̂-এর একটি রৈখিক আসন্নমান, আর সরাসরি F̂ থেকে Monte Carlo নমুনা নিলে আরও সাধারণ একটি পদ্ধতি পাওয়া যায়, যা অমসৃণ পরিসংখ্যানেও (যেমন median) কাজ করে, যেখানে jackknife ব্যর্থ। কিন্তু এর দাম হাজার হাজার বার হিসাব, যা কেবল সস্তা কম্পিউটারের যুগে বাস্তব হয়। তাই বলা যায় bootstrap-এর ধারণা গাণিতিকভাবে আগেও সম্ভব ছিল, কিন্তু ব্যবহারিক হয় হিসাবের খরচ কমার পর।
বড় শিক্ষা হলো দর্শনের পরিবর্তন: “প্রতিটি সমস্যার জন্য একটি সূত্র খোঁজা” থেকে “একটি সাধারণ নীতি (plug-in) আর অনেক হিসাব”. ML-এর অনেক কৌশল এই ধারারই: cross-validation, bagging, random forest-এর OOB, permutation importance. তবে ঝুঁকি হলো, সূত্রের অনুমান চোখের সামনে থাকত, আর bootstrap-এর অনুমান (স্বাধীন সারি, প্রতিনিধিত্বমূলক নমুনা, মসৃণ পরিসংখ্যান) লুকিয়ে থাকে, কারণ কোড সবসময় একটি সংখ্যা দেয়; অংশ ৬ আর ৭ সেই অনুমান ভাঙার ফল দেখিয়েছে। আর jackknife হারিয়ে যায়নি: BCa-র a আজও jackknife থেকেই আসে।
মূল উপলব্ধি: bootstrap সূত্রের জায়গায় হিসাব দেয়, কিন্তু অনুমান সরায় না, শুধু লুকায়; তাই প্রতিটি প্রয়োগে শর্তগুলো নিজে যাচাই করতে হয়।
প্র ০২Percentile, basic, BCa আর bootstrap-t — কোনটি কখন ব্যবহার করবেন, আর বিশেষজ্ঞদের মধ্যে মতভেদ কোথায়?
তত্ত্বের দিক থেকে interval-গুলোর তুলনা হয় coverage-ভুল n বাড়লে কত দ্রুত কমে, তা দিয়ে। Percentile আর basic interval-এর coverage-ভুল সাধারণত 1/√n হারে কমে, যাকে first-order accuracy বলা হয়। BCa আর bootstrap-t (প্রতিটি resample-এ (θ̂* − θ̂)/SE* হিসাব করে, t-বণ্টনের মতো) দুই-পার্শ্বিক interval-এ 1/n হারে কমে, যাকে second-order accuracy বলা হয়। Efron ১৯৮৭ সালে BCa প্রস্তাব করেন, আর DiCiccio ও Efron-এর ১৯৯৬ সালের পর্যালোচনা প্রবন্ধে BCa-কে সাধারণ ব্যবহারের ভালো বিকল্প বলা হয়।
কিন্তু তত্ত্ব আর ছোট নমুনার বাস্তবতা এক নয়। এই পর্বের অংশ ৩-এ n = ২৫-এ lognormal-এর গড়ে BCa আর percentile দুটিই ৮৭%; BCa শুধু ভুলের দিককে কিছুটা সমান করেছে। Median-এ jackknife অমসৃণ বলে a = ০, তাই BCa আর percentile হুবহু এক। Bootstrap-t-এ প্রতিটি resample-এ SE* লাগে; AUC বা median-এ তা nested bootstrap দিয়ে বের করতে হয় (খরচ B² মাত্রার), আর ছোট n-এ interval অস্বাভাবিক চওড়া হতে পারে।
মতভেদ মূলত তিন জায়গায়। প্রথম, সরলতা বনাম নির্ভুলতা: অনেকে percentile পছন্দ করেন কারণ এটি বোঝানো সহজ; তাত্ত্বিকেরা বলেন BCa প্রায় একই খরচে ভালো। দ্বিতীয়, ছোট n: কারও মতে সেখানে কোনো bootstrap interval-ই নির্ভরযোগ্য নয়, অন্যদের মতে সাবধানে পড়লে তবু কাজের। তৃতীয়, basic interval ডেটার সীমার বাইরে যেতে পারে বলে অনেকে এড়ান, অথচ sample max-এ (অংশ ৭) এটি percentile-এর চেয়ে ভালো coverage দিয়েছে।
ব্যবহারিক নিয়ম: একটি পদ্ধতি আগে থেকে বেছে নিন (SciPy-র default BCa একটি যুক্তিসঙ্গত শুরু), B যথেষ্ট বড় রাখুন, আর আপনার ডেটার আকার ও আকৃতির মতো একটি simulation-এ coverage যাচাই করুন।
মূল উপলব্ধি: BCa তত্ত্বে ভালো আর ব্যবহারে সুবিধাজনক, কিন্তু কোনো পদ্ধতিই ছোট বা ভারী-লেজের নমুনায় অনুপস্থিত তথ্য ফিরিয়ে আনে না; coverage simulation-ই শেষ বিচারক।
প্র ০৩ML model মূল্যায়নে bootstrap না cross-validation? .632 আর .632+ কেন প্রস্তাব করা হয়েছিল, আর গবেষণা কী বলে?
Model-এর generalization ভুল অনুমানের দুটি resampling পথ আছে। Cross-validation ডেটাকে fold-এ ভাগ করে, প্রতিটি সারি ঠিক একবার test হয়। Bootstrap-ভিত্তিক পথে প্রতিবার ফেরত রেখে n টি সারি টেনে fit করা হয়, আর মাপা হয় out-of-bag সারিতে। OOB অনুমান হতাশাবাদী, কারণ model শেখে গড়ে মাত্র ৬৩.২% আলাদা সারি থেকে। Efron ১৯৮৩ সালে Journal of the American Statistical Association-এ এই হতাশাবাদ আর apparent error-এর আশাবাদ মিশিয়ে .632 অনুমান দেন। পরে দেখা যায় খুব বেশি overfit করা model-এ (যেমন 1-nearest neighbour, যার apparent ভুল শূন্য) .632 নিজেই আশাবাদী হয়ে পড়ে; তাই Efron আর Tibshirani ১৯৯৭ সালে overfitting-এর মাত্রা অনুযায়ী ওজন বাড়ানো .632+ প্রস্তাব করেন।
তুলনামূলক গবেষণার মধ্যে Ron Kohavi-র ১৯৯৫ সালের IJCAI প্রবন্ধ বহুল উদ্ধৃত। কয়েকটি ডেটাসেট আর classifier-এ তাঁর পরীক্ষায় .632 bootstrap-এর variance কম হলেও কিছু ক্ষেত্রে বড় bias দেখা যায়, আর তিনি model বাছাইয়ের জন্য stratified ১০-fold CV-র পরামর্শ দেন। তবে এটি নির্দিষ্ট পরীক্ষার ফল; অন্য গবেষণায় ছোট ডেটায় .632+ ভালো করেছে, তাই একক উত্তর নেই।
অংশ ৫-এর run দেখিয়েছে, শুধু test-সারি resample করলে interval সরু আর পুরো pipeline resample করলে চওড়া ও একটু নিচে। প্রচলিত রীতি: model বাছাই ও tuning-এ repeated বা nested CV (AI-এর ভিত্তি কোর্সের পর্ব ৬৬), চূড়ান্ত model-এর মান রিপোর্টে তালাবন্ধ test set আর তার সারির bootstrap interval, আর bagging-ভিত্তিক model-এ বিনামূল্যে পাওয়া OOB score একটি দ্রুত যাচাই হিসেবে।
মূল উপলব্ধি: bootstrap আর CV একই প্রশ্নের দুই আসন্ন উত্তর; প্রত্যেকটির নিজস্ব bias আছে, তাই কোন অনিশ্চয়তা মাপছেন তা লিখে, একটি পদ্ধতি আগে থেকে ঠিক করে রিপোর্ট করুন।
প্র ০৪নির্ভরশীল ডেটায় (সময়ের ধারা, একই গ্রাহকের অনেক অর্ডার, একই হাসপাতালের অনেক রোগী) resampling-এর একক ভুল হলে কী ঘটে? বাস্তবে কোথায় এই ভুল দেখা যায়?
মূল সমস্যা হলো কার্যকর নমুনার আকার। ৩৬০ দিনের বিক্রি autocorrelated হলে তা ৩৬০টি স্বাধীন তথ্য নয়; φ = ০.৭-এ গড়ের ক্ষেত্রে এটি মোটামুটি ৩৬০ × ০.৩/১.৭ ≈ ৬৪টি স্বাধীন দিনের সমান। Naive bootstrap ধরে নেয় ৩৬০টি, তাই SE প্রায় অর্ধেক আসে। একই কথা গোষ্ঠীবদ্ধ ডেটায়: একজন গ্রাহকের দশটি অর্ডার একই অভ্যাস থেকে আসে; অর্ডার-স্তরে resample করলে মনে হয় দশ গুণ তথ্য আছে।
Block bootstrap-এর তাত্ত্বিক ভিত্তি Hans Künsch ১৯৮৯ সালে আর Regina Liu ও Kesar Singh ১৯৯২ সালে দেন; Politis আর Romano ১৯৯৪ সালে stationary bootstrap প্রস্তাব করেন। অর্থনীতিতে একটি পরিচিত পর্যবেক্ষণ হলো, একই রাজ্য বা প্রতিষ্ঠানের বছরের পর বছরের ডেটায় নির্ভরতা উপেক্ষা করলে standard error অনেক ছোট আসে; Bertrand, Duflo আর Mullainathan ২০০৪ সালে difference-in-differences গবেষণায় এই সমস্যা দেখান, আর cluster-স্তরের পদ্ধতির পরামর্শ দেন।
ML-এ এই ভুল প্রায়ই দেখা যায় তিন জায়গায়। প্রথম, মূল্যায়ন: একই রোগীর অনেক ছবি বা একই ব্যবহারকারীর অনেক session train আর test দুই দিকে ছড়িয়ে গেলে score আশাবাদী হয়, আর সেই score-এর bootstrap interval-ও ভুলভাবে সরু। এজন্য split-ই গ্রাহক-স্তরে করতে হয় (scikit-learn-এর GroupKFold), আর bootstrap-ও গ্রাহক-স্তরে। দ্বিতীয়, forecasting-এ সময় না মেনে মূল্যায়ন (পর্ব ৫২)। তৃতীয়, A/B test-এ ব্যবহারকারী-স্তরে randomize করে page view-স্তরে বিশ্লেষণ (পর্ব ৬৩)।
Cluster খুব কম হলে (ধরুন মাত্র আটটি জেলা) cluster bootstrap-ও অনির্ভরযোগ্য; তখন wild cluster bootstrap-এর মতো বিশেষ পদ্ধতি আলোচিত হয়।
মূল উপলব্ধি: resample-এর একক হলো ডেটার স্বাধীন একক; এটি ভুল হলে interval যতই সুন্দর দেখাক, তার “৯৫%” অর্থহীন।
প্র ০৫Bootstrap কেন sample maximum আর ভারী লেজের গড়ে ব্যর্থ হয়? ব্যবসার ডেটায় এর ব্যবহারিক মানে কী?
Bootstrap-এর সাফল্যের তত্ত্ব দুটি জিনিসের ওপর দাঁড়ায়: F̂ n বাড়লে F-এর কাছে যায়, আর পরিসংখ্যানটি F-এর মসৃণ function, যাতে F̂-এর ছোট ভুল পরিসংখ্যানে ছোট ভুল হয়ে আসে। Sample maximum দ্বিতীয় শর্ত ভাঙে। এর আসল sampling distribution মসৃণ: n(θ − max) মোটামুটি একটি exponential বণ্টনের মতো ছড়ায়। কিন্তু bootstrap-এ প্রায় ৬৩% resample-এর max হুবহু নমুনার max, অর্থাৎ বণ্টনের একটি বড় অংশ একটি বিন্দুতে জমা। n যত বাড়ুক, এই ৬৩% থাকে, তাই bootstrap কখনো ঠিক আকৃতিতে পৌঁছায় না। m-out-of-n bootstrap অনেক ক্ষেত্রে সঠিক ফল ফেরায়, যদিও m বাছাই কঠিন।
ভারী লেজের ক্ষেত্রে সমস্যা প্রথম শর্তে: variance অসীম হলে নমুনার গড় normal সীমায় যায় না (central limit theorem খাটে না), আর Athreya ১৯৮৭ সালে দেখান যে তখন সাধারণ bootstrap সঠিক সীমা দেয় না। স্বজ্ঞাগতভাবে: আসল গড়ের একটি বড় অংশ আসে খুব বিরল, খুব বড় মান থেকে। বেশিরভাগ নমুনায় তারা নেই, তাই নমুনা-গড় আর তার bootstrap দুটিই নিচে থাকে; অংশ ৭-এর run-এ n = ৫০০-তেও coverage মাত্র ৭৫–৮২%.
ব্যবসায় এর মানে স্পষ্ট। অনলাইন দোকানের অর্ডারের মূল্য, কয়েকজন পাইকারি ক্রেতার বিশাল কেনাকাটা, বিমা দাবি, বা একটি ভাইরাল পোস্টের ভিউ প্রায়ই ভারী লেজের। “গ্রাহকপ্রতি গড় আয়ের ৯৫% interval” তখন মিথ্যা আত্মবিশ্বাস দিতে পারে, আর A/B test-এ একজন বড় ক্রেতা কোন দলে পড়ল তাতেই ফল উল্টে যেতে পারে। প্রচলিত প্রতিকার: winsorizing (ধরুন ৯৯তম percentile-এ মান কেটে দেওয়া), log-স্কেল, median বা trimmed mean, আর লেজের জন্য extreme value theory-র মতো parametric model. Winsorize করলে আর আসল গড় মাপা হয় না, তাই রিপোর্টে তা লিখতে হয়।
মূল উপলব্ধি: bootstrap মসৃণ পরিসংখ্যান আর সসীম variance-এর জগতের যন্ত্র; প্রান্তের মান বা ভারী লেজে প্রশ্নটি বদলান বা অন্য যন্ত্র নিন, আর কেন নিলেন তা রিপোর্টে লিখুন।
অনুশীলন
হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। চাইলে অংশ ৩ আর ৬-এর ব্লক বদলে যাচাই করুন।
-
পূর্ণ numerical: (ক) n = ১০ টি সারির একটি bootstrap resample-এ একটি নির্দিষ্ট সারি একবারও না আসার সম্ভাবনা কত, আর গড়ে কতটি আলাদা সারি থাকে? (খ) একটি পরিসংখ্যানের θ̂ = ৪.০, আর ২০০০টি bootstrap মানের ২.৫ ও ৯৭.৫ percentile যথাক্রমে ৩.৪ আর ৫.২. Percentile আর basic interval লিখুন, আর কোনটি ডানের লম্বা লেজ ঠিকভাবে দেখায়, বলুন। (গ) চারটি মান ২, ৪, ৬, ১২-এর গড়ের jackknife SE বের করুন, আর s/√n-এর সঙ্গে মেলান।
(ক) (১ − ১/১০)১০ = ০.৯১০ ≈ ০.৩৪৯. আলাদা সারির প্রত্যাশা ১০ × (১ − ০.৩৪৯) ≈ ৬.৫১, অর্থাৎ ৬৫.১%; n বড় হলে ৬৩.২%. (খ) Percentile: [৩.৪, ৫.২], θ̂ থেকে নিচে ০.৬, ওপরে ১.২. Basic: [২ × ৪ − ৫.২, ২ × ৪ − ৩.৪] = [২.৮, ৪.৬], নিচে ১.২, ওপরে ০.৬: অসমানতা উল্টে গেছে। Percentile লেজ যেমন আছে তেমন দেখায়, basic তার যুক্তি অনুযায়ী উল্টায়; কোনটি সঠিক coverage দেয় তা θ̂-এর bias ও skew-এর ওপর নির্ভর করে, আর এই অনিশ্চয়তাই BCa-র কারণ। (গ) গড় ৬. একটি বাদ দিলে গড়: ২২/৩ = ৭.৩৩৩, ২০/৩ = ৬.৬৬৭, ১৮/৩ = ৬.০, ১২/৩ = ৪.০; এদের গড় ৬.০. বিচ্যুতির বর্গ: ১.৭৭৮, ০.৪৪৪, ০, ৪.০, যোগফল ৬.২২২. SEjack = √(৩/৪ × ৬.২২২) = √৪.৬৬৭ ≈ ২.১৬. সূত্রে s² = (১৬ + ৪ + ০ + ৩৬)/৩ = ১৮.৬৭, s/√৪ = ৪.৩২/২ ≈ ২.১৬: হুবহু মেলে, গড়ের ক্ষেত্রে যা সবসময় হয়।
-
সমালোচনা ও নকশা: বাজারঘরের একজন বিশ্লেষক (বা একটি AI assistant) ৮০০ জন গ্রাহকের ৬০০০টি অর্ডার নিয়ে এভাবে কাজ করেছেন: পুরো ৬০০০টি অর্ডার থেকে ফেরত রেখে ৬০০০টি টেনে, তারপর ৭০/৩০ ভাগ করে একটি gradient boosting model train আর test, ২০০ বার; তার আগে test set-এ ১০টি hyperparameter চেষ্টা করে সেরাটি রাখা; শেষে রিপোর্ট “AUC ০.৯১, ৯৫% CI [০.৯০৫, ০.৯১৫]; model অন্য জেলাতেও এমন করবে।” কী কী সমস্যা, আর সঠিক নকশা কী?
সমস্যা: (১) Resample করার পর ভাগ করায় একই অর্ডারের কপি train আর test দুই দিকে পড়ে: leakage, তাই AUC আশাবাদী। (২) একক ভুল: একজন গ্রাহকের অনেক অর্ডার পরস্পর নির্ভরশীল, অর্ডার-স্তরে resample আর split করায় একই গ্রাহক দুই দিকে, আর interval অস্বাভাবিক সরু। (৩) Test set-এ tuning, তাই test আর নিরপেক্ষ নয়। (৪) “অন্য জেলাতেও”: bootstrap distribution shift মাপে না। সঠিক নকশা: শুরুতেই গ্রাহক-স্তরে একটি test set তালাবন্ধ (
GroupShuffleSplit); train-এGroupKFoldদিয়ে tuning; চূড়ান্ত model একবার test-এ মাপা; interval-এর জন্য test-এর গ্রাহকদের (অর্ডার নয়) cluster bootstrap, B ≥ ২০০০, পদ্ধতি আর seed লেখা; অন্য জেলার দাবির জন্য আলাদা জেলার ডেটায় যাচাই, নইলে দাবিটি বাদ। -
আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড কী ছাপবে।
import numpy as np x = np.array([2, 4, 4, 7]) idx = np.array([[0, 0, 3, 1], [2, 3, 3, 3]]) print(x[idx]) print(np.median(x[idx], axis=1)) print(len(set(idx[0])), (1 - 1/4) ** 4)[[2 2 7 4] [4 7 7 7]] [3. 7.] 3 0.31640625প্রথম লাইন: দুটি resample-এর ২ × ৪ array, integer বলে দশমিক নেই। দ্বিতীয় লাইন: প্রথম resample সাজালে ২ ২ ৪ ৭, মাঝের দুটির গড় ৩.০; দ্বিতীয়টি ৪ ৭ ৭ ৭, median ৭.০. Median সবসময় float দেয়, তাই “3.” আর “7.”. তৃতীয় লাইন: প্রথম resample-এ আলাদা index ০, ১, ৩, অর্থাৎ ৩টি; আর n = ৪-এ একটি সারি না আসার সম্ভাবনা (৩/৪)৪ = ৮১/২৫৬ = ০.৩১৬৪০৬২৫, যা ঠিক float-এ লেখা যায় বলে পুরো ছাপা হয়।
-
কোড লিখুন (cluster bootstrap): একটি DataFrame
df-এ কলামcustomerআরreturned(০/১)। গ্রাহক-স্তরে bootstrap করে ফেরতের হারের ৯৫% percentile interval বের করার একটি NumPy/pandas function লিখুন, আর বলুন এটি অর্ডার-স্তরের bootstrap-এর চেয়ে চওড়া হবে কেন।import numpy as np def cluster_boot_rate(df, B=2000, seed=0): g = df.groupby("customer")["returned"].agg(["sum", "count"]) s, c = g["sum"].to_numpy(), g["count"].to_numpy() rng = np.random.default_rng(seed) idx = rng.integers(0, len(g), (B, len(g))) # গ্রাহক টানা, ফেরত রেখে rates = s[idx].sum(1) / c[idx].sum(1) # তাদের সব অর্ডার একসঙ্গে return s.sum() / c.sum(), np.percentile(rates, [2.5, 97.5])প্রতিটি resample-এ গ্রাহক টানা হয়, আর একজন গ্রাহক এলে তাঁর সব অর্ডার আসে; Interval চওড়া হয় কারণ একজন গ্রাহকের অর্ডারগুলো একই অভ্যাস থেকে আসে (কেউ প্রায়ই ফেরত দেন, কেউ কখনো না); কার্যকর স্বাধীন একক তাই অর্ডারের সংখ্যা নয়, গ্রাহকের সংখ্যার কাছাকাছি।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি bootstrap percentile interval তৈরির ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- পরিসংখ্যান আর resample-এর একক ঠিক করা (সারি, গ্রাহক বা দিনের block)
- সেই একক থেকে ফেরত রেখে মূল নমুনার সমান আকারের একটি resample টানা
- Resample-এ একই পরিসংখ্যান আবার হিসাব করা (প্রয়োজনে পুরো pipeline আবার fit)
- এই কাজ B বার করে সব bootstrap মান জমা রাখা
- জমা মানগুলোর ২.৫ আর ৯৭.৫ percentile নেওয়া
- Interval-এর সঙ্গে একক, B, পদ্ধতি আর seed রিপোর্ট করা
n = ১০০০ সারির একটি bootstrap resample-এ (ফেরত রেখে ১০০০ বার টানা) গড়ে মোটামুটি কত ভাগ আলাদা সারি থাকে?
- ১০০%
- প্রায় ৫০%
- প্রায় ৬৩.২%
- প্রায় ৩৬.৮%
একটি পরিসংখ্যানের θ̂ = ১০, আর bootstrap মানের ২.৫ ও ৯৭.৫ percentile যথাক্রমে ৯ আর ১৪. Basic (reverse percentile) bootstrap interval কোনটি?
- [৯, ১৪]
- [৬, ১১]
- [১১, ১৬]
- [৭.৫, ১২.৫]
BCa interval-এ bias-correction z0 = ০ আর acceleration a = ০ হলে কী ঘটে?
- BCa interval basic interval-এর সমান হয়
- BCa সংজ্ঞায়িত থাকে না
- BCa interval θ̂ ± ১.৯৬ × SE হয়ে যায়
- BCa interval percentile interval-এর সমান হয়
৩৬৫ দিনের দৈনিক বিক্রির lag-1 autocorrelation ০.৭. দিনগুলো আলাদা আলাদা ফেরত রেখে টেনে (i.i.d. bootstrap) বছরের গড়ের ৯৫% interval বানালে সাধারণত কী হয়?
- Interval খুব সরু হয়, coverage ৯৫%-এর অনেক নিচে নামে
- Interval খুব চওড়া হয়, coverage প্রায় ১০০%
- গড় একটি মসৃণ পরিসংখ্যান, তাই কোনো সমস্যা নেই
- B ১০,০০০ করলে সমস্যা মিটে যায়
একটি স্থির model-এর test AUC-র bootstrap interval (শুধু test-সারি resample) আর পুরো pipeline-এর bootstrap interval (train-ও resample করে প্রতিবার আবার fit) তুলনা করলে সাধারণত কী দেখা যায়?
- দুটি সবসময় হুবহু এক
- শুধু test-সারির interval বেশি চওড়া
- পুরো pipeline-এর interval চওড়া, আর কেন্দ্র একটু নিচে থাকতে পারে
- শুধু test-সারির পদ্ধতিই OOB অনুমান দেয়
Uniform(০, θ) থেকে n টি মানের সর্বোচ্চটি দিয়ে θ অনুমান করে তার bootstrap percentile interval বানালে কী হয়?
- n বড় হলে coverage ৯৫%-এ পৌঁছায়
- Interval প্রায় কখনো θ ধরে না, কারণ ওপরের প্রান্ত নমুনার max ছাড়ায় না
- Bootstrap বণ্টন প্রায় normal হয়, তাই ঠিক থাকে
- BCa নিলে সমস্যা পুরো মিটে যায়
৪০০ জন গ্রাহকের ৩০০০টি অর্ডারে ফেরতের হারের interval দরকার। কোন resampling সবচেয়ে যুক্তিসঙ্গত?
- ৩০০০টি অর্ডার আলাদা আলাদা ফেরত রেখে টানা
- প্রতিবার একটি অর্ডার বাদ দিয়ে jackknife
- অর্ডারের তারিখ ধরে ৭ দিনের block bootstrap
- গ্রাহকদের ফেরত রেখে টেনে প্রত্যেকের সব অর্ডার একসঙ্গে নেওয়া
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- মূলনীতি: অজানা F-এর জায়গায় F̂, ফেরত রেখে n টি টেনে θ̂*; θ̂* − θ̂-এর ছড়ানো দিয়ে θ̂ − θ-এর অনুমান। একটি সারি না আসার সম্ভাবনা ≈ ০.৩৬৮. B শুধু Monte Carlo ভুল কমায়।
- Interval: percentile [q০.০২৫, q০.৯৭৫], basic [2θ̂ − q০.৯৭৫, 2θ̂ − q০.০২৫], BCa সংশোধিত percentile (z0 আর jackknife থেকে a); “৯৫%” যাচাই হয় simulation-এর coverage দিয়ে।
- কী resample: স্থির model-এর metric হলে test-সারি; training পদ্ধতির মান হলে পুরো pipeline আবার fit; OOB আর .632 = ০.৩৬৮ · apparent + ০.৬৩২ · OOB.
- নির্ভরতা: resample-এর একক = ডেটার স্বাধীন একক; সময়ের ধারায় block বা stationary bootstrap, গোষ্ঠীবদ্ধ ডেটায় cluster bootstrap. না মানলে interval সরু।
- সীমা: sample max/min, অসীম variance-এর গড় আর খুব ছোট n-এ bootstrap ব্যর্থ; biased নমুনা, leakage বা distribution shift কোনো resampling সারায় না।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ২২ · Model তুলনার পরিসংখ্যান — paired test, McNemar আর একাধিক তুলনাপরবর্তী পর্বMcNemar, corrected t আর 5×2cv, Friedman আর Holm/BH দিয়ে model তুলনা।
- মেশিন লার্নিং · কোর্স হোম৭৭টি পর্ব১৪টি মডিউলের পুরো রোডম্যাপ; পর্ব ২২ (Model তুলনার পরিসংখ্যান) এখান থেকে খুলবেন।
- পর্ব ০৪ · পুনরুৎপাদনযোগ্য ML experimentএই কোর্সSeed, split আর fold-এর ওঠানামা, test AUC-র bootstrap interval আর Nadeau–Bengio সংশোধন।
- AI-এর ভিত্তি · পর্ব ৩৭ · ডেটার জন্য পরিসংখ্যানআগের ভিত্তিSampling distribution, CLT, t-interval-এর coverage আর permutation test.
- AI-এর ভিত্তি · পর্ব ৩০ · seaborn — পরিসংখ্যানের chartআগের ভিত্তিছয়টি ডেলিভারির bootstrap হাতে, আর error bar-এর অর্থ।
- AI-এর ভিত্তি · পর্ব ৬০ · মূল্যায়নের metricআগের ভিত্তিF1-এর bootstrap CI আর দুই model-এর paired তুলনা।