【火炉炼AI】机器学习004-岭回归器的构建和模型评估

1,292 阅读8分钟

【火炉炼AI】机器学习004-岭回归器的构建和模型评估

(本文所使用的Python库和版本号: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

在我的上一篇文章(【火炉炼AI】机器学习003-简单线性回归器的创建,测试,模型保存和加载)中,已经详细的讲解了简单线性回归器的构建和测试,简单线性回归器的优势在“简单”,运行速度快,但缺点也在于“简单”,过于简单以至于难以拟合很多复杂的数据集。

比如,如果数据集中存在一些远离大部分数据点的异常点,如下图所示,那么简单线性回归器就会得到比较差的回归直线,这是因为线性回归器要拟合数据集中所有的数据点,也包括这些异常点,所以异常点往往会把模型“带到沟里去”。

由于异常点(橙色叉号)存在,线性回归器拟合得到的直线(蓝色)偏离期望直线(红色)

面对这样的数据集,该怎么样得到正确的拟合直线了?可以使用正则化项的系数作为阈值来消除异常值的影响,这个方法被称为岭回归。

岭回归是专门用于数据集中存在偏差数据(异常点)的回归分析方法,本质上是对普通最小二乘法进行改良,放弃最小二乘法的无偏性,以损失部分信息,降低精度为代价获得回归系数更符合数据集实际情况的回归方法,所以岭回归对于有偏差数据的数据集的拟合要明显强于使用最小二乘法的线性回归模型。

线性回归模型可以用下列公式表示:

最小二乘法的计算公式

线性回归模型所使用的损失函数为:

线性回归模型所用的损失函数公式

通过最小二乘法求解得到的最优解为:

最小二乘法得到的最优解

前面提到,岭回归的本质是对最小二乘法进行改良,简单来说,岭回归就是在矩阵XTX上加一个I,加入的这一部分相当于引进了偏差性。所以岭回归的损失函数和最优解分别为:

岭回归的损失函数和最优解


1. 岭回归器的构建

在代码层面上,岭回归器的构建方式和线性回归器的构建方法一样,只是创建一个linear_model. Ridge ()对象,而不是linear_model.LinearRegression()回归器对象,如下部分代码所示。

# 岭回归器的构建
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')

运行得到的岭回归器

########################小**********结###############################

1,图中蓝色线条为使用线性回归器得到的结果,红色为使用alpha为0.02时得到的拟合直线,两个重合了,貌似是alpha取值太小所致。

2,alpha参数控制岭回归器的复杂程度,但alpha趋近于0时,岭回归器就相当于普通的最小二乘法(从图中可以看出,alpha=0.02时,两条直线几乎重合),所以,如果希望该岭回归器模型对异常值不那么敏感,就需要设置一个比较大的alpha值(《Python机器学习经典实例》是这么说的,但我表示严重怀疑,原因后面会讲)。所以alpha值的选取是个技术活。

#################################################################


2. 岭回归器模型的评估

同样的,和线性回归器模型一样,我们也需要对岭回归器模型进行评估,以考察该模型的好坏。同样的,可以使用均方误差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()
# 把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)))

-------------------------------------输---------出--------------------------------

平均绝对误差:2.29 均方误差MSE:8.32 中位数绝对误差:2.0 解释方差分:0.53 R方得分:0.33

--------------------------------------------完-------------------------------------

########################小**********结###############################

1,在考察train set和test set时需要考虑两个数据集中数据点的分布,尽量使得两者在空间上的分布一致,这样才能准确地测试模型。

2,本次模型在test set上的效果并不太理想,虽然得到的MSE比较小,但是R方得分,解释方差分都偏低,看来还有改进的余地。

#################################################################


3. 岭回归器模型的改进

从上面得到的岭回归器模型在测试集上的表现可以看出,本模型效果并不太好,可以改进的地方至少有两个,一个是对train set中数据点进行删减,比如发现了很明显偏离的异常点,可以直接将这些异常点删除,但是这就涉及到异常点的鉴定方法,把哪些点标注为可以删除的异常点,偏离中心位置多少距离就被认定为异常点,等等。另外一个方法是优化岭回归器的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)

对alpha值优化后的结果

从上图中可以看出,随着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:4.93
中位数绝对误差:1.1
解释方差分:0.87
R方得分:0.75
------------alpha=-10----------------------
均方误差MSE:6.61
中位数绝对误差:1.62
解释方差分:0.73
R方得分:0.57
------------alpha=-5.0----------------------
均方误差MSE:7.47
中位数绝对误差:1.84
解释方差分:0.64
R方得分:0.46
------------alpha=-2.0----------------------
均方误差MSE:7.98
中位数绝对误差:1.96
解释方差分:0.57
R方得分:0.38
------------alpha=2.0----------------------
均方误差MSE:8.66
中位数绝对误差:2.03
解释方差分:0.48
R方得分:0.27
------------alpha=5.0----------------------
均方误差MSE:9.15
中位数绝对误差:2.06
解释方差分:0.41
R方得分:0.18
------------alpha=10----------------------
均方误差MSE:9.95
中位数绝对误差:2.12
解释方差分:0.27
R方得分:0.02
------------alpha=20----------------------
均方误差MSE:11.47
中位数绝对误差:2.33
解释方差分:-0.05
R方得分:-0.36
------------alpha=50----------------------
均方误差MSE:15.31
中位数绝对误差:2.78
解释方差分:-1.36
R方得分:-1.87

########################小**********结###############################

1,通过修改alpha值,可以修改模型在test set上的表现,由于test set不包含异常点,所以要求alpha取的越小越好,拟合的直线越偏离异常点。

2,alpha取值越大,岭回归得到的拟合直线越“偏向”异常点,此时异常点对直线的影响越大;alpha越小,拟合直线越“偏离”异常点,此时异常点对直线的影响越小,我们所期望的是异常点对直线影响越小越好,所以貌似alpha应该往越小的方向取值。

#################################################################


注:本部分代码已经全部上传到(我的github)上,欢迎下载。

参考资料:

1, Python机器学习经典实例,Prateek Joshi著,陶俊杰,陈小莉译