在AI模型部署,尤其是涉及信贷、招聘或司法等高风险场景时,确保模型决策的公平性(Fairness)至关重要。传统的公平性指标(如群体平等机会或统计奇偶性)仅能发现群体偏差,但无法解释模型对特定个体的决策是否公平。
反事实分析(Counterfactual Analysis, CFA)提供了一种强大的、面向个体(Instance-specific)的公平性测试方法。它能直接回答一个核心问题:如果该个体只改变了其敏感属性(如性别、种族),而所有其他特征保持不变,模型的预测结果是否会改变?如果结果改变,则表明模型对该个体存在歧视性对待(Disparate Treatment)。
一、反事实分析在公平性测试中的应用
CFA是一种因果推理的技术应用,用于探测模型决策的局部敏感性。在公平性场景中,我们聚焦于受保护属性(Protected Attributes)。
基本流程:
- 确定测试实例 ($X$): 选择一个需要分析的个体数据点。
- 生成反事实实例 ($X’$): 只修改 $X$ 中的一个敏感特征(如将性别从“女”改为“男”),其余特征保持不变。
- 观察预测变化: 比较模型对 $X$ 的预测 ($ ext{Model}(X)$) 和对 $X’$ 的预测 ($ ext{Model}(X’)$)。
- 判定: 如果 $ ext{Model}(X)
eq ext{Model}(X’)$,则该实例的决策存在基于敏感属性的歧视证据。
二、实操:使用Python实现局部反事实公平性测试
我们将使用一个简化的模型部署场景:一个信贷模型,用于决定是否批准贷款(1=批准,0=拒绝)。敏感特征是用户的性别(Gender)。
1. 模型与数据准备
我们首先定义一个假设的、可能存在偏差的分类模型。为了简化,我们使用线性得分函数,其中我们故意给男性(Gender_M=1)一个很大的负权重,模拟歧视。
import pandas as pd
import numpy as np
# 假设的信贷模型:Score = 0.5*Age + 0.8*Income - 3.0*Gender_M - 100
# 评分高于 5 分则批准
def credit_approval_model(features: pd.DataFrame) -> int:
"""features: [Age, Income, Gender_M]"""
# 假设 Income 已经过归一化或标准化处理
features['Score'] = 0.5 * features['Age'] + 0.8 * features['Income'] - 3.0 * features['Gender_M'] - 100
return (features['Score'] > 5).astype(int).iloc[0]
# 定义一个需要测试的个体实例 (原始实例)
# 假设该女性用户(Gender_M=0)被批准了
original_instance = pd.DataFrame({
'Age': [35],
'Income': [140.0], # 假设这是一个高收入人群的标准化值
'Gender_M': [0] # 0代表女性
})
original_prediction = credit_approval_model(original_instance)
print(f"原始实例预测 (女性): {original_prediction} (1=批准)\n")
2. 执行反事实分析(CFA)
接下来,我们生成反事实实例,即只改变性别特征,并重新进行预测。
# 敏感特征配置
sensitive_feature = 'Gender_M'
# 生成反事实实例 (只翻转敏感特征)
counterfactual_instance = original_instance.copy()
# 反转性别:0 (女性) -> 1 (男性)
counterfactual_instance[sensitive_feature] = 1 - original_instance[sensitive_feature]
# 获取反事实预测
counterfactual_prediction = credit_approval_model(counterfactual_instance)
print(f"反事实实例 (男性):\n{counterfactual_instance}")
print(f"反事实预测 (男性): {counterfactual_prediction} (1=批准)\n")
# 3. 结果判断
if original_prediction != counterfactual_prediction:
print("\n*** 发现歧视性证据!***")
print(f"原始实例被批准,但仅改变性别后,决策结果翻转为: {'拒绝' if counterfactual_prediction == 0 else '批准'}")
print("该模型对该特定实例存在基于性别的区别对待 (Disparate Treatment)。")
else:
print("决策未改变,该实例不存在局部歧视证据。")
预期输出分析:
在上述代码中,由于原始实例(女性)的得分可能为:$0.5(35) + 0.8(140) – 3.0(0) – 100 = 17.5 + 112 – 100 = 29.5$ (批准)。
反事实实例(男性)的得分将为:$0.5(35) + 0.8(140) – 3.0(1) – 100 = 29.5 – 3 = 26.5$ (仍然批准)。
如果我们将Income值调低,例如 Income = 100.0:
原始得分 (女): $0.5(35) + 0.8(100) – 100 = 17.5 + 80 – 100 = -2.5$ (拒绝)
反事实得分 (男): $0.5(35) + 0.8(100) – 3.0(1) – 100 = -2.5 – 3 = -5.5$ (拒绝)
如果我们将Income值设置为一个临界值,例如 Income = 105.0:
原始得分 (女): $0.5(35) + 0.8(105) – 100 = 17.5 + 84 – 100 = 1.5$ (拒绝)
反事实得分 (男): $0.5(35) + 0.8(105) – 3.0(1) – 100 = 1.5 – 3 = -1.5$ (拒绝)
为了演示结果翻转,我们调整模型的偏见程度。
修改后的有偏模型 (确保结果翻转):
假设我们将分数阈值调整为 15,并且给男性一个巨大的惩罚权重 $30.0$。
# 重新运行:使用高偏见模型和新的高收入人群实例 (Income=140)
def high_bias_model(features: pd.DataFrame) -> int:
features['Score'] = 0.5 * features['Age'] + 0.8 * features['Income'] - 30.0 * features['Gender_M'] - 100
return (features['Score'] > 15).astype(int).iloc[0]
original_instance['Gender_M'] = 0
cf_instance = original_instance.copy()
cf_instance['Gender_M'] = 1
# 女性 (Gender_M=0) 预测
pred_female = high_bias_model(original_instance) # Score: 29.5. Prediction: 1 (批准)
# 男性 (Gender_M=1) 预测
pred_male = high_bias_model(cf_instance) # Score: 29.5 - 30.0 = -0.5. Prediction: 0 (拒绝)
print(f"女性预测: {pred_female}, 男性反事实预测: {pred_male}")
if pred_female != pred_male:
print("*** 歧视证据明确:仅因性别改变,预测结果由批准变为拒绝。***")
3. 将CFA集成到MLOps流程中
CFA不应只在开发阶段使用。在模型部署后的监控阶段,CFA可以集成到数据漂移(Data Drift)和公平性漂移(Fairness Drift)的检测流程中。
- 定期抽样: 从生产流量中抽取边缘案例或高价值实例。
- 自动化CFA测试: 对这些实例自动化生成反事实,并运行测试。
- 警报机制: 如果超过预设阈值比例的实例在敏感属性翻转后决策发生变化,则触发公平性警报,要求模型重新校准或回滚。
三、总结与局限性
反事实分析是检测模型局部歧视的黄金标准之一,因为它专注于个体公平性,为解释为什么某个个体受到了某种对待提供了明确的证据。然而,CFA的局限性在于它只测试了特定少数特征的敏感性,并且假设其他特征可以保持不变。在实际应用中,由于特征间的相关性(如地域与收入的关联),简单地翻转一个特征可能生成非现实的反事实实例。因此,在实际应用中,需要结合领域知识,确保反事实实例的有效性(Plausibility)。
汤不热吧