回归类算法中,有两种不同的角度来看待回归的效果:
第一,是否预测到了正确的数值。
第二,是否拟合到了足够的信息。
上篇博文已经讨论了第一种角度,本文讨论第二种角度:
是否拟合了足够的信息
对于回归类算法而言,只探索数据预测是否准确是不够的。除了数据本身的数值大小之外,还希望模型能够捕捉到数据的“规律”,比如数据的分布规律、单调性等,而是否捕获了这些信息并无法使用MSE来衡量。
上图,红色线是真实标签,蓝色线是拟合模型。这是一种比较极端,但的确可能发生的情况。这张图像上,前半部分的拟合非常成功,看上去真实标签和预测结果几乎重合,但后半部分的拟合却非常糟糕,模型向着与真实标签完全相反的方向去了。对于这样的一个拟合模型,如果使用MSE判断,它的MSE会很小,因为大部分样本其实都被完美拟合了,少数样本的真实值和预测值的巨大差异在被均分到每个样本上之后,MSE就会很小。但这样的拟合结果必然不是一个好结果,因为一旦新样本是处于拟合曲线的后半段的,预测结果必然会有巨大的偏差。所以,希望找到新的指标,除了判断预测的数值是否正确之外,还能够判断模型是否拟合了足够多的,数值之外的信息。
降维算法PCA通过使用方差来衡量数据上的信息量。如果方差越大,代表数据上的信息量越多,此信息量不仅包括数值的大小,还包括希望模型捕捉的那些规律。
为了衡量模型对数据上的信息量的捕捉,定义了
R
2
R^2
R2来帮助我们:
R
2
=
1
−
∑
i
=
0
m
(
y
i
−
y
^
i
)
2
∑
i
=
0
m
(
y
i
−
y
ˉ
i
)
2
=
1
−
R
S
S
∑
i
=
0
m
(
y
i
−
y
ˉ
i
)
2
R^2=1-\large{\frac{\sum_{i=0}^m(y_i-\hat{y}_i)^2}{\sum_{i=0}^m(y_i-\bar{y}_i)^2}}\small=1-\large{\frac{RSS}{\sum_{i=0}^m(y_i-\bar{y}_i)^2}}
R2=1−∑i=0m(yi−yˉi)2∑i=0m(yi−y^i)2=1−∑i=0m(yi−yˉi)2RSS
其中,
y
y
y是真实标签,
y
^
\hat{y}
y^是预测结果,
y
ˉ
\bar{y}
yˉ是均值, (
y
i
−
y
ˉ
y_i-\bar{y}
yi−yˉ)除以样本量m就是方差。方差的本质是任意一个y值和样本均值的差异,差异越大,这些值所带的信息越多。在R2中,减数分子是真实值和预测值之差平方的合计,也就是模型没有捕获到的信息总量,分母是真实标签所带的信息量,所以其衡量的是1 - 模型没有捕获到的信息量占真实标签中所带的信息量的比例,所以,R2越接近1越好。
R2可以用三种方式调用,一种是直接从metrics中导入r2_score,输入预测值和真实值后打分。第二种是直接从线性回归LinearRegression的接口score进行调用。第三种是在交叉验证中,输入"r2"来调用。
#从metrics中导入r2_score
from sklearn.metrics import r2_score
#r2_score(y_true, y_pred, sample_weight=None, multioutput='uniform_average')
r2_score(Ytest,yhat)
0.6043668160178817
#LinearRegression的接口score
#reg.score(X, y, sample_weight=None)
r2 = reg.score(Xtest,Ytest)
r2
0.6043668160178817
#交叉验证中,设定参数scoring="r2"
cross_val_score(reg,X,y,cv=10,scoring="r2").mean()
0.5110068610524564
在加利福尼亚房屋价值数据集上的MSE其实不是一个很大的数(0.5),但R^2不高,这证明模型比较好地拟合了一部分数据的数值,却没有能正确拟合数据的分布。可以在一张图上绘制出预测值的散点图和真实值的折线图,两者趋势越接近,并且预测值的散点图越靠近真实值,则认为模型拟合越优秀。
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
#对Ytest绘图
plt.scatter(range(len(Ytest)),Ytest,c="purple",label= "Data",s=2)
plt.legend()
plt.show()
#因此需要对图像进⾏排序:如果Ytest能够从⼩到大显示,就能看出趋势
plt.scatter(range(len(Ytest)),sorted(Ytest),c="purple",label= "Data",s=2)
plt.legend()
plt.show()
#使⽤用argsort函数对测试集进行从小到大的排序后返回数据在原顺序中的索引
ind = np.argsort(Ytest)
ind
array([2477, 4318, 4930, ..., 2806, 2786, 3736], dtype=int64)
#和直接使用sorted进行排序的结果是⼀致的,从小到大进行排序
Ytest.min()
0.14999
Ytest.tolist().index(Ytest.min())
2477
sorted(Ytest)
[0.14999,
0.14999,
0.225,
0.325,
0.35,
0.375,
0.388,
...
#让yhat按照Ytest中从小到大的排序进行排序
yhat = pd.DataFrame(yhat)
yhat.head(5)
0
0 1.513849
1 0.465662
2 2.256773
3 2.313086
4 2.458239
yhat = pd.DataFrame(yhat,index=ind)
yhat.head(5)
0
2477 1.053998
4318 0.190345
4930 1.923386
173 2.046919
948 1.040453
#绘制图像
plt.plot(range(len(Ytest)),sorted(Ytest),c="black",label= "Data")
plt.scatter(range(len(yhat)),yhat,c="red",label = "Predict",s=1)
plt.legend()
plt.show()
#如果简单粗暴排序来绘制曲线呢?
#可以绘制⼀张图上的两条曲线,⼀条曲线是真实标签Ytest,另⼀条曲线是预测结果yhat
#两条曲线的交叠越多,我们的模型拟合就越好
plt.plot(range(len(Ytest)),sorted(Ytest),c="black",label= "Data")
plt.plot(range(len(yhat)),sorted(yhat.iloc[:,0]),c="red",label = "Predict")
plt.legend()
plt.show()
可见,大部分数据的拟合其实还是围绕在真实值周围的,但是图像的整体趋势却不一致。如果在图像右侧分布着更更多的数据,模型就会越来越偏离我们真正的标签。这种结果类似于我们前面提到的,虽然在有限的数据集上将数值预测相对正确了,但却没有正确拟合数据的分布,如果有更多的数据进入模型,那数据标签被预测错误的可能性是非常大的。