批量训练
数据量大了,每次喂全量太慢——把数据切成小块,让训练又快又稳。
回顾:每次更新都用全部数据?
到目前为止,所有训练循环都是这样写的:
把 全部 N 个样本 都过一遍,求平均 loss,再做 一次 参数更新。 这种做法叫 批量梯度下降 (Batch Gradient Descent,简称 BGD)。
它的 好处 很直接——梯度是 全量平均 , 估计精确、噪声小,loss 曲线最稳定。但代价也很明显:
- 慢 :当 N 很大(百万级),每一步都要算上百万次前向 + 反向, 一个 epoch 才能动一次参数
- 容易卡住 :损失曲面光滑无噪—— 一旦掉到 局部最小值 附近梯度变小,就难以逃脱
另一个极端:随机梯度下降
既然全量更新慢,那干脆每次只用 一个样本 更新一次:
这就是 随机梯度下降 (Stochastic Gradient Descent,SGD)。
它的 好处 是 更新最频繁、单步最便宜 —— N 个样本就有 N 次参数更新,每一步只需要算一条样本的前向 + 反向。 但代价也很明显:
- 梯度噪声大 :单样本梯度方向极不稳定, loss 曲线呈剧烈震荡,难以判断是否真正收敛
- 浪费 GPU 算力 :GPU 一次能并行算上百条样本, 单条单条地喂会让它的算力闲着
最佳折中:mini-batch SGD
实际使用的是两者的折中——每次取 一小批 样本(比如 32 / 64 / 128 条) 计算平均梯度后更新:
式子里的 就是这一批的样本数——它有个专门的名字叫 batch_size (批量大小),是 mini-batch SGD 中最常见的超参之一,决定了每次参数更新一起参与计算的样本数。
这就是 mini-batch SGD ,是当今所有深度学习训练的默认选择。 它兼顾了 BGD 的稳定性与 SGD 的效率,并且能充分利用 GPU 的并行能力—— 一个 batch 一次性算完,吞吐远超单样本逐个算。
batch_size 的影响规律
越大,单步算的样本越多、梯度越接近全量平均; 越小则相反。所以选 32 还是 256 还是 1024 并非随意——这个超参的影响有迹可循:
粗略经验—— 能装下就用大点 ,但不要一味追求大 batch; 小 batch 带来的梯度噪声反而能帮模型逃出尖锐的局部最小值。
PyTorch 数据加载工具链
要做 mini-batch 训练,需要把数据切片成 batch 一个个喂给模型。 PyTorch 提供两个核心工具:
- Dataset :定义"一份数据怎么取"——给定索引返回一对
- DataLoader :自动把 Dataset 按 batch_size 切片、打乱、并行预取
重写训练循环
加上 batch 维度后,训练循环变成 外层 epoch + 内层 batch 的双层循环:
Playground:batch_size 对比实验
最后通过一个 Playground 直观感受 batch_size 对训练的影响—— 选择不同的 batch 大小,点击「训练一次」,下方折线图会按颜色叠加每次试验的 loss 曲线。
观察: batch=1 时单步用单样本梯度,loss 曲线锯齿剧烈; batch=32 已显著平滑; batch=256 的曲线几乎是一条平滑下降线,非常接近 全量 训练。
附录:张量批量操作语法
引入 batch 维度后,张量普遍会多出一个维度(典型形状 )。 所谓「批量友好」是指:这些工具能自动按 batch 维并行计算, 让你不必为 batch 写 for 循环。下面是几个最常被提到的:
记号约定 :下表中 始终表示 batch 维;其他大写字母( 、 、 、 )表示 样本本身 的维度, 与 batch 无关。