用Scikit-Learn对回归模型进行评分

641 阅读8分钟

用Scikit-Learn进行回归模型评分

2022年6月26日 数据 编码 机器学习 python

分享到。

目录

绪论

在上一篇教程中,我们看了评估分类模型的各种方法,以及解释每个指标的微妙方式。

在本教程中,我们看一下回归模型的评估,幸运的是,这要直接得多。没有诸如 "假阳性 "之类令人伤脑筋的概念,也没有根据上下文有不同名称的指标,如召回率,又称真阳性率,又称敏感性。

所有的回归指标只是比较实际值和预测值阵列的不同方式--就像在分类评估的情况下--所以我们不需要关注模型的选择和数据的拟合,尽管我们用线性预测来进行说明。

我们将介绍关键的五个指标,我们可以用它们来评估回归模型的性能。

  • 平均绝对误差(MAE)--最容易推理的,但有点直白。
  • 平均平方误差(MSE)--这个 "标准 "可以惩罚异常值的好坏。
  • 方根误差(RMSE)--就像均方根误差,但没有 "扩大 "数值。
  • 中位绝对误差(MedAE)--在比尔-盖茨上车的情况下,这是一个更好的MAE......
  • R2得分--所有用例的首选指标,除非有充分的理由不这样做。

对于每个指标,我们将显示三个图表,反映三个数据集/情景。

  1. 一个 "基线 "相当线性的数据集,其值接近于平均值
  2. 同样的基线数据集*,有正的离群值*
  3. 同样的基线数据集*,带有负的离群值*

每次我们讨论其中一个指标时,我们都会针对这三个数据集显示所有五个指标的结果,这样我们就可以随时进行比较。

义务性输入和模板

 1import random
 2import numpy as np
 3import pandas as pd
 4import matplotlib.pyplot as plt
 5from IPython.display import display, Markdown
 6from sklearn.model_selection import train_test_split
 7from sklearn.metrics import r2_score
 8from sklearn.metrics import mean_squared_error
 9from sklearn.metrics import mean_absolute_error
10from sklearn.metrics import median_absolute_error
11from sklearn.linear_model import LinearRegression

数据集和可视化

下面的代码创建了三个样本数据集,以及所讨论的五个指标中每个指标的结果。*在本教程中,你不需要理解下面的代码,*它在这里只是为了方便,所以请随意跳过。

 1from sklearn.metrics import r2_score
 2from sklearn.metrics import mean_squared_error
 3from sklearn.metrics import mean_absolute_error
 4from sklearn.metrics import median_absolute_error
 5
 6samples = 100
 7np.random.seed(0)
 8
 9def add_outliers(array, step):
10    return ([ v+(step/41) if i % step == 0 else v 
11             for i,v in enumerate(array) ])
12
13x = np.linspace(0.0,1.0,samples) 
14r = np.random.rand(samples)    
15y = np.array(list(map(lambda t: t[0]/2 + t[1]/5, zip(x,r))))
16y_plus  = add_outliers(y,15)
17y_minus = add_outliers(y,-15)
18X = x.reshape(samples,-1) 
19
20# Baseline
21X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=0)
22model = LinearRegression().fit(X_train,y_train)
23y_pred = model.predict(X_test)
24
25# Positive Outliers
26X_train_plus, X_test_plus, y_train_plus, y_test_plus = train_test_split(X,y_plus,random_state=0)
27model_plus = LinearRegression().fit(X_train_plus,y_train_plus)
28y_pred_plus = model.predict(X_test_plus)
29
30# Negative Outliers
31X_train_minus, X_test_minus, y_train_minus, y_test_minus = train_test_split(X,y_minus,random_state=0)
32model_minus = LinearRegression().fit(X_train_minus,y_train_minus)
33y_pred_minus = model.predict(X_test_minus)
34
35def visualise():
36    plt.rcParams['figure.figsize'] = [16, 4]
37    plt.rcParams['figure.dpi'] = 100 
38    
39    ax = plt.subplot(1, 3, 1)
40    plt.title("Baseline")
41    plt.scatter(X,y,s=0.5)
42    plt.plot(X,model.predict(X),'-r', label="Prediction")
43    plt.legend(loc='upper left')
44    
45    plt.subplot(1, 3, 2, sharey=ax)
46    plt.title("Positive outliers")
47    plt.scatter(X,y_plus,s=0.5)
48    plt.plot(X,model_plus.predict(X),'-r', label="Prediction")
49    plt.legend(loc='upper left')
50    
51    plt.subplot(1, 3, 3, sharey=ax)
52    plt.title("Negative outliers")
53    plt.scatter(X,y_minus,s=0.5)
54    plt.plot(X,model_minus.predict(X),'-r', label="Prediction")
55    plt.legend(loc='upper left')
56    plt.show()
57    
58    print("                   MAE        MSE        RMSE       MedAE      R2")
59    print("         Baseline: {:.8f} {:.8f} {:.8f} {:.8f} {:.8f}"
60        .format(mean_absolute_error(y_test, y_pred),
61                mean_squared_error(y_test,y_pred),
62                mean_squared_error(y_test,y_pred,squared=False),
63                median_absolute_error(y_test,y_pred),
64                r2_score(y_test,y_pred)))
65    print("Positive Outliers: {:.8f} {:.8f} {:.8f} {:.8f} {:.8f}"
66        .format(mean_absolute_error(y_test_plus, y_pred_plus),
67                mean_squared_error(y_test_plus,y_pred_plus),
68                mean_squared_error(y_test_plus,y_pred_plus,squared=False),
69                median_absolute_error(y_test_plus,y_pred_plus),
70                r2_score(y_test_plus,y_pred_plus)))
71    print("Negative Outliers: {:.8f} {:.8f} {:.8f} {:.8f} {:.8f}"       
72        .format(mean_absolute_error(y_test_minus, y_pred_minus),
73                mean_squared_error(y_test_minus,y_pred_minus),
74                mean_squared_error(y_test_minus,y_pred_minus,squared=False),
75                median_absolute_error(y_test_plus,y_pred_minus),
76                r2_score(y_test_minus,y_pred_minus)))    
77

回归评价指标

平均绝对误差(MAE)

平均绝对误差(MAE)是用mean_absolute_error(y_actual,y_pred) 函数得到的。作为一个误差函数,越接近零就越好。

这是评价回归模型的最简单指标,因为它包括计算实际值和预测值之间所有绝对距离的平均值。

Calculating MAE for two values

现在让我们来看看使用MAE对更大数据集的影响。

1visualise()

                   MAE        MSE        RMSE       MedAE      R2
         Baseline: 0.05046426 0.00339777 0.05829042 0.05499852 0.84103010
Positive Outliers: 0.08362021 0.01648675 0.12840072 0.05757718 0.53651638
Negative Outliers: 0.09174612 0.02243254 0.14977495 0.05757718 0.36711532

当观察值和预测值之间的差异的影响是线性的时候,这个指标是合适的。例如,如果实际值是2,而预测值是7,就会偏离'5'点。如果预测值是12,它就会偏离'10'点。

我们可以用下面的公式自己计算平均绝对误差。

1def mae(y_actual,y_pred):
2    r = np.mean([ abs(ya-yp) for ya,yp in zip(y_actual,y_pred)  ])
3    # r = np.mean(abs(y_actual-y_pred)) # Pandas-idiomatic way
4    return r 
5mae(y_test,y_pred) # baseline 
0.0504642646765226

平均平方误差(MSE)

平均平方误差(MSE)是用mean_squared_error(y_actual,y_pred) 函数得到的。作为一个误差函数,越接近零就越好。

这个指标有点像MAE,只是实际值和预测值之间的差异是通过对结果进行平方转换而不是通过应用abs()--对不起,数学界人士的pythonic解释!

Calculating MSE for two values

通过对差异进行平方计算,异常值对结果的惩罚比MAE更大,但仅在非分数差异的情况下。稍后会有更多关于这方面的内容。

让我们先来看看应用于更大数据集的MSE。

1visualise()

                   MAE        MSE        RMSE       MedAE      R2
         Baseline: 0.05046426 0.00339777 0.05829042 0.05499852 0.84103010
Positive Outliers: 0.08362021 0.01648675 0.12840072 0.05757718 0.53651638
Negative Outliers: 0.09174612 0.02243254 0.14977495 0.05757718 0.36711532

我们可以用下面的公式自己计算出平均平方误差。

1def mse(y_actual,y_pred):
2    r = np.mean([ (ya-yp)**2 for ya,yp in zip(y_actual,y_pred)  ])
3    #r = np.mean((y_actual - y_pred)**2) # Numpy-idiomatic way
4    return r
5mse(y_test,y_pred) # baseline
6
0.0033977735623684923

分数距离的问题

如果我们看一下实际值和预测值之间的距离,我们可以观察到大多数是小数(即低于1.0的值)。

1distances = [ (ya-yp) for ya,yp in zip(y_test,y_pred) ]
2distances[:10]
[-0.08240320001531051, 0.02421857651852455, -0.002778311611712825, -0.0640012559810067, -0.07821132666637365, 0.06640940270316664, -0.11213731522943875, 0.03389879138769458, -0.05499852206107747, -0.039196338275046205]

当我们对一个小数进行平方时,我们得到的是一个较小的数字,而不是一个较大的数字!因此,平方的距离是比较小的。

110**2
100
10.1**2
0.010000000000000002

因此,平方的距离比绝对的距离要小!

1pd.DataFrame({"distances" : distances[:10],
2              "absolute": list(map(abs, distances))[:10],
3              "squared": list(map(lambda x: x**2, distances))[:10]
4})
距离绝对值平方
0-0.0824030.0824030.006790
10.0242190.0242190.000587
2-0.0027780.0027780.000008
3-0.0640010.0640010.004096
4-0.0782110.0782110.006117
50.0664090.0664090.004410
6-0.1121370.1121370.012575
70.0338990.0338990.001149
8-0.0549990.0549990.003025
9-0.0391960.0391960.001536

请看,如果我们把数值放大到100,我们就 "解决 "了这个 "问题",MSE报告的分数比MAE更苛刻(离0.0更远),这通常是人们的期望。

1mean_absolute_error(y_test * 100, y_pred * 100)
5.0464264676522586
1mean_squared_error(y_test * 100, y_pred * 100)
33.97773562368493

总之,虽然MSE与MAE相比,已知允许异常值施加更大的影响,但当距离低于1.0时,情况并非如此。

均方根误差(RMSE)

这与MSE完全一样,只是先将平方根应用于结果,这样误差就在与数据集的规模相称的范围内了。

对于RMSE,我们只需要将squared=False 额外的参数传递给mean_square_root(y_actual,y_pred) ,如下所示。

1mean_squared_error(y_test,y_pred,squared=False) # baseline
0.0582904242767926

现在让我们看看RMSE的运行情况。

1visualise()

                   MAE        MSE        RMSE       MedAE      R2
         Baseline: 0.05046426 0.00339777 0.05829042 0.05499852 0.84103010
Positive Outliers: 0.08362021 0.01648675 0.12840072 0.05757718 0.53651638
Negative Outliers: 0.09174612 0.02243254 0.14977495 0.05757718 0.36711532

如果我们想自己实现RMSE,它的代码与mse()加sqrt函数相同。

1def rmse(y_actual,y_pred):
2    return np.sqrt(mse(y_actual,y_pred))
3rmse(y_test,y_pred) # baseline
0.0582904242767926

中位绝对误差(MedAE)

中位绝对误差(MedAE)是用median_absolute_error(y_actual,_y_pred) 函数得到的。作为一个误差函数,越接近零就越好。

1visualise()

                   MAE        MSE        RMSE       MedAE      R2
         Baseline: 0.05046426 0.00339777 0.05829042 0.05499852 0.84103010
Positive Outliers: 0.08362021 0.01648675 0.12840072 0.05757718 0.53651638
Negative Outliers: 0.09174612 0.02243254 0.14977495 0.05757718 0.36711532

MedAE可以看作是MAE的另一种变化,但这种变化并不在于如何使实际和预测之间的差异成为正数,而在于简单地使用median() ,而不是mean(),来总结结果--我再次向数学人道歉。

这种方法最大限度地减少了可能位于极端的异常值的影响,但如果我们确实希望这些异常值被计入分数,它可能会造成问题......

如果比尔-盖茨上了一辆公共汽车,普通乘客变成了百万富翁,但中位数乘客的净资产基本没有变化。

我们可以用下面的公式自己计算MedAE。

1def medae(y_actual,y_pred):
2    r = np.median([ abs(ya-yp) for ya,yp in zip(y_actual,y_pred)  ])
3    #r = np.median(abs(y_actual-y_pred)) # Pandas-idiomatic way
4    return r
5medae(y_test,y_pred) # baseline
0.05499852206107747

R平方(R2)

这是评估回归模型的 "事实 "指标,也是model.score() ,其中model 可能是线性、SVC等。它也可以直接用于r2_score(y_actual,y_pred) 。与错误明智的度量不同,分数越接近于1越好。

1visualise()

                   MAE        MSE        RMSE       MedAE      R2
         Baseline: 0.05046426 0.00339777 0.05829042 0.05499852 0.84103010
Positive Outliers: 0.08362021 0.01648675 0.12840072 0.05757718 0.53651638
Negative Outliers: 0.09174612 0.02243254 0.14977495 0.05757718 0.36711532

这个度量的一个特殊性是它可以产生负分。底限不是0.0。

r2在精神上与MSE相似,因为它使用平方差值,但公式更复杂,下面可以体会到。

1def r2(y_actual,y_pred):
2    mean = np.mean(y_actual)
3    rss = sum([ (ya-yp)**2 for ya,yp in zip(y_actual,y_pred)  ])
4    tss = sum([ (ya-mean)**2 for ya,yp in zip(y_actual,y_pred)  ])
5    return 1-rss/tss
6r2(y_test,y_pred) # baseline
0.8410301048023623

结论

我们看了五个不同的指标来评估回归模型。

  • 平均绝对误差(MAE)
  • 平均平方误差(MSE)
  • 均方根误差(RMSE)
  • 绝对误差中位数(MedAE)
  • R2得分

鉴于R2是唯一能提供一个上限为1.0的一致分数范围的指标,与大多数分类指标类似,它是最受欢迎的指标,也是大多数模型在调用model.score() 方法时实施的指标,这并不奇怪。

对其他 "错误 "指标的选择,可以归结为在下列权衡中的选择。

权衡#1

是否允许大的异常值产生更大的影响(MSE)或不允许(MAE)。

1# MAE
2mean_absolute_error([1,2,3,4,5,6,7,8,9],
3                    [1,2,3,4,5,6,7,8,25])
1.7777777777777777
1# MSE
2mean_squared_error([1,2,3,4,5,6,7,8,9],
3                   [1,2,3,4,5,6,7,8,25])
28.444444444444443

权衡#2

是否忽略位于极端的异常值(MedAE)或不忽略(MAE)。

1# MAE
2mean_absolute_error([1,2,3,4,5,6,7,8,9],
3                    [1,2,3,4,5,6,7,8,25])
1.7777777777777777
1# MedAE
2median_absolute_error([1,2,3,4,5,6,7,8,9],
3                      [1,2,3,4,5,6,7,8,25])
0.0

在你离开之前

🤘 订阅我的100%无垃圾邮件的时事通讯!

电子邮件。