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

একটি অনুমানের ব্যাখ্যা — SHAP আর LIME

Explaining a Single Prediction — Shapley Values, Background Choice, KernelSHAP, TreeSHAP and LIME

Taught in Bangla, this lesson explains one fraud score from a fictional shop by computing exact Shapley values over all 32 coalitions in the browser. It contrasts interventional and conditional SHAP, writes KernelSHAP and LIME by hand, matches a real desktop run of shap's TreeExplainer and lime, and covers pitfalls, fooling attacks and honest customer explanations.

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

  • Local আর global ব্যাখ্যার পার্থক্য, আর Shapley value-র চারটি নীতি
  • তিনটি feature-এ হাতে আর পাঁচটিতে কোডে সব coalition গুনে নিখুঁত Shapley value বের করা
  • Interventional বনাম conditional background কেন আলাদা উত্তর দেয়, সংখ্যা দিয়ে দেখানো
  • KernelSHAP-কে weighted regression হিসেবে নিজে লেখা, আর shap library ডেস্কটপে চালিয়ে মেলানো
  • LIME নিজে লিখে তার অস্থিরতা মাপা, আর ক্রেতার জন্য সৎ ব্যাখ্যা ও counterfactual লেখা

পর্ব ৫৬ (Model বোঝা — permutation importance, PDP আর ICE) পুরো model-এর আচরণ দেখেছে: কোন feature সব মিলিয়ে কতটা কাজে লাগে। এই পর্বের প্রশ্ন ছোট আর ধারালো: এই একটি অনুমান কেন এমন হলো? ভিত্তি হিসেবে লাগবে পর্ব ৩৫-এর gradient boosting, পর্ব ২৬-এর probability, আর AI-এর ভিত্তি কোর্সের পর্ব ৫৯-এর log-odds. AI-এর ভিত্তি কোর্সের পর্ব ৭২-এ model card আর দায়িত্বশীল AI-এর মূল কথা এসেছে; এখানে ব্যাখ্যার গণিত আর তার সীমা। চারটি চালানো যায় এমন ব্লক পাতাতেই চলে, একই namespace-এ (একটি চালালে আগেরগুলো নিজে থেকে চলে)। প্রথমবার scikit-learn নামতে প্রায় দশ সেকেন্ড লাগে।

১ · একটি অর্ডার কেন আটকাল — local ব্যাখ্যা কী চায়

ক্রিকেটে দল ১৮০ রান করল। ম্যাচ শেষে প্রশ্ন: কোন ব্যাটার কত কৃতিত্ব পাবেন? স্কোরকার্ডের রান সোজা উত্তর মনে হয়। কিন্তু ধরুন একজন ব্যাটার শুরুতে ধীরে খেলে উইকেট ধরে রাখলেন বলে পরের জন ঝুঁকি নিতে পারলেন। তখন “কে কতটা দিলেন” নির্ভর করে কে কার সঙ্গে খেললেন তার ওপর। Shapley value ঠিক এই প্রশ্নের একটি ন্যায্য উত্তর: সম্ভাব্য সব জুটি আর ক্রমে প্রত্যেকের গড় অবদান।

বাজারঘর প্রতিটি অর্ডারে একটি প্রতারণা-স্কোর দেয়। একটি অর্ডার আটকে গেছে: ৯,৬০০ টাকার, ১০টি পণ্য, ৬ দিন বয়সী অ্যাকাউন্ট, আগে কোনো অর্ডার নেই, রাত ১২টা থেকে ৫টার মধ্যে দেওয়া। Model বলছে প্রতারণার সম্ভাবনা ০.৭০৯. গ্রাহকসেবার কর্মী জানতে চান কেন; ক্রেতাও জানতে চাইতে পারেন। এই প্রশ্নের উত্তর একটি local explanation: শুধু এই একটি সারির জন্য, প্রতিটি feature স্কোরকে কতটা ঠেলেছে।

Global ব্যাখ্যা (পর্ব ৫৬) বলে “সব অর্ডারে গড়ে টাকার অঙ্ক সবচেয়ে গুরুত্বপূর্ণ”। Local ব্যাখ্যা বলে “এই অর্ডারে অ্যাকাউন্টের বয়স সবচেয়ে বেশি ঠেলেছে”। দুটি একই কথা নয়। একটি feature গড়ে দুর্বল হয়েও কোনো বিশেষ সারিতে প্রধান হতে পারে। ব্যাখ্যা নিয়ে সাধারণ ধারণা (explainability) AI-এর ভিত্তি কোর্সে এসেছে; এখানে আমাদের লক্ষ্য একটি সংখ্যা-ভিত্তিক, যাচাইযোগ্য উত্তর।

প্রথমে একটি মৌলিক প্রশ্ন: “feature-এর অবদান” কোন এককে? প্রতারণার model একটি classifier; তার আউটপুট probability, কিন্তু gradient boosting ভেতরে হিসাব করে log-odds-এ (log(p/(১ − p))), আর গাছের মানগুলো log-odds-এ যোগ হয়। যোগফলে ভাগ করা সহজ বলে এই পর্বের SHAP হিসাব log-odds-এ। Probability-তে ভাগ করলে একই feature-এর অবদান অন্য feature-এর মানের ওপর নির্ভর করে, কারণ sigmoid বাঁকা। এককটি সবসময় স্পষ্ট লিখতে হবে; এটি প্রথম ফাঁদ।

২ · Shapley value — খেলার তত্ত্ব থেকে feature-এর ন্যায্য ভাগ

১৯৫৩ সালে Lloyd Shapley cooperative game-এ লাভ ভাগের একটি নিয়ম প্রকাশ করেন। খেলোয়াড়দের যেকোনো দল (coalition) S একসঙ্গে v(S) মূল্য তৈরি করে। প্রশ্ন: পুরো দলের লাভ v(N) − v(∅) প্রত্যেককে কতটা দেওয়া ন্যায্য? ML-এ খেলোয়াড় হলো feature, আর v(S) হলো “শুধু S-এর feature-গুলো জানলে model গড়ে কী বলত”। M টি feature থাকলে feature i-এর Shapley value:

φi = ∑S ⊆ N∖{i} [ |S|! (M − |S| − ১)! / M! ] · ( v(S ∪ {i}) − v(S) )

বন্ধনীর ভেতরটি i-এর প্রান্তিক অবদান: S দলে i যোগ হলে মূল্য কতটা বাড়ে। ওজনটির একটি সহজ মানে আছে। M টি feature-কে এলোমেলো ক্রমে একে একে দলে ঢোকান; i ঢোকার সময় তার আগে ঠিক S দলটি থাকার সম্ভাবনাই এই ওজন। অর্থাৎ φi = সব M! ক্রমে i-এর প্রান্তিক অবদানের গড়। Shapley দেখান, নিচের চারটি নীতি একসঙ্গে মানে এমন ভাগ মাত্র একটি (Shapley axioms):

Efficiency

সব ভাগের যোগফল = v(N) − v(∅). ML-এ: ∑φi = f(x) − base value. কিছু হারায় না, বাড়তিও আসে না।

Symmetry

দুটি feature সব দলে একই অবদান রাখলে তাদের ভাগ সমান।

Dummy

কোনো দলেই কিছু যোগ না করা feature-এর ভাগ শূন্য।

Additivity

দুটি খেলার যোগফলের ভাগ = আলাদা ভাগের যোগফল। Ensemble-এ: গাছগুলোর ভাগ যোগ করলেই পুরো model-এর ভাগ।

নিচের stepper তিনটি feature-এ পুরো হিসাব হাতে করে: টাকার অঙ্ক বড় (A), নতুন অ্যাকাউন্ট (N), রাতের অর্ডার (T). প্রতিটি coalition-এর মান একটি কাল্পনিক ঝুঁকি-স্কোর।

v(∅)=10 · v(A)=30 · v(N)=40 · v(T)=15 · v(AN)=70 · v(AT)=40 · v(NT)=55 · v(ANT)=90
এখন দেখা হচ্ছে
A-এর অবদান
N-এর অবদান
T-এর অবদান
মন্তব্য
  1. মূল্য-তালিকা। তিনটি feature-এর ২৩ = ৮টি coalition. কিছু না জানলে স্কোর ১০, সব জানলে ৯০; ভাগ করার মোট লাভ ৮০.
  2. A আগে। A→N→T ক্রমে A আনে ৩০ − ১০ = ২০, N আনে ৭০ − ৩০ = ৪০, T আনে ৯০ − ৭০ = ২০. A→T→N ক্রমে A ২০, T ৪০ − ৩০ = ১০, N ৯০ − ৪০ = ৫০.
  3. N আগে। N→A→T: N ৩০, A ৭০ − ৪০ = ৩০, T ২০. N→T→A: N ৩০, T ৫৫ − ৪০ = ১৫, A ৯০ − ৫৫ = ৩৫. A-এর অবদান সঙ্গী অনুযায়ী ২০ থেকে ৩৫: এটিই interaction.
  4. T আগে। T→A→N: T ৫, A ৪০ − ১৫ = ২৫, N ৫০. T→N→A: T ৫, N ৫৫ − ১৫ = ৪০, A ৩৫.
  5. গড়। ছয়টি ক্রমের গড়: A ২৭.৫, N ৪০, T ১২.৫. সূত্রের ওজনে একই: A-এর জন্য ⅓·২০ + ⅙·৩০ + ⅙·২৫ + ⅓·৩৫ = ২৭.৫.
  6. যাচাই। ২৭.৫ + ৪০ + ১২.৫ = ৮০ = v(ANT) − v(∅). Efficiency মানে ভাগগুলো স্কোরের পুরো দূরত্ব ঢেকে দেয়; ব্যাখ্যা থেকে কিছু হারায় না।

সমস্যা গোনায়। ক্রম M! টি, coalition ২M টি। পাঁচটি feature-এ ৩২টি coalition সহজ; ২০টিতে প্রায় দশ লাখ; ৫০টিতে অসম্ভব। তাই বাস্তবে আনুমানিক পদ্ধতি লাগে (অংশ ৫)। তার আগে ছোট ক্ষেত্রে নিখুঁত হিসাব করে রাখা ভালো, যাতে আনুমানিক ফল মেলানো যায়।

৩ · বাজারঘরের অর্ডারে ৩২টি coalition — নিখুঁত হিসাব

নিচের module ৪,০০০টি কাল্পনিক অর্ডার বানায়। পাঁচটি feature: amount (টাকা), items (পণ্যের সংখ্যা, টাকার সঙ্গে জড়িত), acct_days (অ্যাকাউন্টের বয়স), past_orders (আগের অর্ডার, বয়সের সঙ্গে জড়িত), night (রাতের অর্ডার)। আসল নিয়মে প্রতারণা নির্ভর করে শুধু টাকা, অ্যাকাউন্টের বয়স আর রাতের ওপর; items আর past_orders শুধু সঙ্গী। প্রথম ৩,২০০টিতে একটি ছোট HistGradientBoosting classifier শেখে, বাকি ৮০০টি test.

# বাজারঘর (কাল্পনিক দোকান): অর্ডারে প্রতারণার সন্দেহ, seeded ডেটা
import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier

COLS = ["amount", "items", "acct_days", "past_orders", "night"]
ORDER = pd.DataFrame([[9600, 10, 6, 0, 1]], columns=COLS)  # আটকে যাওয়া অর্ডার

def orders(n=4000, seed=57):
    rng = np.random.default_rng(seed)
    amount = np.round(rng.lognormal(7.4, 0.7, n), -1)        # টাকা
    items = np.clip(np.round(amount / 900 + rng.normal(0, 1, n)), 1, 20)
    acct_days = np.round(rng.exponential(300, n))             # অ্যাকাউন্টের বয়স
    past_orders = rng.poisson(acct_days / 30)                 # আগের অর্ডার
    night = (rng.random(n) < 0.15).astype(int)                # রাত ১২টা-৫টা
    logit = (-4.0 + 1.6 * np.log(amount / 1600)
             + 3.0 * np.exp(-acct_days / 30) + 1.4 * night)  # আসল নিয়ম
    fraud = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int)
    df = pd.DataFrame(dict(amount=amount, items=items, acct_days=acct_days,
                           past_orders=past_orders, night=night))
    return df, fraud

def fitted():
    X, y = orders()
    model = HistGradientBoostingClassifier(max_iter=80, max_leaf_nodes=4,
                                           min_samples_leaf=50, random_state=0)
    model.fit(X.iloc[:3200], y[:3200])
    bg = X.iloc[:3200].sample(100, random_state=1)            # background
    return model, X, y, bg

এখন v(S)-এর সংজ্ঞা দরকার: “শুধু S জানলে model কী বলত”। Model-কে তো সব কলামেই মান দিতে হয়। সবচেয়ে প্রচলিত উত্তর: S-এর কলামে অর্ডারের মান বসান, আর বাকি কলামে একটি background dataset-এর (এখানে training-এর ১০০টি এলোমেলো সারি) মান; তারপর ১০০টি অনুমানের গড়। একে বলে interventional (বা marginal) পদ্ধতি। v(∅) হলো background-এ model-এর গড় আউটপুট, যার নাম base value. প্রথম ব্লক ৩২টি coalition-এর প্রতিটিতে v(S) গোনে, তারপর অংশ ২-এর সূত্রে φ বের করে efficiency যাচাই করে।

import numpy as np
import pandas as pd
from itertools import combinations
from math import factorial
from sklearn.metrics import roc_auc_score
from bazar_fraud import fitted, ORDER, COLS

model, X, y, bg = fitted()
print(f"fraud rate {y.mean():.3f} | test AUC "
      f"{roc_auc_score(y[3200:], model.predict_proba(X.iloc[3200:])[:, 1]):.3f}")
x, B, M = ORDER.to_numpy()[0], bg.to_numpy(), len(COLS)

def f(Z):                                   # model-এর log-odds
    return model.decision_function(pd.DataFrame(Z, columns=COLS))

def v_int(S):                               # S অর্ডার থেকে, বাকিটা background থেকে
    Z = B.copy()
    Z[:, list(S)] = x[list(S)]
    return f(Z).mean()

def shapley(v):                             # সব 2^M coalition গুনে নিখুঁত হিসাব
    vals = {S: v(S) for k in range(M + 1) for S in combinations(range(M), k)}
    phi = np.zeros(M)
    for S, vS in vals.items():
        for i in set(range(M)) - set(S):
            w = factorial(len(S)) * factorial(M - len(S) - 1) / factorial(M)
            phi[i] += w * (vals[tuple(sorted(S + (i,)))] - vS)
    return phi, vals
phi, vals = shapley(v_int)
base, fx = vals[()], vals[tuple(range(M))]
print("coalitions:", len(vals))
print(f"base E[f] = {base:.3f} (p {1 / (1 + np.exp(-base)):.3f}) | "
      f"f(order) = {fx:.3f} (p {model.predict_proba(ORDER)[0, 1]:.3f})")
print(pd.Series(phi, index=COLS).round(3).to_string())
print(f"sum(phi) = {phi.sum():.4f}  f - base = {fx - base:.4f}")

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

fraud rate 0.059 | test AUC 0.840
coalitions: 32
base E[f] = -3.457 (p 0.031) | f(order) = 0.892 (p 0.709)
amount         1.578
items          0.595
acct_days      1.644
past_orders   -0.268
night          0.799
sum(phi) = 4.3484  f - base = 4.3484

Model: ৫.৯% অর্ডার প্রতারণা, test AUC ০.৮৪. Base value −৩.৪৬ log-odds (p ≈ ০.০৩১); লক্ষ করুন, এটি গড় log-odds, তাই এর probability সরাসরি প্রতারণার হার ০.০৫৯ নয়। এই অর্ডারে স্কোর ০.৮৯২ (p = ০.৭০৯). ভাগ: অ্যাকাউন্টের বয়স +১.৬৪, টাকা +১.৫৮, রাত +০.৮০, items +০.৬০; আগের অর্ডার শূন্য হওয়া উল্টো −০.২৭, কারণ এই model-এ (অ্যাকাউন্টের বয়স জানা থাকলে) past_orders = ০ একটু “স্বাভাবিক” দিকে ঠেলেছে। Efficiency: যোগফল ৪.৩৪৮৪, ঠিক f − base. লক্ষ করুন, আসল নিয়মে items নেই, তবু তার ভাগ +০.৬০: model items ব্যবহার করেছে, কারণ এটি টাকার সঙ্গে জড়িত। SHAP model-কে ব্যাখ্যা করে, পৃথিবীকে নয়।

আটকে যাওয়া অর্ডারের waterfall: base value থেকে prediction পর্যন্ত

লাল = স্কোর বাড়ায়, নীল = কমায়; একক log-odds

SHAP waterfall for the flagged order: base −3.46, acct_days +1.64, amount +1.58, night +0.80, items +0.60, past_orders −0.27, prediction 0.89 base value = −3.46 (p ≈ 0.03) acct_days = 6 দিন +1.64 amount = 9,600 টাকা +1.58 night = 1 +0.80 items = 10 +0.60 past_orders = 0 −0.27 f(x) = 0.89 → p = 0.709 −4−3−2 −101 log-odds
ছবি ৩.১ — প্রথম ব্লকের সংখ্যায় একটি waterfall plot, shap library যেমন আঁকে। প্রতিটি দণ্ড আগের দণ্ডের শেষ থেকে শুরু; সব দণ্ড মিলে base value (−৩.৪৬) থেকে prediction (০.৮৯) পর্যন্ত পথ ঢেকে দেয়: efficiency-র ছবি।

৪ · “অনুপস্থিত” feature-এর মানে — interventional বনাম conditional

Shapley-র সূত্র নির্দিষ্ট; অস্পষ্ট শুধু v(S). “items জানা নেই” মানে কী? দুটি প্রধান উত্তর আছে। Interventional: items-এর জায়গায় background-এর যেকোনো মান বসানো হয়, বাকি feature যা-ই হোক। এটি model-কে প্রশ্ন করে: “এই কলাম বদলে দিলে আউটপুট কী হয়?” Conditional (conditional SHAP): জানা feature-গুলোর সঙ্গে মানানসই সারি থেকে মান নেওয়া হয়, অর্থাৎ E[f(X) | XS = xS]. এটি ডেটাকে প্রশ্ন করে: “S জানলে বাকিগুলো সাধারণত কেমন হয়?” Feature স্বাধীন হলে দুটি একই। জড়িত হলে নয়। নিচের stepper সবচেয়ে ছোট উদাহরণে পার্থক্যটি দেখায়।

Model: f = 2 যদি amount > 5000, নইলে 0 (items ব্যবহার করে না) · অর্ডার x = (9000, 9)
Background (amount, items)
v(∅), v(A), v(I), v(AI)
φ_amount, φ_items
পদ্ধতি
  1. প্রস্তুতি। ডেটায় items সবসময় amount/১০০০, অর্থাৎ পুরোপুরি জড়িত। Background-এর চারটি সারিতে f = ০, ০, ২, ২; base value v(∅) = ১. অর্ডারে f = ২, ভাগ করার লাভ ১.
  2. Interventional v(I). items = ৯ রেখে amount background থেকে: চারটি সারির f = ০, ০, ২, ২, গড় ১. খেয়াল করুন (২০০০, ৯) আর (৩০০০, ৯): ২,০০০ টাকায় ৯টি পণ্য বাস্তবে এই ডেটায় কখনো ঘটে না।
  3. Interventional ফল। φamount = ½[(২ − ১) + (২ − ১)] = ১, φitems = ½[(১ − ১) + (২ − ২)] = ০. Model items ছোঁয় না, তাই dummy নীতিতে শূন্য: “true to the model”.
  4. Conditional v(I). items = ৯ জানলে কোন সারিগুলো মানানসই? শুধু (৯০০০, ৯), যার f = ২. তাই v(I) = ২: items জানা মানেই amount প্রায় জানা।
  5. Conditional ফল। φamount = ½[(২ − ১) + (২ − ২)] = ০.৫, φitems = ০.৫. Model যে feature ব্যবহারই করে না, সে অর্ধেক কৃতিত্ব পেল: “true to the data”. কোনটি ঠিক, তা নির্ভর করে প্রশ্নের ওপর।

দ্বিতীয় ব্লক বাজারঘরের ডেটায় conditional রূপ চালায়। প্রতিটি coalition S-এর জন্য training-এর ৩,২০০টি সারি থেকে S-এর কলামে অর্ডারের সবচেয়ে কাছের ৫০টি সারি (standardised দূরত্বে, kd-tree দিয়ে) নিয়ে তাদের ওপর গড় করে। এটি conditional প্রত্যাশার একটি সরল, আনুমানিক রূপ; গবেষণায় আরও নিখুঁত পদ্ধতি আছে (যেমন Aas ও সহকর্মীদের ২০২১ সালের কাজ)।

from sklearn.neighbors import NearestNeighbors

P = X.iloc[:3200].to_numpy()                # conditional-এর জন্য বড় pool
sd = P.std(axis=0)
print("corr(amount, items) =", round(np.corrcoef(P[:, 0], P[:, 1])[0, 1], 2),
      "| corr(acct_days, past_orders) =", round(np.corrcoef(P[:, 2], P[:, 3])[0, 1], 2))

def v_cond(S, k=50):                        # S-এ অর্ডারের মতো সারিগুলোর গড়
    if not S:
        return f(P).mean()
    S = list(S)
    nn = NearestNeighbors(n_neighbors=k, algorithm="kd_tree").fit(P[:, S] / sd[S])
    idx = nn.kneighbors(x[S][None, :] / sd[S], return_distance=False)[0]
    Z = P[idx].copy()
    Z[:, S] = x[S]
    return f(Z).mean()

phi_c, vals_c = shapley(v_cond)
print(pd.DataFrame({"interventional": phi, "conditional": phi_c},
                   index=COLS).round(3).to_string())
print(f"conditional: base {vals_c[()]:.3f}, sum {phi_c.sum():.3f}, "
      f"f - base {fx - vals_c[()]:.3f}")
# বাস্তবে অসম্ভব সারি: interventional কী মিশিয়েছে?
Z = B.copy(); Z[:, 1] = x[1]                # items = 10, amount background থেকে
print("background rows with items = 10 but amount < 3000 Tk:",
      int((Z[:, 0] < 3000).sum()), "of", len(Z))

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

corr(amount, items) = 0.88 | corr(acct_days, past_orders) = 0.95
             interventional  conditional
amount                1.578        1.016
items                 0.595        1.205
acct_days             1.644        1.312
past_orders          -0.268       -0.024
night                 0.799        0.829
conditional: base -3.446, sum 4.338, f - base 4.338
background rows with items = 10 but amount < 3000 Tk: 79 of 100

জড়তা: টাকা আর items-এর correlation ০.৮৮, বয়স আর আগের অর্ডারের ০.৯৫. উল্টে যাওয়া ক্রম: conditional-এ items (+১.২১) টাকাকে (+১.০২) ছাড়িয়ে গেছে, যদিও আসল নিয়মে items নেই আর model-এও তার ভূমিকা ছোট। কারণ conditional হিসাবে “items = ১০ জানা” মানে “টাকাও বেশি, প্রায় জানা”। একইভাবে past_orders-এর ঋণাত্মক ভাগ প্রায় শূন্যে এসেছে, বয়সের ভাগের কিছুটা তার দিকে গেছে। Night স্বাধীন, তাই দুই পদ্ধতিতে প্রায় সমান (০.৮০ বনাম ০.৮৩)। দুটিতেই efficiency খাটে (৪.৩৩৮ = f − base), কিন্তু ভাগ আলাদা। শেষ লাইন: interventional হিসাবে items = ১০ বসানোর পর ১০০টির ৭৯টি সারিতে টাকা ৩,০০০-এর কম: এমন অর্ডার ডেটায় প্রায় নেই, অথচ model-কে সেগুলোর ওপর প্রশ্ন করা হচ্ছে। এই off-manifold সারিতে model-এর আচরণ কখনো যাচাই হয়নি।

কোনটি বেছে নেবেন? প্রশ্ন যদি হয় “model কোন কলামের ওপর নির্ভর করছে” (debugging, নিয়ম মানা হচ্ছে কি না দেখা), তাহলে interventional: সে model যা ছোঁয় না তাকে শূন্য দেয়। প্রশ্ন যদি হয় “ডেটায় কোন তথ্য এই ঝুঁকি বহন করে”, তাহলে conditional. Janzing ও সহকর্মীরা ২০২০ সালে যুক্তি দেন যে feature বদলানো একটি intervention, তাই interventional রূপই স্বাভাবিক; Chen ও সহকর্মীরা একই বছর “true to the model or true to the data” নামে দুই দৃষ্টিভঙ্গি পাশাপাশি রাখেন। বিতর্কটি এখনো খোলা। নিয়ম একটাই: কোন background, কোন রূপ, আর কোন একক, তা ব্যাখ্যার পাশে লিখে রাখুন।

৫ · KernelSHAP আর TreeSHAP — দ্রুত হিসাবের কৌশল

২০১৭ সালে Scott Lundberg আর Su-In Lee NeurIPS-এ SHAP (SHapley Additive exPlanations) প্রস্তাব করেন। তাঁদের একটি পর্যবেক্ষণ: Shapley value হলো একটি weighted least squares সমস্যার সমাধান। প্রতিটি coalition-কে ০/১ ভেক্টর z দিয়ে লিখুন (১ = feature উপস্থিত)। তারপর v(S) ≈ φ0 + ∑ φi zi সরলরেখা fit করুন, যেখানে |S| = s আকারের coalition-এর ওজন

π(s) = (M − ১) / [ C(M, s) · s · (M − s) ]

এটি KernelSHAP. C(M, s) হলো M থেকে s বাছাইয়ের সংখ্যা। খালি আর পূর্ণ coalition-এর ওজন অসীম (বাস্তবে খুব বড়), যাতে φ0 = base value আর efficiency মানতেই হয়। খুব ছোট আর খুব বড় coalition বেশি ওজন পায়, কারণ সেগুলো একটি feature-এর একক প্রভাব সবচেয়ে পরিষ্কার দেখায়। সুবিধা: যেকোনো model-এ চলে, আর সব coalition না নিয়ে নমুনা থেকেও আনুমানিক উত্তর দেয়। তৃতীয় ব্লক প্রথমে ৩২টির সবগুলো দিয়ে, তারপর ৮, ১৪ আর ২০টি নমুনা coalition দিয়ে (প্রতিটি ২০টি seed-এ) regression চালায়।

from math import comb

def kernel_w(s):                            # Shapley kernel, s = coalition-এর আকার
    return 1e6 if s in (0, M) else (M - 1) / (comb(M, s) * s * (M - s))

def kernel_shap(coals):                     # weighted least squares: v(S) ≈ φ0 + Σ φi z_i
    Zb = np.array([[1.0] + [i in S for i in range(M)] for S in coals])
    w = np.sqrt([kernel_w(len(S)) for S in coals])
    t = np.array([vals[S] for S in coals])
    coef = np.linalg.lstsq(Zb * w[:, None], t * w, rcond=None)[0]
    return coef[1:]

allS = list(vals)                           # 32টি coalition-ই
print("w by size:", [round(kernel_w(s), 3) for s in range(1, M)])
print("all 32  max|kernel - exact| =", f"{np.abs(kernel_shap(allS) - phi).max():.1e}")
sizes = np.arange(1, M)
p = np.array([kernel_w(s) * comb(M, s) for s in sizes]); p /= p.sum()
for m in (8, 14, 20):                       # নমুনা থেকে আনুমানিক হিসাব
    errs = []
    for seed in range(20):
        rng = np.random.default_rng(seed)
        picked = {(), tuple(range(M))}
        while len(picked) < m:
            s = rng.choice(sizes, p=p)
            picked.add(tuple(sorted(int(i) for i in rng.choice(M, s, replace=False))))
        errs.append(np.abs(kernel_shap(list(picked)) - phi).max())
    print(f"m = {m:2d} coalitions: median max error {np.median(errs):.3f}, "
          f"worst {max(errs):.3f}")

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

w by size: [0.2, 0.067, 0.067, 0.2]
all 32  max|kernel - exact| = 9.4e-09
m =  8 coalitions: median max error 0.516, worst 1.189
m = 14 coalitions: median max error 0.237, worst 0.463
m = 20 coalitions: median max error 0.074, worst 0.283

ওজন: M = ৫-এ আকার ১ আর ৪-এর প্রতিটি coalition ০.২, আকার ২ আর ৩-এর ০.০৬৭: প্রান্তের coalition তিন গুণ ভারী। সব নিলে: regression-এর উত্তর নিখুঁত Shapley value থেকে ১০−৮-এর ঘরে দূরে, অর্থাৎ একই (পার্থক্য শুধু “অসীম” ওজনকে ১০৬ ধরার জন্য)। নমুনায়: ৮টি coalition-এ সবচেয়ে বড় ভুল মাঝামাঝি ০.৫২ log-odds, যা টাকার ভাগের এক-তৃতীয়াংশ; ২০টিতে ০.০৭. এটি একটি ছোট অনুশীলন, প্রকাশিত KernelSHAP নয়: আসল library জোড়া coalition, পুনরাবৃত্তি আর regularization-এর মতো কৌশল যোগ করে। শিক্ষা একটাই: নমুনা-নির্ভর ব্যাখ্যার নিজেরও অনিশ্চয়তা আছে, তাই seed বদলে দেখতে হয়।

TreeSHAP (Lundberg ও সহকর্মীরা, Nature Machine Intelligence, ২০২০) গাছ-ভিত্তিক model-এ গাছের গঠন কাজে লাগিয়ে নিখুঁত হিসাব করে, ২M গোনা ছাড়াই। এর দুটি রূপ। Interventional রূপ একটি background নেয় আর প্রথম ব্লকের সংজ্ঞাই হুবহু মানে; খরচ background-এর আকারের সঙ্গে রৈখিক। Tree path dependent রূপ background ছাড়া চলে: কোনো feature “অনুপস্থিত” হলে গাছের দুই শাখায় training-এর কতগুলো সারি গিয়েছিল, সেই অনুপাতে গড় নেয়। এটি conditional-এর একটি আনুমানিক রূপ হিসেবে প্রায়ই বর্ণনা করা হয়, তবে সেটি সব ক্ষেত্রে conditional প্রত্যাশার সমান নয়। পরের অংশে দুটিই চলবে।

৬ · shap library ডেস্কটপে — TreeExplainer আর lime-এর সত্যিকারের run

shap আর lime পাতার Python-এ নেই, তাই ডেস্কটপে একটি আলাদা প্রকল্পে একই bazar_fraud.py চালানো হলো (uv দিয়ে প্রকল্প বানানো AI-এর ভিত্তি কোর্সের পর্ব ০৭-এ আছে)। scikit-learn ইচ্ছে করে পাতার মতোই 1.8.0-তে বাঁধা, যাতে model হুবহু এক হয়।

~/bazarghar-explain$ uv init --bare --vcs none --author-from none
~/bazarghar-explain$ uv add "scikit-learn==1.8.0" numpy pandas scipy matplotlib shap lime
 …
 + lime==0.2.0.1
 + numpy==2.5.4
 + pandas==3.1.1
 + scikit-learn==1.8.0
 + shap==0.54.1
 …
import numpy as np
import pandas as pd
import shap
from lime.lime_tabular import LimeTabularExplainer
from bazar_fraud import fitted, ORDER, COLS

model, X, y, bg = fitted()
ex = shap.TreeExplainer(model, data=bg, feature_perturbation="interventional")
sv = ex(ORDER)                                   # একটি অর্ডারের Explanation
print("base value:", round(float(sv.base_values[0]), 3))
for c, v in zip(COLS, sv.values[0]):
    print(f"  {c:12s} {v:+.3f}")
print("base + sum =", round(float(sv.base_values[0] + sv.values[0].sum()), 3),
      "| model log-odds =", round(float(model.decision_function(ORDER)[0]), 3))

tp = shap.TreeExplainer(model)                   # background ছাড়া: path-dependent
print("tree_path_dependent:", np.round(tp.shap_values(ORDER)[0], 3))

test = X.iloc[3200:]
all_sv = ex(test)                                # 800টি অর্ডার, beeswarm-এর কাঁচামাল
print("mean |SHAP| on 800 test orders:")
for c, m in sorted(zip(COLS, np.abs(all_sv.values).mean(0)), key=lambda t: -t[1]):
    print(f"  {c:12s} {m:.3f}")

lx = LimeTabularExplainer(X.iloc[:3200].to_numpy(), feature_names=COLS,
                          class_names=["ok", "fraud"], mode="classification",
                          random_state=0)
for seed in (0, 1):
    lx.random_state = np.random.RandomState(seed)
    e = lx.explain_instance(ORDER.to_numpy()[0], lambda a: model.predict_proba(
        pd.DataFrame(a, columns=COLS)), num_features=5)
    print(f"LIME seed {seed}:", [(n, round(w, 3)) for n, w in e.as_list()])

সত্যিকারের run (ডেস্কটপ, Python 3.14.4; লেখার সময়ে shap 0.54.1, lime 0.2.0.1, scikit-learn 1.8.0, NumPy 2.5.4, pandas 3.1.1):

~/bazarghar-explain$ uv run python desktop_shap.py
base value: -3.457
  amount       +1.578
  items        +0.595
  acct_days    +1.644
  past_orders  -0.268
  night        +0.799
base + sum = 0.892 | model log-odds = 0.892
tree_path_dependent: [ 1.54   0.592  1.632 -0.216  0.79 ]
mean |SHAP| on 800 test orders:
  amount       0.786
  acct_days    0.494
  night        0.277
  past_orders  0.093
  items        0.064
LIME seed 0: [('amount > 2640.00', 0.125), ('night > 0.00', 0.088), ('acct_days <= 85.00', 0.073), ('past_orders <= 2.00', -0.016), ('items > 3.00', 0.011)]
LIME seed 1: [('amount > 2640.00', 0.123), ('night > 0.00', 0.09), ('acct_days <= 85.00', 0.072), ('past_orders <= 2.00', -0.01), ('items > 3.00', 0.009)]

পড়া: (১) Interventional TreeExplainer প্রথম ব্লকের নিজে গোনা মানের সঙ্গে তিন দশমিক পর্যন্ত মেলে (+১.৫৭৮, +০.৫৯৫, +১.৬৪৪, −০.২৬৮, +০.৭৯৯), কারণ দুটি একই সংজ্ঞা, একই ১০০টি background সারি। এভাবে library-কে নিজের ছোট হিসাব দিয়ে যাচাই করা যায়। (২) Background ছাড়া path-dependent রূপ কাছাকাছি কিন্তু আলাদা (past_orders −০.২১৬). (৩) ৮০০টি test অর্ডারে গড় |SHAP| হলো shap-এর bar plot আর beeswarm plot-এর (beeswarm plot: প্রতিটি অর্ডার একটি বিন্দু) কাঁচামাল: টাকা প্রথম, তারপর বয়স আর রাত; items সবার শেষে। এই অর্ডারে বয়স প্রথম, সবার গড়ে টাকা: local আর global আলাদা। (৪) lime library-র default আচরণ আমাদের অংশ ৭-এর রূপ থেকে আলাদা: সে প্রতিটি সংখ্যাকে quartile-এ ভাগ করে (“amount > 2640”) আর ৫,০০০ নমুনা নেয়, তাই দুই seed-এ প্রায় একই উত্তর। এখানে টাকা সবার ওপরে, অথচ পরের ব্লকের continuous LIME-এ টাকার ওজন প্রায় শূন্য। কেন, তা পরের অংশে।

৭ · LIME — আশপাশে নমুনা, নৈকট্যের ওজন আর সরল model

২০১৬ সালে Marco Tulio Ribeiro, Sameer Singh আর Carlos Guestrin KDD-তে LIME (Local Interpretable Model-agnostic Explanations) প্রস্তাব করেন। AI-এর ভিত্তি কোর্সের পর্ব ০৬-এ তাঁদের husky আর নেকড়ের উদাহরণ এসেছে। ধারণাটি সরল: জটিল model-এর একটি বিন্দুর চারপাশে ছোট এলাকায় একটি সরল (রৈখিক) model প্রায় ঠিক থাকে, যেমন পৃথিবী গোল হলেও একটি মাঠ সমতল মনে হয়। ধাপ চারটি: (১) অর্ডারের চারপাশে এলোমেলো নমুনা বানান; (২) প্রতিটির জন্য আসল model-এর probability নিন; (৩) কাছের নমুনাকে বেশি ওজন দিন, proximity kernel exp(−d²/w²) দিয়ে, যেখানে d = standardised দূরত্ব আর w = kernel width; (৪) সেই ওজনে একটি ridge regression fit করে তার coefficient-কে ব্যাখ্যা বলুন।

লক্ষ করুন, LIME-এর প্রশ্ন SHAP-এর থেকে আলাদা। SHAP জিজ্ঞেস করে “গড় অর্ডার থেকে এই অর্ডার পর্যন্ত পথে কে কতটা দিল”। LIME জিজ্ঞেস করে “এই অর্ডারের চারপাশে কোন feature সামান্য বদলালে probability কত বদলায়”, অর্থাৎ ঢাল। চতুর্থ ব্লক তিনটি kernel width (০.৫, lime-এর default ০.৭৫·√M ≈ ১.৬৮, আর ৫) আর দশটি seed-এ, প্রতিবার মাত্র ২০০ নমুনায় LIME চালায়, আর শেষে তুলনার জন্য পর্ব ৫৬-এর global permutation importance.

import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge
from sklearn.inspection import permutation_importance

def lime(seed, width, n=200):               # LIME নিজে লেখা
    rng = np.random.default_rng(seed)
    E = rng.normal(size=(n, M))             # standard একক-এ আশপাশে নমুনা
    E[:, 4] = ((rng.random(n) < 0.15) - x[4]) / sd[4]   # night: 0 বা 1
    Z = np.maximum(x + E * sd, 0)           # ঋণাত্মক টাকা/দিন বাদ
    E = (Z - x) / sd
    w = np.exp(-(E ** 2).sum(axis=1) / width ** 2)      # নৈকট্যের ওজন
    p = model.predict_proba(pd.DataFrame(Z, columns=COLS))[:, 1]
    return Ridge(alpha=1.0).fit(E, p, sample_weight=w).coef_

rows = {}
for width in (0.5, 0.75 * np.sqrt(M), 5.0):
    C = np.array([lime(seed, width) for seed in range(10)])
    rows[f"w={width:.2f}"] = C
    print(f"width {width:.2f}: mean", np.round(C.mean(0), 3),
          "| sd", np.round(C.std(0), 3))
    rank = [COLS[j] for j in np.argmax(np.abs(C), axis=1)]
    print("   top feature over 10 seeds:", pd.Series(rank).value_counts().to_dict())
fig, axes = plt.subplots(1, 3, figsize=(10, 3.4), sharey=True)
for ax, (name, C) in zip(axes, rows.items()):
    ax.boxplot(C, tick_labels=COLS, orientation="horizontal")
    ax.axvline(0, color="grey", lw=0.8)
    ax.set_title(f"LIME, kernel {name}")
    ax.set_xlabel("weight per 1 sd (prob.)")
fig.subplots_adjust(left=0.12, right=0.98, bottom=0.18, wspace=0.08)
plt.show()
pi = permutation_importance(model, X.iloc[3200:], y[3200:], scoring="roc_auc",
                            n_repeats=5, random_state=0)
print("global permutation (AUC drop):",
      {c: round(float(m), 3) for c, m in zip(COLS, pi.importances_mean)})

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

width 0.50: mean [ 0.004  0.005 -0.017  0.008  0.   ] | sd [0.014 0.008 0.013 0.007 0.   ]
   top feature over 10 seeds: {'acct_days': 5, 'items': 2, 'past_orders': 2, 'amount': 1}
width 1.68: mean [ 0.003  0.027 -0.212  0.044  0.073] | sd [0.01  0.012 0.013 0.022 0.004]
   top feature over 10 seeds: {'acct_days': 10}
width 5.00: mean [ 0.     0.026 -0.263  0.061  0.077] | sd [0.01  0.01  0.023 0.016 0.005]
   top feature over 10 seeds: {'acct_days': 10}
global permutation (AUC drop): {'amount': 0.168, 'items': 0.004, 'acct_days': 0.087, 'past_orders': 0.001, 'night': 0.041}

সরু kernel (০.৫): ২০০টি নমুনার প্রায় কোনোটিই এত কাছে পড়ে না, তাই কার্যকর নমুনা খুব কম। সব ওজন শূন্যের কাছে, standard deviation গড়ের সমান বা বেশি, আর দশটি seed-এ “সবচেয়ে গুরুত্বপূর্ণ feature” চারটি আলাদা উত্তর দেয় (বয়স ৫ বার, items ২, past_orders ২, টাকা ১)। Default আর চওড়া kernel: উত্তর স্থির হয়: বয়স প্রতিবার প্রথম, ঋণাত্মক চিহ্নে (বয়স এক sd বাড়লে probability প্রায় ০.২১–০.২৬ কমে), তারপর রাত +০.০৭. কিন্তু width ১.৬৮ থেকে ৫-এ গেলে বয়সের ওজন ২৪% বদলায়: “আশপাশ” কতটা বড়, সেই পছন্দ উত্তরে ঢুকে পড়ে। টাকা প্রায় শূন্য কেন? ৯,৬০০ টাকায় model আগেই সর্বোচ্চ ঝুঁকির ধাপে, তাই আশপাশে টাকা বদলালে probability প্রায় নড়ে না: ঢাল শূন্য। SHAP সেখানে +১.৫৮ দেয়, কারণ গড় অর্ডারের (প্রায় ২,০০০ টাকা) তুলনায় এই অর্ডার অনেক দূরে। lime library-র quartile রূপ (অংশ ৬) “amount > 2640” বনাম নিচে তুলনা করে, তাই সেখানেও টাকা বড়। তিনটি পদ্ধতি তিনটি আলাদা প্রশ্নের সৎ উত্তর। Permutation পুরো test সেটের global মাপ: টাকা AUC ০.১৬৮ কমায়, বয়স ০.০৮৭; কোনো একটি অর্ডারের কথা বলে না।

পদ্ধতিপ্রশ্নএই অর্ডারে শীর্ষ তিনদুর্বলতা
Interventional SHAPগড় থেকে এই অর্ডার পর্যন্ত কে কত দিল (model-এর চোখে)বয়স +১.৬৪, টাকা +১.৫৮, রাত +০.৮০off-manifold সারি; background-নির্ভর
Conditional SHAPএকই, কিন্তু ডেটার জড়তা মেনেবয়স +১.৩১, items +১.২১, টাকা +১.০২অব্যবহৃত feature কৃতিত্ব পায়; অনুমান কঠিন
LIME (continuous)আশপাশে ঢাল কতবয়স −০.২১, রাত +০.০৭, past_orders +০.০৪kernel width আর seed-এ অস্থির
Permutation (global)সব অর্ডারে কোন কলাম কত দরকারিটাকা, বয়স, রাতএকটি অর্ডারের কথা বলে না

৮ · ফাঁদ, প্রতারণা আর ক্রেতাকে সৎ ব্যাখ্যা

ফাঁদ ১, কারণ নয়: SHAP value model-এর হিসাবের ভাগ, পৃথিবীর কারণ নয়। items-এর +০.৬০ মানে “model items দেখে স্কোর বাড়িয়েছে”, “বেশি পণ্য কিনলে প্রতারণা বাড়ে” নয়। কারণ-ফলের প্রশ্ন পর্ব ৬৪ (Causal inference-এর পরিচয় — DAG, matching আর difference-in-differences)-এর বিষয়। ফাঁদ ২, জড়িত feature: অংশ ৪-এ দেখেছেন, ভাগ দুই জড়িত কলামের মধ্যে যেকোনোভাবে ছড়াতে পারে; এ জন্য জড়িত কলাম একসঙ্গে গোষ্ঠী করে দেখানো নিরাপদ। ফাঁদ ৩, একক: log-odds-এর ভাগ probability-র ভাগ নয়। ফাঁদ ৪, নমুনার অনিশ্চয়তা: KernelSHAP আর LIME-এর উত্তর seed-এর সঙ্গে নড়ে।

ফাঁদ ৫, ইচ্ছাকৃত প্রতারণা: ২০২০ সালে Dylan Slack ও সহকর্মীরা (AIES সম্মেলনে, “Fooling LIME and SHAP”) দেখান, একটি model প্রায় নিশ্চিতভাবে চিনতে পারে কোন সারি বাস্তব আর কোনটি ব্যাখ্যার জন্য বানানো off-manifold নমুনা। বাস্তব সারিতে সে পক্ষপাতদুষ্ট নিয়ম চালায়, বানানো নমুনায় নিরীহ নিয়ম। ফলে LIME আর KernelSHAP নিরীহ ব্যাখ্যা দেখায়, অথচ আসল সিদ্ধান্ত পক্ষপাতদুষ্ট। শিক্ষা: explanation manipulation সম্ভব, তাই ব্যাখ্যা একা নিরীক্ষার প্রমাণ নয়; আসল সিদ্ধান্তের ডেটায় সরাসরি মাপ (পর্ব ৬০-এর ন্যায্যতার মাপের মতো) লাগে।

ক্রেতাকে ব্যাখ্যা: সংখ্যার তালিকা ক্রেতার কাজে আসে না। দরকার এমন বাক্য যা সত্য, বোঝা যায়, ক্ষতি করে না, আর সম্ভব হলে পথ দেখায়। পথ দেখানোর একটি ধারণা counterfactual explanation (Sandra Wachter, Brent Mittelstadt আর Chris Russell, ২০১৭ সালে প্রকাশিত প্রবন্ধে জনপ্রিয় করেন): “কোন ন্যূনতম পরিবর্তনে ফল বদলাত?” ক্রেতা নিজে যে পরিবর্তন করতে পারেন, তার নাম algorithmic recourse. নিচের stepper প্রথম ব্লকের সংখ্যা থেকে একটি বাক্য বানায় (counterfactual probability-গুলো একই model-এ ডেস্কটপে গোনা)।

অর্ডার: 9,600 টাকা, 10টি পণ্য, অ্যাকাউন্ট 6 দিন, রাতp = 0.709, সীমা 0.5

            
  1. কাঁচা মান। প্রথম ব্লকের interventional SHAP. ক্রেতার কাছে log-odds অর্থহীন, তাই এগুলো শুধু ক্রম আর দিক বোঝার জন্য।
  2. গোষ্ঠী। জড়িত কলাম একসঙ্গে: অর্ডারের আকার +২.১৭, নতুন অ্যাকাউন্ট +১.৩৮, রাত +০.৮০. দুটি Shapley value যোগ করা একটি ব্যবহারিক সরলীকরণ, গোষ্ঠীর নিজস্ব Shapley value-র হুবহু সমান হওয়ার নিশ্চয়তা নেই।
  3. যা বলা যাবে না। কারণের দাবি নয়, model-এর হুবহু সীমা নয় (প্রতারকেরা তা কাজে লাগাবে), প্রযুক্তিগত শব্দ নয়। কোনো সংবেদনশীল বৈশিষ্ট্য ব্যাখ্যায় এলে সেটি নিজেই একটি সমস্যার সংকেত।
  4. Counterfactual. একই model-এ: দিনের বেলায় হলে p = ০.৫৪৪, তখনো সীমার ওপরে; দিনের বেলা আর ৬০ দিনের অ্যাকাউন্ট হলে ০.২৪৭. বয়স ক্রেতা এখন বদলাতে পারেন না, তাই সৎ recourse হলো যাচাইয়ের একটি পথ।
  5. বাক্য। সত্য (নিয়মটি আসলেই এই তিনটি বৈশিষ্ট্যে চলে), বোধগম্য, অভিযোগহীন, আর পরের করণীয়সহ। কোন ব্যাখ্যা দেখানো হলো, তা লগে রাখা দরকার, যাতে পরে নিরীক্ষা করা যায়।

ব্যাখ্যার আইনি দিক নিয়েও আলোচনা আছে। যেমন ইউরোপের তথ্য-সুরক্ষা আইন (GDPR) স্বয়ংক্রিয় সিদ্ধান্তে “ব্যাখ্যার অধিকার” দেয় কি না, তা নিয়ে গবেষকেরা ভিন্নমত দিয়েছেন। নিয়ম দেশ আর খাতভেদে আলাদা, তাই নির্দিষ্ট প্রয়োগের আগে আইনজ্ঞের পরামর্শ নেওয়াই নিরাপদ। প্রযুক্তিগত দিক থেকে সাধারণ নীতি: কোন পদ্ধতি, কোন background, কোন একক, আর তার সীমা, সব লিখে রাখা।

আরও গভীরে: Erik Štrumbelj আর Igor Kononenko ২০১০ ও ২০১৪ সালে Shapley value দিয়ে prediction ব্যাখ্যার নমুনা-ভিত্তিক পদ্ধতি দেন, SHAP-এর কয়েক বছর আগে। Covert, Lundberg আর Lee (JMLR, ২০২১) দেখান, অনেক ব্যাখ্যা-পদ্ধতি “feature সরিয়ে দেখো” পরিবারের সদস্য, যারা তিনটি পছন্দে আলাদা: কীভাবে সরানো হয়, কোন আচরণ মাপা হয়, আর কীভাবে ভাগ করা হয়। Kumar ও সহকর্মীরা (ICML, ২০২০) Shapley-ভিত্তিক importance-এর গাণিতিক আর মানবিক সমস্যা তুলে ধরেন। Neural network-এর জন্য Integrated Gradients-এর মতো gradient-ভিত্তিক পদ্ধতি আছে; সেগুলো deep learning-এর কোর্সে আসবে।

পরীক্ষা ও ইন্টারভিউতে: (১) Shapley value-র চারটি নীতি? Efficiency, symmetry, dummy, additivity. (২) M = ৩-এ নিখুঁত হিসাব কীভাবে? ছয়টি ক্রমে প্রান্তিক অবদানের গড়, বা ওজন ⅓, ⅙, ⅙, ⅓. (৩) Interventional আর conditional-এর পার্থক্য? প্রথমটি model ছোঁয় না এমন feature-কে শূন্য দেয়; দ্বিতীয়টি জড়িত feature-এ কৃতিত্ব ছড়ায়। (৪) LIME-এর kernel width ছোট করলে? কার্যকর নমুনা কমে, ব্যাখ্যা অস্থির হয়। (৫) SHAP value কি কারণ? না, model-এর আউটপুটের ভাগ মাত্র।

৯ · AI-এর যুগে — ব্যাখ্যার কোড আর ভাষা যাচাই

“এই prediction ব্যাখ্যা করো” বললে AI assistant প্রায়ই দ্রুত shap-এর তিন লাইন দেয়, সঙ্গে আত্মবিশ্বাসী ব্যাখ্যা: “অ্যাকাউন্টের বয়স প্রতারণার প্রধান কারণ”। কোড চলতে পারে, ভাষা ভুল। নিচের ভুলগুলো প্রায়ই চোখে পড়ে।

AI প্রায়ই যা করেকেন সমস্যাকীভাবে ধরবেন
SHAP value-কে কারণ হিসেবে লেখেভাগ model-এর, পৃথিবীর নয়“model-এর স্কোরে অবদান” ভাষা; কারণের দাবি আলাদা নকশা চায়
Background, রূপ আর একক উল্লেখ করে নাlog-odds আর probability মিশে যায়; উত্তর background-নির্ভরbase + ∑φ = আউটপুট মিলিয়ে একক নিশ্চিত করুন
পুরোনো বা বানানো API লেখেshap-এর পুরোনো আর নতুন interface মেশায়, নেই এমন argument দেয়নিজের version-এর docs; ছোট model-এ নিজের নিখুঁত হিসাবের সঙ্গে মেলান
Training সারি বা test-এর বদলে ভুল ডেটায় ব্যাখ্যাoverfit model-এর ব্যাখ্যা বিভ্রান্ত করেআলাদা test সেট; model-এর মান আগে যাচাই
একটি seed-এর LIME-কে চূড়ান্ত ধরেkernel width আর নমুনায় উত্তর বদলায়কয়েকটি seed আর width-এ চালিয়ে বিস্তার দেখুন
জড়িত feature আলাদা করে ব্যাখ্যা করেভাগ এলোমেলো ছড়ায়correlation দেখুন; গোষ্ঠী করে দেখান

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

আমার একটি HistGradientBoostingClassifier আছে (scikit-learn 1.8.0), বাজারঘরের অর্ডারে প্রতারণা ধরে; feature: amount, items, acct_days, past_orders, night. ১. একটি অর্ডারের জন্য shap TreeExplainer চালানোর কোড দাও; feature_perturbation আর background কী নিচ্ছ, আর আউটপুটের একক (log-odds না probability) স্পষ্ট লেখো। ২. base value + SHAP-এর যোগফল model-এর আউটপুটের সঙ্গে মেলে কি না, তা যাচাইয়ের একটি লাইন যোগ করো। ৩. amount আর items জড়িত হলে interventional আর conditional ব্যাখ্যা কীভাবে আলাদা হতে পারে, দুই লাইনে বলো। ৪. প্রতিটি function আর parameter কোন version-এর docs থেকে, লেখো; নিশ্চিত না হলে বলো। ৫. ক্রেতার জন্য একটি বাক্য লেখো, যাতে কারণের দাবি, model-এর সীমা বা সংবেদনশীল তথ্য নেই।

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

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

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

প্র ০১১৯৫৩ সালের খেলার তত্ত্ব কীভাবে ২০১৭ সালের ML ব্যাখ্যার মূল হাতিয়ার হলো, আর “একমাত্র ন্যায্য ভাগ” দাবিটি কতটা শর্তসাপেক্ষ?

Lloyd Shapley ১৯৫৩ সালে “A Value for n-Person Games” প্রবন্ধে প্রশ্ন করেন: কয়েকজন খেলোয়াড় মিলে লাভ করলে, প্রত্যেকের ভাগ কী হওয়া উচিত? তিনি কয়েকটি স্বাভাবিক নীতি লিখে দেখান যে সেগুলো একসঙ্গে মানে এমন ভাগ মাত্র একটি। পরের দশকগুলোতে এই ধারণা অর্থনীতি, ভোটের ক্ষমতা মাপা, খরচ ভাগাভাগি আর নেটওয়ার্কের বিশ্লেষণে ব্যবহৃত হয়। Shapley ২০১২ সালে Alvin Roth-এর সঙ্গে অর্থনীতিতে Nobel স্মারক পুরস্কার পান, যদিও সেই স্বীকৃতি মূলত stable matching-এর কাজের জন্য।

ML-এ ধারণাটি আসে ধাপে ধাপে। ২০০০-এর দশকে regression-এ R²-কে feature-গুলোর মধ্যে ভাগ করতে Shapley-ধাঁচের পদ্ধতি ব্যবহৃত হয়। Erik Štrumbelj আর Igor Kononenko ২০১০ ও ২০১৪ সালে একটি নির্দিষ্ট prediction ব্যাখ্যা করতে নমুনা-ভিত্তিক Shapley হিসাব দেন। ২০১৭ সালে Lundberg আর Lee দেখান LIME, DeepLIFT-এর মতো কয়েকটি পরিচিত পদ্ধতি একই “additive feature attribution” পরিবারের, আর সেই পরিবারে নির্দিষ্ট কিছু গুণ মানে এমন সমাধান Shapley value. সঙ্গে দেন KernelSHAP আর একটি open-source library, যা দ্রুত জনপ্রিয় হয়।

কিন্তু “একমাত্র” দাবিটি খেলার সংজ্ঞা স্থির ধরে নিয়ে। ML-এ খেলার সংজ্ঞাই, অর্থাৎ v(S), একটি পছন্দ: interventional না conditional, কোন background, কোন আউটপুট (log-odds, probability, না loss). এই পর্বের অর্ডারে একই model আর একই Shapley সূত্রে items-এর ভাগ +০.৬০ বা +১.২১, শুধু v(S)-এর সংজ্ঞা বদলে। Sundararajan আর Najmi (২০২০) দেখান, একই নীতি মেনে Shapley-র কয়েকটি আলাদা রূপ তৈরি হয়। তাই নীতিগুলো ভাগের নিয়মকে অনন্য করে, ব্যাখ্যাকে নয়।

মূল উপলব্ধি: Shapley value একটি নির্দিষ্ট খেলায় অনন্য ন্যায্য ভাগ; ML-এ খেলাটি কীভাবে সংজ্ঞায়িত হলো, সেই পছন্দই ব্যাখ্যার অর্থ ঠিক করে, তাই সেটি সবসময় লিখে রাখতে হয়।

প্র ০২“True to the model” নাকি “true to the data”: জড়িত feature-এ কোন ব্যাখ্যা ঠিক, আর এই বিতর্ক কেন মেটেনি?

Interventional রূপ জিজ্ঞেস করে, “একটি কলামের মান বদলে দিলে model-এর আউটপুট কীভাবে বদলায়?” ফলে model যে কলাম ছোঁয় না, তার ভাগ সবসময় শূন্য। Debugging-এ এটি খুব কাজের: কোনো নিষিদ্ধ বা সংবেদনশীল কলাম model সত্যিই ব্যবহার করছে কি না, তা সরাসরি দেখা যায়। Dominik Janzing ও সহকর্মীরা ২০২০ সালে যুক্তি দেন, feature বসিয়ে দেওয়া আসলে model-এর ইনপুটে একটি intervention, তাই এখানে interventional প্রত্যাশাই স্বাভাবিক সংজ্ঞা।

সমালোচনা হলো off-manifold সমস্যা। জড়িত feature-এ interventional হিসাব এমন সারি বানায় যা বাস্তবে নেই, যেমন এই পর্বে ২,০০০ টাকায় ১০টি পণ্য। Model সেসব সারিতে কখনো শেখেনি, তাই তার আউটপুট যেকোনো কিছু হতে পারে, আর Slack ও সহকর্মীদের আক্রমণ ঠিক এই দুর্বলতা কাজে লাগায়। Conditional রূপ বাস্তবসম্মত সারিতে থাকে, কিন্তু তার দাম আছে: model যে কলাম ব্যবহারই করে না, সেটিও কৃতিত্ব পায়, যেমন এই পর্বে items-এর +১.২১. তাছাড়া অনেক feature-এ conditional বণ্টন অনুমান করা নিজেই কঠিন; এই পর্বের ৫০টি প্রতিবেশীর পদ্ধতি একটি স্থূল উপায়।

Hugh Chen ও সহকর্মীরা ২০২০ সালে বিতর্কটির নাম দেন “true to the model or true to the data”. তাঁদের যুক্তি, দুটিই সঠিক, কিন্তু আলাদা প্রশ্নের উত্তর। Model নিরীক্ষা করতে হলে প্রথমটি; ডেটার কোন তথ্য ঝুঁকি বহন করে, তা জানতে হলে দ্বিতীয়টি, যদিও সেটিও কারণ-ফলের প্রমাণ নয়। ক্রেতাকে ব্যাখ্যায় বিষয়টি আরও জটিল: সে জানতে চায় “আমার কী বদলালে ফল বদলাবে”, যা counterfactual-এর প্রশ্ন, আর জড়িত feature একটি বদলালে অন্যটিও সাধারণত বদলায়। বিতর্ক মেটেনি কারণ প্রশ্নটি গণিতের নয়, উদ্দেশ্যের।

মূল উপলব্ধি: Interventional ব্যাখ্যা model-এর প্রতি সৎ, conditional ব্যাখ্যা ডেটার প্রতি; কোনটি নেবেন তা ঠিক করে আপনার প্রশ্ন, আর জড়িত feature থাকলে দুটির পার্থক্য দেখানোই সবচেয়ে সৎ রিপোর্ট।

প্র ০৩ব্যাখ্যা যদি ঠকানো যায়, তাহলে SHAP বা LIME দিয়ে কি কোনো model-কে “ন্যায্য” বলে সার্টিফাই করা চলে?

২০২০ সালে Dylan Slack, Sophie Hilgard, Emily Jia, Sameer Singh আর Himabindu Lakkaraju একটি পরীক্ষা দেখান। তাঁরা ইচ্ছে করে একটি পক্ষপাতদুষ্ট classifier বানান, যা বাস্তব ডেটায় শুধু একটি সংবেদনশীল বৈশিষ্ট্য দেখে সিদ্ধান্ত নেয়। তারপর একটি ছোট classifier শেখান, যা চেনে কোন ইনপুট বাস্তব আর কোনটি LIME বা KernelSHAP-এর বানানো perturbation. বানানো ইনপুট এলে model একটি নিরীহ, সম্পর্কহীন feature দেখে সিদ্ধান্ত নেয়। তাঁদের পরীক্ষায় ব্যাখ্যা-পদ্ধতিগুলো প্রায়ই নিরীহ feature-কেই প্রধান দেখায়, অথচ বাস্তব সিদ্ধান্তে পক্ষপাত পুরোপুরি থাকে।

আক্রমণটি সম্ভব কারণ perturbation-ভিত্তিক পদ্ধতি model-কে এমন ইনপুটে প্রশ্ন করে, যা বাস্তব ডেটা থেকে আলাদা করা যায়। এই পর্বের দ্বিতীয় ব্লকে দেখেছেন, interventional হিসাবে ১০০টির ৭৯টি সারি বাস্তবে প্রায় অসম্ভব। অন্য গবেষণাও দেখিয়েছে, ছোট ইনপুট-পরিবর্তনে ব্যাখ্যা অনেক বদলাতে পারে (যেমন Alvarez-Melis আর Jaakkola, ২০১৮), আর কিছু ক্ষেত্রে ব্যাখ্যা ইচ্ছেমতো সাজানোও যায়।

এর মানে এই নয় যে ব্যাখ্যা অর্থহীন। Model-এর নির্মাতা নিজের debugging-এ ব্যবহার করলে ব্যাখ্যা খুব কাজের, কারণ তিনি ঠকাতে চান না। সমস্যা হয় যখন বাইরের নিরীক্ষক শুধু ব্যাখ্যা দেখে সিদ্ধান্ত নেন, আর model-এর মালিকের ঠকানোর প্রণোদনা থাকে। তখন দরকার সিদ্ধান্তের সরাসরি মাপ: আসল ডেটায় দলভিত্তিক selection rate, ভুলের হার (পর্ব ৬০-এর মাপ), model-এর কোড আর training ডেটায় প্রবেশাধিকার, আর deploy-এর পরে নজরদারি। Interpretable-by-design model, যেখানে ব্যাখ্যা model নিজেই, এই ঝুঁকি কমায়; Cynthia Rudin ২০১৯ সালে উচ্চ-ঝুঁকির সিদ্ধান্তে এমন model-এর পক্ষে জোরালো যুক্তি দেন।

মূল উপলব্ধি: Post-hoc ব্যাখ্যা নির্মাতার জন্য একটি ভালো আতশকাচ, কিন্তু প্রতিপক্ষের সামনে প্রমাণ নয়; ন্যায্যতার দাবি আসল সিদ্ধান্তের ডেটায় সরাসরি মাপ আর স্বাধীন প্রবেশাধিকার চায়।

প্র ০৪Counterfactual ব্যাখ্যা কেন ক্রেতার কাছে feature-এর ভাগের চেয়ে বেশি কাজের হতে পারে, আর তার নিজের ফাঁদগুলো কী?

ক্রেতা সাধারণত জানতে চান না যে অ্যাকাউন্টের বয়স log-odds-এ +১.৬৪ দিয়েছে। তিনি জানতে চান, “আমি কী করলে অর্ডারটি যাবে?” Sandra Wachter, Brent Mittelstadt আর Chris Russell ২০১৭ সালে প্রকাশিত প্রবন্ধে যুক্তি দেন, এমন প্রশ্নের উত্তর দেওয়া যায় model-এর ভেতর না খুলেই: “আপনার আয় এত বেশি হলে ঋণ মঞ্জুর হতো” ধরনের counterfactual. গণিতে এটি একটি optimisation: সবচেয়ে কাছের এমন ইনপুট খুঁজুন, যেখানে সিদ্ধান্ত উল্টে যায়। এর সুবিধা, ক্রেতা একটি করণীয় পান, আর model-এর গোপন বিবরণ পুরো ফাঁস হয় না।

কিন্তু ফাঁদ অনেক। প্রথম, “সবচেয়ে কাছের” পরিবর্তন অবাস্তব হতে পারে, যেমন বয়স কমানো বা অ্যাকাউন্টকে রাতারাতি ৬০ দিনের পুরোনো করা। তাই পরিবর্তনযোগ্য আর অপরিবর্তনীয় feature আলাদা করতে হয়; Berk Ustun ও সহকর্মীরা ২০১৯ সালে এই ধারণাকে algorithmic recourse নাম দিয়ে আলাদা করে দেখেন। দ্বিতীয়, জড়িত feature: টাকা কমালে পণ্যের সংখ্যাও সাধারণত কমে, অথচ সরল counterfactual একটিকে স্থির রেখে অন্যটি বদলায়। তৃতীয়, একাধিক counterfactual থাকতে পারে, আর কোনটি দেখানো হলো তা ক্রেতার আচরণ বদলায়। চতুর্থ, model পরে আবার শেখানো হলে আজকের প্রতিশ্রুতি কাল সত্য না-ও থাকতে পারে। পঞ্চম, প্রতারণার ক্ষেত্রে বিশেষ ঝুঁকি: হুবহু সীমা জানালে প্রতারকেরা ঠিক তার নিচে থাকবে। এই পর্বের stepper তাই সীমা না বলে যাচাইয়ের পথ দেখিয়েছে।

বাজারঘরের মতো একটি দোকানে বাস্তব সমাধান সাধারণত মিশ্র: কর্মীর জন্য SHAP-এর পূর্ণ বিবরণ, ক্রেতার জন্য সংক্ষিপ্ত, সত্য, অভিযোগহীন বাক্য আর একটি করণীয়, আর সব ব্যাখ্যার লগ যাতে পরে নিরীক্ষা করা যায়।

মূল উপলব্ধি: Counterfactual ক্রেতাকে করণীয় দেয়, কিন্তু তা বাস্তবসম্মত, পরিবর্তনযোগ্য feature-এ, জড়তা মেনে, আর ঠকানোর ঝুঁকি বুঝে বানাতে হয়।

প্র ০৫LIME আর SHAP একই অর্ডারে আলাদা কথা বললে কোনটি বিশ্বাস করবেন, আর কখন দুটিই ভুল পথে নিতে পারে?

এই পর্বের অর্ডারে SHAP বলে টাকা দ্বিতীয় প্রধান কারণ (+১.৫৮), continuous LIME বলে টাকার ওজন প্রায় শূন্য, আর lime library-র quartile রূপ বলে টাকাই প্রথম। তিনটির কোনোটিই কোডের ভুল নয়; তিনটি আলাদা প্রশ্ন। SHAP একটি দূরের তুলনা-বিন্দু (background-এর গড় অর্ডার) থেকে এই অর্ডার পর্যন্ত পুরো পথ ভাগ করে। Continuous LIME শুধু এই বিন্দুর আশপাশের ঢাল মাপে; ৯,৬০০ টাকায় model-এর স্কোর সমতল, তাই ঢাল শূন্য। Quartile LIME তুলনা করে “২,৬৪০ টাকার ওপরে” বনাম নিচে, অর্থাৎ আরেকটি দূরের তুলনা।

কোনটি বিশ্বাস করবেন, তা নির্ভর করে প্রশ্নের ওপর। “এই অর্ডার সাধারণ অর্ডার থেকে কেন আলাদা”: SHAP. “অল্প বদলালে কী হবে”: LIME-এর ঢাল বা সরাসরি counterfactual. Ribeiro ও সহকর্মীরা LIME-এর মূল প্রবন্ধেই কার্যকারিতা আর ব্যাখ্যার সরলতার মধ্যে আপসের কথা বলেন, আর LIME-এর ফল kernel width আর নমুনার ওপর নির্ভর করে, যা এই পর্বের চতুর্থ ব্লকে ০.৫ width-এ দশটি seed-এ চারটি আলাদা “প্রধান feature” দিয়ে দেখা গেছে।

দুটিই ভুল পথে নিতে পারে কয়েকভাবে। Model নিজে খারাপ বা overfit হলে নিখুঁত ব্যাখ্যাও শুধু একটি খারাপ model-এর নিখুঁত বর্ণনা। Feature জড়িত হলে দুটিই কৃতিত্ব এলোমেলো ভাগ করতে পারে। Label-এ পক্ষপাত থাকলে (যেমন অতীতে শুধু কিছু এলাকার অর্ডার যাচাই হতো) ব্যাখ্যা সেই পক্ষপাতকে যুক্তিসংগত দেখাতে পারে। আর মানুষ ব্যাখ্যা দেখে model-কে বেশি বিশ্বাস করতে শুরু করতে পারেন; কিছু ব্যবহারকারী-গবেষণায় এমন অতিরিক্ত আস্থার লক্ষণ পাওয়া গেছে, যদিও ফল সব পরীক্ষায় এক নয়।

মূল উপলব্ধি: আলাদা পদ্ধতি আলাদা প্রশ্নের উত্তর; একাধিক পদ্ধতি পাশাপাশি চালানো, তাদের অমিলের কারণ খোঁজা, আর model-এর মান আলাদাভাবে যাচাই করাই সবচেয়ে নিরাপদ অভ্যাস।

অনুশীলন

হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। চাইলে পাতার ব্লকগুলোতে সংখ্যা বদলে যাচাই করুন।

  1. পূর্ণ numerical: তিনটি feature a, b, c-এর coalition মূল্য: v(∅) = ০.২, v(a) = ০.৫, v(b) = ০.৩, v(c) = ০.২, v(ab) = ০.৮, v(ac) = ০.৬, v(bc) = ০.৪, v(abc) = ১.০. (ক) তিনটির Shapley value বের করুন। (খ) Efficiency যাচাই করুন। (গ) c একা কিছু যোগ করে না (v(c) = v(∅)); তবু তার ভাগ কি শূন্য?

    (ক) ওজন ⅓, ⅙, ⅙, ⅓ দিয়ে: φa = ⅓(০.৫ − ০.২) + ⅙(০.৮ − ০.৩) + ⅙(০.৬ − ০.২) + ⅓(১.০ − ০.৪) = ০.১ + ০.০৮৩ + ০.০৬৭ + ০.২ = ০.৪৫. একইভাবে φb = ⅓(০.১) + ⅙(০.৩) + ⅙(০.২) + ⅓(০.৪) = ০.২৫, φc = ⅓(০) + ⅙(০.১) + ⅙(০.১) + ⅓(০.২) = ০.১০. (খ) ০.৪৫ + ০.২৫ + ০.১০ = ০.৮০ = ১.০ − ০.২ ✓. (গ) না। Dummy নীতি খাটে শুধু যদি কোনো দলেই c কিছু যোগ না করে; এখানে a বা b-এর সঙ্গে c +০.১ থেকে +০.২ যোগ করে। এটি interaction: c-এর প্রভাব অন্যের উপস্থিতির ওপর নির্ভর করে।

  2. সমালোচনা ও নকশা: একটি দল প্রস্তাব দিল: “প্রতিটি আটকানো অর্ডারে ক্রেতার অ্যাপে shap-এর waterfall ছবি দেখাব, প্রতিটি feature-এর মান আর SHAP সংখ্যাসহ; background হিসেবে গত সপ্তাহের সব অর্ডার নেব, যা প্রতি সপ্তাহে বদলাবে।” তিনটি সমস্যা চিহ্নিত করুন আর একটি ভালো নকশা দিন।

    সমস্যা: (১) log-odds-এর সংখ্যা ক্রেতার কাছে অর্থহীন, আর সংখ্যাগুলো কারণের মতো পড়া হবে। (২) প্রতিটি feature আর তার ভাগ দেখালে প্রতারকেরা model-এর সীমা শিখে ফেলবে। (৩) Background প্রতি সপ্তাহে বদলালে একই অর্ডারের ব্যাখ্যা সপ্তাহভেদে বদলাবে, base value-ও বদলাবে, আর নিরীক্ষায় পুরোনো ব্যাখ্যা পুনরুৎপাদন করা যাবে না। (৪) জড়িত feature আলাদা দেখালে ভাগ বিভ্রান্তিকর। ভালো নকশা: একটি নির্দিষ্ট, version-যুক্ত background (যেমন training-এর একটি স্থির নমুনা), ব্যাখ্যা কর্মীর পর্দায় পুরো বিবরণসহ, ক্রেতার জন্য গোষ্ঠী করা দুই-তিনটি কারণের সংক্ষিপ্ত বাক্য আর একটি করণীয় (যাচাই), সংখ্যা বা সীমা ছাড়া; প্রতিটি দেখানো ব্যাখ্যা model version আর background version-সহ লগে।

  3. আউটপুট অনুমান করুন: চালানোর আগে দুটি উত্তর লিখুন। (ক) M = 4 হলে [round(kernel_w(s), 3) for s in range(1, 4)] কী ছাপবে, যেখানে kernel_w(s) = (M - 1) / (comb(M, s) * s * (M - s))? (খ) একটি model শুধু amount দেখে; ডেটায় amount2 নামে amount-এর হুবহু কপি আছে, যা model ব্যবহার করে না। Interventional আর conditional SHAP-এ amount2-এর ভাগ কত হবে, amount-এর ভাগ φ ধরে?
    [0.25, 0.125, 0.25]

    (ক) s = ১: ৩/(৪·১·৩) = ০.২৫; s = ২: ৩/(৬·২·২) = ০.১২৫; s = ৩: ৩/(৪·৩·১) = ০.২৫. প্রান্তের coalition দ্বিগুণ ভারী। (খ) Interventional: amount2 বদলালে model-এর আউটপুট বদলায় না, তাই তার ভাগ ০, আর amount পায় পুরো φ. Conditional: দুটি কলাম পুরোপুরি জড়িত, তাই যেকোনো একটি জানলে অন্যটি জানা; দুটি সব দলে একই অবদান রাখে, symmetry অনুযায়ী প্রত্যেকে φ/২. অংশ ৪-এর stepper-এর মতোই।

  4. LIME-এর ওজন: তিনটি perturbed নমুনার standardised দূরত্ব d = ০.৫, ১ আর ২. Proximity kernel exp(−d²/w²). (ক) w = ১ আর w = ৩-এ তিনটির ওজন বের করুন। (খ) কোন width-এ ব্যাখ্যা বেশি “local”, আর তার দাম কী? (গ) ৯,৬০০ টাকার অর্ডারে continuous LIME টাকার ওজন প্রায় শূন্য দিল, অথচ SHAP +১.৫৮. দুই বাক্যে ব্যাখ্যা করুন।

    (ক) w = ১: e−০.২৫ = ০.৭৭৯, e−১ = ০.৩৬৮, e−৪ = ০.০১৮. w = ৩: e−০.০২৮ = ০.৯৭৩, e−০.১১১ = ০.৮৯৫, e−০.৪৪৪ = ০.৬৪১. (খ) w = ১ বেশি local: দূরের নমুনা প্রায় বাদ (০.০১৮)। দাম: কার্যকর নমুনা কম, তাই seed বদলালে ওজন বেশি নড়ে; w = ৩-এ স্থির কিন্তু বড় এলাকার গড়, যেখানে model আর রৈখিক না-ও থাকতে পারে। (গ) ৯,৬০০ টাকায় model-এর স্কোর একটি সমতল ধাপে, তাই আশপাশে টাকা বদলালে probability প্রায় নড়ে না: ঢাল শূন্য। SHAP গড় অর্ডার (প্রায় ২,০০০ টাকা) থেকে এই অর্ডার পর্যন্ত পুরো পথ মাপে, যেখানে টাকার বড় অবদান আছে।

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

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

ক্রমএকটি prediction-এর জন্য LIME নিজে লিখে ব্যাখ্যা বানানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. ব্যাখ্যা করার সারিটি বেছে standardise করার মাপ (প্রতিটি feature-এর sd) ঠিক করা
  2. সারিটির চারপাশে এলোমেলো perturbed নমুনা বানানো
  3. প্রতিটি নমুনায় আসল model-এর probability নেওয়া
  4. সারি থেকে দূরত্ব মেপে proximity kernel দিয়ে ওজন দেওয়া
  5. সেই ওজনে একটি সরল (ridge) রৈখিক model fit করা
  6. কয়েকটি seed আর kernel width-এ পুনরাবৃত্তি করে coefficient-এর বিস্তার দেখা

দুটি feature-এর একটি model-এ coalition মূল্য: v(∅) = ০, v({১}) = ৪, v({২}) = ২, v({১, ২}) = ১০. Feature ১-এর Shapley value কত?

  • ৪
  • ৫
  • ৬
  • ৮

একটি classifier-এর base value −২.০ log-odds, একটি সারিতে আউটপুট ১.০ log-odds. তিনটি feature-এর interventional SHAP value ১.৫ আর ০.৮ হলে, তৃতীয়টির মান কত?

  • −০.৩
  • ০.৭
  • ১.০
  • তথ্য যথেষ্ট নয়

একটি model শুধু feature A ব্যবহার করে। ডেটায় B, A-এর সঙ্গে প্রায় পুরোপুরি জড়িত (correlation ০.৯৯), কিন্তু model B দেখে না। কোন বক্তব্য সঠিক?

  • Interventional SHAP-এ B-এর ভাগ শূন্য, conditional-এ B উল্লেখযোগ্য ভাগ পেতে পারে
  • দুই পদ্ধতিতেই B-এর ভাগ শূন্য
  • Interventional SHAP-এ B বেশি ভাগ পায়, conditional-এ শূন্য
  • Conditional SHAP efficiency মানে না

LIME-এ ২০০টি perturbed নমুনা আর খুব ছোট kernel width ব্যবহার করলে সাধারণত কী হয়?

  • ব্যাখ্যা সবচেয়ে নির্ভুল আর স্থির হয়
  • আসল model-এর probability বদলে যায়
  • সব coefficient ঠিক শূন্য হয়, seed যা-ই হোক
  • কার্যকর নমুনা কমে যায়, আর seed বদলালে ব্যাখ্যা অনেক বদলায়

প্রতারণার একটি model-এ একটি অর্ডারের “night” feature-এর interventional SHAP value +০.৮০ log-odds. কোন ব্যাখ্যা সবচেয়ে সঠিক?

  • রাতে অর্ডার করা এই ক্রেতার প্রতারণার কারণ
  • Background-এর তুলনায় এই অর্ডারে night model-এর log-odds স্কোরে প্রায় ০.৮০ যোগ করেছে
  • রাতের কারণে প্রতারণার probability ঠিক ০.৮০ বেড়েছে
  • সব অর্ডারে গড়ে night সবচেয়ে গুরুত্বপূর্ণ feature

Slack ও সহকর্মীদের (২০২০) “Fooling LIME and SHAP” আক্রমণে একটি পক্ষপাতদুষ্ট model নিরীহ ব্যাখ্যা দেখায় কীভাবে?

  • Training ডেটা থেকে সংবেদনশীল কলাম মুছে দিয়ে
  • shap আর lime library-র কোড বদলে
  • ব্যাখ্যার জন্য বানানো off-manifold নমুনা চিনে সেগুলোতে আলাদা, নিরীহ আচরণ করে
  • LIME-এর kernel width খুব বড় করে দিয়ে

শব্দকোষ

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

মনে রাখুন

  1. Local বনাম global: একটি অনুমানের ব্যাখ্যা সব অনুমানের গড় গুরুত্ব থেকে আলাদা হতে পারে। একক (log-odds বা probability) সবসময় লিখুন।
  2. Shapley: সব coalition-এ প্রান্তিক অবদানের ভারযুক্ত গড়; efficiency, symmetry, dummy, additivity. ছোট M-এ ২M coalition গুনে নিখুঁত, ∑φ = f(x) − base.
  3. Background: interventional model-এর প্রতি সৎ (অব্যবহৃত feature শূন্য), conditional ডেটার প্রতি (জড়িত feature-এ ভাগ ছড়ায়); জড়তা থাকলে উত্তর আলাদা।
  4. KernelSHAP আর TreeSHAP: Shapley kernel-এর ওজনে weighted regression; গাছে TreeSHAP নিখুঁত আর দ্রুত। Library-কে ছোট নিখুঁত হিসাব দিয়ে মিলিয়ে নিন।
  5. LIME আর সৎ ব্যাখ্যা: LIME আশপাশের ঢাল, kernel width আর seed-এ অস্থির। ব্যাখ্যা কারণ নয়, ঠকানো যায়; ক্রেতাকে সত্য, অভিযোগহীন বাক্য আর করণীয় দিন।

পরের পর্বে: পর্ব ৫৮ (Conformal prediction — নিশ্চয়তাসহ অনুমানের পরিসর). এবার প্রশ্ন “কেন” থেকে “কতটা নিশ্চিত”: একটি অনুমানের চারপাশে এমন পরিসর বা সেট, যার coverage-এর একটি সুনির্দিষ্ট নিশ্চয়তা আছে।

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

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