一、正则线性模型
减少过度拟合的一个好办法就是对模型正则化(即约束它):它拥有的自由度越低,就越不容易过度拟合数据。比如,将多项式模型正则化的简单方法就是降低多项式的阶数。
对线性模型来说,正则化通常通过约束模型的权重来实现。常见的对权重进行约束的方法有:岭回归(Ridge Regression)、套索回归(lasso Regression)及弹性网络(Elastic Regression)。
二、岭回归
岭回归(也叫作吉洪诺夫正则化)是线性回归的正则化版:在成本函数中添加一个等于 α ∑ i = 1 n θ i 2 \alpha\sum_{i=1}^n\theta_i^2 α∑i=1nθi2的正则项。这使得学习中的算法不仅需要拟合数据,同时还要让模型权重保持最小。
- 正则项只能在训练的时候添加到成本函数中,一旦训练完成,我们需要使用未经正则化的性能指标来评估模型性能。
超参数 α \alpha α控制的是对模型进行正则化的程度。如果 α = 0 \alpha=0 α=0,则岭回归就是线性模型。如果 α \alpha α非常大,那么所有的权重都将非常接近于零,结果是一条穿过数据平均值的水平线。
岭回归的成本函数:
J
(
θ
)
=
M
S
E
(
θ
)
+
α
1
2
∑
i
=
1
n
θ
i
2
J(\theta)=MSE(\theta)+\alpha\frac12\sum_{i=1}^n\theta_i^2
J(θ)=MSE(θ)+α21i=1∑nθi2
注意,这里偏置项
θ
0
\theta_0
θ0没有正则化(求和从
i
=
1
i=1
i=1开始,不是
i
=
0
i=0
i=0)。如果我们将
w
w
w定义为特征权重的向量(
θ
1
\theta_1
θ1到
θ
n
\theta_n
θn),那么正则项即等于
1
2
(
∣
∣
w
∣
∣
2
)
2
\frac12(||w||_2)^2
21(∣∣w∣∣2)2其中
∣
∣
w
∣
∣
2
||w||_2
∣∣w∣∣2为权重向量的
l
2
l_2
l2范数。而对于梯度下降,只需要在MSE梯度向量上添加
α
w
\alpha w
αw即可。
- 在执行岭回归之前,必须对数据进行缩放,因为它对输入特征的大小非常敏感。大多数正则化模型都是如此。
与线性回归一样,我们也可以在计算闭市方程或者执行梯度下降时,执行岭回归。利弊都一样。
闭式解的岭回归(其中
A
A
A是一个nXn的单位矩阵,除了左上单元格为0,其他与偏置项对应):
θ
^
=
(
X
T
⋅
X
+
α
A
)
−
1
⋅
X
T
⋅
y
\hat{\theta}=(X^T\cdot X+\alpha A)^{-1}\cdot X^T\cdot y
θ^=(XT⋅X+αA)−1⋅XT⋅y
下面是如何使用Scikit-Learn执行闭式解的岭回归:
首先随机生成一些含有噪声的数据:
import numpy as np
import matplotlib.pyplot as plt
X=3*np.random.rand(100,1)
y=3+2*X+np.random.randn(100,1)
plt.plot(X,y,'b.')
plt.show()
训练模型(使用的是闭式解公式的一种变体,利用Andre-Louis Cholesky的矩阵因式分解法):
from sklearn.linear_model import Ridge
ridge_reg=Ridge(alpha=1,solver='cholesky')
ridge_reg.fit(X,y)
Ridge(alpha=1, copy_X=True, fit_intercept=True, max_iter=None,
normalize=False, random_state=None, solver='cholesky', tol=0.001)
ridge_reg.intercept_,ridge_reg.coef_
(array([ 3.02790174]), array([[ 1.907145]]))
ridge_reg.predict([[1.5]])
array([[ 5.88861924]])
使用随机梯度下降:
from sklearn.linear_model import SGDRegressor
sgd_reg=SGDRegressor(penalty='l2')
sgd_reg.fit(X,y.ravel())
D:\Anaconda\lib\site-packages\sklearn\linear_model\stochastic_gradient.py:128: FutureWarning: max_iter and tol parameters have been added in <class 'sklearn.linear_model.stochastic_gradient.SGDRegressor'> in 0.19. If both are left unset, they default to max_iter=5 and tol=None. If tol is not None, max_iter defaults to max_iter=1000. From 0.21, default max_iter will be 1000, and default tol will be 1e-3.
"and default tol will be 1e-3." % type(self), FutureWarning)
SGDRegressor(alpha=0.0001, average=False, epsilon=0.1, eta0=0.01,
fit_intercept=True, l1_ratio=0.15, learning_rate='invscaling',
loss='squared_loss', max_iter=None, n_iter=None, penalty='l2',
power_t=0.25, random_state=None, shuffle=True, tol=None, verbose=0,
warm_start=False)
超参数penalty设置的是使用正则项的类型。设为“ l 2 l2 l2”表示希望SGD在成本函数中添加一个正则项,等于权重向量的 l 2 l2 l2范数的平方的一半,即岭回归。
sgd_reg.intercept_,sgd_reg.coef_
(array([ 1.91897045]), array([ 2.51547769]))
sgd_reg.predict([[1.5]])
array([ 5.69218699])
三、套索回归
线性回归的另一种正则化,叫作最小绝对收缩和选择算子回归(Least Absolute Shrinkage and Selection Operator Regression,简称Lasso回归,或套索回归)。与岭回归一样,它也是向成本函数增加一个正则项,但是它增加的是权重 向量的 l 1 l1 l1范数,而不是 l 2 l2 l2范数的平方的一半。
Lasso回归成本函数:
J
(
θ
)
=
M
S
E
(
θ
)
+
α
∑
i
=
1
n
∣
θ
i
∣
J(\theta)=MSE(\theta)+\alpha\sum_{i=1}^n|\theta_i|
J(θ)=MSE(θ)+αi=1∑n∣θi∣
Lasso回归的一个重要特点是它倾向于完全消除掉最不重要特征的权重(也就是将它们设置为零)。换句话说,Lasso回归会自动执行特征选择并输出一个稀疏模型(即只有很少的特征有非零权重)。
下面是一个使用Scikit-Learn的Lasso类的例子,我们也可以用SGDRegression(penalty=’ l 1 l1 l1’):
from sklearn.linear_model import Lasso
lasso_reg=Lasso(alpha=0.1)
lasso_reg.fit(X,y)
Lasso(alpha=0.1, copy_X=True, fit_intercept=True, max_iter=1000,
normalize=False, positive=False, precompute=False, random_state=None,
selection='cyclic', tol=0.0001, warm_start=False)
lasso_reg.intercept_,lasso_reg.coef_
(array([ 3.18622698]), array([ 1.79448133]))
lasso_reg.predict([[1.5]])
array([ 5.87794898])
四、弹性网络
弹性网络是岭回归与Lasso回归之间的中间地带。其正则项就是岭回归和Lasso回归的正则项的混合,混合比例通过r来控制。当r=0时,弹性网络即等同于岭回归,而当r=1时,即相当于Lasso回归。
弹性网络成本函数:
J
(
θ
)
=
M
S
E
(
θ
)
+
r
α
∑
i
=
1
n
∣
θ
i
∣
+
1
−
r
2
α
∑
i
=
1
n
θ
i
2
J(\theta)=MSE(\theta)+r\alpha\sum_{i=1}^n|\theta_i|+\frac{1-r}{2}\alpha\sum_{i=1}^n\theta_i^2
J(θ)=MSE(θ)+rαi=1∑n∣θi∣+21−rαi=1∑nθi2
下面是一个使用Scikit-Learn的ElasticNet的例子(
l
1
_
r
a
t
i
o
l1\_ratio
l1_ratio对应混合比例
r
r
r):
from sklearn.linear_model import ElasticNet
elastic_net=ElasticNet(alpha=0.1,l1_ratio=0.5)
elastic_net.fit(X,y)
ElasticNet(alpha=0.1, copy_X=True, fit_intercept=True, l1_ratio=0.5,
max_iter=1000, normalize=False, positive=False, precompute=False,
random_state=None, selection='cyclic', tol=0.0001, warm_start=False)
elastic_net.intercept_,elastic_net.coef_
(array([ 3.25888406]), array([ 1.74277881]))
elastic_net.predict([[1.5]])
array([ 5.87305228])
- 那么,到底如何选用线性回归、岭回归、Lasso回归和弹性回归呢?通常来说,有正则化——哪怕是很小,总是比没有更可取一些。所以大多数情况下,我们应该避免使用纯线性回归。岭回归是个不错的默认选择,但是如果你觉得实际用到的特征只有少数几个,那就更倾向于Lasso回归或是弹性网络,因为它们会将无用特征的权重降为零。一般而言,弹性网络优于Lasso回归,因为当特征数量超过训练实例数量,又或者是几个特征强相关时,Lasso回归的表现可能非常不稳定。
五、早期停止法
对于梯度下降这一类迭代学习的算法,还有一个与众不同的正则化方法,就是在验证误差达到最小值时停止训练,该方法叫作早期停止法。通过早期停止法,一旦验证误差达到最小值就立刻停止训练。这是一个非常简单而有效的正则化技巧,所以Geoffery Hinton(神经网络之父)称其为“美丽的免费午餐”。
对随机梯度下降和小批量梯度下降来说,曲线没有那么平滑,所以很难知道是否已经达到最小值。一种解决办法是等验证误差超过最小值一段时间之后再停止,然后将模型参数回滚到验证误差最小时的位置。
下面是早期停止法的基本实现:
from sklearn.base import clone
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
sgd_reg=SGDRegressor(n_iter=1,warm_start=True,penalty=None,learning_rate='constant',eta0=0.0005)
minimum_val_error=float('inf')
best_epoch=None
best_model=None
X_train,X_val,y_train,y_val=train_test_split(X,y,test_size=0.2)
for epoch in range(1000):
sgd_reg.fit(X_train,y_train)
y_val_predict=sgd_reg.predict(X_val)
val_error=mean_squared_error(y_val_predict,y_val)
if val_error<minimum_val_error:
minimum_val_error=val_error
best_epoch=epoch
best_model=clone(sgd_reg)
当warm_start=True时,调用fit()方法,会从停下的地方继续开始训练,而不会重新开始。
看下最终结果:
best_epoch
324
算法迭代到第324轮验证误差已经达到最小值。
再看下最终得到的最佳模型:
best_model
SGDRegressor(alpha=0.0001, average=False, epsilon=0.1, eta0=0.0005,
fit_intercept=True, l1_ratio=0.15, learning_rate='constant',
loss='squared_loss', max_iter=None, n_iter=1, penalty=None,
power_t=0.25, random_state=None, shuffle=True, tol=None, verbose=0,
warm_start=True)