线性回归 - 评价指标(三)

416 阅读11分钟

根据菜菜的课程进行整理,方便记忆理解

代码位置如下:

回归类的模型评估指标

回归类算法的模型评估一直都是回归算法中的一个难点,但不像我们曾经讲过的无监督学习算法中的轮廓系数等等评估指标,回归类与分类型算法的模型评估其实是相似的法则——找真实标签和预测值的差异。只不过在分类型算法中,这个差异只有一种角度来评判,那就是是否预测到了正确的分类,而在我们的回归类算法中,我们有两种不同的 角度来看待回归的效果:

  • 第一,我们是否预测到了正确的数值
  • 第二,我们是否拟合到了足够的信息

这两种角度,分别对应着不同的模型评估指标

是否预测了正确的数值

回忆一下我们的RSS残差平方和,它的本质是我们的预测值与真实值之间的差异,也就是从第一种角度来评估我们回归的效力,所以RSS既是我们的损失函数,也是我们回归类模型的模型评估指标之一。但是,RSS有着致命的缺点它是一个无界的和,可以无限地大。我们只知道,我们想要求解最小的RSS,从RSS的公式来看,它不能为负,所以RSS越接近0越好,但我们没有一个概念,究竟多小才算好,多接近0才算好?为了应对这种状况,sklearn中使用RSS的变体,均方误差MSE(mean squared error)来衡量我们的预测值和真实值的差异:

image.png

均方误差,本质是在RSS的基础上除以了样本总量,得到了每个样本量上的平均误差。有了平均误差,我们就可以将平均误差和我们的标签的取值范围在一起比较,以此获得一个较为可靠的评估依据

在sklearn当中,我们有两种方式调用这个评估指标

  • 一种是使用sklearn专用的模型评估模块metrics里的类mean_squared_error
  • 另一种是调用交叉验证的类cross_val_score并使用里面的scoring参数来设置使用均方误差
from sklearn.metrics import mean_squared_error as MSE
MSE(yhat,Ytest)
# 0.5309012639324571

Ytest.mean()
# 2.0819292877906976

y.max()
# 5.00001
y.min()
# 0.14999

cross_val_score(reg,X,y,cv=10,scoring="mean_squared_error") # 报错

#为什么报错了?来试试看!
import sklearn
sorted(sklearn.metrics.SCORERS.keys())

['accuracy',
 'adjusted_mutual_info_score',
 'adjusted_rand_score',
 'average_precision',
 'balanced_accuracy',
 'completeness_score',
 'explained_variance',
 'f1',
 'f1_macro',
 'f1_micro',
 'f1_samples',
 'f1_weighted',
 'fowlkes_mallows_score',
 'homogeneity_score',
 'jaccard',
 'jaccard_macro',
 'jaccard_micro',
 'jaccard_samples',
 'jaccard_weighted',
 'max_error',
 'mutual_info_score',
 'neg_brier_score',
 'neg_log_loss',
 'neg_mean_absolute_error',
 'neg_mean_absolute_percentage_error',
 'neg_mean_gamma_deviance',
 'neg_mean_poisson_deviance',
 'neg_mean_squared_error',
 'neg_mean_squared_log_error',
 'neg_median_absolute_error',
 'neg_root_mean_squared_error',
 'normalized_mutual_info_score',
 'precision',
 'precision_macro',
 'precision_micro',
 'precision_samples',
 'precision_weighted',
 'r2',
 'rand_score',
 'recall',
 'recall_macro',
 'recall_micro',
 'recall_samples',
 'recall_weighted',
 'roc_auc',
 'roc_auc_ovo',
 'roc_auc_ovo_weighted',
 'roc_auc_ovr',
 'roc_auc_ovr_weighted',
 'top_k_accuracy',
 'v_measure_score']

cross_val_score(reg,X,y,cv=10,scoring="neg_mean_squared_error").mean()
# -0.5509524296956583
线性回归的大坑一号:均方误差为负

我们在决策树和随机森林中都提到过,虽然均方误差永远为正,但是sklearn中的参数scoring下,均方误差作为评判标准时,却是计算”负均方误差“(neg_mean_squared_error)。这是因为sklearn在计算模型评估指标的时候,会考虑指标本身的性质,均方误差本身是一种误差,所以被sklearn划分为模型的一种损失(loss)。在sklearn当中,所有的损失都使用负数表示,因此均方误差也被显示为负数了。真正的均方误差MSE的数值,其实就是neg_mean_squared_error去掉负号的数字。

除了MSE,我们还有与MSE类似的MAE(Mean absolute error,绝对均值误差):

image.png

其表达的概念与均方误差完全一致,不过在真实标签和预测值之间的差异外我们使用的是L1范式(绝对值)。现实使用中,MSE和MAE选一个来使用就好了。在sklearn当中,我们使用命令from sklearn.metrics importmean_absolute_error来调用MAE,同时,我们也可以使用交叉验证中的scoring = "neg_mean_absolute_error",以此在交叉验证时调用MAE。

是否拟合了足够的信息

对于回归类算法而言,只探索数据预测是否准确是不足够的。除了数据本身的数值大小之外,我们还希望我们的模型能够捕捉到数据的”规律“,比如数据的分布规律,单调性等等,而是否捕获了这些信息并无法使用MSE来衡量

image.png

来看这张图,其中红色线是我们的真实标签,而蓝色线是我们的拟合模型。这是一种比较极端,但的确可能发生的情况。这张图像上,前半部分的拟合非常成功,看上去我们的真实标签和我们的预测结果几乎重合,但后半部分的拟合却非常糟糕,模型向着与真实标签完全相反的方向去了。对于这样的一个拟合模型,如果我们使用MSE来对它进行判断,它的MSE会很小,因为大部分样本其实都被完美拟合了,少数样本的真实值和预测值的巨大差异在被均分到每个样本上之后,MSE就会很小。但这样的拟合结果必然不是一个好结果,因为一旦我的新样本是处于拟合曲线的后半段的,我的预测结果必然会有巨大的偏差,而这不是我们希望看到的。所以,我们希望找到新的指标,除了判断预测的数值是否正确之外,还能够判断我们的模型是否拟合了足够多的,数值之外的信息。

在我们学习降维算法PCA的时候,我们提到我们使用方差来衡量数据上的信息量。如果方差越大,代表数据上的信息量越多,而这个信息量不仅包括了数值的大小,还包括了我们希望模型捕捉的那些规律。为了衡量模型对数据上的信息量的捕捉,我们定义了R2R^2来帮助我们:

image.png

其中yy是我们的真实标签, y^\hat{y}是我们的预测结果, yˉ\bar{y}是我们的均值, yiyˉy_i - \bar{y}如果除以样本量m就是我们的方差。方差的本质是任意一个值和样本均值的差异,差异越大,这些值所带的信息越多。

  • R2R^2中,
    • 分子是真实值和预测值之差的差值,也就是我们的模型没有捕获到的信息总量
    • 分母是真实标签所带的信息量,所以其衡量的是1 - 我们的模型没有捕获到的信息量占真实标签中所带的信息量的比例
    • 所以, R2R^2越接近1越好。

R2R^2可以使用三种方式来调用

  • 一种是直接从metrics中导入r2_score,输入预测值和真实值后打分
  • 第二种是直接从线性回归LinearRegression的接口score来进行调用
  • 第三种是在交叉验证中,输入"r2"来调用
#调用R2
from sklearn.metrics import r2_score
r2_score(yhat,Ytest)
# 0.3380653761555983

r2 = reg.score(Xtest,Ytest)
r2
# 0.6043668160178817

r2_score(Ytest,yhat)
# 0.6043668160178817

#或者你也可以指定参数,就不必在意顺序了
r2_score(y_true = Ytest,y_pred = yhat)
# 0.6043668160178817

cross_val_score(reg,X,y,cv=10,scoring="r2").mean()
# 0.5110068610524564
线性回归的大坑二号:相同的评估指标不同的结果。

为什么结果会不一致呢?这就是回归和分类算法的不同带来的坑。

在我们的分类模型的评价指标当中,我们进行的是一种 if a == b的对比,这种判断和if b == a其实完全是一种概念,所以我们在进行模型评估的时候,从未踩到我们现在在的这个坑里。然而看R2的计算公式,R2明显和分类模型的指标中的accuracy或者precision不一样,

R2涉及到的计算中对预测值和真实值有极大的区别,必须是预测值在分子,真实值在分母,所以我们在调用metrcis模块中的模型评估指标的时候,必须要检查清楚,指标的参数中,究竟是要求我们先输入真实值还是先输入预测值

我们观察到,我们在加利福尼亚房屋价值数据集上的MSE其实不是一个很大的数(0.5),但我们的R2R^2不高,这证明我们的模型比较好地拟合了一部分数据的数值,却没有能正确拟合数据的分布。让我们与绘图来看看,究竟是不是这样一回事。我们可以绘制一张图上的两条曲线,一条曲线是我们的真实标签Ytest,另一条曲线是我们的预测结果yhat,两条曲线的交叠越多,我们的模型拟合就越好。

import matplotlib.pyplot as plt
sorted(Ytest)

plt.plot(range(len(Ytest)),sorted(Ytest),c="black",label= "Data")
plt.plot(range(len(yhat)),sorted(yhat),c="red",label = "Predict")
plt.legend()
plt.show()

image.png

可见,虽然我们的大部分数据被拟合得比较好,但是图像的开头和结尾处却又着较大的拟合误差。如果我们在图像右侧分布着更多的数据,我们的模型就会越来越偏离我们真正的标签。这种结果类似于我们前面提到的,虽然在有限的数据集上将数值预测正确了,但却没有正确拟合数据的分布,如果有更多的数据进入我们的模型,那数据标签被预测错误的可能性是非常大的。

现在,来看一组有趣的情况:

import numpy as np
rng = np.random.RandomState(42)
X = rng.randn(100, 80)
y = rng.randn(100)
cross_val_score(LR(), X, y, cv=5, scoring='r2')

# array([-180.73467658,   -5.66893072,  -15.72168144,  -77.08556174, -65.48568926])
线性回归的三号大坑:负的R2R^2

许多学习过统计理论的小伙伴此时可能会感觉到,太坑了!sklearn真的是设置了太多障碍,均方误差是负的, R2R^2也是负的,不能忍了。还有的小伙伴可能觉得,这个R2R^2名字里都带平方了,居然是负的,好气哦!无论如何,我们再来看看R2R^2的计算公式:

image.png

第一次学习机器学习或者统计学的小伙伴,可能会感觉没什么问题了, R2R^2是1减一个数,后面的部分只要大于1的话R2R^2完全可以小于0。但是学过机器学习,尤其是在统计学上有基础的小伙伴可能会坐不住了:这不对啊!

一直以来,众多的机器学习教材中都有这样的解读:

除了RSS之外,我们还有解释平方和ESS(Explained Sum of Squares,也叫做SSR回归平方和)以及总离差平方和TSS(Total Sum of Squares,也叫做SST总离差平方和)。解释平方和ESS定义了我们的预测值和样本均值之间的差异,而总离差平方和定义了真实值和样本均值之间的差异(就是R2R^2中的分母),两个指标分别写作

image.png

而我们有公式:

image.png

看我们的R2R^2的公式,如果带入我们的TSS和ESS,那就有:

image.png

而ESS和TSS都带平方,所以必然都是正数,那怎么可能是负的呢? 好了,颠覆认知的时刻到来了——公式TSS = RSS + ESS不是永远成立的!就算所有的教材和许多博客里都理所当然这样写了大家也请抱着怀疑精神研究一下,你很快就会发现很多新世界。我们来看一看我们是如何证明(1)这个公式的:

image.png

许多教材和博客中让2i=0m(yiyi^)(yi^yˉ)2\sum_{i=0}^{m}(y_i - \hat{y_i})(\hat{y_i} - \bar{y})这个式子为0,公式TSS=RSS+ESSTSS = RSS + ESS自然就成立了,但要让这个式子成立是有条件的。现在有了这个式子的存在, R2R^2就可以是一个负数了。只要我们的(yiyi^)(y_i - \hat{y_i})衡量的是真实值到预测值的距离,而yi^yˉ\hat{y_i} - \bar{y}衡量的是预测值到均值的距离,只要当这两个部分的符号不同的时候,我们的式子2i=0m(yiyi^)(yi^yˉ)2\sum_{i=0}^{m}(y_i - \hat{y_i})(\hat{y_i} - \bar{y})就为负,而R2R^2就有机会是一个负数。

看下面这张图,蓝色的横线是我们的均值线yˉ\bar{y},橙色的线是我们的模型y^\hat{y},蓝色的点是我们的样本点。现在对于xix_i来说,我们的真实标签减预测值的值(yiyi^)(y_i - \hat{y_i})为正,但我们的预测值 却是一个负数,这说明,数据本身的均值,比我们对数据的拟合模型本身更接近数据的真实值,那我们的模型就是废的,完全没有作用,类似于分类模型中的分类准确率为50%,不如瞎猜

也就是说,当我们的R2R^2显示为负的时候,这证明我们的模型对我们的数据的拟合非常糟糕,模型完全不能使用。所有,一个负的R2R^2是合理的。当然了,现实应用中,如果你发现你的线性回归模型出现了负的R2R^2,不代表你就要接受他了,首先检查你的建模过程和数据处理过程是否正确,也许你已经伤害了数据本身,也许你的建模过程是存在bug的。如果是集成模型的回归,检查你的弱评估器的数量是否不足,随机森林,提升树这些模型在只有两三棵树的时候很容易出现负的R2R^2。如果你检查了所有的代码,也确定了你的预处理没有问题,但你的R2R^2也还是负的,那这就证明,线性回归模型不适合你的数据,试试看其他的算法吧