想象一下,你正置身于一座云雾缭绕的大山之中,你的目标是找到这座山的最低点,也就是山谷的位置。这座山就像是我们在机器学习里要优化的损失函数,而山的最低点就是损失函数的最小值,也就是我们模型参数的最优解。接下来,我们就借助这个有趣的场景,深入了解三种常见的梯度下降算法。

一、批量梯度下降(Batch Gradient Descent,BGD):众人齐心探谷底

原理

批量梯度下降就像是一群人一起行动,大家站在山上的同一个位置,每个人都仔细观察周围的地形,然后一起商量出一个大家都认为是最陡峭的下山方向。这里的 “一群人” 就代表了所有的训练数据。每一次决定往哪个方向走,都需要综合考虑所有训练数据给出的信息。

具体来说,每次我们要下山(更新模型参数)的时候,都要把所有的训练数据拿出来,根据这些数据计算出一个整体的下山方向(损失函数关于参数的梯度)。然后,朝着这个方向迈出一步(更新参数)。这个过程会不断重复,直到我们觉得已经接近或者到达了山谷(损失函数收敛)。

通俗化的数学公式

假设我们站在山上的位置用参数 θ 来表示,山的高度就是损失函数 J(θ)。我们要找到一个下山的方向,这个方向就是损失函数在当前位置的梯度 ∇J(θ)。梯度就像是一个箭头,它指向山上升最快的方向,而我们要下山,所以要朝着它的反方向走。

每走一步的大小由学习率 α 决定,学习率就像是我们每一步跨出去的距离。那么更新参数的公式就可以写成:

θ=θ−α∇J(θ)

这就好比我们先确定了下山的方向(梯度的反方向),然后按照学习率规定的步长,朝着这个方向跨出一步,到达一个新的位置(新的参数值)。

代码示例

import numpy as np

# 定义损失函数,这里以简单的线性回归均方误差为例
def loss_function(X, y, theta):
    m = len(y)
    # 计算预测值
    predictions = X.dot(theta)
    # 计算均方误差
    loss = (1/(2*m)) * np.sum(np.square(predictions - y))
    return loss

# 批量梯度下降算法
def batch_gradient_descent(X, y, theta, alpha, num_iters):
    m = len(y)
    loss_history = []
    for iter in range(num_iters):
        # 计算预测值
        predictions = X.dot(theta)
        # 计算梯度
        gradient = (1/m) * X.T.dot(predictions - y)
        # 更新参数
        theta = theta - alpha * gradient
        # 计算当前损失
        loss = loss_function(X, y, theta)
        loss_history.append(loss)
    return theta, loss_history

# 示例数据
X = np.array([[1, 1], [1, 2], [1, 3], [1, 4]])
y = np.array([2, 4, 6, 8])
# 初始化参数
theta = np.zeros(X.shape[1])
# 学习率
alpha = 0.01
# 迭代次数
num_iters = 1000

# 运行批量梯度下降算法
theta, loss_history = batch_gradient_descent(X, y, theta, alpha, num_iters)
print("最终参数 theta:", theta)

二、随机梯度下降(Stochastic Gradient Descent,SGD):独行侠的冒险之旅

原理

随机梯度下降就像是一个勇敢的独行侠,他在山上随机选择一个方向就开始往下走。每走一步,他就重新观察周围的地形,然后再随机选择一个新的方向继续走。这里的 “独行侠” 就代表了每次只使用一个训练数据来计算下山的方向。

这种方法的好处是速度非常快,因为每次只需要考虑一个数据,不需要像批量梯度下降那样综合所有数据。但是,由于每次只根据一个数据来决定方向,所以这个方向可能不是全局最优的,可能会让我们在下山的过程中走很多弯路,甚至在山谷附近来回震荡。

通俗化的数学公式

同样,我们还是用参数 \(\theta\) 表示我们在山上的位置,损失函数表示根据第 i 个训练数据计算出来的山的高度。我们随机选择一个训练数据\left ( x^{i},y^{i} \right ),计算出这个数据对应的下山方向(梯度\bigtriangledown J(\Theta ;x^{i},y^{i})),然后朝着这个方向按照学习率\alpha规定的步长走一步,更新参数的公式为:

代码示例

import numpy as np

# 定义损失函数,这里以简单的线性回归均方误差为例
def loss_function(X, y, theta):
    m = len(y)
    # 计算预测值
    predictions = X.dot(theta)
    # 计算均方误差
    loss = (1/(2*m)) * np.sum(np.square(predictions - y))
    return loss

# 随机梯度下降算法
def stochastic_gradient_descent(X, y, theta, alpha, num_iters):
    m = len(y)
    loss_history = []
    for iter in range(num_iters):
        for i in range(m):
            # 随机选择一个样本
            random_index = np.random.randint(m)
            xi = X[random_index:random_index+1]
            yi = y[random_index:random_index+1]
            # 计算预测值
            predictions = xi.dot(theta)
            # 计算梯度
            gradient = xi.T.dot(predictions - yi)
            # 更新参数
            theta = theta - alpha * gradient
        # 计算当前损失
        loss = loss_function(X, y, theta)
        loss_history.append(loss)
    return theta, loss_history

# 示例数据
X = np.array([[1, 1], [1, 2], [1, 3], [1, 4]])
y = np.array([2, 4, 6, 8])
# 初始化参数
theta = np.zeros(X.shape[1])
# 学习率
alpha = 0.01
# 迭代次数
num_iters = 1000

# 运行随机梯度下降算法
theta, loss_history = stochastic_gradient_descent(X, y, theta, alpha, num_iters)
print("最终参数 theta:", theta)

三、小批量梯度下降(Mini - Batch Gradient Descent,MBGD):小队协作寻幽谷

原理

小批量梯度下降就像是一群人分成了几个小队,每个小队都根据自己观察到的地形,商量出一个下山的方向。这里的 “小队” 就代表了一小批训练数据。每次更新参数的时候,我们不是使用所有的数据,也不是只使用一个数据,而是随机选择一小批数据(比如 16、32 或者 64 个),根据这一小批数据计算出下山的方向,然后朝着这个方向更新参数。

这种方法结合了批量梯度下降和随机梯度下降的优点。它不像批量梯度下降那样需要考虑所有数据,计算量相对较小;也不像随机梯度下降那样只根据一个数据做决定,更新相对更稳定,不容易在山谷附近震荡。

通俗化的数学公式

我们还是用参数 \(\theta\) 表示位置,损失函数表示根据一小批(b 个)训练数据计算出来的山的高度。我们随机选择一小批训练数据 \left \{ x^{i},y^{i} \right \}_{i = 1}^{b},计算出这一小批数据对应的下山方向,然后按照学习率\alpha规定的步长更新参数:

import numpy as np

# 定义损失函数,这里以简单的线性回归均方误差为例
def loss_function(X, y, theta):
    m = len(y)
    # 计算预测值
    predictions = X.dot(theta)
    # 计算均方误差
    loss = (1/(2*m)) * np.sum(np.square(predictions - y))
    return loss

# 小批量梯度下降算法
def mini_batch_gradient_descent(X, y, theta, alpha, num_iters, batch_size):
    m = len(y)
    loss_history = []
    for iter in range(num_iters):
        # 随机打乱样本顺序
        permutation = np.random.permutation(m)
        X_shuffled = X[permutation]
        y_shuffled = y[permutation]
        for i in range(0, m, batch_size):
            # 选取一小批样本
            xi = X_shuffled[i:i+batch_size]
            yi = y_shuffled[i:i+batch_size]
            # 计算预测值
            predictions = xi.dot(theta)
            # 计算梯度
            gradient = (1/len(yi)) * xi.T.dot(predictions - yi)
            # 更新参数
            theta = theta - alpha * gradient
        # 计算当前损失
        loss = loss_function(X, y, theta)
        loss_history.append(loss)
    return theta, loss_history

# 示例数据
X = np.array([[1, 1], [1, 2], [1, 3], [1, 4]])
y = np.array([2, 4, 6, 8])
# 初始化参数
theta = np.zeros(X.shape[1])
# 学习率
alpha = 0.01
# 迭代次数
num_iters = 1000
# 批量大小
batch_size = 2

# 运行小批量梯度下降算法
theta, loss_history = mini_batch_gradient_descent(X, y, theta, alpha, num_iters, batch_size)
print("最终参数 theta:", theta)

 

总结

  • 批量梯度下降就像是一群人一起商量,方向比较准,但行动起来比较慢。
  • 随机梯度下降就像独行侠,行动迅速,但方向可能不太靠谱,容易走弯路。
  • 小批量梯度下降就像小队协作,既保证了一定的速度,又能让方向相对稳定。

通过这三种不同的 “下山” 方式,我们可以根据实际情况选择最合适的方法,帮助我们更快、更准确地找到损失函数的最小值,优化我们的模型参数。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐