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

AI প্রকল্পের জীবনচক্র — সমস্যা বাছাই থেকে maintenance আর পরের পথ

The AI Project Lifecycle — Problem Selection, Framing, Baselines, Experiment Tracking, Deployment, Drift Monitoring, Retraining, Retirement and Next Steps

Taught in Bangla, this lesson follows a fictional return-risk project from impact and feasibility scoring through framing, baselines and a JSON Lines run log (with a real MLflow run), batch versus online deployment, shadow and canary releases, drift monitoring with PSI and the KS test, concept drift and retraining triggers, feedback loops, documentation and planned retirement.

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

  • কয়েকটি সম্ভাব্য প্রকল্পকে impact আর feasibility দিয়ে তুলনা করা, আর কখন ML-ই ভুল হাতিয়ার তা চেনা
  • একটি সমস্যাকে label, সিদ্ধান্ত, ভুলের দাম আর ব্যবসার মাপে framing করা; ডেটা, গোপনীয়তা আর build বনাম buy যাচাই
  • Baseline দিয়ে শুরু করে প্রতিটি পরীক্ষার seed, config, ডেটার version আর ফল একটি log-এ রাখা
  • Batch বনাম online deploy, latency ও খরচের বাজেট, shadow আর canary; PSI ও KS দিয়ে drift মাপা আর retrain-এর নিয়ম বানানো
  • Feedback loop, rollback, দলের ভূমিকা, নথি, অবসর আর আপনার পরের পথ

শেখানোর অংশের শেষ পর্ব এটি; এরপর মডিউল ১৪ পুরোটাই পরীক্ষা আর ইন্টারভিউর প্রস্তুতি। পর্ব ৭৩ (ক্যাপস্টোন — বাংলা পণ্য-রিভিউ থেকে একটি পূর্ণ AI প্রকল্প)-এ হাতে-কলমে একটি প্রকল্প শেষ হয়েছে; এখানে ক্যামেরা পেছনে সরে: প্রকল্প কোথা থেকে আসে, deploy-এর পরে কী হয়, কবে বন্ধ হয়। চলমান উদাহরণ বাজারঘরের (কাল্পনিক অনলাইন দোকান) “ফেরত-ঝুঁকি” প্রকল্প; সব ডেটা seed দেওয়া কোডে বানানো, কোনো আসল প্রতিষ্ঠান বা মানুষের নয়।

চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python; pandas 3.0.2, scipy 1.18.0, scikit-learn 1.8.0). প্রথমবার scikit-learn নামতে প্রায় ১০ সেকেন্ড লাগে। ব্লক ২ আর ৪ পাতার ভেতরে রাখা bazar_returns.py module ব্যবহার করে।

১ · জীবনচক্রের মানচিত্র — ধারণা থেকে অবসর

অনেক টিউটোরিয়াল শুরু হয় “এই নিন ডেটা, model বানান” দিয়ে, আর শেষ হয় একটি accuracy সংখ্যায়। বাস্তবে এই অংশটি ছোট। আগে প্রশ্ন: কোন সমস্যাটি সমাধানের যোগ্য? পরে প্রশ্ন: চালু model কে দেখবেন, আর কবে সেটি বদলাবেন বা বন্ধ করবেন? এই পুরো পথই প্রকল্পের জীবনচক্র (lifecycle); deploy-এর পরের কাজ দায়বদ্ধভাবে চালানোর অভ্যাস আর হাতিয়ারকে বলা হয় MLOps.

চায়ের দোকানে নতুন নাস্তা যোগ করার কথা ভাবুন। দোকানি আগে ভাবেন কোনটি বিক্রি হবে আর চুলায় বানানো সম্ভব কি না (বাছাই); পুরোনো সিঙাড়ার সঙ্গে তুলনা করেন (baseline); কয়েকজন নিয়মিত খদ্দেরকে আগে খাওয়ান (canary); রোজ খাতায় বিক্রি লেখেন (নজরদারি); রুচি বদলালে রেসিপি বদলান (retraining); কেউ না খেলে মেনু থেকে তুলে দেন (অবসর)।

একটি AI প্রকল্পের আটটি ধাপ আর ফিরে আসার পথ

The lifecycle: select → frame → check feasibility → baseline & experiments → deploy → monitor → retrain (loop) → retire

AI project lifecycle with a retraining loop ১ · সমস্যা বাছাই impact × feasibility ২ · Framing label, সিদ্ধান্ত, metric ৩ · সম্ভাব্যতা ডেটা, গোপনীয়তা, buy? ৪ · Baseline, পরীক্ষা rule → linear → জটিল ৫ · Deploy batch/API, canary ৬ · নজরদারি drift, PSI, label ৭ · Retrain নিয়মিত বা ট্রিগারে ৮ · অবসর বন্ধ, নথি, মোছা ড্যাশ তীর: retrain-এর পরে আবার যাচাই

ছবিটি চক্র: retrain-এর পর নতুন model আবার পরীক্ষার ধাপ পেরোয়। আরও পেছনে ফিরে যাওয়াও সাধারণ, যেমন ডেটা দেখে বোঝা গেল label-এর সংজ্ঞাই ভুল (ধাপ ২)।

ইতিহাসের টুকরো: এমন ধাপভিত্তিক কাঠামোর মধ্যে সবচেয়ে পরিচিত CRISP-DM (Cross-Industry Standard Process for Data Mining). প্রচলিত বর্ণনা অনুযায়ী ১৯৯০-এর দশকের শেষ দিকে কয়েকটি ইউরোপীয় প্রতিষ্ঠানের একটি কনসোর্টিয়াম এটি তৈরি শুরু করে, আর ১.০ সংস্করণের গাইড প্রকাশিত হয় প্রায় ২০০০ সালে। এর ছয় ধাপ: ব্যবসা বোঝা, ডেটা বোঝা, ডেটা প্রস্তুতি, modeling, মূল্যায়ন আর deployment, তীর দিয়ে চক্রাকারে জোড়া। সমালোচনা: এতে deploy-এর পরের নজরদারি আর retraining প্রায় নেই।

নিচের stepper বাজারঘরের ফেরত-ঝুঁকিকে সাতটি ধাপে নিয়ে যায়; সংখ্যাগুলো এই পাতার ব্লকের আসল আউটপুট।

  1. বাছাইকোনটি আগে
  2. Framinglabel, সিদ্ধান্ত
  3. যাচাইডেটা, গোপনীয়তা
  4. পরীক্ষাbaseline, log
  5. Deployshadow, canary
  6. নজরদারিretrain, অবসর
ধাপ ১ · বাছাইব্লক ১-এর নম্বর
return_risk   impact 4.0 × feasible 4.25 = 17.00
eid_demand    4.5 × 3.25 = 14.62
chatbot       4.0 × 2.00 =  8.00
সিদ্ধান্ত: আগে ফেরত-ঝুঁকি
ধাপ ২ · Framingলিখিত সংজ্ঞা
label  : ডেলিভারির ৭ দিনের মধ্যে ফেরত
         বা দরজায় প্রত্যাখ্যান = 1
সিদ্ধান্ত: ঝুঁকি বেশি হলে পাঠানোর আগে ফোনে নিশ্চিত
ব্যবসার মাপ: প্রতি ১০০ অর্ডারে ফেরতের খরচ
ধাপ ৩ · যাচাইশুরুর আগে
ডেটা   : ৩ মাসে ২,৪০০ অর্ডার, ফেরত ৪৫.৩%
label  : জানা যায় প্রায় এক মাস পরে
গোপনীয়তা: ফোন নম্বর শুধু ডেলিভারি নিশ্চিত করতে
build  : নিজে, ছোট model; কেনা সেবা লাগবে না
ধাপ ৪ · পরীক্ষাব্লক ২-এর run log
run 1  rule_cloth      val_auc 0.7047
run 2  logreg C=1.0    val_auc 0.7824  ← বাছাই
run 3  tree depth 8    val_auc 0.6982
test AUC (একবারই খোলা) 0.7664
ধাপ ৫ · Deployঘণ্টায় একবার batch
সপ্তাহ ১–২: shadow — শুধু score লেখা, কোনো ফোন নয়
সপ্তাহ ৩  : canary — ১০% অর্ডারে ফোন
এরপর      : A/B test, ফেরতের খরচ তুলনা
rollback   : আগের নিয়মে এক ধাপে ফেরা
ধাপ ৬ · নজরদারিব্লক ৪-এর মাস ৬–৭
input PSI (দাম)    0.012, 0.013  → শান্ত
accuracy, মাস ৬    0.586
accuracy, মাস ৭    0.529  (baseline 0.599)
ট্রিগার: পরপর দুই মাস 0.65-এর নিচে
ধাপ ৭ · retrain বা অবসরমাস ৮
retrain: মাস ৬–৭-এর ১,৬০০ অর্ডারে
মাস ৮ accuracy: পুরোনো 0.480 → নতুন 0.670
অবসর কবে: ফেরত এত কমে যে সরল নিয়মই যথেষ্ট,
          বা সিদ্ধান্তটিই আর নেওয়া হয় না
  1. বাছাই। ফেরত-ঝুঁকির impact × feasibility সবচেয়ে বেশি (১৭.০০); chatbot শেষে (৮.০০)।
  2. Framing. “ফেরত” কী, model কোন সিদ্ধান্তে কাজে লাগবে, আর সফলতার ব্যবসায়িক মাপ: লিখিত।
  3. যাচাই। ২,৪০০ অর্ডার, ফেরত ৪৫.৩%, label আসে দেরিতে; ফোন নম্বর ডেলিভারির উদ্দেশ্যেই।
  4. পরীক্ষা। নিয়ম ০.৭০৪৭, logistic regression ০.৭৮২৪, tree ০.৬৯৮২; test একবার: ০.৭৬৬৪.
  5. Deploy. আগে shadow, তারপর ১০% অর্ডারে canary আর A/B test; ফেরার পথ তৈরি।
  6. নজরদারি। PSI প্রায় শূন্য, তবু accuracy ০.৫৮৬, ০.৫২৯; শেষটি baseline (০.৫৯৯)-এর নিচে।
  7. Retrain বা অবসর। মাস ৮-এ ০.৪৮০ থেকে ০.৬৭০; সমস্যা ফুরোলে পরিকল্পিত অবসর।

২ · কোন সমস্যা সমাধানের যোগ্য — impact × feasibility, আর কখন ML নয়

বাজারঘরের সভায় পাঁচটি ধারণা এসেছে:

  • ফেরত-ঝুঁকি (return_risk): কোন অর্ডার ফেরত আসার সম্ভাবনা বেশি, তা আগে অনুমান করা, যাতে পাঠানোর আগে গ্রাহককে ফোনে নিশ্চিত করা যায়।
  • রিভিউ বাছাই (review_triage): বাংলা রিভিউয়ের মধ্যে অভিযোগগুলো (ভাঙা পণ্য, দেরি) আলাদা করে গ্রাহকসেবার সারিতে আগে পাঠানো; পর্ব ৬৭-এর classifier এর কাছাকাছি।
  • ঈদের আগে চাহিদার পূর্বাভাস (eid_demand): কোন পণ্য কত পিস মজুত রাখবেন; পর্ব ৫৪-এর “ঈদের আগে ১০০ না ২০০ পিস” প্রশ্নটির ডেটা-চালিত রূপ।
  • Chatbot: গ্রাহকের প্রশ্নের স্বয়ংক্রিয় উত্তর, একটি ভাষা model দিয়ে (পর্ব ৭০)।
  • প্রতারণার সংকেত (fraud_flag): ভুয়া অর্ডার আগে ধরা (পর্ব ৬০-এর উদাহরণ)।

সবগুলোই আকর্ষণীয়, কিন্তু দল ছোট। একটি সরল হাতিয়ার impact × feasibility ছক: প্রতিটি ধারণাকে দুই অক্ষে নম্বর দেওয়া। Impact মানে সফল হলে লাভ কত আর কতজনের কাজে লাগে। Feasibility মানে কতটা করা সম্ভব: ডেটা আছে কি না, label কতটা ভরসাযোগ্য, ভুল হলে ক্ষতি কত, আর কাজ কত। নম্বরগুলো মাপা সত্য নয়, দলের আলোচনার অনুমান; ছকের আসল কাজ আলোচনাকে খোলা আর লিখিত রাখা।

import pandas as pd
import matplotlib.pyplot as plt
pd.set_option("display.width", 90)
# ১–৫ নম্বর: দলের আলোচনায় দেওয়া অনুমান (কাল্পনিক), মাপা সংখ্যা নয়
ideas = pd.DataFrame({
    "idea":   ["return_risk", "review_triage", "eid_demand", "chatbot", "fraud_flag"],
    "value":  [4, 3, 5, 3, 5],    # সফল হলে ব্যবসার লাভ
    "reach":  [4, 3, 4, 5, 2],    # কত অর্ডার/মানুষের কাজে লাগে
    "data":   [5, 4, 2, 2, 3],    # ডেটা আছে কি না, কত দিনের
    "labels": [4, 3, 4, 2, 2],    # label কত ভরসাযোগ্য ও কত দ্রুত আসে
    "safety": [4, 4, 4, 2, 2],    # ভুল হলে ক্ষতি কম = বেশি নম্বর
    "effort": [4, 4, 3, 2, 3],    # কাজ কম = বেশি নম্বর
}).set_index("idea")
ideas["impact"] = ideas[["value", "reach"]].mean(axis=1)
ideas["feasible"] = ideas[["data", "labels", "safety", "effort"]].mean(axis=1)
ideas["priority"] = ideas.impact * ideas.feasible
print(ideas.sort_values("priority", ascending=False).round(2).to_string())
w = {"data": 0.4, "labels": 0.3, "safety": 0.2, "effort": 0.1}   # অন্য ওজনে কি ক্রম বদলায়?
alt = ideas.impact * sum(ideas[k] * v for k, v in w.items())
print("\nডেটা-ভারী ওজনে ক্রম:", alt.sort_values(ascending=False).round(2).to_dict())
ax = ideas.plot.scatter(x="feasible", y="impact", s=60, figsize=(5.6, 4.0),
                        xlim=(1, 5.2), ylim=(1, 5.2), title="Impact x feasibility (team estimates)")
for name, r in ideas.iterrows():
    ax.annotate(name, (r.feasible, r.impact), xytext=(5, 4), textcoords="offset points")
ax.axvline(3, ls="--", c="gray"); ax.axhline(3, ls="--", c="gray")
ax.set_xlabel("feasibility (1-5)"); ax.set_ylabel("impact (1-5)"); plt.show()

আসল আউটপুট (ব্রাউজারের Python, pandas 3.0.2; ছবিটি এর নিচে আঁকা হয়):

               value  reach  data  labels  safety  effort  impact  feasible  priority
idea
return_risk        4      4     5       4       4       4     4.0      4.25     17.00
eid_demand         5      4     2       4       4       3     4.5      3.25     14.62
review_triage      3      3     4       3       4       4     3.0      3.75     11.25
fraud_flag         5      2     3       2       2       3     3.5      2.50      8.75
chatbot            3      5     2       2       2       2     4.0      2.00      8.00

ডেটা-ভারী ওজনে ক্রম: {'return_risk': 17.6, 'eid_demand': 13.95, 'review_triage': 11.1, 'fraud_flag': 8.75, 'chatbot': 8.0}

পড়ুন সারি ধরে। ফেরত-ঝুঁকি দুই অক্ষেই ভালো: খাতা পুরোনো, আর ফেরতের label নিজে থেকেই লেখা হয়। ঈদের চাহিদার impact সবচেয়ে বেশি (৪.৫), কিন্তু ডেটায় ২: বছরে মাত্র দুটি ঈদ, তাই নমুনা হাতে-গোনা। Chatbot-এর নাগাল বড় (৫), কিন্তু ভুল উত্তরের ঝুঁকি বেশি আর মান যাচাইয়ের label কম। প্রতারণায় নিশ্চিত উদাহরণ কম, আর সৎ গ্রাহককে প্রতারক বলার ক্ষতি বড়।

ক্রম কতটা স্থির? ডেটা-ভারী ওজনেও ক্রম একই। ওজন বদলে ক্রম উল্টে গেলে সিদ্ধান্তটি ওজনের ওপর দাঁড়িয়ে; তখন আলোচনা দরকার, আরও সংখ্যা নয়।

কখন ML নয় — নিয়ম, SQL, বা কাজের ধারা ঠিক করা

ছকে ওঠার আগেই একটি প্রশ্ন: সমস্যাটির জন্য শেখা model কি আদৌ দরকার? অনেক সময় উত্তর “না”, আর সেটিই সবচেয়ে সস্তা, দ্রুত আর ব্যাখ্যাযোগ্য উত্তর।

📜 নিয়ম

নীতি যখন স্পষ্ট আর লিখে ফেলা যায়: “খোলা প্যাকেটের অন্তর্বাস ফেরত নয়।” পর্ব ৪৭-এ বাজারঘরের ফেরত-নীতি ২০টি নিয়মের rule-based system হিসেবেই ঠিকঠাক চলেছিল।

স্পষ্ট নীতি

🗃️ একটি SQL query

“গত মাসে কোন পণ্য সবচেয়ে বেশি ফেরত এসেছে?” এটি অনুমান নয়, গোনা: GROUP BY আর ORDER BY (DBMS ও SQL কোর্সের aggregation পর্ব)।

রিপোর্ট

🔧 কাজের ধারা ঠিক করা

পোশাক ফেরতের বড় কারণ যদি পাতায় সাইজ চার্ট না থাকা হয়, তবে প্রথম কাজ সাইজ চার্ট যোগ করা, ঝুঁকির model নয়।

মূল কারণ

🤝 মানুষ

দিনে দশটি সিদ্ধান্তে একজন অভিজ্ঞ কর্মীই যথেষ্ট; model-এর খরচ তখন লাভের চেয়ে বেশি।

কম পরিমাণ

সরল পরীক্ষা: উত্তরটি লিখে ফেলা যায়? নিয়ম। শুধু গোনা? Query. ডেটার জটিল, বদলাতে থাকা প্যাটার্নে লুকোনো, আর সিদ্ধান্ত অনেক? তখনই ML. Martin Zinkevich-এর বহুল-পঠিত “Rules of Machine Learning”-এর প্রথম নিয়মও প্রায় এমন: ML ছাড়া পণ্য চালু করতে ভয় পাবেন না। নিয়মটিই পরে আপনার baseline হয়।

৩ · Framing আর সম্ভাব্যতা — label, সিদ্ধান্ত, metric, ডেটা আর baseline

Label-এর সংজ্ঞা আর তার দ্ব্যর্থতা

“ফেরত” শব্দটি সহজ শোনায়। কিন্তু খাতায় অন্তত পাঁচ রকম ঘটনা আছে: দরজায় প্রত্যাখ্যান (ক্যাশ অন ডেলিভারিতে খুব সাধারণ), হাতে পেয়ে সাত দিনের মধ্যে ফেরত, সাইজ বদল (exchange), অর্ধেক পণ্য ফেরত, আর courier-এর হারানো পার্সেল। কোনটি label-এ ১ হবে? সাইজ বদলকে ফেরত ধরলে model পোশাককে খুব ঝুঁকিপূর্ণ ভাববে; courier-এর হারানো পার্সেলকে ধরলে model আসলে courier-এর মান শিখবে, গ্রাহকের আচরণ নয়, যা পর্ব ৭২-এর label bias-এর মতো ফাঁদ। সময়ের জানালাও সংজ্ঞার অংশ। এই পর্বে label = “ডেলিভারির সাত দিনের মধ্যে ফেরত বা দরজায় প্রত্যাখ্যান”.

Model কোন সিদ্ধান্তে কাজে লাগবে, আর ভুলের দাম

একটি score নিজে কিছু করে না; তার পরের কাজটি গুরুত্বপূর্ণ। বাজারঘরের সিদ্ধান্ত: ঝুঁকি বেশি হলে পাঠানোর আগে একজন কর্মী গ্রাহককে ফোন করে অর্ডার নিশ্চিত করবেন। তাহলে দুই রকম ভুলের দাম আলাদা। False positive: অকারণ একটি ফোন, কর্মীর কয়েক মিনিট আর হয়তো একটু বিরক্ত গ্রাহক। False negative: ফেরতের পরিবহন খরচ আর নষ্ট সময়। এই অসমতা থেকে threshold ঠিক হয়, ০.৫ থেকে নয়: পর্ব ৬০-এর cost matrix আর পর্ব ৫৪-এর expected cost এখানে সরাসরি কাজে লাগে। সিদ্ধান্ত “অর্ডার বাতিল” হলে false positive অনেক দামি, আর threshold অনেক উঁচু লাগত।

Offline metric বনাম ব্যবসার মাপ

Validation set-এ মাপা AUC বা accuracy একটি offline metric: পুরোনো ডেটায় model কতটা ভালো সাজায়। ব্যবসা জানতে চায় অন্য কিছু, একটি business metric: প্রতি ১০০ অর্ডারে ফেরতের খরচ কি কমল, আর ফোনের খরচ যোগ করেও কি লাভ থাকল? দুটি এক নয়: ফোন পেয়ে কিছু গ্রাহক এমন অর্ডারও বাতিল করতে পারেন যা রাখতেন। আসল প্রভাব জানার নির্ভরযোগ্য উপায় একটি A/B test: ঝুঁকিপূর্ণ অর্ডারের এলোমেলো অর্ধেকে ফোন, বাকিতে নয়, তারপর মোট খরচ তুলনা (হিসাব পর্ব ৩৭-এ)।

সম্ভাব্যতার যাচাই, কোড লেখার আগে

প্রশ্নকেন জরুরিফেরত-ঝুঁকির উত্তর (কাল্পনিক)
ডেটা আছে?সিদ্ধান্তের মুহূর্তে জানা তথ্যই feature (পর্ব ৫৬-এর leakage)দাম, পণ্যের ধরন, পরিশোধ, প্রথম অর্ডার: সবই তখন জানা
Label কতটা ভালো?ভুল বা দেরির label দুটিকেই দুর্বল করেখাতায় স্বয়ংক্রিয়, তবে চূড়ান্ত হয় প্রায় এক মাস পরে
পরিমাণ যথেষ্ট?ছোট ডেটায় মাপ নড়েমাসে প্রায় ৮০০; সরল model-এ চলে
গোপনীয়তা আর সম্মতিডেটা যে উদ্দেশ্যে নেওয়া, তার বাইরে ব্যবহারে নতুন সম্মতি বা ভিত্তি লাগে (পর্ব ৭২)ফোন নম্বর ডেলিভারি নিশ্চিত করতেই নেওয়া, তাই নিশ্চিতকরণ ফোন একই উদ্দেশ্যে; বিজ্ঞাপনে ব্যবহার নিষেধ
Build, buy, না pretrained?নিজে বানানো, তৈরি সেবা কেনা, বা pretrained model যাচাই করে ব্যবহারনিজের টেবিলের ডেটা, ছোট model: নিজে বানানো

Build বনাম buy-এর উত্তর কাজভেদে আলাদা। রিভিউ বাছাইয়ে একটি pretrained model নিজের লেবেল করা রিভিউতে যাচাই করে ব্যবহার করা যুক্তিসংগত হতে পারে, লাইসেন্স আর model card পড়ে (পর্ব ৭১)। Chatbot-এ কেনা সেবা দ্রুত, কিন্তু গ্রাহকের কথা বাইরের সার্ভারে যায়, খরচ ব্যবহারের সঙ্গে বাড়ে, আর ভুল উত্তরের দায় তবু আপনার।

Baseline আগে, জটিলতা পরে

প্রথম model কখনো জটিল হওয়া উচিত নয়। ক্রম: (১) একটি সরল নিয়ম, যেমন “পোশাক হলে ঝুঁকি”; (২) একটি linear model, যেমন logistic regression; (৩) জটিল model শুধু যদি তা validation-এ স্থির উন্নতি দেয় আর বাড়তি খরচ পুষিয়ে দেয়। Baseline ছাড়া “AUC ০.৭৮”-এর অর্থ নেই: নিয়মটি ০.৭৭ দিলে model প্রায় কিছুই যোগ করেনি। পর্ব ৬৬-এর DummyClassifier সবচেয়ে নিচের তাক, ব্যবসার নিয়ম তার ওপরে।

৪ · Experiment tracking — seed, config, ডেটার version আর ফলের খাতা

তিন সপ্তাহ পরে কেউ জিজ্ঞেস করলেন: “গত মাসের ০.৭৮ AUC কোন ডেটায়, কোন সেটিংয়ে এসেছিল?” খাতা না থাকলে উত্তর অনুমান। Experiment tracking মানে প্রতিটি run-এর জন্য অন্তত এগুলো লিখে রাখা: কোডের version (Git commit), ডেটার version (একটি hash বা তারিখ), config (model আর hyperparameter), seed, package-এর version, আর ফল। ফোল্ডার, lockfile আর ডেটার version-এর ভিত্তি পর্ব ১২-এ; validation আর test-এর নিয়ম পর্ব ৫৬ ও পর্ব ৬৬-এ।

সবচেয়ে সরল খাতা একটি JSON Lines ফাইল: প্রতিটি run এক লাইনের একটি JSON. যোগ করা সহজ, Git-এ পার্থক্য দেখা যায়, pandas এক লাইনে পড়ে। নিচের ডেটা আসে পাতার ভেতরের এই module থেকে:

def make_orders(n=2000, seed=42, change=0.0):
    """change 0 = পুরোনো অবস্থা; 1 = নতুন নিয়ম পুরো চালু (concept drift)"""
    rng = np.random.default_rng(seed)
    price = rng.lognormal(7.0, 0.6, n).round()           # অর্ডারের দাম, টাকা
    cloth = (rng.random(n) < 0.4).astype(int)             # পোশাক কি না
    cod = (rng.random(n) < 0.6).astype(int)               # ক্যাশ অন ডেলিভারি
    first = (rng.random(n) < 0.3).astype(int)             # প্রথম অর্ডার
    w_cloth = 1.8 - 3.2 * change                          # সাইজ চার্টে পোশাকের ফেরত কমে
    w_cod = 0.3 + 1.6 * change                            # নতুন courier: দরজায় ফেরত বাড়ে
    z = (-1.3 + w_cloth * cloth + w_cod * cod + 0.7 * first
         + 0.8 * np.log(price / 1100))
    returned = (rng.random(n) < 1 / (1 + np.exp(-z))).astype(int)
    return pd.DataFrame({"price": price, "cloth": cloth, "cod": cod,
                         "first": first, "returned": returned})

শুরুতে (change = 0) পোশাক আর দামি অর্ডারে ফেরত বেশি; change লাগবে অংশ ৭-এ। ব্লকটি তিনটি run চালায়: নিয়ম-baseline, logistic regression আর গভীর decision tree. Test শুরুতেই তালাবদ্ধ; বাছাই validation দেখে, তারপর বিজয়ীর test একবার।

import json, hashlib
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
from bazar_returns import make_orders

df = make_orders(n=3000, seed=42)                    # কাল্পনিক অর্ডার, ফেরত = 1
data_ver = hashlib.sha256(df.to_csv(index=False).encode()).hexdigest()[:10]
dev, test = train_test_split(df, test_size=0.2, random_state=0, stratify=df.returned)
tr, va = train_test_split(dev, test_size=0.25, random_state=42, stratify=dev.returned)
X = lambda d: d[["cloth", "cod", "first"]].assign(logp=np.log(d.price))
configs = [{"model": "rule_cloth"}, {"model": "logreg", "C": 1.0},
           {"model": "tree", "max_depth": 8}]
models = {}
with open("runs.jsonl", "w", encoding="utf-8") as f:   # এক run = এক লাইন JSON
    for i, cfg in enumerate(configs, start=1):
        if cfg["model"] == "rule_cloth":                # baseline নিয়ম: পোশাক = ঝুঁকি
            m, s = None, va.cloth.to_numpy()
        else:
            m = (LogisticRegression(C=cfg["C"]) if cfg["model"] == "logreg" else
                 DecisionTreeClassifier(max_depth=cfg["max_depth"], random_state=42))
            s = m.fit(X(tr), tr.returned).predict_proba(X(va))[:, 1]
        models[i] = m
        run = {"run": i, "seed": 42, "data": data_ver, "n_train": len(tr), **cfg,
               "val_auc": round(float(roc_auc_score(va.returned, s)), 4)}
        f.write(json.dumps(run) + "\n")
lines = open("runs.jsonl", encoding="utf-8").read().splitlines()
print("\n".join(lines))
runs = pd.DataFrame([json.loads(l) for l in lines])
best = int(runs.loc[runs.val_auc.idxmax(), "run"])
p = models[best].predict_proba(X(test))[:, 1]          # test খোলা হলো একবারই
print(f"বাছাই: run {best} | test AUC (একবার) = {roc_auc_score(test.returned, p):.4f}")

আসল আউটপুট (ব্রাউজারের Python, scikit-learn 1.8.0):

{"run": 1, "seed": 42, "data": "71ee2acb4e", "n_train": 1800, "model": "rule_cloth", "val_auc": 0.7047}
{"run": 2, "seed": 42, "data": "71ee2acb4e", "n_train": 1800, "model": "logreg", "C": 1.0, "val_auc": 0.7824}
{"run": 3, "seed": 42, "data": "71ee2acb4e", "n_train": 1800, "model": "tree", "max_depth": 8, "val_auc": 0.6982}
বাছাই: run 2 | test AUC (একবার) = 0.7664

খাতাটি পড়ুন। তিনটি লাইনেই একই data hash (ডেটার SHA-256-এর প্রথম ১০টি অক্ষর), তাই তুলনাটি একই ডেটায়। নিয়ম-baseline ০.৭০৪৭; logistic regression তার চেয়ে ০.০৭৮ বেশি, অর্থপূর্ণ উন্নতি। গভীর tree (depth ৮) ০.৬৯৮২, নিয়মের চেয়েও খারাপ: ১,৮০০টি সারিতে সে মুখস্থ করেছে (পর্ব ৬১)। জটিলতা নিজে থেকে লাভ দেয় না।

Test ০.৭৬৬৪, validation-এর চেয়ে কম। স্বাভাবিক: সবচেয়ে ভালো validation বাছাই করলে তাতে একটু ভাগ্যও থাকে। তাই test আলাদা আর একবার খোলা। এক seed, এক ভাগের ফল।

একই কাজ MLflow দিয়ে (ডেস্কটপে সত্যিকারের run)

দল বড় হলে বা run শত শত হলে হাতের JSON খাতার বদলে একটি tracking server সুবিধাজনক। MLflow একটি open-source হাতিয়ার: প্রতিটি run-এর parameter, metric, tag আর ফাইল একটি database-এ রাখে, আর একটি ওয়েব পাতায় তুলনা দেখায়। ব্রাউজারের Python-এ এটি চলে না, তাই একটি ডেস্কটপ venv-এ একই ডেটা আর split দিয়ে চালানো হলো (লেখার সময়ের version: Python 3.14.4, MLflow 3.18.0, scikit-learn 1.9.1; শুধু CPU).

# ডেটা, dev/test আর tr/va ভাগ, X: ব্লক ২-এর মতোই
import mlflow
mlflow.set_tracking_uri("sqlite:///mlflow.db")       # এই ফোল্ডারে একটি ছোট database
mlflow.set_experiment("return-risk")
for name, m in [("logreg_C1", LogisticRegression(C=1.0)),
                ("tree_d8", DecisionTreeClassifier(max_depth=8, random_state=42))]:
    with mlflow.start_run(run_name=name):
        mlflow.log_params({"seed": 42, "data_version": data_ver, **m.get_params()})
        m.fit(X(tr), tr.returned)
        mlflow.log_metric("val_auc", roc_auc_score(va.returned, m.predict_proba(X(va))[:, 1]))
        mlflow.set_tag("stage", "experiment")
runs = mlflow.search_runs(experiment_names=["return-risk"], order_by=["metrics.val_auc DESC"])
print(runs[["tags.mlflow.runName", "params.data_version", "metrics.val_auc"]]
      .round(4).to_string(index=False))
~/bazarghar-ai$ python track_mlflow.py
… INFO mlflow.store.db.utils: Creating initial MLflow database tables...
… INFO mlflow.store.db.utils: Updating database tables
… INFO mlflow.tracking.fluent: Experiment with name 'return-risk' does not exist. Creating a new experiment.
tags.mlflow.runName params.data_version  metrics.val_auc
          logreg_C1          4674300605           0.7824
            tree_d8          4674300605           0.6982

AUC পাতার খাতার সঙ্গে হুবহু মেলে (তারিখ-সময় আর কিছু INFO লাইন “…” দিয়ে ছাঁটা)। কিন্তু ডেটার hash আলাদা: ব্রাউজারে 71ee2acb4e, ডেস্কটপে 4674300605. Hash লেখার প্রতিটি অক্ষর ধরে, আর NumPy বা pandas-এর ভিন্ন version-এ CSV-র লেখা সামান্য আলাদা হতে পারে; তাই ডেটার version একই পরিবেশে তুলনা করুন, বা সংরক্ষিত ফাইলের byte থেকেই hash নিন। mlflow ui এখানে চালানো হয়নি। হাতিয়ার খাতাকে সহজ করে, কিন্তু commit, ডেটার version আর seed লেখার শৃঙ্খলা আপনার।

৫ · Deploy — batch না API, latency ও খরচের বাজেট, shadow আর canary

Deployment মানে model-কে এমন জায়গায় বসানো যেখান থেকে আসল সিদ্ধান্তে তার অনুমান ব্যবহার হয়। দুটি মূল ধরন:

দিকBatch scoringOnline inference (API)
কখন চলেসময় ধরে, যেমন প্রতি ঘণ্টায় বা রাতে, অনেক সারি একসঙ্গেপ্রতিটি অনুরোধ আসামাত্র
ফল কোথায়একটি টেবিলে, যা অ্যাপ বা কর্মীরা পড়েনঅনুরোধের উত্তরে, সঙ্গে সঙ্গে
সহজতাসহজ, ব্যর্থ হলে আবার চালানো যায়সার্ভার সবসময় চালু, বিকল্প নিয়ম লাগে
বাজারঘরে মানানসইফেরত-ঝুঁকি: অর্ডার পাঠানো হয় পরের সকালে, তাই ঘণ্টায় একবার যথেষ্টপ্রতারণার সংকেত checkout-এর মুহূর্তে, বা রিভিউ লেখার সঙ্গে সঙ্গে বাছাই

প্রথম প্রশ্ন সবসময়: সিদ্ধান্তটি কত তাড়াতাড়ি দরকার? উত্তর “কাল সকালে” হলে batch; “এই মুহূর্তে” হলে API. নকশার বিস্তারিত এই সাইটের System Design কোর্সের batch ও stream processing আর REST API design পর্বে; Python-এ একটি ছোট API লেখার উদাহরণ Full-Stack কোর্সের FastAPI পর্বে, আর নির্ধারিত কাজের ধরন background jobs পর্বে। Model ফাইল সংরক্ষণ, joblib-এর ঝুঁকি আর version মিলিয়ে রাখার কথা পর্ব ৬৪-এ।

Latency আর খরচের বাজেট

Online হলে আগে একটি latency বাজেট লিখুন: checkout-এর পুরো উত্তরের সময়ের একটি ছোট অংশ model পাবে, আর মাপবেন গড় নয়, সবচেয়ে ধীর অনুরোধগুলো (p95, p99; latency ও throughput). বাজেট ভাঙলে সরল নিয়মে উত্তর, অর্ডার আটকানো নয়। খরচের বাজেটও তেমন: সবসময় চালু সার্ভার না রাতের ছোট job, তা আপনার পরিমাণ আর দামের ওপর নির্ভর করে।

নীরব ফাঁদ: training-এ দাম টাকায়, অ্যাপে পয়সায় এলে model চুপচাপ ভুল করে। প্রতিকার দুই জায়গায় একই pipeline কোড।

Shadow আর canary — ঝুঁকি কমিয়ে চালু করা

  • Shadow deployment: নতুন model আসল অর্ডারে পাশাপাশি চলে আর score লেখে, কোনো ফোন হয় না; label এলে ঝুঁকি ছাড়াই তার আসল মান দেখা যায়।
  • Canary release: তারপর ধরুন ১০% অর্ডারে সত্যিকারের সিদ্ধান্ত; সব ঠিক থাকলে ধাপে ধাপে বাড়ানো।
  • Rollback: ভুল দেখলে এক ধাপে আগের নিয়ম বা model-এ ফেরা; আগের ফাইল, config আর একটি সুইচ প্রস্তুত রাখা।

এগুলো software-এর পরিচিত ধারণা; কৌশলগুলোর তুলনা Cloud ও DevOps কোর্সের deployment strategies আর System Design-এর deployment ও release পর্বে। Model-এর বাড়তি জটিলতা: তার ভুল দেখা যায় label আসার পরে।

৬ · নজরদারি — data drift, PSI, KS test, prediction drift আর label delay

Deploy-এর দিন model যা জানে, তা training-এর মাসগুলোর জগৎ। জগৎ বদলায়: দাম বাড়ে, ঈদ আসে, নতুন courier আসে। Model monitoring-এ চার স্তর দেখা হয়, সহজ থেকে কঠিন:

  1. ব্যবস্থা: job কি সময়মতো চলেছে, API-র latency আর error কত (observability).
  2. ডেটা: missing value, অসম্ভব মান (ঋণাত্মক দাম), আর data drift: input-এর বণ্টন সরে যাওয়া। Label লাগে না, তাই সঙ্গে সঙ্গে পাওয়া যায়।
  3. অনুমান: prediction drift, যেমন হঠাৎ অনেক বেশি অর্ডারকে ঝুঁকিপূর্ণ বলা। এটিও সঙ্গে সঙ্গে পাওয়া যায়।
  4. ফল: আসল accuracy বা ব্যবসার মাপ। এর জন্য label লাগে, আর label আসে দেরিতে: label delay. ফেরত চূড়ান্ত হতে প্রায় এক মাস; প্রতারণা ধরা পড়তে কখনো আরও বেশি।

PSI — বণ্টন কতটা সরল, একটি সংখ্যায়

Population Stability Index (PSI) ঋণ-scoring-এর চর্চা থেকে আসা একটি সরল মাপ। পুরোনো (reference) ডেটাকে কয়েকটি bin-এ ভাগ করুন, সাধারণত quantile দিয়ে যাতে প্রতিটি bin-এ সমান ভাগ থাকে। তারপর প্রতিটি bin-এ পুরোনো ভাগ e আর নতুন ভাগ a তুলনা করুন:

PSI = ∑ (ai − ei) × ln(ai ÷ ei)

প্রতিটি পদ শূন্য বা ধনাত্মক, কারণ (a − e) আর ln(a ÷ e)-এর চিহ্ন সবসময় একই। প্রচলিত হাতের নিয়ম: ০.১-এর কম “স্থির”, ০.১–০.২৫ “নজরে রাখুন”, ০.২৫-এর বেশি “বড় পরিবর্তন”। সীমাগুলো চর্চা থেকে আসা, কোনো একক উৎস থেকে নয়, আর bin-এর সংখ্যা ও নমুনার আকারে PSI বদলায়; নিজের ডেটায় যাচাই করুন। খালি bin-এ ln(০) অসীম, তাই ভাগগুলোকে ছোট একটি সংখ্যায় (যেমন ০.০০০১) নিচে আটকানো হয়।

অর্ডারের দাম: training-এর মাস বনাম এই মাসের ৬০০টি অর্ডার (গোনা কাল্পনিক)
এই ধাপ
bin ১ (≤ ৭২৩ টাকা)
bin ২ (৭২৩–১০৯৬)
bin ৩ (১০৯৬–১৬০৫)
bin ৪ (১৬০৫-এর বেশি)
ফল
  1. Bin বানানো। Training-এর মাসের দামের quartile (৭২৩, ১০৯৬ আর ১৬০৫ টাকা) দিয়ে চারটি bin, তাই সংজ্ঞা অনুযায়ী প্রতিটিতে e = ০.২৫. সীমাগুলো একবার ঠিক হয়, প্রতি মাসে বদলায় না।
  2. গোনা। এই মাসের ৬০০টি অর্ডার একই সীমায় ফেলা: ৯০, ১২০, ১৮০ আর ২১০টি।
  3. ভাগ। প্রতিটিকে ৬০০ দিয়ে ভাগ: ০.১৫, ০.২০, ০.৩০, ০.৩৫; যোগ ১.
  4. পার্থক্য আর অনুপাত। সস্তা দুই bin-এ ভাগ কমেছে (a − e ঋণাত্মক, অনুপাত ১-এর কম), দামি দুই bin-এ বেড়েছে। বণ্টন দামি দিকে সরছে।
  5. প্রতিটি পদ। চিহ্ন মিলে যায় বলে চারটি পদই ধনাত্মক: ০.০৫১১, ০.০১১২, ০.০০৯১ আর ০.০৩৩৬. দুই প্রান্তের bin-এই সবচেয়ে বড় অবদান।
  6. যোগ আর রায়। PSI ≈ ০.১০৫: হাতের নিয়মে “নজরে রাখুন” স্তর, ০.২৫-এর অনেক নিচে। একা এই সংখ্যা দেখে retrain নয়; কারণ খোঁজা (দাম বেড়েছে? নতুন পণ্য?) আর পরের মাস দেখা।

দ্বিতীয় হাতিয়ার দুই নমুনার Kolmogorov–Smirnov test (১৯৩০-এর দশকে Andrey Kolmogorov আর Nikolai Smirnov-এর কাজ থেকে)। এটি দুই নমুনার ECDF পাশাপাশি এঁকে সবচেয়ে বড় উল্লম্ব ফাঁক D মাপে, আর “দুটি একই বণ্টন থেকে এসেছে” এই ধারণার বিপরীতে একটি p-value দেয় (পর্ব ৩৭)। scipy-তে scipy.stats.ks_2samp. নিচের ব্লক ১২ মাসের দাম বানায়: মাস ৪ ঈদ (ঝুড়ি বড়, সাময়িক), আর মাস ৮ থেকে দাম ধীরে বাড়ে।

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import ks_2samp

rng = np.random.default_rng(42)
ref = rng.lognormal(7.0, 0.6, 3000)                  # training-এর মাসের অর্ডারের দাম
edges = np.quantile(ref, np.linspace(0, 1, 11))      # ১০টি সমান-ভাগ bin, ref থেকে
edges[0], edges[-1] = -np.inf, np.inf
def psi(e, a, edges=edges):
    pe = np.histogram(e, edges)[0] / len(e)
    pa = np.histogram(a, edges)[0] / len(a)
    pe, pa = np.clip(pe, 1e-4, None), np.clip(pa, 1e-4, None)   # log(0) এড়াতে
    return float(np.sum((pa - pe) * np.log(pa / pe)))
rows = []
for m in range(1, 13):
    shift = 0.30 if m == 4 else 0.0                  # মাস ৪ = ঈদ: বড় ঝুড়ি, সাময়িক
    shift += 0.06 * max(0, m - 7)                    # মাস ৮ থেকে দাম বাড়ছে
    cur = rng.lognormal(7.0 + shift, 0.6, 600)
    ks = ks_2samp(ref, cur)
    p = psi(ref, cur)
    rows.append({"month": m, "median_tk": int(np.median(cur)), "PSI": round(p, 3),
                 "KS_D": round(float(ks.statistic), 3), "KS_p": float(ks.pvalue),
                 "flag": "ALERT" if p >= 0.25 else ("watch" if p >= 0.10 else "")})
t = pd.DataFrame(rows)
t["KS_p"] = t.KS_p.map(lambda v: f"{v:.1e}")
print(t.to_string(index=False))
ax = t.plot(x="month", y="PSI", marker="o", figsize=(6.4, 3.2), legend=False,
            title="Order value drift: PSI vs training month (simulated)")
ax.axhline(0.10, ls=":", c="gray"); ax.axhline(0.25, ls="--", c="red")
ax.set_ylabel("PSI"); ax.set_xticks(range(1, 13)); plt.show()

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

 month  median_tk   PSI  KS_D    KS_p  flag
     1       1129 0.017 0.036 5.2e-01
     2       1055 0.018 0.031 7.1e-01
     3       1101 0.013 0.028 8.1e-01
     4       1478 0.267 0.218 2.4e-21 ALERT
     5       1177 0.012 0.057 7.9e-02
     6       1076 0.033 0.048 1.9e-01
     7       1059 0.031 0.030 7.4e-01
     8       1079 0.024 0.035 5.5e-01
     9       1242 0.072 0.106 2.5e-05
    10       1337 0.130 0.146 1.1e-09 watch
    11       1264 0.119 0.125 3.2e-07 watch
    12       1472 0.258 0.226 7.3e-23 ALERT

মাস ১–৩, ৫–৮: পরিবর্তন নেই, তবু PSI ০.০১২–০.০৩৩: নমুনার ওঠানামা।

মাস ৪ (ঈদ): PSI ০.২৬৭, সতর্কতা, কিন্তু প্রত্যাশিত আর পরের মাসেই ফিরে আসে। Drift মানেই সমস্যা নয়; প্রশ্ন হলো model ঈদের ঝুড়িতে train হয়েছিল কি না। ভালো নজরদারি ঋতুর (seasonality) জন্য আলাদা reference রাখে, বা সতর্কতার পাশে “প্রত্যাশিত” লিখে রাখে।

মাস ৯: KS-এর p = ২.৫ × ১০−৫, “পরিসংখ্যানগতভাবে তাৎপর্যপূর্ণ”, অথচ PSI মাত্র ০.০৭২. Reference-এ ৩,০০০টি সারি থাকায় KS খুব ছোট পার্থক্যও ধরে। p-value বলে পার্থক্য আছে কি না, কত বড় তা নয়; কাজের সিদ্ধান্তে আকারের মাপ (PSI, D, মধ্যমার পরিবর্তন) দেখুন। মাস ১২-এ টানা বৃদ্ধি ০.২৫৮-এ পৌঁছায়: এটি ঈদের মতো সাময়িক নয়, তাই আসল সতর্কতা।

Prediction drift আর label delay: label আসার আগের মাসে সবচেয়ে কাজের সংকেত ঝুঁকিপূর্ণ বলা অর্ডারের ভাগ। এটি হঠাৎ দ্বিগুণ হলে প্রায়ই কোনো feature-এর ভুল (একটি কলাম ফাঁকা) ধরা পড়ে। তবে পরের অংশ দেখাবে, input আর অনুমান শান্ত থাকলেও model নষ্ট হতে পারে।

৭ · Concept drift আর retraining — মান কমা, ট্রিগার, feedback loop আর rollback

Concept drift মানে input আর উত্তরের সম্পর্কটাই বদলে যাওয়া; input-এর বণ্টন একই থাকতে পারে (তুলনায় data drift বা covariate shift-এ শুধু input সরে)। গল্প: মাস ৫ থেকে বাজারঘর পোশাকের পাতায় সাইজ চার্ট আর ভিডিও কলে মাপ নেওয়া চালু করল, তাই পোশাকের ফেরত কমল; একই সময়ে নতুন courier এল, যার কাছে দরজায় প্রত্যাখ্যান বেশি, তাই ক্যাশ অন ডেলিভারির ফেরত বাড়ল। পরিবর্তন ধীরে, মাস ৫ থেকে ৯-এর মধ্যে পুরোটা। অর্ডারের ধরন (দাম, পোশাকের ভাগ, COD-এর ভাগ) একই থাকে।

নিয়ম কোডে লেখা: label এক মাস দেরিতে আসে, তাই মাস m-এর শুরুতে শুধু মাস m−১ পর্যন্ত accuracy জানা। যদি চালু model-এর শেষ দুই জানা মাসের accuracy দুটিই ০.৬৫-এর নিচে হয়, শেষ দুই মাসের ডেটায় retrain.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from bazar_returns import make_orders

X = lambda d: d[["cloth", "cod", "first"]].assign(logp=np.log(d.price))
fit = lambda d: LogisticRegression().fit(X(d), d.returned)
acc = lambda m, d: float((m.predict(X(d)) == d.returned).mean())
change = {m: min(1.0, max(0.0, (m - 4) / 5)) for m in range(-2, 13)}   # মাস ৫–৯-এ নতুন নিয়ম
month = {m: make_orders(n=800, seed=200 + m, change=change[m]) for m in range(-2, 13)}
old = fit(pd.concat([month[m] for m in (-2, -1, 0)]))   # deploy-এর আগের ৩ মাসে train
live, known, rows = old, [], []
for m in range(1, 13):
    note = ""
    # label আসে এক মাস দেরিতে: মাস m-এর শুরুতে শুধু m-1 পর্যন্ত accuracy জানা
    if len(known) >= 2 and max(known[-2:]) < 0.65:      # ট্রিগার: পরপর দুই মাস 0.65-এর নিচে
        live = fit(pd.concat([month[m - 2], month[m - 1]]))   # সাম্প্রতিক জানালা: শেষ ২ মাস
        known, note = [], f"retrain (মাস {m-2}–{m-1})"
    a = acc(live, month[m])
    known.append(a)
    rows.append({"month": m, "change": change[m], "acc_old": acc(old, month[m]),
                 "acc_live": a, "baseline": float((month[m].returned == 0).mean()),
                 "note": note})
t = pd.DataFrame(rows).round(3)
print(t.to_string(index=False))
cols = X(month[1]).columns
print("পুরোনো ওজন:", dict(zip(cols, old.coef_[0].round(2).tolist())))
print("শেষ ওজন:   ", dict(zip(cols, live.coef_[0].round(2).tolist())))
ax = t.plot(x="month", y=["acc_old", "acc_live", "baseline"], marker="o",
            figsize=(6.4, 3.2), ylim=(0.4, 0.8), title="Concept drift and triggered retraining (simulated)")
ax.axhline(0.65, ls=":", c="gray"); ax.set_ylabel("accuracy"); plt.show()

আসল আউটপুট (ব্রাউজারের Python, scikit-learn 1.8.0; ছবিটি এর নিচে):

 month  change  acc_old  acc_live  baseline              note
     1     0.0    0.725     0.725     0.545
     2     0.0    0.696     0.696     0.531
     3     0.0    0.730     0.730     0.574
     4     0.0    0.680     0.680     0.569
     5     0.2    0.655     0.655     0.519
     6     0.4    0.586     0.586     0.552
     7     0.6    0.529     0.529     0.599
     8     0.8    0.480     0.670     0.570 retrain (মাস 6–7)
     9     1.0    0.439     0.644     0.584
    10     1.0    0.436     0.671     0.609
    11     1.0    0.429     0.669     0.610
    12     1.0    0.416     0.635     0.552
পুরোনো ওজন: {'cloth': 1.82, 'cod': 0.26, 'first': 0.6, 'logp': 0.79}
শেষ ওজন:    {'cloth': 0.17, 'cod': 1.18, 'first': 0.63, 'logp': 0.93}

ক্ষয়। পুরোনো model (acc_old) মাস ১–৩-এ ০.৬৯৬–০.৭৩০, তারপর ধীরে নামে, আর মাস ১২-তে ০.৪১৬. মাস ৭ থেকে সে “কেউ ফেরত দেবে না” বলা baseline-এর চেয়েও খারাপ: পোশাককে সে এখনও ঝুঁকি ভাবে (ওজন ১.৮২), অথচ এখন পোশাক নিরাপদ দিকে।

ট্রিগার আর পুনরুদ্ধার। মাস ৬ আর ৭ দুটিই ০.৬৫-এর নিচে, তাই মাস ৮-এর শুরুতে মাস ৬–৭-এর ১,৬০০টি অর্ডারে retrain: মাস ৮-এ ০.৪৮০-এর বদলে ০.৬৭০. নতুন ওজনে COD এখন বড় সংকেত (১.১৮), পোশাক প্রায় শূন্য (০.১৭)। পুনরুদ্ধার আংশিক: পরে ০.৬৩৫–০.৬৭১, শুরুর ০.৭৩-এর নিচে, কারণ মাস ৬–৭-এ নতুন নিয়ম অর্ধেক চালু ছিল (change ০.৪–০.৬); জানালাটি পুরোনো আর নতুন জগতের মিশ্রণ। ছোট কাল্পনিক ডেটার এক run; সংখ্যা seed-এ বদলাবে, প্যাটার্ন নয়।

লক্ষ করুন, input শান্ত: মাস ৬ আর ৭-এ দামের PSI ০.০১২ আর ০.০১৩ (একই কোডে আলাদা করে মাপা), পোশাক আর COD-এর ভাগ training-এর মতোই, ঝুঁকিপূর্ণ বলা অর্ডারের ভাগও (০.৩৮–০.৪৩) স্পষ্ট কিছু বলে না। Concept drift ধরেছে শুধু দেরিতে আসা label; তাই label-এর পথ প্রথম দিনেই ঠিক করা দরকার।

হাতে যা আছে (ব্লক ৩-এর মতো PSI, ব্লক ৪-এর accuracy)
এই ধাপ
দেখা যাচ্ছে
সিদ্ধান্ত
  1. সংকেত। মাস ৬-এ ০.৫৮৬, মাস ৭-এ ০.৫২৯: লিখিত ট্রিগার (পরপর দুই মাস ০.৬৫-এর নিচে) ভেঙেছে।
  2. ভুয়া সতর্কতা কি না। Pipeline-এ ভুল নেই, মাসটি ঈদের নয়, label-এর সংজ্ঞা একই।
  3. ডেটা না সম্পর্ক। দামের PSI ০.০১২ আর ০.০১৩, অথচ ভুল বাড়ছে: concept drift. ব্যবসার কারণও জানা: সাইজ চার্ট আর নতুন courier.
  4. Baseline-এর সঙ্গে তুলনা। মাস ৭-এ baseline ০.৫৯৯, model ০.৫২৯: model এখন ক্ষতি করছে।
  5. Retrain. মাস ৬–৭-এর ১,৬০০টি অর্ডারে, একই feature কোড আর config; run log-এ নতুন ডেটার version.
  6. চালুর আগে যাচাই। Shadow-তে মাস ৮-এ নতুন ০.৬৭০, পুরোনো ০.৪৮০; তারপর canary, আর পুরোনো ফাইল rollback-এর জন্য রাখা।

Retraining-এর নীতি — নিয়মিত না ট্রিগারে

নীতিকীভাবেভালো দিকঝুঁকি
নিয়মিত (scheduled)প্রতি মাসে বা প্রতি তিন মাসে, সাম্প্রতিক জানালায়সহজ, অনুমানযোগ্য, দল অভ্যস্ত হয়দ্রুত পরিবর্তনে দেরি; অকারণ retrain-এ প্রতিবার যাচাইয়ের খরচ
ট্রিগারে (triggered)লিখিত শর্ত ভাঙলে, যেমন accuracy বা PSI সীমা পেরোলেদরকার হলেই খরচlabel দেরিতে এলে ট্রিগারও দেরিতে; ভুল সীমায় বারবার বা কখনোই না
মিশ্রনিয়মিত সময়সূচি, আর জরুরি ট্রিগার আলাদাবাস্তবে প্রচলিত আপসদুই রকম নিয়ম বোঝাতে নথি লাগে

Retraining-এর জানালাও সিদ্ধান্ত: ছোট জানালা দ্রুত শেখে কিন্তু নমুনা কম; বড় জানালা স্থির কিন্তু পুরোনো সম্পর্ক টেনে আনে। যাচাই ছাড়া retrain কখনো চালু নয়।

Feedback loop আর incident

ফোন পেয়ে কিছু গ্রাহক অর্ডার বাতিল করেন; তখন সেগুলো “ফেরত হতো কি না” আর জানা যায় না, আর পরের training-এ ভুল ছবি ঢোকে। এটি feedback loop আর selective labels, যা পর্ব ৭২-এ দেখেছেন। প্রতিকার: একটি ছোট এলোমেলো holdout-এ model-এর সিদ্ধান্ত প্রয়োগ না করা, আর প্রভাবিত সারি চিহ্নিত রাখা।

ভুল হলে কী করবেন, তা আগে লেখা থাকা চাই (incident response): (১) ক্ষতি থামানো, দরকারে rollback; (২) প্রভাব মাপা; (৩) মূল কারণ; (৪) স্থায়ী সমাধান; (৫) দোষারোপ ছাড়া লিখিত শিক্ষা। বিস্তারিত Cloud ও DevOps কোর্সের incident management পর্বে।

৮ · মানুষ, খরচ, নথি আর অবসর — এক পাতার checklist

কে কী করেন

ছোট দলে একজনই অনেক ভূমিকা নেন; সাধারণ ভাগটি এমন:

ভূমিকামূল কাজ
Product ownerসমস্যা, অগ্রাধিকার, সফলতার মাপ আর চালু বা বন্ধের সিদ্ধান্ত
Domain expertকাজের বাস্তবতা জানেন: label-এর অর্থ, ব্যতিক্রম, ঝুঁকি
Data engineerডেটার পথ: সংগ্রহ, পরিষ্কার টেবিল, সময়মতো job, ডেটার version
AnalystEDA, রিপোর্ট, A/B test-এর বিশ্লেষণ
ML engineerModel, পরীক্ষা, deploy, নজরদারি, retrain

খরচ — শুধু সার্ভার নয়

  • Compute: train আর অনুমানের খরচ। ছোট tabular model-এ সাধারণত সামান্য; বড় ভাষা model বা সবসময় চালু API-তে উল্লেখযোগ্য হতে পারে। নিজের পরিমাণে হিসাব করুন (FinOps).
  • মানুষ: প্রায়ই সবচেয়ে বড় খরচ, আর deploy-এর পরেও চলে।
  • Label: খাতায় নিজে থেকে label না এলে মানুষকে লেবেল করতে হয়, নির্দেশিকা লিখে আর মান যাচাই করে।

D. Sculley ও সহকর্মীদের ২০১৫ সালের প্রবন্ধ “Hidden Technical Debt in Machine Learning Systems” (NeurIPS) এই খরচের একটি বড় অংশকে technical debt বলে ব্যাখ্যা করে: আসল ব্যবস্থায় model-এর কোড ছোট একটি অংশ, চারপাশে ডেটা সংগ্রহ, যাচাই, feature, নজরদারি আর জোড়াতালির কোড অনেক বড়।

নথি — model card, datasheet আর decision log

তিন রকম নথি যথেষ্ট: model card (উদ্দেশ্য, নিষিদ্ধ ব্যবহার, দলভিত্তিক ফল, সীমা, মালিক), datasheet (ডেটা কোথা থেকে, label কী মাপে, জানা সমস্যা); দুটির বিস্তারিত নমুনা পর্ব ৭২-এ। তৃতীয়টি decision log: বড় সিদ্ধান্ত, তারিখ, বিকল্প, কারণ আর কে নিলেন। ছয় মাস পরে নতুন সহকর্মী যখন জিজ্ঞেস করবেন “threshold ০.৬ কেন?”, উত্তর খাতায় থাকবে।

# বাজারঘর ফেরত-ঝুঁকি — decision log (কাল্পনিক)
D1  label = ৭ দিনের মধ্যে ফেরত বা দরজায় প্রত্যাখ্যান; সাইজ বদল বাদ
    কারণ: সাইজ বদলে খরচ কম, আর তা আলাদা সমস্যা (সাইজ চার্ট)
D2  বাছাই: logistic regression (val AUC 0.7824), tree বাদ (0.6982)
    কারণ: নিয়মের (0.7047) চেয়ে ভালো, আর ওজন পড়ে কারণ বলা যায়
D3  deploy: ঘণ্টায় একবার batch; API নয়, পাঠানো হয় পরের সকালে
D4  retrain-এর ট্রিগার: পরপর দুই জানা মাস accuracy 0.65-এর নিচে
D5  মাস ৮: retrain (মাস ৬–৭); কারণ সাইজ চার্ট আর নতুন courier

Model-এর অবসর

প্রতিটি model একদিন বন্ধ হয়; অবসর পরিকল্পিত হলে ক্ষতি কম। কখন: সমস্যাটি আর নেই, সরল নিয়ম সমান কাজ করছে, ভালো বিকল্প এসেছে, বা মেরামতের খরচ লাভের চেয়ে বেশি। কীভাবে: বিকল্প আগে চালু; যাঁরা score পড়েন তাঁদের খুঁজে জানানো (Sculley ও সহকর্মীদের “undeclared consumers”); ফাইল, log আর ডেটা নিয়মমতো রাখা বা মোছা; card-এ কারণ ও তারিখ।

এক পাতার checklist · ধারণা থেকে অবসর
  1. বাছাই: ML ছাড়া সমাধান ভাবা হয়েছে? Impact আর feasibility লিখিত?
  2. Framing: label-এর সংজ্ঞা, সিদ্ধান্ত, ভুলের দাম।
  3. মাপ: offline metric আর ব্যবসার মাপ দুটিই; আসল প্রভাব মাপতে A/B test-এর পরিকল্পনা।
  4. সম্ভাব্যতা: feature, label-এর মান ও দেরি, গোপনীয়তা, build বনাম buy.
  5. Baseline: নিয়ম → linear → জটিল; test শুরুতেই আলাদা, একবার খোলা।
  6. খাতা: প্রতিটি run-এ commit, ডেটার version, config, seed, package version আর ফল।
  7. Deploy: batch না API, latency ও খরচের বাজেট, একই feature কোড, shadow → canary, rollback-এর সুইচ।
  8. নজরদারি: ব্যবস্থা, ডেটা (PSI/KS), অনুমান, দেরিতে আসা label; সীমা আর মালিক।
  9. Retrain: নিয়মিত না ট্রিগারে, জানালা, যাচাই ছাড়া চালু নয়; feedback loop-এর জন্য এলোমেলো holdout.
  10. নথি ও মানুষ: model card, datasheet, decision log, incident-এর পরিকল্পনা, দলের ভূমিকা।
  11. অবসর: বন্ধের শর্ত, বিকল্প, নির্ভরশীলদের জানানো।

৯ · AI-এর যুগে — AI assistant দিয়ে প্রকল্পের পরিকল্পনা আর নজরদারির কোড

AI assistant পরিকল্পনা, MLflow-এর কোড বা drift-এর ড্যাশবোর্ড দ্রুত লিখে দেয়। সাধারণ ভুল: পুরোনো API (যেমন pandas-এর append, NumPy-র np.float), অস্তিত্বহীন function বা parameter (MLflow বা scipy-র এমন argument যা আপনার version-এ নেই), test set দেখে model বাছাই, training ডেটায় metric জানানো, PSI-র সীমাকে “মানদণ্ড” বলে চালানো, KS-এর ছোট p-value-কে বড় সমস্যা বলা, warning চাপা দেওয়া, আর baseline ছাড়া “উন্নতি” দাবি।

নির্দিষ্ট হোন, যাচাই করা যায় এমন আউটপুট চান। আসল গ্রাহকের ডেটা, ফোন নম্বর বা ঠিকানা কখনো পেস্ট করবেন না; কলামের নাম আর কয়েকটি বানানো সারিই যথেষ্ট।

আমার একটি binary classifier আছে (returned = 1 মানে অর্ডার ফেরত)। প্রতি মাসের ডেটা একটি DataFrame-এ, কলাম: month, price, cloth, cod, first, score, returned (returned আসে এক মাস দেরিতে)। Python 3.14, pandas, numpy আর scipy দিয়ে কোড লেখো যা: ১. training-এর মাসের quantile দিয়ে ১০টি bin বানিয়ে প্রতি মাসে price-এর PSI দেবে; খালি bin-এর জন্য ছোট eps, আর সূত্র comment-এ। ২. scipy.stats.ks_2samp দিয়ে D আর p-value দেবে, আর comment-এ লিখবে বড় নমুনায় p-value কেন ছোট হয়। ৩. প্রতি মাসে score ≥ 0.5 অর্ডারের ভাগ (prediction drift) দেবে। ৪. শুধু যে মাসগুলোর label এসেছে, সেগুলোর accuracy আর "সবাই 0" baseline পাশাপাশি দেবে। ৫. একটি ৪-bin হাতে বানানো উদাহরণে PSI আগে হাতে হিসাব করে কোডের সঙ্গে মিলিয়ে দেখাবে। ৬. কোনো warning চাপা দেবে না, আর "model ঠিক আছে" ধরনের সিদ্ধান্ত লিখবে না; শুধু সংখ্যা আর তাদের সীমা।

AI-এর লেখা পরিকল্পনা বা নজরদারির কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট

১০ · পরের পথ — এই কোর্সে যা শিখলেন, এরপর কী পড়বেন, আর portfolio

মডিউল ধরে এক নজরে

মডিউলযা হাতে এল
১ · AI-এর মানচিত্রAI কী, ইতিহাস, তিন পথ, AI/ML/DL/GenAI, সমস্যার ধরন, বাস্তবের সীমা
২ · Python-এর কাজের পরিবেশuv, venv, Jupyter, ডেটার Python, ফাইলের ধরন, পরিচ্ছন্ন কোড, পুনরুৎপাদনযোগ্য প্রকল্প
৩ · NumPyarray, indexing, broadcasting, random, linear algebra, ছবি, গতি
৪ · pandasDataFrame, পড়া-লেখা, বাছাই, পরিষ্কার, groupby, merge, time series, বড় ডেটা
৫ · Visualizationmatplotlib, chart বাছাই, seaborn, interactive chart, সৎ chart
৬ · AI-এর গণিতvector, gradient descent, সম্ভাবনা, distribution, পরিসংখ্যান, entropy
৭ · SearchBFS, DFS, A*, minimax, constraint, local search
৮ · জ্ঞান, যুক্তি, পরিকল্পনাlogic, rule-based system, knowledge graph, planning
৯ · অনিশ্চয়তাBayes, Naive Bayes, Bayesian network, Markov, সিদ্ধান্ত ও MDP
১০ · ডেটা থেকে শেখাsplit ও leakage, kNN, linear ও logistic regression, metric, overfitting
১১ · scikit-learnEDA, feature, pipeline, clustering ও PCA, cross-validation, বাংলা লেখা
১২ · আধুনিক AIneural network, embedding, LLM, pretrained model, দায়িত্বশীল AI
১৩ · ক্যাপস্টোনএকটি পূর্ণ প্রকল্প আর এই জীবনচক্র
১৪ · পরীক্ষা ও ইন্টারভিউnumerical, MCQ, লিখিত ও ভাইভা, Python ও AI-এর ইন্টারভিউ (সামনে)

কোথায় আরও গভীরে যাবেন

ABCL TECH-এ তৈরি হতে থাকা পরবর্তী কোর্সগুলো আরও গভীরে যাবে: Machine Learning (আরও অ্যালগরিদম, ensemble, তত্ত্ব), Deep Learning (network, training, CNN, Transformer), NLP (ভাষার model, বাংলা লেখার প্রক্রিয়া), Generative AI (LLM দিয়ে অ্যাপ, RAG, মূল্যায়ন), MLOps (এই পর্বের deploy, নজরদারি আর retrain বিস্তারিত), Data Engineering (ডেটার pipeline, warehouse) আর AI Ethics (নীতি, আইন, সমাজ)।

এখনই পাশে পড়া যায় এমন সম্পূর্ণ কোর্স

ক্যাপস্টোন থেকে একটি সৎ portfolio

পর্ব ৭৩ (ক্যাপস্টোন — বাংলা পণ্য-রিভিউ থেকে একটি পূর্ণ AI প্রকল্প)-এর কাজটিকে এভাবে একটি portfolio প্রকল্প বানাতে পারেন:

  1. README-তে সমস্যা আর framing: কী সিদ্ধান্তে কাজে লাগবে, label-এর সংজ্ঞা, কেন ML লাগল।
  2. পুনরুৎপাদনযোগ্যতা: lockfile, seed, ডেটা কীভাবে বানানো বা কোথা থেকে, এক কমান্ডে পুরো ফল (পর্ব ১২)।
  3. Baseline আর run log: নিয়ম বা TF-IDF baseline-এর পাশে আপনার model; JSON Lines খাতাটি repo-তে।
  4. সৎ মূল্যায়ন: test একবার, ব্যবধানসহ, error analysis, আর যেখানে model ভালো করে না।
  5. Model card আর একটি নজরদারির পরিকল্পনা: কী মাপবেন, কোন সীমায় retrain, কবে অবসর।
  6. গোপনীয়তা: কোনো আসল ব্যক্তিগত ডেটা নয়; বানানো বা উন্মুক্ত লাইসেন্সের ডেটা, উৎসসহ।

আরও গভীরে: Eric Breck ও সহকর্মীদের ২০১৭ সালের “The ML Test Score” প্রবন্ধ ডেটা, model, infrastructure আর নজরদারির জন্য production-এর প্রস্তুতির একটি checklist-ভিত্তিক মাপ প্রস্তাব করে। Dataset shift ধরার পদ্ধতিগুলোর তুলনা আছে Stephan Rabanser ও সহকর্মীদের ২০১৯ সালের “Failing Loudly” (NeurIPS) প্রবন্ধে, যেখানে দুই-নমুনার পরীক্ষা (যেমন KS) একাধিক মাত্রায় কীভাবে প্রয়োগ করা যায় তা আলোচিত।

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

  • Data drift আর concept drift-এর পার্থক্য? প্রথমটিতে input-এর বণ্টন বদলায়; দ্বিতীয়টিতে input আর উত্তরের সম্পর্ক বদলায়, input একই থাকলেও। দ্বিতীয়টি ধরতে label লাগে।
  • ৪ bin-এ e = ০.২৫ করে, a = ০.১৫, ০.২০, ০.৩০, ০.৩৫; PSI কত? ∑(a − e) ln(a/e) ≈ ০.০৫১ + ০.০১১ + ০.০০৯ + ০.০৩৪ = ০.১০৫, “নজরে রাখুন” স্তর।
  • Batch আর online inference কখন? সিদ্ধান্ত পরে লাগলে batch (সহজ, সস্তা); সঙ্গে সঙ্গে লাগলে API, latency বাজেটসহ।
  • Shadow আর canary-র পার্থক্য? Shadow-তে নতুন model-এর উত্তর ব্যবহার হয় না, শুধু লেখা হয়; canary-তে অল্প অংশে সত্যিকারের সিদ্ধান্তে ব্যবহার হয়।
  • প্রথম model হিসেবে কী বানাবেন? একটি সরল নিয়ম বা linear model, test আলাদা রেখে; জটিল model শুধু validation-এ স্থির উন্নতি দিলে।

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

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

প্র ০১Sculley ও সহকর্মীরা কেন বলেছিলেন ML ব্যবস্থায় technical debt বিশেষভাবে লুকোনো থাকে? বাজারঘরের প্রকল্পে এর কয়েকটি রূপ কোথায় দেখা দিতে পারে?

২০১৫ সালে NeurIPS-এ প্রকাশিত “Hidden Technical Debt in Machine Learning Systems” প্রবন্ধে D. Sculley ও সহকর্মীরা (তখন একটি বড় প্রযুক্তি প্রতিষ্ঠানের গবেষক) যুক্তি দেন: ML দ্রুত একটি কাজের ব্যবস্থা বানাতে দেয়, কিন্তু তা রক্ষণাবেক্ষণে দীর্ঘমেয়াদি খরচ তৈরি করে, যা সাধারণ software-এর ঋণের চেয়ে কঠিনভাবে চোখে পড়ে। প্রবন্ধের বিখ্যাত ছবিতে model-এর কোড একটি ছোট বাক্স, আর চারপাশে ডেটা সংগ্রহ, যাচাই, feature বানানো, configuration, নজরদারি আর serving-এর বড় বড় বাক্স।

তাঁরা কয়েকটি নাম দেন। “CACE” (Changing Anything Changes Everything): একটি feature-এর মাপ বদলালে বাকি সব ওজন বদলে যায়, তাই আলাদা করে পরীক্ষা করা কঠিন। “Glue code” আর “pipeline jungle”: বিভিন্ন package জোড়ার জন্য লেখা অনেক জোড়াতালির কোড। “Undeclared consumers”: অন্য কোনো দল চুপচাপ model-এর আউটপুট ব্যবহার করছে, ফলে model বদলালে অজানা জায়গায় ক্ষতি। আর লুকোনো feedback loop, যেখানে model-এর সিদ্ধান্ত পরের training ডেটাকে বদলে দেয়।

বাজারঘরে এর রূপ সহজেই কল্পনা করা যায়। গুদামের দল যদি ফেরত-ঝুঁকির score দেখে প্যাকিংয়ের মান ঠিক করতে শুরু করে, অথচ ML দল তা না জানে, তবে retrain-এর পর score-এর মাত্রা বদলালে গুদাম ভুল করবে (undeclared consumer). Training-এ দাম টাকায় আর অ্যাপে পয়সায় এলে তা জোড়াতালির কোডের ভুল। ফোন পেয়ে বাতিল হওয়া অর্ডার পরের খাতা থেকে হারিয়ে যাওয়া feedback loop. প্রতিকারগুলো পরিচিত: একই feature কোড, ডেটার যাচাই, নির্ভরশীলদের তালিকা, decision log আর এলোমেলো holdout.

মূল উপলব্ধি: model-এর কোড ব্যবস্থার ছোট অংশ; ডেটা, নির্ভরতা আর feedback-এর ঋণ শুরু থেকে চিহ্নিত আর নথিভুক্ত না করলে তা পরে সুদে-আসলে ফেরত আসে।

প্র ০২CRISP-DM দুই দশকের বেশি পুরোনো। আজকের AI প্রকল্পে এটি কতটা কাজে লাগে, আর কোথায় এটি অসম্পূর্ণ?

প্রচলিত বর্ণনা অনুযায়ী CRISP-DM তৈরি শুরু হয় ১৯৯০-এর দশকের শেষ দিকে কয়েকটি ইউরোপীয় প্রতিষ্ঠানের একটি কনসোর্টিয়ামে, আর ১.০ সংস্করণের গাইড প্রকাশিত হয় প্রায় ২০০০ সালে। তখন “data mining” শব্দটি জনপ্রিয় ছিল; লক্ষ্য ছিল এমন একটি কাঠামো যা কোনো নির্দিষ্ট শিল্প বা software-এর ওপর নির্ভর করে না। ছয় ধাপ: business understanding, data understanding, data preparation, modeling, evaluation আর deployment, আর ছবিতে ধাপগুলোর মধ্যে আগে-পিছে তীর।

যা আজও কাজের: প্রথম ধাপটিই ব্যবসা বোঝা, model নয়। এই পর্বের অংশ ২ আর ৩ ঠিক সেটি: কোন সমস্যা, কোন সিদ্ধান্ত, সফলতার মাপ কী। দ্বিতীয়ত, ছবিটি চক্রাকার; ডেটা দেখে আবার ব্যবসার প্রশ্নে ফেরা স্বাভাবিক বলে স্বীকার করা হয়েছে। তৃতীয়ত, evaluation ধাপ শুধু metric নয়, ব্যবসার লক্ষ্যের সঙ্গে মিলিয়ে দেখা।

যা অসম্পূর্ণ: deployment সেখানে প্রায় শেষ ধাপ, একটি রিপোর্ট বা একবারের বসানো। কিন্তু আজকের অনেক model প্রতি ঘণ্টায় সিদ্ধান্ত নেয়, আর তার মান সময়ের সঙ্গে কমে (অংশ ৭)। নজরদারি, label-এর দেরি, retraining-এর নীতি, rollback, feedback loop, দলের ভূমিকা আর অবসর সেখানে স্পষ্টভাবে নেই। এই কারণেই পরে MLOps-এর কাঠামোগুলো deploy-এর পরের চক্রকে কেন্দ্রে আনে। আরেকটি সমালোচনা: CRISP-DM দায়িত্বশীলতা, গোপনীয়তা আর ক্ষতিগ্রস্তদের কথা আলাদা করে বলে না, যা আজ প্রায় সব নির্দেশিকায় থাকে।

মূল উপলব্ধি: CRISP-DM-এর “ব্যবসা আগে” আর “চক্রাকার কাজ” আজও খাঁটি; deploy-এর পরের জীবন, দায়িত্ব আর অবসর তার সঙ্গে যোগ করতে হয়।

প্র ০৩Validation-এ AUC বাড়ল, অথচ চালু করার পর ফেরতের খরচ কমল না। এটি কীভাবে সম্ভব, আর Goodhart-এর সূত্র এখানে কী বলে?

ব্রিটিশ অর্থনীতিবিদ Charles Goodhart ১৯৭৫ সালে মুদ্রানীতি প্রসঙ্গে একটি পর্যবেক্ষণ দেন, যা পরে সাধারণ রূপ পায়; সবচেয়ে প্রচলিত বাক্যটি নৃবিজ্ঞানী Marilyn Strathern-এর ১৯৯০-এর দশকের লেখা থেকে বলে উল্লেখ করা হয়: একটি মাপ যখন লক্ষ্য হয়ে যায়, তখন সেটি আর ভালো মাপ থাকে না। AUC model-এর সাজানোর ক্ষমতার একটি ভালো মাপ, কিন্তু ব্যবসার লক্ষ্য নয়। দল যদি শুধু AUC বাড়াতে থাকে, তবে তার সঙ্গে আসল লাভের সম্পর্ক ঢিলে হতে পারে।

ব্যাখ্যাগুলো বাস্তব। এক, সাজানো ভালো হলেও সিদ্ধান্তের threshold-এর কাছে উন্নতি না হলে ফোনের তালিকা প্রায় একই থাকে; AUC পুরো বক্ররেখার গড়, সিদ্ধান্ত হয় একটি বিন্দুতে। দুই, সিদ্ধান্তের প্রভাব নিজেই অনিশ্চিত: ফোন পেয়ে কিছু গ্রাহক এমনিই বাতিল করেন, যাঁরা হয়তো অর্ডার রাখতেন; তখন ফেরত কমলেও বিক্রিও কমে। তিন, validation-এর ডেটা পুরোনো জগতের; deploy-এর মাসে জগৎ বদলে গেলে (অংশ ৭) offline উন্নতি টেকে না। চার, ফোনের খরচ, কর্মীর সময় আর গ্রাহকের বিরক্তি AUC-তে নেই।

প্রতিকার: শুরুতেই ব্যবসার মাপ লেখা (প্রতি ১০০ অর্ডারে ফেরত আর ফোন মিলিয়ে খরচ), threshold বাছাই cost matrix দিয়ে, আর আসল প্রভাব মাপতে A/B test: ঝুঁকিপূর্ণ অর্ডারের এলোমেলো অর্ধেকে ফোন, অর্ধেকে নয়। Randomization-ই নিশ্চিত করে যে পার্থক্যটি ফোনের, ঋতু বা প্রচারণার নয়।

মূল উপলব্ধি: offline metric বলে model “চালু করার মতো” কি না; ব্যবসার মাপ আর নিয়ন্ত্রিত পরীক্ষা বলে চালু করে লাভ হলো কি না। মাপকে লক্ষ্য বানালে মাপটিই নষ্ট হতে পারে।

প্র ০৪Predictive policing নিয়ে প্রকাশিত “runaway feedback loop” গবেষণা কী দেখায়, আর বাজারঘরের প্রতারণা-সংকেতের নকশায় তা থেকে কী শিখবেন?

২০১৮ সালে FAT* সম্মেলনে Danielle Ensign ও সহকর্মীরা “Runaway Feedback Loops in Predictive Policing” প্রবন্ধে একটি সরল গাণিতিক model দিয়ে দেখান: পুলিশ যদি পাঠানো হয় সেখানে যেখানে আগের অপরাধের রেকর্ড বেশি, আর নতুন রেকর্ড আসে মূলত যেখানে পুলিশ আছে, তবে একটি এলাকার প্রাথমিক সামান্য বেশি রেকর্ড ক্রমশ আরও বেশি পুলিশ আর আরও বেশি রেকর্ড টেনে আনতে পারে, দুই এলাকার আসল অপরাধের হার সমান হলেও। ডেটা আসল জগৎ নয়, model-এর নিজের সিদ্ধান্তের ছাপ দেখায়।

বাজারঘরের প্রতারণা-সংকেতে একই কাঠামো। ধরুন model একটি এলাকার অর্ডারকে বেশি সন্দেহ করে, আর সন্দেহজনক অর্ডার আটকে যাচাই করা হয়। যাচাই করা অর্ডারেই প্রতারণা “ধরা” পড়ে; যাচাই না করা অর্ডারের প্রতারণা খাতায় ওঠে না। পরের training-এ সেই এলাকায় প্রতারণা বেশি দেখায়, model আরও সন্দেহ করে। এদিকে আটকানো সৎ অর্ডারের ফল কখনো জানা যায় না, কারণ সেগুলো বাতিল: এটি selective labels (পর্ব ৭২)। ক্ষতি পড়ে সেই এলাকার সৎ গ্রাহকের ওপর।

নকশার শিক্ষা: (১) একটি ছোট এলোমেলো অংশ সবসময় একই নিয়মে যাচাই করা, যাতে model-নিরপেক্ষ একটি আসল হার পাওয়া যায়; (২) training ডেটায় চিহ্ন রাখা কোন label model-এর সিদ্ধান্তের ফলে এসেছে; (৩) এলাকা বা দলভিত্তিক flag-এর হার আর ভুলের হার মাসে মাসে দেখা; (৪) আটকানোর বদলে কম ক্ষতিকর সিদ্ধান্ত (একটি নিশ্চিতকরণ ফোন), যাতে ফল দেখা যায়; (৫) আপিলের পথ।

মূল উপলব্ধি: যে model নিজের ভবিষ্যৎ ডেটা তৈরি করে, তার খাতা জগতের ছবি নয়; এলোমেলো holdout আর প্রভাবের চিহ্ন ছাড়া retrain ভুল ধারণাকে আরও পাকা করে।

প্র ০৫Google Flu Trends-এর গল্প নজরদারি, baseline আর model-এর অবসর নিয়ে কী শেখায়?

২০০৮ সালে চালু হওয়া Google Flu Trends search-এর প্রশ্ন থেকে যুক্তরাষ্ট্রে ফ্লু-এর মতো অসুখে ডাক্তারের কাছে যাওয়ার হার অনুমান করত, সরকারি স্বাস্থ্য সংস্থা CDC-র রিপোর্টের আগেই। ২০০৯ সালে Nature-এ এর পদ্ধতি প্রকাশিত হয়, আর শুরুতে ফল আশাব্যঞ্জক মনে হয়েছিল। ২০১৪ সালে Science-এ David Lazer ও সহকর্মীরা “The Parable of Google Flu” প্রবন্ধে বিশ্লেষণ করেন যে ২০১২–১৩ মৌসুমের একটি সময়ে এর অনুমান CDC-র মাপের প্রায় দ্বিগুণ ছিল, আর আগের কয়েক বছরেও এটি প্রায়ই বেশি অনুমান করেছিল। প্রকাশ্য খবর অনুযায়ী ২০১৫ সালে অনুমানগুলো প্রকাশ বন্ধ করা হয়।

Lazer ও সহকর্মীদের ব্যাখ্যা কয়েকটি স্তরে। মানুষের search-এর আচরণ বদলায়, সংবাদমাধ্যমের আতঙ্কে সুস্থ মানুষও ফ্লু নিয়ে খোঁজেন; search engine নিজেও বদলায়, যেমন প্রশ্নের পরামর্শ দেখানো, যা কোন শব্দ খোঁজা হবে তা প্রভাবিত করে। এটি concept drift আর feedback-এর মিশ্রণ: input আর লক্ষ্যের সম্পর্ক সরে গেছে। তাঁরা আরও দেখান, CDC-র কয়েক সপ্তাহ পুরোনো ডেটা দিয়ে একটি সরল model বা দুইয়ের মিশ্রণ অনেক ক্ষেত্রে ভালো করত। অর্থাৎ একটি শক্ত baseline-এর সঙ্গে নিয়মিত তুলনা থাকলে সমস্যা আগে দেখা যেত।

শিক্ষাগুলো এই পর্বের সঙ্গে মেলে। আসল ফলের (এখানে CDC-র মাপ) সঙ্গে ধারাবাহিক তুলনা ছাড়া নজরদারি অসম্পূর্ণ; একটি সরল baseline চালু রাখা উচিত; আর model-এর অবসর ব্যর্থতা নয়, ঠিক সিদ্ধান্ত হতে পারে, যদি তা পরিকল্পিত, ব্যাখ্যাসহ আর বিকল্প রেখে হয়।

মূল উপলব্ধি: জগৎ আর হাতিয়ার দুটিই বদলায়; আসল ফলের সঙ্গে নিয়মিত তুলনা, একটি সরল baseline আর সৎ অবসরের পরিকল্পনা একটি model-কে বিশ্বাসযোগ্য রাখে।

অনুশীলন

হাতে হিসাব, সমালোচনা, আউটপুট অনুমান আর কোড; প্রতিটির উত্তর নিচে লুকানো আছে।

  1. হাতে PSI (হিসাব): Training-এর মাসের quartile দিয়ে চারটি bin, প্রতিটিতে e = ০.২৫. এই মাসের ৪০০টি অর্ডার পড়েছে ৪০, ৮০, ১২০ আর ১৬০টি করে। (ক) PSI বের করুন আর হাতের নিয়মে স্তর বলুন। (খ) অন্য একটি মাসে গোনা ০, ১০০, ১৫০, ১৫০ (৪০০-এর মধ্যে)। খালি bin-এর ভাগকে ০.০০০১-এ আটকালে PSI কত হয়, আর এ থেকে কী শিখবেন?
    • (ক) ভাগ a = ০.১০, ০.২০, ০.৩০, ০.৪০.
    • পদ: (−০.১৫) × ln ০.৪ = ০.১৩৭৪; (−০.০৫) × ln ০.৮ = ০.০১১২; (০.০৫) × ln ১.২ = ০.০০৯১; (০.১৫) × ln ১.৬ = ০.০৭০৫.
    • যোগ PSI ≈ ০.২২৮: “নজরে রাখুন” স্তর (০.১–০.২৫), বড় পরিবর্তনের সীমার কাছাকাছি।
    • (খ) প্রথম bin: (০.০০০১ − ০.২৫) × ln(০.০০০১ ÷ ০.২৫) ≈ (−০.২৫) × (−৭.৮২) ≈ ১.৯৫৫; বাকি তিনটি: ০ + ০.১২৫ × ln ১.৫ × ২ ≈ ০.১০১. মোট ≈ ২.০৫৭ (অনুশীলন ৪-এর কোডে একই)।

    শিক্ষা: একটি খালি bin PSI-কে বিশাল করে, আর মানটি eps-এর ওপর নির্ভর করে। সরু bin এড়ান, আর খালি bin দেখলে আগে কারণ খুঁজুন।

  2. সমালোচনা ও নতুন নকশা: একজন সহকর্মীর পরিকল্পনা: “প্রতারণা ধরার জন্য একটি বড় neural network বানাব। পুরো ডেটায় accuracy ৯৯%, কারণ প্রতারণা খুব কম। সোমবার সবার জন্য চালু করব; সন্দেহজনক অর্ডার স্বয়ংক্রিয়ভাবে বাতিল হবে। প্রতি রাতে নতুন ডেটায় আপনা-আপনি retrain হবে, তাই নজরদারি লাগবে না।” কী কী সমস্যা, আর আপনি কীভাবে সাজাবেন?
    • Baseline নেই: প্রতারণা ১% হলে “সব সৎ” বললেই ৯৯% accuracy. Precision, recall আর cost matrix দরকার (পর্ব ৬০); আগে নিয়ম, তারপর linear model.
    • পুরো ডেটায় মাপ: train আর test মেশানো; test আলাদা আর একবার খোলা।
    • সবার জন্য একসঙ্গে, স্বয়ংক্রিয় বাতিল: shadow নেই, canary নেই, rollback নেই; একজন সৎ গ্রাহককে প্রতারক বলার ক্ষতি বড়। কম ক্ষতিকর সিদ্ধান্ত (নিশ্চিতকরণ ফোন) আর আপিলের পথ।
    • যাচাই ছাড়া রাতের retrain: নতুন ডেটার একটি ভুল সরাসরি চালু হয়ে যাবে; আর বাতিল অর্ডারের ফল কখনো আসে না, তাই feedback loop (প্র ০৪)।
    • নজরদারি নেই: label দেরিতে আসে; flag-এর হার, ডেটার drift আর দলভিত্তিক ভুলের হার দেখা দরকার।

    নতুন নকশা: framing, নিয়ম-baseline, run log, test একবার, shadow, ১০% canary, এলোমেলো holdout, যাচাইসহ retrain আর decision log.

  3. আউটপুট অনুমান: নিচের কোড কী ছাপবে?
    import json
    lines = ['{"run": 1, "C": 0.1, "val_f1": 0.61}',
             '{"run": 2, "C": 1.0, "val_f1": 0.66}',
             '{"run": 3, "C": 10.0, "val_f1": 0.66}']
    runs = [json.loads(s) for s in lines]
    best = max(runs, key=lambda r: r["val_f1"])
    print(best["run"], best["C"])
    print(sorted(r["run"] for r in runs if r["val_f1"] >= 0.65))
    print(len({r["val_f1"] for r in runs}))

    সত্যিকারের run (ডেস্কটপ, Python 3.14.4):

    2 1.0
    [2, 3]
    2

    প্রথম লাইন: run ২ আর ৩ দুটিরই val_f1 ০.৬৬; max সমান মানের মধ্যে প্রথমটি ফেরত দেয়, তাই run ২, আর তার C = 1.0 (JSON-এর 1.0 float হিসেবে পড়া হয়)। দ্বিতীয় লাইন: ০.৬৫ বা বেশি এমন run ২ আর ৩, সাজানো। তৃতীয় লাইন: set-এ আলাদা মান দুটি (০.৬১ আর ০.৬৬)। শিক্ষা: সমান ফলে বাছাই খাতার ক্রমের ওপর নির্ভর করে, তাই tie ভাঙার নিয়ম লিখে রাখুন।

  4. নিজের PSI function (কোড): psi_counts(ref_counts, cur_counts) আর psi(ref, cur, bins=10) লিখুন: দ্বিতীয়টি reference-এর quantile দিয়ে bin বানাবে, দুই প্রান্ত অসীম রাখবে, আর খালি bin-এর জন্য eps ব্যবহার করবে। Stepper-এর উদাহরণ (০.১০৫), অনুশীলন ১-এর দুটি ফল, আর একটি normal বণ্টনে কোনো পরিবর্তন না করে ও গড় ০.৫ সরিয়ে PSI মিলিয়ে দেখুন।
    import numpy as np
    def psi_counts(ref_counts, cur_counts, eps=1e-4):
        e = np.asarray(ref_counts, float); a = np.asarray(cur_counts, float)
        e = np.clip(e / e.sum(), eps, None); a = np.clip(a / a.sum(), eps, None)
        return float(np.sum((a - e) * np.log(a / e)))
    def psi(ref, cur, bins=10):
        edges = np.quantile(ref, np.linspace(0, 1, bins + 1))
        edges[0], edges[-1] = -np.inf, np.inf          # নতুন ডেটা সীমার বাইরে গেলেও ধরা পড়ে
        return psi_counts(np.histogram(ref, edges)[0], np.histogram(cur, edges)[0])
    print(round(psi_counts([150] * 4, [90, 120, 180, 210]), 4))   # stepper-এর উদাহরণ
    print(round(psi_counts([100] * 4, [40, 80, 120, 160]), 4))    # অনুশীলন ১
    rng = np.random.default_rng(0)
    ref = rng.normal(0, 1, 5000)
    print(round(psi(ref, rng.normal(0, 1, 1000)), 3), round(psi(ref, rng.normal(0.5, 1, 1000)), 3))
    print(round(psi_counts([100] * 4, [0, 100, 150, 150]), 3))   # খালি bin

    সত্যিকারের run (ডেস্কটপ, Python 3.14.4, NumPy 2.5.4):

    0.105
    0.2282
    0.01 0.25
    2.057

    হাতের হিসাব আর কোড মেলে। একই বণ্টনেও PSI ০.০১, শূন্য নয়: নমুনার ওঠানামা। গড় অর্ধেক standard deviation সরালে ০.২৫, ঠিক “বড় পরিবর্তনের” সীমায়; অর্থাৎ প্রচলিত সীমাটি একটি মাঝারি সরণেই পৌঁছায়। দুই প্রান্ত অসীম না রাখলে সীমার বাইরের দাম কোনো bin-এ পড়ত না।

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

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

ক্রমএকটি AI প্রকল্পের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. সমস্যা বাছাই আর ML আদৌ দরকার কি না ভাবা
  2. Label, সিদ্ধান্ত আর সফলতার মাপ লিখে framing করা
  3. সরল baseline বানিয়ে run log-সহ পরীক্ষা, test একবার
  4. Shadow, তারপর canary দিয়ে চালু করা
  5. Drift আর দেরিতে আসা label দিয়ে নজরদারি, দরকারে retrain
  6. পরিকল্পিত অবসর: বিকল্প চালু, নির্ভরশীলদের জানানো, নথি হালনাগাদ

দুটি bin-এ reference-এর ভাগ ০.৫ আর ০.৫, নতুন মাসে ০.৬ আর ০.৪. PSI = ∑ (a − e) ln(a ÷ e) প্রায় কত?

  • ০.২০০
  • ০.০৪১
  • ০ (দুই পদ কাটাকাটি)
  • ০.৪১০

একটি চালু model-এর সব input feature-এর PSI কয়েক মাস ধরে ০.০২-এর নিচে, ঝুঁকিপূর্ণ বলা অর্ডারের ভাগও প্রায় একই, কিন্তু দেরিতে আসা label দেখাচ্ছে accuracy ০.৭২ থেকে ০.৫৩-এ নেমেছে। সবচেয়ে সম্ভাব্য ব্যাখ্যা কোনটি?

  • বড় data drift
  • Model ফাইল নষ্ট হয়ে গেছে
  • Concept drift: input একই, কিন্তু input আর ফলের সম্পর্ক বদলেছে
  • PSI কম, তাই আসলে কোনো সমস্যা নেই

একটি দোকানের অর্ডার পাঠানো হয় পরের দিন সকালে, আর ফেরত-ঝুঁকির score দেখে সকালে কর্মীরা ফোন করেন। কোন deploy-এর ধরন সবচেয়ে মানানসই?

  • ঘণ্টায় বা রাতে একবার batch scoring, ফল একটি টেবিলে
  • প্রতিটি অর্ডারের জন্য সবসময় চালু একটি online API
  • Model শুধু নোটবুকে রাখা, deploy নয়
  • প্রতিটি নতুন অর্ডারে model আবার train করা

নতুন একটি model আসল অর্ডারে পাশাপাশি চলে আর তার score লিখে রাখা হয়, কিন্তু কোনো সিদ্ধান্তে সেই score ব্যবহার হয় না। এই কৌশলের নাম কী?

  • Canary release
  • A/B test
  • Rollback
  • Shadow deployment

তিনটি config-এর মধ্যে বাছাই করতে একজন প্রতিটির test set-এর score দেখে সবচেয়ে ভালোটি নিলেন, আর সেই test score-টিই রিপোর্টে লিখলেন। সমস্যা কোথায়?

  • কোনো সমস্যা নেই, কারণ মাত্র তিনটি config
  • শুধু seed লেখা হয়নি
  • Test দিয়ে বাছাই করায় test score আশাবাদী; বাছাই হওয়া উচিত validation-এ, আর test একবার শেষে
  • Test set যথেষ্ট বড় নয়, তাই সমস্যা

ব্যবস্থাপক জানতে চান “গত মাসে কোন ১০টি পণ্য সবচেয়ে বেশি ফেরত এসেছে?” প্রতি সপ্তাহে। কোন সমাধান সবচেয়ে উপযুক্ত?

  • একটি classification model train করা
  • একটি SQL query: পণ্য ধরে ফেরত গোনা, সাজানো আর প্রথম ১০টি
  • একটি chatbot চালু করা
  • প্রতিটি পণ্যের PSI হিসাব করা

শব্দকোষ

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

মনে রাখুন

  1. আগে বাছাই আর “ML না হলে চলে কি না”: impact × feasibility লিখিতভাবে; নিয়ম, SQL বা কাজের ধারা ঠিক করাও বৈধ উত্তর।
  2. Framing: label-এর সংজ্ঞা, সিদ্ধান্ত, ভুলের দাম আর ব্যবসার মাপ; আসল প্রভাব A/B test-এ।
  3. Baseline আর খাতা: নিয়ম → linear → জটিল; প্রতিটি run-এর seed, config, ডেটার version আর ফল; test শুরুতে আলাদা, শেষে একবার।
  4. সাবধানে চালু, চোখ খোলা রাখা: batch না API, latency ও খরচের বাজেট, shadow → canary → rollback; PSI/KS দিয়ে data drift, তবে concept drift ধরে শুধু দেরিতে আসা label.
  5. শেষ পর্যন্ত দায়: retrain-এর নিয়ম, holdout, incident-এর পরিকল্পনা, নথি আর পরিকল্পিত অবসর।

পরের পর্বে: মডিউল ১৪ শুরু। পর্ব ৭৫ (Numerical সমস্যার অনুশীলন — search, সম্ভাবনা, metric আর gradient, পূর্ণ সমাধানসহ)-এ পুরো কোর্সের হিসাবগুলো বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষার ঢঙে।

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

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