从 SGD 到梯度累积:Epoch、Batch、Step 的关系全解析

本文将简单介绍一些深度学习中的基本概念:

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

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

首先了解调度器

  1. 学习率调度器(Scheduler)

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

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

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

      python 复制代码
      scheduler = ...
      
      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 精细控制学习率变化周期。
  • 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函数

具体完整代码

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

相关推荐
还梦呦几秒前
2025年09月计算机二级Java选择题每日一练——第一期
java·开发语言
音视频牛哥12 分钟前
从H.264到AV1:音视频技术演进与模块化SDK架构全解析
人工智能·音视频·大牛直播sdk·rtsp h.265·h.264 h.265 av1·h.265和h.266·enhenced rtmp
♞沉寂18 分钟前
信号以及共享内存
linux·c语言·开发语言
AIbase202422 分钟前
如何快速找到最适合的AI绘画工具?避免在200+工具中挑花眼?
人工智能
答题卡上的情书34 分钟前
java第一个接口
java·开发语言
王廷胡_白嫖帝1 小时前
Qt密码生成器项目开发教程 - 安全可靠的随机密码生成工具
开发语言·qt
机器之心1 小时前
DeepSeek开源新基础模型,但不是V4,而是V3.1-Base
人工智能·openai
金融小师妹1 小时前
AI多因子模型解析:黄金涨势受阻与美联储9月降息政策预期重构
大数据·人工智能·算法
R-G-B1 小时前
【P38 6】OpenCV Python——图片的运算(算术运算、逻辑运算)加法add、subtract减法、乘法multiply、除法divide
人工智能·python·opencv·图片的运算·图片加法add·图片subtract减法·图片乘法multiply
拖拖7651 小时前
解读《Thyme: Think Beyond Images》——让大模型“写代码”思考图像
人工智能