Python模型偏见怎么检测

wen python案例 20

本文目录导读:

Python模型偏见怎么检测

  1. 核心概念与检测维度
  2. 检测工具与库
  3. 数据层面检测
  4. 模型预测层面检测
  5. NLP模型偏见检测(以BERT为例)
  6. 完整检测流程示例
  7. 偏见缓解方法
  8. 最佳实践建议

检测Python模型中的偏见(Bias)是一个涉及数据、模型和评估的综合性过程,以下是一套系统化的检测方法,从概念到具体代码实现。

核心概念与检测维度

首先明确需要检测的偏见类型:

  • 数据偏见:训练数据本身存在的性别、种族等不均衡
  • 算法偏见:模型对不同群体预测表现的差异
  • 表征偏见:NLP模型中词向量的性别/种族关联

检测工具与库

# 主要工具库
# 1. AIF360 - IBM的公平性工具包
from aif360.datasets import BinaryLabelDataset
from aif360.metrics import ClassificationMetric, BinaryLabelDatasetMetric
from aif360.algorithms.preprocessing import Reweighing
# 2. Fairlearn - 微软的公平性工具
from fairlearn.metrics import MetricFrame, demographic_parity_difference, equalized_odds_difference
from fairlearn.datasets import fetch_adult
# 3. 其他辅助库
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

数据层面检测

数据分布分析

def check_data_bias(df, protected_attr, target_col):
    """
    检查数据中保护属性与目标变量的分布偏差
    """
    # 计算各群体在目标变量中的比例
    for group in df[protected_attr].unique():
        subset = df[df[protected_attr] == group]
        positive_rate = subset[target_col].mean()
        print(f"{protected_attr}={group}: 正类比例 = {positive_rate:.2%}")
    # 计算群体间比例差异
    proportions = df.groupby(protected_attr)[target_col].mean()
    print(f"\n最大差异: {proportions.max() - proportions.min():.2%}")
# 示例:检查UCI Adult数据集中的性别偏见
from fairlearn.datasets import fetch_adult
adult_data = fetch_adult(as_frame=True)
X = adult_data.data
y = adult_data.target
# 检查性别与收入的关系
check_data_bias(X.assign(income=y), protected_attr='sex', target_col='income')

统计检验

from scipy import stats
def statistical_parity_test(df, protected_attr, target_col):
    """
    统计奇偶性检验:不同群体获得正预测的概率是否相等
    """
    groups = df.groupby(protected_attr)[target_col]
    for (g1, data1), (g2, data2) in itertools.combinations(groups, 2):
        # 卡方检验
        contingency = pd.crosstab(
            df[protected_attr].isin([g1, g2]).replace({True: g1, False: g2}),
            df[target_col]
        )
        chi2, p, _, _ = stats.chi2_contingency(contingency)
        print(f"{g1} vs {g2}: p-value = {p:.4f}")

模型预测层面检测

公平性指标计算

def calculate_fairness_metrics(y_true, y_pred, sensitive_features):
    """
    计算主要公平性指标
    """
    from fairlearn.metrics import (
        demographic_parity_difference,
        equalized_odds_difference,
        equal_opportunity_difference,
        false_positive_rate_difference
    )
    metrics = {
        'demographic_parity': demographic_parity_difference(
            y_true, y_pred, sensitive_features=sensitive_features),
        'equalized_odds': equalized_odds_difference(
            y_true, y_pred, sensitive_features=sensitive_features),
        'equal_opportunity': equal_opportunity_difference(
            y_true, y_pred, sensitive_features=sensitive_features),
        'false_positive_rate_diff': false_positive_rate_difference(
            y_true, y_pred, sensitive_features=sensitive_features)
    }
    return metrics
# 使用示例
y_true = [1, 0, 1, 1, 0, 0]
y_pred = [0, 0, 1, 0, 0, 1]
sensitive = [0, 0, 0, 1, 1, 1]  # 假设敏感属性
results = calculate_fairness_metrics(y_true, y_pred, sensitive)
print("公平性指标:")
for metric, value in results.items():
    print(f"{metric}: {value:.4f}")

群体间性能差异分析

def group_performance_analysis(model, X_test, y_test, sensitive_attr):
    """
    分析模型在不同群体间的性能差异
    """
    from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
    # 创建DataFrame包含预测结果
    results = X_test.copy()
    results['y_true'] = y_test
    results['y_pred'] = model.predict(X_test)
    # 按敏感属性分组计算指标
    metrics_frame = MetricFrame(
        metrics={
            'accuracy': accuracy_score,
            'precision': precision_score,
            'recall': recall_score,
            'f1_score': f1_score
        },
        y_true=results['y_true'],
        y_pred=results['y_pred'],
        sensitive_features=results[sensitive_attr]
    )
    # 输出各群体指标
    print("各群体性能指标:")
    print(metrics_frame.by_group)
    # 计算差异
    print("\n指标差异:")
    print(metrics_frame.difference())
    return metrics_frame
# 示例:使用AIF360进行完整分析
from aif360.datasets import BinaryLabelDataset
from aif360.metrics import BinaryLabelDatasetMetric, ClassificationMetric
# 准备数据
protected_attr = 'sex'
privileged_groups = [{'sex': 1}]  # 假设男性是特权群体
unprivileged_groups = [{'sex': 0}]
# 创建AIF360数据集对象
dataset = BinaryLabelDataset(
    df=X.assign(label=y),
    label_names=['label'],
    protected_attribute_names=[protected_attr],
    favorable_class=1,
    unfavorable_class=0
)
# 计算数据层面的公平性指标
data_metric = BinaryLabelDatasetMetric(
    dataset,
    unprivileged_groups=unprivileged_groups,
    privileged_groups=privileged_groups
)
print(f"统计奇偶性差异: {data_metric.statistical_parity_difference():.4f}")
print(f"不一致性影响: {data_metric.disparate_impact():.4f}")

NLP模型偏见检测(以BERT为例)

from transformers import pipeline
from bias_bench import benchmark
from bias_bench.datasets import CrowSPairsDataset
def check_nlp_bias(model_name='bert-base-uncased'):
    """
    检测NLP模型中的刻板印象偏见
    """
    # 使用CrowS-Pairs数据集检测
    dataset = CrowSPairsDataset()
    # 加载模型
    unmasker = pipeline('fill-mask', model=model_name)
    # 测试刻板印象句子
    test_sentences = [
        "The [MASK] was a good nurse.",  # 护士性别偏见
        "The [MASK] was a successful CEO.",  # CEO性别偏见
        "The [MASK] worked as a software engineer.",  # 工程师性别偏见
    ]
    for sentence in test_sentences:
        results = unmasker(sentence)
        print(f"\n句子: {sentence}")
        for result in results[:5]:
            token = result['token_str'].strip()
            score = result['score']
            print(f"  {token}: {score:.4f}")
    # 使用专业偏见检测工具
    from bias_bench.benchmark import run_benchmark
    # benchmark_results = run_benchmark(model_name)
    # print(benchmark_results)
# 检查词向量偏见
def word_embedding_bias_check():
    """
    使用Word Embedding Association Test (WEAT)检测
    """
    from weat import WEAT  # 需要安装weat库
    # 定义目标和社会属性
    target_concepts = {
        'male': ['man', 'boy', 'brother', 'father', 'son'],
        'female': ['woman', 'girl', 'sister', 'mother', 'daughter']
    }
    attribute_concepts = {
        'career': ['career', 'manager', 'professional', 'executive'],
        'family': ['family', 'home', 'parent', 'children']
    }
    # 计算效应大小
    weat = WEAT(model='glove')
    effect_size = weat.run_test(
        target_concepts['male'], target_concepts['female'],
        attribute_concepts['career'], attribute_concepts['family']
    )
    print(f"WEAT效应大小: {effect_size:.4f}")

完整检测流程示例

class BiasDetectionPipeline:
    """
    完整的偏见检测流程
    """
    def __init__(self, model, sensitive_attr, privileged_groups=None):
        self.model = model
        self.sensitive_attr = sensitive_attr
        self.privileged_groups = privileged_groups or [{'sex': 1}]
        self.unprivileged_groups = [{'sex': 0}]
    def run_full_check(self, X, y, X_test, y_test):
        results = {}
        # 1. 数据偏见检测
        print("=== 数据层面偏见检测 ===")
        results['data_bias'] = self._check_data_bias(X, y)
        # 2. 模型预测偏见
        print("\n=== 模型预测偏见检测 ===")
        y_pred = self.model.predict(X_test)
        results['prediction_bias'] = self._check_prediction_bias(
            y_test, y_pred, X_test[self.sensitive_attr]
        )
        # 3. 特征重要性分析
        print("\n=== 特征重要性分析 ===")
        results['feature_importance'] = self._analyze_feature_importance(X)
        # 4. 生成报告
        self._generate_report(results)
        return results
    def _check_data_bias(self, X, y):
        """
        检查数据偏见
        """
        from fairlearn.metrics import demographic_parity_difference
        data = X.assign(label=y)
        # 检查各群体比例
        group_sizes = data.groupby(self.sensitive_attr).size()
        print("群体样本分布:")
        print(group_sizes)
        # 检查目标变量分布
        group_positive_rate = data.groupby(self.sensitive_attr)['label'].mean()
        print("\n正类比例:")
        print(group_positive_rate)
        return {
            'group_sizes': group_sizes,
            'positive_rates': group_positive_rate
        }
    def _check_prediction_bias(self, y_true, y_pred, sensitive):
        """
        检查预测偏见
        """
        metrics = calculate_fairness_metrics(y_true, y_pred, sensitive)
        # 阈值判断
        thresholds = {
            'demographic_parity': 0.1,
            'equalized_odds': 0.1,
            'equal_opportunity': 0.1
        }
        print("公平性指标值:")
        for metric, value in metrics.items():
            status = "⚠️ 可能存在偏见" if value > thresholds.get(metric, 0.1) else "✓ 正常"
            print(f"{metric}: {value:.4f} {status}")
        return metrics
    def _analyze_feature_importance(self, X):
        """
        分析特征重要性是否偏向敏感属性
        """
        if hasattr(self.model, 'feature_importances_'):
            importances = pd.DataFrame({
                'feature': X.columns,
                'importance': self.model.feature_importances_
            }).sort_values('importance', ascending=False)
            print("前5个最重要特征:")
            print(importances.head())
            # 检查敏感属性是否在重要特征中
            if self.sensitive_attr in importances['feature'].head().values:
                print(f"⚠️ 注意: 敏感属性'{self.sensitive_attr}'出现在重要特征中")
            return importances
        return None
    def _generate_report(self, results):
        """
        生成综合报告
        """
        print("\n" + "="*50)
        print("偏见检测总结报告")
        print("="*50)
        # 统计发现的偏见数量
        bias_count = 0
        if results.get('prediction_bias'):
            for metric, value in results['prediction_bias'].items():
                if value > 0.1:
                    bias_count += 1
        print(f"发现的潜在偏见数量: {bias_count}")
        if bias_count > 0:
            print("\n建议措施:")
            print("1. 数据层: 使用重采样或重加权技术平衡数据")
            print("2. 算法层: 考虑使用公平性约束的优化算法")
            print("3. 后处理层: 应用校准或阈值调整")
# 使用示例
if __name__ == "__main__":
    # 加载数据
    from fairlearn.datasets import fetch_adult
    adult = fetch_adult(as_frame=True)
    X = adult.data[['age', 'education-num', 'sex', 'hours-per-week']]
    y = adult.target
    # 划分数据
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.3, random_state=42
    )
    # 训练简单模型
    model = LogisticRegression(max_iter=1000)
    model.fit(X_train[['age', 'education-num', 'hours-per-week']], y_train)
    # 运行偏见检测
    pipeline = BiasDetectionPipeline(
        model=model,
        sensitive_attr='sex',
        privileged_groups=[{'sex': 1}]
    )
    results = pipeline.run_full_check(X_train, y_train, X_test, y_test)

偏见缓解方法

# 1. 数据重加权(Pre-processing)
from aif360.algorithms.preprocessing import Reweighing
rw = Reweighing(unprivileged_groups=unprivileged_groups, 
                privileged_groups=privileged_groups)
dataset_transformed = rw.fit_transform(dataset)
# 2. 公平性约束训练(In-processing)
from fairlearn.reductions import ExponentiatedGradient, DemographicParity
constraint = DemographicParity()
classifier = LogisticRegression(max_iter=1000)
mitigator = ExponentiatedGradient(classifier, constraint)
mitigator.fit(X_train, y_train, sensitive_features=X_train['sex'])
# 3. 后处理校准(Post-processing)
from fairlearn.postprocessing import ThresholdOptimizer
postprocessor = ThresholdOptimizer(
    estimator=model,
    constraints="demographic_parity",
    predict_method='predict_proba'
)
postprocessor.fit(X_train, y_train, sensitive_features=X_train['sex'])

最佳实践建议

  1. 持续监控:在生产环境中设置偏见检测的CI/CD管道
  2. 多样性测试:确保测试集包含多种人口统计学群体
  3. 指标选择:根据应用场景选择合适的公平性定义
  4. 透明报告:在模型文档中披露检测到的偏见和缓解措施
  5. 交叉验证:使用不同数据集和随机种子验证发现是否一致

通过以上方法,可以系统性地检测和量化Python模型中的偏见,为后续的公平性改进提供数据支持。

抱歉,评论功能暂时关闭!