ARK · 理解深度學習CHAPTER 21 / 21

CHAPTER 21 / 21 · PART 5 · 決策、原理與責任

深度學習與倫理:偏誤、風險與責任

Deep learning and ethics

兌現第 1 章的三問:偏誤怎麼量、解釋能給到哪、責任在流程的哪一段指認得出來。

§01學習重點

§02課程內容

一、把倫理翻譯成工程語言:目標與代理

第 1 章的最後留了三個問題:「這份訓練資料是替誰說話的?」「決定做出來以後,誰能檢查?」「出事的時候,誰承擔、誰得利?」並且說第 21 章會完整處理。現在就是那個時候。但「處理」不等於給答案——這三個問題沒有通用答案。本章要給的是拿到問題之後具體能做什麼:偏誤可以量,量法有數學;解釋可以給,但要先知道它能給到哪裡;責任可以指認,前提是你知道要往流程的哪一段看。這一章沒有新的網路架構,可是它的工具跟前二十章一樣是技術的:條件機率、混淆矩陣、最小平方法。

先立一個貫穿全章的分析格式。接下來每遇到一個「系統造成傷害」的情境,我們都問同樣三欄:誰做了決定、誰承擔後果、哪一個環節有辦法介入。這個格式的用意是把討論從「這樣對不對」搬到「這件事的結構長什麼樣」——前者每個人答案不同,後者可以攤開來檢查。

第一個要拆的是一句常見的辯護:「系統只是照設定執行。」這句話字面為真,而它恰恰是問題所在。要讓一個系統的目標跟人的價值一致,這個工程問題叫價值對齊(value alignment),它難在兩個地方,兩個都跟「照設定執行」相容。

第一個難處:設定本身就是替身。第 5 章走過一遍損失函數的推導:先假設輸出服從哪種分布,最大概似把式子推出來。那條推導保證的是「式子與分布假設一致」,從頭到尾沒有保證「分布假設與你真正想要的東西一致」。你真正想要的是「安置方案公道」「城市變得宜居」,這些東西列不完整、也寫不成可微分的式子;能寫進訓練腳本的是量得到的替身——評分誤差、通過率、預測準確度。這種「拿它當目標來優化、但它其實只是目標的影子」的量,叫代理指標(proxy metric)。而優化器對代理指標是誠實的:它會找到所有把數字壓低的路徑,包括那些完全沒有滿足本意的路徑。這件事有個常被點名的名字,古德哈特法則(Goodhart's law)——用自己的話界定它:一個量一旦被拿來當目標或分配依據,它跟原本想衡量的東西之間的關係,就會被優化的壓力本身破壞掉

第二個難處:就算目標寫對了,學出來的行為也未必是目標。訓練是有限資料上的有限步下坡,第 6 章與第 8 章都看過它留下的縫——收斂到哪裡、泛化到哪裡,都不是目標函數單方面說了算。所以「目標函數沒問題」跟「部署出去的行為沒問題」是兩個命題,中間隔著整個訓練過程。

還有一層在這兩個難處更上游:該以誰的價值為準。「公道」在不同文化、不同處境的人眼裡不是同一件事,而寫進系統的那一版必然是某一群人的版本。這不是工程能收掉的問題,但工程可以做到一件事:把「這套系統偏向誰的價值」變成一個可以被檢查的問題,而不是藏在參數裡的既成事實。本章後面的量法,都是在做這件事。

比喻: 設想一個都市計畫案:官方目標是「讓這一帶變得更好」,但這句話進不了任何評估模型,實際寫進去的是量得到的代理——捷運沿線的地價漲幅、預估運量、開發面積。幾年後指標全數達標,同時原本住在這裡的人被漲起來的租金擠了出去;系統沒有出錯,每一個數字都照設定優化了,只是「更好」的那個「好」從頭到尾沒有被寫進去,也就從頭到尾沒有被優化過。這個比喻有一處失準:都市計畫的代理指標——地價、運量——至少是公開數字,誰都查得到、吵得起來;損失函數藏在訓練腳本裡,被它影響的人多半不知道它存在,連「指標選錯了」這場爭論都無從開始。

把這一節收攏成本章的地圖:目標是替身(本節)→ 替身吃的資料有來歷(第二節)→ 替身的公道可以量、但量法之間要做選擇(第三節)→ 做出來的決定能被看見多少(第四節)→ 出了事往流程哪一段找人(第五節)→ 以及寫這些系統的人自己站在哪裡(第六節)。

二、偏誤的來源地圖:它從哪些門進來

「偏誤」最常見的誤解是把它想成一種髒污:資料不乾淨,洗一洗就好。這個想像會讓你只守一道門,而它其實有五道門,分布在流程的不同位置。以下逐一走過,每一道門配一個種在同一個世界裡的例子。

第一道門:資料的生成過程。 資料不是自然物,它是誰、在什麼制度下、為了什麼目的留下的紀錄。設想規劃單位拿「歷年公聽會意見書」當民意資料訓練一個排序模型,決定哪些陳情優先處理。意見書只會來自有時間出席、知道管道、寫得動陳情書的人;上夜班的、行動不便的、不知道有公聽會的人,在這批資料裡是空白。模型忠實地學會這批資料,也就忠實地學會了把安靜的人當成不存在。三欄攤開:決策的是選了這批資料的人,承擔的是沒被記錄的人,可介入的環節在資料選定之前——問一句「這批紀錄的生成機制漏掉誰」。

第二道門:結構性偏斜。 這一道最違反直覺:就算取樣完整、標註無誤,資料照樣可以帶著偏誤,因為它如實記錄了一個本來就不平等的世界。設想用「歷年成交行情」評估各區的補償基準:某幾個區的行情低,是幾十年來公共投資繞過它的結果;模型把這個低價學起來、再用它算出更低的補償,等於把歷史的不平等蓋上一層數學的章。這裡沒有任何一筆資料是錯的——錯的是把「過去如此」當成「應該如此」的那一步,而那一步是定題的人做的,不是資料做的。

第三道門:定題本身。 「要優化什麼」是流程裡最早、也最少被檢查的價值判斷。同一個徵收案,題目定成「最大化開發效益」和定成「最小化被迫遷離的戶數」,後面所有的資料、模型、門檻都會不一樣。定題的人往往不覺得自己在做價值判斷,因為題目看起來像業務需求;但兩個題目服務的是兩群不同的人,這正是三欄裡「誰得利」那一欄。

第四道門:度量的選擇。 決定「什麼算好」的那把尺自己就帶立場。下一節會看到,光是「公平」就有三把同樣直觀、彼此衝突的尺——選哪一把不是技術判斷。

第五道門:部署後的回饋迴圈。 第 1 章用第一問開過這扇門:模型的輸出會改變下一輪訓練資料的分布。這裡把它的一般形式寫出來:只要「這一輪的決定」影響「下一輪誰被記錄、被記錄成什麼樣」,系統就會拿自己的輸出當證據,越走越確信。設想開發評估案量按「哪一區既有紀錄多」來分配:被評估過的區產生更多紀錄,下一輪分到更多評估,其他區的檔案越來越薄——薄不是因為沒事發生,是因為沒人去記。這個迴圈會不會把初始的微小差距放大成鴻溝,取決於分配規則的形狀,作業三會請你親手量出那個分水嶺;這裡先記住結論:介入點在分配規則,不在模型準確度——模型越準,只是讓這個迴圈轉得越順。

五道門排開之後,兩個常見說法可以直接檢驗。「資料洗乾淨就好」——洗資料只守第一道門,對定題、度量與回饋迴圈毫無作用。「資料量夠大會自動中和偏誤」——大量複製同一種偏斜,只會把偏斜估計得更穩定;樣本數解決的是變異數問題,偏誤不是變異數問題。

比喻: 把徵收案的公聽會紀錄當民意,就像用「誰的聲音被錄進了會議室的麥克風」代表「這一帶的人怎麼想」。麥克風沒有壞,逐字稿也沒有錯——錯位發生在麥克風架設的位置:它只架在會議室裡,而受影響最深的人可能正在上工,走不開。這個比喻有一處失準:公聽會至少有出席簽到表,事後可以清點誰沒來;訓練資料通常沒有「缺席名單」——沒被記錄的人不會留下任何痕跡,連「少了誰」這個問題都要靠別的資料才問得出來。

三、公平寫成數學之後:量得出來,但選不掉價值

現在進入本章最能動手的一段。把場景定死:捷運線徵收後的安置資格審查,一個二元決定。記號先備齊:真實情況寫 \(y\)(\(y=1\) 代表這一戶真的需要安置),模型的判定寫 \(\hat y\)(\(\hat y=1\) 代表判給資格),受保護屬性寫 \(s\)——例如這一戶屬於哪個社群。有些文獻把受保護屬性寫成 \(a\),本課的 \(a[\cdot]\) 從第 3 章起就是激活函數,所以改用 \(s\),讀別的材料時對得上就好。

「對兩個群體一樣公平」聽起來是一句話,寫成數學至少是三句不同的話。

第一把尺,通過率一致,通稱人口統計平價(demographic parity):

$$ Pr(\hat y = 1 \mid s = 0) \;=\; Pr(\hat y = 1 \mid s = 1) $$

逐項拆解:\(Pr(\hat y = 1 \mid s = 0)\) 讀作「在屬於群體 0 的條件下,被判給資格的機率」,也就是群體 0 的通過率;右邊是群體 1 的通過率。這把尺只看決定的分布,完全不看決定對不對。

第二把尺,校準一致(calibration by group)。校準(calibration)這個詞第 5 章末尾出現過:模型宣稱的機率要跟實際頻率對得上。用在這裡是問「判給資格的人裡,真的需要的比例」在兩群是否相同:

$$ Pr(y = 1 \mid \hat y = 1, s = 0) \;=\; Pr(y = 1 \mid \hat y = 1, s = 1) $$

第三把尺,錯誤率結構一致(equalized odds,可譯等化錯誤率):兩群的偽陽率相同、偽陰率也相同——

$$ Pr(\hat y = 1 \mid y = 0, s = 0) \;=\; Pr(\hat y = 1 \mid y = 0, s = 1) $$

(偽陰率那條同型,把條件換成 \(y=1\)、事件換成 \(\hat y = 0\)。)

停一下,看第二把尺和第三把尺的條件機率——方向是反的。校準是「給定判定,看真實」;錯誤率是「給定真實,看判定」。這兩個方向的數字可以差很遠,初學者最常在這裡把它們混成同一件事。一句口訣:校準是被判的人想問的(「判我需要的這個判定,有多可信?」),錯誤率是被漏掉的人想問的(「像我這樣真的需要的人,被漏掉的比例多高?」)。兩個問題都正當,它們就是不同的問題。

三把尺都直觀,那能不能全要?用數字說話。底下造兩個里:甲里是老社區,真正需要安置的比例六成;乙里是新社區,比例兩成半。同一套評分模型、同一道門檻,評分規則對兩個里一視同仁:

PYTHON
import numpy as np

rng = np.random.default_rng(0)

# 假設情境:捷運線徵收後的安置資格審查。
# 甲里是老社區,真正需要安置的住戶比例高;乙里是新社區,比例低。
n = 2000
need_rate = {"甲里": 0.60, "乙里": 0.25}

for li, rate in need_rate.items():
    y = (rng.random(n) < rate).astype(int)          # 1 = 真的需要安置
    score = 0.35 + 0.30 * y + rng.normal(0, 0.15, n)  # 同一套評分模型
    yhat = (score >= 0.60).astype(int)              # 同一道門檻

    tp = int(np.sum((yhat == 1) & (y == 1)))
    fp = int(np.sum((yhat == 1) & (y == 0)))
    fn = int(np.sum((yhat == 0) & (y == 1)))
    tn = int(np.sum((yhat == 0) & (y == 0)))

    pass_rate = (tp + fp) / n                       # 通過率
    precision = tp / (tp + fp)                      # 判「需要」中真需要的比例
    fpr = fp / (fp + tn)                            # 偽陽率
    tpr = tp / (tp + fn)                            # 真陽率
    print(f"{li}  TP={tp} FP={fp} FN={fn} TN={tn}")
    print(f"    通過率={pass_rate:.3f}  判準確度={precision:.3f}"
          f"  偽陽率={fpr:.3f}  真陽率={tpr:.3f}")

實跑輸出:甲里 TP=749 FP=39 FN=460 TN=752,通過率 0.394、判準確度 0.951、偽陽率 0.049、真陽率 0.620;乙里 TP=327 FP=72 FN=182 TN=1419,通過率 0.200、判準確度 0.820、偽陽率 0.048、真陽率 0.642。先看好消息:偽陽率 0.049 對 0.048、真陽率 0.620 對 0.642——第三把尺幾乎完美,這是當然的,因為兩個里用的是同一套評分、同一道門檻,錯誤結構天生一樣。再看另外兩把尺:通過率 0.394 對 0.200,差了將近一倍;判準確度 0.951 對 0.820。沒有任何一行代碼對哪個里不同對待,兩把尺照樣裂開——裂縫的來源不在模型,在基率:需要安置的底層比例一個六成、一個兩成半。

這不是這組數字湊巧。判準確度、錯誤率、基率之間有一條恆等式綁著:

$$ Pr(y = 1 \mid \hat y = 1) \;=\; \frac{\pi\, t}{\pi\, t + (1 - \pi)\, f} $$

逐項拆解:\(\pi\) 是該群體的基率,即 \(Pr(y=1)\);\(t\) 是真陽率 \(Pr(\hat y = 1 \mid y = 1)\);\(f\) 是偽陽率 \(Pr(\hat y = 1 \mid y = 0)\)。分子是「真的需要而且被判到」的機率,分母是「被判到」的總機率,相除就是判準確度。盯著這條式子看:若兩群的 \(t\) 與 \(f\) 都相等(第三把尺成立)而 \(\pi\) 不等,判準確度就必然不等(第二把尺必倒)——除非兩群的偽陽率或真陽率恰好為零,而真實系統沒有這種事。所以「基率不同的兩群,三把尺不可兼得」不是經驗觀察,是代數事實。

那把門檻分開調,硬要通過率追平呢?可以,代價馬上到:

PYTHON
import numpy as np

rng = np.random.default_rng(0)

# 與上一段同一批資料(同 seed、同順序)
n = 2000
data = {}
for li, rate in {"甲里": 0.60, "乙里": 0.25}.items():
    y = (rng.random(n) < rate).astype(int)
    score = 0.35 + 0.30 * y + rng.normal(0, 0.15, n)
    data[li] = (y, score)

# 甲里維持原門檻 0.60,算出它的通過率當作目標
y_a, s_a = data["甲里"]
target = float(np.mean(s_a >= 0.60))

# 把乙里的門檻往下調,直到通過率追平甲里
y_b, s_b = data["乙里"]
th_b = float(np.quantile(s_b, 1.0 - target))
yhat_b = (s_b >= th_b).astype(int)

fp = int(np.sum((yhat_b == 1) & (y_b == 0)))
tn = int(np.sum((yhat_b == 0) & (y_b == 0)))
tp = int(np.sum((yhat_b == 1) & (y_b == 1)))
print(f"乙里新門檻={th_b:.3f}(原 0.600)")
print(f"乙里通過率={np.mean(yhat_b):.3f}(追平甲里的 {target:.3f})")
print(f"乙里偽陽率={fp / (fp + tn):.3f}(原 0.048,甲里 0.049)")
print(f"乙里判準確度={tp / (tp + fp):.3f}(原 0.820,甲里 0.951)")

實跑輸出:乙里新門檻 0.473,通過率 0.394 追平了,但偽陽率從 0.048 跳到 0.225,判準確度從 0.820 掉到 0.575。第一把尺立起來的瞬間,第三把尺倒了。你可以繼續換方案——每個方案都是立一把、倒一把。數學把選項攤開,但不會替你選:選通過率一致,是在說「決定的分布不該看群體」;選錯誤率一致,是在說「犯錯的方式不該看群體」;選校準一致,是在說「判定的可信度不該看群體」。三句話各自保護不同的人,這個選擇是價值決定,做這個決定的人應該被指認得出來——這正是三欄分析的第一欄。

比喻: 徵收補償的算法之爭是同一個結構:按公告現值算,每一戶適用同一條公式,程序上無可挑剔;按實際居住年資與安置需求算,弱勢住戶才拿得到活得下去的補償。兩種算法各自直觀,兩群人各有一種算法對自己有利,而預算固定時兩種算法算出的分配不可能同時成立。這個比喻有一處失準:補償方案可以折衷——各按一半、另設專案補助;公平準則的互斥是定理層級的,基率不同時那幾條等式在數學上就是不能同時成立,這不是協商桌上可以各讓一步的事。

還剩一個常見的迴避方案要拆:「把敏感欄位刪掉,模型看不到,總公平了吧。」看不到欄位不等於拿不到資訊——只要其他欄位跟它相關,資訊就還在,這叫冗餘編碼(redundant encoding)。用最小平方法就能示範:

PYTHON
import numpy as np

rng = np.random.default_rng(0)

# 假設情境:安置審查表刪掉了「原居社區」欄位(s=1 代表老社區)
n = 3000
s = (rng.random(n) < 0.5).astype(int)

# 但其餘欄位跟 s 相關——老社區的房子老、巷子窄、自有比例高
age = 12 + 28 * s + rng.normal(0, 6, n)              # 屋齡(年)
lane = 8.0 - 3.5 * s + rng.normal(0, 1.5, n)          # 門前巷道寬(公尺)
own = (rng.random(n) < 0.40 + 0.30 * s).astype(float)  # 是否自有
extra = rng.normal(0, 1, n)                           # 一個與 s 無關的欄位

# 用最小平方法,從「不含 s」的欄位把 s 預測回來
X = np.column_stack([age, lane, own, extra, np.ones(n)])
w, *_ = np.linalg.lstsq(X, s, rcond=None)
s_hat = (X @ w >= 0.5).astype(int)
print(f"表單上已無敏感欄位,重建準確率={np.mean(s_hat == s):.3f}")

# 對照組:只用那個真正無關的欄位
X0 = np.column_stack([extra, np.ones(n)])
w0, *_ = np.linalg.lstsq(X0, s, rcond=None)
s0 = (X0 @ w0 >= 0.5).astype(int)
print(f"只用無關欄位,重建準確率={np.mean(s0 == s):.3f}")

實跑輸出:重建準確率 0.995,對照組 0.509。欄位刪了,資訊一分沒少——屋齡、巷道寬、自有比例合起來,幾乎就是那個被刪掉的欄位。任何吃這些特徵的模型,等於還是看得到 \(s\),只是換了讀法。深度網路在這件事上比最小平方法更擅長,不是更收斂。這一段同時解釋了作業二要你做的事:刪到哪裡才算刪乾淨,以及刪乾淨之後還剩什麼。

量法的地圖補完最後三塊。交叉性(intersectionality):兩個屬性各自看都達標,交疊的那一小群可以獨自很糟——老社區裡的高齡租屋者,可能在「社區」與「年齡」兩張報表上都被平均掉了;量的時候要下鑽到交叉組合,而交叉組合的樣本數會急速變小,估計的變異數隨之變大,這是量測上的真實兩難。不可觀測的屬性:想量「對某個群體公不公平」,得先知道誰屬於那個群體;查不到(或不該查)成員身分時,那種不公平就量不出來——量法保護得了看得見的群體,看不見的群體連進報表的資格都沒有。介入的時點:把上面所有手段沿流程排開,事前改資料(重新取樣、補蒐集)、事中改訓練目標(把公平準則寫進約束)、事後改決策門檻(像第二段代碼那樣);一般而言介入得越早,動到的東西越根本、成本也越高。附帶一句:把公平寫成約束通常要付整體準確度的代價,這筆帳該不該付、由誰付,又是一個三欄問題。

四、可解釋性:「知道」這個詞得拆開來用

第二問「誰能檢查」聽起來像一個問題,動手時會裂成三個。「這個系統透明嗎?」——先問你要的是哪一層:知道原理(用的是哪一族模型、什麼目標訓練的)、拿得到實作(權重檔、訓練資料、前處理腳本在不在你手上)、重建得出某一次決策(當時的輸入、版本、門檻設定能不能還原)。三層是遞進的,而且每一層都可能單獨缺席:原理公開但權重不給,是第一層有第二層無;全部檔案都給你,那幾億個參數你也讀不出「它整體依據什麼在判」,是第二層有、第三層的「懂」仍然沒有。所以「透明(transparency)」和「可解釋(interpretability)」是兩件事:前者是拿得到多少材料,後者是拿到之後能形成多少理解。順帶一提,解釋、理解、可詮釋這幾個詞在文獻裡至今沒有公認的定義切分,讀到時留意各家用法。

三層都到位仍不夠的原因,第 1 章其實已經斷言過:單筆決定可以解釋,全域規則說不出來。本章要給的是這個斷言背後的原理——單筆解釋是怎麼做出來的,以及為什麼它拼不回全域。

做法的核心是一個換模型的動作:複雜模型在單一決策點附近的行為,用一個簡單模型去近似。具體工序:在這筆輸入的鄰域裡取樣一批擾動點、拿複雜模型對它們評分、按離原點的距離加權(越近權重越大)、再對加權樣本擬合一個線性模型。線性模型的係數就是「在這一點附近,每個特徵各推了多少分」——這叫局部替代模型(local surrogate model)。用假設的補償評分函數示範:

PYTHON
import numpy as np

rng = np.random.default_rng(0)


def comp(m, d):
    """假設的補償評分:m=坪數,d=與捷運站的距離(公尺)。
    非線性:坪數報酬遞減,距離有一個甜蜜點(太近吵、太遠不便)。"""
    return 0.9 * m - 0.004 * m ** 2 + 25.0 * np.exp(-((d - 300.0) / 250.0) ** 2)


def local_fit(m0, d0, n=500, sigma=(8.0, 60.0)):
    """在 (m0, d0) 附近取樣,加距離權重,擬合一個局部線性模型。"""
    m = m0 + rng.normal(0, sigma[0], n)
    d = d0 + rng.normal(0, sigma[1], n)
    y = comp(m, d)
    dist2 = ((m - m0) / sigma[0]) ** 2 + ((d - d0) / sigma[1]) ** 2
    w = np.exp(-dist2 / 2.0)                     # 離得越近,權重越重
    X = np.column_stack([m, d, np.ones(n)])
    A = X * w[:, None]
    coef, *_ = np.linalg.lstsq(A.T @ X, A.T @ y, rcond=None)
    return coef  # [坪數係數, 距離係數, 截距]


for tag, m0, d0 in [("A 戶", 60.0, 150.0), ("B 戶", 140.0, 700.0)]:
    c = local_fit(m0, d0)
    print(f"{tag}({m0:.0f} 坪、距站 {d0:.0f} 公尺)  評分={comp(m0, d0):.1f}")
    print(f"    局部解釋:每多 1 坪 {c[0]:+.3f} 分,每遠 1 公尺 {c[1]:+.4f} 分")

實跑輸出:A 戶(60 坪、距站 150 公尺)評分 57.0,局部解釋是每多 1 坪 +0.419 分、每遠 1 公尺 +0.0792 分;B 戶(140 坪、距站 700 公尺)評分 49.5,局部解釋是每多 1 坪 -0.222 分、每遠 1 公尺 -0.0267 分。兩份解釋各自都對——在各自的鄰域裡,線性近似確實貼著那個非線性函數走。但把它們放在一起讀:坪數在 A 戶加分、在 B 戶扣分;距離在 A 戶是越遠越好、在 B 戶相反。每一點各有一套說法,而且說法彼此矛盾——因為底下的函數本來就是彎的,每個局部的切線方向本來就不同。你可以對每一戶都給出誠實的單筆解釋,同時永遠給不出一條「本系統依據什麼在判」的全域規則。這就是第 1 章那個斷言的機制版本。

知道了解釋能給到哪裡,就能看清它給不到的部分落在誰頭上。被評分的那一戶拿到的即使是誠實的局部解釋,也只回答「當時為什麼這樣判」,不回答兩個更要緊的問題:「我要改變什麼,結果才會不同」——局部係數只在鄰域內有效,照著它走出鄰域,方向可能整個反過來(B 戶照 A 戶的解釋去搬家,分數不升反降);以及「這個判法整體上對我這樣的人公不公平」——那是上一節的全域問題,單筆解釋原理上答不了。有些司法轄區把「對自動化決定請求說明」寫進了資料保護法規,但法規要的「說明」和技術給得出的「局部近似」之間有多大落差、誰來填,仍是進行中的爭論——這裡只需要記住結構:解釋權寫得再清楚,技術供給跟不上,權利就是空的

比喻: 徵收範圍公告那天,住戶收到的是一紙結論公文:你這一戶,補償若干、限期搬遷。依據呢?技術報告書幾百頁,放在市政大樓某一層供「調閱」;評分模型跑在顧問公司的電腦裡,參數不隨公文送達。住戶要申訴,程序上得先指出「哪裡算錯了」——可是他連「依據什麼算的」都拿不到,申訴權形同要求他反駁一份看不到的計算。這就是資訊不對稱(information asymmetry):做決定的一方與承受決定的一方,拿到的材料不在同一個量級。這個比喻有一處失準:行政體系再不透明,公文有文號、檔案有保存年限、調閱有法定程序,磨得夠久總能挖出一些;演算法決策可以在毫秒內完成且不落任何可稽核的紀錄,連「調閱」這個動作的對象都可以不存在——這個落差不是比喻的瑕疵,它就是下一節的主題。

五、問責:在委託鏈上指認責任

第三問「誰承擔、誰得利」在第 1 章是三個斷言:能力集中在少數組織、受害者不在決策桌旁、傷害有時間差。本章不重複斷言,改給分析工具:把一個系統的部署攤開成一連串委託關係,沿著鏈條問三欄。

鏈條長這樣:出資方把目標交給規劃單位(「提升整體效益」);規劃單位把目標翻成需求書交給開發方(「預測各區開發潛力,誤差越小越好」)——注意第一節講的價值翻譯損耗就發生在這一步;開發方把需求書變成模型交回去;操作方拿模型輸出做成千上萬個日常決定;決定落在被影響的人身上。每一段交接都可能發生利益錯位——委託的人要的和受託的人優化的不是同一件事——而且每往下一段,離「當初為什麼要做這件事」就遠一步。

這個結構會系統性地生產一種現象:多手問題(problem of many hands)——環節多到人人有份時,實際上無人負責。出了事,操作方說「我照模型的建議做」;開發方說「我照需求書的規格交付」;規劃單位說「技術細節是專業判斷」;出資方說「我只定了大方向」。每一句都對,每一手都真的只做了一小段,而傷害是整條鏈合起來造成的。責任不是被誰推掉的——是這個結構讓它沒有落點

指認的工具是問痕跡:哪一段決策留下了可稽核的紀錄?出資決議有會議紀錄,需求書有版本,公文有文號;但「門檻為什麼從 0.6 調到 0.55」「訓練資料換過哪一版」「哪些替代方案被試過又放棄」——這些最關鍵的決定,往往落在沒有留痕義務的縫隙裡。一條實用的判準:沿著鏈條找「有裁量權、卻不留紀錄」的那幾段,問責的修復工程就從那裡做起——不是先問「誰的錯」,是先讓每一段裁量都留得下痕跡、指認得出人。這跟第 8 章的測試紀律是同一種精神:紀律不是懷疑誰作弊,是讓「事後想查」這件事在事前就被設計成可能。

同一套三欄+委託鏈的工具,可以拿去分析各種常被並列的「AI 風險」,而不必背一份危害清單。示範幾個,每個都只問結構:

六、技術人員的位置——兼全課收尾

最後一塊是本章獨有的:寫這些系統的人,自己站在哪裡。最常見的自我定位是「我做的是數學,數學是中性的」。這句話的破洞可以用技術語言指出來,不需要任何道德詞彙。

破洞叫歸納風險(inductive risk):接受或駁回一個模型的預測,永遠可能犯兩種錯,而兩種錯的代價落在不同的人身上。把它寫出來:

$$ C \;=\; c_{\mathrm{FP}}\, Pr(\hat y = 1,\, y = 0) \;+\; c_{\mathrm{FN}}\, Pr(\hat y = 0,\, y = 1) $$

逐項拆解:\(Pr(\hat y = 1, y = 0)\) 是偽陽性發生的機率(判給資格但其實不需要),\(Pr(\hat y = 0, y = 1)\) 是偽陰性發生的機率(真的需要卻被漏掉);\(c_{\mathrm{FP}}\) 與 \(c_{\mathrm{FN}}\) 是兩種錯各自的代價;\(C\) 是期望代價。模型只能給你機率那兩項;兩個 \(c\) 不在資料裡,也永遠不會在資料裡——偽陽性浪費的是預算,偽陰性毀掉的是一戶人的安置,這兩者怎麼折算,是有人替別人做的價值判斷。而第三節裡那道「門檻切在 0.6」的決定,恰恰就是對 \(c_{\mathrm{FP}} / c_{\mathrm{FN}}\) 的一次表態——寫下門檻的那個人做了這個判斷,不管他有沒有意識到。這就是價值中立迷思的技術反駁:不是「數學帶有立場」,是「用數學做決定的每一個參數選擇都在分配代價」,選題、蒐證、假說取捨、發布門檻,一路都是。

價值判斷不會因為交給系統就消失,只會變得看不見——而看不見有它自己的動力學。自動化偏誤(automation bias):輸出是數字,就被當成客觀;被當成客觀,就免於質疑。第 5 章末尾其實給過解藥的一半:模型輸出的機率形式上合法,不代表統計上可信。另一半是制度性的:質疑一個數字需要時間、專業和職權,流程設計若沒有預留這三樣,「人類監督」就只是在系統輸出上蓋章。蓋章蓋久了,下一層退化跟著來:判斷力是用進廢退的,把價值判斷外包給系統的組織,慢慢失去做這種判斷的人。

再往上一層是結構問題。開發團隊的組成若同質——相似的背景、相似的處境、相似的盲點——預設視角就會固化成慣例,滲進資料選擇、特徵設計、產品預設值,而團隊裡沒有人覺得有異狀,因為異狀需要對照才看得見。對照的來源只能是受技術影響的人真的參與設計,而不是產品做完後被「徵詢意見」;被徵詢跟坐在決策桌旁,是第二節公聽會例子的差別在組織內的重演。同時要提防反方向的天真:不是所有問題都有技術解。一個因為公共投資長期繞路而衰敗的街區,需要的是投資回來,不是一個更準的衰敗預測模型——把社會結構問題餵給最先進的模型,得到的是被更精確執行的原問題。

那麼,知道了這些的個人,實際有哪些選項?把它當成一個集體行動問題(collective action problem)來看會誠實得多:所有人都守住規範時結果最好,但單一個人守規範的成本是自己的、收益是大家的,誘因天生不足——這跟「大家都是壞人」無關,跟結構有關。結構問題的出路是改變誘因與成本,而個人在其中真實握有的槓桿至少有三支:替誰工作(你的技能是稀缺資源,供給給誰是一個每次跳槽都在做的選擇)、怎麼宣稱(對能力與限制的誠實描述,是你完全控制的變項——不誇大、不用擬人化措辭包裝統計模型)、做什麼題目(研究與開發的議程本身就是資源分配)。這三支槓桿都不英雄主義,也都真實存在。結論不替你下——本章的格式從頭到尾是三欄,這裡只把第三欄攤開:這些環節,你介入得了。

課程到這裡收尾。第 1 章說這門課其實在回答四個問題:怎麼把函數族裡的成員挑得又快又準(第 2 到 9 章)、不同形狀的資料配什麼結構(第 10 到 13 章)、沒有答案時能學到什麼(第 14 到 18 章)、訊號又晚又稀疏時怎麼學(第 19 章);第 20 章追問了這一切為什麼有效。然後是第 1 章開出的那張支票——三個關於「誰」的問題,第 21 章會完整處理。現在可以清點兌現了什麼:第一問「資料替誰說話」,你有了偏誤的來源地圖和三把可以實算的尺;第二問「誰能檢查」,你有了透明的三層拆法和局部解釋的原理與邊界;第三問「誰承擔、誰得利」,你有了三欄分析和委託鏈上找斷點的方法。都不是答案,都是工具——這門課教不了你價值判斷該怎麼下,它能做到的是讓你下判斷的時候,看得見自己正在下判斷。

最後回到本章的比喻世界收一次尾。都市計畫再怎麼失衡,至少有法定程序、有主管機關、決策以年為單位、留下公文紀錄——不滿的人知道去哪裡吵。演算法系統常常四者皆無:決策以毫秒計、不留痕跡、找不到對應的主管機關、而且安靜地嵌在每一次載入的頁面裡。這個落差就是為什麼這一章放在課程的最後、也放在你面前:你剛學完的這二十章技術,決定了你會成為委託鏈上的哪一手。 下一次看到「模型很準」,記得那只是開頭——它錯的時候,帳算在誰頭上?現在,你有工具把這一題拆開來答了。

§03原書對照

原書第 21 章是全書的收尾章,也是唯一一章不以技術機制為主題的章節,由原作者與一位研究人工智慧倫理的學者合寫(p.421)。開章先交代這個領域的成形:對倫理後果的系統性關注比深度學習的當代熱潮晚了數年才出現,原書用會議投稿量的變化與政策文件的暴增等具體數據描繪這條曲線,點名兩件 2016 年的標誌性事件,並自陳目的在鋪開議題、開啟對話,而非提供道德指導(p.421)。

前半部的骨架是價值對齊。原書 pp.421–422 說明「讓系統的目標與人類的價值一致」為何困難,引入損失函數作為代理目標的落差、以及目標寫對了訓練仍可能學歪的第二種病灶,附有一個下棋代理的例子;p.422 進一步把整個問題改寫成委託人與代理人之間的結構問題,p.423 的結構圖把「目標錯定」與「資訊不對稱」兩條病根收進同一個框架。隨後關於偏誤與公平、人工道德代理、透明性、可解釋性的四個小節,全部掛在這副骨架下(pp.422–427)。

偏誤小節(pp.422–425)值得單獨一提:原書逐項檢視偏誤混入模型生命週期各環節的方式,每一項都附上可追的文獻,並收錄兩個廣被引用的真實案例——臉部影像超解析度模型的種族偏誤、臉部分析系統在膚色與性別交叉組合上的表現落差;p.424 的圖把偏誤緩解方法沿訓練管線從資料蒐集一路排到後處理,並配有線上互動筆記本;交叉性與受保護特徵不可觀測的難題也在這幾頁(pp.424–425)。

透明與可解釋的部分(pp.426–427)把「知道系統怎麼運作」拆成不同粒度,以商用語言模型為對照案例;p.426 提到歐盟資料保護法規中「取得決定之解釋」的規範,註腳同時指出該條文是否真的賦予這項權利仍有爭論;pp.426–427 用四格小圖逐步拆解局部替代模型解釋法的原理,想知道「打不開模型時還能解釋什麼」的人,這兩頁最值得細讀。

誤用與副作用的部分(pp.427–431)逐節盤點臉部辨識、軍事化與政治操作、詐騙、資料隱私、智慧財產、自動化偏誤與道德退化、環境成本、就業衝擊、權力集中,幾乎每節都有具體事件與量化估計,參考文獻密度是全章之最;p.429 的去匿名化案例與 p.430 的訓練排碳估計,是課堂討論常用的素材。智慧財產一節把「生成內容能否取得著作權、模仿特定藝術家風格是否正當」列為懸而未決的開放問題(pp.429–430)。

pp.431–432 的個案研究把前述議題收攏到同一個爭議研究上,從資料、建模、用例、解釋、隱私、科學傳播六個層面逐一解剖。pp.432–433 轉向科學哲學,論證價值中立的理想為何在機器學習裡站不住,以及歸納風險為何讓「接受一個預測」本身成為價值判斷,是全章論證密度最高的兩頁。pp.433–435 以集體行動問題收尾,給出負責任的科學溝通與多元參與兩個方向,最後以四點主張作結。原書在 p.435 為想再往外讀的人點名了兩個跨領域研討會與一部線上哲學百科。p.436 列出十三道討論題,多數附有指定的延伸閱讀,適合當研討題庫。原書第 21 章對應印刷頁 pp.421–436。

§04作業和解答

作業一:三把尺,一張表算到底

設想一個徵收安置審查的假設數據:甲里 100 戶,其中 60 戶真的需要安置,模型判了 45 戶真需要者給資格(另 15 戶被漏)、也誤判了 6 戶不需要者給資格;乙里 100 戶,其中 20 戶真的需要,模型判對 15 戶(漏 5 戶)、誤判 12 戶。請回答:(a)逐里算出通過率、真陽率、偽陽率、判準確度(判給資格者中真需要的比例);(b)三把公平尺——通過率一致、校準一致、錯誤率結構一致——哪幾把成立、哪幾把不成立;(c)若把乙里的門檻放寬讓通過率追平甲里,哪些數字必然變動?往哪個方向?說明理由(不必給精確值)。

解答 SOLUTION

(a)甲里:判給資格共 45 + 6 = 51 戶,通過率 51/100 = 0.51;真陽率 45/60 = 0.75;偽陽率 6/40 = 0.15;判準確度 45/51 ≈ 0.882。乙里:判給資格共 15 + 12 = 27 戶,通過率 27/100 = 0.27;真陽率 15/20 = 0.75;偽陽率 12/80 = 0.15;判準確度 15/27 ≈ 0.556。(以上除法已用程式重算核對。)

(b)錯誤率結構一致成立:兩里的真陽率同為 0.75、偽陽率同為 0.15。通過率一致不成立(0.51 對 0.27),校準一致不成立(0.882 對 0.556)。這正是課文那條恆等式的整數版:\(t\) 與 \(f\) 都相等,但基率 \(\pi\) 一個 0.6、一個 0.2,代入 \(\pi t / (\pi t + (1-\pi)f)\):甲里 \(0.45/(0.45+0.06) \approx 0.882\),乙里 \(0.15/(0.15+0.12) \approx 0.556\)——判準確度的差完全由基率驅動,模型的錯誤結構無可挑剔。

(c)放寬門檻等於把更多分數較低的戶判進資格。乙里的通過率上升到 0.51 的同時:偽陽率必然上升——新增的通過者大多來自不需要的 80 戶(因為需要的 20 戶已被抓到 15 戶,剩餘空間很小);判準確度必然下降——分母膨脹主要靠偽陽性灌入;真陽率上升(漏掉的 5 戶有機會被撈回),於是原本成立的「錯誤率結構一致」反而被打破。立起第一把尺的動作本身,就是放倒第三把尺的動作——與課文第二段代碼的實測方向完全一致。

作業二:冗餘編碼——刪到哪裡才算刪乾淨

沿用課文冗餘編碼那段代碼的資料生成方式(同 seed、同順序),依次縮減特徵集重跑重建:全部四欄、刪掉屋齡、再刪掉巷道寬、只剩無關欄位。(a)寫出程式並報告四個重建準確率;(b)解釋準確率的下降模式:為什麼每刪一欄只掉一階、而不是一刀歸零?(c)由此推論:如果任務本身真的需要屋齡這一欄(例如評估建物安全),「刪欄位」這條路的兩難是什麼?

解答 SOLUTION

(a)程式如下,實跑輸出見註解:

PYTHON
import numpy as np

rng = np.random.default_rng(0)
n = 3000
s = (rng.random(n) < 0.5).astype(int)
age = 12 + 28 * s + rng.normal(0, 6, n)
lane = 8.0 - 3.5 * s + rng.normal(0, 1.5, n)
own = (rng.random(n) < 0.40 + 0.30 * s).astype(float)
extra = rng.normal(0, 1, n)


def acc(cols):
    X = np.column_stack(cols + [np.ones(n)])
    w, *_ = np.linalg.lstsq(X, s, rcond=None)
    return np.mean((X @ w >= 0.5).astype(int) == s)


print(f"全部欄位(無 s):{acc([age, lane, own, extra]):.3f}")   # 0.995
print(f"再刪屋齡:{acc([lane, own, extra]):.3f}")               # 0.885
print(f"再刪巷道寬:{acc([own, extra]):.3f}")                   # 0.656
print(f"只剩無關欄位:{acc([extra]):.3f}")                      # 0.509

四個準確率依序是 0.9950.8850.6560.509

(b)因為 \(s\) 的資訊分散在多個相關欄位裡,不是集中在哪一欄。刪掉屋齡,巷道寬單獨仍是不錯的預測子(兩群平均差 3.5 公尺、標準差 1.5,分得開);再刪巷道寬,自有比例還殘留一點訊號(0.70 對 0.40 的比例差);全刪光只剩無關欄位,才落回擲銅板的 0.509。冗餘編碼的「冗餘」就是這個意思——同一份資訊被編了很多份,逐欄刪除是在逐份銷毀副本,而不是銷毀資訊本身。

(c)兩難在於:跟 \(s\) 相關的欄位往往同時是任務真正需要的欄位。屋齡既洩漏社區身分、又是建物安全評估的必要輸入——刪掉它,模型對任務變盲;留著它,敏感資訊就從這條通道流回模型。這說明「刪欄位」不是公平問題的解,充其量是姿態;真正的取捨要回到第三節的量法上談:留著欄位,然後模型輸出在群體間的差異、在訓練目標或門檻上介入——把問題從「看不看得到」搬回「造成什麼分配」。

作業三:回饋迴圈什麼時候放大差距

課文第二節說回饋迴圈會不會把微小差距放大成鴻溝,「取決於分配規則的形狀」。用一個最小模擬驗證:兩個區的初始紀錄比例 0.52 對 0.48,每輪按分配權重把案量發下去,被評估的區紀錄增加,然後正規化。分配權重取「紀錄比例的 \(p\) 次方」:(a)寫程式比較 \(p=1\)(按比例分配)與 \(p=2\)(超比例分配)在 20 輪後的結果;(b)對 \(p=2\) 報告第 1、5、10、20 輪的差距軌跡;(c)說明為什麼 \(p=1\) 不放大、\(p=2\) 放大,並指出這對「介入點」的含義。

解答 SOLUTION

(a)程式如下(此模擬是確定性的,無隨機數):

PYTHON
import numpy as np


def run(power, rounds=20, c=0.30):
    r = np.array([0.52, 0.48])
    for _ in range(rounds):
        share = r ** power / np.sum(r ** power)   # 分配權重
        r = r + c * share                          # 被評估的區累積紀錄
        r = r / r.sum()                            # 正規化成比例
    return r


for p in (1, 2):
    r = run(p)
    print(f"p={p}:20 輪後 {r[0]:.3f} vs {r[1]:.3f}(差距 {abs(r[0] - r[1]):.3f})")

實跑輸出:p=1 時 20 輪後仍是 0.520 vs 0.480(差距 0.040,與初始完全相同);p=2 時變成 0.935 vs 0.065(差距 0.870)。

(b)\(p=2\) 的差距軌跡(實跑):第 1 輪 0.049、第 5 輪 0.112、第 10 輪 0.298、第 20 輪 0.870——前幾輪看起來無害,中段開始加速,尾段贏者全拿。

(c)\(p=1\) 時每輪的更新是 \(r_i + c\, r_i = (1+c)\, r_i\),兩區被乘上同一個倍率,正規化後比例絲毫不動——按比例分配是這個動力系統的不動點結構,差距既不放大也不縮小。\(p=2\) 時領先區分到的份額超過它的占比(\(0.52^2/(0.52^2+0.48^2) \approx 0.540 > 0.52\)),每一輪都把「多一點」複利成「多更多」,這就是自我強化。含義:迴圈放大與否,跟模型準不準無關,完全由分配規則的曲率決定——同一個模型,接在按比例的分配規則後面是無害的,接在贏者多拿的規則後面就是差距製造機。所以介入點在分配規則的形狀(把超比例壓回線性、或給落後區保底配額),不在把模型再訓練得更準。這也是課文三欄分析的一次完整演練:決策在定分配規則的人,承擔在紀錄越來越薄的區,介入在規則的那一個指數上。

§05參考資料