• Fairlearn 中的 API(二)


    前言

    Fairlearn 是一个旨在帮助数据科学家提高人工智能系统公平性的开源项目。目前国内并没有相关教程来讲解这个库的使用方式,所以笔者用一系列博客尽可能详细地教学 Fairlearn 库的使用方法。「在官网教程的基础上加入自己的个人见解。」

    Fairlearn 官网

    本篇博客是此系列的第三篇,主要内容是 Fairlearn 中内置的公平性评估方法。

    1. Metrics 模块

    功能:Metrics 模块提供了评估模型的公平性相关指标的方法。

    1.1 非群体性指标 (ungrouped Metrics)

    最简单的情况下,评估指标采取的是标签 Y t r u e Y_{true} Ytrue 和预测集 Y p r e d Y_{pred} Ypred 之间的关系。例如真阳性率 T P = P ( Y p r e d = 1 ∣ Y t r u e = 1 ) TP = P(Y_{pred}=1|Y_{true}=1) TP=P(Ypred=1∣Ytrue=1),即预测为正,实际也为正的概率。假阴性率 F N = P Y p e r d = 0 ∣ Y t r u e = 1 FN = P{Y_{perd}=0|Y_{true}=1} FN=PYperd=0∣Ytrue=1 ,即预测为负,实际为正的概率。召回率 R e c a l l = T P T P + F N Recall = \frac{TP}{TP+FN} Recall=TP+FNTP。召回率在代码中的一个具体实现是 sklearn.metrics.recall_score()

    代码展示 「Jupyter NoteBook」

    import sklearn.metrics as skm
    y_true = [0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1]
    y_pred = [0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1]
    skm.recall_score(y_true,y_pred)
    
    • 1
    • 2
    • 3
    • 4

    输出结果如下

    1.2 群体性指标 (Metrics with grouping)

    当考虑到公平时,我们想看到在不同组之间的指标有什么差异,这就涉及到群体性指标。

    1.2.1 将数据分组
    import numpy as np
    import pandas as pd
    
    group_membership_data = ['d', 'a', 'c', 'b', 'b', 'c', 'c', 'c',
                             'b', 'd', 'c', 'a', 'b', 'd', 'c', 'c']
    pd.set_option('display.max_columns',20)
    pd.set_option('display.width',80)
    pd.DataFrame({'y_true':y_true,'y_pred':y_pred,'group_membership_data':group_membership_data})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们将前一个例子的 y_truey_preda,b,c,d 划分为四个不同的组。

    1.2.2 预测组间召回率
    from fairlearn.metrics import MetricFrame
    grouped_metrics = MetricFrame(metrics= skm.recall_score,y_pred=y_pred,y_true=y_true,sensitive_features=group_membership_data)
    print(grouped_metrics.overall)
    print(grouped_metrics.by_group)
    
    • 1
    • 2
    • 3
    • 4

    1.2.3 输出组间统计信息

    除了输出各个组件的评估指标,MetricFrame 还支持输出其他统计信息,例如最大最小精度,比例等等

    print("min recall over groups = ", grouped_metrics.group_min()) # 最小召回率
    print("max recall over groups = ", grouped_metrics.group_max()) # 最大召回率
    print("difference in recall = ", grouped_metrics.difference(method='between_groups')) # 组间召回率的最大差异
    print("ratio in recall = ", grouped_metrics.ratio(method='between_groups')) # 最小召回率比最大召回率
    
    • 1
    • 2
    • 3
    • 4

    结果如下:

    1.2.4 评估组间的多个指标

    上述的例子只展示了召回率这一个指标,实际上 MetricFrame 可以一次评估多个指标

    from fairlearn.metrics import count
    multi_metrics = MetricFrame({'precision':skm.precision_score,'recall':skm.recall_score,'count':count},y_true=y_true,y_pred=y_pred,sensitive_features=group_membership_data) # precision_score 是差准率,是 TP/TP + TN
    print(multi_metrics.overall)
    print(multi_metrics.by_group)
    
    • 1
    • 2
    • 3
    • 4

    结果如下:

    1.2.5 带权重的组间评估指标

    通常情况下,不同个体的权重是不同的,得到带权重的组间评估指标只需要在 MetricFrame 中多加一个 sample_params 参数

    s_w = [1, 2, 1, 3, 2, 3, 1, 2, 1, 2, 3, 1, 2, 3, 2, 3]
    s_p = { 'sample_weight':s_w }
    weighted_metrics = MetricFrame(metrics=skm.recall_score,
                           y_true=y_true,
                           y_pred=y_pred,
                           sensitive_features=group_membership_data,
                           sample_params=s_p)
    print(weighted_metrics.overall)
    print(weighted_metrics.by_group)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    结果如下:

    1.2.6 使用携带参数的评估指标

    前面 MetricFrame 使用到的 metrics 都是不携带参数的,如果我们需要使用携带参数的 metric 该怎么办呢?

    直接将携带参数的 metric 传给 metrics 会直接报错,如下

    解决方法是,用functools.partial()fbeta_scorebeta 参数封装成一个方法,具体如下

    import functools
    
    fbeta_06 = functools.partial(skm.fbeta_score, beta=0.6)
    metric_beta = MetricFrame(metrics=fbeta_06, y_true=y_true, y_pred=y_pred, sensitive_features=group_membership_data)
    print(metric_beta.overall)
    print(metric_beta.by_group)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:这里回顾一下 fbeta_score,它是召回率和差准率的加权平均。召回率为 r e c a l l recall recall,查准率是 p r e c i s i o n precision precision ,那么 F − B e t a = ( 1 + β 2 ) ⋅ p r e c i s i o n ⋅ r e c a l l ( β 2 ⋅ p r e c i s i o n ) + r e c a l l F-Beta = (1+\beta^2)\cdot\frac{precision\cdot recall}{(\beta^2\cdot precision)+recall} FBeta=(1+β2)(β2precision)+recallprecisionrecall。当 β < 1 \beta < 1 β<1 时,偏向于召回率, β > 1 \beta > 1 β>1 时偏向于查全率。

    输出结果如下:

    1.2.7 多敏感属性的评估

    前面,我们只使用了单个敏感属性,如果我们需要用多个敏感属性,该如何实现呢?

    g_2 = [ 8,6,8,8,8,8,6,6,6,8,6,6,6,6,8,6]
    s_f_frame = pd.DataFrame(np.stack([group_membership_data,g_2],axis=1),columns=['SX 0','SX 1'])
    metric_2_f = MetricFrame(metrics=skm.recall_score,y_pred=y_pred,y_true=y_true,sensitive_features=s_f_frame)
    print(metric_2_f.overall)
    print(metric_2_f.by_group)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出结果如下:

    上述代码中,我们唯一需要了解的是 np.stack() 方法,它用于将两个相同维度 array() 在某一维度上合并**「axis = 0 按 x 轴,axis = 1 按 y 轴」。**

    a = np.array([1,2,3])
    b = np.array([4,5,6])
    np.stack([a,b],axis=1)
    
    • 1
    • 2
    • 3

    合并结果如下:

    1.3 来自 MetricFrame 的标量结果 (Scalar Results from MetricFrame)

    高级的机器学习算法通常使用度量函数来指导它们的优化。这样的算法通常和标量结果一起工作,所以如果我们希望根据公平性指标进行调优,就要在 MetricFrame 上执行聚合。

    这一小节,我们用 fairlearn 提供的 make_derived_metric 方法来自定义 metric 。通过 make_derived_metric 基于四种基本 metric 来自定义,它们分别是 group_mingroup_maxdifferenceratio「四选一」

    from fairlearn.metrics import make_derived_metric
    fbeta_difference = make_derived_metric(metric=skm.fbeta_score,transform='difference') # 选择的 Base Metric 是 differebce,即最大 F_Beta 与最小 F_Beta 的差
    fbeta_difference(y_true,y_pred,beta=0.7,sensitive_features = group_membership_data)
    
    • 1
    • 2
    • 3

    输出结果如下:

    上述方法可以用以下代码来替代,区别是后者需要使用 functools.partial

    fbeta_07 = functools.partial(skm.fbeta_score, beta=0.7)
    fbeta_07_metrics = MetricFrame(metrics=fbeta_07, y_true=y_true, y_pred=y_pred, sensitive_features=group_membership_data)
    fbeta_07_metrics.difference()
    
    • 1
    • 2
    • 3

    个人感觉下面的方法更好,因为它可以很方便的调用其他方法。

    1.4 组间指标的控制属性 ( Control features for grouped metrics)

    控制属性也称为条件属性,通过提供将数据分成子组的进一步方法,实现更加详细的公平性洞察。当数据被划分为子组时,控制属性的作用类似于敏感属性。不同的是,度量的 overall 值是在控制属性划分的各个子组上进行的**「其他方法如 group_maxgroup_min也是如此」**。

    控制属性对于属性的一些预期变化情况是有用的,所以我们需要在控制属性的同时计算差异。例如,在一个贷款场景中,我们希望不同收入的人以不同的利率获得批准。但是,在每个收入阶层中,我们仍然希望测量不同敏感特征之间的差异。

    在 MetricFrame 的一个构造方法中,提供了 control_features 参数。

    decision = [
       0,0,0,1,1,0,1,1,0,1,
       0,1,0,1,0,1,0,1,0,1,
       0,1,1,0,1,1,1,1,1,0
    ]
    prediction = [
       1,1,0,1,1,0,1,0,1,0,
       1,0,1,0,1,1,1,0,0,0,
       1,1,1,0,0,1,1,0,0,1
    ]
    control_feature = [
       'H','L','H','L','H','L','L','H','H','L',
       'L','H','H','L','L','H','L','L','H','H',
       'L','H','L','L','H','H','L','L','H','L'
    ]
    sensitive_feature = [
       'A','B','B','C','C','B','A','A','B','A',
       'C','B','C','A','C','C','B','B','C','A',
       'B','B','C','A','B','A','B','B','A','A'
    ]
    metric_c_f = MetricFrame(metrics=skm.accuracy_score,
                             y_true=decision,
                             y_pred=prediction,
                             sensitive_features={'SF' : sensitive_feature},
                             control_features={'CF' : control_feature})
    print(metric_c_f.overall)
    print(metric_c_f.by_group)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    结果如下:

    对于控制属性我是这么理解的,当我们需要比较不同组之间的差异时使用敏感属性,如不同地区人民的收入水平。但如果我们想看组内不同小群体,如某地区的男性和女性收入水平时,就可以用到控制属性。

    1.5 绘制群体性指标图

    1.5.1 基本绘制方法

    绘图主要用到 MetricFrame 的 plot 方法,它内部调用了 matplotlib

    from fairlearn.metrics import false_positive_rate, true_positive_rate, selection_rate
    from sklearn.metrics import accuracy_score, recall_score, precision_score
    
    metrics = {
        'accuracy': accuracy_score,
        'precision': precision_score,
        'recall': recall_score,
        'false positive rate': false_positive_rate,
        'true positive rate': true_positive_rate,
        'selection rate': selection_rate,
        'count': count}
    metric_frame = MetricFrame(metrics=metrics,
                               y_true=y_true,
                               y_pred=y_pred,
                               sensitive_features=group_membership_data)
    metric_frame.by_group.plot.bar(
        subplots=True,
        layout=[3, 3],
        legend=False,
        figsize=[12, 8],
        title="Show all metrics",
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    1.5.2 修改统一的 ylim

    在上一小节的图中,我们可以看出,不同的图 y 轴的范围是不同的,如果我们需要让它们的范围一致(方便比较),可以使用 plot() 方法中规定 ylim 参数来调节。

    metric_frame.by_group.plot.bar(
        subplots=True,
        ylim=[0,1],
        layout=[3, 3],
        legend=False,
        figsize=[12, 8],
        title="Show all metrics",
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    结果如下:

    1.5.3 修改绘图的色系

    到这里,可能有人要吐槽,上面的图选的什么阴间配色!!就不能自己选好看的颜色画吗?

    这一点,matplotlib 在设计时就有考虑到,我们可以通过 colormap 参数来调节。更多好康的色系可以参考这里: 好康的色系

    这里我们选用 accent 色系

    metric_frame.by_group.plot.bar(
        subplots=True,
        ylim=[0,1],
        layout=[3, 3],
        legend=False,
        figsize=[12, 8],
        colormap = 'Accent',
        title="Show all metrics",
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    结果如下:

    1.5.4 定义绘制图的样式

    到这里,可能小伙伴们又要问了,你这画的都是柱状图啊,只能画柱状图也太垃了吧!!

    唉~~,作为一个专业的机器学习公平库,fairlearn 怎么可能没考虑到这点呢?**「其实是 Pandas 考虑到的」**我们可以通过 kind 参数来调节绘图的类型

    metric_frame.by_group.plot(
        kind = 'pie',
        subplots=True,
        ylim=[0,1],
        layout=[3, 3],
        legend=False,
        figsize=[12, 8],
        colormap = 'Accent',
        title="Show all metrics",
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    结果如下:

    那我们该怎么知道有哪些图形可以画呢?可以参考如下链接: 可绘制的类型

  • 相关阅读:
    SpringBoot 源码分析(三) 监听器分析以及属性文件加载分析
    HarmonyOS应用开发
    【luogu P2508】圆上的整点(高斯素数模板)
    java用位运算实现加减乘除
    SpringBoot学习笔记(五)——Git版本控制
    odoo 开发入门教程系列-计算的字段和变更(Computed Fields And Onchanges)
    力扣SQL50 每台机器的进程平均运行时间 SUM AVG DISTINCT
    【笔记】【信息论与编码】第三章 离散信源
    Java代码审计安全篇-XXE(XML外部实体注入)漏洞
    Git基本操作
  • 原文地址:https://blog.csdn.net/jiaweilovemingming/article/details/127739932