神经网络系列---常用梯度下降算法


文章目录

    • 常用梯度下降算法
      • 随机梯度下降(Stochastic Gradient Descent,SGD):
        • 随机梯度下降数学公式:
        • 代码演示
      • 批量梯度下降(Batch Gradient Descent)
        • 批量梯度下降数学公式:
        • 代码演示
      • 小批量梯度下降(Mini-batch Gradient Descent):
        • 小批量梯度下降数学公式:
        • 代码演示
      • 动量优化(Momentum Optimization):
        • 动量优化数学公式:
        • 添加正则化 实例
        • 代码演示
      • Adagrad(Adaptive Gradient Algorithm):
        • Adagrad数学公式:
        • 添加正则化 实例
        • 代码演示
      • RMSprop(Root Mean Square Propagation):
        • RMSprop数学公式:
        • 代码演示
      • Adam(Adaptive Moment Estimation):
        • Adam数学公式:
        • 代码演示
      • NAG (Nesterov Accelerated Gradient)
        • Nesterov加速梯度法数学公式:
        • 代码演示
        • Nesterov第二种写法
        • 代码演示2
      • Nadam(Nesterov-accelerated Adaptive Moment Estimation)
        • Nadam算法数学公式:
        • 添加正则化
        • 代码演示
        • Nadam另一种算法:


常用梯度下降算法

当涉及不同的梯度下降算法时,每种算法都有其独特的特点和优化策略。下面对每种算法进行详细解释:

随机梯度下降(Stochastic Gradient Descent,SGD):

  • 算法原理:在每次迭代中,随机选择一个训练样本来计算损失函数的梯度,并更新模型的参数。由于随机选择样本,梯度估计存在一定的噪声,导致优化路径不稳定,但收敛速度较快。

  • 优点:收敛速度快,计算开销较小。

  • 缺点:优化路径不稳定,可能会震荡,难以找到全局最优解。

  • 算法原理:在每次迭代中,使用所有训练样本来计算损失函数的梯度,并更新模型的参数。由于使用了更多数据,梯度估计更准确,收敛路径较稳定。

  • 优点:收敛路径稳定,梯度估计准确。

  • 缺点:计算开销较大,内存要求高,不适用于大规模数据集。

以下是随机梯度下降的详细解释:

  1. 算法原理:

    • 对于一个样本的训练数据 (x, y),其中 x 是输入特征,y 是对应的实际标签。
    • SGD通过计算损失函数关于该样本的梯度来更新参数。梯度表示损失函数在每个参数处的变化率,它指示了在当前参数值下增加或减少参数值将如何影响损失函数的值。
    • SGD更新参数的规则为:新的参数 = 旧的参数 - 学习率 * 损失函数关于该样本的梯度。
    • 通过不断地使用不同的样本,迭代训练直至达到预定的训练轮数或损失函数收敛到一个满意的程度。
  2. 优点:

    • 计算开销小:由于每次迭代只使用一个样本,计算梯度的代价较小,特别适用于大规模数据集。
    • 更新频率高:参数的更新频率高,使得算法可能在较少的迭代次数内找到一个相对较好的解。
  3. 缺点:

    • 不稳定性:由于随机性,每次迭代的参数更新可能不同,导致优化路径不稳定,甚至可能出现震荡的情况。
    • 收敛性较慢:由于随机性导致的不稳定性,可能会导致收敛速度较慢,尤其是在损失函数存在大的震荡时。
  4. 改进方法:

    • 学习率调整:由于SGD的不稳定性,通常会使用学习率衰减技术来逐渐减小学习率,使得在训练初期更快收敛,后期细调参数。
    • Mini-batch:SGD的随机性使其不稳定,为了兼顾计算效率和稳定性,通常采用小批量梯度下降(Mini-batch Gradient Descent),在每次迭代中使用一小批样本来计算梯度。
    • Momentum:引入动量项,有助于加速优化过程,减少震荡。
随机梯度下降数学公式:

假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数 θ θ θ表示模型的权重和偏置),其中 θ θ θ是一个向量。我们希望找到使损失函数最小化的最优参数 θ ∗ θ* θ

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 随机梯度下降公式:
    在随机梯度下降中,我们使用一个样本 ( x , y ) (x, y) (x,y) 来计算损失函数关于该样本的梯度,并更新参数。梯度表示损失函数在参数 θ θ θ处的变化率。

    损失函数关于参数 θ θ θ的梯度(梯度向量): ∇ J ( θ ) = [ ∂ J ( θ ) / ∂ θ 1 , ∂ J ( θ ) / ∂ θ 2 , . . . , ∂ J ( θ ) / ∂ θ r ] ∇J(θ) = [∂J(θ)/∂θ₁, ∂J(θ)/∂θ₂, ..., ∂J(θ)/∂θᵣ] J(θ)=[J(θ)/θ1,J(θ)/θ2,...,J(θ)/θr]

    参数更新规则(学习率为α):
    θ ← θ − α ∗ ∇ J ( θ ) θ ← θ - α * ∇J(θ) θθαJ(θ)

代码演示
#include <iostream>
#include <vector>
#include <cmath>// 随机梯度下降函数
void stochasticGradientDescent(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, int epochs) {int num_samples = data.size();int num_features = data[0].size();for (int epoch = 0; epoch < epochs; epoch++) {for (int i = 0; i < num_samples; i++) {double y_pred = predict(data[i], weights);double loss = lossFunction(y_pred, labels[i]);// 更新每个权重for (int j = 0; j < num_features; j++) {double gradient = (y_pred - labels[i]) * data[i][j];weights[j] -= learning_rate * gradient;}}}
}

批量梯度下降(Batch Gradient Descent)

批量梯度下降(Batch Gradient Descent)是梯度下降算法的一种变体,在每次迭代中使用所有训练样本来计算损失函数关于参数的梯度,并更新模型的参数。与随机梯度下降(SGD)和小批量梯度下降(Mini-batch Gradient Descent)不同,它在每次迭代中使用全部训练样本,因此在计算梯度时具有更好的稳定性和准确性。

以下是批量梯度下降的详细解释:

  1. 算法原理:

    • 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
    • 在每次迭代中,使用所有训练样本计算损失函数关于参数θ的梯度。批量梯度下降会遍历所有样本,计算梯度的平均值。
    • 梯度表示损失函数在每个参数处的变化率。通过计算梯度,我们可以确定在当前参数值下,增加或减少参数值将如何影响损失函数的值。
    • 参数更新规则:新的参数 = 旧的参数 - 学习率 * (1 / 批大小) * ∑(损失函数关于所有样本的梯度)
  2. 优点:

    • 稳定性:由于使用所有样本的梯度,批量梯度下降的梯度估计更稳定,通常能够更准确地朝向损失函数的最小值方向移动。
    • 全局最优:相比随机梯度下降,批量梯度下降更有可能收敛到全局最优解,尤其是在凸优化问题中。
  3. 缺点:

    • 计算开销较大:由于每次迭代需要使用所有样本来计算梯度,批量梯度下降的计算开销较大。特别是在大规模数据集上,计算可能非常耗时。
  4. 改进方法:

    • 学习率调整:为了避免学习率过大或过小导致的优化问题,可以使用学习率衰减技术逐渐减小学习率,以便在训练初期更快收敛,后期更细调参数。
    • 随机梯度下降(SGD)和小批量梯度下降(Mini-batch Gradient Descent):批量梯度下降在大规模数据集上的计算开销较大,为了加速优化过程,可以采用随机梯度下降或小批量梯度下降等变体。
批量梯度下降数学公式:

假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数 θ ∗ θ* θ

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 批量梯度下降公式:
    在批量梯度下降中,我们使用所有训练样本来计算损失函数关于参数θ的梯度,并更新模型的参数。

    损失函数关于参数θ的梯度(梯度向量): ∇ J ( θ ) = [ ∂ J ( θ ) / ∂ θ 1 , ∂ J ( θ ) / ∂ θ 2 , . . . , ∂ J ( θ ) / ∂ θ r ] ∇J(θ) = [∂J(θ)/∂θ₁, ∂J(θ)/∂θ₂, ..., ∂J(θ)/∂θᵣ] J(θ)=[J(θ)/θ1,J(θ)/θ2,...,J(θ)/θr]

    参数更新规则(学习率为α):
    θ ← θ − α ∗ ( 1 / 批大小 ) ∗ ∑ ( ∇ J ( θ ) ) θ ← θ - α * (1 / 批大小) * ∑(∇J(θ)) θθα(1/批大小)(J(θ))

其中∇表示梯度运算符,α是学习率(learning rate),r是参数的数量,批大小是在每次迭代中使用的样本数量。

代码演示
#include <iostream>
#include <vector>
#include <cmath>// 批量梯度下降函数
void batchGradientDescent(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, int epochs) {int num_samples = data.size();int num_features = data[0].size();for (int epoch = 0; epoch < epochs; epoch++) {std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);// 初始化梯度std::vector<double> gradient(num_features, 0.0);// 计算梯度for (int i = 0; i < num_samples; i++) {for (int j = 0; j < num_features; j++) {gradient[j] += (y_pred[i] - labels[i]) * data[i][j];}}// 更新每个权重for (int j = 0; j < num_features; j++) {weights[j] -= learning_rate * (1.0 / num_samples) * gradient[j];}// 输出每次迭代的损失std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}

小批量梯度下降(Mini-batch Gradient Descent):

  • 算法原理:在每次迭代中,随机选择一小批训练样本来计算损失函数的梯度,并更新模型的参数。小批量梯度下降是批量梯度下降和随机梯度下降的折中方案。
  • 优点:收敛速度较快,梯度估计相对稳定,适用于大规模数据集。
  • 缺点:仍然可能会受到一定的优化路径波动影响。

小批量梯度下降(Mini-batch Gradient Descent)是梯度下降算法的一种改进版本,它是批量梯度下降和随机梯度下降的折中方案。在每次迭代中,小批量梯度下降使用一小批(通常为 2 的幂次 2的幂次 2的幂次)训练样本来计算损失函数关于参数的梯度,并更新模型的参数。相比于批量梯度下降,它在计算梯度时具有更好的效率,而相比于随机梯度下降,它的梯度估计更稳定,从而更容易收敛到较好的解。

以下是小批量梯度下降的详细解释:

  1. 算法原理:

    • 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
    • 在每次迭代中,选择一小批(通常为2的幂次)训练样本,计算损失函数关于该批样本的梯度。小批量梯度下降会遍历整个训练集,但每次迭代只使用一小批样本。
    • 梯度表示损失函数在每个参数处的变化率。通过计算梯度,我们可以确定在当前参数值下,增加或减少参数值将如何影响损失函数的值。
    • 参数更新规则:新的参数 = 旧的参数 - 学习率 * (1 / 批大小) * ∑(损失函数关于该批样本的梯度)
  2. 优点:

    • 计算效率高:相比于批量梯度下降,小批量梯度下降的计算开销较小,尤其适用于大规模数据集。
    • 梯度估计稳定:相比于随机梯度下降,小批量梯度下降使用一小批样本计算梯度,因此梯度估计更稳定,更容易收敛到较好的解。
  3. 缺点:

    • 超参数选择:小批量梯度下降中需要选择合适的批大小,这是一个超参数,不同的批大小可能会对优化过程产生影响。
  4. 改进方法:

    • 学习率调整:为了避免学习率过大或过小导致的优化问题,可以使用学习率衰减技术逐渐减小学习率,以便在训练初期更快收敛,后期更细调参数。
小批量梯度下降数学公式:

假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数θ表示模型的权重和偏置),其中 θ θ θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数 θ ∗ θ* θ

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 小批量梯度下降公式:
    在小批量梯度下降中,我们使用一小批(通常为2的幂次)训练样本来计算损失函数关于参数θ的梯度,并更新模型的参数。

    损失函数关于参数θ的梯度(梯度向量): ∇ J ( θ ) = [ ∂ J ( θ ) / ∂ θ 1 , ∂ J ( θ ) / ∂ θ 2 , . . . , ∂ J ( θ ) / ∂ θ r ] ∇J(θ) = [∂J(θ)/∂θ₁, ∂J(θ)/∂θ₂, ..., ∂J(θ)/∂θᵣ] J(θ)=[J(θ)/θ1,J(θ)/θ2,...,J(θ)/θr]

    参数更新规则(学习率为α,批大小为b):
    θ ← θ − α ∗ ( 1 / b ) ∗ ∑ ( ∇ J ( θ ) ) θ ← θ - α * (1 / b) * ∑(∇J(θ)) θθα(1/b)(J(θ))

其中∇表示梯度运算符,α是学习率(learning rate),r是参数的数量,b是小批量的大小。

代码演示
#include <iostream>
#include <vector>
#include <cmath>// 小批量梯度下降函数
void miniBatchGradientDescent(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, int batch_size, int epochs) {int num_samples = data.size();int num_features = data[0].size();for (int epoch = 0; epoch < epochs; epoch++) {for (int i = 0; i < num_samples; i += batch_size) {int end_idx = std::min(i + batch_size, num_samples);std::vector<double> batch_labels(labels.begin() + i, labels.begin() + end_idx);std::vector<std::vector<double>> batch_data(data.begin() + i, data.begin() + end_idx);std::vector<double> y_pred = predict(batch_data, weights);double loss = lossFunction(y_pred, batch_labels);// 初始化梯度std::vector<double> gradient(num_features, 0.0);// 计算梯度for (int j = 0; j < batch_size; j++) {for (int k = 0; k < num_features; k++) {gradient[k] += (y_pred[j] - batch_labels[j]) * batch_data[j][k];}}// 更新每个权重for (int j = 0; j < num_features; j++) {weights[j] -= learning_rate * (1.0 / batch_size) * gradient[j];}}// 输出每次迭代的损失std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}

动量优化(Momentum Optimization):

  • 算法原理:引入动量项来加速梯度下降过程。动量项利用参数更新的历史梯度信息来决定下一步的方向,从而增加了在参数空间中的“动量”,有助于快速穿越平坦区域和避免震荡。
  • 优点:加速收敛过程,减少震荡,有助于逃离局部最优解。
  • 缺点:可能会在某些情况下引入一定的摩擦,导致收敛变慢。

动量优化(Momentum Optimization)是一种梯度下降算法的改进版本,它通过模拟物体在惯性作用下的运动来加速收敛,并且有助于在梯度更新时减少震荡。动量优化可以在训练过程中更快地达到收敛,并且在复杂的非凸优化问题中通常表现较好。

动量优化算法的核心思想是在更新参数时,利用之前的梯度信息来为当前的梯度方向提供一个“动量”。这样做可以在梯度在一个方向上连续增大或减小时,使得参数更新更加平滑,从而加快收敛速度。

以下是动量优化算法的详细解释:

  1. 算法原理:

    • 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
    • 在每次迭代中,利用当前梯度和之前的动量来计算参数的更新量。
    • 动量的引入类似于模拟物体在运动过程中的惯性。在更新参数时,当前梯度方向上的更新将受到之前动量方向的影响。
    • 参数更新规则:新的参数 = 旧的参数 - 学习率 * 动量 * (上一次动量方向 + 当前梯度方向)
  2. 动量的计算:
    动量的计算类似于梯度的累积。我们引入一个动量系数β(通常取值为0.9或0.99),并维护一个动量向量v,初始化为0。在每次迭代中,根据当前梯度计算动量,并更新动量向量v。然后,用动量向量v来更新参数。

  3. 优点:

    • 收敛速度快:动量优化可以在训练过程中更快地达到收敛,尤其在高维、复杂的非凸优化问题中表现较好。
    • 减少震荡:动量的引入可以减少参数更新时的震荡,使得参数更新更加平滑。
  4. 超参数选择:

    • 学习率:动量优化算法依然需要选择适当的学习率,较大的学习率可能导致振荡,较小的学习率可能导致收敛缓慢。
    • 动量系数β:通常情况下,β取 0.9 0.9 0.9 0.99 0.99 0.99是一个合理的选择,较大的β可以增加动量的影响。
动量优化数学公式:

在这里插入图片描述

在这里插入图片描述

假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 动量向量的初始化:
    初始化动量向量 v = 0 v = 0 v=0,v的维度与θ相同。

  3. 动量的计算:
    在每次迭代中,利用当前梯度和之前的动量来计算动量。

    动量系数:β(通常取值为0.9或0.99)

    更新动量向量: v = β ∗ v + ( 1 − β ) ∗ ∇ J ( θ ) v = β * v + (1 - β) * ∇J(θ) v=βv+(1β)J(θ)

    其中, ∇ J ( θ ) ∇J(θ) J(θ)是损失函数关于参数θ的梯度(梯度向量)。

  4. 参数更新规则:
    使用动量向量v来更新参数θ。

    学习率: α α α

    参数更新: θ ← θ − α ∗ v θ ← θ - α * v θθαv

在每次迭代中,我们计算梯度 ∇ J ( θ ) ∇J(θ) J(θ)并更新动量向量 v v v,然后使用动量向量 v v v来更新参数 θ θ θ。动量向量v模拟了之前梯度的“动量”,在梯度的方向上引入了惯性,使得参数更新更加平滑,从而加快收敛速度。

添加正则化 实例

当然!下面是在使用动量和L2正则化的参数更新中的具体数学公式。

首先,我们有L2正则化项的梯度:
∇ R ( θ ) = 2 λ θ \nabla R(\theta) = 2 \lambda \theta R(θ)=2λθ

其中, λ \lambda λ是正则化强度。

然后,我们计算总梯度,该总梯度包括损失函数的梯度 ∇ J ( θ ) \nabla J(\theta) J(θ)和正则化项的梯度:
totalGradient = ∇ J ( θ ) + ∇ R ( θ ) \text{totalGradient} = \nabla J(\theta) + \nabla R(\theta) totalGradient=J(θ)+R(θ)

接下来,我们根据以下公式计算动量向量 v v v
v = β v + ( 1 − β ) totalGradient v = \beta v + (1 - \beta) \text{totalGradient} v=βv+(1β)totalGradient

其中, β \beta β 是动量系数。

最后,我们使用学习率 α \alpha α 和动量向量 v v v来更新参数 θ \theta θ
θ ← θ − α v \theta \leftarrow \theta - \alpha v θθαv

这样,我们就将动量和L2正则化结合在了同一优化步骤中。这两个技术通常用于更快地训练神经网络,以及减少过拟合。

代码演示
#include <iostream>
#include <vector>
#include <cmath>// 动量优化函数
void momentumOptimization(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, double beta, int epochs) {int num_samples = data.size();int num_features = data[0].size();std::vector<double> velocity(num_features, 0.0);for (int epoch = 0; epoch < epochs; epoch++) {std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);// 初始化梯度std::vector<double> gradient(num_features, 0.0);// 计算梯度for (int i = 0; i < num_samples; i++) {for (int j = 0; j < num_features; j++) {gradient[j] += (y_pred[i] - labels[i]) * data[i][j];}}// 更新动量for (int j = 0; j < num_features; j++) {velocity[j] = beta * velocity[j] + (1 - beta) * gradient[j];}// 更新每个权重for (int j = 0; j < num_features; j++) {weights[j] -= learning_rate * velocity[j];}// 输出每次迭代的损失std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}

Adagrad(Adaptive Gradient Algorithm):

  • 算法原理:Adagrad是一种自适应学习率的优化算法。它为每个参数使用不同的学习率,根据每个参数的历史梯度信息来自适应地调整学习率。较大梯度参数的学习率逐渐减小,较小梯度参数的学习率逐渐增大。
  • 优点:自适应学习率,适应不同参数的更新需求。
  • 缺点:学习率可能过早减小,导致收敛速度过慢。

Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的梯度下降算法,它可以针对每个参数自适应地调整学习率,从而更有效地优化模型。Adagrad的主要思想是在训练过程中为每个参数维护一个学习率,根据参数的历史梯度信息来自适应地调整学习率大小。

以下是Adagrad算法的详细解释:

  1. 算法原理:

    • 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
    • Adagrad算法为每个参数θ维护一个累积梯度平方和(sum of squared gradients),并使用该平方和来调整参数的学习率。
    • 参数更新规则:对于第t次迭代,学习率为α,θ在第t+1次迭代的更新公式为:
      θ(t+1) ← θ(t) - (α / sqrt(G(t) + ε)) * ∇J(θ(t))

    其中∇J(θ(t))是损失函数关于参数θ在第t次迭代的梯度,G(t)是累积梯度平方和,ε是一个小的常数(通常取较小的值,例如1e-8)以防止除以0。

  2. 累积梯度平方和的计算:
    在每次迭代中,对参数的梯度进行平方,然后将平方梯度值加到之前的累积梯度平方和中。

  3. 优点:

    • 自适应学习率:Adagrad根据参数的历史梯度信息自适应地调整学习率,较大梯度对应的参数学习率较小,较小梯度对应的参数学习率较大,使得参数的学习过程更加平稳。
    • 对于稀疏特征优化较好:Adagrad在处理稀疏特征时相对于其他算法更具优势。
  4. 缺点:

    • 累积梯度平方和:由于Adagrad累积梯度平方和,对于长时间训练的模型,梯度平方和会变得较大,导致学习率过度减小,可能导致训练过早停止。
  5. 改进方法:

    • 学习率调整:由于累积梯度平方和会变大,可以使用学习率衰减技术逐渐减小学习率,以便在训练过程中更细调参数。
Adagrad数学公式:

在这里插入图片描述

假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数θ表示模型的权重和偏置),其中 θ θ θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数 θ ∗ θ* θ

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 学习率: α α α

  3. Adagrad的参数更新规则:
    Adagrad算法为每个参数θ维护一个累积梯度平方和(sum of squared gradients),并使用该平方和来调整参数的学习率。

    在第t次迭代时,我们计算参数θ的梯度∇J(θ(t)),并将其平方进行累加:
    G ( t ) = G ( t − 1 ) + ( ∇ J ( θ ( t ) ) ) 2 G(t) = G(t-1) + (∇J(θ(t)))^2 G(t)=G(t1)+(J(θ(t)))2

    然后,使用累积梯度平方和G(t)来更新参数θ:
    θ ( t + 1 ) = θ ( t ) − ( α / s q r t ( G ( t ) + ε ) ) ∗ ∇ J ( θ ( t ) ) θ(t+1) = θ(t) - (α / sqrt(G(t) + ε)) * ∇J(θ(t)) θ(t+1)=θ(t)(α/sqrt(G(t)+ε))J(θ(t))

    其中:

    • ∇J(θ(t))是损失函数关于参数θ在第t次迭代的梯度(梯度向量)。
    • G(t)是累积梯度平方和,它维护了参数θ每个维度梯度的累积平方和。
    • ε是一个小的常数,通常取较小的值(例如1e-8)以防止除以0。
添加正则化 实例

正则化通常通过在损失函数中添加一个与模型参数的大小有关的项来实现。对于L1或L2正则化,这个额外的项会考虑参数的绝对值或平方值。对于你提供的优化器公式,添加正则化需要考虑正则化项对梯度的影响。

以L2正则化为例,L2正则化项与参数的平方成正比,因此其对梯度的贡献是线性的。假设正则化强度为 λ \lambda λ,我们可以将L2正则化项的梯度表示为 2 λ θ ( t ) 2\lambda\theta(t) 2λθ(t)

因此,损失函数的梯度将变为:

KaTeX parse error: {align*} can be used only in display mode.

然后,我们可以将这个新的梯度插入到原始的优化器更新公式中:

KaTeX parse error: {align*} can be used only in display mode.

其中

  • θ ( t ) \theta(t) θ(t)是第(t)次迭代的参数。
  • α \alpha α是学习率。
  • G ( t ) G(t) G(t)是累积梯度平方和。
  • ϵ \epsilon ϵ是一个小的常数,通常取较小的值以防止除以0。
  • ∇ J ( θ ( t ) ) \nabla J(\theta(t)) J(θ(t))是原始损失函数关于参数 θ \theta θ在第 t t t次迭代的梯度。
  • λ \lambda λ是正则化强度对不起,我的之前的解释确实引入了混淆。为了澄清,让我们回顾一下具体的步骤。
代码演示
#include <iostream>
#include <vector>
#include <cmath>// Adagrad优化函数
void adagradOptimization(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, double epsilon, int epochs) {int num_samples = data.size();int num_features = data[0].size();std::vector<double> sum_squared_gradients(num_features, 0.0);for (int epoch = 0; epoch < epochs; epoch++) {std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);// 初始化梯度std::vector<double> gradient(num_features, 0.0);// 计算梯度for (int i = 0; i < num_samples; i++) {for (int j = 0; j < num_features; j++) {gradient[j] += (y_pred[i] - labels[i]) * data[i][j];}}// 更新累积梯度平方和for (int j = 0; j < num_features; j++) {sum_squared_gradients[j] += gradient[j] * gradient[j];}// 更新每个权重for (int j = 0; j < num_features; j++) {weights[j] -= (learning_rate / (std::sqrt(sum_squared_gradients[j]) + epsilon)) * gradient[j];}// 输出每次迭代的损失std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}

RMSprop(Root Mean Square Propagation):

  • 算法原理:RMSprop也是一种自适应学习率算法,类似于Adagrad,但采用了对梯度进行指数加权平均,以便更快地遗忘旧的梯度信息。这样,它在一定程度上解决了Adagrad的学习率过早减小的问题。
  • 优点:自适应学习率,对旧梯度有较好遗忘性。
  • 缺点:学习率衰减依然存在,可能在训练后期导致学习率过小。

RMSprop(Root Mean Square Propagation)是一种自适应学习率的梯度下降算法,它是Adagrad算法的一种改进版本。RMSprop通过引入一个衰减系数(decay rate)来解决Adagrad算法中累积梯度平方和过大导致学习率过度减小的问题。RMSprop在处理长时间训练的模型时表现更好,并且在深度学习中广泛应用。

以下是RMSprop算法的详细解释:

  1. 算法原理:

    • 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
    • RMSprop算法为每个参数θ维护一个累积梯度平方和(sum of squared gradients),并使用该平方和来调整参数的学习率。
    • 参数更新规则:对于第t次迭代,学习率为α,衰减系数为β,θ在第t+1次迭代的更新公式为:
      G ( t ) = β ∗ G ( t − 1 ) + ( 1 − β ) ∗ ( ∇ J ( θ ( t ) ) ) 2 G(t) = β * G(t-1) + (1 - β) * (∇J(θ(t)))^2 G(t)=βG(t1)+(1β)(J(θ(t)))2
      θ ( t + 1 ) = θ ( t ) − ( α / s q r t ( G ( t ) + ε ) ) ∗ ∇ J ( θ ( t ) ) θ(t+1) = θ(t) - (α / sqrt(G(t) + ε)) * ∇J(θ(t)) θ(t+1)=θ(t)(α/sqrt(G(t)+ε))J(θ(t))

    其中:

    • ∇J(θ(t))是损失函数关于参数θ在第t次迭代的梯度(梯度向量)。
    • G(t)是累积梯度平方和,它维护了参数θ每个维度梯度的累积平方和。
    • β是衰减系数,通常取值为0.9,用于控制历史梯度平方和的衰减程度。
    • ε是一个小的常数,通常取较小的值(例如1e-8)以防止除以0。
  2. 优点:

    • 自适应学习率:RMSprop根据参数的历史梯度信息自适应地调整学习率,较大梯度对应的参数学习率较小,较小梯度对应的参数学习率较大,使得参数的学习过程更加平稳。
    • 衰减系数:RMSprop引入了衰减系数β,可以解决Adagrad算法中累积梯度平方和过大导致学习率过度减小的问题。
  3. 改进方法:

    • 学习率调整:可以使用学习率衰减技术逐渐减小学习率,以便在训练过程中更细调参数。
RMSprop数学公式:

假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数θ表示模型的权重和偏置),其中 θ θ θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数 θ ∗ θ* θ

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 学习率: α α α

  3. 衰减系数: β β β

  4. RMSprop的参数更新规则:
    RMSprop算法为每个参数θ维护一个累积梯度平方和(sum of squared gradients),并使用该平方和来调整参数的学习率。

    在第t次迭代时,我们计算参数θ的梯度∇J(θ(t)),并将其平方进行累加,然后利用衰减系数β对累积梯度平方和进行衰减:
    G ( t ) = β ∗ G ( t − 1 ) + ( 1 − β ) ∗ ( ∇ J ( θ ( t ) ) ) 2 G(t) = β * G(t-1) + (1 - β) * (∇J(θ(t)))^2 G(t)=βG(t1)+(1β)(J(θ(t)))2

    然后,使用累积梯度平方和G(t)来更新参数θ:
    θ ( t + 1 ) = θ ( t ) − ( α / s q r t ( G ( t ) + ε ) ) ∗ ∇ J ( θ ( t ) ) θ(t+1) = θ(t) - (α / sqrt(G(t) + ε)) * ∇J(θ(t)) θ(t+1)=θ(t)(α/sqrt(G(t)+ε))J(θ(t))

    其中:

    • ∇J(θ(t))是损失函数关于参数θ在第t次迭代的梯度(梯度向量)。
    • G(t)是累积梯度平方和,它维护了参数θ每个维度梯度的累积平方和。
    • β是衰减系数,通常取值为0.9,用于控制历史梯度平方和的衰减程度。
    • ε是一个小的常数,通常取较小的值(例如1e-8)以防止除以0。
代码演示
#include <iostream>
#include <vector>
#include <cmath>// RMSprop优化函数
void rmspropOptimization(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, double beta, double epsilon, int epochs) {int num_samples = data.size();int num_features = data[0].size();std::vector<double> squared_gradients(num_features, 0.0);for (int epoch = 0; epoch < epochs; epoch++) {std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);// 初始化梯度std::vector<double> gradient(num_features, 0.0);// 计算梯度for (int i = 0; i < num_samples; i++) {for (int j = 0; j < num_features; j++) {gradient[j] += (y_pred[i] - labels[i]) * data[i][j];}}// 更新累积梯度平方和for (int j = 0; j < num_features; j++) {squared_gradients[j] = beta * squared_gradients[j] + (1 - beta) * gradient[j] * gradient[j];}// 更新每个权重for (int j = 0; j < num_features; j++) {weights[j] -= (learning_rate / (std::sqrt(squared_gradients[j]) + epsilon)) * gradient[j];}// 输出每次迭代的损失std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}

Adam(Adaptive Moment Estimation):

  • 算法原理:Adam是结合了动量优化和RMSprop的优势的自适应学习率算法。它利用梯度的一阶矩估计(平均梯度)和二阶矩估计(梯度的方差)来调整参数的更新步长,并结合动量项,使得参数更新更加平稳且具有自适应性。
  • 优点:自适应学习率,收敛速度快,对于不同参数具有较好的适应性。
  • 缺点:有一些超参数需要调整,计算开销较大。

以下是Adam算法的详细解释:

  1. 算法原理:

    • 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。

    • Adam算法为每个参数θ维护两个一阶矩估计(first moment estimate)m和二阶矩估计(second moment estimate)v,并使用它们来调整参数的学习率。

    • 参数更新规则:对于第t次迭代,学习率为α,衰减系数为β1和β2,ε是一个小的常数(通常取较小的值,例如1e-8)以防止除以0,θ在第t+1次迭代的更新公式为:

      m ( t ) = β 1 ∗ m ( t − 1 ) + ( 1 − β 1 ) ∗ ∇ J ( θ ( t ) ) m(t) = β1 * m(t-1) + (1 - β1) * ∇J(θ(t)) m(t)=β1m(t1)+(1β1)J(θ(t)) (一阶矩估计)
      v ( t ) = β 2 ∗ v ( t − 1 ) + ( 1 − β 2 ) ∗ ( ∇ J ( θ ( t ) ) ) 2 v(t) = β2 * v(t-1) + (1 - β2) * (∇J(θ(t)))^2 v(t)=β2v(t1)+(1β2)(J(θ(t)))2 (二阶矩估计)

      m h a t ( t ) = m ( t ) / ( 1 − β 1 t ) m_hat(t) = m(t) / (1 - β1^t) mhat(t)=m(t)/(1β1t) (修正一阶矩估计)
      v h a t ( t ) = v ( t ) / ( 1 − β 2 t ) v_hat(t) = v(t) / (1 - β2^t) vhat(t)=v(t)/(1β2t) (修正二阶矩估计)

      θ ( t + 1 ) = θ ( t ) − ( α / ( s q r t ( v h a t ( t ) ) + ε ) ) ∗ m h a t ( t ) θ(t+1) = θ(t) - (α / (sqrt(v_hat(t)) + ε)) * m_hat(t) θ(t+1)=θ(t)(α/(sqrt(vhat(t))+ε))mhat(t)

    其中:

    • ∇J(θ(t))是损失函数关于参数θ在第t次迭代的梯度(梯度向量)。
    • m(t)和v(t)分别表示一阶矩估计和二阶矩估计,它们初始化为0向量。
    • β1和β2是衰减系数,通常取值为0.9和0.999,分别用于控制一阶矩和二阶矩的衰减程度。
    • m_hat(t)和v_hat(t)是修正后的一阶矩估计和二阶矩估计,用于解决算法初期估计偏向0的问题。
  2. 优点:

    • 自适应学习率:Adam根据参数的一阶矩和二阶矩估计自适应地调整学习率,使得参数的学习过程更加平稳。
    • 鲁棒性:Adam对于稀疏梯度的处理较为优秀,使得算法在处理非平稳目标函数和大规模数据集时表现更好。
  3. 改进方法:

    • 学习率调整:可以使用学习率衰减技术逐渐减小学习率,以便在训练过程中更细调参数。
Adam数学公式:

假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数θ表示模型的权重和偏置),其中 θ θ θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数 θ ∗ θ* θ

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 学习率: α α α

  3. 衰减系数: β 1 和 β 2 β1和β2 β1β2

  4. 一阶矩估计(m)和二阶矩估计(v):
    Adam算法为每个参数θ维护两个一阶矩估计和二阶矩估计,并使用它们来调整参数的学习率。

    在第t次迭代时,我们计算参数θ的梯度∇J(θ(t)),然后分别计算一阶矩估计m和二阶矩估计v:
    m ( t ) = β 1 ∗ m ( t − 1 ) + ( 1 − β 1 ) ∗ ∇ J ( θ ( t ) ) m(t) = β1 * m(t-1) + (1 - β1) * ∇J(θ(t)) m(t)=β1m(t1)+(1β1)J(θ(t))
    v ( t ) = β 2 ∗ v ( t − 1 ) + ( 1 − β 2 ) ∗ ( ∇ J ( θ ( t ) ) ) 2 v(t) = β2 * v(t-1) + (1 - β2) * (∇J(θ(t)))^2 v(t)=β2v(t1)+(1β2)(J(θ(t)))2

    其中:

    • ∇J(θ(t))是损失函数关于参数θ在第t次迭代的梯度(梯度向量)。
    • m(t)是一阶矩估计,它表示过去梯度的一阶矩(均值)。
    • v(t)是二阶矩估计,它表示过去梯度平方的二阶矩(平均平方)。
    • β1和β2是衰减系数,通常取值为0.9和0.999,分别用于控制一阶矩和二阶矩的衰减程度。
  5. 修正后的一阶矩估计(m_hat)和二阶矩估计(v_hat):
    由于在算法初期估计偏向0,需要对一阶矩估计m和二阶矩估计v进行修正。

    m h a t ( t ) = m ( t ) / ( 1 − β 1 t ) m_hat(t) = m(t) / (1 - β1^t) mhat(t)=m(t)/(1β1t)
    v h a t ( t ) = v ( t ) / ( 1 − β 2 t ) v_hat(t) = v(t) / (1 - β2^t) vhat(t)=v(t)/(1β2t)

  6. 参数更新规则:
    使用修正后的一阶矩估计m_hat和二阶矩估计v_hat来更新参数θ。

    θ ( t + 1 ) = θ ( t ) − ( α / ( s q r t ( v h a t ( t ) ) + ε ) ) ∗ m h a t ( t ) θ(t+1) = θ(t) - (α / (sqrt(v_hat(t)) + ε)) * m_hat(t) θ(t+1)=θ(t)(α/(sqrt(vhat(t))+ε))mhat(t)

    其中:

    • α是学习率,用于控制参数更新的步长。
    • ε是一个小的常数,通常取较小的值(例如1e-8)以防止除以0。
代码演示
#include <iostream>
#include <vector>
#include <cmath>// Adam优化函数
void adamOptimization(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, double beta1, double beta2, double epsilon, int epochs) {int num_samples = data.size();int num_features = data[0].size();std::vector<double> m(num_features, 0.0); // 一阶矩估计std::vector<double> v(num_features, 0.0); // 二阶矩估计int t = 0; // 时间步数for (int epoch = 0; epoch < epochs; epoch++) {std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);// 初始化梯度std::vector<double> gradient(num_features, 0.0);// 计算梯度for (int i = 0; i < num_samples; i++) {for (int j = 0; j < num_features; j++) {gradient[j] += (y_pred[i] - labels[i]) * data[i][j];}}// 更新时间步数t++;// 更新一阶矩估计和二阶矩估计for (int j = 0; j < num_features; j++) {m[j] = beta1 * m[j] + (1 - beta1) * gradient[j];v[j] = beta2 * v[j] + (1 - beta2) * gradient[j] * gradient[j];}// 进行修正后的一阶矩估计和二阶矩估计std::vector<double> m_hat(num_features, 0.0);std::vector<double> v_hat(num_features, 0.0);for (int j = 0; j < num_features; j++) {m_hat[j] = m[j] / (1 - std::pow(beta1, t));v_hat[j] = v[j] / (1 - std::pow(beta2, t));}// 更新每个权重for (int j = 0; j < num_features; j++) {weights[j] -= (learning_rate / (std::sqrt(v_hat[j]) + epsilon)) * m_hat[j];}// 输出每次迭代的损失std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}

NAG (Nesterov Accelerated Gradient)

(Nesterov加速梯度法),简称NAG,是一种优化算法,用于训练神经网络和其他机器学习模型。Nesterov算法是梯度下降算法的一种变种,通过引入动量(momentum)的概念来加速收敛,并在损失函数梯度的计算中采用"look-ahead"策略来调整参数的更新方向,从而在训练过程中减少震荡和摆动。

Nesterov算法的特点在于在计算梯度时先对参数进行一次"look-ahead"更新,然后在该位置上计算梯度。这使得梯度计算的方向更加准确,能够更好地适应参数的变化情况。

以下是Nesterov算法的详细解释:

  1. 算法原理:

    • 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。

    • Nesterov算法引入了动量(momentum)的概念。动量是过去梯度的加权平均,它在梯度的计算中增加了一个惯性项,从而加速收敛。

    • 参数更新规则:对于第t次迭代,学习率为α,动量系数为γ,参数θ的一阶梯度为∇J(θ)。

      v ( t ) = γ ∗ v ( t − 1 ) + α ∗ ∇ J ( θ − γ ∗ v ( t − 1 ) ) v(t) = γ * v(t-1) + α * ∇J(θ - γ * v(t-1)) v(t)=γv(t1)+αJ(θγv(t1)) (计算动量)
      θ ( t + 1 ) = θ ( t ) − v ( t ) θ(t+1) = θ(t) - v(t) θ(t+1)=θ(t)v(t) (更新参数)

    其中:

    • v(t)是动量的累积向量,它代表过去梯度的加权平均。
    • γ是动量系数,通常取值范围为0到1之间,用于控制动量的权重。较大的γ值会增加惯性,使得更新更加平滑,但可能导致震荡;较小的γ值会减小惯性,使得更新更加准确,但可能导致更新速度较慢。
  2. 优点:

    • 收敛加速:动量的引入使得参数更新更加平滑,从而加速收敛过程。
    • 减少震荡:Nesterov算法通过"look-ahead"策略调整参数的更新方向,减少了梯度下降过程中的震荡和摆动。
  3. 改进方法:

    • 学习率调整:可以使用学习率衰减技术逐渐减小学习率,以便在训练过程中更细调参数。
Nesterov加速梯度法数学公式:

假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数θ表示模型的权重和偏置),其中 θ θ θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数 θ ∗ θ* θ

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 学习率: α α α

  3. 动量系数: γ γ γ

  4. 动量(momentum)的累积向量(v):
    Nesterov算法引入了动量的概念,动量是过去梯度的加权平均。

    在第t次迭代时,我们首先进行动量的累积,然后计算梯度:
    v ( t ) = γ ∗ v ( t − 1 ) + α ∗ ∇ J ( θ − γ ∗ v ( t − 1 ) ) v(t) = γ * v(t-1) + α * ∇J(θ - γ * v(t-1)) v(t)=γv(t1)+αJ(θγv(t1))

    其中:

    • v(t)是动量的累积向量,它代表过去梯度的加权平均。
    • γ是动量系数,通常取值范围为0到1之间,用于控制动量的权重。较大的γ值会增加惯性,使得更新更加平滑,但可能导致震荡;较小的γ值会减小惯性,使得更新更加准确,但可能导致更新速度较慢。
  5. 参数更新规则:
    使用动量的累积向量v来更新参数θ。

    θ ( t + 1 ) = θ ( t ) − v ( t ) θ(t+1) = θ(t) - v(t) θ(t+1)=θ(t)v(t)

代码演示
#include <iostream>
#include <vector>
#include <cmath>// 第一种写法-Nesterov算法优化函数
void nesterovOptimization(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, double momentum, int epochs) {int num_samples = data.size();int num_features = data[0].size();std::vector<double> v(num_features, 0.0); // 动量的累积向量for (int epoch = 0; epoch < epochs; epoch++) {// 提前计算"look-ahead"位置的参数更新std::vector<double> theta_ahead(num_features, 0.0);for (int j = 0; j < num_features; j++) {theta_ahead[j] = weights[j] - momentum * v[j];}// 计算"look-ahead"位置的梯度std::vector<double> gradient_ahead(num_features, 0.0);std::vector<double> y_pred_ahead = predict(data, theta_ahead);for (int i = 0; i < num_samples; i++) {for (int j = 0; j < num_features; j++) {gradient_ahead[j] += (y_pred_ahead[i] - labels[i]) * data[i][j];}}// 更新动量的累积向量for (int j = 0; j < num_features; j++) {v[j] = momentum * v[j] + learning_rate * gradient_ahead[j];}// 更新参数for (int j = 0; j < num_features; j++) {weights[j] -= v[j];}// 计算损失并输出std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}
Nesterov第二种写法

在这里插入图片描述

在这里插入图片描述

代码演示2

//  第二种写法-Nesterov算法优化函数(推荐)
void nesterovOptimization(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, double momentum, int epochs) {int num_samples = data.size();int num_features = data[0].size();std::vector<double> v(num_features, 0.0); // 动量的累积向量for (int epoch = 0; epoch < epochs; epoch++) {std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);// 初始化梯度std::vector<double> gradient(num_features, 0.0);// 计算梯度for (int i = 0; i < num_samples; i++) {for (int j = 0; j < num_features; j++) {gradient[j] += (y_pred[i] - labels[i]) * data[i][j];}}// 更新动量的累积向量for (int j = 0; j < num_features; j++) {v[j] = momentum * v[j] + learning_rate * gradient_ahead[j];}// 更新参数for (int j = 0; j < num_features; j++) {weights[j] -= (momentum * v[j] + learning_rate * gradient_ahead[j]);}// 计算损失并输出std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}

Nadam(Nesterov-accelerated Adaptive Moment Estimation)

Nadam 是一种优化算法,结合了Nesterov Accelerated Gradient(NAG)和Adam两种优化算法的特点。Nadam在深度学习中广泛应用,是目前训练神经网络的优化算法之一。

Nadam算法在Adam的基础上进行了改进,主要是对Adam的动量部分进行了修改。Nadam的优点在于对于凸优化和非凸优化问题都具有较好的性能,并且对于稀疏梯度的处理也比较优秀。

以下是Nadam算法的详细解释:

  1. 算法原理:

    • 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。

    • Nadam算法为每个参数θ维护两个一阶矩估计(first moment estimate)m和二阶矩估计(second moment estimate)v,并使用它们来调整参数的学习率。

    • 参数更新规则:对于第t次迭代,学习率为α,衰减系数为β1和β2,ε是一个小的常数(通常取较小的值,例如1e-8)以防止除以0,β1_t和β2_t是β1和β2的时间步数的衰减校正。

      m ( t ) = β 1 ∗ m ( t − 1 ) + ( 1 − β 1 ) ∗ ∇ J ( θ ( t ) ) m(t) = β1 * m(t-1) + (1 - β1) * ∇J(θ(t)) m(t)=β1m(t1)+(1β1)J(θ(t)) (一阶矩估计)
      v ( t ) = β 2 ∗ v ( t − 1 ) + ( 1 − β 2 ) ∗ ( ∇ J ( θ ( t ) ) ) 2 v(t) = β2 * v(t-1) + (1 - β2) * (∇J(θ(t)))^2 v(t)=β2v(t1)+(1β2)(J(θ(t)))2 (二阶矩估计)
      m h a t ( t ) = m ( t ) / ( 1 − β 1 t ) m_hat(t) = m(t) / (1 - β1^t) mhat(t)=m(t)/(1β1t) (修正一阶矩估计)
      v h a t ( t ) = v ( t ) / ( 1 − β 2 t ) v_hat(t) = v(t) / (1 - β2^t) vhat(t)=v(t)/(1β2t) (修正二阶矩估计)
      θ ( t + 1 ) = θ ( t ) − ( α / ( s q r t ( v h a t ( t ) ) + ε ) ) ∗ ( β 1 t ∗ m h a t ( t ) + ( 1 − β 1 t ) ∗ ∇ J ( θ ( t ) ) ) θ(t+1) = θ(t) - (α / (sqrt(v_hat(t)) + ε)) * (β1_t * m_hat(t) + (1 - β1_t) * ∇J(θ(t))) θ(t+1)=θ(t)(α/(sqrt(vhat(t))+ε))(β1tmhat(t)+(1β1t)J(θ(t)))

    其中:

    • ∇J(θ(t))是损失函数关于参数θ在第t次迭代的梯度(梯度向量)。
    • m(t)和v(t)分别表示一阶矩估计和二阶矩估计,它们初始化为0向量。
    • β1和β2是衰减系数,通常取值为0.9和0.999,分别用于控制一阶矩和二阶矩的衰减程度。
    • β1_t和β2_t是β1和β2的时间步数的衰减校正,它们用于修正动量的计算。
    • m_hat(t)和v_hat(t)是修正后的一阶矩估计和二阶矩估计,用于解决算法初期估计偏向0的问题。
  2. 优点:

    • 自适应学习率:Nadam根据参数的一阶矩和二阶矩估计自适应地调整学习率,使得参数的学习过程更加平稳。
    • 对凸优化和非凸优化都有效:Nadam在处理凸优化和非凸优化问题时都具有较好的性能。
  3. 改进方法:

    • 学习率调整:可以使用学习率衰减技术逐渐减小学习率,以便在训练过程中更细调参数。

Nadam算法在深度学习中广泛应用,特别适用于训练复杂的深度神经网络和处理大规模数据集。它是目前训练神经网络的优化算法之一,并且在很多实际任务中表现优秀。

Nadam算法数学公式:

假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。

  1. 损失函数: J ( θ ) J(θ) J(θ)

  2. 学习率: α α α

  3. 衰减系数: β 1 和 β 2 β1和β2 β1β2

  4. 一阶矩估计(m)和二阶矩估计(v):
    Nadam算法为每个参数θ维护两个一阶矩估计和二阶矩估计,并使用它们来调整参数的学习率。

    在第t次迭代时,我们计算参数θ的梯度∇J(θ(t)),然后分别计算一阶矩估计m和二阶矩估计v:
    m ( t ) = β 1 ∗ m ( t − 1 ) + ( 1 − β 1 ) ∗ ∇ J ( θ ( t ) ) m(t) = β1 * m(t-1) + (1 - β1) * ∇J(θ(t)) m(t)=β1m(t1)+(1β1)J(θ(t))
    v ( t ) = β 2 ∗ v ( t − 1 ) + ( 1 − β 2 ) ∗ ( ∇ J ( θ ( t ) ) ) 2 v(t) = β2 * v(t-1) + (1 - β2) * (∇J(θ(t)))^2 v(t)=β2v(t1)+(1β2)(J(θ(t)))2

    其中:

    • ∇J(θ(t))是损失函数关于参数θ在第t次迭代的梯度(梯度向量)。
    • m(t)是一阶矩估计,它表示过去梯度的一阶矩(均值)。
    • v(t)是二阶矩估计,它表示过去梯度平方的二阶矩(平均平方)。
    • β1和β2是衰减系数,通常取值为0.9和0.999,分别用于控制一阶矩和二阶矩的衰减程度。
  5. 时间步数的衰减校正:
    Nadam算法引入了时间步数的衰减校正,用于修正动量的计算。

    β 1 t = β 1 ∗ ( 1 − 0.5 ∗ 0.9 6 ( t / 250 ) ) β1_t = β1 * (1 - 0.5 * 0.96^(t / 250)) β1t=β1(10.50.96(t/250))
    β 2 t = β 2 ∗ ( 1 − 0.9 9 t ) β2_t = β2 * (1 - 0.99^t) β2t=β2(10.99t)

  6. 修正后的一阶矩估计(m_hat)和二阶矩估计(v_hat):
    由于在算法初期估计偏向0,需要对一阶矩估计m和二阶矩估计v进行修正。

    m h a t ( t ) = m ( t ) / ( 1 − β 1 t ) m_hat(t) = m(t) / (1 - β1^t) mhat(t)=m(t)/(1β1t)
    v h a t ( t ) = v ( t ) / ( 1 − β 2 t ) v_hat(t) = v(t) / (1 - β2^t) vhat(t)=v(t)/(1β2t)

  7. 参数更新规则:
    使用修正后的一阶矩估计m_hat和二阶矩估计v_hat来更新参数θ。

θ ( t + 1 ) = θ ( t ) − ( α / ( s q r t ( v h a t ( t ) ) + ε ) ) ∗ ( β 1 t ∗ m h a t ( t ) + ( 1 − β 1 t ) ∗ ∇ J ( θ ( t ) ) ) θ(t+1) = θ(t) - (α / (sqrt(v_hat(t)) + ε)) * (β1_t * m_hat(t) + (1 - β1_t) * ∇J(θ(t))) θ(t+1)=θ(t)(α/(sqrt(vhat(t))+ε))(β1tmhat(t)+(1β1t)J(θ(t)))

其中:

  • α是学习率,用于控制参数更新的步长。
  • ε是一个小的常数,通常取较小的值(例如1e-8)以防止除以0。

Nadam算法通过引入Nesterov Accelerated Gradient(NAG)和Adam两种优化算法的特点,实现了对凸优化和非凸优化问题都有较好性能的优化算法。在深度学习中,Nadam广泛应用于训练复杂的深度神经网络和处理大规模数据集。

添加正则化

Nadam(Nesterov-accelerated Adaptive Moment Estimation)结合了Adam优化器的概念和Nesterov加速梯度的想法。在Nadam中添加正则化与在Adam中添加正则化非常相似,你只需要在计算梯度时包括正则化项。

以下是将L2正则化与Nadam结合使用的步骤:

  1. 计算损失函数的梯度(不包括正则化项): ∇ J ( θ ( t ) ) \nabla J(\theta(t)) J(θ(t))

  2. 计算正则化项的梯度 2 λ θ ( t ) 2\lambda\theta(t) 2λθ(t)(对于L2正则化)

  3. 结合梯度 ∇ J ( θ ( t ) ) + 2 λ θ ( t ) \nabla J(\theta(t)) + 2\lambda\theta(t) J(θ(t))+2λθ(t)

  4. 更新一阶矩估计
    m ( t ) = β 1 m ( t − 1 ) + ( 1 − β 1 ) ( ∇ J ( θ ( t ) ) + 2 λ θ ( t ) ) m(t) = \beta_1 m(t-1) + (1-\beta_1)\left(\nabla J(\theta(t)) + 2\lambda\theta(t)\right) m(t)=β1m(t1)+(1β1)(J(θ(t))+2λθ(t))

  5. 更新二阶矩估计
    v ( t ) = β 2 v ( t − 1 ) + ( 1 − β 2 ) ( ∇ J ( θ ( t ) ) + 2 λ θ ( t ) ) 2 v(t) = \beta_2 v(t-1) + (1-\beta_2)\left(\nabla J(\theta(t)) + 2\lambda\theta(t)\right)^2 v(t)=β2v(t1)+(1β2)(J(θ(t))+2λθ(t))2

  6. 修正一阶和二阶矩的偏差
    m ^ ( t ) = m ( t ) 1 − β 1 t \hat{m}(t) = \frac{m(t)}{1-\beta_1^t} m^(t)=1β1tm(t)
    v ^ ( t ) = v ( t ) 1 − β 2 t \hat{v}(t) = \frac{v(t)}{1-\beta_2^t} v^(t)=1β2tv(t)

  7. 应用Nesterov修正
    m Nesterov ( t ) = β 1 m ^ ( t ) + ( 1 − β 1 ) ( ∇ J ( θ ( t ) ) + 2 λ θ ( t ) ) m_{\text{Nesterov}}(t) = \beta_1 \hat{m}(t) + (1-\beta_1)\left(\nabla J(\theta(t)) + 2\lambda\theta(t)\right) mNesterov(t)=β1m^(t)+(1β1)(J(θ(t))+2λθ(t))

  8. 更新权重
    θ ( t + 1 ) = θ ( t ) − α v ^ ( t ) + ϵ m Nesterov ( t ) \theta(t+1) = \theta(t) - \frac{\alpha}{\sqrt{\hat{v}(t)} + \epsilon} m_{\text{Nesterov}}(t) θ(t+1)=θ(t)v^(t) +ϵαmNesterov(t)

代码实现与Adam类似,主要区别在于加入了Nesterov修正。你可以按照上述步骤编写代码来实现Nadam优化器,并包括你选择的正则化项。

注意:Nesterov修正为动量项增加了一种"预览"效果,以更准确地预测下一个参数更新的方向。与Adam相比,这可能会在某些情况下提供更好的训练性能。

代码演示
#include <iostream>
#include <vector>
#include <cmath>// 定义损失函数(假设为均方误差)
double lossFunction(std::vector<double>& y_pred, std::vector<double>& y_actual) {double loss = 0.0;for (int i = 0; i < y_pred.size(); i++) {loss += 0.5 * std::pow(y_pred[i] - y_actual[i], 2);}return loss;
}// 定义模型预测函数
std::vector<double> predict(std::vector<std::vector<double>>& data, std::vector<double>& weights) {std::vector<double> y_pred(data.size(), 0.0);for (int i = 0; i < data.size(); i++) {for (int j = 0; j < data[i].size(); j++) {y_pred[i] += data[i][j] * weights[j];}}return y_pred;
}// Nadam优化函数
void nadamOptimization(std::vector<std::vector<double>>& data, std::vector<double>& labels,std::vector<double>& weights, double learning_rate, double beta1, double beta2, double epsilon, int epochs) {int num_samples = data.size();int num_features = data[0].size();std::vector<double> m(num_features, 0.0); // 一阶矩估计std::vector<double> v(num_features, 0.0); // 二阶矩估计int t = 0; // 时间步数for (int epoch = 0; epoch < epochs; epoch++) {std::vector<double> y_pred = predict(data, weights);double loss = lossFunction(y_pred, labels);// 初始化梯度std::vector<double> gradient(num_features, 0.0);// 计算梯度for (int i = 0; i < num_samples; i++) {for (int j = 0; j < num_features; j++) {gradient[j] += (y_pred[i] - labels[i]) * data[i][j];}}// 更新时间步数t++;// 时间步数的衰减校正double beta1_t = beta1 * (1 - 0.5 * std::pow(0.96, t / 250));double beta2_t = beta2 * (1 - std::pow(0.99, t));// 更新一阶矩估计和二阶矩估计for (int j = 0; j < num_features; j++) {m[j] = beta1 * m[j] + (1 - beta1) * gradient[j];v[j] = beta2 * v[j] + (1 - beta2) * gradient[j] * gradient[j];}// 进行修正后的一阶矩估计和二阶矩估计std::vector<double> m_hat(num_features, 0.0);std::vector<double> v_hat(num_features, 0.0);for (int j = 0; j < num_features; j++) {m_hat[j] = m[j] / (1 - std::pow(beta1, t));v_hat[j] = v[j] / (1 - std::pow(beta2, t));}// 更新每个权重for (int j = 0; j < num_features; j++) {weights[j] -= (learning_rate / (std::sqrt(v_hat[j]) + epsilon)) * (beta1_t * m_hat[j] + (1 - beta1_t) * gradient[j]);}// 输出每次迭代的损失std::cout << "Epoch " << epoch + 1 << ", Loss: " << loss << std::endl;}
}int main() {// 假设训练数据为2维特征std::vector<std::vector<double>> data = { {1.0, 2.0}, {2.0, 3.0}, {3.0, 4.0}, {4.0, 5.0}, {5.0, 6.0} };// 对应的标签std::vector<double> labels = {3.0, 4.0, 5.0, 6.0, 7.0};// 初始化权重std::vector<double> weights = {0.0, 0.0};double learning_rate = 0.1;double beta1 = 0.9;double beta2 = 0.999;double epsilon = 1e-8;int epochs = 1000;// 使用Nadam优化权重nadamOptimization(data, labels, weights, learning_rate, beta1, beta2, epsilon, epochs);// 打印最终学到的权重std::cout << "Weights: ";for (double w : weights) {std::cout << w << " ";}std::cout << std::endl;return 0;
}
Nadam另一种算法:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/265973.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

五种查看Spring容器中bean的方法

五种查看Spring容器中bean的方法 在Spring应用程序中&#xff0c;了解和查看容器中的Bean是进行调试和问题排查的关键。Spring提供了多种方法来查看容器中注册的Bean&#xff0c;以便我们深入了解应用程序的内部结构和调试潜在问题。本文将介绍五种常用的查看Spring容器中Bean的…

如何在宝塔面板中设置FTP文件传输服务并实现远程文件管理

文章目录 1. Linux安装Cpolar2. 创建FTP公网地址3. 宝塔FTP服务设置4. FTP服务远程连接小结 5. 固定FTP公网地址6. 固定FTP地址连接 宝塔FTP是宝塔面板中的一项功能&#xff0c;用于设置和管理FTP服务。通过宝塔FTP&#xff0c;用户可以创建FTP账号&#xff0c;配置FTP用户权限…

Docker使用数据卷自定义镜像Dockerfile

目录 一、数据卷 1.1、简介 1.2、用途 1.3、特性&#xff1a; 1.4、数据卷相关操作 1.5、使用情况 二、自定义镜像Dockerfile 2.1、Dockerfile 2.1、使用情况 2.3、具体操作 a、自定义centos 创建文件 编辑内容 制作镜像 测试 b、自定义tomcat 创建文件 编…

人工智能到深度学习:药物发现的机器智能方法(综述学习)

Artificial intelligence to deep learning: machine intelligence approach for drug discovery - PubMed (nih.gov) 人工神经网络、深度神经网络、支持向量机、分类和回归、生成对抗网络、符号学习和元学习是应用于药物设计和发现过程的算法的例子。人工智能已应用于药物设计…

论文阅读:《High-Resolution Image Synthesis with Latent Diffusion Models》

High-Resolution Image Synthesis with Latent Diffusion Models 论文链接 代码链接 What’s the problem addressed in the paper?(这篇文章究竟讲了什么问题&#xff1f;比方说一个算法&#xff0c;它的 input 和 output 是什么&#xff1f;问题的条件是什么) 这篇文章提…

el-autocomplete 提示文字出不来?修改支持模糊搜索提示

查看本专栏目录 关于作者 还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#x…

Groovy(第九节) Groovy 之单元测试

JUnit 利用 Java 对 Song 类进行单元测试 默认情况下 Groovy 编译的类属性是私有的,所以不能直接在 Java 中访问它们,必须像下面这样使用 setter: 编写这个测试用例余下的代码就是小菜一碟了。测试用例很好地演示了这样一点:用 Groovy 所做的一切都可以轻易地在 Java 程序…

[ai笔记12] chatGPT技术体系梳理+本质探寻

欢迎来到文思源想的ai空间&#xff0c;这是技术老兵重学ai以及成长思考的第12篇分享&#xff01; 这周时间看了两本书&#xff0c;一本是大神斯蒂芬沃尔弗拉姆学的《这就是ChatGPT》,另外一本则是腾讯云生态解决方案高级架构师宋立恒所写的《AI制胜机器学习极简入门》&#xf…

JavaScript+PHP实现视频文件分片上传

摘要 视频文件分片上传&#xff0c;整体思路是利用JavaScript将文件切片&#xff0c;然后循环调用上传接口 upload.php 将切片上传到服务器。这样将由原来的一个大文件上传变为多个小文件同时上传&#xff0c;节省了上传时间&#xff0c;这就是文件分片上传的其中一个好处。 上…

算法沉淀——动态规划之两个数组的 dp(下)(leetcode真题剖析)

算法沉淀——动态规划之两个数组的 dp 01.正则表达式匹配02.交错字符串03.两个字符串的最小ASCII删除和04.最长重复子数组 01.正则表达式匹配 题目链接&#xff1a;https://leetcode.cn/problems/regular-expression-matching/ 给你一个字符串 s 和一个字符规律 p&#xff0c…

【Unity每日一记】角色控制器Character Contorller

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

Python中的数据类型

如果说python中的数据类型,那我们要从标准数据类型说起,在python中标准数据类型如下: 数字类型: 数字数据类型用于存储数值。 他们是不可改变的数据类型&#xff0c;这意味着改变数字数据类型会分配一个新的对象。 在python2.X中数据类型分的比较多,有int(有符号整型),long(…

Redis——服务器

Redis服务器负责与多个客户端建立网络连接&#xff0c;处理客户端发送的命令请求&#xff0c;在数据库中保存客户端执行命令所产生的数据&#xff0c;并通过资源管理来维持服务器自身的运行。 一. 命令请求的执行过程 一个命令请求从发送到获得回复过程中&#xff0c;客户端和服…

【MySQL】基本查询(表的增删改查)-- 详解

CRUD&#xff1a;Create&#xff08;创建&#xff09;&#xff0c;Retrieve&#xff08;读取&#xff09;&#xff0c;Update&#xff08;更新&#xff09;&#xff0c;Delete&#xff08;删除&#xff09;。 一、Create insert [into] table_name [(column [, column] ...)] v…

从0到1实现五子棋游戏!!

Hello&#xff0c;好久不见宝子们&#xff0c;今天来给大家更一个五子棋的程序~ 我们今天要讲的内容如下&#xff1a; 文章目录 1.五子棋游戏介绍1.1 游戏玩法介绍&#xff1a; 2.准备工作2.1 具体操作流程 3.游戏程序主函数4.初始化棋盘4.1.定义宏变量4.2 初始化棋盘 5.打印…

什么是VR紧急情况模拟|消防应急虚拟展馆|VR游戏体验馆加盟

VR紧急情况模拟是利用虚拟现实&#xff08;Virtual Reality&#xff0c;简称VR&#xff09;技术来模拟各种紧急情况和应急场景的训练和演练。通过VR技术&#xff0c;用户可以身临其境地体验各种紧急情况&#xff0c;如火灾、地震、交通事故等&#xff0c;以及应对这些紧急情况的…

常用git 打tag命令

1.查看所有tag git tag 2.创建 v5.0.0的tag git tag v5.0.0 git tag &#xff08;创建后查看&#xff09; 3.推送到远程tag git push origin v5.0.0 4.删除远程tag git push origin --delete v5.0.0 5.删除本地tag git tag -d v5.0.0 6.添加带有备注信息的tag git tag v5.…

linux系统Jenkins的安装

Jenkins安装 安装上传安装包解压包首次登录要去服务器查看密码&#xff0c;更改密码选择需要安装的插件设置Admin用户和密码安装完成 安装 上传安装包 上传 jdk17 tomcat jenkins.war的安装包 . 上传 tomcat安装包解压包 解压jdk tar xf jdk-11.0.18_linux-x64_bin.tar.gz解…

Golang embed 库全面解析:从基础到高级应用

Golang embed 库全面解析&#xff1a;从基础到高级应用 引言Golang的 embed&#xff1a;简化资源管理提升可移植性与便利性适用场景的拓展 embed 库的基本概念embed 库的工作原理使用 embed 的基本语法访问嵌入资源的方法embed 的限制 如何使用 embed嵌入单个文件嵌入整个目录结…