本文将简单介绍一些深度学习中的基本概念:
Epoch、Batch、Step 三者之间的关系
SGD、BGD、MBGD 方法的区别
梯度累积的使用和...
Epoch、Batch、Step 三者之间的关系
| 概念 | 定义 | 公式 |
|---|---|---|
| Epoch | 整个训练集完整遍历一次 | - |
| Batch | 一小组样本,用于一次参数更新前的前/后向传播 | Number of Batches per Epoch=⌈NB⌉\text{Number of Batches per Epoch} = \left\lceil \frac{N}{B} \right\rceilNumber of Batches per Epoch=⌈BN⌉ |
| Step | 一次完整的参数更新过程(前向+反向传播+更新参数) | Total Steps=⌈NB⌉×E\text{Total Steps} = \left\lceil \frac{N}{B} \right\rceil \times ETotal Steps=⌈BN⌉×E |
举例说明
假设:
- 数据集大小 N=10,000N = 10,000N=10,000
- batch size B=32B = 32B=32
- epoch 数 E=5E = 5E=5
- 计算 1 个 epoch 中的 batch 数量:
Number of Batches per Epoch=⌈10,00032⌉=⌈312.5⌉=313 \text{Number of Batches per Epoch} = \left\lceil \frac{10,000}{32} \right\rceil = \left\lceil 312.5 \right\rceil = 313 Number of Batches per Epoch=⌈3210,000⌉=⌈312.5⌉=313
- 计算总步数:
Total Steps=⌈10,00032⌉×5=313×5=1565 \text{Total Steps} = \left\lceil \frac{10,000}{32} \right\rceil \times 5 = 313 \times 5 = 1565 Total Steps=⌈3210,000⌉×5=313×5=1565
图示(以前 2 个 epoch 为例):
bash
Epoch 1
└── Batch 1 → Step 1 (前向+反向传播+更新参数)
└── Batch 2 → Step 2
└── Batch 3 → Step 3
└── ...
└── Batch 313 → Step 313
Epoch 2
└── Batch 1 → Step 314
└── Batch 2 → Step 315
└── Batch 3 → Step 316
└── ...
└── Batch 313 → Step 626
- 每个 epoch 包含多个 batch,每个 batch 对应 1 次参数更新(即 1 个 step)。
- 我们可以用
epochs控制训练回合数或用max_steps控制训练总 step 数(AI 画图的 UI 界面中常出现这个超参数选项)。
我们来实现了一个基础的PyTorch二分类神经网络训练流程来进一步学习
代码示例
python
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
def same_seeds(seed=42):
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed)
# 设置随机种子
same_seeds()
# 数据集参数
N = 10000 # 数据集总样本数
B = 32 # batch_size
E = 5 # epochs
# 创建一个示例数据集
X = torch.randn(N, 10) # 生成服从标准正态分布(均值为0,方差为1)的随机数数列N*10
y = torch.randint(0, 2, (N,)) # 二分类标签 如果N=5,可能会得到类似这样的结果: tensor([0, 1, 1, 0, 1])
# 这里的 X 和 y 都是 torch.tensor 类型
# 定义数据集和 DataLoader
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=B, shuffle=True, drop_last=False)
# 定义模型
model = nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 2)
)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 训练模型
steps_per_epoch = len(dataloader) # 一个 epoch 内 batch 的数量
total_steps = steps_per_epoch * E
device = "cuda" if torch.cuda.is_available() else "cpu"
# 这里设置 device 为 cuda,如果没有 cuda 设备,则使用 cpu
model.to(device)
# 记录最佳损失
best_loss = float('inf')
# 记录当前 step
current_step = 0
# 这里设置成从 1 开始只是为了不在 print 中额外设置,实际写代码的时候不需要纠结这一点
for epoch in range(1, E + 1):
for batch_idx, (inputs, targets) in enumerate(dataloader, start=1):
inputs = inputs.to(device)
targets = targets.to(device)
# 清零梯度(这一步放在反向传播前和参数更新之后都可以)
optimizer.zero_grad()
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, targets)
# 反向传播(计算梯度)
loss.backward()
# 更新参数
optimizer.step()
best_loss = min(best_loss, loss.item())
current_step += 1
# 每 50 步打印一次
if batch_idx % 50 == 0:
# 如果不需要打印累积 step,可以去除 current_step 项直接使用 batch_idx
print(f"Epoch [{epoch}/{E}], Batch [{batch_idx}/{steps_per_epoch}], "
f"Step [{current_step}/{total_steps}], Loss: {loss.item():.4f}")
# 参数更新
print(best_loss)
# 可以看到:
# - epoch 从 1 到 E
# - batch 从 1 到 steps_per_epoch
# - step 累计从 1 到 total_steps
输出:
Epoch [1/5], Batch [50/313], Step [50/1565], Loss: 0.6813
Epoch [1/5], Batch [100/313], Step [100/1565], Loss: 0.6892
Epoch [1/5], Batch [150/313], Step [150/1565], Loss: 0.7314
Epoch [1/5], Batch [200/313], Step [200/1565], Loss: 0.7105
Epoch [1/5], Batch [250/313], Step [250/1565], Loss: 0.6966
Epoch [1/5], Batch [300/313], Step [300/1565], Loss: 0.6974
Epoch [2/5], Batch [50/313], Step [363/1565], Loss: 0.6866
Epoch [2/5], Batch [100/313], Step [413/1565], Loss: 0.6960
Epoch [2/5], Batch [150/313], Step [463/1565], Loss: 0.7177
Epoch [2/5], Batch [200/313], Step [513/1565], Loss: 0.7086
Epoch [2/5], Batch [250/313], Step [563/1565], Loss: 0.7078
Epoch [2/5], Batch [300/313], Step [613/1565], Loss: 0.6947
Epoch [3/5], Batch [50/313], Step [676/1565], Loss: 0.6869
Epoch [3/5], Batch [100/313], Step [726/1565], Loss: 0.7031
Epoch [3/5], Batch [150/313], Step [776/1565], Loss: 0.6880
Epoch [3/5], Batch [200/313], Step [826/1565], Loss: 0.7015
Epoch [3/5], Batch [250/313], Step [876/1565], Loss: 0.6961
Epoch [3/5], Batch [300/313], Step [926/1565], Loss: 0.6881
Epoch [4/5], Batch [50/313], Step [989/1565], Loss: 0.7042
Epoch [4/5], Batch [100/313], Step [1039/1565], Loss: 0.6856
Epoch [4/5], Batch [150/313], Step [1089/1565], Loss: 0.6768
Epoch [4/5], Batch [200/313], Step [1139/1565], Loss: 0.6967
Epoch [4/5], Batch [250/313], Step [1189/1565], Loss: 0.6800
Epoch [4/5], Batch [300/313], Step [1239/1565], Loss: 0.6909
Epoch [5/5], Batch [50/313], Step [1302/1565], Loss: 0.6770
...
Epoch [5/5], Batch [200/313], Step [1452/1565], Loss: 0.6987
Epoch [5/5], Batch [250/313], Step [1502/1565], Loss: 0.6860
Epoch [5/5], Batch [300/313], Step [1552/1565], Loss: 0.7003
0.6238939762115479
...
最best 的loss is
0.6238939762115479我们来不断的尝试降低loss
首先了解调度器
-
学习率调度器(Scheduler)
常见的学习率更新方式有两种:
-
以 step 为基础:在每个 step 结束后更新学习率。
pythonscheduler = ... for epoch in range(E): for batch_idx, (inputs, targets) in enumerate(dataloader): # 前向、后向、更新参数 ... # 在每个 step 后更新学习率 scheduler.step() -
以 epoch 为基础:在每个 epoch 结束后更新学习率。
pythonscheduler = ... for epoch in range(E): for batch_idx, (inputs, targets) in enumerate(dataloader): # 前向、后向、更新参数 ... # 在每个 epoch 后更新学习率 scheduler.step()
我们的代码是以 step 为基础的scheduler.step()
让我们试一试以 epoch 为基础
-
修改如下
python
# 更新参数
# optimizer.step()
best_loss = min(best_loss, loss.item())
current_step += 1
# 每 50 步打印一次
if batch_idx % 50 == 0:
# 如果不需要打印累积 step,可以去除 current_step 项直接使用 batch_idx
print(f"Epoch [{epoch}/{E}], Batch [{batch_idx}/{steps_per_epoch}], "
f"Step [{current_step}/{total_steps}], Loss: {loss.item():.4f}")
optimizer.step()
print(best_loss)
此时best loss is
0.5978379249572754效果更好了,为什么?
在深度学习中,以 step 和以 epoch 为基础更新学习率的主要区别体现在更新频率、数据覆盖度和实际应用场景上。以下是具体对比:
更新频率不同
-
Step-based:
- 每次迭代(batch)后更新:学习率在每个 step(即单个 batch 训练完成后)调整一次。
- 更频繁 :若数据集有
N个样本,batch size 为B,则每 epoch 包含N/B次更新。 - 适用场景:适合动态调整(如损失波动大时需快速响应)或大数据集(epoch 耗时过长)。
-
Epoch-based:
- 每轮完整遍历数据后更新:学习率在每个 epoch 结束后调整一次。
- 更稳定:适用于对噪声敏感的任务(如小数据集或需要稳定下降的场景)。
典型应用场景
-
Step-based 的常见用途:
- 学习率预热(Warmup) :如
BERT训练中,前几千个 step 线性增加学习率,避免初期梯度爆炸。 - 大模型/大数据集:当 1 个 epoch 包含百万级 step 时(如 GPT-3),按 step 调整更高效。
- 动态调度器 :如
OneCycleLR按 step 精细控制学习率变化周期。
- 学习率预热(Warmup) :如
-
Epoch-based 的常见用途:
- 简单衰减策略 :如每 5 个 epoch 学习率乘以
0.1(经典 ResNet 训练)。 - 依赖验证指标时:如根据 epoch 结束时的验证准确率决定是否调整。
- 小数据集:epoch 耗时短,按 epoch 调整更易实现。
- 简单衰减策略 :如每 5 个 epoch 学习率乘以
选择建议
- 优先 Step-based:当数据量大、需要高频调整或使用 warmup 时。
- 优先 Epoch-based:当训练稳定性和可解释性更重要(如学术实验复现)。
- 混合策略 :某些调度器(如
CosineAnnealingLR)可同时支持两种模式,需根据任务需求选择。
总之,两者的核心差异在于 更新的时间粒度 ,选择时需权衡调整频率与训练稳定性。实际应用中,step-based 更常见于现代大规模训练,而 epoch-based 多用于传统小规模任务。
因为是小规模,所以我们的模型loss更低了
接下来我们添加早停机制,因为我们可以观察训练过程发现模型在某个epoch就达到了best loss,但还在训练导致了过拟合
早停(Early Stopping)
可以基于 epoch 或 step 来监控验证集性能,若在一定 patience(耐心值)内验证性能没有提高,则提前停止训练来避免过拟合。
首先把训练函数封装成一个train_one_epoch函数
具体完整代码
python
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
def same_seeds(seed=42):
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed)
# 设置随机种子
same_seeds()
# 数据集参数
N = 10000 # 数据集总样本数
B = 32 # batch_size
E = 5 # epochs
# 创建一个示例数据集
X = torch.randn(N, 10) # 生成服从标准正态分布(均值为0,方差为1)的随机数数列N*10
y = torch.randint(0, 2, (N,)) # 二分类标签 如果N=5,可能会得到类似这样的结果: tensor([0, 1, 1, 0, 1])
# 这里的 X 和 y 都是 torch.tensor 类型
# 定义数据集和 DataLoader
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=B, shuffle=True, drop_last=False)
# 定义模型
model = nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 2)
)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 训练模型
steps_per_epoch = len(dataloader) # 一个 epoch 内 batch 的数量
total_steps = steps_per_epoch * E
device = "cuda" if torch.cuda.is_available() else "cpu"
# 这里设置 device 为 cuda,如果没有 cuda 设备,则使用 cpu
model.to(device)
# 记录最佳损失
best_loss = float('inf')
# 记录当前 step
current_step = 0
# 这里设置 patience=2,即如果连续 2 次没有提升,则提前结束训练
patience=5
origin_patience=patience
def train_one_epoch(model, dataloader,optimizer,criterion,current_step=0):
epoch_best_loss=float('inf')
for batch_idx, (inputs, targets) in enumerate(dataloader, start=1):
inputs = inputs.to(device)
targets = targets.to(device)
# 清零梯度(这一步放在反向传播前和参数更新之后都可以)
optimizer.zero_grad()
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, targets)
# 反向传播(计算梯度)
loss.backward()
# 更新参数
# optimizer.step()
epoch_best_loss = min(epoch_best_loss, loss.item())
current_step += 1
# 每 50 步打印一次
if batch_idx % 50 == 0:
# 如果不需要打印累积 step,可以去除 current_step 项直接使用 batch_idx
print(f"Epoch [{epoch}/{E}], Batch [{batch_idx}/{steps_per_epoch}], "
f"Step [{current_step}/{total_steps}], Loss: {loss.item():.4f}")
optimizer.step()
return epoch_best_loss
# 这里设置成从 1 开始只是为了不在 print 中额外设置,实际写代码的时候不需要纠结这一点
for epoch in range(1, E + 1):
current_loss=train_one_epoch(model, dataloader,optimizer,criterion,current_step=current_step)
if current_loss<best_loss:
best_loss=current_loss
patience=origin_patience
# 保存最佳模型参数
print(f"Best model saved with loss {best_loss:.4f}")
torch.save(model.state_dict(), "best_model.pth")
else:
patience-=1
if patience==0:
print(f"No improvement in validation for {epoch} epochs, stopping early.")
break
print(f'Training complete with best loss {best_loss:.6f}')
# 可以看到:
# - epoch 从 1 到 E
# - batch 从 1 到 steps_per_epoch
# - step 累计从 1 到 total_steps
# - 最佳模型保存了
bash
Epoch [1/5], Batch [50/313], Step [50/1565], Loss: 0.6825
Epoch [1/5], Batch [100/313], Step [100/1565], Loss: 0.6923
Epoch [1/5], Batch [150/313], Step [150/1565], Loss: 0.7371
Epoch [1/5], Batch [200/313], Step [200/1565], Loss: 0.7104
Epoch [1/5], Batch [250/313], Step [250/1565], Loss: 0.6962
Epoch [1/5], Batch [300/313], Step [300/1565], Loss: 0.6859
Best model saved with loss 0.6125
Epoch [2/5], Batch [50/313], Step [50/1565], Loss: 0.7190
Epoch [2/5], Batch [100/313], Step [100/1565], Loss: 0.7225
Epoch [2/5], Batch [150/313], Step [150/1565], Loss: 0.7078
Epoch [2/5], Batch [200/313], Step [200/1565], Loss: 0.7102
Epoch [2/5], Batch [250/313], Step [250/1565], Loss: 0.6941
Epoch [2/5], Batch [300/313], Step [300/1565], Loss: 0.7258
Epoch [3/5], Batch [50/313], Step [50/1565], Loss: 0.7133
Epoch [3/5], Batch [100/313], Step [100/1565], Loss: 0.7375
Epoch [3/5], Batch [150/313], Step [150/1565], Loss: 0.6734
Epoch [3/5], Batch [200/313], Step [200/1565], Loss: 0.7107
Epoch [3/5], Batch [250/313], Step [250/1565], Loss: 0.7341
Epoch [3/5], Batch [300/313], Step [300/1565], Loss: 0.7034
Epoch [4/5], Batch [50/313], Step [50/1565], Loss: 0.7158
Epoch [4/5], Batch [100/313], Step [100/1565], Loss: 0.6138
Epoch [4/5], Batch [150/313], Step [150/1565], Loss: 0.6502
Epoch [4/5], Batch [200/313], Step [200/1565], Loss: 0.7071
Epoch [4/5], Batch [250/313], Step [250/1565], Loss: 0.7239
Epoch [4/5], Batch [300/313], Step [300/1565], Loss: 0.7386
...
Epoch [5/5], Batch [250/313], Step [250/1565], Loss: 0.6980
Epoch [5/5], Batch [300/313], Step [300/1565], Loss: 0.7369
Best model saved with loss 0.5978
Training complete with best loss 0.597838
没啥变化loss,我们可以增大epoch from 5 to 50
还是没任何变化
bash
Training complete with best loss 0.597838
我们试一试增加batch size不过要注意
Batch Size 与显存
更大的 batch size 意味着每个 step 会处理更多数据,占用更多显存。当遇到 GPU 内存不足(Out of Memory,OOM)错误时,可以尝试减小 batch size,如果仍想达成大 batch size 的效果,使用梯度累积技巧(见下文)。
梯度累积(Gradient Accumulation)
当 GPU 显存不足以支持较大的 batch_size 时,可以使用梯度累积。梯度累积的核心思想是将多个小批量(mini-batch)的梯度累加起来,然后再更新参数,这样可以模拟更大的 batch_size。
代码示例
假设理想 batch_size = 32 ,但受显存限制只能一次处理 8 个样本(b=8),则需要累积 4 次(32/8=4)小批次的梯度后再更新参数:
梯度累积的基本步骤:
在每个小批次(batch_size=8)中,我们执行一次前向传播、一次反向传播,并将梯度累加到模型参数的 .grad 中。
当累计了 4 次(也就是相当于处理了 32 个样本后),我们执行 optimizer.step() 来更新参数,然后用 optimizer.zero_grad() 清空累积的梯度。
使用 accelerate 可以自动管理设备与梯度累积逻辑,先进行安装:
bash
pip install accelerate
完整实现代码
python
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from accelerate import Accelerator
def same_seeds(seed=42):
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed)
# 设置随机种子
same_seeds()
# 数据集参数
N = 10000 # 数据集总样本数
B = 32 # 理想batch_size
b = 8 # 实际 batch size
E = 50 # epochs
gradient_accumulation_steps = B // b # 等于 4,可以手动设置
# gradient_accumulation_steps:梯度累积步数,这是一个重要的训练技巧。它允许你在较小的实际batch size下模拟较大的有效batch size。
accelerator=Accelerator(gradient_accumulation_steps=gradient_accumulation_steps)
# 创建一个示例数据集
X = torch.randn(N, 10) # 生成服从标准正态分布(均值为0,方差为1)的随机数数列N*10
y = torch.randint(0, 2, (N,)) # 二分类标签 如果N=5,可能会得到类似这样的结果: tensor([0, 1, 1, 0, 1])
# 这里的 X 和 y 都是 torch.tensor 类型
# 定义数据集和 DataLoader
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=B, shuffle=True, drop_last=False)
# 定义模型
model = nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 2)
)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 将模型和优化器交给 Accelerator 管理
model, optimizer, dataloader = accelerator.prepare(model, optimizer, dataloader)
# 训练模型
steps_per_epoch = len(dataloader) # 一个 epoch 内 batch 的数量
total_steps = steps_per_epoch * E
device = "cuda" if torch.cuda.is_available() else "cpu"
# 这里设置 device 为 cuda,如果没有 cuda 设备,则使用 cpu
model.to(device)
# 记录最佳损失
best_loss = float('inf')
# 记录当前 step
current_step = 0
# 这里设置 patience=2,即如果连续 2 次没有提升,则提前结束训练
patience=50
origin_patience=patience
def train_one_epoch(model, dataloader,optimizer,criterion,current_step=0):
epoch_best_loss=float('inf')
for batch_idx, (inputs, targets) in enumerate(dataloader, start=1):
# with accelerator
with accelerator.accumulate(model):
inputs = inputs.to(device)
targets = targets.to(device)
# 清零梯度(这一步放在反向传播前和参数更新之后都可以)
optimizer.zero_grad()
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, targets)
# 反向传播(计算梯度)
# loss.backward()
# accelerator.backward(loss) 会自动把 loss 累积到当前的 batch 内,然后反向传播
accelerator.backward(loss)
# 更新参数
# optimizer.step()
epoch_best_loss = min(epoch_best_loss, loss.item())
current_step += 1
# 每 50 步打印一次
if batch_idx % 50 == 0:
# 如果不需要打印累积 step,可以去除 current_step 项直接使用 batch_idx
print(f"Epoch [{epoch}/{E}], Batch [{batch_idx}/{steps_per_epoch}], "
f"Step [{current_step}/{total_steps}], Loss: {loss.item():.4f}")
optimizer.step()
return epoch_best_loss
# 这里设置成从 1 开始只是为了不在 print 中额外设置,实际写代码的时候不需要纠结这一点
for epoch in range(1, E + 1):
current_loss=train_one_epoch(model, dataloader,optimizer,criterion,current_step=current_step)
if current_loss<best_loss:
best_loss=current_loss
patience=origin_patience
# 保存最佳模型参数
print(f"Best model saved with loss {best_loss:.4f}")
torch.save(model.state_dict(), "best_model.pth")
else:
patience-=1
if patience==0:
print(f"No improvement in validation for {epoch} epochs, stopping early.")
break
print(f'Training complete with best loss {best_loss:.6f}')
# 可以看到:
# - epoch 从 1 到 E
# - batch 从 1 到 steps_per_epoch
# - step 累计从 1 到 total_steps
# - 最佳模型保存了
最后结果
bash
Training complete with best loss 0.58797
使用了梯度累计实现了实际的batchsize=32和未实现但是batchsize=32相比Loss更低了
Q:使用了梯度累积后,step 和 batch 的对应关系有什么变化?
- 无梯度累积时:1 个 batch 对应 1 次参数更新(1 step)。
- 有梯度累积时:多个小批次(k 个 batch)累积后才更新一次参数,这时 k 个 batch 才对应 1 次 step。
也就是说,step 的频率降低了,但每次 step 的意义相当于在更大有效 batch_size 上进行一次更新。:
SGD、BGD、MBGD 之间的区别
SGD(随机梯度下降)、BGD(批量梯度下降)和MBGD(小批量梯度下降)是梯度下降算法的三种主要变体,核心区别在于每次迭代时使用的数据量不同,进而影响计算效率、收敛速度和稳定性。以下是具体对比:
1. BGD(批量梯度下降)
- 数据使用 :每次迭代使用全部训练数据计算梯度。
- 特点 :
- 稳定性高:梯度方向是全局最优方向,收敛平稳。
- 计算开销大:尤其数据量大时,每次更新需遍历整个数据集。
- 内存占用高:需同时加载所有数据。
- 适用场景:小数据集或凸优化问题。
2. SGD(随机梯度下降)
- 数据使用 :每次迭代随机选取一个样本计算梯度。
- 特点 :
- 速度快:单样本计算,迭代频率高。
- 波动大:梯度噪声多,可能无法收敛到最优解(但可能跳出局部最优)。
- 泛化性好:适合在线学习(动态数据流)。
- 适用场景:大数据集、非凸问题(如深度学习)。
3. MBGD(小批量梯度下降)
- 数据使用 :每次迭代使用小批量样本(如32、64个)计算梯度。
- 特点 :
- 平衡BGD和SGD:兼顾计算效率和稳定性。
- 并行化友好:适合GPU加速(深度学习常用)。
- 需调批量大小:批量过小则波动大,过大则接近BGD。
- 适用场景:绝大多数深度学习任务。
对比总结
| 指标 | BGD | SGD | MBGD |
|---|---|---|---|
| 梯度准确性 | 高(全局最优) | 低(单样本噪声) | 中(小批量平均) |
| 收敛速度 | 慢(每步计算量大) | 快(但需更多迭代) | 中(平衡步长与迭代) |
| 内存需求 | 高 | 低 | 中 |
| 收敛稳定性 | 平滑 | 波动大 | 相对稳定 |
| 典型应用 | 传统机器学习 | 在线学习 | 深度学习 |
选择建议
- 数据量小 → 用BGD。
- 数据量大或动态数据 → 用SGD或MBGD。
- 深度学习 → 默认MBGD(结合动量、Adam等优化器效果更佳)。
扩展
如果你还想进一步降低Loss但是没有好的方法,或者效果不好
,我只能说这是没有意义的
因为数据是随机生成的
bash
X = torch.randn(N, 10)
y = torch.randint(0, 2, (N,))
X 是 高斯分布噪声
y 是 完全随机的 0/1 标签
这里 特征和标签之间没有任何关系!换句话说,模型是没法"学会规律"的,因为压根没有规律
当然你还是执着于的话可以这样设置
bash
N = 10000 # 数据集总样本数
B = 1 # 理想batch_size
b = 1 # 实际 batch size
E = 50 # epochs
gradient_accumulation_steps = B // b # 等于 4,可以手动设置
bash
Epoch [1/50], Batch [50/10000], Step [50/500000], Loss: 0.8390
Epoch [1/50], Batch [100/10000], Step [100/500000], Loss: 0.6147
Epoch [1/50], Batch [150/10000], Step [150/500000], Loss: 0.5295
Epoch [1/50], Batch [200/10000], Step [200/500000], Loss: 0.6609
Epoch [1/50], Batch [250/10000], Step [250/500000], Loss: 0.4225
Epoch [1/50], Batch [300/10000], Step [300/500000], Loss: 1.0206
Epoch [1/50], Batch [350/10000], Step [350/500000], Loss: 0.8659
Epoch [1/50], Batch [400/10000], Step [400/500000], Loss: 0.5234
Epoch [1/50], Batch [450/10000], Step [450/500000], Loss: 0.8742
Epoch [1/50], Batch [500/10000], Step [500/500000], Loss: 0.8313
Epoch [1/50], Batch [550/10000], Step [550/500000], Loss: 0.5952
Epoch [1/50], Batch [600/10000], Step [600/500000], Loss: 0.7431
Epoch [1/50], Batch [650/10000], Step [650/500000], Loss: 0.5761
Epoch [1/50], Batch [700/10000], Step [700/500000], Loss: 0.6298
Epoch [1/50], Batch [750/10000], Step [750/500000], Loss: 0.6725
Epoch [1/50], Batch [800/10000], Step [800/500000], Loss: 0.5078
Epoch [1/50], Batch [850/10000], Step [850/500000], Loss: 0.8275
Epoch [1/50], Batch [900/10000], Step [900/500000], Loss: 0.5330
Epoch [1/50], Batch [950/10000], Step [950/500000], Loss: 0.8798
Epoch [1/50], Batch [1000/10000], Step [1000/500000], Loss: 0.8836
Epoch [1/50], Batch [1050/10000], Step [1050/500000], Loss: 0.5622
Epoch [1/50], Batch [1100/10000], Step [1100/500000], Loss: 0.5411
Epoch [1/50], Batch [1150/10000], Step [1150/500000], Loss: 0.7891
Epoch [1/50], Batch [1200/10000], Step [1200/500000], Loss: 0.7982
Epoch [1/50], Batch [1250/10000], Step [1250/500000], Loss: 0.4537
...
Epoch [19/50], Batch [9950/10000], Step [9950/500000], Loss: 0.5194
Epoch [19/50], Batch [10000/10000], Step [10000/500000], Loss: 1.6206
No improvement in validation for 19 epochs, stopping early.
Training complete with best loss 0.143794
最后best_loss:0.143794