🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】 深度学习【DL】
🖍foreword
✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。
如果你对这个系列感兴趣的话,可以关注订阅哟👋
至今我们将机器学习模型及其训练算法视为黑匣子。如果您完成了前几章中的一些练习,您可能会惊讶于在不了解底层内容的情况下可以完成多少工作:您优化了回归系统,改进了数字图像分类器,甚至从头开始构建垃圾邮件分类器,所有这一切都不知道它们实际上是如何工作的。事实上,在许多情况下,您并不需要真正了解实现细节。
但是,充分了解事物的工作原理可以帮助您快速了解适当的模型、要使用的正确训练算法以及适合您任务的一组良好的超参数。了解底层内容也将帮助您更有效地调试问题和执行错误分析。最后,本章讨论的大部分主题对于理解、构建和训练神经网络(在本书的第二部分中讨论)至关重要。
在本章中,我们将从查看线性回归模型,最简单的模型之一。我们将讨论两种非常不同的训练方法:
使用直接的“封闭式”方程,直接计算最适合模型到训练集的模型参数(即最小化训练集成本函数的模型参数)。
使用称为迭代优化的方法梯度下降 (GD) 逐渐调整模型参数以最小化训练集上的成本函数,最终收敛到与第一种方法相同的参数集。我们将研究一些梯度下降的变体,我们将在第二部分研究神经网络时反复使用它们:Batch GD、Mini-batch GD 和 Stochastic GD。
接下来我们来看多项式回归,一种更复杂的模型,可以拟合非线性数据集。由于这个模型的参数比线性回归多,它更容易过拟合训练数据,所以我们将看看如何使用学习曲线来检测是否是这种情况,然后我们将看看几种正则化技术可以降低过度拟合训练集的风险。
最后,我们将看看另外两个常用于分类任务的模型:Logistic Regression 和 Softmax Regression。
警告
本章将有很多数学方程,使用的基本概念线性代数和微积分。要理解这些方程,你需要知道什么是向量和矩阵;如何对它们进行转置、乘法和逆运算;以及什么是偏导数。如果您不熟悉这些概念,请阅读在线补充材料中作为 Jupyter 笔记本提供的线性代数和微积分入门教程。对于那些真正对数学过敏的人,你还是应该读完这一章,直接跳过方程式;希望文本足以帮助您理解大部分概念。
在第 1 章中,我们看一个简单的生活满意度回归模型:life_satisfaction = θ 0 + θ 1 × GDP_per_capita。
该模型只是输入特征的线性函数GDP_per_capita
。θ 0和θ 1是模型的参数。
更一般地说,线性模型通过简单地计算输入特征的加权和来进行预测,加上一个称为偏置项(也称为截距项)的常数,如公式 4-1所示。
公式 4-1。线性回归模型预测
在这个等式中:
ŷ是预测值。
n是特征的数量。
x i是第i个特征值。
θ j是第j个模型参数(包括偏置项θ 0和特征权重θ 1 , θ 2 ,⋯, θ n)。
这可以使用矢量化形式更简洁地编写,如公式 4-2所示。
公式 4-2。线性回归模型预测(矢量化形式)
笔记
在机器学习中,向量通常表示为列向量,它们是具有单列的二维数组。如果θ和x是列向量,则预测为y^=θ⊺x, 在哪里θ⊺是θ的转置(行向量而不是列向量)和θ⊺x是矩阵乘法θ⊺和x。这当然是相同的预测,只是它现在表示为单细胞矩阵而不是标量值。在本书中,我将使用这种表示法来避免在点积和矩阵乘法之间切换。
好的,就是这样线性回归模型——但我们如何训练它?好吧,回想一下,训练模型意味着设置其参数,以便模型最适合训练集。为此,我们首先需要衡量模型对训练数据的拟合程度(或差)。在第 2 章中,我们看到回归模型最常见的性能度量是均方根误差 (RMSE)(公式 2-1)。因此,要训练线性回归模型,我们需要找到使 RMSE 最小化的θ值。在实践中,最小化均方误差 (MSE) 比 RMSE 更简单,并且会导致相同的结果(因为最小化函数的值也会最小化其平方根)。1
使用公式 4-3计算训练集X上的线性回归假设h θ的 MSE 。
公式 4-3。线性回归模型的 MSE 成本函数
这些符号中的大部分都在第 2 章中介绍过(参见“符号”)。唯一的区别是我们写h θ而不是仅仅h以清楚地表明模型是由向量θ参数化的。为了简化符号,我们将只写 MSE( θ ) 而不是 MSE( X , h θ )。
至找到最小化成本函数的θ值,就有一个封闭形式的解决方案——换句话说,一个直接给出结果的数学方程。这称为正规方程(方程 4-4)。
公式 4-4。正规方程
在这个等式中:
θ^是使成本函数最小化的θ值。
y是包含y (1)到y ( m )的目标值向量。
让我们生成一些线性数据来测试这个方程(图 4-1):
- import numpy as np
-
- X = 2 * np.random.rand(100, 1)
- y = 4 + 3 * X + np.random.randn(100, 1)
图 4-1。随机生成的线性数据集
现在让我们计算𝛉θ^使用正态方程。我们将使用inv()
NumPy 的线性代数模块 ( np.linalg
) 中的函数来计算矩阵的逆矩阵,以及dot()
矩阵乘法的方法:
- 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)
我们用来生成数据的函数是y = 4 + 3 x 1 + 高斯噪声。让我们看看等式发现了什么:
>>> theta_best
- array([[4.21509616],
- [2.77011339]])
我们希望θ 0 = 4 和θ 1 = 3 而不是θ 0 = 4.215 和θ 1 = 2.770。足够接近,但噪声使得无法恢复原始函数的确切参数。
现在我们可以使用θ^:
- >>> 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)
- >>> y_predict
- array([[4.21509616],
- [9.75532293]])
让我们绘制这个模型的预测(图 4-2):
- plt.plot(X_new, y_predict, "r-")
- plt.plot(X, y, "b.")
- plt.axis([0, 2, 0, 15])
- plt.show()
图 4-2。线性回归模型预测
表演使用 Scikit-Learn 的线性回归很简单:2
- >>> from sklearn.linear_model import LinearRegression
- >>> lin_reg = LinearRegression()
- >>> lin_reg.fit(X, y)
- >>> lin_reg.intercept_, lin_reg.coef_
- (array([4.21509616]), array([[2.77011339]]))
- >>> lin_reg.predict(X_new)
- array([[4.21509616],
- [9.75532293]])
该类LinearRegression
基于scipy.linalg.lstsq()
函数(名称代表“最小二乘”),您可以直接调用它:
- >>> theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6)
- >>> theta_best_svd
- array([[4.21509616],
- [2.77011339]])
该函数计算θ^=X+y, 在哪里X+是X的伪逆(特别是 Moore-Penrose逆)。您可以使用直接计算伪逆:np.linalg.pinv()
- >>> np.linalg.pinv(X_b).dot(y)
- array([[4.21509616],
- [2.77011339]])
这伪逆本身是使用称为奇异值分解(SVD)的标准矩阵分解技术计算的,该技术可以将训练集矩阵X分解为三个矩阵U Σ V ⊺的矩阵乘法(参见参考资料numpy.linalg.svd()
)。伪逆计算为X+=VΣ+U⊺. 计算矩阵Σ+,该算法采用Σ并将所有小于微小阈值的值设置为零,然后将所有非零值替换为它们的逆矩阵,最后转置结果矩阵。这种方法比计算正规方程更有效,而且它可以很好地处理边缘情况:实际上,如果矩阵X ⊺ X不可逆(即奇异),例如如果m < n或如果一些,则正规方程可能不起作用特征是多余的,但总是定义伪逆。
这Normal Equation 计算X ⊺ X的逆矩阵,它是一个 ( n + 1) × ( n + 1) 矩阵(其中n是特征数)。根据实现的不同,对这种矩阵求逆的计算复杂度通常约为O ( n 2.4 ) 到O ( n 3 )。换句话说,如果将特征数量翻倍,则计算时间大约乘以 2 2.4 = 5.3 到 2 3 = 8。
Scikit-Learn 的LinearRegression
班级使用的 SVD 方法大约是O ( n 2 )。如果将特征数量增加一倍,则计算时间大约乘以 4。
警告
当特征数量变大(例如,100,000)时,Normal Equation 和 SVD 方法都会变得非常缓慢。从积极的方面来说,两者都与训练集中的实例数量呈线性关系(它们是O ( m )),因此它们可以有效地处理大型训练集,前提是它们可以放入内存中。
此外,一旦您训练了线性回归模型(使用正态方程或任何其他算法),预测就会非常快:计算复杂度与您要进行预测的实例数量和特征数量呈线性关系. 换句话说,对两倍多的实例(或两倍的特征)进行预测将花费大约两倍的时间。
梯度下降是一种通用优化算法,能够为各种问题找到最佳解决方案。梯度下降的一般思想是迭代地调整参数以最小化成本函数。
假设你迷失在浓雾中的山区,你只能感觉到脚下地面的坡度。快速到达谷底的一个好策略是沿着最陡坡的方向下坡。这正是梯度下降所做的:它测量误差函数相对于参数向量θ的局部梯度,并且沿着梯度下降的方向前进。一旦梯度为零,您就达到了最小值!
具体来说,你首先用随机值填充θ(这称为随机初始化)。然后你逐渐改进它,一次迈出一小步,每一步都试图降低成本函数(例如,MSE),直到算法收敛到最小值(见图 4-3)。
图 4-3。在梯度下降的描述中,模型参数被随机初始化并反复调整以最小化成本函数;学习步长与成本函数的斜率成正比,因此随着参数接近最小值,步长逐渐变小
一个梯度下降中的重要参数是步长,由学习率超参数决定。如果学习率太小,那么算法要经过多次迭代才能收敛,耗时较长(见图4-4)。
图 4-4。学习率太小
另一方面,如果学习率太高,你可能会跳过山谷,最终走到另一边,甚至可能比以前更高。这可能会使算法发散,值越来越大,无法找到好的解决方案(见图 4-5)。
图 4-5。学习率太大
最后,并不是所有的成本函数看起来都像普通的碗。可能有洞、山脊、高原和各种不规则的地形,使收敛到最低限度的困难。图 4-6显示了梯度下降的两个主要挑战。如果随机初始化启动左边的算法,那么它将收敛到一个局部最小值,它不如全局最小值。如果它从右边开始,那么穿过高原需要很长时间。如果你停止得太早,你将永远不会达到全局最小值。
图 4-6。梯度下降陷阱
幸运的是,线性回归模型的 MSE 成本函数恰好是一个凸函数,这意味着如果您选择曲线上的任意两个点,连接它们的线段永远不会穿过曲线。这意味着没有局部最小值,只有一个全局最小值。它也是一个斜率不会突然变化的连续函数。3这两个事实有一个很大的后果:梯度下降保证会接近任意接近全局最小值(如果你等待的时间足够长并且学习率不是太高)。
事实上,成本函数具有碗的形状,但如果特征具有非常不同的尺度,它可以是一个细长的碗。图 4-7显示了特征 1 和 2 具有相同尺度的训练集(左侧)和特征 1 的值远小于特征 2 的值(右侧)的训练集上的梯度下降。
图 4-7。具有(左)和不具有(右)特征缩放的梯度下降
正如你所看到的,在左边,梯度下降算法直奔最小值,从而快速到达它,而在右边,它首先沿着与全局最小值方向几乎正交的方向前进,并以长征结束沿着一个几乎平坦的山谷。它最终会达到最小值,但需要很长时间。
警告
使用梯度下降时,您应该确保所有特征都具有相似的规模(例如,使用 Scikit-Learn 的
StandardScaler
类),否则将需要更长的时间才能收敛。
该图还说明了训练模型意味着搜索使成本函数(在训练集上)最小化的模型参数组合这一事实。它是在模型的参数空间中搜索:模型的参数越多,这个空间的维度就越多,搜索就越困难:在 300 维的大海捞针中搜索比在 3 维中要复杂得多。幸运的是,由于在线性回归的情况下成本函数是凸的,所以针只是在碗的底部。
至实现梯度下降,您需要计算成本函数关于每个模型参数θ j的梯度。换句话说,你需要计算一下,如果你稍微改变θ j ,代价函数会改变多少。这个称为偏导数。这就像问“如果我朝东,我脚下的山坡是什么坡度?” 然后面向北方问同样的问题(如果你能想象一个超过三个维度的宇宙,那么对于所有其他维度等等)。公式 4-5计算成本函数关于参数θ j的偏导数,记为∂ MSE( θ ) / ∂θ j。
公式 4-5。成本函数的偏导数
您可以使用公式 4-6一次性计算所有这些偏导数,而不是单独计算这些偏导数。梯度向量,记为∇ θ MSE( θ ),包含成本函数的所有偏导数(每个模型参数一个)。
公式 4-6。成本函数的梯度向量
警告
一旦你有了指向上坡的梯度向量,就朝相反的方向走下坡。这意味着从θ中减去 ∇ θ MSE( θ ) 。这就是学习率η发挥作用的地方:5将梯度向量乘以η以确定下坡步长的大小(公式 4-7)。
公式 4-7。梯度下降步骤
让我们看一下这个算法的快速实现:
- eta = 0.1 # learning rate
- n_iterations = 1000
- m = 100
-
- theta = np.random.randn(2,1) # random initialization
-
- for iteration in range(n_iterations):
- gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)
- theta = theta - eta * gradients
那不是太难!让我们看看结果theta
:
>>> theta
array([[4.21509616],
[2.77011339]])
嘿,这正是正规方程发现的!梯度下降完美地工作。但是如果你使用不同的学习率eta
呢?图 4-8显示了梯度下降的前 10 步,使用了三种不同的学习率(虚线表示起点)。
图 4-8。具有不同学习率的梯度下降
左边,学习率太低:算法最终会到达解,但需要很长时间。在中间,学习率看起来相当不错:在几次迭代中,它已经收敛到了解决方案。在右边,学习率太高:算法发散,到处跳跃,实际上每一步都离解越来越远。
要找到一个好的学习率,可以使用网格搜索(参见第 2 章)。但是,您可能希望限制迭代次数,以便网格搜索可以消除需要太长时间才能收敛的模型。
您可能想知道如何设置迭代次数。如果太低,当算法停止时,你仍然远离最优解;但是如果它太高,你会浪费时间而模型参数不再改变。一个简单的解决方案是设置非常大的迭代次数,但在梯度向量变得很小时中断算法——也就是说,当它的范数变得小于一个很小的数字ϵ(称为容差)时——因为当梯度下降有 (几乎)达到最小值。
收敛率
当成本函数是凸的并且其斜率没有突然变化时(如 MSE 成本函数的情况),具有固定学习率的 Batch Gradient Descent 最终会收敛到最优解,但您可能需要等待一段时间:根据成本函数的形状,它可能需要O (1/ ϵ ) 次迭代才能在ϵ的范围内达到最优值。如果您将容差除以 10 以获得更精确的解决方案,那么算法可能需要运行大约 10 倍的时间。
这批量梯度下降的主要问题是它使用整个训练集来计算每一步的梯度,这使得当训练集很大时它会变得非常慢。在相反的极端,随机梯度下降在每一步都在训练集中选择一个随机实例,并仅基于该单个实例计算梯度。显然,一次处理一个实例会使算法速度更快,因为它在每次迭代中需要处理的数据非常少。它还可以在庞大的训练集上进行训练,因为每次迭代只需要一个实例在内存中(随机 GD 可以实现为核外算法;参见第 1 章)。
另一方面,由于其随机性(即随机性),该算法的规律性远不如批量梯度下降法:成本函数不会平缓下降直至达到最小值,而是会上下反弹,仅平均下降. 随着时间的推移,它最终会非常接近最小值,但一旦到达那里,它将继续反弹,永远不会稳定下来(见图 4-9)。所以一旦算法停止,最终的参数值是好的,但不是最优的。
图 4-9。使用 Stochastic Gradient Descent,每个训练步骤都比使用 Batch Gradient Descent 时快得多,但也更加随机
当成本函数非常不规则时(如图 4-6 所示),这实际上可以帮助算法跳出局部最小值,因此随机梯度下降比批量梯度下降更有机会找到全局最小值。
因此,随机性可以很好地摆脱局部最优,但不好,因为这意味着算法永远无法稳定在最小值。解决这种困境的一种方法是逐渐降低学习率。这些步骤一开始很大(这有助于快速进步并避开局部最小值),然后变得越来越小,从而使算法能够稳定在全局最小值处。这个过程类似于模拟退火,这是一种受退火冶金过程启发的算法,其中熔融金属被缓慢冷却。这确定每次迭代学习率的函数称为学习计划。如果学习率降低得太快,您可能会陷入局部最小值,甚至最终冻结到最小值的一半。如果学习率降低得太慢,你可能会在最小值附近跳跃很长时间,如果你过早停止训练,最终会得到一个次优的解决方案。
此代码使用简单的学习计划实现随机梯度下降:
- n_epochs = 50
- t0, t1 = 5, 50 # learning schedule hyperparameters
-
- def learning_schedule(t):
- return t0 / (t + t1)
-
- theta = np.random.randn(2,1) # random initialization
-
- for epoch in range(n_epochs):
- for i in range(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
经过约定我们通过m轮迭代进行迭代;每一轮称为一个纪元。虽然 Batch Gradient Descent 代码在整个训练集上迭代了 1000 次,但这段代码只在训练集上迭代了 50 次并达到了一个很好的解决方案:
>>> theta
array([[4.21076011],
[2.74856079]])
图 4-10显示了训练的前 20 个步骤(注意这些步骤的不规则性)。
图 4-10。随机梯度下降的前 20 步
请注意,由于实例是随机选择的,因此每个 epoch 可能会多次选择某些实例,而其他实例可能根本不会被选择。如果您想确保算法在每个 epoch 遍历每个实例,另一种方法是打乱训练集(确保联合打乱输入特征和标签),然后逐个实例遍历它,然后打乱它再次,依此类推。然而,这种方法通常收敛得更慢。
警告
什么时候使用随机梯度下降,训练实例必须是独立同分布(IID),以确保参数平均拉向全局最优。确保这一点的一种简单方法是在训练期间对实例进行混洗(例如,随机选择每个实例,或在每个时期开始时对训练集进行混洗)。如果您不打乱实例(例如,如果实例按标签排序),则 SGD 将首先针对一个标签进行优化,然后是下一个,依此类推,并且它不会接近全局最小值。
要使用带有 Scikit-Learn 的随机 GD 执行线性回归,您可以使用SGDRegressor
该类,该类默认优化平方误差成本函数。max_iter=1000
以下代码最多运行 1,000 个 epoch 或直到在一个 epoch ( , )期间损失下降小于 0.001 tol=1e-3
。它以 0.1 ( ) 的学习率开始eta0=0.1
,使用默认的学习计划(与前一个不同)。最后,它不使用任何正则化(稍后penalty=None
将详细介绍):
- from sklearn.linear_model import SGDRegressor
- sgd_reg = SGDRegressor(max_iter=1000, tol=1e-3, penalty=None, eta0=0.1)
- sgd_reg.fit(X, y.ravel())
再一次,您会发现一个非常接近 Normal Equation返回的解:
- >>> sgd_reg.intercept_, sgd_reg.coef_
- (array([4.24365286]), array([2.8250878]))
这我们将看到的最后一个梯度下降算法称为Mini-batch Gradient Descent。一旦你了解了 Batch 和 Stochastic Gradient Descent,就很容易理解:在每一步,不是基于完整的训练集(如在 Batch GD 中)或仅基于一个实例(如在 Stochastic GD 中)计算梯度,Mini-批量 GD 计算小的随机实例集的梯度称为小批量。Mini-batch GD 相对于 Stochastic GD 的主要优势在于,您可以从矩阵运算的硬件优化中获得性能提升,尤其是在使用 GPU 时。
该算法在参数空间中的进展不如随机 GD 不稳定,尤其是在相当大的 mini-batch 中。因此,Mini-batch GD 最终会比 Stochastic GD 更接近最小值——但它可能更难摆脱局部最小值(在受局部最小值影响的问题的情况下,不像线性回归)。图 4-11显示了三种梯度下降算法在训练过程中在参数空间中所采用的路径。它们最终都接近最小值,但 Batch GD 的路径实际上停止在最小值处,而 Stochastic GD 和 Mini-batch GD 都继续走动。但是,不要忘记,Batch GD 每一步都需要花费大量时间,如果使用良好的学习计划,Stochastic GD 和 Mini-batch GD 也会达到最小值。
图 4-11。参数空间中的梯度下降路径
让我们比较一下到目前为止我们讨论过的线性回归6的算法(回想一下,m是训练实例的数量,n是特征的数量);见表4-1。
算法 | 大米_ | 核外支持 | 大n | 超参数 | 需要缩放 | Scikit-学习 |
---|---|---|---|---|---|---|
正规方程 | 快速地 | 不 | 减缓 | 0 | 不 | 不适用 |
SVD | 快速地 | 不 | 减缓 | 0 | 不 |
|
批量 GD | 减缓 | 不 | 快速地 | 2 | 是的 |
|
随机GD | 快速地 | 是的 | 快速地 | ≥2 | 是的 |
|
小批量GD | 快速地 | 是的 | 快速地 | ≥2 | 是的 |
|
什么如果你的数据比直线更复杂?令人惊讶的是,您可以使用线性模型来拟合非线性数据。一个简单的方法是添加每个特征的幂作为新特征,然后在这个扩展的特征集上训练一个线性模型。这种技术称为多项式回归。
让我们看一个例子。首先,让我们根据一个简单的二次方程7生成一些非线性数据(加上一些噪声;见图 4-12):
- m = 100
- X = 6 * np.random.rand(m, 1) - 3
- y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)
图 4-12。生成的非线性和噪声数据集
显然,一条直线永远无法正确拟合这些数据。所以让我们使用 Scikit-Learn 的PolynomialFeatures
类来转换我们的训练数据,将训练集中每个特征的平方(二次多项式)添加为一个新特征(在这种情况下只有一个特征):
- >>> from sklearn.preprocessing import PolynomialFeatures
- >>> poly_features = PolynomialFeatures(degree=2, include_bias=False)
- >>> X_poly = poly_features.fit_transform(X)
- >>> X[0]
- array([-0.75275929])
- >>> X_poly[0]
- array([-0.75275929, 0.56664654])
X_poly
现在包含原始特征X
加上这个特征的平方。现在你可以LinearRegression
为这个扩展的训练数据拟合一个模型(图 4-13):
- >>> lin_reg = LinearRegression()
- >>> lin_reg.fit(X_poly, y)
- >>> lin_reg.intercept_, lin_reg.coef_
- (array([1.78134581]), array([[0.93366893, 0.56456263]]))
图 4-13。多项式回归模型预测
不错:模型估计 y^=0.56x12+0.93x1+1.78事实上,原来的功能是 y=0.5x12+1.0x1+2.0+Gaussian noise.
请注意,当有多个特征时,多项式回归能够找到特征之间的关系(这是普通线性回归模型无法做到的)。这可以通过PolynomialFeatures
将所有特征组合添加到给定程度的事实来实现。例如,如果有两个特征a和b,PolynomialFeatures
withdegree=3
不仅会添加特征a 2、a 3、b 2和b 3,还会添加组合ab、a 2 b和ab 2。
如果如果您执行高度多项式回归,您可能会比使用普通线性回归更好地拟合训练数据。例如,图 4-14将 300 次多项式模型应用于前面的训练数据,并将结果与纯线性模型和二次模型(二次多项式)进行比较。请注意 300 度多项式模型如何摆动以尽可能接近训练实例。
图 4-14。高次多项式回归
这种高度多项式回归模型严重过拟合训练数据,而线性模型则欠拟合。在这种情况下,最能泛化的模型是二次模型,这是有道理的,因为数据是使用二次模型生成的。但一般情况下,您不知道生成数据的函数是什么,那么您如何确定模型的复杂程度呢?你怎么知道你的模型是过拟合还是欠拟合数据?
在第 2 章中,您使用了交叉验证来估计模型的泛化性能。如果模型在训练数据上表现良好,但根据交叉验证指标泛化能力较差,则您的模型过度拟合。如果它在两者上都表现不佳,那么它是欠拟合的。这是判断模型何时太简单或太复杂的一种方法。
另一种判断方法是查看学习曲线:这些是模型在训练集和验证集上的性能图,作为训练集大小(或训练迭代)的函数。要生成绘图,请在训练集的不同大小子集上多次训练模型。下面的代码定义了一个函数,给定一些训练数据,绘制模型的学习曲线:
- from sklearn.metrics import mean_squared_error
- from sklearn.model_selection import train_test_split
-
- def plot_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 in range(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")
让我们看一下普通线性回归模型的学习曲线(一条直线;见图 4-15):
- lin_reg = LinearRegression()
- plot_learning_curves(lin_reg, X, y)
图 4-15。学习曲线
这个欠拟合的模型值得解释一下。首先,让我们看看在训练数据上的表现:当训练集中只有一两个实例时,模型可以完美地拟合它们,这就是曲线从零开始的原因。但是随着新的实例被添加到训练集中,模型不可能完美地拟合训练数据,因为数据是嘈杂的,而且因为它根本不是线性的。因此,训练数据上的误差会一直上升,直到达到一个平台期,此时向训练集中添加新实例不会使平均误差变得更好或更糟。现在让我们看看模型在验证数据上的表现。当模型在很少的训练实例上训练时,它无法正确泛化,这就是验证误差最初很大的原因。然后,随着模型显示更多的训练示例,它会学习,因此验证错误会慢慢下降。然而,再一次,一条直线不能很好地对数据进行建模,因此误差最终会达到一个平台,非常接近另一条曲线。
这些学习曲线是欠拟合模型的典型特征。两条曲线都达到了平台期;它们接近且相当高。
小费
如果您的模型对训练数据的拟合不足,则添加更多训练示例将无济于事。您需要使用更复杂的模型或提出更好的功能。
现在让我们看一下 10 次多项式模型在相同数据上的学习曲线(图 4-16):
- 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-16。10 次多项式模型的学习曲线
这些学习曲线看起来有点像以前的,但有两个非常重要的区别:
训练数据的误差远低于线性回归模型。
曲线之间有间隙。这意味着该模型在训练数据上的表现明显优于在验证数据上的表现,这是过拟合模型的标志。但是,如果您使用更大的训练集,两条曲线将继续接近。
小费
改进过拟合模型的一种方法是为其提供更多的训练数据,直到验证误差达到训练误差为止。
偏差/方差权衡
一个统计学和机器学习的重要理论结果是模型的泛化误差可以表示为三个非常不同的误差之和:
偏见
这部分泛化误差是由于错误的假设造成的,例如假设数据是线性的,而实际上它是二次的。高偏差模型最有可能欠拟合训练数据。8
方差
这部分是由于模型对训练数据的微小变化过于敏感。具有许多自由度的模型(例如高次多项式模型)可能具有高方差,因此会过度拟合训练数据。
不可减少的错误
这部分是由于数据本身的噪声。减少这部分错误的唯一方法是清理数据(例如,修复数据源,例如损坏的传感器,或检测并删除异常值)。
增加模型的复杂性通常会增加其方差并减少其偏差。相反,降低模型的复杂性会增加其偏差并减少其方差。这就是为什么它被称为权衡。
作为我们在第1章和第2章中看到,减少过度拟合的一个好方法是对模型进行正则化(即约束它):它的自由度越少,它就越难以过度拟合数据。正则化多项式模型的一种简单方法是减少多项式次数。
对于线性模型,正则化通常是通过约束模型的权重来实现的。我们现在将看看 Ridge 回归、Lasso 回归和 Elastic Net,它们实现了三种不同的方法来约束权重。
岭回归(也称为Tikhonov 正则化)是线性回归的正则化版本:正则化项等于α∑i=1nθi2被添加到成本函数中。这迫使学习算法不仅要拟合数据,还要使模型权重尽可能小。请注意,正则化项只能在训练期间添加到成本函数中。训练模型后,您希望使用非正则化性能度量来评估模型的性能。
笔记
训练期间使用的成本函数与用于测试的性能度量不同是很常见的。除了正则化之外,它们可能不同的另一个原因是良好的训练成本函数应该具有优化友好的导数,而用于测试的性能度量应该尽可能接近最终目标。例如,分类器通常使用诸如对数损失(稍后讨论)之类的成本函数进行训练,但使用精度/召回率进行评估。
超参数α控制您希望对模型进行多少正则化。如果α = 0,那么岭回归就是线性回归。如果α非常大,那么所有权重最终都非常接近于零,结果是一条穿过数据均值的平线。公式 4-8给出了岭回归成本函数。9
公式 4-8。岭回归成本函数
请注意,偏置项θ 0未正则化(总和从i = 1 开始,而不是 0)。如果我们将w定义为特征权重向量(θ 1到θ n),则正则化项等于 ½(∥ w ∥ 2 ) 2,其中∥ w ∥ 2表示权重向量的 ℓ 2范数。10对于梯度下降,只需将α w添加到 MSE 梯度向量(公式 4-6)。
警告
在执行岭回归之前缩放数据(例如,使用
StandardScaler
)很重要,因为它对输入特征的规模很敏感。大多数正则化模型都是如此。
图 4-17显示了几个使用不同α值在一些线性数据上训练的 Ridge 模型。在左侧,使用了普通的 Ridge 模型,导致线性预测。在右侧,数据首先使用 扩展PolynomialFeatures(degree=10)
,然后使用 缩放StandardScaler
,最后将 Ridge 模型应用于结果特征:这是带有 Ridge 正则化的多项式回归。请注意增加α如何导致更平坦(即,不那么极端,更合理)的预测,从而减少模型的方差但增加其偏差。
图 4-17。线性模型(左)和多项式模型(右),都具有不同级别的 Ridge 正则化
与线性回归一样,我们可以通过计算封闭式方程或执行梯度下降来执行岭回归。优点和缺点是一样的。公式 4-9显示闭式解,其中A是 ( n + 1) × ( n + 1)单位矩阵,11除了左上角单元格中的 0 对应于偏置项。
公式 4-9。岭回归闭式解
以下是如何使用 Scikit-Learn 使用封闭形式的解决方案(公式 4-9的变体,使用 André-Louis Cholesky 的矩阵分解技术)执行岭回归:
- >>> from sklearn.linear_model import Ridge
- >>> ridge_reg = Ridge(alpha=1, solver="cholesky")
- >>> ridge_reg.fit(X, y)
- >>> ridge_reg.predict([[1.5]])
- array([[1.55071465]])
并使用随机梯度下降:12
- >>> sgd_reg = SGDRegressor(penalty="l2")
- >>> sgd_reg.fit(X, y.ravel())
- >>> sgd_reg.predict([[1.5]])
- array([1.47012588])
penalty
超参数设置要使用的正则化术语的类型。指定"l2"
表示您希望 SGD 向成本函数添加一个正则化项,等于权重向量的 ℓ 2范数平方的一半:这就是岭回归。
最小绝对收缩和选择算子回归(通常简称为Lasso Regression)是线性回归的另一个正则化版本:就像岭回归一样,它在成本函数中添加了一个正则化项,但它使用权重向量的 ℓ 1范数而不是 ℓ 2范数的平方的一半(参见公式 4-10) .
公式 4-10。套索回归成本函数
图 4-18显示了与图 4-17相同的内容,但将 Ridge 模型替换为 Lasso 模型并使用更小的α值。
图 4-18。线性模型(左)和多项式模型(右),均使用不同级别的 Lasso 正则化
Lasso Regression 的一个重要特征是它倾向于消除最不重要特征的权重(即,将它们设置为零)。例如,图 4-18右侧图中的虚线(α = 10 -7)看起来大致为三次:所有高次多项式特征的权重都等于 0。换句话说,Lasso Regression 自动执行特征选择并输出一个稀疏模型(即,具有很少的非零特征权重)。
通过查看图 4-19,您可以了解为什么会出现这种情况:轴代表两个模型参数,背景轮廓代表不同的损失函数。在左上角的图中,等高线表示 ℓ 1损失 (| θ 1 | + | θ 2 |),随着您靠近任何轴,它会线性下降。例如,如果您将模型参数初始化为θ 1 = 2 和θ 2 = 0.5,运行梯度下降将相等地递减这两个参数(如黄色虚线所示);因此θ 2将首先达到 0(因为它开始时更接近 0)。之后,梯度下降将向下滚动,直到达到θ 1 = 0(有一点反弹,因为 ℓ 1的梯度永远不会接近 0:它们对于每个参数都是 –1 或 1)。在右上角的图中,等高线表示 Lasso 的成本函数(即 MSE 成本函数加上 ℓ 1损失)。白色小圆圈显示梯度下降优化某些模型参数的路径,这些参数在θ 1 = 0.25 和θ 2 = –1 附近初始化:再次注意路径如何快速达到θ 2= 0,然后滚下排水沟并最终围绕全局最优值(由红色方块表示)反弹。如果我们增加α,全局最优值将沿黄色虚线向左移动,而如果我们减小α,全局最优值将向右移动(在本例中,非正则化 MSE 的最优参数为θ 1 = 2 和θ 2 = 0.5)。
图 4-19。Lasso 与 Ridge 正则化
底部的两个图显示了相同的内容,但惩罚为 ℓ 2 。在左下角的图中,您可以看到 ℓ 2损失随着到原点的距离而减小,因此梯度下降只是朝着该点采取直线路径。在右下角的图中,等高线代表岭回归的成本函数(即 MSE 成本函数加上 ℓ 2损失)。与 Lasso 有两个主要区别。首先,随着参数接近全局最优,梯度会变小,所以梯度下降自然会减慢,这有助于收敛(因为没有反弹)。其次,当你增加α时,最优参数(由红色方块表示)越来越接近原点,但他们永远不会被完全淘汰。
小费
为了避免梯度下降在使用 Lasso 时最后在最优值附近反弹,需要在训练时逐渐降低学习率(它仍然会在最优值附近反弹,但步长会越来越小,所以会收敛)。
这Lasso 成本函数在θ i = 0 时不可微(对于i = 1, 2,⋯, n ),但如果在任何θ i = 0时使用次梯度向量 g 13代替,梯度下降仍然可以正常工作。公式 4-11显示了一个次梯度向量方程,您可以将其用于带有 Lasso 成本函数的梯度下降。
公式 4-11。套索回归次梯度向量
这是一个使用该类的小型 Scikit-Learn 示例Lasso
:
- >>> from sklearn.linear_model import Lasso
- >>> lasso_reg = Lasso(alpha=0.1)
- >>> lasso_reg.fit(X, y)
- >>> lasso_reg.predict([[1.5]])
- array([1.53788174])
请注意,您可以改为使用SGDRegressor(penalty="l1")
.
Elastic Net是岭回归和套索回归之间的中间地带。正则化项是 Ridge 和 Lasso 正则化项的简单混合,您可以控制混合比r。当r = 0 时,Elastic Net 相当于 Ridge Regression,当r = 1 时,它相当于 Lasso Regression(见公式 4-12)。
公式 4-12。弹性网络成本函数
那么什么时候应该使用简单的线性回归(即没有任何正则化)、Ridge、Lasso 或 Elastic Net?至少有一点正则化几乎总是可取的,所以通常你应该避免简单的线性回归。Ridge 是一个很好的默认值,但如果您怀疑只有少数特征有用,您应该更喜欢 Lasso 或 Elastic Net,因为它们倾向于将无用特征的权重降低到零,正如我们所讨论的。一般来说,Elastic Net 比 Lasso 更受欢迎,因为当特征数量大于训练实例的数量或多个特征高度相关时,Lasso 可能会出现异常行为。
这是一个使用 Scikit-Learn 的简短示例ElasticNet
(l1_ratio
对应于混合比r):
- >>> from sklearn.linear_model import ElasticNet
- >>> elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
- >>> elastic_net.fit(X, y)
- >>> elastic_net.predict([[1.5]])
- array([1.54333232])
一个正则化迭代学习算法(例如梯度下降)的非常不同的方法是在验证错误达到最小值时立即停止训练。这称为提前停止。图 4-20显示了一个使用批量梯度下降训练的复杂模型(在本例中为高度多项式回归模型)。随着 epochs 算法的学习,它在训练集上的预测误差 (RMSE) 和在验证集上的预测误差一起下降。但过了一段时间,验证错误停止减少并开始回升。这表明模型已经开始过度拟合训练数据。通过提前停止,您只需在验证错误达到最小值时立即停止训练。这是一种简单而有效的正则化技术,Geoffrey Hinton 称其为“美丽的免费午餐”。
图 4-20。早停正则化
小费
使用 Stochastic 和 Mini-batch Gradient Descent,曲线不是那么平滑,可能很难知道你是否达到了最小值。一种解决方案是仅在验证误差高于最小值一段时间后停止(当您确信模型不会做得更好时),然后将模型参数回滚到验证误差最小的点.
这是早期停止的基本实现:
- from copy import deepcopy
-
- # 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)
-
- minimum_val_error = float("inf")
- best_epoch = None
- best_model = None
- for epoch in range(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 = deepcopy(sgd_reg)
作为我们在第 1 章讨论过,一些回归算法可用于分类(反之亦然)。逻辑回归(也称为Logit 回归)通常用于估计实例属于特定类别的概率(例如,这封电子邮件是垃圾邮件的概率是多少?)。如果估计概率大于 50%,则模型预测实例属于该类(称为正类,标记为“1”),否则预测不属于该类(即,它属于负类,标记为“0”)。这使它成为一个二元分类器。
所以逻辑回归如何工作?就像线性回归模型一样,逻辑回归模型计算输入特征的加权和(加上一个偏差项),但它不像线性回归模型那样直接输出结果,而是输出这个结果的逻辑(参见方程4-13 )。
公式 4-13。逻辑回归模型估计概率(向量化形式)
后勤——记为σ (·)——是输出 0 到 1 之间数字的sigmoid 函数(即S形)。它的定义如公式 4-14和图 4-21 所示。
公式 4-14。Logistic function
图 4-21。物流功能
一旦逻辑回归模型估计了概率p^= h θ ( x ) 表示实例x属于正类,它可以很容易地做出预测ŷ(见公式 4-15)。
公式 4-15。逻辑回归模型预测
请注意,当t < 0时σ ( t ) < 0.5,当t ≥ 0 时σ ( t ) ≥ 0.5 ,因此如果θ ⊺ x为正,则逻辑回归模型预测为 1 ,如果为负,则预测为 0。
现在你知道了逻辑回归模型如何估计概率并做出预测。但它是如何训练的?训练的目标是设置参数向量θ,以便模型估计正实例的高概率(y = 1)和负实例的低概率(y = 0)。这个想法被公式 4-16中显示的单个训练实例x的成本函数捕获。
公式 4-16。单个训练实例的成本函数
这个成本函数是有意义的,因为当t接近 0 时 –log( t ) 变得非常大,因此如果模型估计正实例的概率接近 0,成本会很大,如果模型估计的概率也会很大负实例的概率接近 1。另一方面,当t接近 1 时,–log( t ) 接近 0 ,因此如果估计概率对于负实例接近 0 或对于正实例接近 1,则成本将接近 0,这正是我们想要的。
这整个训练集的成本函数是所有训练实例的平均成本。它可以写成一个名为log loss的表达式,如公式 4-17所示。
公式 4-17。逻辑回归成本函数(对数损失)
坏消息是没有已知的封闭式方程来计算最小化这个成本函数的θ值(没有等价的正规方程)。好消息是这个成本函数是凸的,所以梯度下降(或任何其他优化算法)可以保证找到全局最小值(如果学习率不是太大并且你等待的时间足够长)。成本函数关于第j个模型参数θ j的偏导数由公式 4-18给出。
公式 4-18。逻辑成本函数偏导数
这个方程看起来很像方程 4-5:对于每个实例,它计算预测误差并将其乘以第j个特征值,然后计算所有训练实例的平均值。一旦有了包含所有偏导数的梯度向量,就可以在批量梯度下降算法中使用它。就是这样:您现在知道如何训练逻辑回归模型了。对于 Stochastic GD,您将一次使用一个实例,而对于 Mini-batch GD,您将一次使用一个 mini-batch。
让我们使用这iris 数据集来说明逻辑回归。这是一个著名的数据集,包含三种不同物种的 150 朵鸢尾花的萼片和花瓣长度和宽度:Iris setosa、Iris versicolor和Iris virginica(见图 4-22)。
图 4-22。三种鸢尾植物的花14
让我们尝试构建一个分类器,仅基于花瓣宽度特征来检测Iris virginica类型。首先让我们加载数据:
- >>> from sklearn import datasets
- >>> iris = datasets.load_iris()
- >>> list(iris.keys())
- ['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename']
- >>> X = iris["data"][:, 3:] # petal width
- >>> y = (iris["target"] == 2).astype(np.int) # 1 if Iris virginica, else 0
现在让我们训练一个逻辑回归模型:
- from sklearn.linear_model import LogisticRegression
-
- log_reg = LogisticRegression()
- log_reg.fit(X, y)
让我们看看模型对花瓣宽度从 0 厘米到 3 厘米不等的花朵的估计概率(图 4-23):15
- 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")
- # + more Matplotlib code to make the image look pretty
图 4-23。估计概率和决策边界
鸢尾花的花瓣宽度(以三角形表示)从1.4厘米到2.5厘米不等,而其他鸢尾花(以正方形表示)的花瓣宽度一般较小,从0.1厘米到1.8厘米不等。注意有一点重叠。大约 2 厘米以上,分类器高度确信这朵花是弗吉尼亚鸢尾(它输出该类的概率很高),而低于 1 厘米它高度确信它不是弗吉尼亚鸢尾(“非鸢尾花”的概率很高)弗吉尼亚”类)。在这两个极端之间,分类器是不确定的。但是,如果您要求它预测类(使用predict()
方法而不是predict_proba()
方法),它将返回最有可能的类。因此,有一个决策边界在 1.6 cm 左右,其中两个概率都等于 50%:如果花瓣宽度高于 1.6 cm,分类器将预测该花是Iris virginica,否则将预测它不是(即使它不是很自信):
>>> log_reg.predict([[1.7],[1.5]])
array([1, 0])
图 4-24显示了相同的数据集,但这次显示了两个特征:花瓣宽度和长度。一旦经过训练,Logistic 回归分类器就可以根据这两个特征来估计一朵新花是Iris virginica的概率。虚线表示模型估计 50% 概率的点:这是模型的决策边界。请注意,它是一个线性边界。16每条平行线代表模型输出特定概率的点,从 15%(左下)到 90%(右上)。根据该模型,右上角线以外的所有花朵都有超过 90% 的可能性是Iris virginica 。
图 4-24。线性决策边界
就像其他线性模型一样,逻辑回归模型可以使用 ℓ 1或 ℓ 2惩罚进行正则化。Scikit-Learn 实际上默认增加了 ℓ 2的惩罚。
笔记
控制 Scikit-Learn
LogisticRegression
模型正则化强度的超参数不是alpha
(如在其他线性模型中),而是它的逆参数:C
. 的值越高C
,模型的正则化越少。
这逻辑回归模型可以推广到直接支持多个类,而无需训练和组合多个二元分类器(如第 3 章所述)。这称为Softmax 回归或多项 Logistic 回归。
这个想法很简单:当给定一个实例x时,Softmax 回归模型首先计算每个类k的分数s k ( x ) ,然后估计每个类的概率通过将softmax 函数(也称为归一化指数)应用于分数。计算s k ( x ) 的方程应该看起来很熟悉,因为它就像线性回归预测的方程(见方程 4-19)。
公式 4-19。k 类的 Softmax 分数
笔记每个类都有自己的专用参数向量θ ( k )。所有这些向量通常存储为参数矩阵 Θ中的行。
一旦你计算了实例x的每个类的分数,你就可以估计概率p^k通过 softmax 函数(公式 4-20 )运行分数,实例属于k类。该函数计算每个分数的指数,然后对它们进行归一化(除以所有指数的总和)。这些分数通常称为 logits 或 log-odds(尽管它们实际上是非标准化的 log-odds)。
公式 4-20。Softmax 函数
在这个等式中:
K是类的数量。
s ( x ) 是一个向量,其中包含实例x的每个类的分数。
σ ( s ( x )) k是实例x属于类k的估计概率,给定该实例的每个类的分数。
就像 Logistic Regression 分类器一样,Softmax Regression 分类器预测具有最高估计概率的类(简单来说就是得分最高的类),如公式 4-21所示。
公式 4-21。Softmax 回归分类器预测
argmax运算符返回使函数最大化的变量的值。在这个等式中,它返回使估计概率σ ( s ( x )) k最大化的k值。
小费
Softmax Regression 分类器一次只预测一个类(即它是多类,而不是多输出),因此它应该只用于互斥类,例如不同类型的植物。您不能使用它来识别一张图片中的多个人。
既然您知道模型如何估计概率和进行预测,那么让我们来看看训练。目标是有一个模型来估计目标类的高概率(因此其他类的概率低)。最小化公式 4-22中显示的成本函数,称为交叉熵,应该导致这个目标,因为它会在模型估计目标类的低概率时惩罚模型。交叉熵经常用于衡量一组估计的类概率与目标类的匹配程度。
公式 4-22。交叉熵成本函数
在这个等式中:
是ķ(一世)是第i个实例属于类k的目标概率。通常,它等于 1 或 0,这取决于实例是否属于该类。
请注意,当只有两个类 ( K = 2) 时,此成本函数等效于 Logistic 回归的成本函数(对数损失;请参见公式 4-17)。
交叉熵
交叉熵起源于信息论。假设您希望每天有效地传输有关天气的信息。如果有八个选项(晴天、下雨等),您可以使用三个比特对每个选项进行编码,因为 2 3 = 8。但是,如果您认为几乎每天都是晴天,那么编码“ Sunny”仅在一位 (0) 上,其他七个选项在四位上(从 1 开始)。交叉熵测量每个选项实际发送的平均位数。如果您对天气的假设是完美的,交叉熵将等于天气本身的熵(即,其内在的不可预测性)。但是如果你的假设是错误的(例如,如果经常下雨),交叉熵将大于称为Kullback-Leibler (KL) 散度的量。
两个概率分布p和q之间的交叉熵定义为H ( p , q ) = —Σ x p ( x ) log q ( x )(至少当分布是离散的时)。有关更多详细信息,请查看我关于该主题的视频。
该成本函数关于θ ( k )的梯度向量由公式 4-23给出。
公式 4-23。k 类的交叉熵梯度向量
现在您可以计算每个类的梯度向量,然后使用梯度下降(或任何其他优化算法)来找到最小化成本函数的参数矩阵Θ 。
让我们使用 Softmax Regression 将鸢尾花分为所有三类。当您在两个以上的类上训练时, Scikit-LearnLogisticRegression
默认使用 one-versus-the-rest,但您可以multi_class
将超参数设置为"multinomial"
将其切换到 Softmax Regression。您还必须指定支持 Softmax Regression 的求解器,例如"lbfgs"
求解器(有关更多详细信息,请参阅 Scikit-Learn 的文档)。它还默认应用 ℓ 2C
正则化,您可以使用超参数来控制:
- 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)
所以下次当你找到一个花瓣长 5 厘米、宽 2 厘米的鸢尾花时,你可以让你的模型告诉你它是什么类型的鸢尾花,它会以 94.2% 的概率回答Iris virginica (class 2) (或云芝,概率为 5.8%):
- >>> softmax_reg.predict([[5, 2]])
- array([2])
- >>> softmax_reg.predict_proba([[5, 2]])
- array([[6.38014896e-07, 5.74929995e-02, 9.42506362e-01]])
图 4-25显示了最终的决策边界,由背景颜色表示。请注意,任何两个类之间的决策边界都是线性的。该图还显示了花斑鸢尾类的概率,由曲线表示(例如,标有 0.450 的线表示 45% 的概率边界)。请注意,该模型可以预测估计概率低于 50% 的类别。例如,在所有决策边界相交的点,所有类别的估计概率相等,为 33%。
图 4-25。Softmax 回归决策边界
如果您有一个包含数百万个特征的训练集,您可以使用哪种线性回归训练算法?
假设您的训练集中的特征具有非常不同的尺度。哪些算法可能会受到这种影响,以及如何受到影响?你能为这个做什么?
训练逻辑回归模型时,梯度下降会陷入局部最小值吗?
如果您让它们运行足够长的时间,是否所有梯度下降算法都会导致相同的模型?
假设您使用 Batch Gradient Descent 并在每个 epoch 绘制验证错误。如果您注意到验证错误持续上升,那么可能发生了什么?你怎么能解决这个问题?
当验证错误上升时立即停止小批量梯度下降是个好主意吗?
哪种梯度下降算法(在我们讨论过的算法中)会最快到达最优解附近?哪个会真正收敛?你怎么能让其他人也收敛?
假设您正在使用多项式回归。您绘制了学习曲线,并注意到训练误差和验证误差之间存在很大差距。怎么了?解决这个问题的三种方法是什么?
假设您正在使用岭回归,并且您注意到训练误差和验证误差几乎相等且相当高。你会说模型存在高偏差或高方差吗?你应该增加正则化超参数α还是减少它?
为什么要使用:
岭回归而不是普通的线性回归(即,没有任何正则化)?
套索而不是岭回归?
弹性网代替套索?
假设您要将图片分类为户外/室内和白天/夜间。您应该实现两个逻辑回归分类器还是一个 Softmax 回归分类器?
使用 Softmax 回归的早期停止实现批量梯度下降(不使用 Scikit-Learn)。
附录 A中提供了这些练习的解决方案。
1通常情况下,学习算法会尝试优化与用于评估最终模型的性能度量不同的函数。这通常是因为该函数更容易计算,因为它具有性能度量所缺乏的有用的微分属性,或者因为我们希望在训练期间约束模型,正如您将在讨论正则化时看到的那样。
2请注意,Scikit-Learn 将偏差项 ( intercept_
) 与特征权重 ( coef_
) 分开。
3从技术上讲,它的导数是Lipschitz 连续的。
4由于特征1较小,因此需要较大的θ1变化来影响成本函数,这就是碗沿 θ1 轴伸长的原因。
5Eta ( η ) 是希腊字母的第七个字母。
6正如我们将看到的,虽然正态方程只能执行线性回归,但梯度下降算法可用于训练许多其他模型。
7二次方程的形式为y = ax2+ bx + c。
8不要将这种偏差概念与线性模型的偏差项相混淆。
9对于没有短名称的成本函数,通常使用符号J ( θ );在本书的其余部分,我们将经常使用这种表示法。上下文将清楚地说明正在讨论哪个成本函数。
11一个除主对角线(左上到右下)上的 1 外全为 0 的方阵。
12或者,您可以将Ridge
类与"sag"
求解器一起使用。随机平均 GD 是随机 GD 的变体。有关更多详细信息,请参阅Mark Schmidt 等人的演讲“使用随机平均梯度算法最小化有限和” 。来自不列颠哥伦比亚大学。
13您可以将不可微点处的次梯度向量视为围绕该点的梯度向量之间的中间向量。
14从相应的维基百科页面复制的照片。弗兰克·梅菲尔德 (Frank Mayfield) 拍摄的Iris virginica照片(Creative Commons BY-SA 2.0), D. Gordon E. Robertson 拍摄的Iris versicolor照片(Creative Commons BY-SA 3.0),Iris setosa照片公共领域。
15NumPy 的reshape()
函数允许一维为 –1,这意味着“未指定”:该值是从数组的长度和其余维度推断出来的。
16它是一组点x使得θ 0 + θ 1 x 1 + θ 2 x 2 = 0,它定义了一条直线。