pytorch二范式 pytorch加入l2正则化_正则


目录:

  • 0 前言
  • 1 学习模型正则化
  • 1.1 什么是模型正则化
  • 1.2 模型的过拟合
  • 2 L1正则化
  • 2.1 LASSO回归思路
  • 2.2 L1正则化项与稀疏性
  • 2.3 L1正则使用
  • 2.4 调参效果
  • 3 L2正则化
  • 3.1 岭回归思路
  • 3.2 L2正则防止过拟合
  • 3.3 L2正则的使用及调参
  • 总结

0 前言

我们已经知道了模型误差 = 偏差 + 方差 + 不可避免的误差,且在机器学习领域中最重要就是解决过拟合的问题,也就是降低模型的方差。在上一篇文章《ML/DL重要基础概念:偏差和方差》已经列出了如下方法:

  • 降低模型复杂度
  • 减少数据维度;降噪
  • 增加样本数
  • 使用验证集

其实还有一个降低方差的重要方法:模型正则化。本文从理论及代码两个方面对L1正则、L2正则进行了介绍,帮助大家了解其背后的原理以及实际的使用方法。

1 学习模型正则化

1.1 什么是模型正则化

模型正则化(Regularization),对学习算法的修改,限制参数的大小,减少泛化误差而不是训练误差。我们在构造机器学习模型时,最终目的是让模型在面对新数据的时候,可以有很好的表现。当你用比较复杂的模型比如神经网络,去拟合数据时,很容易出现过拟合现象(训练集表现很好,测试集表现较差),这会导致模型的泛化能力下降,这时候,我们就需要使用正则化,降低模型的复杂度。

正则化的策略包括: 约束和惩罚被设计为编码特定类型的先验知识 偏好简单模型 其他形式的正则化,如:集成的方法,即结合多个假说解释训练数据

在实践中,过于复杂的模型不一定包含数据的真实的生成过程,甚至也不包括近似过程,这意味着控制模型的复杂程度不是一个很好的方法,或者说不能很好的找到合适的模型的方法。实践中发现的最好的拟合模型通常是一个适当正则化的大型模型。

1.2 模型的过拟合

准备一下数据


import numpy as np
import matplotlib.pyplot as plt

x = np.random.uniform(-3, 3, size=100)
X = x.reshape(-1, 1)
y = 0.5 + x**2 + x + 2 + np.random.normal(0, 1, size=100)
plt.scatter(x, y)


pytorch二范式 pytorch加入l2正则化_正则_02


下面我们要使用多项式回归过拟合一个样本,生成的曲线非常弯曲、陡峭。前面的参数会非常大,正则化要完成的,就是要限制这些系数的大小。


from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split

lin_reg = LinearRegression()
def PolynomialRegression(degree):
    return Pipeline([
        ('poly',PolynomialFeatures(degree)),
        ('std_scaler',StandardScaler()),
        ('lin_reg',lin_reg)
    ])
np.random.seed(666)
X_train, X_test, y_train, y_test = train_test_split(X,y)

poly30_reg = PolynomialRegression(degree=30)
poly30_reg.fit(X_train,y_train)
y30_predict = poly30_reg.predict(X_test)
mean_squared_error(y_test,y30_predict)
# 输出:2.8492121876294063
X_plot = np.linspace(-3,3,100).reshape(100,1)
y_plot = poly30_reg.predict(X_plot)
plt.scatter(X,y)
plt.plot(X_plot[:,0],y_plot,color='r')
plt.axis([-3,3,0,10])
plt.show()


pytorch二范式 pytorch加入l2正则化_正则化的通俗解释_03


在拟合完之后,就是非常明显的过拟合的效果,即目标函数为了尽可能地去拟合数据,减小模型和样本的误差,使得曲线变得很陡峭,在数学上就表示为线性方程前面的系数很大。

那么模型正则化如何解决上述问题呢?

2 L1正则化

所谓的L1正则化,就是在目标函数中加了L1范数这一项。使用L1正则化的模型建叫做LASSO回归。

2.1 LASSO回归思路

线性回归问题去怎样求最优解,其目标相当于:

尽可能小。

这也就是等同于求原始数据


和使用参数


预测的


的均方误差尽可能的小:


尽可能小。

如果模型过拟合的话,参数


就会非常大。为了限制参数


,我们改变损失函数,加入模型正则化,即将其改为:


尽可能小。

这样的话,要使


尽可能小,就要综合考虑两项,对于第二项来说,是


的绝对值,因此我们要考虑让


尽可能小。这样参数


就限制住了,曲线也就没有那么陡峭了,这就是一种模型正则化的基本原理。


该模型正则化的方式被称为“LASSO回归”(Least Absolute Shrinkage and Selection Operator Regression)

在这里有几个细节需要注意:

  • ,取值范围是1~n,即不包含 。这是因为, 不是任何一个参数的系数,是截距。反映到图形上就是 反映了曲线的高低,而不决定曲线每一部分的陡峭与缓和。所以模型正则化时不需要。
  • 对于超参数alpha系数,在模型正则化的新的损失函数中,要让每个 都尽可能小的程度占整个优化损失函数程度的多少。即 alpha的大小表示优化的侧重。

2.2 L1正则化项与稀疏性

我们说,LASSO回归的全称是:Least Absolute Shrinkage and Selection Operator

这里面有一个特征选择的部分,或者说L1正则化可以使得参数稀疏化,即得到的参数是一个稀疏矩阵。

所谓稀疏性,说白了就是模型的很多参数是0。通常机器学习中特征数量很多,例如文本处理时,如果将一个词组(term)作为一个特征,那么特征数量会达到上万个(bigram)。在预测或分类时,那么多特征显然难以选择,但是如果代入这些特征得到的模型是一个稀疏模型,很多参数是0,表示只有少数特征对这个模型有贡献,绝大部分特征是没有贡献的,即使去掉对模型也没有什么影响,此时我们就可以只关注系数是非零值的特征

这相当于对模型进行了一次特征选择,只留下一些比较重要的特征提高模型的泛化能力,降低过拟合的可能

假设在二维参数空间中,损失函数的等高线如下图所示


pytorch二范式 pytorch加入l2正则化_正则_04


此时,L1正则化为


,对应的等高线是一个菱形(我们可以画出多个这样的菱形):


pytorch二范式 pytorch加入l2正则化_pytorch二范式_05


首先来看一下不加L1正则的情况:我们使用梯度下降法去优化损失函数,随机选择一点,沿着梯度方向下降,得到一个近似的最优解M:


pytorch二范式 pytorch加入l2正则化_正则化的通俗解释_06


下面加上L1正则,情况则会有所不同。


两点在同一等高线上,即

P与Q两个点的损失函数这一项上是相同的。但是

的距离要大于

距离

:

pytorch二范式 pytorch加入l2正则化_正则_07


可以得到经验损失函数(损失函数+正则项):



因为点


的L1范数小于点


的L1范数,因此我们更倾向于选择点


,而不是点



而如果选择点


,在直角的顶点上,对应的参数


,这就体现了稀疏性。

因此L1正则化会产生系数模型,好处是应用的特征比较小,模型更简单,运算更快。

由此可见:加入L1正则项相当于倾向将参数向离原点近的方向去压缩。直观上来说,就是加上正则项,参数空间会被缩小,意味着模型的复杂度会变小。

2.3 L1正则使用

我们利用第一节得到的数据来对比使用LASSO回归进行正则化的方式。

在sklearn中,包含了一个方法:Lasso。下面我们以Pipeline的方式去封装一个LASSO回归的过程:


from sklearn.linear_model import Lasso

def LassoRegression(degree,alpha):
    return Pipeline([
        ('poly',PolynomialFeatures(degree=degree)),
        ('std_scaler',StandardScaler()),
        ('lasso_reg',Lasso(alpha=alpha))
    ])


在封装好了一个LASSO回归函数后,传入dregree参数和alpha参数,就可以验证LASSO回归的效果了:


lasso_reg1 = LassoRegression(30,0.0001)
lasso_reg1.fit(X_train,y_train)
y1_predict=lasso_reg1.predict(X_test)
mean_squared_error(y_test,y1_predict)
# 输出:0.8965099322966458
plot_model(lasso_reg1)


pytorch二范式 pytorch加入l2正则化_pytorch l2正则化_08


可以看到,在加入L1正则后,均方误差比原来小,且曲线光滑了很多,效果比较好。

2.4 调参效果

我们保持degree参数不变,调整alpha参数,让其变大,也就是将L1正则项的比重放大,即让参数


变小:


lasso_reg2 = LassoRegression(30,0.1)
lasso_reg2.fit(X_train,y_train)
y2_predict=lasso_reg2.predict(X_test)
mean_squared_error(y_test,y2_predict)
# 输出:0.7848672707093821
plot_model(lasso_reg2)


pytorch二范式 pytorch加入l2正则化_正则_09


我们发现曲线更加的平滑了,相应的均方误差也变得更小了,说明结果更优了。那么如果继续放大alpha系数呢?


lasso_reg3 = LassoRegression(30,10)
lasso_reg3.fit(X_train,y_train)
y3_predict=lasso_reg3.predict(X_test)
mean_squared_error(y_test,y3_predict)
# 输出:12.007790775597146
plot_model(lasso_reg3)


pytorch二范式 pytorch加入l2正则化_正则化_10


很明显,我们正则化得有些过了,变成一条直线了。因此,我们需要找到合适的alpha系数,使正则化效果最好。

3 L2正则化

3.1 岭回归思路

除了如L1正则化一般,将参数累加(


)以外,很自然地联想到,我们也可以用平方和来做正则项。


即将为:

尽可能小。

同样地,要使


尽可能小,就要综合考虑两项,要考虑让


的平方和尽可能小。


该模型正则化的方式被称为“岭回归”

3.2 L2正则防止过拟合

同样地,在二维参数空间中,L2正则项为:


。即L2等高线是一个个的同心圆。


pytorch二范式 pytorch加入l2正则化_正则化的通俗解释_11


二维平面下L2正则化的函数图形是个圆,与方形相比,被磨去了棱角。因此等高线与损失函数相交于


点时,


都不为0,但是仍然比较靠近坐标轴。因此这也就是我们说的,

L2范数能让解比较小(靠近0),但是比较平滑(不等于0)且不具有稀疏性。

此时和w2都不为0. 所以L2范式得到的不是稀疏解


pytorch二范式 pytorch加入l2正则化_正则化_12


3.3 L2正则的使用及调参

我们利用第一节得到的数据来对比使用岭回归进行正则化的方式。

在sklearn中,包含了一个岭回归的方法:Ridge。下面我们以Pipeline的方式去封装一个岭回归的过程:


from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
# 需要传入一个多项式项数的参数degree以及一个alpha值
def ridgeregression(degree,alpha):
    return Pipeline([
        ("poly", PolynomialFeatures(degree=degree)),
        ("standard", StandardScaler()),
        ("ridge_reg", Ridge(alpha=alpha))   #alpha值就是正则化那一项的系数
    ])


在封装好了一个岭回归函数后,就可以验证岭回归的效果了:


ridge1_reg = ridgeregression(degree=30,alpha=0.0001)
ridge1_reg.fit(X_train,y_train)
y1_predict = ridge1_reg.predict(X_test)
mean_squared_error(y_test,y1_predict)
# 输出:1.00677921937119
plot_model(ridge1_reg)


pytorch二范式 pytorch加入l2正则化_正则化_13


我们可以看到输出的均方误差在1左右,比以前的2点多变好了。且通过画图可以看到,曲线变平滑了。

如果我们调整


系数,将其变大,意味着对参数的约束又变强了,曲线会更加光滑:


ridge2_reg = ridgeregression(degree=30,alpha=1)
ridge2_reg.fit(X_train,y_train)
y2_predict = ridge2_reg.predict(X_test)
mean_squared_error(y_test,y2_predict)
# 输出:1.0074844695164857
plot_model(ridge2_reg)


pytorch二范式 pytorch加入l2正则化_正则化的通俗解释_14


如果我们继续增大


系数的话,就会正则化得有些过了,其均方误差也会变小。


ridge3_reg = ridgeregression(degree=30,alpha=100)
ridge3_reg.fit(X_train,y_train)
y3_predict = ridge3_reg.predict(X_test)
mean_squared_error(y_test,y3_predict)
# 输出:3.4183903466684176
plot_model(ridge3_reg)


pytorch二范式 pytorch加入l2正则化_pytorch二范式_15


可见LASSO回归和岭回归类似,


取值过大反而会导致误差增加,拟合曲线为直线。但是LASSO更趋向于使得一部分的


值为0,拟合曲线更趋向于直线,所以可以作为特征选择来使用,去除一些模型认为不需要的特征。 LASSO可能会去除掉正确的特征,从而降低准确度,但如果特征特别大,使用LASSO可以使模型变小。


总结

L1正则化就是在loss function后边所加正则项为L1范数,加上L1范数容易得到稀疏解(0比较多)。

L2正则化就是loss function后边所加正则项为L2范数的平方,加上L2正则相比于L1正则来说,得到的解比较平滑(不是稀疏),但是同样能够保证解中接近于0(但不是等于0,所以相对平滑)的维度比较多,降低模型的复杂度。