📑 本页目录(点开跳转)
19 · 合规、审计与模型卡
⏱ 44 分钟 | 📋 不是走过场,是给自己留后路
🎯 一句话
当模型影响到人的钱、机会或权利时,「效果好」就不再是唯一标准。 这一章讲三件事:公平性怎么量、决策怎么解释、以及怎么写一份能救你的模型卡。
⚖️ 一、公平性:先想清楚你要哪一种
「公平」不是一个定义,是好几个互相冲突的定义:
| 定义 | 含义 | 例子 |
|---|---|---|
| 群体公平(统计均等) | 各群体的通过率相同 | 男女的贷款通过率一样 |
| 机会均等 ⭐ | 各群体中真正合格的人通过率相同 | 有还款能力的人,不分性别都能过 |
| 预测均等 | 各群体的预测精度相同 | 对各群体的预测同样准 |
| 个体公平 | 相似的个体得到相似的结果 | — |
🔑 一个必须知道的数学事实: 除非各群体的基础率恰好相同,否则上面几个定义不可能同时满足。 这是被证明过的(fairness impossibility)。
⭐ 实践含义:你必须先选一个,并且说清为什么选它 —— 而不是笼统地说「我们的模型是公平的」。
为什么不可能同时满足?一个 30 秒能算完的例子:
两个群体,基础率不同:A 组 30% 真的合格,B 组 10% 真的合格
假设模型对两组【同样准】:TPR = 0.8,FPR = 0.1 ← 机会均等 ✅
A 组通过率 = 0.3×0.8 + 0.7×0.1 = 0.31
B 组通过率 = 0.1×0.8 + 0.9×0.1 = 0.17 ⭐
💀 「机会均等」一满足,「群体公平」自动就不满足(0.17 / 0.31 = 0.55)
想把通过率拉平?只能对 B 组降门槛 —— 那又会破坏「预测均等」
⭐ 这不是模型不够好,是数学上的必然。 基础率不同的两个群体,你只能选一个方向去对齐。 报告里要写的是「我们选了机会均等,因为……」,而不是「我们的模型公平」。
怎么量
import pandas as pd
def fairness_report(df, group_col, y_true, y_pred):
"""⭐ 给每个群体算一整排数字放在一张表里 —— 单看任何一个都会误导"""
out = []
for g, d in df.groupby(group_col):
out.append({
"群体": g, "样本量": len(d),
"基础率": d[y_true].mean(), # ⭐ 冲突的根源,必须报
"通过率": d[y_pred].mean(), # 群体公平看这个
"TPR": d.loc[d[y_true] == 1, y_pred].mean(), # ⭐ 机会均等看这个
"FPR": d.loc[d[y_true] == 0, y_pred].mean(),
"PPV": d.loc[d[y_pred] == 1, y_true].mean(), # 预测均等看这个
})
t = pd.DataFrame(out)
# ⭐ 四五分之规则:最低通过率 / 最高通过率 ≥ 0.8,否则要能解释清楚
t["通过率之比"] = t["通过率"] / t["通过率"].max()
return t
⭐ 「四五分之规则」(80% rule)值得记住: 弱势群体的通过率 ÷ 优势群体的通过率 < 0.8 时,就要能拿出解释。 它不是数学定理,是一条被广泛引用的经验红线 —— 但它的价值在于:它是一条你今天就能算出来的、具体的数字。 上面那个例子里比值是 0.55,直接触线。
⚠️ 两个常见误区:
| 误区 | 真相 |
|---|---|
| 「把敏感特征删掉就公平了」 | ❌ 其他特征会代理它(邮编 ≈ 种族,购物记录 ≈ 性别)⭐ |
| 「差异 = 歧视」 | ❌ 差异可能来自真实的基础率不同;要看的是「同等条件下是否被区别对待」 |
⭐ 第一条特别重要:删掉敏感特征反而会让你失去检测偏差的能力 —— 你连"有没有偏差"都测不出来了。 正确做法通常是:不用它做预测,但保留它做审计。(具体受当地法规约束)
💀 一个案例:删掉了性别,却比带着性别更歧视
场景:简历初筛模型
合规要求:不得使用性别
团队做法:删掉性别字段,顺便也删掉了姓名 ✅ 看起来很干净
上线半年后,外部审计(带着自己的数据)算出来:
| 指标 | 数字 |
|---|---|
| 女性候选人通过率 ÷ 男性候选人通过率 | 0.61 💀(四五分之规则要求 ≥ 0.8) |
| 团队自己在这半年里发现的问题 | 零 —— 因为他们没有性别字段,根本算不出这个比值 ⭐ |
模型学到了三个强代理:
① 毕业院校 —— 女子学院、师范类院校的分布差异
② 简历关键词 —— 社团、爱好、志愿经历的用词差异
③ 工作年限的【连续性】⭐ —— 生育带来的空档期被学成了负面信号
⭐⭐ 这个案例的核心结论,值得抄在合规评审的第一页: 删掉敏感特征不会消除偏见,只会消除你发现偏见的能力。 半年里唯一有能力做这个检测的,是外部审计——因为只有他们手上有性别标签。
🔍 上线前跑一次「代理检测」,20 行代码
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import cross_val_score
import pandas as pd
def proxy_audit(X_without_sensitive, sensitive, top_k=10):
"""⭐ 用【剩下的所有特征】去预测敏感属性 —— 预测得越准,代理越强"""
clf = GradientBoostingClassifier(random_state=0)
auc = cross_val_score(clf, X_without_sensitive, sensitive,
cv=5, scoring="roc_auc").mean()
clf.fit(X_without_sensitive, sensitive)
imp = pd.Series(clf.feature_importances_,
index=X_without_sensitive.columns).nlargest(top_k)
return {"能多准地推出敏感属性(AUC)": round(auc, 3), "最强的代理": imp}
| 这个 AUC | 说明 |
|---|---|
| ≈ 0.5 | ✅ 基本没有代理,删掉敏感特征确实有效 |
| 0.65 – 0.85 | ⚠️ 有明显代理,"删了字段"是自欺欺人 |
| > 0.9 | 💀 敏感属性几乎被完整重建了,模型事实上"知道"每个人的性别/种族 |
⭐ 这个检查的妙处在于:它不需要模型上线,也不需要标签。 只要有历史数据和敏感属性标签,半小时就能跑完 —— 而且它输出的
最强的代理那张表,直接告诉你该去审查哪几个特征。
📜 二、可解释性:给人看的解释
技术解释 ≠ 给用户的解释:
❌ 「特征 f_23 的 SHAP 值是 −0.18」
✅ 「主要因为您近 30 天有 5 次贷款申请记录」
⭐ 好的对外解释要满足三条:
① 用业务语言,不用特征名
② 是【可行动的】—— 告诉他怎么做能改变结果
③ 数量不超过 3-4 条,按影响排序
🔗 技术侧怎么算在第 10 章 SHAP; 这一章关心的是怎么把它翻译成人能用的话。
⚠️ 一个法律与技术的落差: 很多法规要求「对自动化决策提供解释」, 但SHAP 给的是「模型为什么这么算」,不是「现实中你该怎么改」(第 10 章的边界)。 如果解释要用于指导用户行动,需要额外确认因果方向(第 13 章)。
📋 三、模型卡(Model Card)
一页纸,回答别人会问的所有问题:
# 模型卡:<模型名> v<版本>
## 1. 基本信息
- 用途:解决什么业务问题
- 版本 / 上线日期 / 负责人
- 上游依赖、下游消费方
## 2. 预期用途与【不适用范围】⭐⭐
- 设计用于:______
- **不应用于**:______(这一条最重要)
- 已知失效场景:______
## 3. 训练数据
- 来源、时间范围、样本量
- 人群构成(各群体占比)⭐
- 已知的采样偏差
## 4. 评估结果
- 整体指标
- **分人群指标** ⭐(不只是总体)
- 公平性指标(选了哪个定义、为什么)
## 5. 局限与风险
- 已知的失效模式
- 分布外行为
- 反馈闭环风险
## 6. 运维
- 监控项与告警阈值
- 重训周期与触发条件
- 降级策略、回滚方式
- 联系人 / 值班
⭐ 第 2 节的「不应用于」是整张卡最有价值的部分: 模型被滥用,绝大多数是因为使用者不知道它的边界。
💀 一个典型的滥用:给「用户流失预测」模型的分数, 被业务方拿去当作「用户价值分」来分配客服资源 —— 两者完全不是一回事,但分数看起来都像"用户好坏"。 模型卡写清楚「不应用于评估用户价值」,就能挡住它。
⚠️ 模型卡真正的难点不是写,是「不腐烂」
💀 最常见的失败模式:
模型卡在上线那天写得很好,然后【再也没有更新过】
→ 半年后它描述的是一个已经不存在的模型
→ 而所有人还在拿它当依据 —— 比没有模型卡更危险 ⭐
三条让它活下来的机制:
| 机制 | 做法 |
|---|---|
| 和发布单元绑在一起 ⭐⭐ | MODEL_CARD.md 放进第 17 章的发布目录,每个版本一份 |
| 关键字段自动生成 | 训练数据时间范围、样本量、人群构成、分人群指标 —— 这些都该由训练脚本写出来,不靠人手填 |
| 过期即告警 | 模型卡的 last_reviewed 超过 6 个月 → 进值班列表 |
⭐ 「自动生成」这条最有效: 需要人手填的部分越少,模型卡越不容易腐烂。 真正需要人写的其实只有两节:「不应用于」 和 「已知的失效模式」 —— 而这两节恰好也是最有价值的两节。
🔍 四、审计要能回答的五个问题
① 这个决策是怎么做出的? → 第 17 章的日志 + 第 10 章的 SHAP
② 模型用了哪些数据训练? → 数据快照 + 模型卡
③ 有没有对某群体系统性不利? → 分人群评估 + 公平性指标
④ 出问题时怎么发现和纠正? → 监控 + 回滚 + 申诉通道
⑤ 谁负责? → 模型卡里的负责人
⭐ ④ 里的「申诉通道」常被忽略: 自动化决策必须有人工复核的入口。 这既是很多法规的要求,也是发现模型系统性错误的重要来源 —— 申诉数据本身就是一份高质量的错误样本集。
🧭 五、一个务实的分级
不是所有模型都需要这一整套:
| 影响 | 该做的 |
|---|---|
| 内部工具、无个人影响 | 模型卡(简版)+ 基础监控 |
| 影响用户体验(推荐、排序) | + 分人群评估 + 申诉通道 |
| 影响机会(信贷、招聘、定价) | + 完整公平性评估 + 可解释 + 人工复核 ⭐ |
| 影响安全/健康 | + 外部审计 + 强制人工确认 |
⭐ 判据是「模型的错误会给个人造成什么后果」, 不是模型有多复杂、也不是用了什么算法。
⚠️ 六、一张坑表
| 坑 | 后果 | 怎么修 |
|---|---|---|
| 说「我们的模型是公平的」 | 没说清是哪种公平,等于什么都没说 | 明写选了哪个定义、为什么 ⭐ |
| 删掉敏感特征就算完事 💀 | 代理照样歧视,而你失去了检测能力 | 不用于预测,保留用于审计;上线前跑 proxy_audit |
| 只报整体指标 | 分人群的问题永远看不见 | 分人群评估进闸门(第 16 章闸3) |
| 只看通过率差异,不看基础率 | 把真实的基础率差异当成歧视 | 基础率必须报,它是所有冲突的根源 |
| 模型卡写完不再更新 ⭐ | 半年后它描述的是一个不存在的模型,比没有更危险 | 绑进发布单元 + 关键字段自动生成 + 过期告警 |
| 模型卡不写「不应用于」 | 分数被拿去做完全不同的决策 | 那一节是整张卡最有价值的部分 ⭐⭐ |
| 自动化决策没有申诉入口 | 既违规,又丢掉了最好的错误样本来源 | 申诉通道 + 人工复核 |
| 申诉数据只用来处理个案 | 浪费了一份高质量的标注 | 定期把申诉样本喂回评估集 ⭐ |
| 对外解释直接吐 SHAP 值 | 用户看不懂,且可能给出错误的行动建议 | 翻译成业务语言 + 先确认因果方向(第 13 章) |
| 所有模型都上全套合规 | 成本高到没人执行,最后一起摆烂 | 按影响分级,判据是「错误对个人的后果」 |
🔗 七、和站内其他章的关系
| 相关的地方 | 和这一章的关系 |
|---|---|
| 第 10 章 SHAP | 技术解释的来源 |
| 第 13 章因果 | 「可行动的解释」需要它 |
| 第 17 章回溯 | 审计问题 ① 的基础 |
| 全景导论 14 | LLM 场景的合规 |
| 全景导论 12 · 对齐与AI安全 | 治理那一侧:模型该不该做某件事,是在训练阶段用对齐解决的。本章管的是「做完之后怎么留证据、怎么应付审计」——同一件事的前后两半 |
| 智能体工程 15 | Agent 的安全边界 |
✅ 检查点
- 四种公平性定义分别是什么?为什么必须先选一个?用那个 30 秒的例子说明它们为什么冲突。
- 什么是四五分之规则?上面那个例子里的比值是多少?
- 「删掉敏感特征就公平了」错在哪?用那个简历初筛的案例说明。正确做法通常是什么?
- 代理检测怎么做?AUC 落在哪个区间说明"删字段是自欺欺人"?这个检查为什么特别划算?
- 对外解释要满足哪三条?SHAP 用于对外解释时有什么落差?
- 模型卡里最有价值的是哪一节?举那个滥用的例子。
- 模型卡真正的难点是什么?哪三条机制能让它不腐烂?其中最有效的是哪一条、为什么?
- 审计要回答哪五个问题?哪一项常被忽略、为什么它重要?
- 分级的判据是什么?为什么不该所有模型都上全套?
👀 答案
- 群体公平(通过率相同)、机会均等(真正合格的人通过率相同)、预测均等(预测精度相同)、个体公平。必须先选一个是因为:除非各群体基础率恰好相同,否则这些定义不可能同时满足(已被证明)。例子:A 组 30% 合格、B 组 10% 合格,模型对两组同样准(TPR=0.8、FPR=0.1,机会均等成立)→ A 组通过率 0.31、B 组 0.17,群体公平自动就不成立;想拉平通过率只能对 B 组降门槛,那又破坏预测均等。这不是模型不够好,是数学上的必然。
- 弱势群体的通过率 ÷ 优势群体的通过率不低于 0.8,否则要能拿出解释。它不是数学定理,是一条被广泛引用的经验红线,价值在于今天就能算出来。上面那个例子里是 0.17 / 0.31 = 0.55,直接触线。
- 因为其他特征会代理它(邮编≈种族、购物记录≈性别)。案例:简历初筛模型删掉性别和姓名,半年后外部审计算出女性通过率是男性的 0.61 倍,而团队自己发现的问题是零——因为没有性别字段,他们根本算不出这个比值。模型学到的三个代理是:毕业院校、简历关键词(社团/爱好用词)、以及工作年限的连续性(生育空档期被学成负面信号)。核心结论:删掉敏感特征不会消除偏见,只会消除你发现偏见的能力。正确做法通常是:不用它做预测,但保留它做审计(受当地法规约束)。
- 用剩下的所有特征去预测敏感属性,预测得越准说明代理越强。AUC 0.65–0.85 说明有明显代理、"删了字段"是自欺欺人;接近 0.5 才算真的没代理;超过 0.9 意味着敏感属性几乎被完整重建了。划算是因为不需要模型上线也不需要标签,有历史数据和敏感属性标签半小时就能跑完,而且它输出的「最强的代理」那张表直接告诉你该去审查哪几个特征。
- ①用业务语言不用特征名 ②是可行动的(告诉他怎么做能改变结果)③不超过 3-4 条、按影响排序。落差在于:SHAP 给的是「模型为什么这么算」,不是「现实中你该怎么改」;如果解释要用于指导用户行动,需要额外确认因果方向。
- 第 2 节的「不应用于」。因为模型被滥用绝大多数是使用者不知道边界。例子:「流失预测」的分数被拿去当「用户价值分」分配客服资源——两者完全不是一回事但分数看起来都像"用户好坏"。
- 难点不是写,是不腐烂:最常见的失败是上线那天写得很好、然后再也没更新,半年后它描述的是一个不存在的模型,而所有人还拿它当依据——比没有模型卡更危险。三条机制:和发布单元绑在一起(每个版本一份)、关键字段由训练脚本自动生成、
last_reviewed超过 6 个月就告警。最有效的是自动生成——需要人手填的部分越少,模型卡越不容易腐烂;真正需要人写的只有「不应用于」和「已知失效模式」两节,而这两节恰好也最有价值。 - ①决策怎么做出的 ②用了哪些数据训练 ③有没有对某群体系统性不利 ④怎么发现和纠正 ⑤谁负责。④里的「申诉通道」常被忽略——它既是法规要求,申诉数据本身也是一份高质量的错误样本集,应该定期喂回评估集。
- 模型的错误会给个人造成什么后果——不是模型有多复杂、用了什么算法。不该所有模型都上全套,是因为成本高到没人执行,最后会一起摆烂。
🛑 可以停在这里
⚡ 走神救援
⭐「公平」不是一个定义是好几个互相冲突的定义:群体公平(通过率相同)、机会均等(真正合格的人通过率相同)、预测均等、个体公平;⭐⭐已被证明:除非各群体基础率恰好相同,否则不可能同时满足 → 你必须先选一个并说清为什么。⚠️两个误区:⭐「删掉敏感特征就公平了」是错的——其他特征会代理它(邮编≈种族),而且删掉后你连有没有偏差都测不出来,正确做法通常是不用它预测但保留它做审计;「差异=歧视」也错,要看的是同等条件下是否被区别对待。对外解释三条:用业务语言、可行动、不超过3-4条;⚠️落差:SHAP 给的是"模型为什么这么算"不是"现实中你该怎么改",要指导行动得先确认因果方向。⭐⭐模型卡最有价值的是「不应用于」那一节——滥用绝大多数是因为使用者不知道边界(典型:流失预测分数被拿去当"用户价值分"分配客服资源)。审计五问:怎么做出的/用了什么数据/有无群体不利/怎么发现纠正/谁负责;⭐「申诉通道」常被忽略——它既是法规要求,申诉数据本身也是高质量的错误样本集。⭐分级判据是「错误会给个人造成什么后果」,不是模型多复杂(全都上全套的结果是成本高到没人执行、最后一起摆烂)。⭐30 秒看懂"为什么不可能同时满足":A 组 30% 合格、B 组 10% 合格,模型对两组同样准(TPR=0.8/FPR=0.1,机会均等成立)→ A 组通过率 0.31、B 组 0.17,群体公平自动就不成立;想拉平只能对 B 组降门槛,那又破坏预测均等。这不是模型不够好,是数学上的必然。 ⭐四五分之规则:弱势群体通过率 ÷ 优势群体通过率 不低于 0.8,上面这个例子是 0.55,直接触线——它的价值在于今天就能算出来。💀真实案例:简历初筛模型删掉性别和姓名,半年后外部审计算出女性通过率是男性的 0.61 倍,而团队自己发现的问题是零——因为没有性别字段,他们根本算不出这个比值;模型学到的三个代理是毕业院校、简历关键词、以及工作年限的连续性(生育空档期被学成负面信号)。⭐⭐该抄在合规评审第一页的一句:删掉敏感特征不会消除偏见,只会消除你发现偏见的能力。 🔍上线前跑 20 行的代理检测:用剩下的特征去预测敏感属性,AUC 0.65–0.85 就说明"删了字段"是自欺欺人,超过 0.9 意味着敏感属性几乎被完整重建;不需要上线也不需要标签,半小时跑完,还直接告诉你该审查哪几个特征。⚠️模型卡的难点不是写是不腐烂——上线那天写得很好然后再没更新,半年后它描述的是一个不存在的模型,比没有更危险;三条机制:绑进发布单元、⭐关键字段自动生成(人手填的越少越不容易腐烂)、过期告警;真正需要人写的只有「不应用于」和「已知失效模式」两节,而这两节恰好最有价值。
下一节 👉 20-实战与挑战项目.md