关于作者:饼干同学,某人工智能公司交付开发工程师/建模科学家。专注于AI工程化及场景落地,希望和大家分享成长中的专业知识与思考感悟。
0x00 前言:
本篇内容是线性回归系列的第三篇。
在《模型之母:简单线性回归&最小二乘法》、《模型之母:简单线性回归&最小二乘法》中我们学习了简单线性回归、最小二乘法,并完成了代码的实现。在结尾,我们抛出了一个问题:在之前的kNN算法(分类问题)中,使用分类准确度来评价算法的好坏,那么回归问题中如何评价好坏呢?
本篇内容就是关于回归模型的评价,首先介绍线性回归模型的三个常用评价方法,然后通过波士顿房产预测的实际例子,对评价方法进行代码实现。最后我们会隆重引出最好的衡量线性回归法的指标:R Square
0x01 线性回归算法的衡量标准
简单线性回归的目标是:
已知训练数据样本、 ,找到和的值,使 尽可能小
实际上是找到训练数据集中 最小。
衡量标准是看在测试数据集中y的真实值与预测值之间的差距。
因此我们可以使用下面公式作为衡量标准:
但是这里有一个问题,这个衡量标准是和m相关的。在具体衡量时,测试数据集不同将会导致误差的累积量不同。因此很快的
首先我们从“使损失函数尽量小”这个思路出发:
对于训练数据集合来说,使 尽可能小
在得到a和b之后将 代入a、b中。可以使用 来作为衡量回归算法好坏的标准。
1.1 均方误差MSE
测试集中的数据量m
不同,因为有累加操作,所以随着数据的增加 ,误差会逐渐积累;因此衡量标准和 m
相关。为了抵消掉数据量的形象,可以除去数据量,抵消误差。通过这种处理方式得到的结果叫做 均方误差MSE(Mean Squared Error):
1.2 均方根误差RMSE
但是使用均方误差MSE收到量纲的影响。例如在衡量房产时,y的单位是(万元),那么衡量标准得到的结果是(万元平方)。为了解决量纲的问题,可以将其开方(为了解决方差的量纲问题,将其开方得到平方差)得到均方根误差RMSE(Root Mean Squarde Error):
1.3 平均绝对误差MAE
对于线性回归算法还有另外一种非常朴素评测标准。要求真实值 与 预测结果 之间的距离最小,可以直接相减做绝对值,加m次再除以m,即可求出平均距离,被称作平均绝对误差MAE(Mean Absolute Error):
在之前确定损失函数时,我们提过,绝对值函数不是处处可导的,因此没有使用绝对值。但是在评价模型时不影响。因此模型的评价方法可以和损失函数不同。
0x02 评价标准的代码实现
2.1 数据探索
代码语言:javascript复制import numpy as npimport matplotlib.pyplot as pltfrom sklearn import datasets
# 查看数据集描述boston = datasets.load_boston()print(boston.DESCR)
(输出略)
因为是测试简单回归算法,因此我们选择其中的一个特征进行建模。选择:
- RM average number of rooms per dwelling 每个住宅的平均房间数
下面我们进行简单的数据探索:
代码语言:javascript复制# 查看数据集的特征列表boston.feature_names输出:array(['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT'], dtype='<U7') # 取出数据中的第六例的所有行(房间数量)x = boston.data[:,5]x.shape输出:(506,)
# 取出样本标签y = boston.targety.shape输出:(506,)
plt.scatter(x,y)plt.show()
在图中我们可以看到 50W 美元的档分布着一些点。这些点可能是超出了限定范围(比如在问卷调查中,价格的最高档位是“50万及以上”,那么就全都划到50W上了,因此在本例中,可以将这部分数据去除)
代码语言:javascript复制np.max(y)# 这里有一个骚操作,用比较运算符返回一个布尔值的向量,将其作为索引,直接在矩阵里对每个元素进行过滤。x = x[y < 50.0]y = y[y < 50.0]plt.scatter(x,y)plt.show()
2.2 简单线性回归预测
代码语言:javascript复制from myAlgorithm.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, seed=666)print(x_train.shape) # (392,)print(y_train.shape) #(392,)print(x_test.shape) #(98,)print(y_test.shape) #(98,)
from myAlgorithm.SimpleLinearRegression import SimpleLinearRegression
reg = SimpleLinearRegression()reg.fit(x_train,y_train)print(reg.a_) # 7.8608543562689555print(reg.b_) # -27.459342806705543
plt.scatter(x_train,y_train)plt.plot(x_train, reg.predict(x_train),color='r')plt.show()
代码语言:javascript复制y_predict = reg.predict(x_test)print(y_predict)
(输出略)
2.3 MSE 均方误差
代码语言:javascript复制mse_test = np.sum((y_predict - y_test) ** 2) / len(y_test)mse_test
24.156602134387438
2.4 RMSE 均方根误差
代码语言:javascript复制from math import sqrt
rmse_test = sqrt(mse_test)rmse_test
4.914936635846635
RMSE消除了量纲的差异,输出的结果是4.9,与y的量纲相同。解释为在RMSE指标下,我们预测的房产数据平均误差在4.9万美元左右。
2.5 MAE 平均绝对误差
代码语言:javascript复制mae_test = np.sum(np.absolute(y_predict - y_test)) / len(y_test)mae_test
3.5430974409463873
在MAE指标下,我们预测的房产数据平均误差在3.54万美元左右。我们看到MAE指标得到的误差要比RMSE指标得到的误差小。说明不同的评价指标的结果不同。
从数学角度来分析,RMSE和MAE的量纲相同,但RMSE的结果较大,这是因为RMSE是将错误值平方,平方操作会放大样本中预测结果和真实结果较大的差距。MAE没有放大。而我们就是要解决目标函数最大差距,因为选RMSE更好一点。
0x03 封装及调用
3.1 在工程文件中封装
在工程文件的metrics.py中添加以上评价指标:
代码语言:javascript复制mport numpy as npfrom math import sqrt
def accuracy_score(y_true, y_predict): """计算y_true和y_predict之间的准确率""" assert y_true.shape[0] != y_predict.shape[0], "the size of y_true must be equal to the size of y_predict" return sum(y_true == y_predict) / len(y_true)
def mean_squared_error(y_true, y_predict): """计算y_true和y_predict之间的MSE""" assert len(y_true) == len(y_predict), "the size of y_true must be equal to the size of y_predict" return np.sum((y_true - y_predict) ** 2) / len(y_true)
def root_mean_squared_error(y_true, y_predict): """计算y_true和y_predict之间的RMSE""" return sqrt(mean_squared_error(y_true, y_predict))
def mean_absolute_error(y_true, y_predict): """计算y_true和y_predict之间的MAE""" assert len(y_true) == len(y_predict), "the size of y_true must be equal to the size of y_predict"
return np.sum(np.absolute(y_predict - y_true)) / len(y_predict)
3.2 调用
我们可以在jupyter notebook进行调用:
代码语言:javascript复制from myAlgorithm.metrics import mean_squared_errorfrom myAlgorithm.metrics import root_mean_squared_errorfrom myAlgorithm.metrics import mean_absolute_error
mean_squared_error(y_test, y_predict)# 输出:24.156602134387438
root_mean_squared_error(y_test, y_predict)# 输出:4.914936635846635
mean_absolute_error(y_test, y_predict)# 输出:3.5430974409463873
3.3 sklearn中的MSE和MAE
sklearn中不存在RMSE,我们可以手动对MSE开方:
代码语言:javascript复制from sklearn.metrics import mean_squared_errorfrom sklearn.metrics import mean_absolute_error
mean_squared_error(y_test, y_predict)# 输出:24.156602134387438
mean_absolute_error(y_test, y_predict)# 输出:3.5430974409463873
0x04 更好用的 R Square
4.2 R Square介绍以及为什么好
分类准确率,就是在01之间取值。但RMSE和MAE没有这样的性质,得到的误差。因此RMSE和MAE就有这样的局限性,比如我们在预测波士顿方差,RMSE值是4.9(万美元) 我们再去预测身高,可能得到的误差是10(厘米),我们不能说后者比前者更准确,因为二者的量纲根本就不是一类东西。
其实这种局限性,可以被解决。用一个新的指标R Squared。
R方这个指标为什么好呢?
- 对于分子来说,预测值和真实值之差的平方和,即使用我们的模型预测产生的错误。
- 对于分母来说,是均值和真实值之差的平方和,即认为“预测值=样本均值”这个模型(Baseline Model)所产生的错误。
- 我们使用Baseline模型产生的错误较多,我们使用自己的模型错误较少。因此用1减去较少的错误除以较多的错误,实际上是衡量了我们的模型拟合住数据的地方,即没有产生错误的相应指标。
我们根据上述分析,可以得到如下结论:
- R^2 <= 1
- R2越大也好,越大说明减数的分子小,错误率低;当我们预测模型不犯任何错误时,R2最大值1
- 当我们的模型等于基准模型时,R^2 = 0
- 如果R^2 < 0,说明我们学习到的模型还不如基准模型。此时,很有可能我们的数据不存在任何线性关系。
4.2 R Square实现
下面我们从具体实现的层面再来分析一下R方:
如果分子分母同时除以m,我们会发现,分子就是之前介绍过的均方误差,分母实际上是y这组数据对应的方差:
下面我们具体编程实践一下:
代码语言:javascript复制1 - mean_squared_error(y_test, y_predict) / np.var(y_test)
输出:0.61293168039373225
下面我们在工程文件metrics.py中添加自己实现的r2_score方法:
代码语言:javascript复制def score(self, x_test, y_test): """根据测试数据x_test、y_test计算简单线性回归准确度(R方)""" y_predict = self.predict(x_test) return r2_score(y_test, y_predict)
然后我们再来调用它:
代码语言:javascript复制from myAlgorithm.metrics import r2_scorer2_score(y_test, y_predict)
其实这跟掉sklearn中的方法相同
代码语言:javascript复制from sklearn.metrics import r2_scorer2_score(y_test, y_predict)
0xFF 总结
线性回归的评价指标与分类的评价指标有很大的不同,本篇介绍了均方误差MSE(预测值与真实值之差的平方和,再除以样本量)、均方根误差RMSE(为了消除量纲,将MSE开方)、平均绝对误差MAE(预测值与真实值之差的绝对值,再除以样本量)、以及非常重要的、效果非常好的R方(因此用1减去较少的错误除以较多的错误,实际上是衡量了我们的模型拟合住数据的地方,即没有产生错误的相应指标)。
在实际应用过程中,我们需要这些评价指标,来判别模型的好坏。
在下一篇,我们将会抛弃简单线性回归中每个样本只能有一个特征的限制,考虑更一般的、多个特征的多元线性回归。