百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

为什么机器学习模型会失败?(为什么机器学习模型会失败说明了)

toyiye 2024-08-19 22:27 13 浏览 0 评论

本文最初发表于 Towards Data Science 博客,经原作者 Delgado Panadero 授权,InfoQ 中文站翻译并分享。


本文通过一个真实的例子,分析了模型选择不当还是训练数据噪声导致了模型性能不佳。

前言

在机器学习中,当你建立和训练一个模型并检验其准确性时,一个最常见的问题就是“准确性是我能从数据中得到的最好的,还是能找到一个更好的模型呢?”


此外,一旦模型被部署,下一个常见的问题就是“为什么模型会失败?”。有时候,这两个问题都无法回答,但有时我们可以通过研究模型误差的统计分布,找出预处理错误、模型偏差,以及数据泄露等。


在本教程中,我们将解释并演示如何统计分析模型结果,以找出示例中错误的原因。

业务案例

在这个案例中,我们将使用来自 Driven Data 竞赛的数据,通过一系列社会经济变量来预测一个民族是否处于贫困状态。


这个业务案例的价值不仅在于能够用机器学习模型来预测贫困状况,而且还在于通过社会经济变量对衡量贫困状态的预测程度,并从特征上分析原因。

模型训练

数据由一组九个描述性变量组成,其中四个是类别变量,另外五个是数值变量(但其中一个似乎是一个 id,所以我们将舍弃它)。


import pandas as pd


pd.set_option('display.max_columns', None)
train = pd.read_csv('train.csv', index_col='id')
print(train)

返回结果如下:

Unnamed: 0 kjkrfgld bpowgknt raksnhjf vwpsxrgk  omtioxzz  yfmzwkru
id                                                                          
29252  2225    KfoTG    zPfZR    DtMvg      NaN      12.0      -3.0   
98286  1598    ljBjd    THHLT    DtMvg    esAQH      21.0      -2.0   
49040  7896    Lsuai    zPfZR    zeYAm    ZCIYy      12.0      -3.0   
35261  1458    KfoTG    mDadf    zeYAm    ZCIYy      12.0      -1.0   
98833  1817    KfoTG    THHLT    DtMvg    ARuYG      21.0      -4.0   


       tiwrsloh  weioazcf   poor  
id                                
29252      -1.0       0.5  False  
98286      -5.0      -9.5   True  
49040      -5.0      -9.5   True  
35261      -5.0      -9.5  False  
98833      -5.0      -9.5   True  

数据分布可以在下面看到:


图由作者提供。数据集中所有特征的配对图,以目标为颜色。黄色块代表 False,紫色块表示 True。


通过某些预处理(NaN 值插补、缩放、分类编码等等),我们将对一个支持向量机模型进行训练(通常在独热编码的高维数据中工作良好)。

支持向量机

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import RobustScaler
from sklearn.neighbors import KNeighborsClassifier


model = Pipeline(steps=preprocess+[
                 ('scaler', RobustScaler()),
                 ('estimator', KNeighborsClassifier(n_neighbors=5))])


model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(classification_report(y_test,y_pred))`

返回结果如下:

precision    recall  f1-score   support


       False       0.73      0.77      0.75       891
        True       0.70      0.66      0.68       750


    accuracy                           0.72      1641
   macro avg       0.72      0.71      0.71      1641
weighted avg       0.72      0.72      0.72      1641

就二元分类问题而言,0.72 的准确率并不高。相比之下,召回率和查准率看起来是平衡的,这使得我们认为,这个模型不是一个有利于任何类别的先验偏见。

测试其他模型

想要改进这个模型,下一步就是尝试其他机器学习模型和超参数,看看我们是否找到任何可以提高性能的配置(甚至只是检查性能是否保持稳定)。


在不同的函数族集中,我们将使用另外两个模型。KNN 模型,对于学习局部模型的影响是一个很好的选择,还有梯度提升树,它也是机器学习中容量最大的模型之一。

KNN

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import RobustScaler
from sklearn.neighbors import KNeighborsClassifier


model = Pipeline(steps=preprocess+[
                 ('scaler', RobustScaler()),
                 ('estimator', KNeighborsClassifier(n_neighbors=5))])


model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(classification_report(y_test,y_pred))

返回结果如下:

precision    recall  f1-score   support


       False       0.71      0.74      0.72       891
        True       0.67      0.63      0.65       750


    accuracy                           0.69      1641
   macro avg       0.69      0.69      0.69      1641
weighted avg       0.69      0.69      0.69      1641

梯度提升

from sklearn.pipeline import Pipeline
from sklearn.ensemble import GradientBoostingClassifier


model = Pipeline(steps=preprocess+[
                 ('estimator', 
                  GradientBoostingClassifier(max_depth=5,
                                             n_estimators=100))])


model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(classification_report(y_test,y_pred))

返回结果如下:


precision    recall  f1-score   support


       False       0.76      0.78      0.77       891
        True       0.73      0.70      0.72       750


    accuracy                           0.74      1641
   macro avg       0.74      0.74      0.74      1641
weighted avg       0.74      0.74      0.74      1641

我们可以看到,其他两个模型的表现似乎都非常相似。这就提出了以下问题:

这就是我们用机器学习模型所能预测的最好结果吗?

模型预测分布

除了检查性能的一般指标外,分析模型的输出分布也很重要。不但要检查测试数据集的分布,也要检查训练数据集的分布。这是因为我们不想看到模型的表现,而是想看看它是否也学会了如何分割训练数据。


import matplotlib.pyplot as plt


pd.DataFrame(model.predict_proba(X_train))[1].hist()
plt.show()



图由作者提供。对训练集进行评估的模型输出分布。


pd.DataFrame(model.predict_proba(X_test))[1].hist()
plt.show()


图由作者提供。对测试集进行评估的模型输出分布。


可见,预测为 0 的数量具有较高的峰值,这表示存在一个数据子集,模型非常确定它的标签是 0,除此之外,分布看起来比较均匀。


如果模型知道一定要区分这两个标签,分布会有两个峰值,一个在 0 附近,另一个在 1 附近。因此,我们可以看到,模型并没有正确地学习模式来区分数据。

偏差分布

我们已经看到,该模型还没有学会明确地区分这两个类别,但我们还没有看到它是否在不自信的情况下也能猜到预测结果,还是一直失败。


此外,重要的是要检查模型是否更倾向于一类或另一类的失败。为检验这两个方面,我们可以绘制预测值与目标值偏差的分布图:


train_proba = model.predict_proba(X_train)[:,1]
pd.DataFrame(train_proba-y_train.astype(int)).hist(bins=50)
plt.show()



图由作者提供。通过训练集评估的模型置信度输出与基准真相的偏差。


test_proba = model.predict_proba(X_test)[:,1]
pd.DataFrame(test_proba-y_test.astype(int)).hist(bins=50)
plt.show()



图由作者提供。通过测试集评估的模型置信度输出与基准真相的偏差。


从这两张图中,我们可以看到,偏差分布似乎是对称的,并且以零点为中心。差距只是在零点,因为模型从来没有返回 0 和 1 的准确值,所以我们不必担心这个问题。


如果模型的误差来自于训练数据的统计/测量噪声误差,而不是偏置误差,则我们会期望偏差分布遵循高斯分布。


这一分布与高斯分布相似,在零点处有一个较高的峰值,但这个峰值可能是因为模型预测的零点数量较多(也就是说,模型已经学会了一种模式来区分 0 和 1 类别的子集)。

验证正态性

由于训练数据中存在的统计噪声,我们必须确保模型预测的偏差符合高斯分布,然后才能证明其偏差。


import scipy


scipy.stats.normaltest(train_proba-y_train.astype(int))



返回结果如下:


NormaltestResult(statistic=15.602215177113427, pvalue=0.00040928141243470884)

当 P-value=0.0004 时,我们可以假设预测与目标的偏差遵循高斯分布,这样从训练数据中的噪声导致模型误差的理论是合理的。

模型可解释性

如前所述,这一业务案例的目的不仅仅是要预测模型发生的原因,还包括与之相关的社会经济变量。


可解释的模型不仅能预测未见过的数据,还能让你了解特征如何影响模型(全局可解释性),以及为什么某些预测会如此(局部可解释性)。


尽管如此,一个模型的可解释性仍然可以帮助我们理解为什么它能做出预测,以及为什么它会失败。从梯度提升模型中,我们可以提取全局可解释性如下:


cols = X_train.columns
vals= dict(model.steps)['estimator'].feature_importances_


plt.figure()
plt.bar(cols, vals)
plt.show()



图由作者提供。梯度提升特征输入。


接下来,我们将进行相同的特征重要性分析,但是只对数据的一个子集进行训练。具体地说,我们将只使用明显为零的数据(那些模型之前明确预测为零的数据)来训练模型的零类别。


zero_mask = model.predict_proba(X_train)[:,1]<=0.1
one_mask = y_train==1
mask = np.logical_or(zero_mask,one_mask)
X_train = X_train.loc[mask,:]
y_train = y_train.loc[mask]
model.fit(X_train,y_train)


现在特征的重要性是:



图由作者提供。在模型表现最好的训练集子样本上训练的梯度提升特征导入。


我们可以看到,现在,tiwrslohyfmzwkru 这两个变量的重要性增加了,而 vwpsxrgk 的数值却下降了。这意味着,拥有一个子集的人口显然不是穷人(类别 0),可以通过这两个变量从穷人的变量和 vwpsxrgk 在许多情况下可能很重要,但不具备决定性。


如果我们绘制这两个特征的过滤值,我们可以看到:



图由作者提供,对模型明确检测到非贫困的特征区域进行分割并表征。


对于这两个特征,模型已经学会了区分两个类别,同时,对于这些变量的其他值,在整个数据集中,类别 0 和类别 1 是混合的,所以不能明确区分。


我们还可以从前面的图表中找出一个明显的非贫困人口子集的特征,即 tiwrsloh<0yfmzwkru<-2 的人。

总结

我们分析了在给定的数据集中检测贫困的问题,并在给定的社会经济数据中分析其原因,发现贫困并不容易预测,但是,我们可以对某些特定数据进行定义,以明确确定人们的贫困状况:tiwrsloh<0yfmzwkru<-2


我们尝试了许多不同的模型和配置,在 0.75 的时候性能就会处于平稳状态。通过这一点,再加上模型预测和误差偏差分布的统计学性质,我们可以得出结论,问题在于缺乏从训练数据中预测目标的能力。所以不可能建立一个更好的模型。

我们学到了什么?

我们用一个真实的例子解决了模型不能得到足够好的结果的问题。在这种情况下,我们的目标是尝试了解模型无法理解的问题是在数据中还是在模型中。


回答这一问题的过程是:


  1. 尝试不同的函数族模型和超参数,并确认所有的下降在性能上处于平稳状态。
  2. 为得到最好的解释,计算模型的输出分布和目标的偏差分布。如果数据是问题所在,则输出必须是均匀的,并且偏差必须遵循高斯分布。
  3. 尽管数据是问题所在,但试着从模型输出和偏差分布中找到一个模型表现良好的区域。尝试对这个区域进行分割和定性,例如,用这个子集重新训练模型,并提取其可解释性。
  4. 此外,在表征某些子集时,我们可以尝试从业务知识中思考,问题是来自数据的统计/测量噪音,还是来自缺乏预测因变量所需的一些特征值。


作者介绍:


Delgado Panadero,研究人工智能的物理学家。

相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码