梯度下降法(Gradient Descent)是机器学习中最核心的优化算法之一,用于寻找使损失函数最小化的参数值。
1. 基本概念
梯度下降法的核心思想是:沿着函数梯度的反方向逐步调整参数,使函数值不断减小。
从数学上看:
- 梯度(∇f)方向是函数增长最快的方向
- 梯度的反方向(-∇f)是函数下降最快的方向
在进行模型训练时,有三个基础的概念:
- Epoch:使用全部数据对模型进行以此完整训练,训练轮次
- Batch_size:使用训练集中的小部分祥本对模型权重进行以此反向传播的参数更新,每次训练每批次样本数量
- Iteration:使用一个Batch数据对模型进行一次参数更新的过程
例如:
- 假设数据集有50000个训练样本,现在选择Batch Size=256对模型进行训练:
- 每个Epoch要训练的图片数量:50000
- 训练集具有的Batch个数:50000 / 256 + 1 = 196
- 每个Epoch具有的Iteration个数:196
- 10个Epoch具有的Iteration个数:1960
2. 公式定义
参数更新公式为:
\[W_{ij}^{new} = W_{ij}^{old} - \eta \frac{\partial E}{\partial w_{ij}} \]
其中:
- \(W_{ij}^{new}\):更新后的权重
- \(W_{ij}^{old}\):更新前的权重
- \(\eta\):学习率(learning rate),控制每次更新的步长
- \(\frac{\partial E}{\partial w_{ij}}\):损失函数 \(E\) 对权重 \(w_{ij}\) 的偏导数(梯度)
学习率 \(\eta\) 的选择至关重要:
- 学习率太小:收敛速度慢,训练时间长
- 学习率太大:可能跳过最优解,甚至导致发散(无法收敛)
解决方法:
- 使用学习率衰减策略:随着训练进行逐渐减小学习率
- 使用自适应学习率算法
根据训练集大小、批量大小和批次数量的不同,梯度下降主要分为三种形式:
| 类型 | 训练集大小 | Batch Size | Number of Batches | 说明 |
|---|---|---|---|---|
| BGD (Batch Gradient Descent) | \(N\) | \(N\) | \(1\) | 使用全部数据计算梯度 |
| SGD (Stochastic Gradient Descent) | \(N\) | \(1\) | \(N\) | 每次使用单个样本更新 |
| Mini-Batch GD | \(N\) | \(B\) | \(N/B+1\) | 折中方案,使用小批量数据 |
上述 Mini-Batch 的 Batch 的个数为 N / B + 1 是针对未整除的情况,整除情况下为 N / B
2.1 BGD
更新公式:
\[\theta_{t+1} = \theta_t - \eta \cdot \frac{1}{N} \sum_{i=1}^N \nabla_\theta J(\theta; x_i, y_i) \]
特点:
- 每次迭代使用全部N个样本计算梯度
- 梯度估计准确,但计算成本高
- 可能陷入局部极小点
代码示例:
python
import torch
import torch.nn as nn
import torch.optim as optim
# 生成示例数据
X = torch.randn(100, 1) # 100个样本
y = 3 * X + 2 + 0.1 * torch.randn(100, 1) # 线性关系 + 噪声
# 定义模型
model = nn.Linear(1, 1)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# BGD 训练
num_epochs = 100
for epoch in range(num_epochs):
# 前向传播
outputs = model(X)
loss = criterion(outputs, y)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch+1) % 10 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')2.2 SGD
更新公式:
\[\theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta J(\theta; x_i, y_i) \]
其中 \(i\) 随机选择
特点:
- 每次随机使用1个样本更新
- 计算快,适合在线学习
- 梯度估计噪声大,收敛不稳定
代码示例:
python
import torch
import torch.nn as nn
import torch.optim as optim
# 生成示例数据
X = torch.randn(100, 1) # 100个样本
y = 3 * X + 2 + 0.1 * torch.randn(100, 1) # 线性关系 + 噪声
# 定义模型
model = nn.Linear(1, 1)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# SGD 训练
num_epochs = 100
for epoch in range(num_epochs):
for i in range(len(X)): # 遍历每个样本
# 取单个样本
x_i = X[i].unsqueeze(0)
y_i = y[i].unsqueeze(0)
# 前向传播
outputs = model(x_i)
loss = criterion(outputs, y_i)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch+1) % 10 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')2.3 Mini-Batch GD
更新公式:
\[\theta_{t+1} = \theta_t - \eta \cdot \frac{1}{B} \sum_{i=k}^{k+B-1} \nabla_\theta J(\theta; x_i, y_i) \]
其中 \(B\) 是 batch_size
特点:
- 折中方案,通常B取32-256
- 利用向量化加速计算
- 梯度估计比SGD更稳定
python
from torch.utils.data import DataLoader, TensorDataset
# 将数据封装为 DataLoader
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=16, shuffle=True) # batch_size=16
# Minibatch GD 训练
num_epochs = 100
for epoch in range(num_epochs):
for batch_x, batch_y in dataloader: # 遍历每个小批次
# 前向传播
outputs = model(batch_x)
loss = criterion(outputs, batch_y)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch+1) % 10 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')