Loading

机器学习实战——训练模型:线性回归、梯度下降、正则化、逻辑回归

线性回归模型

在机器学习的线性模型中,尤其是线性回归和逻辑回归等,模型的预测通常可以表示为特征的线性组合加上一个常数项(即截距项或偏置项)。这个常数项允许模型的预测结果在没有任何特征影响时也能有一个非零的基础值。

当我们使用矩阵运算来表示线性模型时,特征矩阵X的每一行代表一个观测样本的特征值,每一列代表一个特征。为了将截距项纳入这个矩阵运算中,我们需要在特征矩阵X中添加一个额外的列,这一列的所有元素都是1(或称为单位列向量)。这样,当我们用模型参数(包括截距项和每个特征的系数)与这个扩展后的特征矩阵相乘时,截距项就会与这个全1列相乘,从而作为一个单独的项加到每个预测值上。

线性回归模型预测:


其中,y是预测值,θ0是偏差项,也可以说是截距项,θ1,θ2,…,θn是特征权重,也就是特征的系数,\(x_1, x_2, \ldots, x_n\) 是特征值。

写成向量化形式:


这里的\(X\)只包含特征值,没有截距项的位置。为了包含截距项,我们需要在\(X\)的左侧添加一个全1的列,并相应地扩展\(\beta\)向量,使其第一个元素是截距项\(\theta_0\)。这样,新的方程就变成了:

在这个新的矩阵方程中,全1列与截距项\(\theta_0\)相乘,确保了每个预测值都加上了截距项的影响。因此,通过在特征矩阵前多加一列全1的列,我们就能够方便地在模型中包含截距项。

训练线性回归模型

训练模型就是设置模型参数直到模型最拟合训练集的过程。在训练线性回归模型时,需要找到最小化RMSE的\(\theta\)值。
线性回归模型的MSE成本函数:

为了得到使得成本函数MSE最小,可以由标准方程求出:

  • \(\theta\)是使成本函数最小的θ值
  • y是包含了y1到ym的目标值向量

正规方程的代码:

theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y) 
  1. X_b:这是扩展后的特征矩阵,它包含了原始的特征矩阵X以及一个额外的全1列(作为截距项)。因此,X_b的形状是(m, n+1),其中m是样本数量,n是原始特征的数量。
  2. X_b.T:这是X_b的转置矩阵,其形状为(n+1, m)。转置操作是为了在后续的矩阵乘法中满足维度要求。
  3. X_b.T.dot(X_b):这是X_b的转置与X_b自身的点积(也称为矩阵乘法),结果是一个(n+1, n+1)的矩阵。这个矩阵通常被称为设计矩阵(Design Matrix)的Gram矩阵或协方差矩阵。
  4. np.linalg.inv(X_b.T.dot(X_b)):这是计算上一步得到的矩阵的逆矩阵。逆矩阵的存在性依赖于X_b.T.dot(X_b)的可逆性,这通常要求特征矩阵X(不包括截距项的全1列)的列向量是线性独立的。
  5. np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T):这是逆矩阵与X_b的转置的乘积。由于矩阵乘法的结合律,这个表达式也可以写为np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T.dot(np.eye(m))),其中np.eye(m)m阶单位矩阵,但在这个上下文中直接写X_b.T就足够了,因为单位矩阵与任何矩阵的乘积都是该矩阵本身。
  6. np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y):最后,我们将上一步的结果与目标变量y(形状为(m,))进行点积,得到参数向量theta_best。这个向量的形状是(n+1,),包含了截距项和每个特征的系数。

线性回归模型预测

>>> 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]])

y_predict = X_new_b.dot(theta_best) 这行代码在机器学习的上下文中,通常用于根据模型的参数(即权重和偏置项)和输入特征来预测目标变量的值

.dot():这是NumPy库中用于执行矩阵乘法的函数。在这个上下文中,它执行的是 X_new_btheta_best 的矩阵乘法。但是,由于 theta_best 是一维数组,NumPy 会自动将其视为列向量来进行计算,这等价于将 theta_best 转换为一个 (n+1, 1) 的二维数组,然后进行标准的矩阵乘法。

使用Scikit-Learn执行线性回归

 from sklearn.linear_model import LinearRegression
 lin_reg = LinearRegression()
 lin_reg.fit(X, y)
 lin_reg.intercept_, lin_reg.coef_  ## 访问模型参数
## intercept截距   coef权重
lin_reg.predict(X_new) ## 直接进行预测

用最小二乘拟合数据(找到最佳参数):

theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6)
## 此时预测的结果跟上面 lin_reg.intercept_, lin_reg.coef_  ## 访问模型参数差不多

Scikit-Learn的LinearRegression类使用的SVD方法的复杂度约为O(n2)。
如果你将特征数量加倍,那计算时间大约是原来的4倍。
特征数量比较大(例如100 000)时,标准方程和SVD的计算将极其缓慢。

算法优化:梯度下降

步骤:首先使用一个随机的θ值(这被称为随机初始化),然后逐步改进,每次踏出一步,每一步都尝试降低一点成本函数(如MSE),直到算法收敛出一个最小值。

  • 学习率太低会在最小值的左边,算法需要经过大量迭代才能收敛。
  • 学习率太高则会在最小值的右边,会导致值越来越大,算法发散。

线性回归模型的MSE成本函数恰好是个凸函数,这意味着连接曲线上任意两点的线段永远不会跟曲线相交。也就是说,不存在局部最小值,只有一个全局最小值。

批量梯度下降

要实现梯度下降,你需要计算每个模型关于参数θj的成本函数的梯度。也就是说你需要直到当你改变了θj,成本函数会改变多少。这被称为偏导数。

  • 4-5:计算关于参数θj的成本函数的偏导数
  • 4-6:成本函数的梯度向量

在计算梯度下降的每一步时,都是基于完整的训练集X的。因此,面对非常庞大的训练集时,算法会变得极慢。但是,梯度下降算法随特征数量扩展的表现比较好。如果要训练的线性模型拥有几十万个特征,使用梯度下降比标准方程或者SVD要快得多。

批量梯度下降的代码:

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

这段代码是一个简单的线性回归模型的梯度下降实现,用于根据给定的数据点(通过矩阵X_b和向量y表示)来拟合模型参数theta

  • eta = 0.1:学习率(Learning Rate),用于控制梯度下降过程中的步长大小。较小的学习率可能导致收敛速度较慢,但较稳定;较大的学习率可能导致收敛速度较快,但也可能导致在最优解附近震荡甚至发散。
  • n_iterations = 1000:迭代次数,即梯度下降算法执行的次数。
  • m = 100:样本数量,这里假设数据集中有100个样本点。不过,在代码中并未直接使用m来直接计算,而是通过X_by的形状来隐含地表示。
  • theta = np.random.randn(2,1):模型参数的初始值,这里假设是一个二维向量(因为theta的形状是(2,1)),对应线性回归模型中的权重和截距(如果考虑一个特征和一个截距项的话)。np.random.randn(2,1)生成一个形状为(2,1)的数组,其元素是从标准正态分布中随机抽取的。
    在梯度下降过程中,我们迭代地更新模型参数theta,以最小化损失函数(这里是均方误差)。
  1. 计算梯度:gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)
    • 这行代码计算了损失函数关于theta的梯度。首先,X_b.dot(theta) - y计算了模型预测值与实际值之间的差(残差)。然后,X_b.T.dot(...)计算了残差与特征矩阵X_b的转置的矩阵乘积,这实际上是计算了损失函数对每个theta元素的偏导数(在均方误差损失函数下)。最后,2/m是梯度的缩放因子,与损失函数的二阶导数(即Hessian矩阵的对角线元素)有关,用于确保正确的步长。
  2. 更新参数theta = theta - eta * gradients
    • 使用学习率eta乘以梯度,然后从当前参数值中减去这个乘积,以更新参数。这个过程会重复进行,直到达到迭代次数n_iterations或满足其他停止条件(如梯度非常小,表示已接近最优解)。

答案跟上面的截距和权重差不多。

学习率对梯度下降的影响


左图学习率太低,能找到方法但是需要很长时间;中间图很好,几次迭代就能找到解;右图效率太高,算法发散,跳过了数据区域。

随机梯度下降

批量梯度下降的主要问题是它要用整个训练集来计算每一步的梯度,所以训练集很大时,算法会特别慢。与之相反的极端是随机梯度下降,每一步在训练集中随机选择一个实例,并且仅基于该单个实例来计算梯度。

由于算法的随机性质,它比批量梯度下降要不规则得多。成本函数将不再是缓缓降低直到抵达最小值,而是不断上上下下,但是从整体来看,还是在慢慢下降。随着时间的推移,最终会非常接近最小值,但是即使它到达了最小值,依旧还会持续反弹,永远不会停止。所以算法停下来的参数值肯定是足够好的,但不是最优的。


随机性的好处在于可以逃离局部最优,但缺点是永远定位不出最小值。要解决这个困境,有一个办法是逐步降低学习率。开始的步长比较大(这有助于快速进展和逃离局部最小值),然后越来越小,让算法尽量靠近全局最小值。
随机梯度下降代码:

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

这段代码实现了一个带有学习率调度的随机梯度下降(Stochastic Gradient Descent, SGD)算法来优化一个线性模型的参数。
与标准的批量梯度下降(每次迭代使用所有样本)或小批量梯度下降(每次迭代使用一小部分样本)不同,这里使用的是随机梯度下降,它每次迭代只使用一个样本(尽管这里通过随机索引选择了样本,但实际上每次迭代仍然只处理了一个样本的梯度)。

此外,该算法还引入了一个学习率调度函数,以随着迭代次数的增加逐渐减小学习率。

  • n_epochs = 50:总的迭代次数,即整个数据集将被遍历多少次。但由于这是随机梯度下降,每次迭代只使用一个样本,所以实际上每个样本在n_epochs * m次迭代中会被随机选择到一次。
  • t0, t1 = 5, 50:学习率调度的超参数,用于控制学习率如何随着迭代次数的增加而减少。
  • learning_schedule(t):学习率调度函数,根据当前的迭代次数(或更准确地说,是已经处理的样本总数)t来计算当前的学习率。学习率随着t的增加而逐渐减小,有助于在接近最优解时减小步长,从而更稳定地收敛。
  1. 初始化参数:随机初始化模型参数theta

  2. 迭代过程:对于每个轮次(epoch),执行以下步骤:

    • 对于数据集中的每个样本(尽管是随机选择的),执行以下操作:
    • 随机选择一个样本的索引random_index
    • 根据索引选择对应的特征向量xi和目标值yi。注意,这里使用了切片操作random_index:random_index+1来保持向量的形状,但实际上只选择了一个样本。
    • 计算当前参数的梯度gradients。由于只使用了一个样本,这里的梯度计算与批量或小批量梯度下降有所不同,它只基于单个样本的误差。
    • 根据当前的迭代次数(实际上是已经处理的样本总数,通过epoch * m + i计算)和学习率调度函数计算当前的学习率eta
    • 使用学习率和梯度来更新模型参数theta

注意事项由于每次迭代只使用一个样本,随机梯度下降通常比批量或小批量梯度下降更快,但也可能导致收敛过程中的更多波动。

学习率调度函数在这里被用来随着迭代次数的增加而逐渐减小学习率,这有助于在接近最优解时减小步长,从而更稳定地收敛。

使用随机梯度下降时,训练实例必须独立且均匀分布(IID),以确保平均而言将参数拉向全局最优值。确保这一点的一种简单方法是在训练过程中对实例进行随机混洗(例如,随机选择每个实例,或者在每个轮次开始时随机混洗训练集)。如果不对实例进行混洗(例如,如果实例按标签排序),那么SGD将首先针对一个标签进行优化,然后针对下一个标签进行优化,以此类推,并且它不会接近全局最小值。


要使用带有Scikit-Learn的随机梯度下降执行线性回归,可以使用SGDRegressor类,该类默认优化平方误差成本函数。

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())

代码创建了一个SGDRegressor的实例sgd_reg,并通过几个参数来配置它的行为:

  • max_iter=1000:指定了算法的最大迭代次数为1000次。如果算法在达到这个迭代次数之前满足其他停止条件(如tol),则可能提前停止。
  • tol=1e-3:这是算法的容忍度或收敛阈值。当损失(或成本)的改进小于这个值时,算法认为已经收敛,并停止迭代。这里的1e-3表示10−3或0.001。
  • penalty=None:这个参数用于指定正则化项的类型。在回归问题中,正则化项有助于防止过拟合。None表示不使用正则化。其他常见的选项包括'l2'(L2正则化)和'l1'(L1正则化),分别对应于Ridge回归和Lasso回归。
  • eta0=0.1:这是初始学习率。学习率决定了在每次迭代中权重更新的步长大小。较大的学习率可能导致更快的收敛,但也可能导致收敛到次优解或发散。SGDRegressor支持多种学习率调整策略,但eta0是初始值。

小批量梯度下降

在每一步中,不是根据完整的训练集(如批量梯度下降)或仅基于一个实例(如随机梯度下降)来计算梯度,小批量梯度下降在称为小型批量的随机实例集上计算梯度。小批量梯度下降优于随机梯度下降的主要优点是,你可以通过矩阵操作的硬件优化来提高性能,特别是在使用GPU时。
与随机梯度下降相比,该算法在参数空间上的进展更稳定,尤其是在相当大的小批次中。结果,小批量梯度下降最终将比随机梯度下降走得更接近最小值,但它可能很难摆脱局部最小值(在受局部最小值影响的情况下,不像线性回归)。


显示了训练期间参数空间中三种梯度下降算法所采用的路径。它们最终都接近最小值,但是批量梯度下降的路径实际上是在最小值处停止,而随机梯度下降和小批量梯度下降都继续走动。


线性回归算法的比较

多项式回归

如果数据比直线复杂,可以通过线性模型来拟合非线性数据,一个简单的方法就是将每个特征的幂次方添加为一个新特征,然后在此扩展特征集上训练一个线性模型。这种技术就是多项式回归

例如:

m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)
from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)
  1. 导入PolynomialFeatures类,PolynomialFeatures类用于生成多项式特征,这些特征是通过原始特征的幂和乘积的线性组合来创建的。

  2. 创建PolynomialFeatures实例。这里,我们创建了一个PolynomialFeatures的实例poly_features,并设置了两个参数:

    • degree=2:这指定了多项式的度(degree)。在这个例子中,我们设置为2,意味着我们想要生成的特征将包括原始特征的平方(例如,如果原始特征为x,则生成的特征将包括x^2)以及原始特征之间的乘积(如果有多个特征的话,例如x*y)。
    • include_bias=False:这个参数指定是否包括一个偏置项(也称为截距项或常数项)。设置为False意味着我们不希望生成一个单独的1(或截距项)作为特征的一部分。在某些情况下,包括偏置项可能是有用的,但在许多情况下,尤其是在已经包含常数项或截距项的情况下(如在线性回归模型中),可能不希望重复包含它。
  3. 应用PolynomialFeatures到数据集。这行代码将poly_features实例应用于数据集Xfit_transform方法首先计算转换所需的参数(尽管对于PolynomialFeatures来说,这一步主要是验证输入,因为转换只依赖于degreeinclude_bias参数),然后将X转换为新的特征空间。

    • fit部分:对于PolynomialFeatures来说,这一步主要是验证输入数据X的形状和类型是否符合要求,因为多项式特征的生成不依赖于X中的具体值,而是依赖于其特征的数量和指定的度。
    • transform部分:根据degreeinclude_bias参数,将X中的原始特征转换为多项式特征。转换后的数据存储在X_poly中。

X_poly现在包含了X的原始特征以及它们的平方(如果X是单特征的)或平方和乘积(如果X是多特征的)的组合。

例如,X_poly[0] 就是第一个数据的原始特征值和平方值:array([1.09934829, 1.20856666])

然后创建回归模型:

lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)

得到:

所以:模型估算

,而实际上原始函数为

的高斯噪声。

学习曲线

如果执行高阶多项式回归,与普通线性回归相比,拟合数据可能会更好。

图中300阶多项式回归比普通线性回归拟合数据更好,但容易严重过拟合训练数据。

在这种情况下,最能泛化的模型是二次模型,因为数据是使用二次模型生成的。

估计模型的泛化性能可以观察学习曲线:这个曲线绘制的是模型在训练集和验证集上关于训练集大小(或训练迭代)的性能函数。要生成这个曲线,只需要在不同大小的训练子集上多次训练模型即可。

绘制学习曲线的代码:

from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
##mean_squared_error:从sklearn.metrics导入,用于计算均方误差(MSE)
##train_test_split:从sklearn.model_selection导入,用于将数据集分割为训练集和测试集

def plot_learning_curves(model, X, y):
    X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
##这行代码将数据集分割为80%的训练集和20%的验证集。test_size=0.2指定了验证集的大小。
    train_errors, val_errors = [], []
##这两个列表将用于存储训练集和验证集上的均方误差(MSE)
    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))
"""
对于每个m(从1到len(X_train)),它都会:
使用前m个样本训练模型。
在前m个训练样本上进行预测,并计算这些预测与真实标签之间的MSE,存储在train_errors中。
在整个验证集上进行预测,并计算这些预测与真实标签之间的MSE,存储在val_errors中。
""" 
    plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train")
    plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val")

结果 :

正则化线性模型

减少过拟合的一个好方法是对模型进行正则化(即约束模型):它拥有的自由度越少,则过拟合数据的难度就越大。正则化多项式模型的一种简单方法是减少多项式的次数。

对于线性模型,正则化通常是通过约束模型的权重来实现的。现在,我们看一下岭回归、Lasso回归和弹性网络,它们实现了三种限制权重的方法。

岭回归

将等于

的正则化项添加到成本函数。这迫使学习算法不仅拟合数据,而且还使模型权重尽可能小。

α=0,则岭回归仅是线性回归。如果α非常大,则所有权重最终都非常接近于零,结果是一条经过数据均值的平线

岭回归成本函数:

在执行岭回归之前缩放数据(例如使用StandardScaler)很重要,因为它对输入特征的缩放敏感。大多数正则化模型都需要如此。

使用不同的α值对某些线性数据进行训练的几种岭模型,α的增加会导致更平坦(即不极端,更合理)的预测,从而减少了模型的方差,但增加了其偏差。

与线性回归一样,我们可以通过计算闭合形式的方程或执行梯度下降来执行岭回归。利弊是相同的。公式4-9

显示了闭式解,其中A是(n+1)×(n+1)单位矩阵,除了在左上角的单元格中有0(对应于偏置项)。

  • 用Scikit-Learn和闭式解实现岭回归的代码:
 from sklearn.linear_model import Ridge
 ## Ridge类用于执行岭回归,这是一种用于回归分析的线性模型,它在损失函数中添加了一个L2正则化项(也称为“岭”项)。
 ridge_reg = Ridge(alpha=1, solver="cholesky")
 ridge_reg.fit(X, y)

这里创建了一个Ridge类的实例ridge_reg,并设置了两个参数:

  • alpha=1表示alpha是正则化强度的倒数(即正则化项的系数)。较大的alpha值表示更强的正则化。在这个例子中,alpha被设置为1,意味着正则化项的强度适中。
  • solver="cholesky":solver参数用于指定求解岭回归的算法。cholesky"是一种适用于大数据集的算法,特别是当数据矩阵X是对称且正定(或可以被认为是正定的)时。这种算法利用了X的Cholesky分解来加快计算速度。

使用给定的训练数据(特征矩阵X和目标向量y)来拟合岭回归模型。在内部,fit方法会计算权重系数(即模型的参数),这些系数将用于后续的预测。具体来说,它会找到最小化损失函数(包括原始损失和正则化项)的权重系数。

预测值

  • 使用随机梯度下降法
sgd_reg = SGDRegressor(penalty="l2")
sgd_reg.fit(X, y.ravel())
  • 创建了一个SGDRegressor类的实例sgd_regSGDRegressor是一个线性模型,它使用随机梯度下降法来最小化损失函数。在这个例子中,通过penalty="l2"参数指定了正则化项的类型为L2正则化。L2正则化会在损失函数中添加一个正则化项,该项是权重系数的平方和,旨在减少权重的大小,从而避免模型过拟合。
  • 使用训练数据(特征矩阵X和目标向量y)来拟合sgd_reg模型。注意,这里对y使用了.ravel()方法。.ravel()方法用于将y(可能是一个二维数组,比如从pandas DataFrame中获取的)转换为一维数组。这是因为SGDRegressorfit方法期望目标变量y是一维的,即使它只有一个特征。然而,如果y本身已经是一维的(比如NumPy数组或Python列表),则不需要使用.ravel()

预测结果:

Lasso回归

线性回归的另一种正则化叫作最小绝对收缩和选择算子回归(Least Absolute Shrinkage and Selection Operator Regression,简称Lasso回归)。与岭回归一样,它也是向成本函数添加一个正则项,但是它增加的是权重向量的L1范数,而不是L2范数的平方的一半

Lasso回归成本函数:


线性模型(左) 多项式模型(右) 使用不同级别的Lasso正则化

Lasso回归的一个重要特点是它倾向于完全消除掉最不重要特征的权重


轴代表两个模型参数,背景轮廓代表不同的损失函数。横坐标是θ1,纵坐标是θ2

  • 在左上图中,轮廓线代表L1损失(|θ1|+|θ2|),当你靠近任何轴时,该损失呈线性下降。例如,左图如果将模型参数初始化为θ1=2和θ1=0.5,运行梯度下降会使两个参数均等地递减(如黄色虚线所示)。因此θ2将首先达到0(因为开始时接近0)。之后,梯度下降将沿山谷滚动直到其达到θ1=0(有一点反弹,因为L1的梯度永远不会接近0:对于每个参数,它们都是-1或1)。
  • 在右上方的图中,轮廓线代表Lasso的成本函数(即MSE成本函数加L1损失)。白色的小圆圈显示了梯度下降优化某些模型参数的路径,这些参数在θ1=0.25和θ2=-1附近初始化:再次注意该路径如何快速到达θ2=0,然后向下滚动并最终在全局最优值附近反弹(由红色正方形表示)。如果增加α,则全局最优值将沿黄色虚线向左移动;如果减少α,则全局最优值将向右移动(在此示例中,非正则化的MSE的最优参数为θ1=2和θ2=0.5)。
  • 在左下图中,你可以看到L2损失随距原点的距离而减小,因此梯度下降沿该点直走。在右下图中,轮廓线代表岭回归的成本函数(即MSE成本函数加L2损失)。
  • Lasso有两个主要区别。首先,随着参数接近全局最优值,梯度会变小,因此,梯度下降自然会减慢,这有助于收敛(因为周围没有反弹)。其次,当你增加α时,最佳参数(用红色正方形表示)越来越接近原点,但是它们从未被完全被消除。

为了避免在使用Lasso时梯度下降最终在最优解附近反弹,你需要逐渐降低训练期间的学习率(它仍然会在最优解附近反弹,但是步长会越来越小,因此会收敛)。

Lasso代码:

 from sklearn.linear_model import Lasso
 lasso_reg = Lasso(alpha=0.1)
 lasso_reg.fit(X, y)

预测:

弹性网络

弹性网络是介于岭回归和Lasso回归之间的中间地带。正则项是岭和Lasso正则项的简单混合,你可以控制混合比r。当r=0时,弹性网络等效于岭回归,而当r=1时,弹性网络等效于Lasso回归

弹性网络成本函数:

代码:

from sklearn.linear_model import ElasticNet
elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic_net.fit(X, y)

预测:

提前停止

在验证误差达到最小值时停止训练,该方法叫作提前停止法


提前停止正则化

代码:

from sklearn.base import clone

# 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 = clone(sgd_reg)

逻辑回归

逻辑回归(Logistic回归,也称为Logit回归)被广泛用于估算一个实例属于某个特定类别的概率。(比如,这封电子邮件属于垃圾邮件的概率是多少?)如果预估概率超过50%,则模型预测该实例属于该类别(称为正类,标记为“1”),反之,则预测不是(称为负类,标记为“0”)。这样它就成了一个二元分类器。

4-13逻辑回归模型的估计概率、4-14逻辑函数:是一个sigmoid函数(即S型函数),输出一个介于0和1之间的数字:


逻辑回归模型预测:


注意,当t<0时,σ(t)<0.5;当t≥0时,σ(t)≥0.5。所以如果xTθ是正类,逻辑回归模型预测结果是1,如果是负类,则预测为0。

如何训练

训练的目的就是设置参数向量θ,使模型对正类实例做出高概率估算(y=1),对负类实例做出低概率估算(y=0)。

成本函数:

成本函数解释:当t接近于0时,-log(t)会变得非常大,所以如果模型估算一个正类实例的概率接近于0,成本将会变得很高。

决策边界

用iris鸢尾花设计一个回归模型

from sklearn import datasets
from sklearn.linear_model import LogisticRegression
iris = datasets.load_iris()
list(iris.keys())
##['data','target','frame','target_names','DESCR','feature_names','filename','data_module']
X = iris["data"][:, 3:] 
y = (iris["target"] == 2).astype(int) 
## 回归模型
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")


绿色表示是维吉尼亚鸢尾,蓝色表示非维吉尼亚鸢尾
预测结果:

与其他线性模型一样,逻辑回归模型可以用L1或L2惩罚函数来正则化。Scikit-Learn默认添加的是L2函数。

控制Scikit-Learn LogisticRegression模型的正则化强度的超参数不是alpha,而是反值C。C值越高,对模型的正则化越少。

Softmax回归

直接支持多个类别,给定一个实例x,Softmax回归模型首先计算出每个类k的分数sk(x),然后对这些分数应用softmax函数(也叫归一化指数),估算出每个类的概率。


每个类都有自己的特定参数向量θ(k)。所有这些向量通常都作为行存储在参数矩阵Θ中。

一旦为实例x计算了每个类的分数,就可以通过softmax函数来估计实例属于类k的概率


该函数计算每个分数的指数,然后对其进行归一化(除以所有指数的总和)。分数通常称为对数或对数奇数(尽管它们实际上是未归一化的对数奇数)。

·K是类数。·s(x)是一个向量,其中包含实例x的每个类的分数。·σ(s(x))k是实例x属于类k的估计概率,给定该实例每个类的分数。

softmax回归分析预测:

argmax运算符返回使函数最大化的变量值

Softmax回归分类器一次只能预测一个类(即它是多类,而不是多输出),因此它只能与互斥的类(例如不同类型的植物)一起使用。你无法使用它在一张照片中识别多个人。

训练模型

训练目标是得到一个能对目标类做出高概率估算的模型(也就是其他类的概率相应要很低)。通过将公式4-22的成本函数(也叫作交叉熵)最小化来实现这个目标,因为当模型对目标类做出较低概率的估算时会受到惩罚。交叉熵经常被用于衡量一组估算出的类概率跟目标类的匹配程度

交叉熵成本函数:

代码:

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)

softmax_reg = LogisticRegression(multi_class="multinomial", solver="lbfgs", C=10)

  • multi_class="multinomial":这个参数指定了多类分类的策略。"multinomial" 表示使用 softmax 函数来计算类别概率,这是处理多类分类问题的推荐方法。
  • solver="lbfgs":这个参数指定了用于优化问题的算法。"lbfgs" 是一个适用于小数据集的稳健优化器,特别是对于 L2 正则化逻辑回归问题。
  • C=10:这个参数是正则化强度的倒数。较小的值指定了更强的正则化。正则化用于防止过拟合,通过添加对模型复杂度的惩罚项来实现。

模型预测结果:

上面一行代码表示[[5,2]]属于哪一类

下面一行代码表示属于三个类别的概率分别多大

题目

  1. 如果训练集具有数百万个特征,那么可以使用哪种线性回归训练算法?

如果你的训练集具有数百万个特征,则可以使用随机梯度下降或小批量梯度下降。如果训练集适合容纳于内存,则可以使用批量梯度下降。不能使用标准方程法或SVD方法,因为随着特征数量的增加,计算复杂度会快速增长(超过二次方)。

  1. 如果训练集里特征的数值大小迥异,哪种算法可能会受到影响?受影响程度如何?你应该怎么做?

如果你的训练集中的特征具有不同的尺寸比例,则成本函数具有细长碗的形状,因此梯度下降算法需要很长时间才能收敛。为了解决这个问题,你应该在训练模型之前缩放数据。标准方程法或SVD方法无须缩放即可正常工作。此外,如果特征未按比例缩放,则正则化模型可能会收敛至次优解:由于正则化会惩罚较大的权重,因此与具有较大值的特征相比,具有较小值的特征往往会被忽略。

  1. 训练逻辑回归模型时,梯度下降会卡在局部最小值中吗?

训练逻辑回归模型时,梯度下降不会陷入局部最小值,因为成本函数是凸函数

  1. 如果你让它们运行足够长的时间,是否所有的梯度下降算法都能得出相同的模型?

如果优化问题是凸的(例如线性回归或逻辑回归),并且假设学习率不是太高,那么所有梯度下降算法都将接近全局最优并最终产生很相似的模型。但是,除非逐步降低学习率,否则随机梯度下降和小批量梯度下降将永远不会真正收敛。相反,它们会一直围绕全局最优值来回跳跃。这意味着即使你让它们运行很长时间,这些梯度下降算法也会产生略微不同的模型。

  1. 假设你使用批量梯度下降,并在每个轮次绘制验证误差。如果你发现验证错误持续上升,可能是什么情况?你该如何解决?

如果验证错误在每个轮次后持续上升,则一种可能性是学习率过高并且算法在发散。如果训练错误也增加了,那么你应该降低学习率。但是,如果训练错误没有增加,则你的模型已经过拟合训练集,则应该停止训练。

  1. 当验证错误上升时立即停止小批量梯度下降是个好主意吗?

由于随机性,随机梯度下降和小批量梯度下降都不能保证在每次训练迭代中都取得进展。因此,如果在验证错误上升时立即停止训练,则可能在达到最优值之前就停止太早了。更好的选择是按照一定的间隔时间保存模型。然后,当它很长时间没有改善(意味着它可能永远不会超过最优值)时,你可以恢复到保存的最佳模型。

  1. 哪种梯度下降算法(在我们讨论过的算法中)将最快到达最佳解附近?哪个实际上会收敛?如何使其他的也收敛?

随机梯度下降法具有最快的训练迭代速度,因为它一次只考虑一个训练实例,因此它通常是第一个到达全局最优值附近的(或是很小批量大小的小批量梯度下降)。但是,给定足够的训练时间,实际上只有批量梯度下降会收敛。如前所述,随机梯度下降和小批量梯度下降会在最优值附近反弹,除非你逐渐降低学习率。

  1. 假设你正在使用多项式回归。绘制学习曲线后,你会发现训练误差和验证误差之间存在很大的差距。发生了什么?解决此问题的三种方法是什么?

如果验证误差远高于训练误差,则可能是因为模型过拟合了训练集。解决此问题的一种方法是降低多项式阶数:较小自由度的模型不太可能过拟合。另一种方法是对模型进行正则化,例如,将L2(Ridge)或L1(Lasso)惩罚添加到成本函数。这也会降低模型的自由度。最后,你可以尝试增加训练集的大小。

  1. 假设你正在使用岭回归,并且你注意到训练误差和验证误差几乎相等且相当高。你是否会说模型存在高偏差或高方差?你应该增加正则化超参数α还是减小它呢?

如果训练误差和验证误差几乎相等且相当高,则该模型很可能欠拟合训练集,这意味着它具有很高的偏差。你应该尝试减少正则化超参数α。

  1. 为什么要使用:a.岭回归而不是简单的线性回归(即没有任何正则化)?b.Lasso而不是岭回归?c.弹性网络而不是Lasso?

让我们来看看:·具有某些正则化的模型通常比没有任何正则化的模型要好,因此,你通常应优先选择岭回归而不是简单的线性回归。

Lasso回归使用L1惩罚,这通常会将权重降低为零。这将导致稀疏模型,其中除了最重要的权重之外,所有权重均为零。这是一种自动进行特征选择的方法,如果你怀疑实际上只有很少的特征很重要,那么这是一种很好的方法。如果你不确定,则应首选岭回归。

与Lasso相比,弹性网络通常更受青睐,因为Lasso在某些情况下可能产生异常(当几个特征强相关或当特征比训练实例更多时)。但是,它确实增加了额外需要进行调整的超参数。如果你希望Lasso没有不稳定的行为,则可以仅使用l1_ratio接近1的弹性网络。

  1. 假设你要将图片分类为室外/室内和白天/夜间。你应该实现两个逻辑回归分类器还是一个Softmax回归分类器?

如果你要将图片分类为室外/室内和白天/夜间,因为它们不是排他的类(即所有四种组合都是可能的),则应训练两个逻辑回归分类器。
2024-07-29 14:59:57 星期一

posted @ 2024-07-29 14:48  江侠  阅读(216)  评论(0)    收藏  举报