一、 岭回归简介
在处理复杂的数据的回归问题时,普通的线性回归会遇到一些问题。
简单线性回归最主要问题是对异常值敏感。在真实世界的数据收集过程中,经过会遇到错误的度量结果。而线性回归使用的普通最小二乘法,其目标是使平方误差最小化。这时,由于异常值误差的绝对值很大,因此破坏整个模型。
对此,我们引入正则化项的系数作为阈值来消除异常的影响。这个方法称为岭回归。
岭回归在最小二乘法的基础上加上了一个
惩罚项。损失函数:
二、岭回归器的构建
# 数据集存在异常点时,线性回归器的拟合效果不好,“被异常点带到沟里去了”
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42) # 使得每次运行得到的随机数都一样
x=np.arange(10,20) # 自变量,随便定义的
error=np.random.normal(size=x.shape)
y=1.8*x+5.9+error # 添加随机数作为噪音
print('x={}'.format(x))
print('noise y={}'.format(y))
print('target y={}'.format(1.8*x+5.9))
代码运行结果:
x=[10 11 12 13 14 15 16 17 18 19]
noise y=[24.39671415 25.5617357 28.14768854 30.82302986 30.86584663 32.66586304
36.27921282 37.26743473 37.83052561 40.64256004]
target y=[23.9 25.7 27.5 29.3 31.1 32.9 34.7 36.5 38.3 40.1]
plt.scatter(x,y)
plt.plot(x,1.8*x+5.9,'-r') # 绘制的是红色的直线
plt.show()
# 以下加入两个异常点,再用线性回归器进行拟合
abnormal_x=[16.5,17.9]
abnormal_y=[25.98,24.12]
# 将异常点绘制出来
plt.scatter(abnormal_x,abnormal_y,marker='x')
# 将异常点加入到原数据集中,构建线性回归器进行拟合,绘制拟合直线
whole_x=np.append(x,abnormal_x).reshape(-1,1)
whole_y=np.append(y,abnormal_y).reshape(-1,1)
from sklearn import linear_model
linear_regressor=linear_model.LinearRegression() # 创建线性回归器对象
linear_regressor.fit(whole_x,whole_y) # 使用训练数据集训练该回归器对象
# 查看拟合结果
y_predict=linear_regressor.predict(whole_x)
plt.plot(whole_x,y_predict,'-b',linewidth=3)
plt.show()
# 岭回归器的构建
from sklearn import linear_model
ridge_regressor=linear_model.Ridge(alpha=0.02,fit_intercept=True,max_iter=10000)
# 构建岭回归器对象,使用的偏差系数为alpha=0.02
ridge_regressor.fit(whole_x,whole_y) # 使用岭回归器进行训练
# 使用训练完成的岭回归器预测数值
y_train_predict=ridge_regressor.predict(whole_x)
plt.scatter(whole_x,whole_y)
plt.plot(whole_x,y_predict,'-b')
plt.plot(whole_x,y_train_predict,'-r')
plt.show()
图中蓝色线条为使用线性回归器得到的结果,红色为使用alpha为0.02时得到的拟合直线,两个重合了,貌似是alpha取值太小所致。
alpha参数控制岭回归器的复杂程度,但alpha趋近于0时,岭回归器就相当于普通的最小二乘法(从图中可以看出,alpha=0.02时,两条直线几乎重合),所以,如果希望该岭回归器模型对异常值不那么敏感,就需要设置一个比较大的alpha值(《Python机器学习经典实例》是这么说的)。所以alpha值的选取是个技术活。
三、 岭回归器模型的评估
和线性回归一样,可以使用均方误差MSE来评估该模型的好坏。
为了对本模型进行评估,需要有评估所需的测试集,此处用随机数生成了一些测试集,如下为测试集生成代码
# 岭回归器模型的评估
# 第一步:构建评估数据,即test set
test_x=np.arange(10,20) # 自变量,随便定义的
shift=np.random.normal(size=test_x.shape)
test_x=test_x+shift # 对test_x进行偏置得到测试集的X
error=np.random.normal(size=x.shape)
test_y=1.8*test_x+5.9+error # 添加随机数作为噪音
plt.scatter(whole_x,whole_y,color='blue',label='train_set')
plt.scatter(test_x,test_y,color='red',label='test_set')
plt.legend()
plt.show()
# 把train set和test set都绘制到一个图中,可以看出偏差不大
从上面数据点的分布来看,除了train set中异常点的位置没有test set之外,其余点都和test set的分布是一致的,表明这个test set可以用于进行测试。
# 第二步:使用test set计算MSE
y_test_predict=ridge_regressor.predict(test_x.reshape(-1,1))
# 第三步:使用评价指标来评估模型的好坏
import sklearn.metrics as metrics
test_y=test_y.reshape(-1,1)
print('平均绝对误差:{}'.format(
round(metrics.mean_absolute_error(y_test_predict,test_y),2)))
print('均方误差MSE:{}'.format(
round(metrics.mean_squared_error(y_test_predict,test_y),2)))
print('中位数绝对误差:{}'.format(
round(metrics.median_absolute_error(y_test_predict,test_y),2)))
print('解释方差分:{}'.format(
round(metrics.explained_variance_score(y_test_predict,test_y),2)))
print('R方得分:{}'.format(
round(metrics.r2_score(y_test_predict,test_y),2)))
平均绝对误差:1.49
均方误差MSE:3.49
中位数绝对误差:1.37
解释方差分:0.72
R方得分:0.69
小结:
1、在考察train set和test set时需要考虑两个数据集中数据点的分布,尽量使得两者在空间上的分布一致,这样才能准确地测试模型。
2、本次模型在test set上的效果并不太理想,虽然得到的MSE比较小,但是R方得分,解释方差分都偏低,还需改进。
四、岭回归器模型的改进
从上面得到的岭回归器模型在测试集上的表现可以看出,本模型效果并不太好,可以改进的地方至少有两个
1、是对train set中数据点进行删减,比如发现了很明显偏离的异常点,可以直接将这些异常点删除,但是这就涉及到异常点的鉴定方法,把哪些点标注为可以删除的异常点,偏离中心位置多少距离就被认定为异常点,等等。
2、是优化岭回归器的alpha值,下面是我尝试优化alpha的结果图。
# 对岭回归器模型进行优化,主要优化alpha的取值
alpha_candidates=[-20,-10,-5.0,-2.0,2.0,5.0,10,20,50]
from sklearn import linear_model
for alpha in alpha_candidates:
ridge_regressor=linear_model.Ridge(alpha=alpha,fit_intercept=True,max_iter=10000)
# 构建岭回归器对象,使用不同的alpha值
ridge_regressor.fit(whole_x,whole_y) # 使用岭回归器进行训练
# 使用训练完成的岭回归器预测数值
y_train_predict=ridge_regressor.predict(whole_x)
plt.plot(whole_x,y_train_predict,label='alpha='+str(alpha))
plt.legend()
plt.scatter(whole_x,whole_y)
plt.show()
从上图中可以看出,随着alpha的取值由0到50慢慢变化,拟合直线会进行顺时针旋转,或者是朝向异常点的方向旋转,貌似是因为alpha越大,异常点对直线越“吸引”,故而拟合直线收到异常点的影响也越来越大。而alpha由0变到负值,且越来越小时,异常点对拟合直线的影响也越来越小。(不知道这么总结对不对???)。虽然linear_model. Ridge的函数说明文档要求alpha取值是positive float,但是此处负值貌似也没有问题,而且貌似alpha为负值时拟合的更好一些。
下面打印出各种不同alpha值时的各种误差:
# 对岭回归器模型进行优化,使用不同alpha值优化后得到的模型计算测试集
alpha_candidates=[-20,-10,-5.0,-2.0,2.0,5.0,10,20,50]
from sklearn import linear_model
for alpha in alpha_candidates:
ridge_regressor=linear_model.Ridge(alpha=alpha,fit_intercept=True,max_iter=10000)
# 构建岭回归器对象,使用不同的alpha值
ridge_regressor.fit(whole_x,whole_y) # 使用岭回归器进行训练
y_test_predict=ridge_regressor.predict(test_x.reshape(-1,1))
print('------------alpha='+str(alpha)+'---------------------->>>')
print('均方误差MSE:{}'.format(
round(metrics.mean_squared_error(y_test_predict,test_y),2)))
print('中位数绝对误差:{}'.format(
round(metrics.median_absolute_error(y_test_predict,test_y),2)))
print('解释方差分:{}'.format(
round(metrics.explained_variance_score(y_test_predict,test_y),2)))
print('R方得分:{}'.format(
round(metrics.r2_score(y_test_predict,test_y),2)))
------------alpha=-20---------------------->>>
均方误差MSE:2.07
中位数绝对误差:1.21
解释方差分:0.94
R方得分:0.89
------------alpha=-10---------------------->>>
均方误差MSE:2.63
中位数绝对误差:1.1
解释方差分:0.86
R方得分:0.81
------------alpha=-5.0---------------------->>>
均方误差MSE:3.04
中位数绝对误差:1.24
解释方差分:0.8
R方得分:0.76
------------alpha=-2.0---------------------->>>
均方误差MSE:3.3
中位数绝对误差:1.32
解释方差分:0.75
R方得分:0.72
------------alpha=2.0---------------------->>>
均方误差MSE:3.68
中位数绝对误差:1.42
解释方差分:0.69
R方得分:0.66
------------alpha=5.0---------------------->>>
均方误差MSE:3.96
中位数绝对误差:1.48
解释方差分:0.64
R方得分:0.61
------------alpha=10---------------------->>>
均方误差MSE:4.45
中位数绝对误差:1.59
解释方差分:0.54
R方得分:0.52
------------alpha=20---------------------->>>
均方误差MSE:5.43
中位数绝对误差:1.81
解释方差分:0.31
R方得分:0.3
------------alpha=50---------------------->>>
均方误差MSE:8.13
中位数绝对误差:2.81
解释方差分:-0.67
R方得分:-0.67
小结:
1,通过修改alpha值,可以修改模型在test set上的表现,由于test set不包含异常点,所以要求alpha取的越小越好,拟合的直线越偏离异常点。
2,alpha取值越大,岭回归得到的拟合直线越“偏向”异常点,此时异常点对直线的影响越大;alpha越小,拟合直线越“偏离”异常点,此时异常点对直线的影响越小,我们所期望的是异常点对直线影响越小越好,所以貌似alpha应该往越小的方向取值。