公平性与偏差缓解:从度量到去偏实战

模型会放大数据里的历史偏见,在信贷、招聘、医疗等场景造成真实伤害。本文讲清偏差的来源分类(历史/表示/测量/聚合/评估)、人口均等与机会均等等公平性定义及不可能定理、差异影响率与均等化几率等度量、Fairlearn 与 AIF360 的检测方法,以及预处理重加权、处理中正则与对抗去偏、后处理阈值调整三类缓解手段,并覆盖代理变量、法规合规与治理闭环。

引言

一个模型在整体测试集上准确率 95%,却在某个少数群体上召回率只有 60%——这类「平均很好、局部很糟」的模型,正在信贷审批、简历筛选、医疗分诊、内容审核中造成系统性伤害。问题的根源往往不在算法,而在数据本身就承载了历史偏见:如果过去某个群体获得贷款的比例偏低,模型会把这个模式学成「规律」,再用它拒绝未来的申请人。

公平性不是道德口号,而是一组可度量、可优化、有明确取舍的工程约束。本文从偏差来源讲起,梳理公平性定义与它们之间的数学冲突,给出检测与三类缓解手段的实战方法。

前置:评估指标与分组评估见 /ml-model-evaluation/;偏差来源的归因分析见 /ml-model-interpretability/。

目录

1. 偏差从哪里来

治理偏差的第一步是分清它从哪来,因为不同来源要用不同手段。

来源机制例子
历史偏差数据反映既有不公过去招聘歧视女性,历史数据里女性录用率低
表示偏差某群体样本过少人脸数据里深肤色占比不足 5%
测量偏差标签本身有偏用「被逮捕」当「犯罪」的代理,警务密度影响标签
聚合偏差用群体模型套个体用全国模型预测某地区,忽略地区差异
评估偏差测试集不具代表性测试集也缺少数群体,问题被掩盖
部署偏差使用场景与训练不符模型被用到分布完全不同的新市场

表示偏差与测量偏差最隐蔽:前者让模型对少数群体欠拟合,后者让模型学到一个错误的「真值」。测量偏差尤其危险——即使模型完美拟合了标签,也可能在拟合一个错误的代理。

2. 公平性定义与不可能定理

「公平」没有唯一定义。常见的三类:

2.1 人口均等(Demographic Parity)

要求各群体获得正例预测的比例相同:

P(Ŷ = 1 | A = 0) = P(Ŷ = 1 | A = 1)

A 是敏感属性(如性别、种族)。它只看预测结果分布,不看真实标签,实现简单但可能牺牲准确性——若某群体真实违约率更高,强行拉平预测率会让决策不公。

2.2 机会均等(Equal Opportunity)

要求在各群体中,真实正例被正确预测的比例相同(即各组 TPR 相等):

P(Ŷ = 1 | Y = 1, A = 0) = P(Ŷ = 1 | Y = 1, A = 1)

它关注「真正应该通过的人有没有被公平地通过」,比人口均等更贴合业务。

2.3 均等化几率(Equalized Odds)

机会均等的加强版,同时要求 TPR 与 FPR 都相等:

P(Ŷ = 1 | Y = y, A = 0) = P(Ŷ = 1 | Y = y, A = 1),  y ∈ {0, 1}

2.4 不可能定理

除非满足极端条件(各组基率相同、或模型完美预测),人口均等、均等化几率、校准三者不可同时满足。这不是工程能力问题,而是数学上的不可能(Kleinberg 等,2016)。

基率不同时:
  预测均等  ⊥  均等化几率  ⊥  校准
  必须根据业务选择其中一个作为主目标

因此公平性工程的第一步不是「选最好的算法」,而是和业务方一起决定用哪个定义、可接受多大精度损失。

3. 公平性度量

把定义落成可计算的指标。

指标定义直觉
人口均等差max - min 组间正例率越小越公平
差异影响率劣势组正例率 / 优势组正例率< 0.8 触发「80% 规则」
机会均等差组间 TPR 差越小越公平
均等化几率差组间 TPR 与 FPR 的最大差综合
预测均等差组间 FPR 差关注误伤
校准差组间各分数段实际正例率差关注概率可靠性

差异影响率(Disparate Impact) 来自美国就业法:若劣势组通过率不足优势组的 80%,即视为存在歧视性影响。它是合规审查中最常被引用的量化门槛。

import numpy as np

def disparate_impact(y_pred, group):
    rates = {g: y_pred[group == g].mean() for g in np.unique(group)}
    return min(rates.values()) / max(rates.values()), rates

y_pred = np.array([1, 0, 1, 1, 0, 1, 0, 0])
group = np.array(["A", "A", "A", "A", "B", "B", "B", "B"])
di, rates = disparate_impact(y_pred, group)
print("各组通过率:", {k: round(v, 3) for k, v in rates.items()})
print("差异影响率:", round(di, 3), "(<0.8 需审查)")

4. 检测:Fairlearn 与分组评估

4.1 分组评估(Group Metrics)

公平性检测的基础是按敏感属性分组,分别计算指标。很多问题只有在分组视图下才暴露。

import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix

def group_metrics(y_true, y_pred, group):
    rows = []
    for g in np.unique(group):
        m = group == g
        tn, fp, fn, tp = confusion_matrix(y_true[m], y_pred[m]).ravel()
        rows.append({
            "group": g, "n": m.sum(),
            "TPR": tp / (tp + fn) if (tp + fn) else 0,
            "FPR": fp / (fp + tn) if (fp + tn) else 0,
            "precision": tp / (tp + fp) if (tp + fp) else 0,
        })
    return pd.DataFrame(rows)

y_true = np.array([1, 1, 0, 0, 1, 0, 1, 0])
y_pred = np.array([1, 0, 0, 0, 1, 1, 1, 0])
group = np.array(["A", "A", "A", "A", "B", "B", "B", "B"])
print(group_metrics(y_true, y_pred, group))

4.2 Fairlearn

Fairlearn 提供 MetricFrame 做分组指标、ThresholdOptimizer 做后处理去偏。

from fairlearn.metrics import MetricFrame, selection_rate, true_positive_rate
from sklearn.metrics import accuracy_score

mf = MetricFrame(
    metrics={"accuracy": accuracy_score,
             "selection_rate": selection_rate,
             "tpr": true_positive_rate},
    y_true=y_true, y_pred=y_pred, sensitive_features=group,
)
print("总体:", mf.overall)
print("分组:\n", mf.by_group)
print("组间差:", mf.difference())

difference() 直接给出组间最大差,是快速判断公平性的第一指标。

5. 缓解一:预处理

在训练前改数据,让数据「看起来更公平」。

5.1 重加权(Reweighting)

给样本加权,使各群体在标签上的加权分布对齐。核心是计算每个 (group, label) 组合的权重:

import numpy as np

def compute_weights(y, group):
    """使各群体在标签上的权重和相等"""
    w = np.ones(len(y))
    for g in np.unique(group):
        for label in np.unique(y):
            mask = (group == g) & (y == label)
            p_group = (group == g).mean()
            p_label = (y == label).mean()
            p_joint = mask.mean()
            if p_joint > 0:
                w[mask] = (p_group * p_label) / p_joint
    return w

y = np.array([1, 1, 0, 0, 1, 0, 1, 0])
group = np.array(["A", "A", "A", "A", "B", "B", "B", "B"])
print("样本权重:", compute_weights(y, group).round(3))

把权重传给 fit(sample_weight=w) 即可。重加权简单、可解释,但不改变特征本身,效果有限。

5.2 数据增强与重采样

对表示偏差,直接补采少数群体样本,或用 SMOTE 类方法合成少数群体样本。对图像任务,做群体平衡的数据增强(如不同肤色、年龄的合成)。

5.3 移除敏感特征与其代理

最直接的做法是训练时删掉敏感属性。但代理变量(如邮编、姓名、浏览行为)会间接携带敏感信息,删了敏感列仍可能不公平。

6. 缓解二:处理中约束

在模型训练过程中施加公平性约束。

6.1 正则化惩罚

在损失里加一项「组间预测差异」,联合优化:

L = L_task + λ · L_fairness

L_fairness 可以是组间正例率差的平方、TPR 差的平方等。λ 控制公平与精度的权衡。

import numpy as np
from sklearn.linear_model import LogisticRegression

def fairness_penalty(scores, group):
    """人口均等惩罚:各组平均预测分数之差的平方"""
    means = [scores[group == g].mean() for g in np.unique(group)]
    return np.var(means)

# 示意:λ 越大,公平性越强、精度越低
for lam in [0.0, 0.1, 1.0]:
    print(f"lambda={lam}: 惩罚示例 {round(fairness_penalty(np.array([0.9,0.8,0.3,0.2]), np.array(['A','A','B','B'])), 4)}")

6.2 对抗去偏(Adversarial Debiasing)

加一个「对手」网络,试图从模型输出预测敏感属性。主模型训练目标是让对手猜不出敏感属性(最小化对手准确率),同时保持任务精度。这本质上是让表示与敏感属性独立。

主模型 → 预测 Ŷ  → 任务损失
       ↘ 中间表示 Z → 对手网络 → 敏感属性 A
                     ↺ 主模型学习让对手失效(梯度反转)

对抗去偏效果好,但训练不稳定、超参多,需要仔细调参。

6.3 约束优化

把公平性写成显式约束,用带约束的优化求解(如 Fairlearn 的 ExponentiatedGradient):

from fairlearn.reductions import ExponentiatedGradient, DemographicParity
from sklearn.linear_model import LogisticRegression

mitigator = ExponentiatedGradient(
    estimator=LogisticRegression(max_iter=1000),
    constraints=DemographicParity(),
)
# mitigator.fit(X, y, sensitive_features=group)

7. 缓解三:后处理

不动模型,只改预测结果。优点是与模型无关、可插拔,适合已上线的黑盒模型。

7.1 分组阈值调整

对每个群体用不同阈值,使各组的 TPR(或正例率)对齐。这是 ThresholdOptimizer 的思路。

import numpy as np

def group_thresholds(scores, group, target_rate):
    """为每个群体找阈值,使正例率达到目标"""
    th = {}
    for g in np.unique(group):
        s = np.sort(scores[group == g])
        idx = int((1 - target_rate) * len(s))
        th[g] = s[min(idx, len(s) - 1)]
    return th

scores = np.array([0.9, 0.7, 0.4, 0.3, 0.8, 0.5, 0.2, 0.1])
group = np.array(["A", "A", "A", "A", "B", "B", "B", "B"])
print("分组阈值:", {k: round(v, 3) for k, v in group_thresholds(scores, group, 0.5).items()})
from fairlearn.postprocessing import ThresholdOptimizer
from sklearn.linear_model import LogisticRegression

base = LogisticRegression(max_iter=1000)
# base.fit(X_train, y_train)
post = ThresholdOptimizer(estimator=base, constraints="equalized_odds",
                          predict_method="predict_proba")
# post.fit(X_train, y_train, sensitive_features=group_train)

7.2 三类手段对比

手段时机优点缺点
预处理训练前简单、可解释效果有限、改动数据
处理中训练中效果最好、可精细控制复杂、需重训
后处理预测后与模型无关、快速上线只改结果不改模型、可能损精度

8. 代理变量与特征选择

公平性治理绕不开一个问题:敏感属性可能被其他特征间接编码。检测代理变量的方法:

  • 相关性/互信息:计算每个特征与敏感属性的互信息,高的可能是代理。
  • 可预测性:用特征去预测敏感属性,若准确率远高于基率,说明存在代理。
  • 消融测试:逐个移除可疑特征,看公平性指标是否改善。
from sklearn.feature_selection import mutual_info_classif
import numpy as np

X = np.random.default_rng(0).normal(size=(200, 4))
sensitive = (X[:, 0] + np.random.default_rng(1).normal(0, 0.1, 200) > 0).astype(int)
mi = mutual_info_classif(X, sensitive, random_state=0)
print("各特征与敏感属性的互信息:", mi.round(3))   # 特征 0 最高,是强代理

但要注意:移除代理变量可能降低模型效用,也可能只是把偏差转移到别的特征。正确做法是识别 + 显式约束,而非盲目删列。可解释性工具(如 SHAP)能帮助定位哪些特征在推动不公平决策,方法见 /ml-model-interpretability/。

9. 法规与治理闭环

9.1 主要法规

法规关键要求
EU AI Act高风险系统须做偏差评估、保留技术文档、人监督
GDPR 第 22 条自动化决策须可解释、可申诉
美国 80% 规则差异影响率 < 0.8 触发审查
中国算法推荐管理规定不得诱导沉迷、须提供关闭选项

9.2 治理闭环

公平性不是一次性检查,而是持续闭环:

定义公平目标 → 分组评估基线 → 选择缓解手段 → 上线分组监控
     ↑                                              │
     └────────── 定期复审 + 申诉反馈 ←──────────────┘

上线后要按群体持续监控关键指标(TPR、FPR、拒绝率),一旦组间差异超出阈值即告警。监控基础设施见 /ml-model-monitoring-drift/。同时应产出模型卡(Model Card):记录模型用途、训练数据构成、分组性能、已知限制与适用边界。

10. 常见坑与检查清单

现象根因处理
删掉敏感列后仍不公平代理变量检测互信息,显式约束
各定义冲突无法兼顾不可能定理与业务确认主目标
公平性改善但精度大跌约束过强调 λ,做精度-公平权衡曲线
线下公平线上不公平线上群体分布不同上线分组监控
测试集无群体标签评估偏差补采并标注敏感属性
后处理阈值泄露隐私阈值依赖群体评估合规性,必要时改处理中方案

上线前检查清单:是否明确敏感属性与公平定义;是否做过分组评估;差异影响率是否 > 0.8;是否有精度-公平权衡记录;是否检查过代理变量;是否有上线后分组监控;是否产出模型卡与申诉渠道。

11. 总结

11.1 核心要点

  • 偏差来自数据与流程,不只是算法;历史偏差与测量偏差最难察觉。
  • 公平性有多种定义,不可能定理说明必须取舍,先定主目标。
  • 差异影响率 < 0.8 是最常被引用的合规红线。
  • 缓解分预处理、处理中、后处理三类,处理中效果最好、后处理最易上线。
  • 代理变量让「删除敏感列」失效,需检测而非盲删。
  • 公平性是持续闭环,必须上线后分组监控。

11.2 与建模流程的衔接

公平性评估建立在分组评估之上(见 /ml-model-evaluation/),归因与持续保障则依赖可解释性工具与监控体系(见 /ml-model-interpretability/)。若关注算法与工程实现的更深讨论,可延伸阅读 AI/ML 专题 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 高斯过程回归与分类:不确定性建模实战
  2. MLOps 全生命周期实践:从实验到生产闭环
  3. 数据标注与质量治理:从标注规范到一致性度量