文章目录
-
- 常用梯度下降算法
-
- 随机梯度下降(Stochastic Gradient Descent,SGD):
- 批量梯度下降(Batch Gradient Descent)
- 小批量梯度下降(Mini-batch Gradient Descent):
- 动量优化(Momentum Optimization):
- Adagrad(Adaptive Gradient Algorithm):
- RMSprop(Root Mean Square Propagation):
- Adam(Adaptive Moment Estimation):
- NAG (Nesterov Accelerated Gradient)
- Nadam(Nesterov-accelerated Adaptive Moment Estimation)
常用梯度下降算法
当涉及不同的梯度下降算法时,每种算法都有其独特的特点和优化策略。下面对每种算法进行详细解释:
随机梯度下降(Stochastic Gradient Descent,SGD):
-
算法原理:在每次迭代中,随机选择一个训练样本来计算损失函数的梯度,并更新模型的参数。由于随机选择样本,梯度估计存在一定的噪声,导致优化路径不稳定,但收敛速度较快。
-
优点:收敛速度快,计算开销较小。
-
缺点:优化路径不稳定,可能会震荡,难以找到全局最优解。
-
算法原理:在每次迭代中,使用所有训练样本来计算损失函数的梯度,并更新模型的参数。由于使用了更多数据,梯度估计更准确,收敛路径较稳定。
-
优点:收敛路径稳定,梯度估计准确。
-
缺点:计算开销较大,内存要求高,不适用于大规模数据集。
以下是随机梯度下降的详细解释:
-
算法原理:
- 对于一个样本的训练数据 (x, y),其中 x 是输入特征,y 是对应的实际标签。
- SGD通过计算损失函数关于该样本的梯度来更新参数。梯度表示损失函数在每个参数处的变化率,它指示了在当前参数值下增加或减少参数值将如何影响损失函数的值。
- SGD更新参数的规则为:新的参数 = 旧的参数 - 学习率 * 损失函数关于该样本的梯度。
- 通过不断地使用不同的样本,迭代训练直至达到预定的训练轮数或损失函数收敛到一个满意的程度。
-
优点:
- 计算开销小:由于每次迭代只使用一个样本,计算梯度的代价较小,特别适用于大规模数据集。
- 更新频率高:参数的更新频率高,使得算法可能在较少的迭代次数内找到一个相对较好的解。
-
缺点:
- 不稳定性:由于随机性,每次迭代的参数更新可能不同,导致优化路径不稳定,甚至可能出现震荡的情况。
- 收敛性较慢:由于随机性导致的不稳定性,可能会导致收敛速度较慢,尤其是在损失函数存在大的震荡时。
-
改进方法:
- 学习率调整:由于SGD的不稳定性,通常会使用学习率衰减技术来逐渐减小学习率,使得在训练初期更快收敛,后期细调参数。
- Mini-batch:SGD的随机性使其不稳定,为了兼顾计算效率和稳定性,通常采用小批量梯度下降(Mini-batch Gradient Descent),在每次迭代中使用一小批样本来计算梯度。
- Momentum:引入动量项,有助于加速优化过程,减少震荡。
随机梯度下降数学公式:
假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数 θ θ θ表示模型的权重和偏置),其中 θ θ θ是一个向量。我们希望找到使损失函数最小化的最优参数 θ ∗ θ* θ∗。
-
损失函数: J ( θ ) J(θ) J(θ)
-
随机梯度下降公式:
在随机梯度下降中,我们使用一个样本 ( 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)不同,它在每次迭代中使用全部训练样本,因此在计算梯度时具有更好的稳定性和准确性。
以下是批量梯度下降的详细解释:
-
算法原理:
- 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
- 在每次迭代中,使用所有训练样本计算损失函数关于参数θ的梯度。批量梯度下降会遍历所有样本,计算梯度的平均值。
- 梯度表示损失函数在每个参数处的变化率。通过计算梯度,我们可以确定在当前参数值下,增加或减少参数值将如何影响损失函数的值。
- 参数更新规则:新的参数 = 旧的参数 - 学习率 * (1 / 批大小) * ∑(损失函数关于所有样本的梯度)
-
优点:
- 稳定性:由于使用所有样本的梯度,批量梯度下降的梯度估计更稳定,通常能够更准确地朝向损失函数的最小值方向移动。
- 全局最优:相比随机梯度下降,批量梯度下降更有可能收敛到全局最优解,尤其是在凸优化问题中。
-
缺点:
- 计算开销较大:由于每次迭代需要使用所有样本来计算梯度,批量梯度下降的计算开销较大。特别是在大规模数据集上,计算可能非常耗时。
-
改进方法:
- 学习率调整:为了避免学习率过大或过小导致的优化问题,可以使用学习率衰减技术逐渐减小学习率,以便在训练初期更快收敛,后期更细调参数。
- 随机梯度下降(SGD)和小批量梯度下降(Mini-batch Gradient Descent):批量梯度下降在大规模数据集上的计算开销较大,为了加速优化过程,可以采用随机梯度下降或小批量梯度下降等变体。
批量梯度下降数学公式:
假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数 θ ∗ θ* θ∗。
-
损失函数: J ( θ ) J(θ) J(θ)
-
批量梯度下降公式:
在批量梯度下降中,我们使用所有训练样本来计算损失函数关于参数θ的梯度,并更新模型的参数。损失函数关于参数θ的梯度(梯度向量): ∇ 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的幂次)训练样本来计算损失函数关于参数的梯度,并更新模型的参数。相比于批量梯度下降,它在计算梯度时具有更好的效率,而相比于随机梯度下降,它的梯度估计更稳定,从而更容易收敛到较好的解。
以下是小批量梯度下降的详细解释:
-
算法原理:
- 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
- 在每次迭代中,选择一小批(通常为2的幂次)训练样本,计算损失函数关于该批样本的梯度。小批量梯度下降会遍历整个训练集,但每次迭代只使用一小批样本。
- 梯度表示损失函数在每个参数处的变化率。通过计算梯度,我们可以确定在当前参数值下,增加或减少参数值将如何影响损失函数的值。
- 参数更新规则:新的参数 = 旧的参数 - 学习率 * (1 / 批大小) * ∑(损失函数关于该批样本的梯度)
-
优点:
- 计算效率高:相比于批量梯度下降,小批量梯度下降的计算开销较小,尤其适用于大规模数据集。
- 梯度估计稳定:相比于随机梯度下降,小批量梯度下降使用一小批样本计算梯度,因此梯度估计更稳定,更容易收敛到较好的解。
-
缺点:
- 超参数选择:小批量梯度下降中需要选择合适的批大小,这是一个超参数,不同的批大小可能会对优化过程产生影响。
-
改进方法:
- 学习率调整:为了避免学习率过大或过小导致的优化问题,可以使用学习率衰减技术逐渐减小学习率,以便在训练初期更快收敛,后期更细调参数。
小批量梯度下降数学公式:
假设我们有一个损失函数 J ( θ ) J(θ) J(θ)(参数θ表示模型的权重和偏置),其中 θ θ θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数 θ ∗ θ* θ∗。
-
损失函数: J ( θ ) J(θ) J(θ)
-
小批量梯度下降公式:
在小批量梯度下降中,我们使用一小批(通常为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)是一种梯度下降算法的改进版本,它通过模拟物体在惯性作用下的运动来加速收敛,并且有助于在梯度更新时减少震荡。动量优化可以在训练过程中更快地达到收敛,并且在复杂的非凸优化问题中通常表现较好。
动量优化算法的核心思想是在更新参数时,利用之前的梯度信息来为当前的梯度方向提供一个“动量”。这样做可以在梯度在一个方向上连续增大或减小时,使得参数更新更加平滑,从而加快收敛速度。
以下是动量优化算法的详细解释:
-
算法原理:
- 假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
- 在每次迭代中,利用当前梯度和之前的动量来计算参数的更新量。
- 动量的引入类似于模拟物体在运动过程中的惯性。在更新参数时,当前梯度方向上的更新将受到之前动量方向的影响。
- 参数更新规则:新的参数 = 旧的参数 - 学习率 * 动量 * (上一次动量方向 + 当前梯度方向)
-
动量的计算:
动量的计算类似于梯度的累积。我们引入一个动量系数β(通常取值为0.9或0.99),并维护一个动量向量v,初始化为0。在每次迭代中,根据当前梯度计算动量,并更新动量向量v。然后,用动量向量v来更新参数。 -
优点:
- 收敛速度快:动量优化可以在训练过程中更快地达到收敛,尤其在高维、复杂的非凸优化问题中表现较好。
- 减少震荡:动量的引入可以减少参数更新时的震荡,使得参数更新更加平滑。
-
超参数选择:
- 学习率:动量优化算法依然需要选择适当的学习率,较大的学习率可能导致振荡,较小的学习率可能导致收敛缓慢。
- 动量系数β:通常情况下,β取 0.9 0.9 0.9或 0.99 0.99 0.99是一个合理的选择,较大的β可以增加动量的影响。
动量优化数学公式:
假设我们有一个损失函数 J(θ)(参数θ表示模型的权重和偏置),其中θ是一个向量。我们的目标是最小化该损失函数,找到使损失函数最小化的最优参数θ*。
-
损失函数: J ( θ ) J(θ) J(θ)
-
动量向量的初始化:
初始化动量向量 v = 0 v = 0 v=0,v的维度与θ相同。 -
动量的计算:
在每次迭代中,利用当前梯度和之前的动量来计算动量。动量系数:β(通常取值为0.9或0.99)
更新动量向量: v = β ∗ v + ( 1 − β ) ∗ ∇ J ( θ ) v = β * v + (1 - β) * ∇J(θ) v=β∗v+(1−β)∗∇J(θ)
其中, ∇ J ( θ ) ∇J(θ) ∇J(θ)是损失函数关于参数θ的梯度(梯度向量)。
-
参数更新规则:
使用动量向量v来更新参数θ。学习率: α α α
参数更新: θ ← θ − α ∗ v θ ← θ - α * v θ←θ−α∗v
在每次迭代中,我们计算梯度 ∇ J ( θ ) ∇J(θ) ∇J(θ)并更新动量向量 v v v,然后使用动量向量 v v v来更新参数 θ θ θ。动量向量v模拟了之前梯度的“动量”,在梯度的方向上引入了惯性,使得参数更新更加平滑,从而加快收敛速度。
添加正则化 实例
当然!下面是在使用动量和L2正则化的参数更新中的具体数学公式。
首先,我们有L2正则化项的梯度:
∇ R ( θ ) =