本文将简单介绍一些深度学习中的基本概念:
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. 计算 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

  1. 计算总步数:

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 为例):

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二分类神经网络训练流程来进一步学习
代码示例
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

首先了解调度器

  1. 学习率调度器(Scheduler)

    常见的学习率更新方式有两种:

    • 以 step 为基础:在每个 step 结束后更新学习率。

      scheduler = ...
      
      for epoch in range(E):
          for batch_idx, (inputs, targets) in enumerate(dataloader):
              # 前向、后向、更新参数
              ...
              # 在每个 step 后更新学习率
              scheduler.step()
      
    • 以 epoch 为基础:在每个 epoch 结束后更新学习率。

      scheduler = ...
      
      for epoch in range(E):
          for batch_idx, (inputs, targets) in enumerate(dataloader):
              # 前向、后向、更新参数
              ...
          # 在每个 epoch 后更新学习率
          scheduler.step()
      

    我们的代码是以 step 为基础的scheduler.step()
    让我们试一试以 epoch 为基础

修改如下

        # 更新参数
        # 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 精细控制学习率变化周期。
  • Epoch-based 的常见用途:

    • 简单衰减策略:如每 5 个 epoch 学习率乘以 0.1(经典 ResNet 训练)。
    • 依赖验证指标时:如根据 epoch 结束时的验证准确率决定是否调整。
    • 小数据集:epoch 耗时短,按 epoch 调整更易实现。

选择建议

  • 优先 Step-based:当数据量大、需要高频调整或使用 warmup 时。
  • 优先 Epoch-based:当训练稳定性和可解释性更重要(如学术实验复现)。
  • 混合策略:某些调度器(如 CosineAnnealingLR)可同时支持两种模式,需根据任务需求选择。

总之,两者的核心差异在于 更新的时间粒度,选择时需权衡调整频率与训练稳定性。实际应用中,step-based 更常见于现代大规模训练,而 epoch-based 多用于传统小规模任务。
因为是小规模,所以我们的模型loss更低了
接下来我们添加早停机制,因为我们可以观察训练过程发现模型在某个epoch就达到了best loss,但还在训练导致了过拟合

早停(Early Stopping)

可以基于 epoch 或 step 来监控验证集性能,若在一定 patience(耐心值)内验证性能没有提高,则提前停止训练来避免过拟合。

首先把训练函数封装成一个train_one_epoch函数

具体完整代码

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
# - 最佳模型保存了
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
还是没任何变化

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 可以自动管理设备与梯度累积逻辑,先进行安装:

pip install accelerate

完整实现代码

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
# - 最佳模型保存了

最后结果

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。
  • 适用场景:绝大多数深度学习任务。

对比总结

指标BGDSGDMBGD
梯度准确性高(全局最优)低(单样本噪声)中(小批量平均)
收敛速度慢(每步计算量大)快(但需更多迭代)中(平衡步长与迭代)
内存需求高低中
收敛稳定性平滑波动大相对稳定
典型应用传统机器学习在线学习深度学习

选择建议

  • 数据量小 → 用BGD。
  • 数据量大或动态数据 → 用SGD或MBGD。
  • 深度学习 → 默认MBGD(结合动量、Adam等优化器效果更佳)。

扩展

如果你还想进一步降低Loss但是没有好的方法,或者效果不好
,我只能说这是没有意义的
因为数据是随机生成的

X = torch.randn(N, 10)
y = torch.randint(0, 2, (N,))

X 是 高斯分布噪声
y 是 完全随机的 0/1 标签

这里 特征和标签之间没有任何关系!换句话说,模型是没法“学会规律”的,因为压根没有规律
当然你还是执着于的话可以这样设置

N = 10000  # 数据集总样本数
B = 1     # 理想batch_size
b = 1      # 实际 batch size
E = 50      # epochs
gradient_accumulation_steps = B // b  # 等于 4,可以手动设置
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

更多推荐