Responsible AI-错误分析

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

原理介绍

错误分析作为Responsible 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框架的回归和二分类模型。

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

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

步骤一:进入DSW Gallery

  1. 登录PAI控制台

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

  3. 在左侧导航栏选择大数据与AI体验 > DSW Gallery,搜索“Responsible AI-错误分析”并单击对应卡片上的在阿里云DSW打开

  4. 选择AI工作空间DSW实例,单击确定,系统会打开“Responsible AI-错误分析”Notebook案例。

步骤二:导入依赖包

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

!pip install raiwidgets==0.34.1

导入Responsible AISklearn依赖包,用于后续的训练。

# 导入Response AI相关依赖包

import zipfile
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer

import pandas as pd
from lightgbm import LGBMClassifier
from raiutils.dataset import fetch_dataset
import sklearn
from packaging import version
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer

步骤三:准备数据集

下载并解压人口普查数据集,解压后包含训练数据adult-train.csv和测试数据adult-test.csv。

# 数据集文件名称
outdirname = 'responsibleai.12.28.21'
zipfilename = outdirname + '.zip'

# 下载数据集,并进行解压
fetch_dataset('https://publictestdatasets.blob.core.windows.net/data/' + zipfilename, zipfilename)
with zipfile.ZipFile(zipfilename, 'r') as unzip:
    unzip.extractall('.')

步骤四:数据预处理

  1. 加载训练数据adult-train.csv和测试数据adult-test.csv。

  2. 把训练数据和测试数据分别拆分为特征变量和目标变量。目标变量是指模型预测的真实结果,特征变量是指每条实例数据中目标变量以外的变量。在本文示例中,目标变量是income,特征变量是workclasseducationmarital-status等。

  3. 将训练数据转换为NumPy数据格式,用于训练。

# 加载训练数据和测试数据
train_data = pd.read_csv('adult-train.csv', skipinitialspace=True)
test_data = pd.read_csv('adult-test.csv', skipinitialspace=True)


# 定义特征变量和目标变量所在的列
target_feature = 'income'
categorical_features = ['workclass', 'education', 'marital-status',
                        'occupation', 'relationship', 'race', 'gender', 'native-country']

# 定义函数:用于拆分特征变量和目标变量
def split_label(dataset, target_feature):
    X = dataset.drop([target_feature], axis=1)
    y = dataset[[target_feature]]
    return X, y


# 拆分特征变量和目标变量
X_train_original, y_train = split_label(train_data, target_feature)
X_test_original, y_test = split_label(test_data, target_feature)


# 转换为numpy格式
y_train = y_train[target_feature].to_numpy()
y_test = y_test[target_feature].to_numpy()

# 定义测试样本
test_data_sample = test_data.sample(n=500, random_state=5)

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

import pandas as pd

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

步骤五:模型训练

本文示例中,基于Sklearn定义一个数据训练流水线并训练一个二分类模型。

# 根据scikit-learn的不同版本定义ohe_params参数
if version.parse(sklearn.__version__) < version.parse('1.2'):
    ohe_params = {"sparse": False}
else:
    ohe_params = {"sparse_output": False}

# 定义分类流水线,进行特征转换,输入参数X表示训练集的数据    
def create_classification_pipeline(X):
    pipe_cfg = {
        'num_cols': X.dtypes[X.dtypes == 'int64'].index.values.tolist(),
        'cat_cols': X.dtypes[X.dtypes == 'object'].index.values.tolist(),
    }
    num_pipe = Pipeline([
        ('num_imputer', SimpleImputer(strategy='median')),
        ('num_scaler', StandardScaler())
    ])
    cat_pipe = Pipeline([
        ('cat_imputer', SimpleImputer(strategy='constant', fill_value='?')),
        ('cat_encoder', OneHotEncoder(handle_unknown='ignore', **ohe_params))
    ])
    feat_pipe = ColumnTransformer([
        ('num_pipe', num_pipe, pipe_cfg['num_cols']),
        ('cat_pipe', cat_pipe, pipe_cfg['cat_cols'])
    ])

    pipeline = Pipeline(steps=[('preprocessor', feat_pipe),
                               ('model', LGBMClassifier(random_state=0))])

    return pipeline
    
# 创建分类模型训练流水线
pipeline = create_classification_pipeline(X_train_original)

# 模型训练
model = pipeline.fit(X_train_original, y_train)

步骤六:添加Responsible AI组件

执行以下脚本,为Responsible AI添加了错误分析组件,并通过rai_insights进行计算。

# 导入RAI仪表盘组件
from raiwidgets import ResponsibleAIDashboard
from responsibleai import RAIInsights

# 定义RAIInsights对象
from responsibleai.feature_metadata import FeatureMetadata
feature_metadata = FeatureMetadata(categorical_features=categorical_features, dropped_features=[])
rai_insights = RAIInsights(model, train_data, test_data_sample, target_feature, 'classification',
                           feature_metadata=feature_metadata)

# 添加错误分析组件
rai_insights.error_analysis.add()

# RAI计算
rai_insights.compute()

步骤七:创建Responsible AI仪表盘

  1. 通过不同的过滤条件,创建不同的数据分组,可以对过滤出的不同的数据分组进行错误分析,例如:

  • 年纪 (age) 小于65岁且每周工作时间 (hours-per-week) 大于40小时。

  • 婚姻状态 (marital-status) 为"Never-married" 或者 "Divorced"。

  • 数据分组索引 (Index) 小于20。

  • 预测值(Predicted Y)大于50K。

  • 实际值 (True Y) 大于50K。

  1. 引入ResponsibleAIDashboard仪表盘,使用responsible-ai-toolbox对模型进行分析。

from raiutils.cohort import Cohort, CohortFilter, CohortFilterMethods
import os
from urllib.parse import urlparse

# 年纪 (age) 小于65岁且每周工作时间 (hours-per-week) 大于40小时
cohort_filter_age = CohortFilter(
    method=CohortFilterMethods.METHOD_LESS,
    arg=[65],
    column='age')
cohort_filter_hours_per_week = CohortFilter(
    method=CohortFilterMethods.METHOD_GREATER,
    arg=[40],
    column='hours-per-week')

user_cohort_age_and_hours_per_week = Cohort(name='Cohort Age and Hours-Per-Week')
user_cohort_age_and_hours_per_week.add_cohort_filter(cohort_filter_age)
user_cohort_age_and_hours_per_week.add_cohort_filter(cohort_filter_hours_per_week)

# 婚姻状态 (marital-status) 为"Never-married" 或者 "Divorced"
cohort_filter_marital_status = CohortFilter(
    method=CohortFilterMethods.METHOD_INCLUDES,
    arg=["Never-married", "Divorced"],
    column='marital-status')

user_cohort_marital_status = Cohort(name='Cohort Marital-Status')
user_cohort_marital_status.add_cohort_filter(cohort_filter_marital_status)

# 数据分组索引 (Index) 小于20
cohort_filter_index = CohortFilter(
    method=CohortFilterMethods.METHOD_LESS,
    arg=[20],
    column='Index')

user_cohort_index = Cohort(name='Cohort Index')
user_cohort_index.add_cohort_filter(cohort_filter_index)

# 预测值(Predicted Y)大于50K
cohort_filter_predicted_y = CohortFilter(
    method=CohortFilterMethods.METHOD_INCLUDES,
    arg=['>50K'],
    column='Predicted Y')

user_cohort_predicted_y = Cohort(name='Cohort Predicted Y')
user_cohort_predicted_y.add_cohort_filter(cohort_filter_predicted_y)

# 实际值 (True Y) 大于50K
cohort_filter_true_y = CohortFilter(
    method=CohortFilterMethods.METHOD_INCLUDES,
    arg=['>50K'],
    column='True Y')

user_cohort_true_y = Cohort(name='Cohort True Y')
user_cohort_true_y.add_cohort_filter(cohort_filter_true_y)

cohort_list = [user_cohort_age_and_hours_per_week,
               user_cohort_marital_status,
               user_cohort_index,
               user_cohort_predicted_y,
               user_cohort_true_y]

# 创建Responsible AI仪表盘
metric_frame_tf = ResponsibleAIDashboard(rai_insights, cohort_list=cohort_list, feature_flights="dataBalanceExperience")

# 设置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') )

步骤八:访问Responsible AI仪表盘,查看错误分析

单击URL,访问Responsible AI仪表盘。

image

查看错误分析:

树形图(Tree Map)

image

  1. 单击Tree map,在Select metric中选择Error rate,进行错误性分析。错误分析树形视图根据模型的所有特征,以二叉树方式按照特征的不同数值进行拆分。例如,该树形根节点下的二叉树两个分支分别表示:

    • marital-status == Married-civ-spouse(54/224)

    • marital-status != Married-civ-spouse(18/276)

  2. 本次示例包含500个样本,有72个预测错误,则错误率为72/500 = 14.4%。每个二叉树的节点展示了满足该分支条件的数据总量以及预测错误的数量和错误比例。

  3. 您需要重点关注红色的节点,红色越深,表明错误率(error rate)越高。

  4. 在本示例中,单击红色最深的叶子节点,可以看出同时满足以下条件的数据,模型预测错误比例高达43.40%。条件如下:

    • marital-status == Married-civ-spouse

    • fnlwgt <= 207583

    • hours-per-week > 40.5

热度图(Heat Map)

image

  1. 单击Heat map,切换到热度图分析视图,在Select metric中选择Error rate,进行错误性分析。

  2. (可选)配置参数:

    • Quantile binning:是一种将连续变量分成若干个具有相同数据点的区间的方法。

      • 设置为OFF,表示关闭。采用默认均匀分段策略,每个区间的长度保持相同。

      • 设置为ON,表示打开。每个区间包含相同数量的数据点,使得数据在每个区间中均匀分布。

    • Binning threshold:表示将数据分割的区间个数,调整threshold可调整分割的区间数量(在本示例中默认为8,表示将agehours-per-week平均分割为8个区间)。

  3. 在热度图中,您可以选定2个输入特征进行数据交叉分析。本示例中选定age、hours-per-week 进行热度图分析。

  4. 您需要重点关注红色的节点,红色越深,表明错误率(error rate)越高。

  5. 从分析结果看,两个特征在以下分布区间的错误率最高,高达 100%:

    • age[71.8,80.9]、hours-per-week[39.0,51.0]

    • age[44.4,53.5]、hours-per-week[75.0,87.0]

    • age[16.9,26.1]、hours-per-week[63.0,75.0]

    • ...