Hypothesis Formation ও Experiment Design
Hypothesis Formation and Experiment Design
একটা আবছা প্রশ্ন ('index কি query দ্রুত করে?') কীভাবে একটা falsifiable hypothesis-এ রূপান্তর করতে হয় — independent/dependent/confounding variable, control condition, আর কেন pre-registration p-hacking ঠেকানোর একমাত্র নির্ভরযোগ্য উপায়।
আগে এটা বুঝি
“আমাদের নতুন index-টা কি query দ্রুত করে?” — এই প্রশ্নটা শুনতে সহজ, কিন্তু এটা এখনো পরীক্ষা করার মতো প্রশ্নই না। “দ্রুত” মানে কী — গড়ে, নাকি worst-case-এ? “Query” মানে কোন ধরনের — point lookup, নাকি range scan? “index” ছাড়া তুলনাটা কোন অবস্থায় — একটা ঠান্ডা (cold) সিস্টেমে, নাকি cache warm হয়ে থাকা অবস্থায়?
আগের লেসনে আমরা শিখেছি অন্যদের গবেষণা কীভাবে পড়তে হয়। এই লেসনে আমরা শিখব নিজের প্রশ্নকে এমন আকারে আনতে হয় যাতে সেটা আসলেই পরীক্ষা করা যায় — আর পরীক্ষার ফলাফল যেন সত্যিই কিছু প্রমাণ করে, শুধু একটা সংখ্যা তৈরি না করে।
মূল ধারণা
Falsifiability — একটা দাবিকে “বিজ্ঞান” বানানোর শর্ত
দার্শনিক Karl Popper একটা সহজ কিন্তু শক্তিশালী মানদণ্ড প্রস্তাব করেছিলেন: একটা দাবি বৈজ্ঞানিকভাবে অর্থপূর্ণ তখনই, যখন এমন কোনো পর্যবেক্ষণ কল্পনা করা যায় যা দাবিটাকে ভুল প্রমাণ করে দিত।
- “এই index query দ্রুত করে” — falsifiable না, কারণ এতে “দ্রুত” কতটা, কোন শর্তে তা বলা নেই। যেকোনো ফলাফল দেখিয়েই এই দাবি “রক্ষা” করা যায়।
- “এই B-tree index যোগ করলে, একই hardware আর একই cache-warm অবস্থায়, ১০ লক্ষ row-এর table-এ point-lookup query-র p50 latency অন্তত ৩০% কমবে” — falsifiable। যদি পরীক্ষায় latency ১০% কমে, বা বাড়ে, দাবিটা ভুল প্রমাণিত হয়ে যায়।
এই দ্বিতীয় রূপটাই একটা hypothesis — একটা নির্দিষ্ট, পরিমাপযোগ্য, পরীক্ষাযোগ্য দাবি। Hypothesis লেখার একটা কার্যকর টেমপ্লেট:
“যদি [নির্দিষ্ট পরিবর্তন] করি, তাহলে [নির্দিষ্ট, পরিমাপযোগ্য রাশি] [নির্দিষ্ট দিকে, নির্দিষ্ট মাত্রায়] বদলাবে, [নির্দিষ্ট শর্তে/workload-এ]।”
তিন ধরনের variable — একটা concrete CS উদাহরণ
“Index যোগ করলে query দ্রুত হয়” পরীক্ষা করতে গেলে তিন ধরনের রাশি আলাদা করে চেনা জরুরি:
- Independent variable (স্বাধীন রাশি) — যা আপনি ইচ্ছাকৃতভাবে বদলান: এখানে, index আছে কি নেই।
- Dependent variable (নির্ভরশীল রাশি) — যা আপনি মাপেন, আর যা independent variable-এর প্রভাবে বদলায় বলে আশা করছেন: query latency।
- Confounding variable (confound) — এমন কোনো রাশি যা আপনার অজান্তে একই সাথে independent variable-এর সাথে বদলে যায়, আর dependent variable-কেও প্রভাবিত করে — ফলে আসল কারণ কোনটা তা আলাদা করা যায় না।
এই তৃতীয়টাই সবচেয়ে বিপজ্জনক, কারণ এটা নিঃশব্দে ফলাফল বিকৃত করে। এই নির্দিষ্ট উদাহরণে কয়েকটা বাস্তব confound:
| Confound | কীভাবে ফলাফল বিকৃত করে |
|---|---|
| Cache warmth | Index যোগ করার আগে টেস্ট করলে disk cold, পরে করলে cache warm — latency কমা index-এর জন্য না, cache-এর জন্য |
| Concurrent load | আগের run-এ সিস্টেমে অন্য কোনো ব্যাচ job চলছিল, পরের run-এ না — তুলনাটা ন্যায্য না |
| Data বদলে যাওয়া | দুই run-এর মাঝে table-এ নতুন row insert/delete হয়েছে — table-এর আকার/গঠন বদলে গেছে |
| Query plan caching | Database-এর query planner প্রথমবার একটা plan cache করে রাখে — দ্বিতীয়বার দ্রুত হওয়া শুধু planning overhead কমার জন্য, index-এর জন্য না |
| হার্ডওয়্যার অবস্থা | দুই run-এর মাঝে thermal throttling, বা অন্য কোনো process CPU নিয়ে নিয়েছে |
Control condition — কেন before/after যথেষ্ট না
একটা সাধারণ (আর প্রায়ই ভুল) পরীক্ষার নকশা:
১. Baseline মাপুন (index ছাড়া)
২. Index যোগ করুন
৩. আবার মাপুন (index সহ)
৪. পার্থক্যটাই index-এর প্রভাবসমস্যা — ধাপ ১ আর ৩-এর মাঝে সময় গেছে। সেই সময়ে উপরের যেকোনো confound ঘটতে পারে। “Before/after” ডিজাইন ধরে নেয় সময়ের সাথে আর কিছু বদলায় না — যা প্রায় কখনোই সত্যি না একটা running system-এ।
সমাধান — একটা control condition রাখা। সবচেয়ে শক্তিশালী রূপ: A/B/A design বা randomized interleaving — index-সহ আর index-ছাড়া query একই সময়ে, এলোমেলোভাবে মিশিয়ে চালানো (যেমন প্রতিটা query-র জন্য coin-flip দিয়ে ঠিক করা কোন সিস্টেমে যাবে)। এতে সময়ের সাথে বদলে যাওয়া যেকোনো বাহ্যিক factor (load, thermal, ইত্যাদি) দুই condition-কেই সমানভাবে প্রভাবিত করে, তাই পার্থক্যটুকু শুধু independent variable-এরই কৃতিত্ব থেকে যায়।
❌ Before/after (দুর্বল):
[সময়=০, index নেই, মাপুন] ... [সময়=১ ঘণ্টা, index যোগ, মাপুন]
সমস্যা: এক ঘণ্টায় আর কী বদলেছে জানা নেই
✓ Randomized interleaving (শক্তিশালী):
query ১ → এলোমেলোভাবে A বা B-তে পাঠানো
query ২ → এলোমেলোভাবে A বা B-তে পাঠানো
...
সময়ের প্রভাব A আর B দুইটাতেই সমানভাবে পড়ে, তাই বাতিল হয়ে যায়ভেতরে কী ঘটছে
নমুনা সংখ্যা — “কয়েকবার চালিয়ে দেখা” কেন যথেষ্ট না
advanced-architecture/benchmarking-methodology লেসনে আমরা দেখেছি
একটা single measurement বিভ্রান্তিকর — outlier, noise। এখানে প্রশ্নটা
আরেক ধাপ গভীরে: কয়টা measurement যথেষ্ট বলে আত্মবিশ্বাসের সাথে
একটা সিদ্ধান্তে পৌঁছানো যায়?
ধরুন index-সহ ৩টা measurement: [8ms, 12ms, 9ms], index-ছাড়া ৩টা:
[11ms, 10ms, 13ms]। গড় যথাক্রমে 9.7ms আর 11.3ms — index-সহটা
দ্রুত মনে হচ্ছে। কিন্তু মাত্র ৩টা নমুনায় এই পার্থক্য (1.6ms) কি
আসল, নাকি শুধু random variation?
এই প্রশ্নের কঠোর, সংখ্যাগত উত্তর — standard deviation, confidence
interval, statistical significance — পরের লেসনের (benchmarking-and-statistical-analysis)
বিষয়। এখানে শুধু নীতিটা ধরে রাখুন:
নমুনা সংখ্যা যত কম, random noise-কে “real effect” ভেবে ভুল করার সম্ভাবনা তত বেশি। “৩-৪ বার চালিয়ে দেখলাম, সব সময় একই রকম লাগলো” — এটা একটা অনুভূতি, প্রমাণ না।
P-hacking — যখন “যথেষ্ট চেষ্টা” জালিয়াতি হয়ে যায়
এখানে একটা সূক্ষ্ম কিন্তু গুরুত্বপূর্ণ বিপদ — p-hacking। এটা সবসময় ইচ্ছাকৃত জালিয়াতি না; প্রায়ই সৎ গবেষকও অজান্তে এটা করে ফেলেন।
সংজ্ঞা: একাধিক hypothesis, metric, subgroup, বা analysis
পদ্ধতি চেষ্টা করে যেটাতে “পরিসংখ্যানগতভাবে তাৎপর্যপূর্ণ” ফলাফল
(পরের লেসনে বিস্তারিত p < 0.05) পাওয়া যায় সেটাই বেছে report
করা — বাকি চেষ্টাগুলো উল্লেখ না করে।
একটা বাস্তবসম্মত উদাহরণ — index পরীক্ষায়:
চেষ্টা ১: গড় latency তুলনা → তাৎপর্যপূর্ণ পার্থক্য নেই
চেষ্টা ২: p95 latency তুলনা → তাৎপর্যপূর্ণ পার্থক্য নেই
চেষ্টা ৩: শুধু "বড়" query-তে (>1MB) তুলনা → তাৎপর্যপূর্ণ! (p=0.04)
চেষ্টা ৪: শুধু সকাল ৯টা-১০টার data → তাৎপর্যপূর্ণ না
রিপোর্টে শুধু চেষ্টা ৩-ই লেখা হলো: "আমাদের index বড় query-তে
পরিসংখ্যানগতভাবে তাৎপর্যপূর্ণভাবে দ্রুত!"সমস্যাটা কী? যদি আপনি যথেষ্ট ভিন্ন metric, subgroup, বা threshold
চেষ্টা করেন, নেহাত কাকতালীয়ভাবেই কোনো একটাতে p < 0.05 পেয়ে
যাবেন — এমনকি যদি প্রকৃতপক্ষে কোনো real effect না থাকে।
এটাকে বলে multiple comparisons problem — প্রতিটা নতুন চেষ্টা
একটা নতুন “লটারি টিকেট”, আর যথেষ্ট টিকেট কাটলে একটা “জিতবেই”,
নিছক সম্ভাবনার কারণে।
Pre-registration — একমাত্র নির্ভরযোগ্য প্রতিকার
Data দেখার আগে লিখে ফেলুন:
- নির্দিষ্ট hypothesis (falsifiable ভাষায়)
- কোন metric মাপবেন (একটা প্রধান metric, ঐচ্ছিক secondary metric-গুলো আলাদা করে চিহ্নিত)
- কত নমুনা সংগ্রহ করবেন, কীভাবে
- Analysis কীভাবে করবেন (কোন statistical test, কোন threshold)
তারপর data সংগ্রহ করুন, আর যা লিখেছিলেন ঠিক তাই বিশ্লেষণ করুন — data দেখার পর metric/subgroup পাল্টানো যাবে না। যদি অন্য কোনো আকর্ষণীয় pattern পরে চোখে পড়ে, সেটাকে exploratory finding হিসেবে আলাদা করে চিহ্নিত করুন — একটা নতুন, আলাদা pre-registered experiment দিয়ে নিশ্চিত করার প্রয়োজন আছে বলে স্পষ্ট করে লিখুন।
নিজে চালিয়ে দেখুন
P-hacking নিজের চোখে দেখা — pure noise-এও 'তাৎপর্যপূর্ণ' ফলাফল পাওয়া
import random
import statistics
random.seed(42)
def fake_measurement():
"""সম্পূর্ণ random noise — কোনো real effect নেই, ইচ্ছাকৃতভাবে।"""
return random.gauss(10.0, 2.0)
def two_sample_ttest_pvalue(a, b):
"""সরলীকৃত Welch's t-test approximation — শুধু demonstration-এর জন্য।"""
mean_a, mean_b = statistics.fmean(a), statistics.fmean(b)
var_a, var_b = statistics.variance(a), statistics.variance(b)
n_a, n_b = len(a), len(b)
se = ((var_a / n_a) + (var_b / n_b)) ** 0.5
if se == 0:
return 1.0
t = abs(mean_a - mean_b) / se
# সরলীকৃত approximation: বড় |t| → ছোট p (সঠিক t-distribution না, শুধু demo)
p_approx = 2 * (1 - min(0.9999, t / (t + 3)))
return p_approx
NUM_METRICS_TRIED = 20
significant_found = 0
for trial in range(NUM_METRICS_TRIED):
# দুইটা group-ই একই distribution থেকে — কোনো real difference নেই
group_a = [fake_measurement() for _ in range(15)]
group_b = [fake_measurement() for _ in range(15)]
p = two_sample_ttest_pvalue(group_a, group_b)
marker = " ← 'তাৎপর্যপূর্ণ'!" if p < 0.05 else ""
if p < 0.05:
significant_found += 1
print(f"metric/subgroup #{trial+1:2d}: p ≈ {p:.4f}{marker}")
print(f"\nমোট চেষ্টা: {NUM_METRICS_TRIED}, 'তাৎপর্যপূর্ণ' পাওয়া গেছে: {significant_found}")
print("বাস্তব effect শূন্য (দুইটা group-ই identical distribution থেকে) —")
print("তবু যথেষ্ট চেষ্টায় 'তাৎপর্যপূর্ণ' ফলাফল খুঁজে পাওয়া প্রায় নিশ্চিত।")Illustrative output:
metric/subgroup # 1: p ≈ 0.3120
metric/subgroup # 2: p ≈ 0.0410 ← 'তাৎপর্যপূর্ণ'!
metric/subgroup # 3: p ≈ 0.6702
...
metric/subgroup #14: p ≈ 0.0289 ← 'তাৎপর্যপূর্ণ'!
...
মোট চেষ্টা: 20, 'তাৎপর্যপূর্ণ' পাওয়া গেছে: 220 টা সম্পূর্ণ স্বাধীন, random চেষ্টায় প্রায়ই ১-২টা “তাৎপর্যপূর্ণ”
ফলাফল আসবে — বিশুদ্ধ কাকতালীয়ভাবে, যদিও group A আর group B আসলে
একই distribution থেকে এসেছে, কোনো real পার্থক্য নেই। একজন
গবেষক যদি শুধু চেষ্টা #২ বা #১৪-কেই বেছে রিপোর্ট করেন (বাকি ১৮টা
উল্লেখ না করে), সেটাই p-hacking — সততার সাথে না করলেও ফলাফল সমান
বিভ্রান্তিকর।
যদি কোনো real effect না থাকা সত্ত্বেও যথেষ্ট metric/subgroup চেষ্টা করা হয়, নিছক সম্ভাবনার কারণেই কোনো না কোনোটায় 'p < 0.05' পাওয়া যাবে — pre-registration ছাড়া এই ফাঁদ এড়ানো কার্যত অসম্ভব।
নিজে বানান
একটা Pre-Registration Document লেখা
- একটা নির্দিষ্ট, falsifiable hypothesis লিখুন (উপরের টেমপ্লেট ব্যবহার করে)
- Independent, dependent, ও সম্ভাব্য confounding variable তালিকাভুক্ত করুন
- Control condition ডিজাইন করুন — before/after না, randomized/interleaved পদ্ধতি
- নমুনা সংখ্যা আর একটামাত্র প্রধান metric ঠিক করুন — এবং commit করে timestamp রাখুন
“Index যোগ করলে query দ্রুত হয়” প্রশ্নটার জন্য একটা সম্পূর্ণ pre-registration:
# Pre-registration: users টেবিলে email-এ index
তারিখ: 2026-09-11 (data সংগ্রহের আগে লেখা, git-এ commit করা)
## Hypothesis
users টেবিলে (১০ লক্ষ row) email কলামে B-tree index যোগ করলে,
একই hardware ও একই concurrent load-এ, `SELECT * FROM users WHERE
email = ?` query-র p50 latency অন্তত ৩০% কমবে, cache-warm অবস্থায়।
## Variable
- Independent: index আছে (B) বনাম নেই (A)
- Dependent: query latency (ms), p50 ও p95 দুইটাই মাপা হবে —
কিন্তু **p50-ই প্রধান metric**, p95 শুধু secondary/exploratory
- Confound ও নিয়ন্ত্রণ:
- cache warmth → দুই condition-এই একই warm-up (১০০০ query, ফলাফল বাতিল)
- concurrent load → randomized interleaving, একই সময়ে দুই
condition-ই চলবে
- query plan cache → warm-up-এর মধ্যেই এটাও স্থিতিশীল হয়ে যাবে
## Design
Randomized interleaving — প্রতিটা query independently ৫০/৫০
সম্ভাবনায় A বা B-তে পাঠানো হবে, একই client প্রসেস থেকে, একই সময়ে।
## Sample size
প্রতি condition-এ ৫০০টা measured query (warm-up বাদে)। এই সংখ্যা
`benchmarking-and-statistical-analysis` লেসনের rule of thumb থেকে
— ৩০%-এর মতো বড় প্রত্যাশিত পার্থক্যের জন্য n=২০-৩০ যথেষ্ট হতে
পারত, কিন্তু p95-এর মতো noisy metric-এর জন্য অতিরিক্ত মার্জিন
রাখা হলো।
## Analysis plan
Bootstrap 95% confidence interval (পরের লেসনের পদ্ধতি) দুই condition-এর
p50-এর জন্য। যদি interval overlap না করে, দাবি করা যাবে পার্থক্যটা
পরিসংখ্যানগতভাবে তাৎপর্যপূর্ণ। p95-এর ফলাফল আলাদাভাবে, "exploratory"
হিসেবে চিহ্নিত করে রিপোর্ট করা হবে, প্রধান দাবির অংশ হিসেবে না।নিজে বাড়ান:
- একটা এমন hypothesis লিখুন যেখানে “index দ্রুত করবে না, বরং ধীর করবে” — কোন পরিস্থিতিতে এটা সত্যি হতে পারে? (হিন্ট: খুব ছোট table, বা খুব বেশি write-heavy workload — index maintenance overhead)
- আপনার আগের কোনো ব্যক্তিগত “benchmark” (যেমন কোনো optimization-এর দাবি) খুঁজে বের করুন যেখানে আপনি pre-registration ছাড়াই সিদ্ধান্ত নিয়েছিলেন — এখন retroactively সেটার জন্য একটা pre-registration লিখলে কী ভিন্ন হতো?
- একটা hypothesis লিখুন যা ইচ্ছাকৃতভাবে falsifiable না — তারপর সেটাকে falsifiable বানান তিনটা ভিন্নভাবে (ভিন্ন metric, ভিন্ন threshold)
বাস্তব সিস্টেমে
এই নীতিগুলো যেখানে প্রতিদিন কাজ করে
Systems paper-এর evaluation section। ভালো systems paper (যেমন আগের লেসনের MapReduce, Raft) স্পষ্টভাবে workload, baseline, আর hardware বর্ণনা করে — এটাই confound নিয়ন্ত্রণের লিখিত প্রমাণ। এখন এই লেসনের পর, একটা evaluation section পড়ার সময় আপনি জিজ্ঞাসা করতে পারবেন “এখানে কোন confound নিয়ন্ত্রণ করা হয়নি” — একটা নতুন, গুরুত্বপূর্ণ সমালোচনামূলক দক্ষতা।
Database vendor benchmark-এর সতর্কতা। Commercial database vendor-রা প্রায়ই নিজেদের সবচেয়ে অনুকূল workload বেছে benchmark প্রকাশ করে (“আমাদের ডেটাবেস X গুণ দ্রুত!”) — এটা p-hacking-এরই একটা রূপ, metric-এর বদলে workload নিয়ে “hacking”। TPC-এর মতো standardized benchmark এই কারণেই কঠোরভাবে workload আর রিপোর্টিং নিয়ম নির্দিষ্ট করে দেয়।
Feature flag ও A/B testing culture। বড় software কোম্পানি (Google, Meta, Netflix) প্রতিটা নতুন feature-এর প্রভাব মাপতে randomized A/B testing ব্যবহার করে ঠিক এই লেসনের নীতিতে — user-দের এলোমেলোভাবে treatment/control-এ ভাগ করে, একই সময়ে, যাতে সময়ভিত্তিক confound (ছুটির দিন, marketing push) দুই দলেই সমান থাকে।
Clinical trial-এর সাদৃশ্য। Medicine-এ randomized controlled
trial (RCT), placebo control, আর pre-registration (clinicaltrials.gov)-এর
পুরো সংস্কৃতিটাই ঠিক এই একই সমস্যার সমাধান — শুধু অনেক বেশি বাজি
(মানুষের স্বাস্থ্য) থাকায় নিয়মগুলো আরো কঠোরভাবে প্রয়োগ করা হয়।
CS গবেষণায় বাজি কম বলে discipline-টা প্রায়ই শিথিল হয়ে যায় — যা
cs-theory/reproducibility লেসনে আমরা দেখব সমস্যা তৈরি করে।
Replication crisis-এর পূর্বাভাস। Psychology-র বিখ্যাত replication crisis-এর একটা বড় কারণই ছিল ব্যাপক, অস্বীকৃত p-hacking — গবেষকরা একাধিক metric/subgroup চেষ্টা করে যেটা তাৎপর্যপূর্ণ এসেছে সেটাই প্রকাশ করতেন। পরের লেসনে এই সংকট বিস্তারিত আসবে — এই লেসনের pre-registration সেই সংকট প্রতিরোধের সরাসরি হাতিয়ার।
যে ভুলগুলো সবাই করে
“P-hacking মানে ইচ্ছাকৃত জালিয়াতি — যদি আমি সৎভাবে ডেটা বিশ্লেষণ করি, আমি p-hacking করছি না।”
এই লেসনের সবচেয়ে গুরুত্বপূর্ণ, পাল্টা-স্বজ্ঞাত (counter-intuitive) শিক্ষা: p-hacking প্রায়ই সম্পূর্ণ সততার সাথেই ঘটে।
“Garden of forking paths” পর্যবেক্ষণ অনুযায়ী, একজন গবেষক যিনি কখনো কোনো তথ্য গোপন করেননি, কখনো data বানাননি, তবুও p-hacking করে ফেলতে পারেন শুধু এভাবে: data দেখার পরে সিদ্ধান্ত নেন কোন metric সবচেয়ে “প্রাসঙ্গিক” মনে হচ্ছে, কোন outlier বাদ দেওয়া “যুক্তিসঙ্গত”, কোন subgroup-এ analysis করা “আকর্ষণীয়” — প্রতিটা সিদ্ধান্ত individually ন্যায্য মনে হলেও, সামগ্রিক প্রক্রিয়াটা multiple-comparisons সমস্যার শিকার হয়।
একমাত্র নির্ভরযোগ্য প্রতিরক্ষা প্রক্রিয়াগত (procedural): pre-registration — data দেখার আগে metric/analysis plan লিখে ফেলা, সময়ের ক্রম প্রমাণযোগ্যভাবে ঠিক রেখে। “আমি সৎ, তাই আমার দরকার নেই” — এই বিশ্বাসই সবচেয়ে বেশি মানুষকে p-hacking-এর ফাঁদে ফেলে, কারণ এটা একটা প্রক্রিয়াগত সমস্যা, ব্যক্তিগত নৈতিকতার সমস্যা না।
“Before/after তুলনা একটা 'বাস্তব' পরিবর্তনের প্রভাব মাপার স্বাভাবিক, সহজবোধ্য উপায় — এতে জটিল randomization-এর দরকার নেই।”
“স্বাভাবিক” মনে হওয়া আর “নির্ভরযোগ্য” হওয়া এক জিনিস না।
Before/after ডিজাইন শুধুমাত্র তখনই বৈধ যখন আপনি নিশ্চিত করতে পারেন independent variable ছাড়া দুই পরিমাপের মাঝে আর কিছুই বদলায়নি — বাস্তব সিস্টেমে (এমনকি বাস্তব জগতেও) এটা প্রায় কখনো নিশ্চিত করা যায় না। Server load, cache state, এমনকি দিনের সময় (ভিন্ন ব্যবহারকারী active) — সবকিছুই confound হতে পারে।
Randomized interleaving জটিল শোনায়, কিন্তু বাস্তবায়ন প্রায়ই before/after-এর চেয়ে সহজ — শুধু একটা coin-flip যোগ করা, প্রতিটা query/request-এর আগে। এই সামান্য বাড়তি জটিলতার বিনিময়ে পুরো confound সমস্যাটাই পরিসংখ্যানগতভাবে বাতিল হয়ে যায়, কোনো নির্দিষ্ট confound চিহ্নিত করার দরকার ছাড়াই।
বুঝেছেন কি না দেখুন
1প্রশ্ন: “আমাদের নতুন caching layer কি সিস্টেম ভালো করে?” — এটাকে
একটা falsifiable hypothesis-এ রূপান্তর করুন, independent, dependent,
আর অন্তত দুইটা confounding variable সহ।
প্রয়োগ
Hypothesis: “একটা ১GB LRU cache যোগ করলে, /api/product/{id}
endpoint-এর p95 response time একই traffic pattern-এ (৯০% repeat
request, ১০% নতুন id) অন্তত ৪০% কমবে, একই hardware ও একই concurrent
load-এ।”
Independent variable: cache আছে কি নেই
Dependent variable: p95 response time (ms)
সম্ভাব্য confound:
- Traffic pattern বদলে যাওয়া — যদি before/after ভিন্ন সময়ে মাপা হয়, request-এর mix (repeat vs নতুন id) ভিন্ন হতে পারে, যা cache hit rate-কে প্রভাবিত করবে independent variable ছাড়াই
- Downstream database-এর অবস্থা — cache যোগ করার ফলে database-এর উপর load কমে, যা database-এর নিজস্ব query cache/buffer pool-কেও প্রভাবিত করতে পারে অন্য, unrelated query-র জন্যও — dependent variable-এর “প্রকৃত কারণ” জটিল হয়ে যায়
- (অতিরিক্ত) Memory pressure — ১GB cache-এর জন্য বরাদ্দ memory অন্য প্রসেস থেকে কেড়ে নিচ্ছে কি না, যা অন্য কোনো পথে latency প্রভাবিত করতে পারে
সঠিক ডিজাইন হবে randomized interleaving — প্রতিটা request এলোমেলোভাবে cache-সহ বা cache-ছাড়া path-এ পাঠানো, একই সময়ে, একই traffic mix-এ।
2একজন সহকর্মী বলছেন: “আমি ১০০ বার benchmark চালিয়েছি, প্রতিবারই
নতুন algorithm দ্রুত এসেছে, তাই এটা নিশ্চিতভাবে সত্য।” এই যুক্তিতে
কী সমস্যা থাকতে পারে, এমনকি ১০০ বার চালানো সত্ত্বেও?
যুক্তি
“১০০ বার চালানো” নিজেই যথেষ্ট না যদি প্রতিটা run একই confound-এর শিকার হয়। যেমন, যদি প্রতিটা run একই মেশিনে, একই সময়ে (সব সকালে), একই ধারাক্রমে (সবসময় নতুন algorithm দ্বিতীয়বার, cache warm থাকা অবস্থায়) চালানো হয় — তাহলে ১০০ বার পুনরাবৃত্তি করাটা শুধু একই নিয়মতান্ত্রিক bias ১০০ বার পুনরুৎপাদন করছে, random noise কমাচ্ছে না।
নমুনা সংখ্যা বাড়ানো শুধু random noise-এর বিরুদ্ধে কার্যকর (confidence interval সংকুচিত করে, পরের লেসনে দেখব)। কিন্তু একটা systematic bias (যেমন run-এর ক্রম সবসময় একই) কোনো সংখ্যক পুনরাবৃত্তি দিয়েই দূর হয় না — সেটা দূর করতে হয় ডিজাইন বদলে (randomized interleaving, ক্রম এলোমেলো করা)।
তাই সঠিক প্রশ্ন শুধু “কতবার চালিয়েছেন” না, বরং “প্রতিটা run কি স্বাধীন এবং ন্যায্যভাবে randomized ছিল”।
3আপনি একটা নতুন sorting algorithm ডিজাইন করেছেন আর দাবি করতে চান এটা
std::sort-এর চেয়ে দ্রুত। একটা সম্পূর্ণ pre-registration লিখুন
(hypothesis, variable, control, sample size, analysis plan) — কম
করে ৫টা অংশ।
ডিজাইন
std::sort-এর চেয়ে দ্রুত। একটা সম্পূর্ণ pre-registration লিখুন
(hypothesis, variable, control, sample size, analysis plan) — কম
করে ৫টা অংশ।Hypothesis: “আমাদের algorithm, 10⁵ থেকে 10⁷ উপাদানের random
integer array-তে, একই hardware ও compiler flag-এ (-O2), std::sort-এর
চেয়ে গড় wall-clock sort time অন্তত ১৫% কম হবে।”
Independent variable: algorithm (আমাদেরটা বনাম std::sort)
Dependent variable: wall-clock sort time (ms), median রিপোর্ট করা হবে প্রধান metric হিসেবে
Confound ও নিয়ন্ত্রণ:
- Compiler optimization level → দুইটাই একই
-O2দিয়ে কম্পাইল - Input data-র বৈশিষ্ট্য (already-sorted, reverse-sorted, random) → আলাদা আলাদা workload হিসেবে আগে থেকেই pre-register করা, পরে বেছে নেওয়া না (নাহলে p-hacking)
- System noise →
benchmarking-methodologyলেসনের অনুযায়ী CPU pin, warm-up, একাধিক run
Sample size: প্রতিটা array size ও input distribution-এর জন্য ৫০টা independent run (ভিন্ন random seed), warm-up ১০টা বাদে
Analysis plan: প্রতিটা (size, distribution) জোড়ার জন্য আলাদা bootstrap 95% CI (পরের লেসনের পদ্ধতি), median সময়ের উপর। Interval non-overlapping হলেই “দ্রুত” দাবি করা হবে, নাহলে “তাৎপর্যপূর্ণ পার্থক্য পাওয়া যায়নি” রিপোর্ট করা হবে — এমনকি সেটা মূল hypothesis খণ্ডন করলেও।
এই শেষ শর্তটাই সবচেয়ে গুরুত্বপূর্ণ — pre-registration-এর পুরো উদ্দেশ্য হলো ফলাফল প্রতিকূল হলেও তা honestly রিপোর্ট করার প্রতিশ্রুতি, আগে থেকেই লিখিত।
এরপর কী
একটা ভালো pre-registered experiment চালানোর পর আপনার হাতে থাকবে একগুচ্ছ সংখ্যা — কিছু latency measurement, কিছু accuracy score। কিন্তু “গড়ে দ্রুত” বলাটা এখনো যথেষ্ট প্রশ্নের উত্তর না — median কেমন, tail (p99) কেমন, আর সবচেয়ে গুরুত্বপূর্ণ, পার্থক্যটা কি পরিসংখ্যানগতভাবে বিশ্বাসযোগ্য নাকি নিছক noise?
পরের লেসনে আমরা এই প্রশ্নগুলোর কঠোর উত্তর দেব — mean/median/percentile, confidence interval, p-value-এর প্রকৃত অর্থ, আর bootstrap resampling, যা এই লেসনের pre-registered analysis plan-কে বাস্তবায়ন করার হাতিয়ার।
আরও পড়ুন
- The Logic of Scientific Discovery — Karl Popper · Falsifiability-কে বিজ্ঞানের সীমানা-নির্ধারক মানদণ্ড হিসেবে প্রস্তাবের মূল উৎস
- The Garden of Forking Paths — Andrew Gelman, Eric Loken · American Scientist, 2014 — p-hacking ইচ্ছাকৃত জালিয়াতি ছাড়াও কীভাবে ঘটে যায় তার স্পষ্টতম ব্যাখ্যা
- Registered Reports: A New Publishing Initiative — Chris Chambers · Pre-registration-ভিত্তিক publishing model-এর প্রবক্তা লেখা