পাঠ ৩৯ · ৫৭-এর মধ্যে · মডিউল ৯
Home / Courses / Ethics in Computing & AI Safety / গুডহার্টের সূত্র

AI সিস্টেমে গুডহার্টের সূত্র

Goodhart's law in AI systems
১০ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

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

  • গুডহার্টের সূত্রের সংজ্ঞা ও এটি কোথা থেকে এসেছে
  • Python দিয়ে একটি সত্যিকারের রৈখিক-সম্পর্ক (linear fit) হিসাব — স্বাভাবিক পরিবর্তনে প্রক্সি ও প্রকৃত লক্ষ্য একসাথে চলার প্রমাণ
  • একই সম্পর্ক ব্যবহার করে তীব্র অপ্টিমাইজেশনের বিন্দুতে "প্রত্যাশিত" মান হিসাব করা, এবং সেটিকে প্রকৃতপক্ষে পরিমাপ করা মানের সাথে তুলনা করা — একটি জেনুইন কম্পিউট করা বিচ্যুতি
  • L38-এর স্পেসিফিকেশন গেমিং-এর সাথে গুডহার্টের সূত্রের সম্পর্ক — একই মেকানিজমের দুটি ভিন্ন লেন্স

১ · সংজ্ঞা

গুডহার্টের সূত্রGoodhart's Lawএকটি মেট্রিক যা স্বাভাবিক, অপ্টিমাইজ-না-করা পরিবর্তনে একটি প্রকৃত লক্ষ্যের ভালো প্রক্সি, সেই মেট্রিককে সরাসরি ও তীব্রভাবে টার্গেট/অপ্টিমাইজ করা শুরু করলে প্রকৃত লক্ষ্য থেকে বিচ্ছিন্ন হয়ে যেতে পারে। অর্থনীতিবিদ চার্লস গুডহার্টের একটি পর্যবেক্ষণ থেকে এসেছে (পরে নৃবিজ্ঞানী ম্যারিলিন স্ট্র্যাদার্ন এটিকে বিখ্যাত সংক্ষিপ্ত রূপে বলেন: "যখন একটি পরিমাপ লক্ষ্যে পরিণত হয়, তখন সেটি আর ভালো পরিমাপ থাকে না")। মূল ধারণাটি L38-এর স্পেসিফিকেশন গেমিং-এর সাথে ঘনিষ্ঠভাবে সম্পর্কিত, কিন্তু একটু ভিন্ন কোণ থেকে দেখে: L38 দেখিয়েছে একটি একক মুহূর্তে প্রক্সি-সর্বোচ্চ সমাধান প্রকৃত লক্ষ্যে ব্যর্থ হতে পারে। গুডহার্টের সূত্র দেখায় এটি ধারাবাহিকভাবে — প্রক্সি ও প্রকৃত লক্ষ্যের মধ্যে সম্পর্কটি অপ্টিমাইজেশনের চাপ বাড়ার সাথে সাথে ধীরে ধীরে ভেঙে যায়, এমনকি এমন মেট্রিকের ক্ষেত্রেও যা সাধারণ পরিসরে সত্যিই নির্ভরযোগ্য।

২ · একটি সম্পূর্ণ কম্পিউট করা before/after দৃশ্যকল্প

দৃশ্যকল্প: একটি কাস্টমার-সাপোর্ট টিমে প্রক্সি মেট্রিক হলো tickets_per_hour (প্রতি ঘণ্টায় বন্ধ করা টিকিটের সংখ্যা) এবং প্রকৃত লক্ষ্য হলো resolution_rate (গ্রাহকের সমস্যা সত্যিই সমাধান হয়েছে কিনা — টিকিট পুনরায় খোলা হয়নি এমন শতাংশ)। প্রথমে ৫ জন এজেন্টের স্বাভাবিক দক্ষতা-পরিবর্তনে (কেউ কম দক্ষ, কেউ বেশি দক্ষ — কেউই মেট্রিক "গেম" করার চেষ্টা করছে না) এই দুটি সংখ্যা কতটা একসাথে চলে তা একটি সত্যিকারের রৈখিক-সম্পর্ক (least-squares fit) দিয়ে পরিমাপ করা হবে। তারপর একটি ষষ্ঠ, তীব্রভাবে-অপ্টিমাইজড কৌশল (টিকিট যাচাই না করেই দ্রুত বন্ধ করে দেওয়া) যোগ করে দেখা হবে সেই সম্পর্কটি কতটা ভেঙে যায়।

Python
# একটি সিন্থেটিক, ইলাস্ট্রেটিভ কাস্টমার-সাপোর্ট দৃশ্যকল্প -- বাস্তব কোনো টিম/কোম্পানির ডেটা নয়

# ধাপ ১ -- "BEFORE": স্বাভাবিক দক্ষতা-পরিবর্তন, কেউই মেট্রিক গেম করছে না
# প্রতিটি এজেন্টের tickets_per_hour (PROXY) ও resolution_rate (TRUE GOAL, %)
normal_agents = [
    {"name": "এজেন্ট A (নতুন)",     "tickets_per_hour": 4, "resolution_rate": 70},
    {"name": "এজেন্ট B",           "tickets_per_hour": 5, "resolution_rate": 74},
    {"name": "এজেন্ট C (মাঝারি)",   "tickets_per_hour": 6, "resolution_rate": 78},
    {"name": "এজেন্ট D",           "tickets_per_hour": 7, "resolution_rate": 81},
    {"name": "এজেন্ট E (অভিজ্ঞ)",   "tickets_per_hour": 8, "resolution_rate": 85},
]

def linear_fit(points):
    # সাধারণ least-squares রৈখিক ফিট: resolution_rate ~= slope * tickets_per_hour + intercept
    xs = [p["tickets_per_hour"] for p in points]
    ys = [p["resolution_rate"] for p in points]
    n = len(points)
    mean_x = sum(xs) / n
    mean_y = sum(ys) / n
    numerator = sum((x - mean_x) * (y - mean_y) for x, y in zip(xs, ys))
    denominator = sum((x - mean_x) ** 2 for x in xs)
    slope = numerator / denominator
    intercept = mean_y - slope * mean_x
    return slope, intercept

def predict(slope, intercept, x):
    return slope * x + intercept

slope, intercept = linear_fit(normal_agents)

print("ধাপ ১ -- BEFORE (স্বাভাবিক পরিবর্তন, কোনো গেমিং নেই):")
print(f"  ফিট করা সম্পর্ক: resolution_rate ~= {slope:.2f} * tickets_per_hour + {intercept:.2f}\n")
for a in normal_agents:
    predicted = predict(slope, intercept, a["tickets_per_hour"])
    residual = a["resolution_rate"] - predicted
    print(f"  {a['name']:<18} প্রকৃত={a['resolution_rate']}  প্রত্যাশিত={predicted:.1f}  পার্থক্য={residual:+.1f}")

# ধাপ ২ -- "AFTER": প্রক্সিটিকে সরাসরি ও তীব্রভাবে অপ্টিমাইজ করা হলো
# (যাচাই না করেই দ্রুত টিকিট বন্ধ করে দেওয়া -- tickets_per_hour অনেক বাড়ে,
#  কিন্তু resolution_rate আলাদাভাবে, সত্যিকারের পুনরায়-খোলা-হার দিয়ে পরিমাপ করা হয়েছে)
gamed_strategy = {"name": "গেমড এজেন্ট (যাচাই ছাড়াই দ্রুত বন্ধ)", "tickets_per_hour": 20, "resolution_rate": 25}

predicted_gamed = predict(slope, intercept, gamed_strategy["tickets_per_hour"])
actual_gamed = gamed_strategy["resolution_rate"]
gap = actual_gamed - predicted_gamed

print(f"\nধাপ ২ -- AFTER (প্রক্সি তীব্রভাবে অপ্টিমাইজ করা হলো):")
print(f"  {gamed_strategy['name']}")
print(f"  tickets_per_hour = {gamed_strategy['tickets_per_hour']} (proxy অনেক বেড়েছে)")
print(f"  BEFORE-সম্পর্ক অনুযায়ী প্রত্যাশিত resolution_rate: {predicted_gamed:.1f}")
print(f"  প্রকৃতপক্ষে আলাদাভাবে পরিমাপ করা resolution_rate:   {actual_gamed}")
print(f"  বিচ্যুতি (গ্যাপ): {gap:+.1f} পয়েন্ট")

    
লক্ষ্য করুন BEFORE-এর ৫টি স্বাভাবিক ডেটা-পয়েন্টে "পার্থক্য" (residual) সবসময় ০.৫-এর কম — অর্থাৎ রৈখিক সম্পর্কটি স্বাভাবিক পরিসরে অত্যন্ত নির্ভরযোগ্যভাবে প্রক্সি থেকে প্রকৃত লক্ষ্য অনুমান করতে পারে। কিন্তু AFTER-এ, একই সম্পর্ক ব্যবহার করে হিসাব করা "প্রত্যাশিত" মান (১২৯.৪ — যা ১০০-এর সম্ভাব্য সর্বোচ্চ সীমাও ছাড়িয়ে যায়) বনাম আলাদাভাবে পরিমাপ করা "প্রকৃত" মান (২৫) — এর মধ্যে ব্যবধান প্রায় ১০৪ পয়েন্ট, যা BEFORE-এর যেকোনো পার্থক্যের চেয়ে দুই শতাধিক গুণ বড়। সম্পর্কটি স্বাভাবিক পরিসরে যতই নিখুঁত মনে হোক, তীব্র, সরাসরি অপ্টিমাইজেশনের সামনে তা ভেঙে পড়ে।

৩ · কেন সম্পর্কটি ভেঙে যায়

স্বাভাবিক দক্ষতা-পরিবর্তনে (এজেন্ট A থেকে E) tickets_per_hour বাড়ার প্রকৃত কারণ ছিল এজেন্টের প্রকৃত দক্ষতা বৃদ্ধি — যা স্বাভাবিকভাবেই resolution_rate-কেও বাড়িয়ে দেয়। কিন্তু যখন কাউকে সরাসরি tickets_per_hour সর্বোচ্চ করতে বলা হয় (এটিকেই "টার্গেট" বানানো হয়), তখন সেই লক্ষ্যে পৌঁছানোর দ্রুততম পথ আর "প্রকৃত দক্ষতা বৃদ্ধি" থাকে না — বরং "যাচাই না করেই টিকিট বন্ধ করে দেওয়া"র মতো একটি শর্টকাট হয়ে যায়, যা প্রক্সিতে বিশাল উন্নতি দেখায় অথচ প্রকৃত লক্ষ্যকে ধ্বংস করে। এটিই গুডহার্টের সূত্রের মূল প্রক্রিয়া — মেট্রিক ও লক্ষ্যের মধ্যে সম্পর্কটি একটি নির্দিষ্ট, অপ্টিমাইজ-না-করা কারণের (এখানে: প্রকৃত দক্ষতা) উপর নির্ভরশীল ছিল; সরাসরি অপ্টিমাইজেশন সেই কারণটিকে বাইপাস করে ফেলে।

L38-এর সাথে সম্পর্ক

L38-এর "দরজা বন্ধ করে দেওয়া" রোবট-স্ট্র্যাটেজি ও এই পাঠের "যাচাই ছাড়াই টিকিট বন্ধ" এজেন্ট — দুটোই একই অন্তর্নিহিত মেকানিজমের উদাহরণ। L38 একটি একক মুহূর্তের candidate-তুলনা দেখিয়েছে; এই পাঠ দেখালো কীভাবে একটি মেট্রিক-লক্ষ্য সম্পর্ক যা বহু স্বাভাবিক ডেটা-পয়েন্টে চমৎকারভাবে কাজ করে, অপ্টিমাইজেশনের চাপ যথেষ্ট তীব্র হলে ভেঙে পড়তে পারে।

মূল কথা · Key takeaway

একটি মেট্রিক স্বাভাবিক, অপ্টিমাইজ-না-করা পরিসরে যতই ভালো প্রক্সি হোক না কেন, সেই সম্পর্কের উপর অন্ধ আস্থা রাখা বিপজ্জনক — কারণ সম্পর্কটি একটি অন্তর্নিহিত, অপ্টিমাইজ-না-করা কারণের উপর নির্ভরশীল, এবং সরাসরি, তীব্র অপ্টিমাইজেশন সেই কারণটিকেই বাইপাস করে দিতে পারে। এটিই একটি কারণ কেন AI সিস্টেম ডিজাইনে শুধু "মেট্রিক সর্বোচ্চ করো" বলাটুকু যথেষ্ট নয়।

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

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

প্র ০১ BEFORE-এর ৫টি ডেটা-পয়েন্টে "পার্থক্য" (residual) এত ছোট কেন, অথচ AFTER-এর পয়েন্টে এত বড়?

BEFORE-এর পয়েন্টগুলোতে tickets_per_hour বৃদ্ধির প্রকৃত কারণ ছিল এজেন্টের স্বাভাবিক দক্ষতা বৃদ্ধি, যা resolution_rate-কেও একইভাবে বাড়িয়েছিল — তাই এই দুটোর মধ্যেকার রৈখিক সম্পর্কটি সেই সীমার মধ্যে অত্যন্ত নির্ভরযোগ্য। AFTER-এর পয়েন্টে tickets_per_hour বৃদ্ধির কারণ সম্পূর্ণ ভিন্ন (যাচাই না করে বন্ধ করে দেওয়া) — এটি একই "দক্ষতা বৃদ্ধি" কারণের মধ্য দিয়ে আসেনি, তাই BEFORE-এর সম্পর্কটি সেখানে আর প্রযোজ্য থাকে না।

প্র ০২ BEFORE-সম্পর্ক অনুযায়ী AFTER-পয়েন্টের জন্য প্রত্যাশিত মান (১২৯.৪) ১০০ শতাংশের সম্ভাব্য সর্বোচ্চ সীমাও ছাড়িয়ে গেছে। এটি কী প্রমাণ করে?

এটি প্রমাণ করে যে রৈখিক সম্পর্কটি শুধুমাত্র সেই সীমিত পরিসরে (৪-৮ টিকিট/ঘণ্টা) বৈধ যেখান থেকে এটি গণনা করা হয়েছিল — এটিকে সেই পরিসরের অনেক বাইরে (২০ টিকিট/ঘণ্টা) এক্সট্রাপোলেট করা মৌলিকভাবে অবৈধ, কারণ বাস্তবে resolution_rate কখনো ১০০%-এর বেশি হতে পারে না। সম্পর্কটি "বাস্তবে অসম্ভব" একটি মান প্রত্যাশা করছে এই তথ্যটিই ইঙ্গিত দেয় যে তীব্র অপ্টিমাইজেশনের বিন্দুতে সম্পর্কটি আর নির্ভরযোগ্য নয়।

প্র ০৩ উপরের কোডে যদি gamed_strategy-এর resolution_rate-কে ২৫ থেকে বাড়িয়ে ৯০ করা হয়, তাহলে কি gap-এর মান পজিটিভ (+) হয়ে যাবে, এবং এর মানে কী হবে?

হ্যাঁ। gap = actual_gamed - predicted_gamed = 90 - 129.4 = -39.4 — এখনও নেগেটিভ, কারণ প্রত্যাশিত মান (১২৯.৪) নিজেই বাস্তবে অসম্ভব রকম বেশি। gap পজিটিভ হতে হলে actual_gamed-কে ১২৯.৪-এর বেশি হতে হবে, যা ১০০%-এর সর্বোচ্চ সীমার কারণে কখনোই সম্ভব নয় — এটিই আবার দেখায় যে BEFORE-সম্পর্কটি AFTER-এর মতো চরম বিন্দুতে এক্সট্রাপোলেট করা যায় না।

অনুশীলন

  1. চিন্তা করুন: "কোড লেখার লাইন সংখ্যা" একজন প্রোগ্রামারের প্রকৃত উৎপাদনশীলতার একটি প্রক্সি হতে পারে স্বাভাবিক পরিসরে (বেশি অভিজ্ঞ প্রোগ্রামার প্রায়ই বেশি কার্যকরী কোড লেখেন)। এই মেট্রিককে সরাসরি "টার্গেট" বানালে (যেমন কর্মক্ষমতা মূল্যায়নে) কী ঘটতে পারে?

    প্রোগ্রামাররা অপ্রয়োজনীয়ভাবে দীর্ঘ, পুনরাবৃত্তিমূলক কোড লিখতে পারেন, বা একই কাজ কম লাইনে করা যায় এমন সহজ সমাধান এড়িয়ে চলতে পারেন — লাইন সংখ্যা বাড়বে (প্রক্সি সর্বোচ্চ হবে), কিন্তু প্রকৃত উৎপাদনশীলতা/কোড-মান কমতে পারে। এটি ঠিক উপরের কোড সেলের "যাচাই ছাড়াই টিকিট বন্ধ করা" এজেন্টের মতোই একটি গুডহার্ট-প্যাটার্ন।

  2. পরীক্ষা করুন: উপরের কোড সেলে normal_agents তালিকায় একটি নতুন এজেন্ট যোগ করুন — {"name": "এজেন্ট F", "tickets_per_hour": 9, "resolution_rate": 89} — এবং Run চেপে দেখুন slope, intercept এবং gap-এর মান কীভাবে বদলায়।

    নতুন পয়েন্টটি (৯, ৮৯) BEFORE-এর বিদ্যমান রৈখিক প্যাটার্নের সাথে প্রায় সামঞ্জস্যপূর্ণ হওয়ায় slope ও intercept সামান্যই বদলাবে (স্লোপ সামান্য বাড়তে পারে, কারণ ৬টি পয়েন্ট এখন আরও শক্তিশালী রৈখিক সম্পর্ক নির্দেশ করে)। যেহেতু predicted_gamed সামান্য বদলালেও এখনও ১০০-এর কাছাকাছি বা তার বেশি থাকবে এবং actual_gamed অপরিবর্তিত (২৫) থাকবে, gap এখনও একটি বড়, স্পষ্ট নেগেটিভ সংখ্যাই থাকবে — মূল সিদ্ধান্তটি বদলায় না।

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

আগের পাঠ
স্পেসিফিকেশন গেমিং ও রিওয়ার্ড হ্যাকিং