KI: PRAKTEK 12: Privacy-Preserving AI (Differential Privacy)

From OnnoWiki
Jump to navigation Jump to search

PRAKTEK 12: Privacy-Preserving AI (Differential Privacy) Tujuan Di akhir sesi ini, mahasiswa bisa: Memahami trade-off: privasi vs akurasi (utility). Menerapkan differential privacy (DP) pada analitik sederhana (mean, count, histogram). Mengukur utility loss (mis. MAE/RMSE) setelah diberi noise. Menghasilkan dataset/hasil analitik yang lebih aman untuk dibagikan. (Opsional) Mengamankan file hasil rilis memakai GnuPG (enkripsi & tanda tangan). Inti mindset: “Kalau datanya akan dibagi, asumsi terburuk: musuh punya informasi tambahan.” DP membantu mengurangi risiko “kebocoran lewat statistik”. Konsep Inti yang Perlu Dipahami 1. Apa itu Differential Privacy? Secara sederhana: sebuah mekanisme memenuhi differential privacy jika hasil rilis tidak berubah drastis walaupun 1 orang di dataset ditambahkan atau dihapus. Parameter utama: ε (epsilon) ε kecil ⇒ privasi lebih kuat ⇒ noise lebih besar ⇒ akurasi turun ε besar ⇒ privasi lebih lemah ⇒ noise kecil ⇒ akurasi naik 2. Kenapa “Noise Injection” masuk akal? Karena banyak kebocoran privasi terjadi bukan dari “membuka row data”, tapi dari: rilis mean / count / histogram dashboard statistik UMKM/kampus/kelas laporan “rata-rata nilai” + filter kecil (mis. per prodi, per angkatan) DP menambahkan noise terukur agar statistik tetap berguna, tapi sulit dipakai untuk menebak data individu. 3. Istilah penting (biar tidak bingung) Sensitivity: seberapa besar output statistik bisa berubah jika 1 data berubah. Laplace mechanism: noise dari distribusi Laplace (umum untuk count/mean). Clipping: membatasi nilai agar sensitivity terkendali (praktik wajib). Rule utama DP di praktikum ini: sebelum DP, lakukan clipping agar sensitivity jelas. Tools (Open Source) Ubuntu 24.04 Python 3 (venv, pip) Library: numpy, pandas (Opsional) matplotlib untuk visualisasi (Opsional) GnuPG untuk mengamankan file rilis Skenario Real yang Bisa Diimplementasi Bayangkan Anda punya dataset internal: age (umur) income (pendapatan) score (nilai) department (kategori) Anda ingin merilis: Rata-rata pendapatan per kelas/kelompok Jumlah orang per kategori Histogram nilai untuk laporan Tanpa DP, mean/count bisa jadi celah (mis. difference attack saat seseorang tahu semua orang kecuali 1). LANGKAH PRAKTIKUM (Ubuntu 24.04) Step 0 — Setup Environment

sudo apt update sudo apt install -y python3 python3-venv python3-pip gnupg mkdir -p praktek12_dp && cd praktek12_dp python3 -m venv .venv source .venv/bin/activate pip install -U pip pip install numpy pandas matplotlib


Step 1 — Siapkan Dataset Contoh (atau pakai data Anda) Kita buat dataset sintetis supaya aman untuk latihan, tapi strukturnya realistis. Buat file: generate_dataset.py import numpy as np import pandas as pd

def main():

   rng = np.random.default_rng(42)
   n = 2000
   dept = rng.choice(["TI", "SI", "DKV", "Bisnis"], size=n, p=[0.35, 0.30, 0.20, 0.15])
   age = rng.integers(18, 45, size=n)
   # income realistis (lognormal) dalam juta rupiah
   income = rng.lognormal(mean=2.2, sigma=0.45, size=n)  # ~ 5-20 jutaan
   income = income * 1_000_000
   # score 0-100
   score = np.clip(rng.normal(loc=78, scale=10, size=n), 0, 100)
   df = pd.DataFrame({
       "department": dept,
       "age": age,
       "income": income.round(0).astype(int),
       "score": score.round(1),
   })
   df.to_csv("raw_dataset.csv", index=False)
   print("OK: raw_dataset.csv dibuat. Jumlah row:", len(df))
   print(df.head(5))

if __name__ == "__main__":

   main()

Jalankan: python generate_dataset.py Step 2 — Implementasi Differential Privacy (Noise Injection + Clipping) Kita akan: clip income ke rentang wajar (mis. 0..50 juta) hitung mean/count/histogram tambahkan noise Laplace dengan parameter ε bandingkan utility (error) Buat file: dp_release.py import argparse import numpy as np import pandas as pd

def laplace_noise(scale: float, rng: np.random.Generator) -> float:

   # Laplace(0, b) => scale = b
   return rng.laplace(0.0, scale)

def dp_count(true_count: int, epsilon: float, rng: np.random.Generator) -> int:

   # sensitivity untuk count = 1
   scale = 1.0 / epsilon
   noisy = true_count + laplace_noise(scale, rng)
   return int(max(0, round(noisy)))

def dp_mean(values: np.ndarray, epsilon: float, clip_min: float, clip_max: float, rng: np.random.Generator) -> float:

   """
   DP mean via Laplace:
   - clip values => sensitivity mean = (clip_max - clip_min) / n
   - add Laplace noise to mean
   """
   clipped = np.clip(values, clip_min, clip_max)
   n = len(clipped)
   if n == 0:
       return float("nan")
   true_mean = float(np.mean(clipped))
   sensitivity = (clip_max - clip_min) / n
   scale = sensitivity / epsilon
   noisy_mean = true_mean + laplace_noise(scale, rng)
   return noisy_mean

def dp_histogram(values: np.ndarray, bins: np.ndarray, epsilon: float, rng: np.random.Generator) -> np.ndarray:

   """
   DP histogram:
   - hitung count per bin
   - tambahkan Laplace noise ke setiap bin
   Catatan: ini menghabiskan budget DP per bin (komposisi).
   Untuk praktikum, kita pakai epsilon_per_bin = epsilon / k
   """
   counts, _ = np.histogram(values, bins=bins)
   k = len(counts)
   epsilon_per_bin = epsilon / max(1, k)
   noisy = []
   for c in counts:
       noisy.append(dp_count(int(c), epsilon_per_bin, rng))
   return np.array(noisy, dtype=int)

def utility_metrics(true_arr: np.ndarray, noisy_arr: np.ndarray) -> dict:

   err = noisy_arr.astype(float) - true_arr.astype(float)
   mae = float(np.mean(np.abs(err)))
   rmse = float(np.sqrt(np.mean(err**2)))
   return {"MAE": mae, "RMSE": rmse}

def main():

   parser = argparse.ArgumentParser()
   parser.add_argument("--input", default="raw_dataset.csv")
   parser.add_argument("--epsilon", type=float, default=1.0)
   parser.add_argument("--seed", type=int, default=123)
   parser.add_argument("--clip_income_max", type=float, default=50_000_000.0)  # 50 juta
   args = parser.parse_args()
   rng = np.random.default_rng(args.seed)
   df = pd.read_csv(args.input)
   eps = args.epsilon
   if eps <= 0:
       raise ValueError("epsilon harus > 0")
   # ====== RELEASE 1: DP Count per department ======
   dept_counts = df["department"].value_counts().sort_index()
   true_counts = dept_counts.values
   noisy_counts = np.array([dp_count(int(c), eps, rng) for c in true_counts], dtype=int)
   # ====== RELEASE 2: DP Mean income (global) ======
   income = df["income"].to_numpy(dtype=float)
   dp_income_mean = dp_mean(income, eps, clip_min=0.0, clip_max=args.clip_income_max, rng=rng)
   true_income_mean = float(np.mean(np.clip(income, 0.0, args.clip_income_max)))
   # ====== RELEASE 3: DP Histogram score ======
   score = df["score"].to_numpy(dtype=float)
   bins = np.array([0,10,20,30,40,50,60,70,80,90,100], dtype=float)
   true_hist, _ = np.histogram(score, bins=bins)
   dp_hist = dp_histogram(score, bins=bins, epsilon=eps, rng=rng)
   # ====== Utility evaluation ======
   count_metrics = utility_metrics(true_counts, noisy_counts)
   hist_metrics = utility_metrics(true_hist, dp_hist)
   result = {
       "epsilon": eps,
       "release": {
           "dp_count_per_department": {
               "departments": dept_counts.index.tolist(),
               "true_counts": true_counts.tolist(),
               "noisy_counts": noisy_counts.tolist(),
               "utility": count_metrics,
           },
           "dp_mean_income": {
               "clip_max": args.clip_income_max,
               "true_mean": true_income_mean,
               "noisy_mean": dp_income_mean,
               "abs_error": abs(dp_income_mean - true_income_mean),
           },
           "dp_hist_score": {
               "bins": bins.tolist(),
               "true_hist": true_hist.tolist(),
               "noisy_hist": dp_hist.tolist(),
               "utility": hist_metrics,
           }
       }
   }
   # Simpan "dataset aman" versi rilis (bukan raw rows!)
   # Konsep penting: seringkali yang aman itu *released statistics*, bukan "anonymized rows"
   import json
   with open("dp_release.json", "w") as f:
       json.dump(result, f, indent=2)
   print("OK: dp_release.json dibuat.")
   print("Ringkas:")
   print("- epsilon:", eps)
   print("- mean income true vs noisy:", true_income_mean, "=>", dp_income_mean)
   print("- count utility (MAE/RMSE):", count_metrics)
   print("- hist utility (MAE/RMSE):", hist_metrics)

if __name__ == "__main__":

   main()

Jalankan beberapa eksperimen epsilon: python dp_release.py --epsilon 0.2 python dp_release.py --epsilon 1.0 python dp_release.py --epsilon 5.0 Yang harus kamu amati: ε=0.2 → privasi kuat, noise besar, error naik ε=5.0 → privasi lemah, noise kecil, error turun Step 3 — Bandingkan Utility Secara “Data Scientist” Biar fun, bikin tabel perbandingan. Jalankan: for e in 0.2 0.5 1 2 5; do

 echo "==== EPSILON $e ===="
 python dp_release.py --epsilon $e | tail -n 6

done Tantangan (menantang tapi doable): Tentukan epsilon “layak rilis” untuk laporan kampus/UMKM. Target utility misalnya: MAE count per kategori < 10 Error mean < 3% dari true mean Output Praktikum (Yang Dikumpulkan) Mahasiswa mengumpulkan: dp_release.json (hasil rilis DP) Laporan singkat (1–2 halaman) berisi: nilai ε yang dicoba perbandingan error (MAE/RMSE) + interpretasi rekomendasi ε final dan alasan catatan trade-off yang terjadi Catatan penting: yang Anda rilis adalah privacy-preserving analytics, bukan “dataset baris-per-baris”. BONUS: Amankan File Rilis dengan GnuPG (Enkripsi + Tanda Tangan) Ini realistis banget: Anda mau kirim dp_release.json ke dosen/tim. 1. Generate key (sekali saja)

gpg --full-generate-key Pilih: jenis: RSA and RSA size: 3072 atau 4096 isi nama & email Cek: gpg --list-keys 2. Enkripsi file untuk penerima (recommended) Misal penerima punya email dosen@example.ac.id (harus sudah ada public key-nya di keyring Anda). Import public key dulu jika perlu: gpg --import dosen_publickey.asc Enkripsi: gpg --encrypt --recipient dosen@example.ac.id dp_release.json

  1. hasil: dp_release.json.gpg

3. Decrypt (di sisi penerima)

gpg --decrypt dp_release.json.gpg > dp_release_decrypted.json 4. Tanda tangan file (integrity)

gpg --detach-sign dp_release.json

  1. hasil: dp_release.json.sig

Verifikasi: gpg --verify dp_release.json.sig dp_release.json Diskusi Kelas Pertanyaan pemantik: Kalau Anda rilis mean income per departemen, berapa minimal ukuran kelompok agar aman? Apa yang terjadi kalau filter dibuat terlalu spesifik (mis. “TI semester 1 kelas A”)? Apakah DP cukup? Kapan perlu tambahan: access control, audit log, dan encryption? Takeaway yang wajib dibawa pulang: Privasi itu desain sistem, bukan cuma “hapus kolom NIK”. DP adalah alat untuk membuat rilis statistik lebih aman dan terukur.


Pranala Menarik