在前几篇文章中,我们已经探讨了批量梯度下降的基本概念及其应用。然而,尽管批量梯度下降在理论上是完美的,但它在实际应用中可能会遇到计算效率的问题,特别是当数据集非常大时。为了解决这一问题,我们可以采用随机梯度下降和小批量梯度下降的方法,这些方法不仅加快了训练速度,还能够帮助模型跳出局部最优解。

随机梯度下降

        回顾一下批量梯度下降公式:

        θ_j := θ_j - α * Σ(hθ(x(i)) - y(i)) * x_j(i)  for every j

        虽然批量梯度下降是通过使用所有样本来估计梯度的,但在实际应用中,这样的计算可能会非常缓慢。因此,我们可以只使用一个训练样本来更新模型参数,这就是随机梯度下降的基本思想。

        在随机梯度下降中,我们可以有两种采样方式:有放回和无放回。

  • 无放回:意味着在同一轮次中,任何样本都不会重复使用。
  • 有放回:指在同一轮次中,某些样本可能会重复出现。

        其中,轮次(epoch)表示训练集被完整遍历一次。在无放回的情况下,我们从第一个样本开始,遍历到第m个样本,在每次遍历中,仅使用一个样本来更新权重:

        ∂J/∂θ_j = (hθ(x(i)) - y(i)) * x_j
        θ_j := θ_j - α * (hθ(x(i)) - y(i)) * x_j(i)

小批量梯度下降

        由于随机梯度下降在某些情况下可能不够稳定,因此引入了小批量梯度下降(Mini-Batch Gradient Descent)。该方法通过使用一部分样本来计算损失和梯度,从而在批量梯度下降和随机梯度下降之间取得平衡。小批量梯度下降在深度学习中被广泛使用。

        ∂J/∂θ_j = Σ(hθ(x(i)) - y(i)) * x_j for every j in the batch
        θ_j := θ_j - α * Σ(hθ(x(i)) - y(i)) * x_j(i)

        同样地,我们可以选择有放回或无放回的方式。在无放回的情况下,我们将训练集平均分成若干批次,并在一个轮次内用尽所有训练样本。

多项式回归

        线性回归的局限性在于当数据呈现非线性关系时,模型无法很好地拟合数据。在这种情况下,我们可以使用多项式回归,通过多项式变换对特征进行工程化处理。例如,一级多项式回归(直线)可以表示为:

y = ax + b

而三次多项式回归则拟合一个三次曲线:

y = ax^3 + bx^2 + cx + d

代码实现

        在实现过程中,我们将演示如何使用MLFlow进行实验跟踪,这是一个广受欢迎的工具,能够帮助我们记录和管理机器学习实验。以下是代码的关键步骤和注意事项。

        首先,在终端运行以下命令以启动MLFlow服务:

docker compose up

        这将会构建一个MLFlow服务,你可以通过http://localhost:5000进行访问。

        接下来,我们将使用Python代码来演示随机梯度下降和小批量梯度下降的实际应用。

import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from time import time
import mlflow
import os

# 加载糖尿病数据集
diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target

# 进行训练集和测试集的划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3)

# 标准化处理
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test  = scaler.transform(X_test)

# 添加截距项
intercept = np.ones((X_train.shape[0], 1))
X_train = np.concatenate((intercept, X_train), axis=1)
intercept = np.ones((X_test.shape[0], 1))
X_test = np.concatenate((intercept, X_test), axis=1)

# 实现线性回归的类
class LinearRegression:
    def __init__(self, lr=0.001, num_epochs=500, batch_size=50, method='batch'):
        self.lr = lr
        self.num_epochs = num_epochs
        self.batch_size = batch_size
        self.method = method

    def mse(self, ytrue, ypred):
        return ((ypred - ytrue) ** 2).sum() / ytrue.shape[0]
    
    def fit(self, X_train, y_train):
        self.theta = np.zeros(X_train.shape[1])
        for epoch in range(self.num_epochs):
            if self.method == 'sto':
                for i in range(X_train.shape[0]):
                    self._train(X_train[i].reshape(1, -1), y_train[i])
            elif self.method == 'mini':
                for i in range(0, X_train.shape[0], self.batch_size):
                    X_batch = X_train[i:i+self.batch_size, :]
                    y_batch = y_train[i:i+self.batch_size]
                    self._train(X_batch, y_batch)
            else:
                self._train(X_train, y_train)
    
    def _train(self, X, y):
        yhat = self.predict(X)
        grad = X.T @ (yhat - y) / X.shape[0]
        self.theta = self.theta - self.lr * grad
    
    def predict(self, X):
        return X @ self.theta
    
    def coef(self):
        return self.theta[1:]
    
    def intercept(self):
        return self.theta[0]

实验

        我们接下来将展示批量梯度下降和小批量梯度下降的实验过程。

# 批量梯度下降
model = LinearRegression(method='batch', lr=0.1)
model.fit(X_train, y_train)
yhat = model.predict(X_test)
mse = model.mse(y_test, yhat)
print("Test MSE: ", mse)

# 小批量梯度下降
model = LinearRegression(method='mini', lr=0.1)
model.fit(X_train, y_train)
yhat = model.predict(X_test)
mse = model.mse(y_test, yhat)
print("Test MSE: ", mse)

如果你觉得这篇博文对你有帮助,请点赞、收藏、关注我,并且可以打赏支持我!

欢迎关注我的后续博文,我将分享更多关于人工智能、自然语言处理和计算机视觉的精彩内容。

谢谢大家的支持!

08-17 09:26