Answer

您询问的是 num_train_epochs 参数的含义和使用方式

参数定义

num_train_epochs 是一个训练参数,表示训练数据集的遍历次数(epoch数) 1 。在标准的监督学习训练中,它控制整个数据集被训练多少轮

在不同Pipeline中的使用

1. DPO Pipeline (监督学习场景)

在DPO等监督学习pipeline中,num_train_epochs直接使用来控制训练循环 2 :

for epoch in range(int(self.pipeline_config.actor_train.training_args.num_train_epochs)):
    logger.info(f"epoch {epoch} start...")
    for batch_dict in tqdm(self.dataloader):
        # 训练逻辑

2. Distill Pipeline (蒸馏场景)

蒸馏pipeline也使用 num_train_epochs 来遍历数据集 3

3. RLVR Pipeline (强化学习场景)

在RLVR等RL训练pipeline中,num_train_epochs 的作用非常有限:

  • 仅用于计算 max_steps: 在某些场景下(如 RLVRMathVLMPipeline),它用于根据数据集大小计算总训练步数 4 :
max_steps = len(self.dataloader) * self.pipeline_config.actor_train.training_args.num_train_epochs
self.pipeline_config.set_max_steps(max_steps=max_steps)
  • 训练循环使用 max_steps: 实际训练时,RLVR pipeline使用 max_steps 而非 num_train_epochs 5 :
for global_step in range(self.pipeline_config.max_steps):
    # 训练逻辑

max_steps 的关系

在标准训练框架(如HuggingFace Trainer)中,max_stepsnum_train_epochs 的关系如下 6 :

  • 如果 max_steps > 0: 优先使用 max_steps,并根据它反推 num_train_epochs
  • 如果 max_steps <= 0: 使用 num_train_epochs 计算 max_steps:
    max_steps = math.ceil(args.num_train_epochs * num_update_steps_per_epoch)
    

Notes

在ROLL的RL训练中(RLVR/Agentic),由于使用在线生成的rollout数据而非固定数据集,不存在传统意义上的"遍历数据集"。因此:

  • num_train_epochs 主要在初始化阶段用于计算 max_steps
  • 实际训练完全由 max_steps 控制
  • 每个pipeline step生成新的rollout数据,而不是重复遍历固定数据集

对于监督学习场景(DPO/Distill),num_train_epochs 的作用与标准深度学习训练一致,控制数据集的遍历次数

Wiki pages you might want to explore:

Citations

File: roll/configs/training_args.py (L21-24)

    num_train_epochs: float = field(
        default=1.0,
        metadata={"help": "Total number of training epochs to perform."}
    )

File: roll/pipeline/dpo/dpo_pipeline.py (L180-180)

        for epoch in range(int(self.pipeline_config.actor_train.training_args.num_train_epochs)):

File: roll/pipeline/distill/distill_pipeline.py (L231-233)

        for epoch in range(self.pipeline_config.student.training_args.num_train_epochs):
            logger.info(f"epoch {epoch} start...")
            for batch_dict in self.dataloader:

File: roll/pipeline/rlvr/rlvr_math_vlm_pipeline.py (L209-210)

        max_steps = len(self.dataloader) * self.pipeline_config.actor_train.training_args.num_train_epochs
        self.pipeline_config.set_max_steps(max_steps=max_steps)

File: roll/pipeline/rlvr/rlvr_pipeline.py (L336-336)

        for global_step in range(self.pipeline_config.max_steps):

File: mcore_adapter/src/mcore_adapter/trainer/trainer.py (L594-605)

            if args.max_steps > 0:
                max_steps = args.max_steps
                num_train_epochs = args.max_steps // num_update_steps_per_epoch + int(
                    args.max_steps % num_update_steps_per_epoch > 0
                )
                # May be slightly incorrect if the last batch in the training dataloader has a smaller size but it's
                # the best we can do.
                num_train_samples = args.max_steps * total_train_batch_size
            else:
                max_steps = math.ceil(args.num_train_epochs * num_update_steps_per_epoch)
                num_train_epochs = math.ceil(args.num_train_epochs)
                num_train_samples = self.num_examples(train_dataloader) * args.num_train_epochs

好的,我们来深入剖析“并传入 max_steps 作为总训练步数”这句话的含义,以及它与 num_train_epochs 之间的关系。

这句描述的核心在于解释**学习率调度器 (Learning Rate Scheduler)**是如何工作的,以及为什么 max_steps 对它至关重要。

学习率调度器的工作原理

想象一下你在下山,目标是到达山谷的最低点(模型损失最小化)。

  • 学习率 (Learning Rate):就是你每一步迈出的步子大小。
  • 学习率调度器 (LR Scheduler):是一个为你规划步子大小策略的向导。

一个好的向导不会让你从头到尾都用同样大小的步子走路。通常的策略是:

  1. 开始时 (Warmup):山顶附近地形复杂,你先迈小步子(小的学习率)热身,避免一步踏空直接摔下去。这个阶段学习率会从小逐渐增大
  2. 中间阶段 (Decay):走到比较平缓的山坡上,你可以迈大步子(大的学习率)快速前进。然后随着你越来越接近谷底,为了不“走过头”,你的步子需要逐渐变小。这个过程就是“衰减 (Decay)”。
  3. 结束时:快到谷底时,你的步子变得非常小(非常小的学习率),以便精确地找到最低点。

关键问题:调度器如何知道整个下山过程有多长,以便规划从“山顶”到“谷底”的整个学习率变化曲线?

答案就是:它需要知道总的训练步数 (max_steps)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
(上图展示了一个典型的带预热和衰减的学习率变化曲线)

  • Total Training Steps (总训练步数): 就是图中的横轴终点,它定义了整个学习率规划的范围。
  • create_optimizer_and_scheduler 方法在创建调度器时,必须告诉它这个“总长度”,调度器才能生成正确的从0到 max_steps 的学习率变化曲线。

max_steps vs num_train_epochs:谁是主导?

现在我们来看 max_stepsnum_train_epochs 的关系。它们都是用来定义训练“多长时间”的,但代表的视角不同。

  • num_train_epochs (训练轮数)以数据为中心的视角。它说:“我要把整个数据集完整地看 N 遍”。
  • max_steps (最大步数)以优化为中心的视角。它说:“我不管看了多少数据,我总共只执行 N 次模型权重更新”。

在现代大模型训练中,我们通常更关心 max_steps,原因如下:

  1. 数据集可能无限大:对于流式数据集 (IterableDataset),num_train_epochs 根本没有意义。
  2. 可复现性与控制max_steps 提供了对训练过程更精确的控制。无论数据集大小如何变化,训练的总计算量(优化器更新次数)是固定的。
  3. 与学习率调度器直接对应:调度器的横轴单位就是“步 (step)”,max_steps 与其完美契合。
两种工作模式

McaTrainer_prepare_train_loop 方法优雅地处理了这两种情况:

模式一:max_steps 主导(推荐)

  • 你的配置: 你在 TrainingArguments 中设置了 max_steps = 10000
  • _prepare_train_loop 的行为:
    1. 它看到 max_steps 是一个正数,于是直接采纳它作为最终的训练目标。
    2. 它将这个 max_steps (10000) 传递给 create_scheduler,调度器就会规划一个从第0步到第10000步的学习率衰减曲线。
    3. num_train_epochs 在这里变成了一个估算值,仅用于日志输出,告诉你“如果要走10000步,大概需要把数据集看2.3遍”。训练循环会在第10000步结束后立即停止,无论此时进行到第几轮。

模式二:num_train_epochs 主导

  • 你的配置: 你设置了 max_steps = -1 (或不设置),但设置了 num_train_epochs = 3
  • _prepare_train_loop 的行为:
    1. 它看到 max_steps 无效,于是转向 num_train_epochs
    2. 它会先计算出“一个 epoch 需要多少个 step”,公式是 num_update_steps_per_epoch = len(dataloader) // gradient_accumulation_steps
    3. 然后它计算出总的 max_stepsmax_steps = num_train_epochs * num_update_steps_per_epoch
    4. 最后,它将这个计算出来的 max_steps 传递给 create_scheduler
    5. 在这种模式下,训练循环会精确地跑完3个 epoch。

结论与总结

并传入 max_steps 作为总训练步数” 这句话,精确地描述了 McaTrainer 的核心设计理念:

  1. max_steps 是驱动学习率调度策略的根本参数。调度器需要这个“全局视野”来规划从开始到结束的学习率变化。

  2. num_train_epochsmax_steps 是定义训练长度的两种方式,McaTrainer 会根据你的配置,将其中一个转换成另一个,但最终总是以一个确定的 max_steps 值去配置学习率调度器

  3. 在以 max_steps 为主的模式下,num_train_epochs 沦为辅助信息;在以 num_train_epochs 为主的模式下,max_steps 是一个基于前者计算得出的中间变量。但无论如何,学习率调度器只认 max_steps

简单来说,num_train_epochs 是“我想把书看几遍”,而 max_steps 是“我总共只打算学习多少个小时”。学习计划(LR Scheduler)是根据“总小时数”来安排的,至于你能把书看几遍,只是这个计划执行后的一个结果而已。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐