第四章 训练模型
· Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition, by Aurélien Géron (O’Reilly). Copyright 2019 Aurélien Géron, 978-1-492-03264-9.
· 环境:Anaconda(Python 3.8) + Pycharm
· 学习时间:2022.04.16
到目前为止,我们已经探讨了不同机器学习的模型,但是它们各自的训练算法在很大程度上还是一个黑匣子。回顾前几章里的部分案例,你大概感到非常惊讶,在对系统内部一无所知的情况下,居然已经实现了这么多:优化了一个回归系统,改进了一个数字图片分类器,从零开始构建了一个垃圾邮件分类器,所有这些,你都不知道它们实际是如何工作的。确实是这样,在许多情况下,你并不需要了解实施细节。
但是,很好地理解系统如何工作也是非常有帮助的。针对你的任务,它有助于快速定位到合适的模型、正确的训练算法,以及一套适当的超参数。不仅如此,后期还能让你更高效地执行错误调试和错误分析。最后还要强调一点,本章探讨的大部分主题对于理解、构建和训练神经网络(本书第二部分)是至关重要的。
本章我们将从最简单的模型之一——线性回归模型,开始介绍两种非常不同的训练模型的方法:
- 通过“闭式”方程,直接计算出最拟合训练集的模型参数(也就是使训练集上的成本函数最小化的模型参数);
- 使用迭代优化的方法,即梯度下降(GD),逐渐调整模型参数直至训练集上的成本函数调至最低,最终趋同于第一种方法计算出来的模型参数。我们还会研究几个梯度下降的变体,包括批量梯度下降、小批量梯度下降以及随机梯度下降。等我们进入到第二部分神经网络的学习时,会频繁地使用这几个的变体。
接着我们将会进入多项式回归的讨论,这是一个更为复杂的模型,更适合非线性数据集。由于该模型的参数比线性模型更多,因此更容易造成对训练数据过拟合,我们将使用学习曲线来分辨这种情况是否发生。然后,再介绍几种正则化技巧,降低过拟合训练数据的风险。
最后,我们将学习两种经常用于分类任务的模型:Logistic回归和Softmax回归。
本章将会出现不少数学公式,需要用到线性代数和微积分的一些基本概念。要理解这些方程式,你需要知道什么是向量和矩阵,如何转置向量和矩阵,什么是点积、逆矩阵、偏导数。如果你不熟悉这些概念,请先通过在线补充材料中的Jupyter notebook,进行线性代数和微积分的入门学习。对于极度讨厌数学的读者,还是需要学习这一章,但是可以跳过那些数学公式,希望文字足以让你了解大多数的概念。
文章目录
4.1 线性回归
线性模型就是对输入特征加权求和,再加上一个我们称为偏置项(也称为截距项)的常数,以此进行预测。
y
=
θ
0
+
θ
1
x
1
+
θ
2
x
2
+
…
…
+
θ
n
x
n
y = θ_0 + θ_1x_1 +θ_2x_2+ …… +θ_nx_n
y=θ0+θ1x1+θ2x2+……+θnxn
线性回归模型预测(向量化形式):
y
=
h
θ
(
x
)
=
θ
⋅
x
y = h_θ(x) = θ·x
y=hθ(x)=θ⋅x。
在机器学习中,向量通常表示为列向量,是有单一列的二维数组。如果
θ
θ
θ和
x
x
x为列向量,则预测为$y = θ^Tx
,
其
中
,其中
,其中θT$为$θ$(行向量而不是列向量)的转置,且$θTx
为
θ
为θ
为θT
和
和
和x$的矩阵乘积。
这就是线性回归模型,我们该怎样训练线性回归模型呢?回想一下,训练模型就是设置模型参数直到模型最拟合训练集的过程。为此,我们首先需要知道怎么测量模型对训练数据的拟合程度是好还是差。在第2章中,我们了解到回归模型最常见的性能指标是均方根误差(RMSE)。因此,在训练线性回归模型时,你需要找到最小化RMSE的
θ
θ
θ值。在实践中,将均方误差(MSE)最小化比最小化RMSE更为简单,二者效果相同(因为使函数最小化的值,同样也使其平方根最小)。
线性回归模型的MSE成本函数:
M
S
E
=
(
X
,
h
0
)
=
1
m
∑
i
=
1
m
(
θ
T
x
(
i
)
−
y
(
i
)
)
2
MSE = (X, h_0) = \frac{1}{m}\sum^m_{i=1}(θ^Tx^{(i)}-y^{(i)})^2
MSE=(X,h0)=m1∑i=1m(θTx(i)−y(i))2。
4.1.1 标准方程
为了得到使成本函数最小的θ值,有一个闭式解方法——也就是一个直接得出结果的数学方程,即标准方程。
θ
′
=
(
X
T
X
)
−
1
X
T
y
θ' = (X^TX)^{-1}X^Ty
θ′=(XTX)−1XTy,我们生成一些线性数据来测试这个公式:
import numpy as np
import matplotlib.pyplot as plt
# 随机生成数据
X =2* np.random.rand(100,1)
y =4+3* X + np.random.randn(100,1)
X_b = np.c_[np.ones((100,1)), X]# add x0 = 1 to each instance
theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)# dot()方法计算矩阵内积print(theta_best)# 输出:期待的是θ0=4,θ1=3得到的是θ0=3.6,θ1=3.2。非常接近,噪声的存在使其不可能完全还原为原本的函数# 根据参数做出预测
X_new = np.array([[0],[2]])
X_new_b = np.c_[np.ones((2,1)), X_new]# add x0 = 1 to each instance
y_predict = X_new_b.dot(theta_best)print(y_predict)# 绘制模型的预测结果
plt.plot(X_new, y_predict,"r-")
plt.plot(X, y,"b.")
plt.axis([0,2,0,15])
plt.show()
使用Scikit-Learn执行线性回归很简单:
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()# 实例化线性模型
lin_reg.fit(X, y)# 训练模型print(lin_reg.intercept_, lin_reg.coef_)# 输出模型训练后的参数print(lin_reg.predict(X_new))# 进行预测# LinearRegression类基于scipy.linalg.lstsq()函数(名称代表“最小二乘”),你可以直接调用它:
theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6)print(theta_best_svd)# 输出最优的参数# LinearRegression()模型的参数和scipy.linalg.lstsq()函数的参数输出是一致的
scipy.linalg.lstsq()
函数计算
θ
′
=
X
+
y
θ' = X^+y
θ′=X+y ,其中
X
+
X^+
X+是
X
X
X的伪逆。伪逆本身是使用被称为**奇异值分解(Singular Value Decomposition,SVD)**的标准矩阵分解技术来计算的,可以将训练集矩阵
X
X
X分解为三个矩阵
U
Σ
V
T
UΣV^T
UΣVT的乘积。为了计算矩阵
Σ
+
Σ^+
Σ+,该算法取
Σ
Σ
Σ并将所有小于一个小阈值的值设置为零,然后将所有非零值替换成它们的倒数,最后把结果矩阵转置。再加上它可以很好地处理边缘情况这种,方法比计算标准方程更有效。
4.1.2 计算复杂度
标准方程计算XT X的逆,XT X是一个(n+1)×(n+1)的矩阵(n是特征数量)。对这种矩阵求逆的计算复杂度通常为O(n2.4)到O(n3)之间,取决于具体实现。换句话说,如果将特征数量翻倍,那么计算时间将乘以大约22.4=5.3倍到23=8倍之间。
Scikit-Learn的LinearRegression类使用的SVD方法的复杂度约为O(n2)。如果你将特征数量加倍,那计算时间大约是原来的4倍。
特征数量比较大(例如100 000)时,标准方程和SVD的计算将极其缓慢。好的一面是,相对于训练集中的实例数量(O(m))来说,两个都是线性的,所以能够有效地处理大量的训练集,只要内存足够。
同样,线性回归模型一经训练(不论是标准方程还是其他算法),预测就非常快速:因为计算复杂度相对于想要预测的实例数量和特征数量来说都是线性的。换句话说,对两倍的实例(或者是两倍的特征数)进行预测,大概需要两倍的时间。
现在,我们再看几个截然不同的线性回归模型的训练方法,这些方法更适合特征数或者训练实例数量大到内存无法满足要求的场景。
4.2 梯度下降及其算法
梯度下降是一种非常通用的优化算法,能够为大范围的问题找到最优解。梯度下降的中心思想就是迭代地调整参数从而使成本函数最小化。
假设你迷失在山上的浓雾之中,你能感觉到的只有你脚下路面的坡度。快速到达山脚的一个策略就是沿着最陡的方向下坡。这就是梯度下降的做法:通过测量参数向量θ相关的误差函数的局部梯度,并不断沿着降低梯度的方向调整,直到梯度降为0,到达最小值!
具体来说,首先使用一个随机的
θ
θ
θ值(这被称为随机初始化),然后逐步改进,每次踏出一步,每一步都尝试降低一点成本函数(如MSE),直到算法收敛出一个最小值(参见图4-3)。
梯度下降中一个重要参数是每一步的步长,这取决于超参数学习率。如果学习率太低,算法需要经过大量迭代才能收敛,这将耗费很长时间;反过来说,如果学习率太高,那你可能会越过山谷直接到达另一边,甚至有可能比之前的起点还要高。这会导致算法发散,值越来越大,最后无法找到好的解决方案。
最后,并不是所有的成本函数看起来都像一个漂亮的碗。有的可能看着像洞、山脉、高原或者各种不规则的地形,导致很难收敛到最小值。下图显示了梯度下降的两个主要挑战:如果随机初始化,算法从左侧起步,那么会收敛到一个局部最小值,而不是全局最小值。如果算法从右侧起步,那么需要经过很长时间才能越过整片高原,如果你停下得太早,将永远达不到全局最小值。
幸好,线性回归模型的MSE成本函数恰好是个凸函数,这意味着连接曲线上任意两点的线段永远不会跟曲线相交。也就是说,不存在局部最小值,只有一个全局最小值。它同时也是一个连续函数,所以斜率不会产生陡峭的变化。这两点保证的结论是:即便是乱走,梯度下降都可以趋近到全局最小值(只要等待时间足够长,学习率也不是太高)。
成本函数虽然是碗状的,但如果不同特征的尺寸差别巨大,那它可能是一个非常细长的碗。如图4-7所示的梯度下降,左边的训练集上特征1和特征2具有相同的数值规模,而右边的训练集上,特征1的值则比特征2要小得多(注:因为特征1的值较小,所以θ1需要更大的变化来影响成本函数,这就是为什么碗形会沿着θ1轴拉长。)。
正如你所见,左图的梯度下降算法直接走向最小值,可以快速到达。而在右图中,先是沿着与全局最小值方向近乎垂直的方向前进,接下来是一段几乎平坦的长长的山谷。最终还是会抵达最小值,但是这需要花费大量的时间。
应用梯度下降时,需要保证所有特征值的大小比例都差不多(比如使用Scikit-Learn的StandardScaler类),否则收敛的时间会长很多。
上图也说明,训练模型也就是搜寻使成本函数(在训练集上)最小化的参数组合。这是模型参数空间层面上的搜索:模型的参数越多,这个空间的维度就越多,搜索就越难。同样是在干草堆里寻找一根针,在一个三百维的空间里就比在一个三维空间里要棘手得多。幸运的是,线性回归模型的成本函数是凸函数,针就躺在碗底。
4.2.1 批量梯度下降(Batch Gradient Descent,BGD)
要实现梯度下降,你需要计算每个模型关于参数
θ
j
θ_j
θj的成本函数的梯度。换言之,你需要计算的是如果改变
θ
j
θ_j
θj,成本函数会改变多少。这被称为偏导数。
如果不想单独计算这些偏导数,可以使用公式对其进行一次性计算。梯度向量记作
▽
θ
M
S
E
(
θ
)
▽θMSE(θ)
▽θMSE(θ),包含所有成本函数(每个模型参数一个)的偏导数。
请注意,在计算梯度下降的每一步时,都是基于完整的训练集X的。这就是为什么该算法会被称为批量梯度下降:每一步都使用整批训练数据(实际上,全梯度下降可能是个更好的名字)。因此,面对非常庞大的训练集时,算法会变得极慢(不过我们即将看到快得多的梯度下降算法)。但是,梯度下降算法随特征数量扩展的表现比较好。如果要训练的线性模型拥有几十万个特征,使用梯度下降比标准方程或者SVD要快得多。
一旦有了梯度向量,哪个点向上,就朝反方向下坡。也就是从
θ
θ
θ中减去
▽
θ
M
S
E
(
θ
)
▽θMSE(θ)
▽θMSE(θ)。这时学习率
η
η
η就发挥作用了:用梯度向量乘以
η
η
η确定下坡步长的大小。
让我们看一下该算法的快速实现:
eta =0.1# learning rate
n_iterations =1000
m =100
theta = np.random.randn(2,1)# random initializationfor iteration inrange(n_iterations):
gradients =2/m * X_b.T.dot(X_b.dot(theta)- y)
theta = theta - eta * gradients
print(theta)
下图展现了分别使用三种不同的学习率时,梯度下降的前十步(虚线表示起点)。
要找到合适的学习率,可以使用⭐网格搜索⭐(见第2章)。但是你可能需要限制迭代次数,这样网格搜索可以淘汰掉那些收敛耗时太长的模型。
你可能会问,要怎么限制迭代次数呢?如果设置太低,算法可能在离最优解还很远时就停了。但是如果设置得太高,模型达到最优解后,继续迭代则参数不再变化,又会浪费时间。一个简单的办法是在开始时设置一个非常大的迭代次数,但是当梯度向量的值变得很微小时中断算法——也就是当它的范数变得低于(称为容差)时,因为这时梯度下降已经(几乎)到达了最小值。
收敛速度:
成本函数为凸函数,并且斜率没有陡峭的变化时(如MSE成本函数),具有固定学习率的批量梯度下降最终会收敛到最佳解,但是你需要等待一段时间:它可以进行O(1/∈)次迭代以在∈的范围内达到最佳值,具体取决于成本函数的形状。换句话说,如果将容差缩小为原来的1/10(以得到更精确的解),算法将不得不运行10倍的时间。
4.2.2 随机梯度下降(Stochastic Gradient Descent,SGD)
批量梯度下降的主要问题是它要用整个训练集来计算每一步的梯度,所以训练集很大时,算法会特别慢。与之相反的极端是随机梯度下降,每一步在训练集中随机选择一个实例,并且仅基于该单个实例来计算梯度。显然,这让算法变得快多了,因为每次迭代都只需要操作少量的数据。它也可以被用来训练海量的数据集,因为每次迭代只需要在内存中运行一个实例即可(SGD可以作为核外算法实现,见第1章)。
另一方面,由于算法的随机性质,它比批量梯度下降要不规则得多。成本函数将不再是缓缓降低直到抵达最小值,而是不断上上下下,但是从整体来看,还是在慢慢下降。随着时间的推移,最终会非常接近最小值,但是即使它到达了最小值,依旧还会持续反弹,永远不会停止(见图4-9)。所以算法停下来的参数值肯定是足够好的,但不是最优的。
当成本函数非常不规则时(见图4-6),随机梯度下降其实可以帮助算法跳出局部最小值,所以相比批量梯度下降,它对找到全局最小值更有优势。
因此,随机性的好处在于可以逃离局部最优,但缺点是永远定位不出最小值。要解决这个困境,有一个办法是逐步降低学习率。开始的步长比较大(这有助于快速进展和逃离局部最小值),然后越来越小,让算法尽量靠近全局最小值。这个过程叫作模拟退火,因为它类似于冶金时熔化的金属慢慢冷却的退火过程。确定每个迭代学习率的函数叫作学习率调度。如果学习率降得太快,可能会陷入局部最小值,甚至是停留在走向最小值的半途中。如果学习率降得太慢,你需要太长时间才能跳到差不多最小值附近,如果提早结束训练,可能只得到一个次优的解决方案。
下面这段代码使用了一个简单的学习率调度实现随机梯度下降:
deflearning_schedule(t):return t0 /(t + t1)
theta = np.random.randn(2,1)# random initializationfor epoch inrange(n_epochs):for i inrange(m):
random_index = np.random.randint(m)
xi = X_b[random_index:random_index+1]
yi = y[random_index:random_index+1]
gradients =2* xi.T.dot(xi.dot(theta)- yi)
eta = learning_schedule(epoch * m + i)
theta = theta - eta * gradients
print(theta)
按照惯例,我们进行m个回合的迭代。每个回合称为一个轮次。虽然批量梯度下降代码在整个训练集中进行了1000次迭代,但此代码仅在训练集中遍历了50次,并达到了一个很好的解决方案:
使用随机梯度下降时,训练实例必须独立且均匀分布(IID),以确保平均而言将参数拉向全局最优值。确保这一点的一种简单方法是在训练过程中对实例进行随机混洗(例如,随机选择每个实例,或者在每个轮次开始时随机混洗训练集)。如果不对实例进行混洗(例如,如果实例按标签排序),那么SGD将首先针对一个标签进行优化,然后针对下一个标签进行优化,以此类推,并且它不会接近全局最小值。
要使用带有Scikit-Learn的随机梯度下降执行线性回归,可以使用SGDRegressor类,该类默认优化平方误差成本函数。以下代码最多可运行100000个轮次,或者直到一个轮次期间损失下降小于0.001为止(max_iter=1000,tol=1e-3)。它使用默认的学习调度(与前一个学习调度不同)以0.1(eta0=0.1)的学习率开始。最后,它不使用任何正则化(penalty=None,稍后将对此进行详细介绍):
from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(max_iter=100000, tol=1e-3, penalty=None, eta0=0.1)# 实例化SGDRegressor的类
sgd_reg.fit(X, y.ravel())# 训练模型print(sgd_reg.intercept_, sgd_reg.coef_)# 输出模型的参数
4.2.3 小批量梯度下降(Mini-Batch Gradient Descent)
我们要研究的最后一个梯度下降算法称为小批量梯度下降。只要你了解了批量和随机梯度下降,就很容易理解它:在每一步中,不是根据完整的训练集(如批量梯度下降)或仅基于一个实例(如随机梯度下降)来计算梯度,小批量梯度下降在称为小型批量的随机实例集上计算梯度。小批量梯度下降优于随机梯度下降的主要优点是,你可以通过矩阵操作的硬件优化来提高性能,特别是在使用GPU时。
与随机梯度下降相比,该算法在参数空间上的进展更稳定,尤其是在相当大的小批次中。结果,小批量梯度下降最终将比随机梯度下降走得更接近最小值,但它可能很难摆脱局部最小值(在受局部最小值影响的情况下,不像线性回归)。下图显示了训练期间参数空间中三种梯度下降算法所采用的路径。它们最终都接近最小值,但是批量梯度下降的路径实际上是在最小值处停止,而随机梯度下降和小批量梯度下降都继续走动。但是,不要忘记批量梯度下降每步需要花费很多时间,如果你使用良好的学习率调度,随机梯度下降和小批量梯度下降也会达到最小值。
让我们比较到目前为止讨论的线性回归算法(回想一下,
m
m
m是训练实例的数量,
n
n
n是特征的数量)。
训练后几乎没有区别:所有这些算法最终都具有非常相似的模型,并且以完全相同的方式进行预测。
4.3 多项式回归
如果你的数据比直线更复杂怎么办?令人惊讶的是,你可以使用线性模型来拟合非线性数据。一个简单的方法就是将每个特征的幂次方添加为一个新特征,然后在此扩展特征集上训练一个线性模型。这种技术称为多项式回归。
让我们看一个示例。
首先,让我们基于一个简单的二次方程式(注:二次方程的形式为y=ax2+bx+c。)(加上一些噪声)生成一些非线性数据:
m =100
X =6* np.random.rand(m,1)-3
y =0.5* X**2+ X +2+ np.random.randn(m,1)
显然,一条直线永远无法正确地拟合此数据。因此,让我们使用Scikit-Learn的PolynomialFeatures类来转换训练数据,将训练集中每个特征的平方(二次多项式)添加为新特征(在这种情况下,只有一个特征):
# 将每一个特征的平方都变成一个新的特征from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)print(X[0])print(X_poly[0])
X_poly现在包含X的原始特征以及该特征的平方。现在你可以将LinearRegression模型拟合到此扩展训练数据中:
# 进行预测
lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)print(lin_reg.intercept_, lin_reg.coef_)# 输出参数
请注意,当存在多个特征时,多项式回归能够找到特征之间的关系(这是普通线性回归模型无法做到的)。PolynomialFeatures还可以将特征的所有组合添加到给定的多项式阶数。例如,如果有两个特征a和b,则degree=3的PolynomialFeatures不仅会添加特征a2、a3、b2和 b3,还会添加组合ab、a2b和ab2。
PolynomialFeatures(degree=d)
可以将一个包含
n n n个特征的数组转换为包含 ( n + d ) ! d ! n ! \frac{(n+d)!}{d!n!} d!n!(n+d)!个特征的数组,其中 n ! n! n!是 n n n的阶乘,等于 1 × 2 × 3 × … × n 1×2×3×…×n 1×2×3×…×n。要小心特征组合的数量爆炸。
4.4 学习曲线
你如果执行高阶多项式回归,与普通线性回归相比,拟合数据可能会更好。
这种高阶多项式回归模型严重过拟合训练数据,而线性模型则欠拟合。在这种情况下,最能泛化的模型是二次模型,因为数据是使用二次模型生成的。但是总的来说,你不知道数据由什么函数生成,那么如何确定模型的复杂性呢?你如何判断模型是过拟合数据还是欠拟合数据呢?
在第2章中,你使用交叉验证来估计模型的泛化性能。⭐如果模型在训练数据上表现良好,但根据交叉验证的指标泛化较差,则你的模型过拟合。如果两者的表现均不理想,则说明欠拟合。⭐这是一种区别模型是否过于简单或过于复杂的方法。
还有一种方法是观察学习曲线:这个曲线绘制的是模型在训练集和验证集上关于训练集大小(或训练迭代)的性能函数。要生成这个曲线,只需要在不同大小的训练子集上多次训练模型即可。下面这段代码在给定训练集下定义了一个函数,绘制模型的学习曲线:
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
defplot_learning_curves(model, X, y):
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
train_errors, val_errors =[],[]for m inrange(1,len(X_train)):
model.fit(X_train[:m], y_train[:m])
y_train_predict = model.predict(X_train[:m])
y_val_predict = model.predict(X_val)
train_errors.append(mean_squared_error(y_train[:m], y_train_predict))
val_errors.append(mean_squared_error(y_val, y_val_predict))
plt.plot(np.sqrt(train_errors),"r-+", linewidth=2, label="train")
plt.plot(np.sqrt(val_errors),"b-", linewidth=3, label="val")
plt.show()# 看一下普通线性回归模型的学习曲线
lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)
这种欠拟合的模型值得解释一下。首先,让我们看一下在训练数据上的性能:当训练集中只有一个或两个实例时,模型可以很好地拟合它们,这就是曲线从零开始的原因。但是,随着将新实例添加到训练集中,模型就不可能完美地拟合训练数据,这既因为数据有噪声,又因为它根本不是线性的。因此,训练数据上的误差会一直上升,直到达到平稳状态,此时在训练集中添加新实例并不会使平均误差变好或变差。现在让我们看一下模型在验证数据上的性能。当在很少的训练实例上训练模型时,它无法正确泛化,这就是验证误差最初很大的原因。然后,随着模型经历更多的训练示例,它开始学习,因此验证错误逐渐降低。但是,直线不能很好地对数据进行建模,因此误差最终达到一个平稳的状态,非常接近另外一条曲线。
这些学习曲线是典型的欠拟合模型。两条曲线都达到了平稳状态。它们很接近而且很高。
如果你的模型欠拟合训练数据,添加更多训练示例将无济于事。你需要使用更复杂的模型或提供更好的特征。
现在让我们看一下在相同数据上的10阶多项式模型的学习曲线:
# 在相同数据上的10阶多项式模型的学习曲线from sklearn.pipeline import Pipeline
polynomial_regression = Pipeline([("poly_features", PolynomialFeatures(degree=10, include_bias=False)),("lin_reg", LinearRegression())])
plot_learning_curves(polynomial_regression, X, y)
这些学习曲线看起来有点像以前的曲线,但是有两个非常重要的区别:
- 与线性回归模型相比,训练数据上的误差要低得多。
- 曲线之间存在间隙。这意味着该模型在训练数据上的性能要比在
验证数据上的性能好得多,这是过拟合模型的标志。但是,如果你使用更大的训练集,则两条曲线会继续接近。
改善过拟合模型的一种方法是向其提供更多的训练数据,直到验证误差达到训练误差为止。
偏差/方差权衡
统计学和机器学习的重要理论成果是以下事实:模型的泛化误差可以表示为三个非常不同的误差之和:
- 偏差:这部分泛化误差的原因在于错误的假设,比如假设数据是线性的,而实际上是二次的。高偏差模型最有可能欠拟合训练数据。
- 方差:这部分是由于模型对训练数据的细微变化过于敏感。具有许多自由度的模型(例如高阶多项式模型)可能具有较高的方差,因此可能过拟合训练数据。
- 不可避免的误差这部分误差是因为数据本身的噪声所致。减少这部分误差的唯一方法就是清理数据(例如修复数据源(如损坏的传感器),或者检测并移除异常值)。
增加模型的复杂度通常会显著提升模型的方差并减少偏差。反过来,降低模型的复杂度则会提升模型的偏差并降低方差。这就是为什么称其为权衡。
不要将这里的偏差概念与线性模型中的偏置项概念弄混。
4.5 线性模型的正则化
正如我们在第1章和第2章中看到的那样,减少过拟合的一个好方法是对模型进行正则化(即约束模型):它拥有的自由度越少,则过拟合数据的难度就越大。正则化多项式模型的一种简单方法是减少多项式的次数。
对于线性模型,正则化通常是通过约束模型的权重来实现的。现在,我们看一下岭回归、Lasso回归和弹性网络,它们实现了三种限制权重的方法。
4.5.1 岭回归
岭回归(也称为Tikhonov正则化)是线性回归的正则化版本:将等于
α
∑
i
=
1
n
θ
i
2
α\sum^n_{i=1}θ_i^2
α∑i=1nθi2的正则化项添加到成本函数。这迫使学习算法不仅拟合数据,而且还使模型权重尽可能小。注意:仅在训练期间将正则化项添加到成本函数中。训练完模型后,你要使用非正则化的性能度量来评估模型的性能。
训练过程中使用的成本函数与用于测试的性能指标不同是很常见的。除正则化外,它们可能不同的另一个原因是好的训练成本函数应该具有对优化友好的导数,而用于测试的性能指标应尽可能接近最终目标。例如,通常使用成本函数(例如对数损失(稍后讨论))来训练分类器,但使用精度/召回率对其进行评估。
超参数α控制要对模型进行正则化的程度。如果α=0,则岭回归仅是线性回归。如果α非常大,则所有权重最终都非常接近于零,结果是一条经过数据均值的平线。公式给出了岭回归成本函数:
岭
回
归
成
本
函
数
:
J
(
θ
)
=
M
S
E
(
θ
)
+
α
1
2
∑
i
=
1
n
θ
i
2
闭
式
解
的
岭
回
归
:
θ
′
=
(
X
T
X
+
α
A
)
−
1
X
T
y
岭回归成本函数:J(θ) = MSE(θ) + α\frac{1}{2}\sum^n_{i=1}θ^2_i\\ 闭式解的岭回归:θ' = (X^TX+αA)^{-1}X^Ty
岭回归成本函数:J(θ)=MSE(θ)+α21i=1∑nθi2闭式解的岭回归:θ′=(XTX+αA)−1XTy
⭐在执行岭回归之前缩放数据(例如使用StandardScaler)很重要,因为它对输入特征的缩放敏感。大多数正则化模型都需要如此。
以下是用Scikit-Learn和闭式解(方程式4-9的一种变体,它使用AndréLouis Cholesky矩阵分解技术)来执行岭回归的方法:
# 岭回归求解from sklearn.linear_model import Ridge
# 直接使用Ridge()函数
ridge_reg = Ridge(alpha=1, solver="cholesky")
ridge_reg.fit(X, y)print(ridge_reg.predict([[1.5]]))# 或者直接使用随机梯度下降法:
sgd_reg = SGDRegressor(penalty="l2")# 超参数penalty设置的是使用正则项的类型。# 设为"l2"表示希望SGD在成本函数中添加一个正则项,等于权重向量的l2范数的平方的一半,即岭回归。
sgd_reg.fit(X, y.ravel())# 需要对y进行一个塑形print(sgd_reg.predict([[1.5]]))
4.5.2 Lasso回归
线性回归的另一种正则化叫作最小绝对收缩和选择算子回归(Least Absolute Shrinkage and Selection Operator Regression,简称Lasso回归)。与岭回归一样,它也是向成本函数添加一个正则项,但是它增加的是权重向量的
l
1
l1
l1范数,而不是
l
2
l2
l2范数的平方的一半。
L
a
s
s
o
回
归
成
本
函
数
:
J
(
θ
)
=
M
S
E
(
θ
)
+
α
1
2
∑
i
=
1
n
∣
θ
i
∣
Lasso回归成本函数:J(θ) = MSE(θ)+α\frac{1}{2}\sum^n_{i=1}|θ_i|\\
Lasso回归成本函数:J(θ)=MSE(θ)+α21i=1∑n∣θi∣
Lasso回归的一个重要特点是它倾向于完全消除掉最不重要特征的权重(也就是将它们设置为零)。换句话说,Lasso回归会自动执行特征选择并输出一个稀疏模型(即只有很少的特征有非零权重)。
为了避免在使用Lasso时梯度下降最终在最优解附近反弹,你需要逐渐降低训练期间的学习率(它仍然会在最优解附近反弹,但是步长会越来越小,因此会收敛)。
这是一个使用Lasso类的Scikit-Learn小示例:
# Lasso回归from sklearn.linear_model import Lasso
# 直接使用Lasso()函数
lasso_reg = Lasso(alpha=0.1)
lasso_reg.fit(X, y)print(lasso_reg.predict([[1.5]]))# 或者使用SGDRegressor(penalty="l1")
sgd_lasso_reg = SGDRegressor(penalty='l1')
sgd_lasso_reg.fit(X, y.ravel())# 需要对y进行一个塑形print(sgd_lasso_reg.predict([[1.5]]))
4.5.3 弹性网络
弹性网络是介于岭回归和Lasso回归之间的中间地带。正则项是岭和Lasso正则项的简单混合,你可以控制混合比r。当r=0时,弹性网络等效于岭回归,而当r=1时,弹性网络等效于Lasso回归。
弹
性
网
络
成
本
函
数
:
J
(
θ
)
=
M
S
E
(
θ
)
+
r
α
1
2
∑
i
=
1
n
∣
θ
i
∣
+
1
−
r
2
α
∑
i
=
1
n
θ
i
2
弹性网络成本函数:J(θ) = MSE(θ)+rα\frac{1}{2}\sum^n_{i=1}|θ_i|+\frac{1-r}{2}α\sum^n_{i=1}θ^2_i
弹性网络成本函数:J(θ)=MSE(θ)+rα21i=1∑n∣θi∣+21−rαi=1∑nθi2
那么什么时候应该使用普通的线性回归(即不进行任何正则化)、岭(Ridge)、Lasso或弹性网络呢?
⭐通常来说,有正则化——哪怕很小,总比没有更可取一些。所以大多数情况下,你应该避免使用纯线性回归。岭回归是个不错的默认选择,但是如果你觉得实际用到的特征只有少数几个,那就应该更倾向于Lasso回归或是弹性网络,因为它们会将无用特征的权重降为零。一般而言,弹性网络优于Lasso回归,因为当特征数量超过训练实例数量,又或者是几个特征强相关时,Lasso回归的表现可能非常不稳定。⭐
这是一个使用Scikit-Learn的ElasticNet的小示例(
l1_ratio
对应于混合比r):
# 弹性回归ElasticNetfrom sklearn.linear_model import ElasticNet
# 使用ElasticNet()函数
elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic_net.fit(X, y)print(elastic_net.predict([[1.5]]))# 使用SGDRegressor(penalty="elasticnet")
sgd_lasso_reg = SGDRegressor(penalty='elasticnet')
sgd_lasso_reg.fit(X, y.ravel())# 需要对y进行一个塑形print(sgd_lasso_reg.predict([[1.5]]))
4.5.4 提前停止
对于梯度下降这一类迭代学习的算法,还有一个与众不同的正则化方法,就是在验证误差达到最小值时停止训练,该方法叫作提前停止法。下图展现了一个用批量梯度下降训练的复杂模型(高阶多项式回归模型)。经过一轮一轮的训练,算法不断地学习,训练集上的预测误差(RMSE)自然不断下降,同样其在验证集上的预测误差也随之下降。但是,一段时间之后,验证误差停止下降反而开始回升。这说明模型开始过拟合训练数据。通过早期停止法,一旦验证误差达到最小值就立刻停止训练。这是一个非常简单而有效的正则化技巧,所以Geoffrey Hinton称其为“美丽的免费午餐”。
使用随机和小批量梯度下降时,曲线不是那么平滑,可能很难知道你是否达到了最小值。一种解决方案是仅在验证错误超过最小值一段时间后停止(当你确信模型不会做得更好时),然后回滚模型参数到验证误差最小的位置。以下是提前停止法的基本实现:
# 提前停止法的基本实现:from sklearn.base import clone
from sklearn.preprocessing import StandardScaler
# prepare the data
poly_scaler = Pipeline([("poly_features", PolynomialFeatures(degree=90, include_bias=False)),("std_scaler", StandardScaler())])
X_train_poly_scaled = poly_scaler.fit_transform(X_train)# 训练集完成数据处理
X_val_poly_scaled = poly_scaler.transform(X_val)# 验证集完成数据处理
sgd_reg = SGDRegressor(max_iter=1, tol=-np.infty, warm_start=True, penalty=None, learning_rate="constant", eta0=0.0005)# 请注意,在使用warm_start=True的情况下,当调用fit()方法时,它将在停止的地方继续训练,而不是从头开始。
minimum_val_error =float("inf")
best_epoch =None
best_model =Nonefor epoch inrange(1000):
sgd_reg.fit(X_train_poly_scaled, y_train)# continues where it left off 在停止的地方继续训练 用训练集训练模型
y_val_predict = sgd_reg.predict(X_val_poly_scaled)# 预测验证集
val_error = mean_squared_error(y_val, y_val_predict)# 计算均方误差if val_error < minimum_val_error:
minimum_val_error = val_error
best_epoch = epoch
best_model = clone(sgd_reg)
4.6 逻辑回归
正如第1章中提到过的,一些回归算法也可用于分类(反之亦然)。逻辑回归(Logistic回归,也称为Logit回归)被广泛用于估算一个实例属于某个特定类别的概率。(比如,这封电子邮件属于垃圾邮件的概率是多少?)如果预估概率超过50%,则模型预测该实例属于该类别(称为正类,标记为“1”),反之,则预测不是(称为负类,标记为“0”)。这样它就成了一个二元分类器。
4.6.1 估计概率
所以逻辑回归是怎么工作的呢?与线性回归模型一样,逻辑回归模型也是计算输入特征的加权和(加上偏置项),但是不同于线性回归模型直接输出结果,它输出的是结果的数理逻辑值。
逻
辑
回
归
模
型
的
估
计
概
率
(
向
量
化
形
式
)
:
p
′
=
h
θ
(
x
)
=
σ
(
x
T
θ
)
逻
辑
函
数
:
σ
(
t
)
=
1
1
+
e
x
p
(
−
t
)
逻辑回归模型的估计概率(向量化形式): p' = h_θ(x) = σ(x^Tθ)\\ 逻辑函数:σ(t) = \frac{1}{1+exp(-t)}
逻辑回归模型的估计概率(向量化形式):p′=hθ(x)=σ(xTθ)逻辑函数:σ(t)=1+exp(−t)1
一旦逻辑回归模型估算出实例x属于正类的概率
p
′
p'
p′,就可以做出预测:
y
′
=
{
0
如
果
p
′
<
0.5
1
如
果
p
′
≥
0.5
y' = \begin{cases}0 & 如果p'<0.5\\1 & 如果p'≥0.5\end{cases}
y′={01如果p′<0.5如果p′≥0.5
分数t通常称为logit。该名称源于以下事实:定义为logit(p)=log(p/(1–p))的logit函数与logistic函数相反。确实,如果你计算估计概率p的对数,则会发现结果为t。对数也称为对数奇数,因为它是正类别的估计概率与负类别的估计概率之比的对数。
4.6.2 训练和成本函数
现在你知道逻辑回归模型是如何估算概率并做出预测了。但是要怎么训练呢?训练的目的就是设置参数向量θ,使模型对正类实例做出高概率估算(y=1),对负类实例做出低概率估算(y=0)。
这个成本函数是有道理的,因为当t接近于0时,-log(t)会变得非常大,所以如果模型估算一个正类实例的概率接近于0,成本将会变得很高。同理估算出一个负类实例的概率接近1,成本也会变得非常高。那么反过来,当t接近于1的时候,-log(t)接近于0,所以对一个负类实例估算出的概率接近于0,对一个正类实例估算出的概率接近于1,而成本则都接近于0,这不正好是我们想要的吗?整个训练集的成本函数是所有训练实例的平均成本。可以用一个称为对数损失的单一表达式来表示,见公式4-17。
但是坏消息是,这个函数没有已知的闭式方程(不存在一个标准方程的等价方程)来计算出最小化成本函数的θ值。而好消息是这是个凸函数,所以通过梯度下降(或是其他任意优化算法)保证能够找出全局最小值(只要学习率不是太高,你又能长时间等待)。
4.6.3 决策边界(鸢尾植物数据集)
这里我们用鸢尾植物数据集来说明逻辑回归。这是一个非常著名的数据集,共有150朵鸢尾花,分别来自三个不同品种(山鸢尾、变色鸢尾和维吉尼亚鸢尾),数据里包含花的萼片以及花瓣的长度和宽度。
我们试试仅基于花瓣宽度这一个特征,创建一个分类器来检测维吉尼亚鸢尾花。
# 鸢尾花 逻辑回归from sklearn import datasets
# 导入数据
iris = datasets.load_iris()print(list(iris.keys()))# 输出:['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename']
X = iris["data"][:,3:]# petal width 花瓣宽度
y =(iris["target"]==2).astype(int)# 1 if Iris virginica, else 0 如果是Iris virginica花,就是1,否则就是0# 训练一个逻辑回归模型from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X, y)# 我们来看看花瓣宽度在0到3cm之间的鸢尾花,模型估算出的概率
X_new = np.linspace(0,3,1000).reshape(-1,1)
y_proba = log_reg.predict_proba(X_new)
plt.plot(X_new, y_proba[:,1],"g-", label="Iris virginica")
plt.plot(X_new, y_proba[:,0],"b--", label="Not Iris virginica")
plt.legend()
plt.show()
上图绘图代码如下:
# + more Matplotlib code to make the image look pretty
X_new = np.linspace(0,3,1000, dtype=object).reshape(-1,1)
y_proba = log_reg.predict_proba(X_new)
decision_boundary = X_new[y_proba[:,1]>=0.5][0]
plt.figure(figsize=(8,3))
plt.plot(X[y ==0], y[y ==0],"bs")
plt.plot(X[y ==1], y[y ==1],"g^")
plt.plot([decision_boundary, decision_boundary],[-1,2],"k:", linewidth=2)
plt.plot(X_new, y_proba[:,1],"g-", linewidth=2, label="Iris virginica")
plt.plot(X_new, y_proba[:,0],"b--", linewidth=2, label="Not Iris virginica")
plt.text(decision_boundary+0.02,0.15,"Decision boundary", fontsize=14, color="k", ha="center")
plt.arrow(decision_boundary,0.08,-0.3,0, head_width=0.05, head_length=0.1, fc='b', ec='b')
plt.arrow(decision_boundary,0.92,0.3,0, head_width=0.05, head_length=0.1, fc='g', ec='g')
plt.xlabel("Petal width (cm)", fontsize=14)
plt.ylabel("Probability", fontsize=14)
plt.legend(loc="center left", fontsize=14)
plt.axis([0,3,-0.02,1.02])
plt.show()
维吉尼亚鸢尾(三角形所示)的花瓣宽度范围为1.4~2.5cm,而其他两种鸢尾花(正方形所示)花瓣通常较窄,花瓣宽度范围为0.1~1.8cm。注意,这里有一部分重叠。对花瓣宽度超过2cm的花,分类器可以很有信心地说它是一朵维吉尼亚鸢尾花(对该类别输出一个高概率值),对花瓣宽度低于1cm以下的,也可以胸有成竹地说其不是(对“非维吉尼亚鸢尾”类别输出一个高概率值)。在这两个极端之间,分类器则不太有把握。但是,如果你要求它预测出类别(使用predict()方法而不是predict_proba()方法),它将返回一个可能性最大的类别。也就是说,在大约1.6cm处存在一个决策边界,这里“是”和“不是”的可能性都是50%,如果花瓣宽度大于1.6cm,分类器就预测它是维吉尼亚鸢尾花,否则就预测不是(即使它没什么把握):
# 测试预测print(log_reg.predict([[1.7],[1.5]]))# 测试1.7和1.5两个值的输出
下图还是同样的数据集,但是这次显示了两个特征:花瓣宽度和花瓣长度。经过训练,这个逻辑回归分类器就可以基于这两个特征来预测新花朵是否属于维吉尼亚鸢尾。虚线表示模型估算概率为50%的点,即模型的决策边界。注意这里是一个线性的边界(注:这是点x的集合,使得θ0+θ1x1+θ2x2=0,它定义了一条直线。)。每条平行线都分别代表一个模型输出的特定概率,从左下的15%到右上的90%。根据这个模型,右上线之上的所有花朵都有超过90%的概率属于维吉尼亚鸢尾。
与其他线性模型一样,逻辑回归模型可以用
l
1
l1
l1或
l
2
l2
l2惩罚函数来正则化。Scikit-Learn默认添加的是
l
2
l2
l2函数。
控制Scikit-Learn LogisticRegression模型的正则化强度的超参数不是alpha(与其他线性模型一样),而是反值C。C值越高,对模型的正则化越少。
4.6.4 Softmax回归
逻辑回归模型经过推广,可以直接支持多个类别,而不需要训练并组合多个二元分类器(如第3章所述)。这就是Softmax回归,或者叫作多元逻辑回归。
原理很简单:给定一个实例x,Softmax回归模型首先计算出每个类k的分数sk(x),然后对这些分数应用softmax函数(也叫归一化指数),估算出每个类的概率。你应该很熟悉计算sk(x)分数的公式(见公式),因为它看起来就跟线性回归预测的方程一样。
类
k
的
S
o
f
t
m
a
x
分
数
:
S
k
(
x
)
=
x
T
θ
(
k
)
类k的Softmax分数:S_k(x) = x^Tθ^{(k)}
类k的Softmax分数:Sk(x)=xTθ(k)
请注意,每个类都有自己的特定参数向量
θ
(
k
)
θ(k)
θ(k)。所有这些向量通常都作为行存储在参数矩阵
θ
θ
θ中。
一旦为实例x计算了每个类的分数,就可以通过softmax函数来估计实例属于类k的概率。该函数计算每个分数的指数,然后对其进行归一化(除以所有指数的总和)。分数通常称为对数或对数奇数(尽管它们实际上是未归一化的对数奇数)。
就像逻辑回归分类器一样,Softmax回归分类器预测具有最高估计概率的类(简单来说就是得分最高的类)。
Softmax回归分类器一次只能预测一个类(即它是多类,而不是多输出),因此它只能与互斥的类(例如不同类型的植物)一起使用。你无法使用它在一张照片中识别多个人。
我们来使用Softmax回归将鸢尾花分为三类。当用两个以上的类训练时,Scikit-Learn的LogisticRegressio默认选择使用的是一对多的训练方式,不过将超参数multi_class设置为"multinomial",可以将其切换成Softmax回归。你还必须指定一个支持Softmax回归的求解器,比如"lbfgs"求解器。默认使用
l
2
l2
l2正则化,你可以通过超参数C进行控制:
# softmax预测鸢尾花
X = iris["data"][:,(2,3)]# petal length, petal width
y = iris["target"]
softmax_reg = LogisticRegression(multi_class="multinomial", solver="lbfgs", C=10)
softmax_reg.fit(X, y)# 当你下次碰到一朵鸢尾花,花瓣长5cm宽2cm,你就可以让模型告诉你它的种类,它会回答说:94.2%的概率是维吉尼亚鸢尾(第2类)或者5.8%的概率为变色鸢尾:print(softmax_reg.predict([[5,2]]))# 预测属于哪一类print(softmax_reg.predict_proba([[5,2]]))# 得到属于每一类的概率
下图展现了由不同背景色表示的决策边界。注意,任何两个类之间的决策边界都是线性的。图中的折线表示属于变色鸢尾的概率(例如,标记为0.45的线代表45%的概率边界)。注意,该模型预测出的类,其估算概率有可能低于50%,比如,在所有决策边界相交的地方,所有类的估算概率都为33%。
4.7 练习题
问题
- 如果训练集具有数百万个特征,那么可以使用哪种线性回归训练算法?
- 如果训练集里特征的数值大小迥异,哪种算法可能会受到影响?受影响程度如何?你应该怎么做?
- 训练逻辑回归模型时,梯度下降会卡在局部最小值中吗?
- 如果你让它们运行足够长的时间,是否所有的梯度下降算法都能得出相同的模型?
- 假设你使用批量梯度下降,并在每个轮次绘制验证误差。如果你发现验证错误持续上升,可能是什么情况?你该如何解决?
- 当验证错误上升时立即停止小批量梯度下降是个好主意吗?
- 哪种梯度下降算法(在我们讨论过的算法中)将最快到达最佳解附近?哪个实际上会收敛?如何使其他的也收敛?
- 假设你正在使用多项式回归。绘制学习曲线后,你会发现训练误差和验证误差之间存在很大的差距。发生了什么?解决此问题的三种方法是什么?
- 假设你正在使用岭回归,并且你注意到训练误差和验证误差几乎相等且相当高。你是否会说模型存在高偏差或高方差?你应该增加正则化超参数α还是减小它呢?
- 为什么要使用:- 岭回归而不是简单的线性回归(即没有任何正则化)?- Lasso而不是岭回归?- 弹性网络而不是Lasso?
- 假设你要将图片分类为室外/室内和白天/夜间。你应该实现两个逻辑回归分类器还是一个Softmax回归分类器?
- 用Softmax回归进行批量梯度下降训练,实现提前停止法(不使用Scikit-Learn)。
答案
- 如果你的训练集具有数百万个特征,则可以使用随机梯度下降或小批量梯度下降。如果训练集适合容纳于内存,则可以使用批量梯度下降。但是你不能使用标准方程法或SVD方法,因为随着特征数量的增加,计算复杂度会快速增长(超过二次方)。
- 如果你的训练集中的特征具有不同的尺寸比例,则成本函数具有细长碗的形状,因此梯度下降算法需要很长时间才能收敛。为了解决这个问题,你应该在训练模型之前缩放数据。请注意,标准方程法或SVD方法无须缩放即可正常工作。此外,如果特征未按比例缩放,则正则化模型可能会收敛至次优解:由于正则化会惩罚较大的权重,因此与具有较大值的特征相比,具有较小值的特征往往会被忽略。
- 训练逻辑回归模型时,梯度下降不会陷入局部最小值,因为成本函数是凸函数。
- 如果优化问题是凸的(例如线性回归或逻辑回归),并且假设学习率不是太高,那么所有梯度下降算法都将接近全局最优并最终产生很相似的模型。但是,除非逐步降低学习率,否则随机梯度下降和小批量梯度下降将永远不会真正收敛。相反,它们会一直围绕全局最优值来回跳跃。这意味着即使你让它们运行很长时间,这些梯度下降算法也会产生略微不同的模型。
- 如果验证错误在每个轮次后持续上升,则一种可能性是学习率过高并且算法在发散。如果训练错误也增加了,那么这显然是问题所在,你应该降低学习率。但是,如果训练错误没有增加,则你的模型已经过拟合训练集,则应该停止训练。
- 由于随机性,随机梯度下降和小批量梯度下降都不能保证在每次训练迭代中都取得进展。因此,如果在验证错误上升时立即停止训练,则可能在达到最优值之前就停止太早了。更好的选择是按照一定的间隔时间保存模型。然后,当它很长时间没有改善(意味着它可能永远不会超过最优值)时,你可以恢复到保存的最佳模型。
- 随机梯度下降法具有最快的训练迭代速度,因为它一次只考虑一个训练实例,因此它通常是第一个到达全局最优值附近的(或是很小批量大小的小批量梯度下降)。但是,给定足够的训练时间,实际上只有批量梯度下降会收敛。如前所述,随机梯度下降和小批量梯度下降会在最优值附近反弹,除非你逐渐降低学习率。
- 如果验证误差远高于训练误差,则可能是因为模型过拟合了训练集。解决此问题的一种方法是降低多项式阶数:较小自由度的模型不太可能过拟合。另一种方法是对模型进行正则化,例如,将 l 2 l2 l2(Ridge)或 l 1 l1 l1(Lasso)惩罚添加到成本函数。这也会降低模型的自由度。最后,你可以尝试增加训练集的大小。
- 如果训练误差和验证误差几乎相等且相当高,则该模型很可能欠拟合训练集,这意味着它具有很高的偏差。你应该尝试减少正则化超参数 α α α。
- 让我们来看看:- 具有某些正则化的模型通常比没有任何正则化的模型要好,因此,你通常应优先选择岭回归而不是简单的线性回归。- Lasso回归使用 l 1 l1 l1惩罚,这通常会将权重降低为零。这将导致稀疏模型,其中除了最重要的权重之外,所有权重均为零。这是一种自动进行特征选择的方法,如果你怀疑实际上只有很少的特征很重要,那么这是一种很好的方法。如果你不确定,则应首选岭回归。- 与Lasso相比,弹性网络通常更受青睐,因为Lasso在某些情况下可能产生异常(当几个特征强相关或当特征比训练实例更多时)。但是,它确实增加了额外需要进行调整的超参数。如果你希望Lasso没有不稳定的行为,则可以仅使用
l1_ratio
接近1的弹性网络。 - 如果你要将图片分类为室外/室内和白天/夜间,因为它们不是排他的
- 类(即所有四种组合都是可能的),则应训练两个逻辑回归分类器。
- 请参阅https://github.com/ageron/handson-ml2上的Jupyter notebooks。
版权归原作者 新四石路打卤面 所有, 如有侵权,请联系我们删除。