পাঠ ৫৭ · ৫৭-এর মধ্যে · মডিউল ১৩
Home / Courses / Ethics in Computing & AI Safety / ক্যাপস্টোন প্রকল্প

চূড়ান্ত প্রকল্প — একটি সম্পূর্ণ এথিক্স ও সেফটি অডিট পরিচালনা করা

Capstone — conducting a full ethics and safety audit
২২ মিনিট পড়া উন্নত · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • কোর্সের একাধিক প্যাটার্নকে (স্টেকহোল্ডার, ফেয়ারনেস, প্রাইভেসি, স্পেসিফিকেশন-গেমিং, ওয়েটেড চেকলিস্ট) একটি সুসংগত অডিট টুলকিটে সংযুক্ত করা
  • দুটি সিন্থেটিক ডেমোগ্রাফিক গ্রুপে ফেয়ারনেস মেট্রিক্স ও একটি ট্রেনিং-ডেটাসেটে k-anonymity গণনা করা
  • একটি proxy মেট্রিক সরাসরি অপ্টিমাইজ করলে প্রকৃত লক্ষ্য থেকে কীভাবে বিচ্যুত হতে পারে তা একটি সত্যিকারের নির্বাচন-কৌশল তুলনার মাধ্যমে দেখা
  • কীভাবে একটি চূড়ান্ত ডিপ্লয়মেন্ট সুপারিশ প্রকৃত গণনা করা প্রমাণের উপর ভিত্তি করে (হার্ডকোড না করে) তৈরি করা যায়

১ · ক্যাপস্টোন পরিকল্পনা — একটি হায়ারিং-রিকমেন্ডেশন AI অডিট করা

নিচের পুরো পরিস্থিতি সম্পূর্ণ হাইপোথেটিক্যাল ও ইলাস্ট্রেটিভ — কোনো বাস্তব কোম্পানি, প্রোডাক্ট, বা প্রকৃত ব্যক্তির তথ্য এখানে ব্যবহৃত হয়নি। ধরা যাক আমরা একটি হাইপোথেটিক্যাল "হায়ারিং-রিকমেন্ডেশন AI" সিস্টেম অডিট করছি, যা প্রার্থীদের রিজিউমি ও ইন্টারভিউ তথ্য বিশ্লেষণ করে নিয়োগকারীদের ইন্টারভিউর জন্য প্রার্থী সুপারিশ করে। কোর্স জুড়ে আমরা একটি একটি করে অডিট-প্যাটার্ন শিখেছি — স্টেকহোল্ডার বিশ্লেষণ (M1), ফেয়ারনেস মেট্রিক্স (M4), প্রাইভেসি/k-anonymity (M3), স্পেসিফিকেশন-গেমিং (M9), ও ওয়েটেড কমপ্লায়েন্স চেকলিস্ট (M12-M13)। এই ক্যাপস্টোনে সেগুলো আলাদা আলাদা না রেখে একসাথে জোড়া লাগিয়ে একটি সম্পূর্ণ, কার্যকর অডিট চালানো হবে।

মডিউল ১ · স্টেকহোল্ডার ম্যাট্রিক্স মডিউল ২ · ফেয়ারনেস মেট্রিক্স মডিউল ৩ · k-anonymity চেক মডিউল ৪ · স্পেসিফিকেশন-গেমিং মডিউল ৫ ওয়েটেড কমপ্লায়েন্স চেকলিস্ট স্কোর মডিউল ৬ চূড়ান্ত সুপারিশ
প্রতিটি বিশ্লেষণ মডিউল একটি প্রকৃত সংখ্যা তৈরি করে; মডিউল ৫ সেই সংখ্যাগুলোকে থ্রেশহোল্ডের বিপরীতে চেক করে একটি ওয়েটেড স্কোরে রূপান্তর করে; মডিউল ৬ সেই স্কোর থেকেই চূড়ান্ত সুপারিশ বের করে।
নিচের কোড সেলগুলো একটি একক Pyodide সেশনে ক্রমানুসারে চলে — একটি সেলে সংজ্ঞায়িত ভ্যারিয়েবল/ফাংশন পরের সেলে ব্যবহার করা যায়, ঠিক যেমন একটি Jupyter নোটবুকে হয়। তাই সঠিক ফলাফল পেতে সেলগুলো উপর থেকে নিচে, ক্রমানুসারে Run করুন।

২ · মডিউল ১ — স্টেকহোল্ডার ইমপ্যাক্ট ম্যাট্রিক্স

প্রথম টুকরোটি M1/L03-এর প্যাটার্নে একটি ওয়েটেড স্টেকহোল্ডার ইমপ্যাক্ট ম্যাট্রিক্স — চারটি গোষ্ঠী (চাকরি আবেদনকারী, নিয়োগকারী প্রতিষ্ঠান, বিদ্যমান কর্মীরা, বৃহত্তর সমাজ/শ্রমবাজার), প্রতিটির জন্য চারটি মাত্রায় (আর্থিক, স্বায়ত্তশাসন, প্রাইভেসি, সেফটি) একটি ইলাস্ট্রেটিভ স্কোর, ও গোষ্ঠীর গুরুত্ব প্রতিফলিত করা একটি ওয়েট।

Python
# ========== মডিউল ১: স্টেকহোল্ডার ইমপ্যাক্ট ম্যাট্রিক্স -- সম্পূর্ণ সিন্থেটিক/ইলাস্ট্রেটিভ ==========
stakeholders = [
    {"name": "চাকরি আবেদনকারী",          "weight": 0.45, "financial": -3, "autonomy": -4, "privacy": -3, "safety": 0},
    {"name": "নিয়োগকারী প্রতিষ্ঠান",      "weight": 0.25, "financial":  4, "autonomy":  3, "privacy":  0, "safety": 0},
    {"name": "বিদ্যমান কর্মীরা",           "weight": 0.20, "financial":  1, "autonomy": -1, "privacy": -1, "safety": 0},
    {"name": "বৃহত্তর সমাজ/শ্রমবাজার",     "weight": 0.10, "financial":  0, "autonomy": -1, "privacy": -1, "safety": 0},
]

DIMENSIONS = ["financial", "autonomy", "privacy", "safety"]

def weighted_impact(stakeholder):
    mean_score = sum(stakeholder[d] for d in DIMENSIONS) / len(DIMENSIONS)
    return mean_score, mean_score * stakeholder["weight"]

print(f"{'স্টেকহোল্ডার':22s} {'গড় ইমপ্যাক্ট':>14s} {'ওয়েট':>8s} {'ওয়েটেড স্কোর':>14s}")
results = []
for s in stakeholders:
    mean_score, weighted = weighted_impact(s)
    results.append((s["name"], mean_score, s["weight"], weighted))
    print(f"{s['name']:22s} {mean_score:14.3f} {s['weight']:8.2f} {weighted:14.4f}")

most_affected = min(results, key=lambda r: r[3])
print(f"\nসবচেয়ে (নেতিবাচকভাবে) বেশি প্রভাবিত গোষ্ঠী: {most_affected[0]}  (ওয়েটেড স্কোর {most_affected[3]:.4f})")

    
গণনা করা ফলাফল: চাকরি আবেদনকারী গোষ্ঠীর ওয়েটেড স্কোর সবচেয়ে বেশি নেতিবাচক (−১.১২৫), যদিও সংখ্যায় তারাই সবচেয়ে বড় গোষ্ঠী (ওয়েট ০.৪৫)। এই most_affected ভ্যারিয়েবলটি মডিউল ৬-এর চূড়ান্ত রিপোর্টে সরাসরি ব্যবহৃত হবে।

৩ · মডিউল ২ — ফেয়ারনেস মেট্রিক্স

দ্বিতীয় টুকরোটি M4/L16-এর প্যাটার্নে দুটি সিন্থেটিক ডেমোগ্রাফিক গ্রুপে (Group A, Group B) ১০ জন করে প্রার্থীর উপর ফেয়ারনেস মেট্রিক্স — ডেমোগ্রাফিক প্যারিটি ($P(\hat{y}=1)$ প্রতি গ্রুপে), False Positive/Negative Rate, সামগ্রিক accuracy, ও ক্যালিব্রেশন।

Python
# ========== মডিউল ২: ফেয়ারনেস মেট্রিক্স -- দুটি সিন্থেটিক ডেমোগ্রাফিক গ্রুপে ==========
group_a = [
    {"id": "A1", "predicted_positive": True,  "actual_positive": True},
    {"id": "A2", "predicted_positive": True,  "actual_positive": True},
    {"id": "A3", "predicted_positive": True,  "actual_positive": True},
    {"id": "A4", "predicted_positive": True,  "actual_positive": False},
    {"id": "A5", "predicted_positive": False, "actual_positive": False},
    {"id": "A6", "predicted_positive": False, "actual_positive": False},
    {"id": "A7", "predicted_positive": False, "actual_positive": False},
    {"id": "A8", "predicted_positive": False, "actual_positive": True},
    {"id": "A9", "predicted_positive": False, "actual_positive": False},
    {"id": "A10","predicted_positive": False, "actual_positive": False},
]

group_b = [
    {"id": "B1", "predicted_positive": True,  "actual_positive": True},
    {"id": "B2", "predicted_positive": True,  "actual_positive": False},
    {"id": "B3", "predicted_positive": False, "actual_positive": False},
    {"id": "B4", "predicted_positive": False, "actual_positive": False},
    {"id": "B5", "predicted_positive": False, "actual_positive": True},
    {"id": "B6", "predicted_positive": False, "actual_positive": True},
    {"id": "B7", "predicted_positive": False, "actual_positive": False},
    {"id": "B8", "predicted_positive": False, "actual_positive": False},
    {"id": "B9", "predicted_positive": False, "actual_positive": False},
    {"id": "B10","predicted_positive": False, "actual_positive": False},
]

def fairness_metrics(records, label):
    n = len(records)
    positives_predicted = [r for r in records if r["predicted_positive"]]
    actual_pos = [r for r in records if r["actual_positive"]]
    actual_neg = [r for r in records if not r["actual_positive"]]

    dp_rate = len(positives_predicted) / n

    fp = [r for r in records if r["predicted_positive"] and not r["actual_positive"]]
    fpr = len(fp) / len(actual_neg) if actual_neg else 0.0

    fn = [r for r in records if not r["predicted_positive"] and r["actual_positive"]]
    fnr = len(fn) / len(actual_pos) if actual_pos else 0.0

    correct = [r for r in records if r["predicted_positive"] == r["actual_positive"]]
    accuracy = len(correct) / n

    calib_pos = (sum(1 for r in positives_predicted if r["actual_positive"]) / len(positives_predicted)
                 if positives_predicted else 0.0)

    print(f"--- {label} (n={n}) ---")
    print(f"  ডেমোগ্রাফিক প্যারিটি (positive prediction rate): {dp_rate:.3f}")
    print(f"  False Positive Rate:                             {fpr:.3f}")
    print(f"  False Negative Rate:                             {fnr:.3f}")
    print(f"  সামগ্রিক accuracy:                                {accuracy:.3f}")
    print(f"  ক্যালিব্রেশন (predicted-positive-দের মধ্যে actual positive rate): {calib_pos:.3f}")
    return {"dp_rate": dp_rate, "fpr": fpr, "fnr": fnr, "accuracy": accuracy, "calibration": calib_pos}

metrics_a = fairness_metrics(group_a, "Group A")
print()
metrics_b = fairness_metrics(group_b, "Group B")

dp_gap = abs(metrics_a["dp_rate"] - metrics_b["dp_rate"])
fnr_gap = abs(metrics_a["fnr"] - metrics_b["fnr"])
print(f"\nডেমোগ্রাফিক প্যারিটি ফারাক: {dp_gap:.3f}  ({dp_gap*100:.1f} পার্সেন্টেজ পয়েন্ট)")
print(f"False Negative Rate ফারাক: {fnr_gap:.3f}  ({fnr_gap*100:.1f} পার্সেন্টেজ পয়েন্ট)")

    
গণনা করা ফলাফল: Group A-কে ৪০% ইন্টারভিউর জন্য সুপারিশ করা হয়, Group B-কে মাত্র ২০% — একটি ২০.০ পার্সেন্টেজ-পয়েন্ট ডেমোগ্রাফিক-প্যারিটি ফারাক (dp_gap)। Group B-এর False Negative Rate ৬৬.৭% (Group A-এর ২৫%-এর তুলনায়) — একটি ৪১.৭ পয়েন্ট ফারাক (fnr_gap)। ক্যালিব্রেশনও ভিন্ন (A: ৭৫%, B: ৫০%) — Group B-কে যখন সুপারিশ করা হয়, সেই সুপারিশ Group A-এর তুলনায় কম নির্ভরযোগ্য। এই তিনটি সংখ্যা (dp_gap, fnr_gap, ও নিচের মডিউলগুলোর ফলাফল) মডিউল ৫-এ সরাসরি ব্যবহৃত হবে।

৪ · মডিউল ৩ — k-anonymity চেক

তৃতীয় টুকরোটি M3/L12-এর প্যাটার্নে সিস্টেমের ট্রেনিং-ডেটার তিনটি কোয়াসি-আইডেন্টিফায়ার (বয়স-ব্র্যাকেট, ৫-সংখ্যার zip কোড, লিঙ্গ) দিয়ে k-anonymity যাচাই — মূল ডেটা, বর্তমান প্রোডাকশন জেনারালাইজেশন, ও একটি বিকল্প আরও কঠোর জেনারালাইজেশন, এই তিন ধাপে।

Python
# ========== মডিউল ৩: k-anonymity চেক -- ট্রেনিং ডেটার কোয়াসি-আইডেন্টিফায়ারে ==========
training_records = [
    {"age_bracket": "20-29", "zip5": "30301", "gender": "F"},
    {"age_bracket": "20-29", "zip5": "30301", "gender": "F"},
    {"age_bracket": "20-29", "zip5": "30302", "gender": "M"},
    {"age_bracket": "30-39", "zip5": "30301", "gender": "M"},
    {"age_bracket": "30-39", "zip5": "30301", "gender": "M"},
    {"age_bracket": "30-39", "zip5": "30302", "gender": "F"},
    {"age_bracket": "40-49", "zip5": "30301", "gender": "F"},
    {"age_bracket": "40-49", "zip5": "30301", "gender": "F"},
    {"age_bracket": "20-29", "zip5": "30303", "gender": "M"},
    {"age_bracket": "30-39", "zip5": "30303", "gender": "F"},
    {"age_bracket": "40-49", "zip5": "30302", "gender": "M"},
    {"age_bracket": "20-29", "zip5": "30302", "gender": "F"},
    {"age_bracket": "30-39", "zip5": "30302", "gender": "M"},
    {"age_bracket": "40-49", "zip5": "30303", "gender": "M"},
    {"age_bracket": "20-29", "zip5": "30301", "gender": "M"},
]

def group_sizes(records, quasi_id_fields):
    groups = {}
    for r in records:
        key = tuple(r[f] for f in quasi_id_fields)
        groups.setdefault(key, []).append(r)
    return {k: len(v) for k, v in groups.items()}

def check_k_anonymity(records, quasi_id_fields, k, label):
    sizes = group_sizes(records, quasi_id_fields)
    min_size = min(sizes.values())
    violating = sum(1 for size in sizes.values() if size < k)
    satisfied = min_size >= k
    print(f"--- {label} ---")
    print(f"  কোয়াসি-আইডেন্টিফায়ার: {quasi_id_fields}")
    print(f"  মোট গ্রুপ সংখ্যা: {len(sizes)},  সর্বনিম্ন গ্রুপ সাইজ: {min_size}")
    print(f"  k={k} লঙ্ঘনকারী গ্রুপ সংখ্যা: {violating}")
    print(f"  k={k}-anonymity satisfied: {satisfied}")
    return min_size, satisfied

K_REQUIRED = 3
print("ধাপ A: মূল, জেনারালাইজ না করা কোয়াসি-আইডেন্টিফায়ার")
min_a, ok_a = check_k_anonymity(training_records, ["age_bracket", "zip5", "gender"], K_REQUIRED, "ধাপ A -- মূল ডেটা")

print("\nধাপ B: zip জেনারালাইজেশন (৫ সংখ্যা -> ৩ সংখ্যা), লিঙ্গ এখনো রাখা হয়েছে -- বর্তমান প্রোডাকশন প্র্যাকটিস")
generalized_zip3 = [dict(r, zip3=r["zip5"][:3]) for r in training_records]
min_b, ok_b = check_k_anonymity(generalized_zip3, ["age_bracket", "zip3", "gender"], K_REQUIRED, "ধাপ B -- zip3 + gender")

print("\nধাপ C: zip3 জেনারালাইজেশন + লিঙ্গ ফিল্ড বাদ দেওয়া (বিকল্প, আরও কঠোর)")
min_c, ok_c = check_k_anonymity(generalized_zip3, ["age_bracket", "zip3"], K_REQUIRED, "ধাপ C -- শুধু age_bracket + zip3")

print(f"\nসারসংক্ষেপ: সর্বনিম্ন গ্রুপ সাইজ ধাপ A-তে {min_a} -> ধাপ B-তে {min_b} -> ধাপ C-তে {min_c}")
print(f"বর্তমান প্রোডাকশন প্র্যাকটিস (ধাপ B) k={K_REQUIRED}-anonymity পূরণ করে কিনা: {ok_b}")

    
গণনা করা ফলাফল: মূল ডেটায় সর্বনিম্ন গ্রুপ সাইজ ১ (১২টি গ্রুপের মধ্যে অনেকগুলোই এক-রেকর্ডের)। বর্তমান প্রোডাকশন প্র্যাকটিসে (zip3 + gender) সর্বনিম্ন গ্রুপ সাইজ ২-এ ওঠে — উন্নতি, কিন্তু K_REQUIRED = 3 এখনো পূরণ হয় না (ok_b = False)। লিঙ্গ ফিল্ডও বাদ দিলে (ধাপ C) সর্বনিম্ন গ্রুপ সাইজ ৪-এ পৌঁছায়, যা k=3 পূরণ করে — কিন্তু এটি বর্তমানে প্রোডাকশনে প্রয়োগ করা হয়নি।

৫ · মডিউল ৪ — স্পেসিফিকেশন-গেমিং চেক

চতুর্থ টুকরোটি M9/L38-এর প্যাটার্নে একটি সত্যিকারের ছোট তুলনা — সিস্টেম যা সরাসরি অপ্টিমাইজ করে (proxy_score, "predicted job performance score") বনাম একটি প্রকৃত-লক্ষ্য-সংশ্লিষ্ট মেট্রিক (true_success, প্রকৃত দীর্ঘমেয়াদী সফলতা/রিটেনশন, হায়ারিং-সময়ে অজানা, শুধু অডিটের জন্য হাইপোথেটিক্যালি জানা)। তিনটি ভিন্ন প্রার্থী-নির্বাচন কৌশল প্রকৃতভাবে গণনা করে তুলনা করা হবে।

Python
# ========== মডিউল ৪: স্পেসিফিকেশন-গেমিং চেক -- proxy বনাম true-goal মেট্রিক ==========
# candidates -- সিন্থেটিক প্রার্থী পুল
# proxy_score          = সিস্টেম যা সরাসরি অপ্টিমাইজ করে ("predicted job performance score")
# structured_interview = হায়ারিং-সময়ে পর্যবেক্ষণযোগ্য একটি বিকল্প সিগন্যাল (গেম করা তুলনামূলক কঠিন)
# true_success          = শুধু অডিটের জন্য (হাইপোথেটিক্যালি) জানা প্রকৃত দীর্ঘমেয়াদী সফলতা -- হায়ারিং-সময়ে অজানা

candidates = [
    {"id": "C1", "proxy_score": 92, "structured_interview": 55, "true_success": 58},
    {"id": "C2", "proxy_score": 88, "structured_interview": 52, "true_success": 54},
    {"id": "C3", "proxy_score": 85, "structured_interview": 88, "true_success": 90},
    {"id": "C4", "proxy_score": 60, "structured_interview": 80, "true_success": 85},
    {"id": "C5", "proxy_score": 55, "structured_interview": 76, "true_success": 80},
    {"id": "C6", "proxy_score": 40, "structured_interview": 42, "true_success": 45},
    {"id": "C7", "proxy_score": 95, "structured_interview": 48, "true_success": 50},
    {"id": "C8", "proxy_score": 70, "structured_interview": 70, "true_success": 72},
]

K_SELECT = 3

def select_top_k(pool, score_fn, k):
    return sorted(pool, key=score_fn, reverse=True)[:k]

def avg(pool, field):
    return sum(c[field] for c in pool) / len(pool)

def report_strategy(name, selected):
    ids = [c["id"] for c in selected]
    avg_proxy = avg(selected, "proxy_score")
    avg_true = avg(selected, "true_success")
    print(f"{name}")
    print(f"  নির্বাচিত: {ids}")
    print(f"  গড় proxy_score (যা অপ্টিমাইজ করা হয়েছে): {avg_proxy:.2f}")
    print(f"  গড় true_success (প্রকৃত লক্ষ্য):          {avg_true:.2f}")
    return avg_proxy, avg_true

strategy_a = select_top_k(candidates, lambda c: c["proxy_score"], K_SELECT)
strategy_b = select_top_k(candidates, lambda c: c["true_success"], K_SELECT)
strategy_c = select_top_k(candidates, lambda c: 0.5 * c["proxy_score"] + 0.5 * c["structured_interview"], K_SELECT)

print("কৌশল (a) -- শুধু proxy_score অনুযায়ী শীর্ষ ৩ জন (বর্তমান প্রোডাকশন সিস্টেম):")
proxy_a, true_a = report_strategy("কৌশল (a)", strategy_a)

print("\nকৌশল (b) -- সত্যিকারের true_success অনুযায়ী শীর্ষ ৩ জন (oracle বেঞ্চমার্ক -- বাস্তবে হায়ারিং-সময়ে অজানা):")
proxy_b, true_b = report_strategy("কৌশল (b)", strategy_b)

print("\nকৌশল (c) -- proxy_score ও structured_interview-এর ব্লেন্ড অনুযায়ী শীর্ষ ৩ জন (প্রস্তাবিত মিটিগেশন):")
proxy_c, true_c = report_strategy("কৌশল (c)", strategy_c)

print(f"\nকৌশল (a) [বর্তমান প্রোডাকশন] বনাম কৌশল (b) [oracle]-এর true_success ফারাক: {true_b - true_a:.2f} পয়েন্ট")
print(f"ব্লেন্ডেড মিটিগেশন কৌশল (c) কতটা এই ফারাক পুনরুদ্ধার করলো: {true_c - true_a:.2f} পয়েন্ট")
print(f"মিটিগেশনের পরও অবশিষ্ট ফারাক (residual gap): {true_b - true_c:.2f} পয়েন্ট")

gap_threshold = 30
print(f"\nডায়াগনস্টিক: proxy_score - structured_interview > {gap_threshold} হলে সম্ভাব্য 'গেমড' সিগন্যাল ফ্ল্যাগ করা হলো")
flagged = []
for c in candidates:
    gap = c["proxy_score"] - c["structured_interview"]
    if gap > gap_threshold:
        flagged.append(c["id"])
        print(f"  ফ্ল্যাগড: {c['id']}  (proxy={c['proxy_score']}, structured={c['structured_interview']}, gap={gap})")
print(f"মোট ফ্ল্যাগড প্রার্থী: {flagged}")

    
গণনা করা ফলাফল: কৌশল (a) (বর্তমান প্রোডাকশন, শুধু proxy_score) সর্বোচ্চ গড় proxy_score (৯১.৬৭) অর্জন করে, কিন্তু গড় true_success মাত্র ৫৪.০। oracle বেঞ্চমার্ক কৌশল (b) গড় true_success ৮৫.০ অর্জন করে (proxy_score মাত্র ৬৬.৬৭ হওয়া সত্ত্বেও) — অর্থাৎ প্রোডাকশন সিস্টেম যা অপ্টিমাইজ করছে তা প্রকৃত লক্ষ্য থেকে ৩১.০ পয়েন্ট দূরে। ব্লেন্ডেড কৌশল (c) এর মধ্যে ১২.০ পয়েন্ট পুনরুদ্ধার করে (true_success ৬৬.০), কিন্তু তারপরও ১৯.০ পয়েন্টের একটি অবশিষ্ট ফারাক থেকে যায়। C1, C2, ও C7-কে সম্ভাব্য "গেমড" সিগন্যাল হিসেবে ফ্ল্যাগ করা হয়েছে — এদের সবারই true_success কম (৫০-৫৮), যা ফ্ল্যাগিং হিউরিস্টিকের কার্যকারিতা সমর্থন করে। এটিই স্পেসিফিকেশন-গেমিং-এর মূল কথা: একটি proxy সরাসরি অপ্টিমাইজ করা প্রকৃত লক্ষ্যে সেরা ফলাফল দেয় না।

৬ · মডিউল ৫ — ওয়েটেড কমপ্লায়েন্স/অডিট চেকলিস্ট

পঞ্চম টুকরোটি M12-M13-এর প্যাটার্নে একটি ওয়েটেড চেকলিস্ট — কিন্তু প্রতিটি ক্রাইটেরিয়ার স্ট্যাটাস হার্ডকোড করা নয়, বরং উপরের চারটি মডিউলে সত্যিই গণনা করা সংখ্যা (dp_gap, fnr_gap, min_b, true_b - true_a) থ্রেশহোল্ডের বিপরীতে চেক করে নির্ধারিত। ওয়েটেড স্কোরের সূত্র: $\text{score} = \sum_i w_i \cdot v(\text{status}_i)$, যেখানে $v(\text{pass})=1$, $v(\text{partial})=0.5$, $v(\text{fail})=0$।

Python
# ========== মডিউল ৫: ওয়েটেড কমপ্লায়েন্স/অডিট চেকলিস্ট ==========
# আগের ৪টি মডিউলের প্রকৃত গণনা করা ফলাফলের উপর ভিত্তি করে প্রতিটি ক্রাইটেরিয়ার স্ট্যাটাস নির্ধারণ করা হচ্ছে --
# হার্ডকোড করা রায় নয়, বরং থ্রেশহোল্ডের বিপরীতে প্রকৃত সংখ্যা চেক করে

STATUS_VALUE = {"pass": 1.0, "partial": 0.5, "fail": 0.0}

DP_GAP_THRESHOLD = 0.10     # ১০ পার্সেন্টেজ পয়েন্টের বেশি ডেমোগ্রাফিক-প্যারিটি ফারাক গ্রহণযোগ্য না
FNR_GAP_THRESHOLD = 0.15    # ১৫ পার্সেন্টেজ পয়েন্টের বেশি FNR ফারাক গ্রহণযোগ্য না
SPEC_GAP_THRESHOLD = 10.0   # proxy-only উৎপাদন বনাম oracle-এর মধ্যে ১০ পয়েন্টের বেশি true_success ফারাক উদ্বেগজনক

parity_status = "fail" if dp_gap > DP_GAP_THRESHOLD else "pass"
error_rate_status = "fail" if fnr_gap > FNR_GAP_THRESHOLD else "pass"
kanon_status = "pass" if min_b >= K_REQUIRED else "fail"

spec_gap = true_b - true_a
specgaming_status = "fail" if spec_gap > SPEC_GAP_THRESHOLD else "pass"

checklist = [
    {"criterion": "স্টেকহোল্ডার বিশ্লেষণ সম্পন্ন হয়েছে (মডিউল ১)",                 "weight": 0.10, "status": "pass"},
    {"criterion": "ডেমোগ্রাফিক-প্যারিটি ফেয়ারনেস মানদণ্ড পূরণ করে",                 "weight": 0.15, "status": parity_status},
    {"criterion": "গ্রুপভেদে False Negative Rate সহনীয় সীমার মধ্যে",               "weight": 0.15, "status": error_rate_status},
    {"criterion": f"ট্রেনিং-ডেটা k={K_REQUIRED}-anonymity পূরণ করে (বর্তমান প্র্যাকটিসে)", "weight": 0.15, "status": kanon_status},
    {"criterion": "প্রোডাকশনে স্পেসিফিকেশন-গেমিং গ্যাপ থ্রেশহোল্ডের মধ্যে",          "weight": 0.15, "status": specgaming_status},
    {"criterion": "মানুষের আপিল/রিভিউ পথ ডকুমেন্টেড আছে",                           "weight": 0.15, "status": "partial"},
    {"criterion": "অডিট-ট্রেইল ও চলমান মনিটরিং ডকুমেন্টেড আছে",                     "weight": 0.15, "status": "partial"},
]

total_weight = sum(item["weight"] for item in checklist)
assert abs(total_weight - 1.0) < 1e-9, "ওয়েট মোট ১.০ হতে হবে"

def audit_score(checklist):
    weighted_total = 0.0
    print(f"{'ক্রাইটেরিয়া':58s} {'ওয়েট':>6s} {'স্ট্যাটাস':>8s} {'ওয়েটেড':>8s}")
    for item in checklist:
        value = STATUS_VALUE[item["status"]]
        weighted = value * item["weight"]
        weighted_total += weighted
        print(f"{item['criterion']:58s} {item['weight']:6.2f} {item['status']:>8s} {weighted:8.3f}")
    return weighted_total

overall_score = audit_score(checklist)
print(f"\nসামগ্রিক ওয়েটেড অডিট স্কোর: {overall_score:.3f}  ({overall_score*100:.1f}%)")

failed = [item["criterion"] for item in checklist if item["status"] == "fail"]
print(f"\nফেইল করা ক্রাইটেরিয়া ({len(failed)}টি):")
for f in failed:
    print(f"  - {f}")

    
গণনা করা ফলাফল: dp_gap (০.২০) থ্রেশহোল্ড (০.১০) ছাড়িয়ে যাওয়ায় প্যারিটি ক্রাইটেরিয়া fail; fnr_gap (০.৪১৭) থ্রেশহোল্ড (০.১৫) ছাড়িয়ে যাওয়ায় error-rate ক্রাইটেরিয়া fail; min_b (২) K_REQUIRED (৩)-এর নিচে থাকায় k-anonymity ক্রাইটেরিয়া fail; spec_gap (৩১.০) থ্রেশহোল্ড (১০.০) ছাড়িয়ে যাওয়ায় স্পেসিফিকেশন-গেমিং ক্রাইটেরিয়া fail। চারটি ফেইল + দুটি আংশিক ক্রাইটেরিয়া মিলিয়ে সামগ্রিক ওয়েটেড স্কোর দাঁড়ায় ০.২৫০ (২৫.০%)।

৭ · মডিউল ৬ — চূড়ান্ত অডিট রিপোর্ট ও সুপারিশ

শেষ টুকরোটি উপরের সবগুলো মডিউলের ফলাফল একত্র করে একটি একক সুপারিশে পৌঁছায় — থ্রেশহোল্ড-ভিত্তিক টিয়ারিং ব্যবহার করে, কোনো হার্ডকোড করা উপসংহার নয়।

Python
# ========== মডিউল ৬: চূড়ান্ত অডিট রিপোর্ট ও সুপারিশ ==========
DEPLOY_THRESHOLD = 0.85
CONDITIONAL_THRESHOLD = 0.50

if overall_score >= DEPLOY_THRESHOLD:
    recommendation = "ডিপ্লয় করা যেতে পারে (Deploy)"
elif overall_score >= CONDITIONAL_THRESHOLD:
    recommendation = "শর্তসাপেক্ষে ডিপ্লয় করা যেতে পারে (Deploy with conditions)"
else:
    recommendation = "বর্তমান অবস্থায় ডিপ্লয় করা উচিত নয় (Do not deploy as-is)"

print("=" * 78)
print("চূড়ান্ত অডিট রিপোর্ট -- হাইপোথেটিক্যাল হায়ারিং-রিকমেন্ডেশন AI সিস্টেম")
print("=" * 78)
print(f"সামগ্রিক ওয়েটেড অডিট স্কোর: {overall_score*100:.1f}%")
print(f"সুপারিশ: {recommendation}")
print()
print("সুনির্দিষ্ট কারণ (প্রকৃত গণনা করা ফলাফলের ভিত্তিতে):")
print(f"  ১. স্টেকহোল্ডার বিশ্লেষণ দেখিয়েছে '{most_affected[0]}' গোষ্ঠী সবচেয়ে বেশি (ওয়েটেড স্কোর "
      f"{most_affected[3]:.3f}) নেতিবাচকভাবে প্রভাবিত, অথচ তাদের ডিজাইন প্রক্রিয়ায় সবচেয়ে কম প্রভাব আছে।")
print(f"  ২. Group A ও Group B-এর মধ্যে ডেমোগ্রাফিক-প্যারিটি ফারাক {dp_gap*100:.1f} পার্সেন্টেজ পয়েন্ট "
      f"(থ্রেশহোল্ড {DP_GAP_THRESHOLD*100:.0f} পয়েন্টের বেশি), আর False Negative Rate ফারাক "
      f"{fnr_gap*100:.1f} পয়েন্ট (থ্রেশহোল্ড {FNR_GAP_THRESHOLD*100:.0f} পয়েন্টের বেশি) -- যোগ্য Group B "
      f"প্রার্থীরা অনুপাতে অনেক বেশি ভুলভাবে বাদ পড়ছেন।")
print(f"  ৩. ট্রেনিং-ডেটার বর্তমান প্রোডাকশন জেনারালাইজেশনে সর্বনিম্ন গ্রুপ সাইজ {min_b}, যা k={K_REQUIRED}-anonymity "
      f"পূরণ করে না (লিঙ্গ ফিল্ড বাদ দিলে সর্বনিম্ন গ্রুপ সাইজ {min_c}-এ উন্নীত হয়ে সন্তুষ্ট হতো)।")
print(f"  ৪. বর্তমান প্রোডাকশন সিস্টেম শুধু proxy_score অপ্টিমাইজ করে -- oracle বেঞ্চমার্কের তুলনায় প্রকৃত "
      f"true_success-এ {spec_gap:.1f} পয়েন্ট ঘাটতি, যার মধ্যে একটি সহজ ব্লেন্ডেড মিটিগেশনেই "
      f"{true_c - true_a:.1f} পয়েন্ট পুনরুদ্ধারযোগ্য -- কিন্তু তা এখনো প্রয়োগ করা হয়নি।")
print(f"  ৫. মানুষের আপিল পথ ও অডিট-ট্রেইল উভয়ই আংশিক (partial), সম্পূর্ণ ডকুমেন্টেড নয়।")
print()
print(f"মোট {len(failed)}টি ওয়েটেড ক্রাইটেরিয়া ফেইল করেছে -- এই ফলাফলগুলো সমাধান না করে ডিপ্লয় করলে সিস্টেমটি "
      f"নিয়মিতভাবে একটি ডেমোগ্রাফিক গ্রুপের যোগ্য প্রার্থীদের বাদ দেবে, এবং 'ভালো দেখায়' এমন প্রার্থী বাছাই "
      f"করবে যাদের প্রকৃত দীর্ঘমেয়াদে সফল হওয়ার সম্ভাবনা প্রমাণিতভাবে কম।")

    
যেহেতু overall_score (০.২৫) ০.৫০ শর্তসাপেক্ষ থ্রেশহোল্ডের নিচে, চূড়ান্ত সুপারিশ হলো "বর্তমান অবস্থায় ডিপ্লয় করা উচিত নয়" — এবং লক্ষ্য করুন এই সুপারিশটি overall_score-এর একটি সাধারণ থ্রেশহোল্ড চেক থেকে বেরিয়ে এসেছে, যা নিজেই মডিউল ১-৪-এর প্রকৃত গণনা করা সংখ্যা থেকে বেরিয়েছে — কোথাও কোনো "উপসংহার" আলাদাভাবে হার্ডকোড করা হয়নি।
মূল কথা · Key takeaway

একটি বাস্তবসম্মত AI এথিক্স ও সেফটি অডিট কখনোই একটি একক মেট্রিক্স নয় — এটি একাধিক স্বাধীন কিন্তু আন্তঃসংযুক্ত পরীক্ষার সমষ্টি: কারা প্রভাবিত হয় (M1), কতটা অন্যায্যভাবে প্রভাবিত হয় (M4), কতটা ব্যক্তিগত তথ্য ঝুঁকিতে আছে (M3), সিস্টেম যা অপ্টিমাইজ করছে তা প্রকৃত লক্ষ্যের সাথে কতটা মেলে (M9), এবং এই সবকিছু মিলিয়ে একটি সংগঠিত সিদ্ধান্তে পৌঁছানো যায় কিনা (M12-M13)। এই কোর্সে আমরা এই প্রতিটি লেন্স আলাদা আলাদা করে গভীরভাবে শিখেছি (M1-M12), আর এই শেষ পাঠে দেখলাম কীভাবে তারা একসাথে একটি বাস্তবসম্মত, প্রমাণ-ভিত্তিক অডিটে রূপ নেয়। কম্পিউটিং এথিক্স কোনো একটি সূত্র বা একটি চেকবক্স নয় — এটি একটি অনুশীলন, যা এই কোর্স জুড়ে ধাপে ধাপে তৈরি করা টুল ও প্রশ্নগুলো দিয়ে প্রতিটি নতুন সিস্টেমের সাথে নতুন করে প্রয়োগ করতে হয়।

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

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

প্র ০১ মডিউল ৪-এ কেন কৌশল (b) [oracle, true_success অনুযায়ী নির্বাচন] সরাসরি প্রোডাকশনে ব্যবহার করা যায় না, যদিও এটি সর্বোচ্চ true_success দেয়?

কারণ true_success সংজ্ঞা অনুযায়ীই এমন কিছু যা শুধু হায়ারিং-এর পরে, দীর্ঘ সময় ধরে পর্যবেক্ষণ করে জানা যায় — হায়ারিং-সময়ে সিস্টেমের কাছে এই তথ্য থাকেই না। কৌশল (b) একটি "oracle বেঞ্চমার্ক" হিসেবে কাজ করে যা দেখায় সর্বোচ্চ কতটা ভালো ফলাফল তাত্ত্বিকভাবে সম্ভব ছিল, যাতে বাস্তবে ব্যবহারযোগ্য কৌশলগুলো (যেমন কৌশল (a) বা (c)) তার সাথে তুলনা করে মূল্যায়ন করা যায় — এটি নিজে একটি ডিপ্লয়যোগ্য বিকল্প নয়।

প্র ০২ মডিউল ৫-এ থ্রেশহোল্ডগুলো (যেমন DP_GAP_THRESHOLD = 0.10) কোথা থেকে আসে, এবং এগুলো বদলালে কী প্রভাব পড়বে?

এই পাঠে থ্রেশহোল্ডগুলো ইলাস্ট্রেটিভভাবে বসানো হয়েছে একটি যুক্তিসঙ্গত সংগঠনের নীতি প্রতিনিধিত্ব করতে — বাস্তবে এগুলো একটি প্রতিষ্ঠানের ঝুঁকি-সহনশীলতা, প্রযোজ্য আইন (যেমন L51-এর GDPR বা L52-এর EU AI Act-এর মতো রেগুলেশন), ও ডোমেইনের গুরুত্বের উপর নির্ভর করে ঠিক করা উচিত। থ্রেশহোল্ড বাড়ালে (যেমন DP_GAP_THRESHOLD = 0.25) বর্তমান ০.২০ ফারাক আর fail দেখাতো না (0.20 > 0.25 মিথ্যা হতো) — এটি দেখায় থ্রেশহোল্ড বাছাই নিজেই একটি নৈতিক সিদ্ধান্ত, নিছক একটি টেকনিক্যাল প্যারামিটার নয়।

প্র ০৩ যদি শুধু k-anonymity ক্রাইটেরিয়াটি ঠিক করা হতো (ধাপ C গ্রহণ করে, kanon_status "fail" থেকে "pass" হতো) কিন্তু বাকি সব একই থাকতো, চূড়ান্ত সুপারিশ কি বদলাতো?

ওয়েট ০.১৫-এর ক্রাইটেরিয়া fail (মান ০) থেকে pass (মান ১.০) হলে স্কোরে 0.15 × 1.0 = 0.15 যোগ হতো, ফলে 0.25 + 0.15 = 0.40। এটি এখনো ০.৫০ শর্তসাপেক্ষ থ্রেশহোল্ডের নিচে, তাই সুপারিশ অপরিবর্তিত থাকতো — "বর্তমান অবস্থায় ডিপ্লয় করা উচিত নয়"। এটি দেখায় যখন একাধিক গুরুত্বপূর্ণ ক্রাইটেরিয়া ফেইল করে, শুধু একটি ঠিক করলেই সিদ্ধান্ত বদলে যায় না — ফেয়ারনেস ও স্পেসিফিকেশন-গেমিং সমস্যাগুলোও একসাথে সমাধান করতে হবে।

অনুশীলন

  1. চিন্তা করুন: মডিউল ৫-এর চেকলিস্টে "মানুষের আপিল/রিভিউ পথ" ও "অডিট-ট্রেইল" ক্রাইটেরিয়া দুটো কেন সরাসরি কোনো গণনা করা সংখ্যা থেকে না বের করে সরাসরি "partial" হিসেবে বসানো হয়েছে? এটি কি ফ্রেমওয়ার্কের একটি দুর্বলতা?

    এটি একটি বাস্তবসম্মত সীমাবদ্ধতা, দুর্বলতা নয় — সব ক্রাইটেরিয়া স্বাভাবিকভাবে পরিমাপযোগ্য নয় (এই কোর্সের CLAUDE.md-এর নীতির সাথে সামঞ্জস্যপূর্ণ: যা সত্যিই গণনা করা যায় তা গণনা করা হবে, যা নয় তা জোর করে "কৃত্রিম গণনা" বানানো হবে না)। একটি আপিল-পথ "ডকুমেন্টেড আছে কিনা" এমন একটি প্রশ্ন যার উত্তর একজন মানুষকে সরাসরি নীতিমালা পড়ে যাচাই করতে হয় — এটি একটি বৈধ, প্রক্রিয়াগত অডিট-ক্রাইটেরিয়া, শুধু ফেয়ারনেস মেট্রিক্সের মতো স্বয়ংক্রিয়ভাবে ডেটা থেকে বের করা যায় না।

  2. পরীক্ষা করুন: মডিউল ৪-এ candidates-এ C7-এর proxy_score-কে ৯৫ থেকে ৭২-এ কমিয়ে মডিউল ৪, ৫, ও ৬ আবার Run করুন (ক্রমানুসারে) — strategy_a-এর নির্বাচন, spec_gap, ও চূড়ান্ত সুপারিশ কীভাবে বদলায়?

    C7-এর proxy_score ৭২ হলে সাজানো ক্রমে (proxy অনুযায়ী নেমে) C1(৯২), C2(৮৮), C8(৭০ বা তার আশেপাশে) শীর্ষে উঠে আসবে, C7 আর শীর্ষ ৩-এ থাকবে না -- strategy_a বদলে যাবে। নতুন গড় true_success সম্ভবত কিছুটা বাড়বে (কারণ C7-এর true_success মাত্র ৫০ ছিল, বাদ পড়ায় গড় উন্নতি হতে পারে), ফলে spec_gap (true_b - true_a) কিছুটা কমবে। যদি spec_gap ১০.০ থ্রেশহোল্ডের নিচে নেমে যায়, specgaming_status "fail" থেকে "pass"-এ বদলে যাবে, এবং মডিউল ৫-এর overall_score বাড়বে -- সঠিক নতুন সংখ্যা যাচাই করতে কোডটি সত্যিই Run করে দেখুন।

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

  • কোর্সের সম্পূর্ণ সিলেবাস আবার দেখুন ৫৭টি পাঠ · সম্পূর্ণ অভিনন্দন — Ethics in Computing & AI Safety কোর্সের সবগুলো পাঠ এখন সম্পূর্ণ। ক্লাসিক্যাল এথিক্যাল ফ্রেমওয়ার্ক থেকে শুরু করে প্রাইভেসি, অ্যালগরিদমিক বায়াস, প্রফেশনাল এথিক্স, সফটওয়্যার সেফটি, সাইবারসিকিউরিটি এথিক্স, AI অ্যালাইনমেন্ট/এক্সিস্টেনশিয়াল সেফটি, রেগুলেশন, ও এই ক্যাপস্টোন পর্যন্ত — সম্পূর্ণ যাত্রা শেষ হলো। কোনো পাঠ আর "শীঘ্রই আসছে" নেই।
  • আগের পাঠ L56 নিজের প্রজেক্টের জন্য একটি এথিক্স রিভিউ ফ্রেমওয়ার্ক তৈরি করা — এই ক্যাপস্টোনের মডিউল ৫-এর পেছনের রিইউজেবল ফাংশন।
  • AI Foundations কোর্সের AI এথিক্স পাঠ সহোদর পাঠ AI এথিক্সের একটি সংক্ষিপ্ত পরিচিতি — এই কোর্স সেই বিষয়টিকেই এই ক্যাপস্টোন পর্যন্ত অনেক গভীরে নিয়ে গেছে।
  • Mobile App Development কোর্স সহোদর কোর্স এই ক্যাপস্টোনের মতোই একটি মডিউল-বাই-মডিউল বিল্ড-আপ স্টাইলে সেই কোর্সের নিজস্ব ক্যাপস্টোন লেখা হয়েছে — একই ধরনের ওয়েটেড-স্কোরিং ও প্রগ্রেসিভ-বিল্ড-আপ কনভেনশন সেখানেও দেখা যাবে।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics, Full-Stack Web Frameworks, Mobile App Development ও Ethics in Computing & AI Safety — সব এক জায়গায়।
আগের পাঠ
নিজের প্রজেক্টের জন্য একটি এথিক্স রিভিউ ফ্রেমওয়ার্ক তৈরি করা