模型公平性与偏见检测

专栏:人工智能训练师(三级)备考全攻略
模块:卷三·知识体系 — 第四部分·模型测试与评估
难度:⭐⭐⭐☆☆
考试权重:中高频(AI伦理必考,选择+简答)


一、为什么模型会有偏见?

AI偏见的来源链条:

  现实世界(存在历史偏见)
       │
       ▼
  训练数据(记录和放大偏见)
       │
       ▼
  机器学习模型(学习到偏见)
       │
       ▼
  模型预测(输出带有偏见的结果)
       │
       ▼
  现实影响(贷款拒绝/简历筛除/错误诊断)

例:
  历史招聘数据中,技术岗位90%为男性。
  模型学到"男性→更适合技术岗"的偏见。
  新简历筛选时,同等条件下女性被拒绝率更高。

1.1 偏见的三个来源

来源 说明 示例
数据偏见 训练数据不代表总体,存在采样偏差 人脸识别训练集以白人为主
标注偏见 标注者的主观偏见影响标签质量 标注者认为"女性不适合编程"
算法偏见 模型优化目标忽视弱势群体 最大化整体准确率导致少数族裔误判率高

二、公平性的定义与度量

2.1 公平性定义全景

公平性(Fairness)没有唯一定义,
常见的公平性定义相互矛盾,不可能同时满足。

常用公平性定义(记住这几个!)

  ① 统计均等(Demographic Parity)
     各群体获得正向预测的概率相等
     P(Ŷ=1|A=0) = P(Ŷ=1|A=1)

  ② 均等机会(Equalized Odds)
     各群体的真正例率(TPR)和假正例率(FPR)相等
     P(Ŷ=1|Y=1, A=0) = P(Ŷ=1|Y=1, A=1)  ← TPR相等
     P(Ŷ=1|Y=0, A=0) = P(Ŷ=1|Y=0, A=1)  ← FPR相等

  ③ 预测率均等(Predictive Parity)
     各群体中预测为正例时,真实为正例的概率相等
     P(Y=1|Ŷ=1, A=0) = P(Y=1|Ŷ=1, A=1)  ← Precision相等

  ④ 个体公平性(Individual Fairness)
     相似的个体应该得到相似的预测结果
公平性定义 数学要求 直觉解释 适用场景
统计均等 各群体正预测率相等 给每类人"同等机会" 招聘/贷款初筛
均等机会 各群体TPR相等 对真正合格者"一视同仁" 信用评分/假释
预测率均等 各群体Precision相等 预测为"高风险"时同等准确 医疗诊断/刑事司法
个体公平 相似者得相似结果 不因身份不同被区别对待 通用场景

2.2 公平性困境

理论证明:统计均等、均等机会、预测率均等三者不可兼得
(Chouldechova 2017, Kleinberg et al. 2017)

意味着:
  没有"完美的"公平性定义
  必须根据业务场景选择优先满足哪种公平性
  工程实践中需要明确权衡

三、偏见检测实战

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

# =========================================
# 构造示例数据(贷款审批场景)
# =========================================
np.random.seed(42)
n = 2000

data = pd.DataFrame({
    "gender": np.random.choice(["male", "female"], n, p=[0.5, 0.5]),
    "age_group": np.random.choice(["young", "middle", "senior"], n),
    "income": np.random.normal(50000, 20000, n),
    "credit_score": np.random.randint(300, 850, n),
})

# 真实标签:根据信用分和收入决定(与性别无关)
data["true_label"] = (
    (data["credit_score"] > 600) & 
    (data["income"] > 40000)
).astype(int)

# 模型预测:存在对女性的偏见(女性批准率人为降低)
data["pred_label"] = data["true_label"].copy()
female_mask = data["gender"] == "female"
data.loc[female_mask & (data["pred_label"] == 1), "pred_label"] = \
    np.random.choice([0, 1], female_mask.sum() // 2, p=[0.2, 0.8])


# =========================================
# 偏见检测工具类
# =========================================
class FairnessAuditor:
    """AI公平性审计工具"""
    
    def __init__(self, y_true, y_pred, sensitive_attr):
        self.y_true = np.array(y_true)
        self.y_pred = np.array(y_pred)
        self.sensitive = np.array(sensitive_attr)
        self.groups = np.unique(self.sensitive)
    
    def demographic_parity(self):
        """统计均等:各群体正预测率"""
        results = {}
        for group in self.groups:
            mask = self.sensitive == group
            results[group] = self.y_pred[mask].mean()
        
        rates = list(results.values())
        disparity = max(rates) - min(rates)
        
        print("\n📊 统计均等(Demographic Parity)")
        for group, rate in results.items():
            print(f"  {group}: 正预测率 = {rate:.2%}")
        print(f"  差异(Disparity) = {disparity:.2%}", 
              "✅" if disparity < 0.1 else "❌ 偏见!")
        return results, disparity
    
    def equalized_odds(self):
        """均等机会:各群体TPR和FPR"""
        results = {}
        for group in self.groups:
            mask = self.sensitive == group
            tn, fp, fn, tp = confusion_matrix(
                self.y_true[mask], self.y_pred[mask]
            ).ravel()
            
            tpr = tp / (tp + fn) if (tp + fn) > 0 else 0
            fpr = fp / (fp + tn) if (fp + tn) > 0 else 0
            results[group] = {"TPR": tpr, "FPR": fpr}
        
        tpr_values = [v["TPR"] for v in results.values()]
        fpr_values = [v["FPR"] for v in results.values()]
        tpr_disparity = max(tpr_values) - min(tpr_values)
        fpr_disparity = max(fpr_values) - min(fpr_values)
        
        print("\n📊 均等机会(Equalized Odds)")
        for group, metrics in results.items():
            print(f"  {group}: TPR={metrics['TPR']:.2%}, FPR={metrics['FPR']:.2%}")
        print(f"  TPR差异 = {tpr_disparity:.2%}", "✅" if tpr_disparity < 0.05 else "❌")
        print(f"  FPR差异 = {fpr_disparity:.2%}", "✅" if fpr_disparity < 0.05 else "❌")
        return results
    
    def disparate_impact_ratio(self):
        """
        差异影响比(DIR)
        DIR = 弱势群体正预测率 / 优势群体正预测率
        DIR < 0.8 → 存在系统性歧视(美国EEOC标准:4/5规则)
        """
        rates = {}
        for group in self.groups:
            mask = self.sensitive == group
            rates[group] = self.y_pred[mask].mean()
        
        max_group = max(rates, key=rates.get)
        min_group = min(rates, key=rates.get)
        dir_value = rates[min_group] / rates[max_group]
        
        print(f"\n📊 差异影响比(DIR)= {dir_value:.3f}")
        if dir_value < 0.8:
            print(f"  ❌ DIR < 0.8,违反4/5规则,存在歧视风险!")
        else:
            print(f"  ✅ DIR ≥ 0.8,通过4/5规则")
        return dir_value
    
    def full_audit(self):
        """运行完整审计"""
        print("=" * 50)
        print("         AI公平性审计报告")
        print("=" * 50)
        self.demographic_parity()
        self.equalized_odds()
        self.disparate_impact_ratio()
        print("=" * 50)


# 运行审计
auditor = FairnessAuditor(
    y_true=data["true_label"],
    y_pred=data["pred_label"],
    sensitive_attr=data["gender"]
)
auditor.full_audit()

# 预期输出:
# 统计均等 - female: 72%, male: 85% → 差异13% ❌ 偏见!
# DIR = 0.847 → 接近4/5规则边界

四、Fairlearn:专业公平性检测库

# pip install fairlearn
from fairlearn.metrics import (
    MetricFrame,
    demographic_parity_difference,
    equalized_odds_difference
)
from sklearn.metrics import accuracy_score

# =========================================
# 使用 MetricFrame 分群评估
# =========================================
mf = MetricFrame(
    metrics={"accuracy": accuracy_score},
    y_true=data["true_label"],
    y_pred=data["pred_label"],
    sensitive_features=data["gender"]
)

print("各群体准确率:")
print(mf.by_group)
print(f"\n最大差异: {mf.difference()['accuracy']:.4f}")

# =========================================
# 公平性指标计算
# =========================================
dp_diff = demographic_parity_difference(
    y_true=data["true_label"],
    y_pred=data["pred_label"],
    sensitive_features=data["gender"]
)
print(f"\n统计均等差异(越接近0越公平): {dp_diff:.4f}")

eo_diff = equalized_odds_difference(
    y_true=data["true_label"],
    y_pred=data["pred_label"],
    sensitive_features=data["gender"]
)
print(f"均等机会差异(越接近0越公平): {eo_diff:.4f}")

五、偏见缓解方法

缓解偏见的三个时间节点:

  ① 预处理(Pre-processing)
     在训练前修正数据
     方法:重采样、重新加权、对抗去偏

  ② 训练中(In-processing)
     在训练过程中添加公平性约束
     方法:公平性正则化项、对抗训练去偏

  ③ 后处理(Post-processing)
     在预测后调整输出
     方法:阈值调整(各群体用不同决策阈值)
           校准(Calibration)
方法 阶段 优点 缺点
重采样 预处理 简单易实现 可能丢失信息
对抗去偏 训练中 效果好,端到端 训练复杂,不稳定
公平性正则化 训练中 与模型紧耦合 需要调整正则强度
阈值调整 后处理 无需改动模型 可能损失整体精度

六、考试重点总结

6.1 核心概念

概念 关键点
三类偏见来源 数据偏见/标注偏见/算法偏见
统计均等 各群体正预测率相等
均等机会 各群体TPR和FPR相等
差异影响比DIR < 0.8则违反4/5规则,存在歧视风险
公平性不可兼得 三种定义数学上相互矛盾

6.2 高频选择题

Q: 以下哪个不是AI偏见的来源?
A: 模型架构选择(架构本身不引入偏见)✅

Q: 差异影响比(DIR)低于什么值被认为存在歧视风险?
A: 0.8(4/5规则)✅

Q: "弱势群体真正例率等于优势群体真正例率"是哪种公平性?
A: 均等机会(Equalized Odds)✅

Q: 统计均等和均等机会可以同时满足吗?
A: 一般情况下不能同时满足 ✅

Q: 以下哪种方法属于后处理阶段的偏见缓解?
A: 阈值调整(为不同群体设置不同决策阈值)✅

七、思维导图

模型公平性与偏见检测

偏见来源

数据偏见

采样偏差

历史偏见

标注偏见

标注者主观

算法偏见

优化目标偏差

公平性定义

统计均等

正预测率相等

均等机会

TPR和FPR相等

预测率均等

Precision相等

个体公平性

相似者相似预测

公平性不可兼得定理

三种定义矛盾

按场景选择

检测工具

FairnessAuditor自定义

Fairlearn库

差异影响比DIR

4/5规则

缓解方法

预处理

重采样

训练中

对抗去偏

公平性正则化

后处理

阈值调整

校准


📌 备考贴士:公平性是AI伦理的核心考点,记住三类偏见来源和三种公平性定义。“差异影响比DIR<0.8存在歧视风险"是选择题高频考点,4/5规则一定要记住。简答题常考"如何缓解AI模型偏见”,背熟预处理/训练中/后处理三阶段框架即可。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐