凌云时刻 · 技术
导读:上一篇我们在验证数据集概念的基础上讲了交叉验证。这一篇我们继续来讲模型正则化。
作者 | 计缘
来源 | 凌云时刻(微信号:linuxpk)
模型正则化(Regularization)
在说模型正则化之前,我们先来看一个例子:
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.plot(np.sort(x), y[np.argsort(x)], color='r')
plt.show()
可以看到当计算y
的方程,多项式前的系数比较低(0.5和1)的时候折线图在横轴从-3到3,纵轴从-1到10的坐标系里还能全部展现,如果将这两个系数扩大10倍,会出现什么情况呢:
y1 = 5 * x ** 2 + 10 * x + 2 + np.random.normal(0, 1, size=100)
plt.plot(np.sort(x), y1[np.argsort(x)], color='r')
plt.axis([-3, 3, -1, 10])
plt.show()
可以看到这个折线图波动已经非常大了,在同样的坐标系中只能展现出一部分了。我们再来回顾一下前面的那张过拟合的图:
上图中,两侧的曲线图波动非常大,其实就说明了这条曲线的线性多项式方程的系数非常大,那么模型正则化做的事情就是限制这些系数的大小。
下面来看看模型正则化的基本思路,在第四篇笔记中将多元线性回归问题的时候,我们知道最终求的是下面这个函数的最优解,既让下面这个损失函数的值尽可能的小:
下面我们来转变一下这个损失函数:
我们在损失函数里加了一部分 ,此时要想让损失函数尽可能的小,就不能只考虑前面那一部分了,还要考虑后面新加的这一部分,又因为后面这部分包含多项式系数的平方,所以就整体约束了多项式系数的大小。这就是模型正则化的基本思路。
这里的 就是一个新的超参数,它的含义代表在模型正则化下,新的损失函数中每一个 都尽可能的小,这个小的程度占整个优化损失函数的多少。比如如果 是0,那么相当于损失函数没有加入模型正则化,如果 非常非常大,那么真正的损失函数就可以忽略了,主要考虑使模型正则化中的 尽可能小。所以 的作用就是让真正的损失函数尽可能小和新加入的模型正则化中的 尽可能小之间找到一个平衡,在实际的运用中,不同的数据, 的取值也不同,是需要有不断调整 这个超参数的过程。
岭回归(Ridge Regression)
在模型正则化中,加入 的方式,称之为岭回归。下面通过不使用模型正则化和使用岭回归对比来看看:
import numpy as np
import matplotlib.pyplot as plt
# 构建样本数据
np.random.seed(666)
x = np.random.uniform(-3, 3, size=100)
X = x.reshape(-1, 1)
y = 0.5 * x + 3 + np.random.normal(0, 1, size=100)
plt.scatter(x, y)
plt.show()
# 导入Pipeline和其他需要打包进Pipeline的类
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
# 将样本数据集拆分为训练数据集和测试数据集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=666)
# 构建多项式线性回归Pipeline
def PloynomialRegression(degree):
return Pipeline([
("poly", PolynomialFeatures(degree=degree)),
("std_scalar", StandardScaler()),
("lr", LinearRegression())
])
# 导入均方误差MSE函数
from sklearn.metrics import mean_squared_error
ploy_reg = PloynomialRegression(degree=20)
ploy_reg.fit(X_train, y_train)
y_poly_predict = ploy_reg.predict(X_test)
mean_squared_error(y_test, y_poly_predict)
# 结果
1.9558727426614517
X_ploy = np.linspace(-3, 3, 100).reshape(100, 1)
y_ploy = ploy_reg.predict(X_ploy)
plt.scatter(x, y)
plt.plot(X_ploy[:, 0], y_ploy, color='r')
plt.axis([-3, 3, 0, 6])
plt.show()
可以看到不试用模型正则化时,MSE为1.95,虽然数值不大,但是拟合曲线的波动非常大,是一个过拟合的特征。下面来看看使用模型正则化岭回归后结果会怎样:
# 导入Scikit Learn中岭回归的类Ridge
from sklearn.linear_model import Ridge
# 构建通过岭回归进行模型正则化的Pipeline,有两个超参数degree和alpha
def RidgeRegression(degree, alpha):
return Pipeline([
("poly", PolynomialFeatures(degree=degree)),
("std_scalar", StandardScaler()),
("rr", Ridge(alpha=alpha))
])
ridge_reg = RidgeRegression(20, 0.0001)
ridge_reg.fit(X_train, y_train)
y_ridge_predict = ridge_reg.predict(X_test)
mean_squared_error(y_test, y_ridge_predict)
# 结果
1.0033258623784587
X_ridge = np.linspace(-3, 3, 100).reshape(100, 1)
y_ridge = ridge_reg.predict(X_ridge)
plt.scatter(x, y)
plt.plot(X_ridge[:, 0], y_ridge, color='r')
plt.axis([-3, 3, 0, 6])
plt.show()
使用岭回归后MSE降低到1,并且拟合曲线波动平缓了许多,尤其是曲线两头。这就是模型正则化的作用。
其实岭回归和LASSO都是模型正则化的一种具体实现,区别就在于增加的模型正则公式不同。岭回归增加的是 ,而LASSO回归中增加的是 。
下面来看看相同的数据使用LASSO回归后的结果:
import numpy as np
import matplotlib.pyplot as plt
# 构建样本数据
np.random.seed(666)
x = np.random.uniform(-3, 3, size=100)
X = x.reshape(-1, 1)
y = 0.5 * x + 3 + np.random.normal(0, 1, size=100)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=666)
# 导入Pipeline和其他需要打包进Pipeline的类
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import Lasso
from sklearn.metrics import mean_squared_error
def LassoRegression(degree, alpha):
return Pipeline([
("poly", PolynomialFeatures(degree=degree)),
("std_scalar", StandardScaler()),
("lasso", Lasso(alpha=alpha))
])
lasso_reg = LassoRegression(20, 0.01)
lasso_reg.fit(X_train, y_train)
y_lasso_predict = lasso_reg.predict(X_test)
mean_squared_error(y_test, y_lasso_predict)
# 结果
0.90488682905372464
X_lasso = np.linspace(-3, 3, 100).reshape(100, 1)
y_lasso = lasso_reg.predict(X_lasso)
plt.scatter(x, y)
plt.plot(X_lasso[:, 0], y_lasso, color='r')
plt.axis([-3, 3, 0, 6])
plt.show()
可以看到相同的数据,通过LASSO处理后的模型MSE更小一些,拟合曲线也更平滑。
下面来解释一下岭回归和LASSO回归之间深层意义上的区别。先来看看岭回归,在前面讲过,当 趋近无穷大时,真正的损失函数就可以忽略了,模型正则化后的损失函数就变成了求解模型正则公式的最小值,既求 的最小值,那么该公式的梯度就是对 求导可得:
数据点按照梯度一步一步寻求最优解,如下图:
所以经过岭回归处理后训练出的模型,拟合线基本都是曲线。
再来看看LASSO回归,当 趋近无穷大时,我们只考虑 ,但是 是不可导的,我们使用数学符号函数来表示其梯度:
符号函数 是很有用的一类函数,能够帮助我们实现一些直接实现有困难的情况。在数学和计算机运算中,其功能是取某个数的符号(正或负),既当 时, ,当 时, ,当 时, 。
对于 而言,当 时,就相当于 这条直线,当 时,就相当于 这条直线。当数据点按照LASSO的梯度寻找最优解的路线是下图情况:
可以看到真正的寻址路线是橘黄色的虚线,应该中间有很多点直接打到了 轴,所以绘制出的拟合蓝色线很多时候就是一条直线,而不是曲线。那些 为0的梯度,既损失函数中的一部分 为0,这也就说明了LASSO中最后的SO(Selection Operator)选择操作符的含义,既将一些噪音比较大的特征过滤掉,选择出主要的、有用的特征。但是这是有风险的,因为很有可能LASSO将实际有用的特征给过滤掉了,所以就模型正则化的准确率来说,岭回归还是更好一些。但是在处理有巨大量特征的样本数据时,使用LASSO可以作为降低特征数量的一种方法。
LP范数
在数学定义上,范数包括向量范数和矩阵范数,向量范数表示向量空间中向量的大小,矩阵范数表征矩阵引起向量变化的大小。比如对于向量范数,向量空间中的向量都是有大小的,这个大小如何度量,就是用范数来度量的,不同的范数都可以来度量这个大小,就好比米和尺都可以来度量远近一样。对于矩阵范数,我们知道,通过运算 ,可以将向量 变化为矩阵 ,矩阵范数就是来度量这个变化大小的。
下面我们再来看一张图:
上图展示了模型正则化岭回归、LASSO回归,线性回归评测标准均方误差(MSE)、平均绝对误差(MAE),距离公式欧拉距离、曼哈顿距离之间的对比。我们可以发现一个很有意思的现象,第一行的三个公式都是平方求和的模式,第二行的三个公式都是绝对值求和的模式。其实虽然机器学习中的公式、名词很多,但是究其背后的数学原理都是有规律可循的。
我们在讲KNN时知道它除了 这个超参数外还有一个超参数 ,继而介绍了明可夫斯基距离:
我们对明可夫斯基距离公式再进行一下泛化,将其提炼成这种形式:
在数学上,我们将上面这个公式称为 范数,当 为1时, 范数就是曼哈顿距离, 为2时, 范数就是欧拉距离。那么在模型正则项中,是 范数就是岭回归,或者叫 正则项, 范数就是LASSO回归,或者叫 正则化。
这里要注意的时,如果是 范数,岭回归的模型正则化公式应该还需要开根号,但是为了计算方便,一般使用时不加这个开根号,但是加不加根号对于模型正则化的效果来说是一样的。
弹性网很简单,就是将 正则项和 正则项都加入模型正则化中,既结合了岭回归和LASSO回归:
公式中看到我们又引入了一个超参数 ,这个超参数表示岭回归和LASSO回归在整个模型正则化中各占的比例。
END
往期精彩文章回顾
机器学习笔记(十七):交叉验证
机器学习笔记(十六):多项式回归、拟合程度、模型泛化
机器学习笔记(十五):人脸识别
机器学习笔记(十四):主成分分析法(PCA)(2)
机器学习笔记(十三):主成分分析法(PCA)
机器学习笔记(十二):随机梯度下降
机器学习笔记(十一):优化梯度公式机器学习笔记(十):梯度下降
机器学习笔记(九):多元线性回归
机器学习笔记(八):线性回归算法的评测标准长按扫描二维码关注凌云时刻
每日收获前沿技术与科技洞见