开始前,会这些就够了
- 能阅读 Python 列表和循环
- 知道训练会读取数据并调整模型参数
遇到不熟悉的词,先看这 2 个解释
- 训练轮次
- 对固定的数据,按既定取数规则走完一轮,通常记为一个 epoch。它描述一轮的范围,不直接等于处理了多少批、更新了多少次参数。
- 学习率
- 在常见的梯度更新中,程序先算出参数变化对目标的影响。学习率用来缩放本次调整的幅度,像控制步子大小的设置,不表示一轮读多少数据。
有人说“训练了十轮”,有人说“跑了一千步”。这两个数字不能直接比较:一轮有多少条数据、一次拿多少条、拿几次才调整参数,都会影响总工作量。
先把模型想成一个边看例子边修正的程序。本文只给例子分组和记账,不计算梯度、不更新任何模型;借此把常见训练日志里的三种计数拆开。
看了几批,准备更新几次?
把一份固定名单按顺序分批,再把几批合成一次计划更新。每轮结束都结清剩余批次,不留到下一轮;这里仅做计数,不计算梯度或训练模型。
2 轮合计:样本访问 20 次,数据批次 6 批,计划更新 4 次。
- 重复轮数
- 2 轮
- 累计样本访问
- 20 次
- 累计数据批次
- 6 批
- 累计计划更新
- 4 次
样本编号始终为 #1 到 #10。重复读取同一条会再次计为访问,并没有增加不同样本的数量。
每一轮,都按这张账本结清
每轮使用 10 条,形成 3 批,安排 2 次更新。固定名单与规则不变,下面的同一轮计划重复 2 次。
每张大框是一项计划更新,里面的小块是它包含的数据批。编号帮助核对有没有漏算或重复。
- 计划更新 18 条样本
- 第 1 批 · 4 条样本 #1–#4满批
- 第 2 批 · 4 条样本 #5–#8满批
4 + 4 = 8 条,汇总 2 批。
已凑满约定的 2 批,在这里结算。
- 计划更新 22 条样本
- 第 3 批 · 2 条样本 #9–#10保留的尾批,不足整批
2 = 2 条,汇总 1 批。
本轮收尾:未凑满约定的 2 批,也在这里结算,不带到下一轮。
这里不计算平均损失,也没有执行优化器。样本数相加只是在记访问量,不是把各批平均值再平均;真实训练需要按样本或有效位置正确加权,还要区分计划更新、跳过更新与参数实际是否改变。本文也没有打乱、重复抽样或多进程分片。
整份过一轮,一次拿一批,更新另记一次
在一份固定、有明确结尾的数据上,按既定取数规则走完一轮,通常记作一个 epoch。一次送去计算的一组例子叫 batch,batch size 是这组有多少条。step 则必须看上下文:可能指处理一批,也可能指执行一次优化器更新;阅读日志时先找它在哪里加一。
下面限定单进程、顺序读取、每条数据一轮最多用一次。十条数据每批四条,保留结尾时分成四、四、二;丢掉不足四条的尾批时,只处理前八条。后一种也可以结束这一轮,但不能说十条都参与了计算。
把尾批和尾部更新都写进账本
本例再约定:每处理两批,安排一次参数更新;每轮结束时,有剩余批次也立即安排一次,不带到下一轮。于是保留尾批时,第一次更新汇总八条,最后一次只汇总两条。这里的 update_samples 记录每次计划更新用了几条。
代码只生成这份计划。第二行改变的是丢弃尾批的规则;第三行把“每轮都结清”的计划重复两轮,sample_visits 数的是累计处理次数,同一条在下一轮再读会再计一次。
def plan_epoch(samples, batch_size, accumulate, drop_last=False):
if type(samples) is not int or samples < 0:
raise ValueError('samples must be a nonnegative integer')
if any(type(x) is not int or x < 1 for x in (batch_size, accumulate)):
raise ValueError('batch_size and accumulate must be positive integers')
if type(drop_last) is not bool:
raise ValueError('drop_last must be a boolean')
sizes = [min(batch_size, samples - start)
for start in range(0, samples, batch_size)]
if drop_last and sizes and sizes[-1] < batch_size:
sizes.pop()
# Every remaining group is settled, including the end-of-epoch remainder.
update_samples = [sum(sizes[start:start + accumulate])
for start in range(0, len(sizes), accumulate)]
return sizes, update_samples
if __name__ == '__main__':
for name, drop in [('keep tail', False), ('drop tail', True)]:
batches, updates = plan_epoch(10, 4, 2, drop)
print(f'{name}: batches={batches}, update_samples={updates}')
batches, updates = plan_epoch(10, 4, 2)
print(f'2 epochs, keep tail: batches={2 * len(batches)}, '
f'planned_updates={2 * len(updates)}, sample_visits={2 * sum(batches)}')怎样运行:保存为 training_counts.py,用 Python 3.10 或更新版本运行:python training_counts.py。仅用标准库,不联网,不训练模型。
运行后对照结果
keep tail: batches=[4, 4, 2], update_samples=[8, 2]
drop tail: batches=[4, 4], update_samples=[8]
2 epochs, keep tail: batches=6, planned_updates=4, sample_visits=20两批之后才更新,不等于已经更新两次
真实训练可以先计算一小批数据对参数的调整信号,也就是梯度,把几批的信号积起来,再交给优化器执行一次更新。这叫梯度累积。数据批次计数每批都增加,更新计数则要等到累积结束;两种 step 的数字从这里开始分开。
“每批四条、积两批”只有凑满时才对应八条。尾批、空数据或不足一个累积窗口都会改变实际数量。本例把尾部也结算是一项明确的选择;真实代码可能跨轮保留或丢弃,需要检查。算平均损失时还要按实际样本或有效位置加权,不能把这个计数程序当作一套梯度实现。
读日志前,先问它在数什么
有些任务的数据一直流入,或者程序主动规定每轮只跑若干批。此时 epoch 更像一个报告区间;例如 Keras 的 steps_per_epoch 可以指定何时结束一轮,不能只看轮数就推断所有数据都被看过。抽样允许重复、多进程分片等情况也超出了本文的顺序取数模型。
一份容易核对的日志应分别写累计处理的样本或有效 token、数据批次、计划更新和实际执行的更新。混合精度训练可能因无效梯度跳过某次更新,本例没有这种机制。即使计数相同,也不能据此断言训练质量、运行时间或参数数值相同。
记住这三点
- epoch 数轮次,batch 描述一组数据,step 的具体含义要从程序和日志约定确认。
- 梯度累积让读取批次数与参数更新次数分开,尾批和尾部累积必须说明如何处理。
- 比较训练投入时,同时记录实际处理量和更新次数;单看“十轮”还不够。
别把例子用过头
- 本例仅统计单进程、顺序读取的固定数据,没有训练、梯度计算或分布式数据加载。
- 计划调用更新,不等于参数数值一定改变;跳过更新、零学习率等情况还需另查实际执行。
- 这里空数据返回空计划,不代表任何训练框架都会接受空训练集;重复访问量也不等于不同样本数。
自己试一小步
把数据改成十三条,每批五条、累计两批后更新,两轮的账应该怎样记?
- 先用纸列出每轮各批的大小,分别考虑保留尾批与丢掉尾批。
- 沿用每轮结束立即结算的规则,标出每次计划更新包含哪些批次。
- 修改代码核对两轮的批次数、计划更新次数与累计样本访问量。
做到这些,就算完成
- 是否清楚指出了哪部分数据被丢掉,或哪次更新没有凑满?
- 是否区分了数据批次计数、更新计数和不同样本数?
- 是否写明尾部不跨轮保留,且没有把计数结果当作训练效果?
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- Keras:Model.fit 的 epoch 与 steps_per_epoch (新标签页)↗
Model.fit的数据迭代、epoch 定义以及steps_per_epoch对每轮结束位置的约定。 - PyTorch 2.14:DataLoader 的自动分批与 drop_last (新标签页)↗
batch_size按批读取样本,drop_last控制不足一批的尾部;多进程IterableDataset的行为需要另看约定。 - PyTorch 2.14:梯度累积与更新时机 (新标签页)↗
累积多个微批的梯度后再执行更新,混合精度检查可能跳过更新;数据迭代与更新时机不同。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
Keras:一轮训练里有多少批、多少次更新 (新标签页)↗
对照 fit 的四个计数参数,为十条数据画每批四条的账本,先声明末批和更新规则。
查看学习准备与访问说明 →