4.1 偏见来源与公平性指标 本节摘要:要消除 AI 偏见,先得知道它从哪来、怎么量它。偏见有三个来源:数据层面(历史偏见、代表性不足、标签偏见)、算法层面(目标函数忽视公平、代理变量)、部署层面(反馈循环、上下漂移)。本节拆解这三个层面,再重点讲四个核心公平性指标——统计均等、机会均等、校准公平性、Theil 指数的数学定义、代码实现,以及一个关键但反直觉的事实:这些指标之间常常互相冲突,你不可能同时满足所有公平定义。
本节摘要:要消除 AI 偏见,先得知道它从哪来、怎么量它。偏见有三个来源:数据层面(历史偏见、代表性不足、标签偏见)、算法层面(目标函数忽视公平、代理变量)、部署层面(反馈循环、上下漂移)。本节拆解这三个层面,再重点讲四个核心公平性指标——统计均等、机会均等、校准公平性、Theil 指数的数学定义、代码实现,以及一个关键但反直觉的事实:这些指标之间常常互相冲突,你不可能同时满足所有公平定义。
阅读完本节,你应当能够:
AI 偏见最著名的案例大概是亚马逊曾经的招聘工具——它用过去十年的简历数据训练,因为历史简历里男性占比极高,模型学会了"惩罚包含女性关键词的简历",哪怕这些关键词和岗位能力无关。问题不在于算法有什么恶意,而在于训练数据本身就反映了"过去十年科技行业男性主导"这个历史事实。模型忠实地学到了这个规律,然后把它放大了。
这个案例点出了偏见的核心特征:偏见通常不是故意的,它是数据里历史不平等的回声。 你拿"过去发生的事"训练模型,模型自然学会"过去的样子",而过去的样子本身可能是不公平的。如果不主动干预,模型会把这种不公平固化甚至放大。
更麻烦的是,偏见会自我强化。一个有偏见的推荐系统会优先把高薪工作推给男性,男性因此获得更多高薪机会,下一代训练数据里男性高薪的比例就更高——这就是"反馈循环"偏见。打破这种循环,必须先能识别偏见的来源,再用指标量化它,最后才能干预。
偏见不是单一现象,它在 AI 生命周期的不同阶段以不同形式出现。
数据层面是最常见的来源:
算法层面往往更隐蔽:
部署层面是上线后才显现的:
| 偏见类型 | 说明 | 典型例子 |
|---|---|---|
| 分配偏见 | 不同群体获得不同结果 | 男性获得更多贷款批准 |
| 质量偏见 | 对不同群体的服务质量不同 | 少数族裔人脸识别准确率低 |
| 再现偏见 | 放大社会刻板印象 | 招聘 AI 偏好男性 |
| 链接偏见 | 基于关联的歧视 | 推荐高薪工作给男性 |
统计均等(Demographic Parity)是最直观的公平定义:不同群体获得正结果的概率应该相等。比如贷款审批,男性和女性的通过率应该接近。
import numpy as np def demographic_parity(y_pred, protected_attr): """统计均等:不同群体的正结果率差异""" group_0_rate = np.mean(y_pred[protected_attr == 0]) group_1_rate = np.mean(y_pred[protected_attr == 1]) difference = abs(group_0_rate - group_1_rate) return { "group_0_rate": float(group_0_rate), "group_1_rate": float(group_1_rate), "difference": float(difference), "satisfied": difference < 0.05, # 常用阈值 5% }
统计均等简单好用,但有个致命问题:它忽视了真实标签。如果某个群体客观上违约率确实更高(比如因为收入更低),强制通过率相等反而对另一群体不公平。
机会均等(Equalized Odds)弥补了这个缺陷:在真实标签相同的条件下,不同群体的预测率应该相等。也就是说,对于"真实会还款的人",无论男女,被批准的概率应该一样;对于"真实会违约的人",被拒绝的概率也应该一样。
def equalized_odds(y_pred, y_true, protected_attr): """机会均等:TPR 和 FPR 在群体间相等""" results = {} for y_val in [0, 1]: for group in [0, 1]: mask = (y_true == y_val) & (protected_attr == group) if mask.sum() > 0: rate = np.mean(y_pred[mask]) results[f"y={y_val}_group={group}"] = float(rate) tpr_diff = abs(results["y=1_group=0"] - results["y=1_group=1"]) fpr_diff = abs(results["y=0_group=0"] - results["y=0_group=1"]) results["satisfied"] = (tpr_diff < 0.05) and (fpr_diff < 0.05) return results
机会均等比统计均等更"合理",因为它考虑了真实标签。但它需要真实标签——在训练时有,部署后不一定有。
校准公平性(Calibration)关注的是:对于相同的预测分数,不同群体的实际概率应该相等。比如模型给某男性和某女性都打了"0.7 的违约概率",那么这两个人实际违约的概率应该都是 70% 左右。
from sklearn.calibration import calibration_curve def calibration_fairness(y_true, y_pred_proba, protected_attr): """校准公平性:相同分数下群体真实概率应相等""" results = {} for group in [0, 1]: mask = protected_attr == group if mask.sum() > 0: prob_true, prob_pred = calibration_curve( y_true[mask], y_pred_proba[mask], n_bins=10) results[f"group_{group}"] = { "prob_true": prob_true.tolist(), "prob_pred": prob_pred.tolist(), } return results
Theil 指数来自经济学,衡量结果分配的不平等程度,0 表示完全平等。它对整体的公平性给一个单一数值,方便横向对比。
def theil_index(y_pred, protected_attr): """Theil 指数:0 表示完全平等""" N = len(y_pred) total = np.sum(y_pred) if total == 0: return 0.0 # 按群体算各自的占比 theil = 0.0 for group in [0, 1]: mask = protected_attr == group group_share = np.sum(y_pred[mask]) / total group_size_share = mask.sum() / N if group_share > 0: ratio = group_share / group_size_share theil += group_size_share * ratio * np.log(ratio) return float(theil)
💡 关键直觉:这是整个公平性领域最反直觉、也最重要的结论——这几个公平性指标在很多情况下不可能同时满足。 你可以做到统计均等,或者做到机会均等,但很难两个都满足(除非两个群体在真实分布上完全一样,或者模型完美无错)。这不是技术不行,是数学上的不可能定理。
所以选指标不能"全都要",得根据业务场景挑最关键的那个。贷款审批适合机会均等(要考虑真实违约),广告投放适合统计均等(曝光机会平等),医疗诊断适合校准(分数要准)。
| 指标 | 强调什么 | 适合场景 | 局限 |
|---|---|---|---|
| 统计均等 | 结果平等 | 广告、推荐 | 忽视真实标签 |
| 机会均等 | 误差平等 | 贷款、招聘 | 需要真实标签 |
| 校准公平性 | 分数准确 | 医疗、风险评估 | 不保证结果平等 |
| Theil 指数 | 整体不平等度 | 全局监控 | 单一数值,细节少 |
公平性检测的第一步是确定"保护哪些属性"——性别、种族、年龄、残疾状况等。但这里有个矛盾:很多法规禁止在决策时使用这些属性(比如贷款不能因为种族拒人),但公平性检测恰恰需要知道这些属性才能算指标。解决办法通常是:检测时收集这些属性(仅用于审计,不进模型特征),或者用代理变量推断(但这又引入新的偏见风险)。
⚠️ 常见坑:不要因为"法规禁止用敏感属性"就完全不收集它们。不收集你没法做公平性检测,没法检测就没法证明你的模型公平。合规的做法是收集但隔离——只用于审计统计,绝不进模型输入特征,并有严格的访问控制。
算出"差异 0.07"之后,怎么判断这算不算有偏见?业界常用的经验阈值是 0.05(5%)和"五分之四规则"(差异影响 ratio 不低于 0.8,来自美国就业法规)。但这些只是经验值,不同场景敏感度不同——医疗诊断的公平阈值显然要比广告推荐更严。最好结合业务风险和法律要求来定。
因为单一指标会骗人,生产环境通常组合监控多个指标。比如同时看统计均等(结果公平)和机会均等(过程公平),如果两者都达标,模型才算真的公平;如果只满足一个,要分析为什么另一个不满足,是不是有更深层的问题。
下一节看怎么把这些指标的计算自动化——用 Fairlearn 和 AIF360 两个工具库做系统化的偏见检测。
指标之间会冲突这件事,工程上需要一个可操作的决策流程,而不是停留在"看场景"的泛泛之谈。实践中好用的是三问。第一问:基率本来就不同吗?如果两个群体在目标变量上的真实分布不同(比如不同地区的违约率确实有差异),强求统计均等等于要求模型对现实视而不见,此时机会均等或校准类指标更合理。第二问:错误的代价对称吗?误放过一个高风险申请和误拒一个低风险申请,业务损失通常不对称,代价偏向哪边,指标就应更关注对应群体的假阳性/假阴性差。第三问:这个决策可申诉吗?有申诉通道的场景,校准公平(分数含义一致)最重要,因为申诉者要拿同样的分数说事;无申诉的自动化决策,则统计均等这类结果指标更关键。三问答完,指标基本能收敛到一两个,把问答记录写进指标定义书,就是将来审计的第一手材料。还要留一个习惯:指标选完后做敏感性分析——换一个合理指标结论会不会反转,会反转说明结果本来就脆弱,别下重注。
最后强调文档化的价值。指标选择的推理过程——为什么选它、放弃谁、依据什么业务判断——应该和代码一样进版本库。实践中见过太多次这样的场景:一年后新成员接手,不明白为什么用机会均等而不用统计均等,只好推倒重来重新争论一遍;或者审计时才发现当初的选择没有任何书面依据,说不清是深思熟虑还是随手默认。一份两三页的指标定义书,写明属性、指标、阈值、选择理由、参与决策的人和日期,成本一个下午,却能让后续所有的争论有据可查。公平性工程的成熟度,很大程度上就看这些"纸面工作"的完备程度。