Responsible AI-公平性分析

Responsible AI对人工智能模型(AI模型)开发者和企业管理者十分重要,Responsible AI贯穿在AI模型的开发、训练、微调、评估、部署等环节,是保障AI模型安全、稳定、公平、符合社会道德的重要方法。PAI已支持用户在DSW中集成Responsible AI的相关工具对产出的AI模型进行公平性分析、错误分析及可解释性分析。

原理介绍

公平性分析是Responsible AI的一部分,它专注于识别和纠正AI系统中的偏见,确保模型不会受到某一不合理因素的影响,从而保证AI系统对所有人都是公平的。公平性分析的核心原理如下:

  • 避免偏见:识别和减少数据和算法中的偏见,确保AI系统不会因性别、种族、年龄等个人特征而导致不公平的决策。

  • 代表性数据:使用代表性数据集练习AI模型,这样模型就可以准确地代表和服务于所有用户群体,同时确保少数群体不会被边缘化。

  • 透明度和解释性:增加AI系统的透明度,通过可解释的AI技术,让最终用户和决策者理解AI决策过程和输出的原因。

  • 持续监控和评估:定期监控和评估AI系统,以识别和纠正可能随时间出现的任何不公平的偏差或做出的决策。

  • 多样性和包容性:AI系统的设计和开发过程中纳入多样性和包容性的观念,确保从多种背景和观点出发考虑问题,包括团队构成的多样性。

  • 合规性和道德原则:遵守相关的法律法规和道德标准,包括对人权的尊重,以及确保AI应用不会造成伤害或不公。

本文以“评估一个预测年收入是否大于50K的模型在性别、种族因素的公平性”为例,介绍了如何在阿里云PAIDSW产品中使用responsible-ai-toolbox对模型进行公平性分析。

准备环境和资源

  • DSW实例:如果您还没有DSW实例,请参见创建DSW实例。推荐配置如下:

    • 推荐实例规格:ecs.gn6v-c8g1.2xlarge

    • 镜像选择:建议使用Python3.9及以上版本。本文选择的官方镜像为:tensorflow-pytorch-develop:2.14-pytorch2.1-gpu-py311-cu118-ubuntu22.04

    • 模型选择:responsible-ai-toolbox支持Sklearn、PyTorchTensorFlow框架的回归和二分类模型。

  • 训练数据集:推荐您使用自己的数据集;如果您需要使用示例数据集,请按步骤三:准备数据集操作。

  • 算法模型:推荐您使用自己的算法模型;如果您需要使用示例算法模型,请按步骤五:模型训练操作。

步骤一:进入DSW Gallery

  1. 登录PAI控制台

  2. 在顶部左上角根据实际情况选择地域。

  3. 在左侧导航栏选择快速开始 > Notebook Gallery,搜索Responsible AI-公平性分析,并单击对应卡片上的DSW中打开

  4. 选择DSW实例,单击打开Notebook,系统会打开Responsible AI-公平性分析Notebook案例。

步骤二:导入依赖包

安装responsible-ai-toolbox的依赖包(raiwidgets),用于后续的评估。

!pip install raiwidgets==0.34.1

步骤三:准备数据集

您可以直接执行以下脚本加载OpenML1590数据集,以完成本文的示例。

from raiutils.common.retries import retry_function
from sklearn.datasets import fetch_openml

class FetchOpenml(object):
    def __init__(self):
        pass
    # 获取 data_id = 1590的OpenML数据集
    def fetch(self):
        return fetch_openml(data_id=1590, as_frame=True)

fetcher = FetchOpenml()
action_name = "Dataset download"
err_msg = "Failed to download openml dataset"
max_retries = 5
retry_delay = 60
data = retry_function(fetcher.fetch, action_name, err_msg,
                      max_retries=max_retries,
                      retry_delay=retry_delay)

您也可以加载自己的数据集,CSV格式的数据集对应的指令如下:

import pandas as pd

# 加载自己的数据集,csv 格式数据集
# 使用pandas读取CSV文件
data = pd.read_csv(filename)

步骤四:数据预处理

获取特征变量和目标变量

目标变量是指模型预测的真实结果,特征变量是指每条实例数据中目标变量以外的变量。在本文示例中,目标变量是“class”,特征变量是“sex、race、age……”:

  • class:年收入是否大于50K

  • sex:性别

  • race:种族

  • age:年龄

执行以下脚本,读取数据到X_raw,并查看一部分示例数据:

# 获取特征变量,不包括目标变量。
X_raw = data.data
# 展示特征变量的前5行数据,不包括目标变量。
X_raw.head(5)

执行以下脚本,设置目标变量的值,并查看一部分示例数据。其中,“1”表示收入>50K,“0”表示收入≤50K。

from sklearn.preprocessing import LabelEncoder

# 转换目标变量,转换为二分类目标
# data.target 是目标变量是“class”
y_true = (data.target == '>50K') * 1
y_true = LabelEncoder().fit_transform(y_true)

import matplotlib.pyplot as plt
import numpy as np

# 查看目标变量的分布
counts = np.bincount(y_true)
classes = ['<=50K', '>50K']
plt.bar(classes, counts)

获取特征敏感特征

执行以下脚本,将性别(sex)、种族(race)设置为敏感特征,并查看一部分示例数据。responsible-ai-toolbox会评估该模型的推理结果是否因为这两个敏感特征产生偏见。在实际使用时,您可以按业务需要设置敏感特征。

# 定义“性别”和“种族”为敏感信息
# 首先从数据集中选取敏感信息相关的列,组成新的Dataframe `sensitive_features`
sensitive_features = X_raw[['sex','race']]
sensitive_features.head(5)

执行以下脚本,删除X_raw中的敏感特征及数据,并查看删除后的示例数据。

# 将特征变量中的敏感特征删除
X = X_raw.drop(labels=['sex', 'race'],axis = 1)
X.head(5)

特征编码和标准化

执行以下脚本,对数据进行标准化处理,转换为适合responsible-ai-toolbox使用的格式。

import pandas as pd
from sklearn.preprocessing import StandardScaler

# one-hot 编码
X = pd.get_dummies(X)

# 对数据集X进行特征标准化(scaling)
sc = StandardScaler()
X_scaled = sc.fit_transform(X)
X_scaled = pd.DataFrame(X_scaled, columns=X.columns)

X_scaled.head(5)

划分训练和测试数据

执行以下脚本,将20%的数据划分为测试数据集,剩余数据为训练数据集。

from sklearn.model_selection import train_test_split

# 按照`test_size`比例,将特征变量X和目标变量y划分为训练集和测试集
X_train, X_test, y_train, y_test = \
    train_test_split(X_scaled, y_true, test_size=0.2, random_state=0, stratify=y_true)

# 使用相同的随机种子,将敏感特征划分为训练集和测试集,确保与上述划分保持一致
sensitive_features_train, sensitive_features_test = \
    train_test_split(sensitive_features, test_size=0.2, random_state=0, stratify=y_true)

分别查看训练数据集、测试数据集的数据量。

print("训练数据集的数据量:", len(X_train))
print("测试数据集的数据量:", len(X_test))

分别重置训练数据集、测试数据集的索引。

# 重置 DataFrame 的索引,避免索引错误问题
X_train = X_train.reset_index(drop=True)
sensitive_features_train = sensitive_features_train.reset_index(drop=True)
X_test = X_test.reset_index(drop=True)
sensitive_features_test = sensitive_features_test.reset_index(drop=True)

步骤五:模型训练

本文示例中,基于Sklearn使用训练数据训练一个逻辑回归模型。

Sklearn

from sklearn.linear_model import LogisticRegression

# 创建逻辑回归模型
sk_model = LogisticRegression(solver='liblinear', fit_intercept=True)
# 模型训练
sk_model.fit(X_train, y_train)

PyTorch

import torch
import torch.nn as nn
import torch.optim as optim

# 定义逻辑回归模型
class LogisticRegression(nn.Module):
    def __init__(self, input_size):
        super(LogisticRegression, self).__init__()
        self.linear = nn.Linear(input_size, 1)

    def forward(self, x):
        outputs = torch.sigmoid(self.linear(x))
        return outputs

# 实例化模型
input_size = X_train.shape[1]
pt_model = LogisticRegression(input_size)

# 损失函数和优化器
criterion = nn.BCELoss()
optimizer = optim.SGD(pt_model.parameters(), lr=5e-5)

# 训练模型
num_epochs = 1
X_train_pt = X_train
y_train_pt = y_train
for epoch in range(num_epochs):
    # 前向传播
    # DataFrame 转换为 Tensor
    if isinstance(X_train_pt, pd.DataFrame):
        X_train_pt = torch.tensor(X_train_pt.values)
        X_train_pt = X_train_pt.float()
    outputs = pt_model(X_train_pt)
    outputs = outputs.squeeze()
    # ndarray 转换为 Tensor
    if isinstance(y_train_pt, np.ndarray):
        y_train_pt = torch.from_numpy(y_train_pt)
        y_train_pt = y_train_pt.float()
    loss = criterion(outputs, y_train_pt)

    # 反向传播和优化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

TensorFlow

import tensorflow as tf
from tensorflow.keras import layers

# 定义逻辑回归模型
tf_model = tf.keras.Sequential([
    layers.Dense(units=1, input_shape=(X_train.shape[-1],), activation='sigmoid')
])

# 编译模型,使用二元交叉熵损失和随机梯度下降优化器
tf_model.compile(optimizer='sgd', loss='binary_crossentropy', metrics=['accuracy'])

# 训练模型
tf_model.fit(X_train, y_train, epochs=1, batch_size=32, verbose=0)

步骤六:模型评估

引入FairnessDashboard,使用responsible-ai-toolbox加载测试数据集进行结果预测,生成公平性评估报告。

FairnessDashboard按照敏感变量分类预测结果和真实结果,并通过对比分析预测结果和真实结果,得到在不同敏感变量情况下的模型预测情况。

以敏感特征值“sex”为例,FairnessDashboard按照“sex”将对应的预测结果(y_pred)和真实结果(y_test)进行分类,在“sex”分类中分别计算“Male”和“Female”的选择率、精确度等信息并进行对比。

本文示例中,基于Sklearn对模型进行评估:

Sklearn

from raiwidgets import FairnessDashboard
import os
from urllib.parse import urlparse
# 根据测试数据集进行预测结果
y_pred_sk = sk_model.predict(X_test)

# 使用 responsible-ai-toolbox  计算每个敏感群体的数据信息
metric_frame_sk = FairnessDashboard(sensitive_features=sensitive_features_test,
                  y_true=y_test,
                  y_pred=y_pred_sk, locale = 'zh-Hans')

# 设置URL跳转链接
metric_frame_sk.config['baseUrl'] =  'https://{}-proxy-{}.dsw-gateway-{}.data.aliyun.com'.format(
    os.environ.get('JUPYTER_NAME').replace("dsw-",""),
    urlparse(metric_frame_sk.config['baseUrl']).port,
    os.environ.get('dsw_region') )
print(metric_frame_sk.config['baseUrl'])

PyTorch

from raiwidgets import FairnessDashboard
import torch
import os
from urllib.parse import urlparse
# 测试模型并评估公平性
pt_model.eval()  # 设置模型为评估模式
X_test_pt = X_test
with torch.no_grad():
    X_test_pt = torch.tensor(X_test_pt.values)
    X_test_pt = X_test_pt.float()
    y_pred_pt = pt_model(X_test_pt).numpy()

# 使用 responsible-ai-toolbox  计算每个敏感群体的数据信息
metric_frame_pt = FairnessDashboard(sensitive_features=sensitive_features_test,
                           y_true=y_test,
                           y_pred=y_pred_pt.flatten().round(),locale='zh-Hans')

# 设置URL跳转链接
metric_frame_pt.config['baseUrl'] =  'https://{}-proxy-{}.dsw-gateway-{}.data.aliyun.com'.format(
    os.environ.get('JUPYTER_NAME').replace("dsw-",""),
    urlparse(metric_frame_pt.config['baseUrl']).port,
    os.environ.get('dsw_region') )
print(metric_frame_pt.config['baseUrl'])

TensorFlow

from raiwidgets import FairnessDashboard
import os
from urllib.parse import urlparse
# 测试模型并评估公平性
y_pred_tf = tf_model.predict(X_test).flatten()

# 使用 responsible-ai-toolbox  计算每个敏感群体的数据信息
metric_frame_tf = FairnessDashboard(
                           sensitive_features=sensitive_features_test,
                           y_true=y_test,
                           y_pred=y_pred_tf.round(),locale='zh-Hans')

# 设置URL跳转链接
metric_frame_tf.config['baseUrl'] =  'https://{}-proxy-{}.dsw-gateway-{}.data.aliyun.com'.format(
    os.environ.get('JUPYTER_NAME').replace("dsw-",""),
    urlparse(metric_frame_tf.config['baseUrl']).port,
    os.environ.get('dsw_region') )
print(metric_frame_tf.config['baseUrl'])

关键参数说明:

  • sensitive_features:敏感属性。

  • y_true:训练数据集提供的真实结果。

  • y_pred:模型预测的结果。

  • locale(可选):评估面板显示语言类型,支持简体中文(“zh-Hans”)和繁体中文(“zh-Hant”),默认为英语(“en”)。

步骤七:查看评估报告

评估完成后,单击URL,查看完整的评估报告。

image

公平性仪表板页面单击开始使用,按照以下参数依次设置敏感特征、性能指标、公平性指标,查看模型针对敏感特征“sex”或“race”的指标数据差异。

敏感特征为“sex”

  • 敏感特征:sex

  • 性能指标:Accuracy

  • 公平性指标:Demographic parity difference

image

  • Accuracy:精确度,是指模型正确预测的样本数占总样本数的比例。模型输出男性个人年收入的准确度(81.6%)低于女性个人年收入的准确度(93.1%),但是两者的准确度与模型总体准确度(85.4%)都比较接近。

  • Selection rate:选择率,是指模型输出“好”的目标变量的概率,这里“好”的目标变量是个人年收入>50K。模型输出男性个人年收入>50K的概率(25.7%)大于女性个人年收入>50K的概率(7.36%),并且模型输出女性个人年收入>50K的概率(7.36%)远低于个人年收入>50K的总体概率(19.6%)。

  • Demographic parity difference:人口统计公平性差异,是指不同受保护群体之间,在指定的敏感属性下获得积极预测结果的概率差异。这个值越接近0,意味着群体间的偏差越小。模型总体的人口统计公平性差异为18.3%。

敏感特征为“race”

  • 敏感特征:race

  • 性能指标:Accuracy

  • 公平性指标:Demographic parity difference

image

  • Accuracy:精确度,是指模型正确预测的样本数占总样本数的比例。模型输出“White”和“Asian-Pac-Islander”个人年收入的准确度(84.9%和79.1%)低于“Black”、“Other”和“Amer-Indian-Eskimo”个人年收入的准确度(90.9%、90.8%和91.9%),但是所有敏感特征人群的准确度与模型总体准确度(85.4%)都比较接近。

  • Selection rate:选择率,是指模型输出“好”的目标变量的概率,这里“好”的目标变量是个人年收入>50K。模型输出“White”和“Asian-Pac-Islander”个人年收入>50K的选择率(21%和23.9%)大于“Black”、“Other”和“Amer-Indian-Eskimo”个人年收入>50K的选择率 (8.34%、10.3%和8.14%),并且“Black”、“Other”和“Amer-Indian-Eskimo”个人年收入>50K的选择率(8.34%、10.3%和8.14%)远低于个人年收入>50K的总体选择率(19.6%)。

  • Demographic parity difference:人口统计公平性差异,是指不同受保护群体之间,在指定的敏感属性下获得积极预测结果的概率差异。这个值越接近0,意味着群体间的偏差越小。模型总体的人口统计公平性差异为15.8%。