數位倫理與社會(進階):把公平與隱私變成可計算的工程
從公平指標的不可能定理、代理變數洩漏,到 k-匿名攻擊與差分隱私的雜訊機制——當倫理變成有公式、有演算法的驗收規格
兩個都很「公平」的模型,為什麼吵不停?
想像你是一家銀行的資料科學家。你訓練了一個信用評分模型,並且做了該做的事:把「性別」「種族」這些敏感欄位全部從特徵中拿掉。某天,合規部門找上你,說你的模型對某個族群「不公平」。你不服氣,調出數據反駁:「我這個模型對每一個分數區間,男性和女性的實際違約率都一致——分數 700 的人不論性別,違約率都是 5%。這不就是公平嗎?」合規部門也調出數據:「可是你的模型把女性『誤判為高風險』的比率,是男性的兩倍。這怎麼會是公平?」
詭異的是,你們兩個都對。你滿足的是一種公平定義(校準, calibration),對方要求的是另一種公平定義(假陽性率均等, equalized false positive rate)。而數學會告訴你一件令人沮喪的事:當兩個群體的真實違約率本來就不同時,這兩種公平不可能同時滿足。這不是程式寫得爛,也不是資料不夠——這是一條數學定理。
入門篇談的是「我們該不該在乎偏誤、隱私、問責」。這篇進階文章要回答更硬的問題:當我們決定要在乎,工程上到底怎麼把「公平」與「隱私」變成可以計算、可以驗收的數字?而當這些數字彼此衝突時,誰來做取捨? 數位倫理在這個層次,不再是道德勸說,而是一門有公式、有演算法、有不可能定理的工程學科。

把「公平」拆成可計算的指標
要討論偏誤,得先把模糊的「公平」翻譯成數學。考慮一個二元分類器(核貸/拒貸、錄取/落選),對每個人輸出預測 $\hat{Y} \in \{0,1\}$,而真實標籤是 $Y$。再加上一個受保護群體屬性 $A$(例如 $A=a$ 與 $A=b$ 兩組)。常見的公平定義有三大家族:
(1) 群體均等 / 統計均等(demographic parity):各群體拿到正面結果的比率相同。
$$P(\hat{Y}=1 \mid A=a) = P(\hat{Y}=1 \mid A=b)$$
它的問題是完全不看真實表現——如果兩群體的真實合格率本來就不同,硬要比率相等反而可能要求你錄取較不合格的人。
(2) 機會均等 / 勝算均等(equalized odds):在「真正合格的人之中」與「真正不合格的人之中」,兩群體被正確/錯誤分類的比率都要相同。也就是要求真陽性率(TPR) 與假陽性率(FPR) 同時跨群體一致:
$$P(\hat{Y}=1 \mid Y=y, A=a) = P(\hat{Y}=1 \mid Y=y, A=b), \quad y \in \{0,1\}$$
(3) 校準(calibration):在「模型給出同樣分數 $s$ 的人之中」,兩群體的真實合格率一致。
$$P(Y=1 \mid \hat{S}=s, A=a) = P(Y=1 \mid \hat{S}=s, A=b)$$
這三者各自捕捉了不同的直覺,且互相不相容。入門篇引用過 Kleinberg 的不可能定理,這裡我們把它「算」出來,讓你親眼看到衝突發生的瞬間。
動手算一下:公平指標的不可能性
我們用一個極簡的合成資料集,模擬兩個群體真實違約率不同的情形,然後看校準與假陽性率均等能不能同時成立。
import numpy as np
np.random.seed(42)
def make_group(name, base_rate, n=10000):
"""產生一個群體:base_rate 是真實『違約』比率。
分數 score 與真實標籤相關,但帶雜訊(模擬不完美模型)。"""
y = np.random.rand(n) < base_rate # 真實是否違約
# 違約者分數偏高、未違約者分數偏低,但分布重疊
score = np.where(y, np.random.normal(0.65, 0.15, n),
np.random.normal(0.35, 0.15, n))
score = np.clip(score, 0, 1)
return {"name": name, "y": y, "score": score}
# 群體 A 真實違約率 20%,群體 B 真實違約率 40%(base rate 不同!)
A = make_group("A", 0.20)
B = make_group("B", 0.40)
THRESH = 0.5 # 同一個門檻,看似最「中立」
for g in (A, B):
pred = g["score"] >= THRESH
y = g["y"]
tp = np.sum(pred & y); fp = np.sum(pred & ~y)
fn = np.sum(~pred & y); tn = np.sum(~pred & ~y)
fpr = fp / (fp + tn) # 假陽性率:被冤枉的好人比率
fnr = fn / (fn + tp) # 假陰性率
print(f"群體 {g['name']}: FPR={fpr:.3f} FNR={fnr:.3f} "
f"放貸率={pred.mean():.3f}")
跑出來大致會是:
群體 A: FPR=0.084 FNR=0.110 放貸率=0.246
群體 B: FPR=0.150 FNR=0.075 放貸率=0.470
注意到了嗎?用完全相同的門檻 0.5,群體 B 的假陽性率(被誤判為高風險的好人比率)幾乎是 A 的兩倍。如果你為了讓 FPR 相等,把 B 群的門檻調高,那麼 B 群裡真正會違約的人就更容易溜過(FNR 變差),而且 B 群的整體放貸率會被壓低——這又違反了統計均等。你按下一個指標,另一個就翹起來。這就是公平的「打地鼠」困境,根源是 $p_A=0.20 \neq p_B=0.40$ 這個無法靠演算法消除的事實。
這帶出進階篇的第一個核心觀念:選哪一種公平,是一個價值決定,不是技術決定。法院的假釋系統可能最在乎「不要冤枉好人」(FPR 均等),醫療篩檢可能最在乎「不要漏掉病人」(FNR 均等),而工程師的工作不是假裝有一個「客觀公平」的按鈕,而是把取捨攤開來、講清楚、讓有權的人負責決定。
代理變數與「公平透過無知」的失敗
入門篇提過:移除敏感欄位無法消除偏誤,因為郵遞區號之類的代理變數(proxy variable) 仍洩漏群體資訊。進階地說,這種做法有個名字叫「公平透過無知(fairness through unawareness)」,而它幾乎總是失敗。我們可以量化「洩漏」有多嚴重:如果用其他特徵就能高準確度地反推出被你刪掉的敏感屬性,那這個屬性根本沒被刪掉,只是換了個馬甲。
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
import numpy as np
np.random.seed(0)
n = 5000
race = np.random.randint(0, 2, n) # 敏感屬性(已從模型移除)
zipcode = race * 3 + np.random.randint(0, 4, n) # 郵遞區號與 race 高度相關
income = 30 + race * (-5) + np.random.normal(0, 8, n)
X = np.column_stack([zipcode, income]) # 「不含 race」的特徵
# 試著用『非敏感』特徵反推敏感屬性
acc = cross_val_score(LogisticRegression(), X, race, cv=5).mean()
print(f"用非敏感特徵反推 race 的準確率:{acc:.2%}")
# → 例如 0.82:刪掉 race 根本沒用,模型仍能間接『看見』它
如果反推準確率遠高於 50%(隨機猜測),代表你的「中立」特徵其實是敏感屬性的編碼。真正的處理途徑有三條:前處理(重新加權或變換資料以削弱相關性)、過程中(in-processing)(在損失函數加上公平性正則項)、後處理(針對不同群體調整決策門檻)。每一條都有代價,沒有免費午餐——這正是研究所視角會深究的地方。
隱私的進階面:重新識別攻擊
入門篇說過「郵遞區號+生日+性別」可識別約 87% 的美國人,並提到「去識別化沒那麼安全」。進階篇要把這句話變成可操作的攻擊與防禦。核心觀念是 k-匿名(k-anonymity):一份資料集若滿足 k-匿名,代表任何一筆紀錄在「準識別欄位(quasi-identifiers)」上,至少和其他 $k-1$ 筆無法區分。$k=1$ 意味著有人是唯一的——也就是可被鎖定。
看一個例子:唯一性就是危險
下面這段程式檢查一份「已拿掉姓名」的就醫紀錄裡,有多少人其實是唯一可識別的。
import pandas as pd
# 已『去識別化』(拿掉姓名)的資料,但保留準識別欄位
df = pd.DataFrame({
"zip": [320, 320, 320, 408, 408, 320],
"birth": [1990, 1990, 1985, 1990, 1990, 1972],
"gender": ["F", "F", "M", "F", "F", "M"],
"disease":["流感", "流感", "糖尿病", "憂鬱症", "氣喘", "癌症"],
})
QI = ["zip", "birth", "gender"] # 準識別欄位組合
group_size = df.groupby(QI)[QI[0]].transform("size")
df["k"] = group_size # 每筆所在等價類的大小
print(df)
unique_rows = (df["k"] == 1).sum()
print(f"\nk=1(唯一可識別)的人數:{unique_rows} / {len(df)}")
輸出顯示,zip=320, birth=1972, gender=M 那位(癌症患者)是唯一的——只要攻擊者從別處(如選舉名冊)知道某位 1972 年生、住在 320 的男性,就能反推出他罹癌。這就是 Latanya Sweeney 在 1990 年代著名的「連結攻擊」:她用公開的麻州選民名冊,比對一份號稱匿名的州政府員工醫療資料,精準鎖定了當時州長的就醫紀錄。
但 k-匿名本身還不夠。即使一個等價類有 5 個人($k=5$),如果這 5 人得的全是同一種病,攻擊者一樣知道你的病情——這叫同質性攻擊。補強概念是 l-多樣性(l-diversity)(每個等價類的敏感值至少要有 $l$ 種)與 t-相近(t-closeness)(等價類內敏感值的分布要接近整體分布)。這條技術階梯說明:匿名化不是「刪掉名字」這麼一個動作,而是一整套需要量化驗收的工程。
隱私的數學保證:差分隱私的雜訊到底怎麼加
k-匿名是「語法式」隱私——它防的是你想得到的攻擊。但攻擊者總有你想不到的旁側資訊。差分隱私(differential privacy, DP) 提供的是「語意式」的數學保證:不管攻擊者掌握多少背景知識,「你這一筆資料在不在資料集裡」對輸出的影響都被嚴格上界限制住。入門篇給了定義式,這裡我們把雜訊實際加出來。
形式上,一個隨機機制 $\mathcal{M}$ 滿足 $\varepsilon$-差分隱私,若對任意只差一筆的相鄰資料集 $D, D'$ 與任意輸出集合 $S$:
$$P(\mathcal{M}(D) \in S) \le e^{\varepsilon} \cdot P(\mathcal{M}(D') \in S)$$
要對一個數值查詢(如「平均薪資」)達成這個保證,最經典的是 Laplace 機制:在真實答案上加一個 Laplace 雜訊,尺度由查詢的敏感度(sensitivity) $\Delta f$(一個人最多能改變答案多少)除以隱私預算 $\varepsilon$ 決定。
動手算一下:用 Laplace 機制保護一個計數查詢
import numpy as np
def laplace_mechanism(true_value, sensitivity, epsilon, rng):
"""對單一數值查詢加 Laplace 雜訊,滿足 ε-DP。"""
scale = sensitivity / epsilon # 噪音尺度 b = Δf / ε
noise = rng.laplace(0.0, scale)
return true_value + noise
rng = np.random.default_rng(7)
# 查詢:『資料集中有多少人罹患某疾病』。一個人進出最多改變計數 1 → 敏感度=1
true_count = 137
for eps in (2.0, 0.5, 0.1):
runs = [laplace_mechanism(true_count, 1.0, eps, rng) for _ in range(5)]
print(f"ε={eps}: " + " ".join(f"{x:7.2f}" for x in runs))
可能的輸出:
ε=2.0: 137.41 136.78 137.92 136.55 137.10
ε=0.5: 139.83 134.21 138.60 135.07 136.44
ε=0.1: 121.55 149.30 128.71 146.92 133.08
觀察重點:$\varepsilon$ 越小,雜訊越大,隱私越強,但答案越不準。$\varepsilon=2$ 時答案幾乎貼著 137;$\varepsilon=0.1$ 時可能偏到 121 或 149。這就是隱私與效用(utility)的根本權衡,而且它是可量化、可寫進合約的——這正是 DP 的革命性:你可以對使用者承諾「你的參與對任何分析結果的影響,數學上不超過 $e^{\varepsilon}$ 倍」。
DP 還有兩個關鍵性質讓它適合真實系統。其一是組合性(composition):如果你跑了多個查詢,各自的 $\varepsilon$ 會累加,總隱私損失 $= \sum \varepsilon_i$,所以系統要管理一個「隱私預算帳本」,花完就不能再查。其二是後處理免疫(post-processing immunity):對 DP 輸出再做任何不碰原始資料的運算,都不會洩漏更多隱私。Apple、Google、美國人口普查局都已在真實產品中部署 DP。
重點回顧
- 「公平」不是一個指標,而是一整族互相衝突的定義:統計均等、勝算均等(TPR/FPR 均等)、校準各自合理,但當群體真實基礎率不同時數學上無法同時滿足,工程師必須把取捨攤開讓有權者決定。
- 公平透過無知會失敗:刪掉敏感欄位無法消除偏誤,因為可用代理變數高準確度反推;要量化洩漏(反推準確率)並從前處理/過程中/後處理三條路徑著手。
- 去識別化是可被攻擊與驗收的工程:k-匿名防唯一性、l-多樣性防同質性攻擊、t-相近防分布洩漏;Sweeney 的連結攻擊證明「刪名字」遠遠不夠。
- 差分隱私提供語意式數學保證:Laplace 機制以 $\Delta f / \varepsilon$ 為尺度加雜訊,$\varepsilon$ 越小越私密但越不準;具備組合性(需管理隱私預算)與後處理免疫。
- 倫理在此層次是可計算、可驗收的:每個原則都對應公式與演算法,但公式不會替你做價值取捨——那永遠是人的責任。
深入探討(研究所視角)
進入研究階段,上述每個主題都展開為活躍的研究前沿,且彼此交織。
一、公平的因果觀。 前述指標都是「觀察式(observational)」的——只看 $\hat{Y}, Y, A$ 的聯合分布。但同一組分布可能對應完全不同的因果機制,導致觀察式公平誤判。反事實公平(counterfactual fairness) 主張:一個決策對個人 $x$ 是公平的,當且僅當「若此人的受保護屬性 $A$ 在因果上被改成另一個值(其餘外生變數不變),決策不變」。形式上要求 $P(\hat{Y}_{A \leftarrow a}(U)=y \mid X=x, A=a) = P(\hat{Y}_{A \leftarrow a'}(U)=y \mid X=x, A=a)$,其中 $U$ 是外生雜訊。這把問題從相關性提升到 Pearl 的結構因果模型(SCM)層次,需要明確畫出因果圖、區分「公平路徑」與「不公平路徑」(路徑特定公平, path-specific fairness)。挑戰在於因果圖通常無法從資料中唯一識別,必須引入領域假設——這讓公平的討論從統計學延伸到因果推論與科學哲學。
二、差分隱私的進階機制與深度學習。 $\varepsilon$-DP(純 DP)在多次組合下預算消耗很快。$(\varepsilon, \delta)$-近似 DP 容許極小機率 $\delta$ 的失敗,搭配 Gaussian 機制與更緊的組合分析(如 Rényi DP、矩會計 moments accountant)能大幅節省預算。在深度學習中,DP-SGD 是核心:每步先對每個樣本的梯度做 L2 範數裁剪(clipping) 把敏感度上界鎖死,再加 Gaussian 雜訊,最後用隱私會計器追蹤總損失。其代價是準確度下降與訓練變慢,且雜訊對少數群體的傷害往往更大——這揭露一個尖銳的隱私-公平張力:保護隱私的機制本身可能加劇不公平。如何同時滿足 DP 與群體公平,是當前未解的開放問題。
三、可審計性與治理的形式化。 當系統部署後,如何事後審計它有沒有違反公平或隱私承諾?這催生了「可審計演算法(auditable algorithms)」研究:用密碼學承諾(cryptographic commitment)讓開發者在不洩漏模型細節的前提下,向稽核者證明模型滿足特定性質;用零知識證明(zero-knowledge proof) 證明「我的模型在這個保留測試集上的群體 FPR 差距小於 $\tau$」而不洩漏模型參數或測試資料。這條路把數位倫理與密碼學、機制設計、法律科技(legal-tech)接在一起。歐盟《AI 法案》(AI Act)對「高風險 AI」要求的技術文件、日誌留存、上市後監控,本質上就是要求這類可審計性的制度化。
這三條線索共同指向一個研究生應內化的判斷:數位倫理的硬核不在於「有沒有倫理意識」,而在於能否把倫理要求轉譯成可定義、可實作、可驗證、且能在多元價值衝突中明確標示取捨的工程規格。當你能寫出公平性正則項、能調 DP 的 $\varepsilon$、能設計一個讓監管者驗證而不洩密的審計協定時,你才真正從「討論倫理」走到了「實作倫理」——而這正是這個領域最缺、也最需要的人才。