Chapter Privasi Data dan Machine Learning menutup dengan satu pengamatan: kebocoran dan ketidakadilan adalah dua dimensi kerugian yang berbeda — yang pertama menyentuh data pribadi di balik model, yang kedua terlihat langsung pada keputusan yang model bagikan ke tiap kelompok. Sistem seleksi kredit, penyaringan lamaran kerja, dan prioritas layanan membagi kesempatan; ketika pembagian tersebut timpang secara sistematis antar kelompok demografis, frasa “model akurasinya tinggi” tidak lagi cukup sebagai jawaban. Chapter ini memetakan empat pintu masuk bias sebelum satu baris data pun dilatihkan, lalu mendefinisikan tiga metrik fairness — demographic parity, equal opportunity, dan equalized odds — yang seluruhnya bisa dihitung dari tabel kontingensi per kelompok. Dua praktikum dengan NumPy dan pandas membuktikannya pada data sintetis: disparitas muncul meski base rate kedua kelompok sengaja disamakan, dan menyetarakan satu metrik menyisakan metrik lain dalam keadaan timpang — pertukaran yang pada akhirnya merupakan keputusan tata kelola, bukan sekadar parameter teknis.

Dari Mana Bias Masuk: Empat Sumber #

Bias pada sistem machine learning hampir selalu bermula sebelum kode ditulis: di data, di alat yang mengukur, dan di keputusan merangkum populasi berbeda menjadi satu model. Memetakan sumbernya penting karena setiap sumber menuntut mitigasi yang berbeda — memperbaiki koleksi data tidak menyelesaikan masalah alat ukur, dan sebaliknya. Tabel berikut merangkum empat sumber yang paling sering dibahas dalam literatur fairness.

Sumber biasMekanismeContoh nyata
Historical biasData masa lalu merekam praktik yang timpang; model mempelajari ketimpangan itu sebagai polaRiwayat persetujuan kredit yang memang memihak satu wilayah
Representation biasSatu kelompok kurang terwakili sehingga model kurang teruji padanyaData latih didominasi pengguna perkotaan
Measurement biasAlat ukur kehilangan validitas bagi satu kelompok; sinyal sama tercatat beda kualitasTes yang kurang tervalidasi bagi satu kelompok pelamar
Aggregation biasSatu model dipaksa mewakili populasi yang proses dasarnya berbedaSatu model penetapan harga untuk seluruh provinsi

Empat sumber tersebut dijawab di lokasi berbeda. Historical bias dijawab dengan keputusan apakah sebuah pola layak dipelajari sama sekali — pertanyaan kebijakan, bukan pertanyaan algoritma. Representation bias dijawab di pengumpulan data. Measurement bias dijawab dengan memperbaiki atau membuang fitur yang alat ukurnya bermasalah. Aggregation bias dijawab dengan fitur interaksi atau model terpisah per segmen. Praktikum kita menanam measurement bias secara eksplisit — skenario yang paling sering luput karena fitur bermasalahnya tetap terlihat seperti fitur biasa.

Tiga Metrik dari Satu Tabel Kontingensi #

Seluruh metrik fairness berbasis label dihitung dari bahan yang sama: tabel kontingensi (confusion matrix) yang dihitung terpisah per kelompok. Dari TP, FP, TN, FN per kelompok, tiga besaran turunan menjadi fondasi: tingkat seleksi (TP+FP)/n — fraksi kelompok yang menerima keputusan positif; TPR TP/(TP+FN) — fraksi kandidat yang benar-benar memenuhi syarat dan lolos; dan FPR FP/(FP+TN) — fraksi kandidat yang tidak memenuhi syarat tetapi lolos. Tiga metrik fairness klasik hanyalah tiga cara mempersyaratkan kesetaraan besaran-besaran tersebut.

MetrikPertanyaan yang dijawabYang disyaratkan setara
Demographic parityApakah tingkat seleksi sama antar kelompok?Tingkat seleksi
Equal opportunityApakah peluang lolos bagi kandidat yang memenuhi syarat sama?TPR
Equalized oddsApakah pola galat kedua arah sama?TPR dan FPR sekaligus

Ketiganya menuntut hal berbeda saat base rate — proporsi label positif sungguhan — berbeda antar kelompok. Demographic parity paling tuntut: jika hanya 30% kelompok X benar-benar memenuhi syarat sementara 60% kelompok Y memenuhi syarat, paritas seleksi memaksa tingkat penerimaan sama, yang berarti komposisi galat kedua kelompok pasti berbeda. Equal opportunity lebih longgar: kesetaraan hanya dituntut di antara kandidat yang memenuhi syarat. Memilih metrik yang mana adalah keputusan kebijakan yang sebaiknya ditulis eksplisit — angka yang tidak kita pilih tetap perlu dilaporkan sebagai konteks.

Praktikum pertama membangun skenario penyaringan rekrutmen: 800 pelamar dari dua kelompok demografis anonim (A dan B) dengan kemampuan sejati berdistribusi sama. Bias ditanam hanya di jalur pengukuran: skor tes kelompok B tercatat lebih rendah secara sistematis dan lebih bising — meniru alat tes yang kehilangan validitas bagi satu kelompok. Split distratakan per kombinasi (kelompok, label) agar base rate kedua kelompok di data uji benar-benar setara, sehingga disparitas apa pun yang muncul tidak bisa disalahkan ke perbedaan komposisi data.

python
# Menghitung tiga metrik fairness secara manual pada DataFrame sintetis.
# Skenario: skrining rekrutmen dengan dua kelompok demografis (anonim:
# A dan B). Bias sengaja ditanam lewat measurement bias: skor_tes kelompok
# B tercatat lebih rendah secara sistematis dan lebih bising - meniru alat
# tes yang kurang valid bagi satu kelompok. Kemampuan sejati dan label
# kinerja nyata berdistribusi sama, sehingga disparitas yang muncul murni
# berasal dari sisi pengukuran dan model.

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

# ---------- Data sintetis: 800 pelamar ----------
rng = np.random.default_rng(42)
n = 800

kemampuan = rng.normal(0, 1, size=n)          # sama utk kedua kelompok
kelompok = np.array(["A"] * (n // 2) + ["B"] * (n // 2))

# Pengalaman kerja: sinyal kemampuan yang tidak bias.
pengalaman = 5 + 1.5 * kemampuan + rng.normal(0, 1.0, size=n)

# Skor tes: sinyal kemampuan dengan bias pengukuran bagi kelompok B -
# rata-rata digeser turun 0.7 dan deraunya dua kali lebih besar.
derau_tes = np.where(kelompok == "A", 0.8, 1.6)
geser_tes = np.where(kelompok == "A", 0.0, -0.7)
skor_tes = kemampuan + rng.normal(0, 1, size=n) * derau_tes + geser_tes

# Label: kinerja nyata satu tahun kemudian (tanpa bias kelompok).
berkinerja_baik = (
    kemampuan + rng.normal(0, 0.8, size=n) > 0
).astype(int)

data = pd.DataFrame({
    "kelompok": kelompok,
    "pengalaman_tahun": pengalaman,
    "skor_tes": skor_tes,
    "berkinerja_baik": berkinerja_baik,
})

# Split distratakan per (kelompok, label) agar base rate kedua kelompok
# di data uji benar-benar setara - disparitas tidak bisa disalahkan ke
# perbedaan komposisi data.
fitur = ["pengalaman_tahun", "skor_tes"]
strata = data["kelompok"] + "_" + data["berkinerja_baik"].astype(str)
X_latih, X_uji, y_latih, y_uji = train_test_split(
    data[fitur], data["berkinerja_baik"], test_size=0.5,
    stratify=strata, random_state=42
)
kelompok_uji = data.loc[X_uji.index, "kelompok"]

model = LogisticRegression(max_iter=1000).fit(X_latih, y_latih)
prediksi = pd.Series(model.predict(X_uji), index=X_uji.index)

print("== Data dan model ==")
print(f"baris total {len(data)} | baris uji {len(X_uji)} "
      f"(per kelompok {int((kelompok_uji == 'A').sum())})")
print(f"akurasi model pada uji: {(prediksi == y_uji).mean():.3f}")
print(f"base rate A: {y_uji[kelompok_uji == 'A'].mean():.3f} | "
      f"base rate B: {y_uji[kelompok_uji == 'B'].mean():.3f}")
print()

# ---------- Tabel kontingensi per kelompok (manual) ----------
def confusion(k):
    """TP, FP, TN, FN untuk satu kelompok pada data uji."""
    p = prediksi[kelompok_uji == k]
    t = y_uji[kelompok_uji == k]
    return {
        "TP": int(((p == 1) & (t == 1)).sum()),
        "FP": int(((p == 1) & (t == 0)).sum()),
        "TN": int(((p == 0) & (t == 0)).sum()),
        "FN": int(((p == 0) & (t == 1)).sum()),
    }


stat = {k: confusion(k) for k in ("A", "B")}

def rasio(k, pembilang, penyebut):
    return stat[k][pembilang] / (stat[k][pembilang] + stat[k][penyebut])


print("== Tabel kontingensi per kelompok (data uji) ==")
print("kelompok |  TP   FP   TN   FN | akurasi | TPR    FPR    sel.rate")
for k in ("A", "B"):
    s = stat[k]
    akurasi = (s["TP"] + s["TN"]) / sum(s.values())
    tpr = rasio(k, "TP", "FN")
    fpr = rasio(k, "FP", "TN")
    sel = (s["TP"] + s["FP"]) / sum(s.values())
    print(f"{k:8s} | {s['TP']:3d} {s['FP']:4d} {s['TN']:4d} {s['FN']:4d} | "
          f"{akurasi:6.3f} | {tpr:.3f}  {fpr:.3f}  {sel:.3f}")
print()

# ---------- Tiga metrik fairness ----------
sel_A = (stat["A"]["TP"] + stat["A"]["FP"]) / sum(stat["A"].values())
sel_B = (stat["B"]["TP"] + stat["B"]["FP"]) / sum(stat["B"].values())
tpr_A, tpr_B = rasio("A", "TP", "FN"), rasio("B", "TP", "FN")
fpr_A, fpr_B = rasio("A", "FP", "TN"), rasio("B", "FP", "TN")

dp_diff = sel_A - sel_B
eo_diff = tpr_A - tpr_B
eod_diff = max(abs(tpr_A - tpr_B), abs(fpr_A - fpr_B))

print("== Tiga metrik fairness (dihitung manual) ==")
print(f"demographic parity difference  : {dp_diff:+.3f} "
      f"(sel.rate A {sel_A:.3f} vs B {sel_B:.3f})")
print(f"equal opportunity difference   : {eo_diff:+.3f} "
      f"(TPR A {tpr_A:.3f} vs B {tpr_B:.3f})")
print(f"equalized odds difference      : {eod_diff:.3f} "
      f"(maks |dTPR|={abs(tpr_A - tpr_B):.3f}, |dFPR|={abs(fpr_A - fpr_B):.3f})")
print()
print("pembacaan: base rate kedua kelompok sama, tetapi skor_tes kelompok")
print("B tercatat lebih rendah - model menerima lebih sedikit kandidat B")
print("saat yang sama kehilangan lebih banyak kandidat B yang berkinerja")
print("baik. Disparitas ini murni karya jalur pengukuran, bukan perbedaan")
print("mutu kedua kelompok")

Output yang Dihasilkan:

text
== Data dan model ==
baris total 800 | baris uji 400 (per kelompok 200)
akurasi model pada uji: 0.730
base rate A: 0.510 | base rate B: 0.480

== Tabel kontingensi per kelompok (data uji) ==
kelompok |  TP   FP   TN   FN | akurasi | TPR    FPR    sel.rate
A        |  75   29   69   27 |  0.720 | 0.735  0.296  0.520
B        |  65   21   83   31 |  0.740 | 0.677  0.202  0.430

== Tiga metrik fairness (dihitung manual) ==
demographic parity difference  : +0.090 (sel.rate A 0.520 vs B 0.430)
equal opportunity difference   : +0.058 (TPR A 0.735 vs B 0.677)
equalized odds difference      : 0.094 (maks |dTPR|=0.058, |dFPR|=0.094)

pembacaan: base rate kedua kelompok sama, tetapi skor_tes kelompok
B tercatat lebih rendah - model menerima lebih sedikit kandidat B
saat yang sama kehilangan lebih banyak kandidat B yang berkinerja
baik. Disparitas ini murni karya jalur pengukuran, bukan perbedaan
mutu kedua kelompok

Mari kita telaah bagian-bagian pentingnya:

  1. Baris 15–19 (fondasi setara): rng berseed 42 membangkitkan kemampuan dan kelompok — kedua kelompok berbagi distribusi kemampuan yang identik, sehingga hasil apa pun tidak bisa dijelaskan oleh perbedaan mutu antar kelompok.
  2. Baris 26–28 (bias pengukuran): derau_tes menggandakan derau skor tes kelompok B dan geser_tes menurunkan rata-ratanya 0,7 — hanya jalur pengukuran yang timpang; pengalaman_tahun (baris 22) dan label berkinerja_baik (baris 31–33) tetap bersih.
  3. Baris 42–51 (split distratakan): strata menggabungkan kelompok dan label, lalu train_test_split(stratify=strata, random_state=42) menjaga base rate data uji kedua kelompok hampir sama — 0,510 berbanding 0,480 — sehingga komposisi data keluar dari daftar penjelasan disparitas.
  4. Baris 65–80 (tabel kontingensi manual): fungsi confusion(k) memfilter prediksi dan label untuk satu kelompok lalu menghitung TP/FP/TN/FN dengan operasi masker pandas; fungsi rasio(k, ...) mengubah angka mentah itu menjadi TPR atau FPR. Seluruh metrik fairness chapter ini bersumber dari empat angka per kelompok ini.
  5. Baris 96–103 (tiga metrik): dp_diff, eo_diff, dan eod_diff hanyalah selisih dan nilai maksimum absolut dari besaran turunan tersebut — tanpa pustaka fairness apa pun, karena intinya memang aritmetika tabel.

Cara membaca hasilnya: akurasi per kelompok nyaris sama — 0,720 berbanding 0,740 — dan justru di situlah jebakannya. Kesetaraan akurasi menyembunyikan ketimpangan komposisi galat: kelompok A lolos 52,0% pelamar sementara kelompok B hanya 43,0% (demographic parity difference +0,090), dan dari kandidat yang benar-benar berkinerja baik, 73,5% kelompok A terdeteksi berbanding 67,7% kelompok B (equal opportunity difference +0,058). Selisih FPR (0,296 berbanding 0,202) membuat equalized odds difference menetap di 0,094. Model yang tampak netral menurut akurasi ternyata membagi kesempatan dan galat secara timpang hanya karena satu fitur kehilangan validitas pengukurannya.

Pertukaran Fairness vs Performa #

Setelah disparitas terukur, pertanyaannya berubah menjadi: apa yang bisa dilakukan terhadap model yang sudah terlatih? Mitigasi termurah bekerja di sisi keputusan: model tetap mengeluarkan peluang, tetapi ambang yang mengubah peluang menjadi keputusan “diterima” diatur terpisah per kelompok. Pendekatan ini termasuk post-processing — tidak menyentuh data latih maupun koefisien, sehingga bisa diterapkan pada model yang sudah berjalan.

Praktikum kedua memakai data dan model yang identik dengan praktikum pertama, lalu membandingkan empat strategi ambang: ambang seragam 0,50 sebagai baseline, ambang yang menyetarakan tingkat seleksi (demographic parity), ambang yang menyetarakan TPR (equal opportunity), dan ambang yang menyetarakan FPR. Pencarian ambang menyapu grid 0,05 sampai 0,95 dengan langkah 0,01 — deterministik dan murah.

python
# Pertukaran fairness vs performa lewat ambang keputusan per kelompok.
# Data dan model sama dengan fairness-metrik.py. Model tetap satu (tidak
# dilatih ulang); yang berubah hanyalah ambang peluang yang memutuskan
# "diterima". Empat strategi dibandingkan: ambang seragam, ambang yang
# menyamakan tingkat seleksi (demographic parity), ambang yang menyamakan
# TPR (equal opportunity), dan ambang yang menyamakan FPR. Grid pencarian
# ambang berjalan di 0.05 sampai 0.95 langkah 0.01 - deterministik.

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

# ---------- Data sintetis yang sama dengan fairness-metrik.py ----------
rng = np.random.default_rng(42)
n = 800

kemampuan = rng.normal(0, 1, size=n)
kelompok = np.array(["A"] * (n // 2) + ["B"] * (n // 2))
pengalaman = 5 + 1.5 * kemampuan + rng.normal(0, 1.0, size=n)
derau_tes = np.where(kelompok == "A", 0.8, 1.6)
geser_tes = np.where(kelompok == "A", 0.0, -0.7)
skor_tes = kemampuan + rng.normal(0, 1, size=n) * derau_tes + geser_tes
berkinerja_baik = (
    kemampuan + rng.normal(0, 0.8, size=n) > 0
).astype(int)

data = pd.DataFrame({
    "kelompok": kelompok,
    "pengalaman_tahun": pengalaman,
    "skor_tes": skor_tes,
    "berkinerja_baik": berkinerja_baik,
})

fitur = ["pengalaman_tahun", "skor_tes"]
strata = data["kelompok"] + "_" + data["berkinerja_baik"].astype(str)
X_latih, X_uji, y_latih, y_uji = train_test_split(
    data[fitur], data["berkinerja_baik"], test_size=0.5,
    stratify=strata, random_state=42
)
kelompok_uji = data.loc[X_uji.index, "kelompok"]

model = LogisticRegression(max_iter=1000).fit(X_latih, y_latih)
peluang = pd.Series(
    model.predict_proba(X_uji)[:, 1], index=X_uji.index
)

GRID = np.arange(0.05, 0.96, 0.01)


def metrik_ambang(k, t):
    """(seleksi, TPR, FPR, akurasi) kelompok k pada ambang t."""
    m = kelompok_uji == k
    p = peluang[m] >= t
    t_ = y_uji[m]
    sel = p.mean()
    tpr = p[t_ == 1].mean()
    fpr = p[t_ == 0].mean()
    akurasi = (p == t_).mean()
    return sel, tpr, fpr, akurasi


def evaluasi(nama, tA, tB):
    """Cetak satu baris perbandingan untuk sepasang ambang."""
    selA, tprA, fprA, akA = metrik_ambang("A", tA)
    selB, tprB, fprB, akB = metrik_ambang("B", tB)
    akurasi_total = (
        (kelompok_uji == "A").sum() * akA + (kelompok_uji == "B").sum() * akB
    ) / len(kelompok_uji)
    print(f"{nama:28s} {tA:.2f}  {tB:.2f}  {akurasi_total:6.3f}   "
          f"{selA - selB:+.3f}   {tprA - tprB:+.3f}   {fprA - fprB:+.3f}")
    return selA, tprA, fprA, selB, tprB, fprB, akurasi_total


print("== Empat strategi ambang keputusan (model tidak dilatih ulang) ==")
print("strategi                     ambA ambB akurasi dSeleksi dTPR  dFPR")
baseline = evaluasi("seragam 0.50", 0.50, 0.50)
selA0, tprA0, fprA0, selB0, tprB0, fprB0, akurasi0 = baseline

# ---------- Strategi 2: samakan tingkat seleksi (demographic parity) ----------
target_sel = (selA0 + selB0) / 2
tA_dp = GRID[np.argmin([abs(metrik_ambang("A", t)[0] - target_sel)
                        for t in GRID])]
tB_dp = GRID[np.argmin([abs(metrik_ambang("B", t)[0] - target_sel)
                        for t in GRID])]
dp = evaluasi("seleksi setara (DP)", float(tA_dp), float(tB_dp))

# ---------- Strategi 3: samakan TPR (equal opportunity) ----------
target_tpr = tprA0
tB_eo = GRID[np.argmin([abs(metrik_ambang("B", t)[1] - target_tpr)
                        for t in GRID])]
eo = evaluasi("TPR setara (EOpp)", 0.50, float(tB_eo))

# ---------- Strategi 4: samakan FPR ----------
target_fpr = fprA0
tB_fpr = GRID[np.argmin([abs(metrik_ambang("B", t)[2] - target_fpr)
                         for t in GRID])]
fpr = evaluasi("FPR setara", 0.50, float(tB_fpr))

print()
print("pembacaan: satu model, empat pasang ambang, empat pola disparitas")
print("yang berbeda. Menyetarakan seleksi menyetarakan pula sebagian")
print("ketimpangan lain, tetapi menyamakan TPR menyisakan FPR timpang")
print("dengan arah berlawanan - dan sebaliknya. Tidak ada pasangan ambang")
print("yang membuat ketiga selisih nol sekaligus karena kurva skor kedua")
print("kelompok memang berbeda bentuk (derau pengukuran berbeda);")
print("memilih definisi fairness berarti memilih selisih mana yang")
print("diperkenankan tersisa")

Output yang Dihasilkan:

text
== Empat strategi ambang keputusan (model tidak dilatih ulang) ==
strategi                     ambA ambB akurasi dSeleksi dTPR  dFPR
seragam 0.50                 0.50  0.50   0.730   +0.090   +0.058   +0.094
seleksi setara (DP)          0.54  0.44   0.750   -0.010   -0.024   -0.026
TPR setara (EOpp)            0.50  0.44   0.735   +0.040   -0.004   +0.056
FPR setara                   0.50  0.38   0.730   -0.010   -0.046   -0.002

pembacaan: satu model, empat pasang ambang, empat pola disparitas
yang berbeda. Menyetarakan seleksi menyetarakan pula sebagian
ketimpangan lain, tetapi menyamakan TPR menyisakan FPR timpang
dengan arah berlawanan - dan sebaliknya. Tidak ada pasangan ambang
yang membuat ketiga selisih nol sekaligus karena kurva skor kedua
kelompok memang berbeda bentuk (derau pengukuran berbeda);
memilih definisi fairness berarti memilih selisih mana yang
diperkenankan tersisa

Bedah baris-per-baris untuk praktikum kedua:

  1. Baris 44–46 (peluang): predict_proba(X_uji)[:, 1] mengambil peluang kelas positif untuk seluruh baris uji — bahan baku seluruh strategi; prediksi keras tidak dipakai karena ambangnya akan diatur ulang.
  2. Baris 51–60 (metrik_ambang): satu fungsi mengevaluasi satu kelompok pada satu ambang: masker peluang[m] >= t menjadi keputusan biner, lalu tingkat seleksi, TPR, FPR, dan akurasi dihitung dari keputusan tersebut — tabel kontingensi praktikum pertama dalam wujud fungsi.
  3. Baris 63–72 (evaluasi): fungsi pencetak menggabungkan kedua kelompok dengan pembobotan jumlah baris sehingga kolom akurasi adalah akurasi total seluruh data uji pada pasangan ambang tersebut.
  4. Baris 81–86 (strategi paritas seleksi): target_sel adalah titik tengah tingkat seleksi baseline; np.argmin mencari ambang grid yang membawa tiap kelompok sedekat mungkin ke target itu — kelompok A naik ke 0,54, kelompok B turun ke 0,44.
  5. Baris 89–98 (dua strategi lain): pola yang sama diulang untuk TPR dan FPR; strategi 3 dan 4 hanya menggeser ambang kelompok B karena kelompok A tetap berpatok pada 0,50 sebagai pembanding.

Cara membaca hasilnya memuat dua pelajaran. Pertama, fairness dan performa tidak selalu berlawanan: strategi paritas seleksi justru menaikkan akurasi dari 0,730 ke 0,750 — ambang seragam ternyata kurang optimal bagi kedua kelompok sekaligus. Kedua, tidak ada pasangan ambang yang meniadakan ketiga selisih sekaligus: menyetarakan seleksi menyisakan dTPR −0,024 dan dFPR −0,026; menyetarakan TPR menyisakan dFPR +0,056; menyetarakan FPR menyisakan dTPR −0,046. Akar masalahnya geometris — kurva skor kedua kelompok berbeda bentuk karena derau pengukurannya berbeda, dan dalam literatur fairness hasil semacam ini dikenal sebagai hasil ketidakmungkinan: ketika base rate dan bentuk distribusi skor berbeda, beberapa definisi fairness tidak bisa dipenuhi bersamaan. Memilih definisi berarti memilih selisih mana yang diperkenankan tersisa.

Mitigasi di Sepanjang Pipeline #

Ambang per kelompok hanyalah satu titik intervensi. Kerangka lengkapnya membagi mitigasi menurut posisinya terhadap pelatihan.

Tahap mitigasiContoh teknikSumber bias yang dijawab
Pre-processingReweighting, resampling, membuang atau memperbaiki fitur pengukuran bermasalahRepresentation, measurement
In-processingKendala atau regularisasi fairness saat pelatihan (konseptual di sini)Historical, aggregation
Post-processingAmbang keputusan per kelompok pada skor keluaranSemua sumber, sebatas sisi keputusan

Praktikum kedua adalah contoh post-processing: cepat, tidak melatih ulang model, tetapi tidak memperbaiki sumber masalahnya — skor tes kelompok B tetap tercatat lebih bising. Perbaikan yang lebih dalam bekerja di hulu: mengumpulkan ulang data dengan alat ukur yang setara, atau menghapus fitur yang validitasnya timpang. Pilihan tahap bergantung pada kendali yang kita miliki atas data dan biaya yang sanggup dibayar.

Angka-angka fairness hasilnya bukan barang sekali hitung: angka tersebut perlu dicatat per versi model, dilaporkan pada dokumentasi, dan dipantau setelah model berjalan — persis pekerjaan tata kelola. Chapter penutup section ini, Praktik Machine Learning yang Bertanggung Jawab, merangkai dokumentasi model, jejak audit, monitoring, respons insiden, dan reviu manusia menjadi satu checklist yang bisa dijalankan pada proyek sungguhan.

Referensi Resmi #