状态(Status): Draft 作者(Authors): @Your_community 创建日期(Created): YYYY-MM-DD 更新日期(Updated): YYYY-MM-DD 相关 Issue/PR: #123(关联 Issue/PR 以便追踪背景)
当前 PyNative 模式下,Trainer 主要面向训练流程设计,缺乏统一、便捷的模型纯前向推理入口。本提案为 PyNative 模式下的 Trainer 新增模型前向推理能力,并天然支持数据并行、模型并行、流水线并行等多种分布式并行策略。
推理由 TrainConfig 顶层字段 run_mode: predict 路由:run_mindformer.py --mode 1 下,run_mode: predict 时走 Trainer.inference(),否则走 Trainer.train()。字段名与图模式顶层 run_mode 对齐,非法值在 Trainer.__init__ 构造期 fail-fast。训练 / 推理是两个独立 msrun 进程、两份 yaml,中间靠 checkpoint 文件衔接。
TrainConfig
run_mode: predict
run_mindformer.py --mode 1
Trainer.inference()
Trainer.train()
run_mode
Trainer.__init__
用户在执行评估、预测或部署前的精度验证时,往往需要自行拼接分布式推理逻辑,不仅开发门槛高,而且容易因并行策略处理不当引入精度或性能问题。本提案降低 PyNative 模式下推理任务开发成本,提供与训练一致的分布式并行体验,使 Trainer 在动态图场景下真正覆盖"训练-推理"全流程。
功能目标(对齐需求交付物):
dp_replicate > 1
sample_idx
inference:
train
精度目标:使用训练配置文件正常拉起 DeepSeek-V4-Flash 模型推理任务,加载 HF 权重可推理出符合逻辑的 token;同 prompt 多入口输出逐位一致,多卡解码不失步。
性能目标:不涉及(POC 阶段聚焦功能与精度)。
非目标:
temperature
top_k
top_p
repetition_penalty
inference()
inference
input_data
model.seq_length
generate_batch
parallelism.*
核心思路:推理 = 训练的前向 + 一个解码循环。模型调用、并行切分全部复用训练代码,只新增"输入归一、token 挑选、落盘"三块推理专属逻辑。
GPTModel.construct(input_ids, position_ids)
labels
_apply_parallelism
parallelize_model
forward_only
build_stages
PipelineScheduleRuntime.run()
ScaledLossPipelineStage
has_backward=False
dyn_shape=True
bwd_send_specs
_load_inference_checkpoint
checkpoint.load_path
ValueError
[bs, L]
_sample_shard_info
gather_vocab_parallel_logits
select_last_logits_row
normalize_input
.jsonl
.json
.txt
流程图(双线框 = 与训练共用的部件,单线框 = 前向推理新增,删除线 = 训练有但推理被裁掉的部件):
yaml(run_mode: predict + inference: 段) ──► Trainer.__init__ │ ▼ ┌─ Trainer.__init__ ─────────────────────────────────────────────┐ │ run_mode 校验 ∈ {train, predict},非法值 fail-fast │ │ is_inference = (run_mode == "predict") │ │ is_inference 且 config.inference is None → ValueError │ │ │ │ │ ▼ │ │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ │ ┃ configure_inference(剥 MTP 层) ┃ build 前裁 MTP + │ │ ┃ build_inference_tokenizer ┃ compress_ratios 尾部 │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ ▼ │ │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │ │ ┃ _create_model + _apply_parallelism ┃ 与训练同代码路径 │ │ ┃ (forward_only=True 透传 build_stages)┃ TP/CP/DP/EP/PP/FSDP │ │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ │ │ ▼ │ │ ~~数据集 / 优化器 / lr / callback / LoRA / MTP-sync~~ ✂ 裁掉 │ │ is_inference 分支:return,跳过训练态 init │ └────────────┬────────────────────────────────────────────────────┘ ▼ ┌─ trainer.inference() ──────────────────────────────────────────┐ │ set_train(False) → construct 返回 logits │ │ _validate_pp_inference()(PP 路径 fail-fast) │ │ _load_inference_checkpoint(HF / MF 格式自动识别) │ │ ▼ │ │ normalize_input → records 流 │ │ _sample_shard_info:按并行形态判分流轴 │ │ ├─ 单卡/纯 FSDP/PP ──► 不分流,全体 rank 共享同一条 batch 流│ │ └─ dp_replicate > 1 ──► 条带分流到各副本 │ │ ▼ │ │ generate_from_records → generate_batch 批量贪心解码 │ │ rank 0 落盘(.jsonl 流式 / .json 数组 / .txt 逐行) │ └─────────────────────────────────────────────────────────────────┘
classDiagram direction TB class Trainer { +config: TrainConfig +model: PreTrainedModel +run_mode: Optional~str~ +train_dataset: Optional~Dataset~ +eval_dataset: Optional~Dataset~ +optimizer: Optional~Any~ +lr_scheduler: Optional~Any~ +callbacks: Optional~List~ +compute_loss_func: Optional~Callable~ +inference(input_data: Optional~Union~, max_new_tokens: Optional~int~, output: Optional~str~, batch_size: Optional~int~, dataset_field: str) list -_validate_pp_inference() -_load_inference_checkpoint(model: PreTrainedModel) } class TrainConfig { +run_mode: str = train +inference: Optional~InferenceConfig~ } class InferenceConfig { +max_new_tokens: int = 50 +batch_size: int = 8 +output: str = inference_result.jsonl +input_data: Optional~Union~ } class inference_module { +configure_inference(config: Any) +build_inference_tokenizer(config: Any) +normalize_input(input_data: Any) list +generate_from_records(model: Any, tokenizer: Any, records: Any, dataset_field: str, parallel_dims: Any, max_new_tokens: int, batch_size: int, rank: int, output_path: str, pp_ctx: Any) list +generate_batch(model: Any, tokenizer: Any, prompts: Any, pad_token_id: int, parallel_dims: Any, max_new_tokens: int, max_seq_length: int, rank: int, pp_ctx: Any) list +gather_vocab_parallel_logits(logits: Any, parallel_dims: Any) Any +select_last_logits_row(logits: Any, last_index: int, parallel_dims: Any) Any +_sample_shard_info(parallel_dims: Any, pp_ctx: Any) tuple } class parallelize_model { +forward_only bool } class StageModelBuilder { +build_stages(model_config: Any, pp_mesh: Any, tp_mesh: Any, has_backward: bool, dyn_shape: bool) tuple } class ScaledLossPipelineStage { +bwd_send_specs(micro_index: int) list } class PipelineInferenceContext { +schedule: Any +is_last_stage: bool +group: Any +src: int +broadcast_tokens(token_ids: Any) list } class PreTrainedModel { <<abstract>> } class GPTModel { +config: TransformerConfig +vocab_size: int +max_sequence_length: int +pre_process: bool +post_process: bool +share_embeddings_and_output_weights: bool +position_embedding_type: str +layer_start: int +layer_end: int +mtp_process: bool +return_logits: bool +construct(input_ids: Tensor, position_ids: Tensor, labels: Tensor) Tensor } GPTModel --|> PreTrainedModel Trainer *-- TrainConfig : Trainer 自建, 从 yaml/dict 加载 Trainer o-- PreTrainedModel : 外部传入 model 引用 TrainConfig o-- InferenceConfig : Optional 字段, 可为 None Trainer ..> inference_module : 构造期与推理期两次调用模块函数 Trainer ..> parallelize_model : forward_only 透传 parallelize_model ..> StageModelBuilder : 路由至构建器 StageModelBuilder ..> ScaledLossPipelineStage : 构建 stage 实例 inference_module ..> GPTModel : 非 PP 路径直接 construct 前向 inference_module ..> PipelineInferenceContext : PP 路径下条件持有
调用链要点:
inference.py
configure_inference
build_inference_tokenizer
StageModelBuilder
run_mode="predict"
generate_from_records
PipelineInferenceContext
schedule.run
broadcast_tokens
下列 UML 时序图描述关键路径的参与者协同与消息时序。参与者与类图节点对应:
Trainer
inference_module
Model
GPTModel
Schedule
PipelineInferenceContext.schedule
Rank0
从 yaml 加载到结果落盘的全链路。一次性动作(权重加载、set_train)在批循环外,loop 只保留批量解码。
loop
sequenceDiagram autonumber participant Trainer participant inference as inference_module participant Model participant Schedule participant Rank0 Note over Trainer: build 阶段 Trainer.__init__ Trainer->>Trainer: 校验 run_mode 属于 train 或 predict alt run_mode == predict Trainer->>Trainer: is_inference 置 True opt config.inference is None Trainer->>Trainer: raise ValueError end Trainer->>inference: configure_inference 传 config Note right of inference: 剥 MTP 层 + 截 compress_ratios Trainer->>inference: build_inference_tokenizer 传 config inference->>inference: 从 load_path 检测 tokenizer 文件 inference->>inference: 解析 pad_token_id 写回 model.config inference-->>Trainer: tokenizer else run_mode == train Note over Trainer: 走训练路径 行为不变 end Trainer->>Model: _create_model + _apply_parallelism forward_only=is_inference Note right of Model: build_stages 参数 has_backward=False dyn_shape=True opt is_inference Trainer->>Trainer: return 跳过训练态 init end Note over Trainer: inference 阶段 Trainer.inference Trainer->>Model: set_train False opt PP enabled Trainer->>Trainer: _validate_pp_inference Note right of Trainer: interleave/overlap/swap 触发 NotImplementedError end Trainer->>Trainer: _load_inference_checkpoint 传 model alt is_hf_checkpoint load_path Trainer->>Trainer: load_hf_checkpoint else MF 格式 Trainer->>Trainer: load_checkpoint end Trainer->>inference: generate_from_records 传 model tokenizer records pp_ctx opt PP enabled Trainer->>Trainer: 构造 PipelineInferenceContext end loop 每个 batch 在 generate_from_records 内部 inference->>inference: _sample_shard_info 得 shard_id num_shards alt PP enabled inference->>Schedule: pp_ctx.schedule.run 传相同 kwargs Note right of Schedule: first stage 消费输入 last stage 产 logits else 非 PP 路径 inference->>Model: model construct 传 input_ids position_ids Note right of Model: 不传 labels 即返 logits end inference->>inference: select_last_logits_row 与 gather_vocab_parallel_logits inference->>inference: argmax 取 token, EOS 冻结陪跑 end inference->>Rank0: all_gather_object 汇聚 按 sample_idx 去重排序 Rank0->>Rank0: 落盘 .jsonl / .json / .txt Trainer->>Trainer: barrier 然后 destroy_process_group
端到端时序图(3.3.1)覆盖了构建期与推理期的全链路。下面三点是 PR 实现的关键交互细节,对应 inference.py 的热路径:
批量解码(generate_batch):每步将活跃样本右 pad 到最长,一次 [bs, L] 前向;逐活跃样本经 select_last_logits_row(CP)与 gather_vocab_parallel_logits(TP)取最后逻辑 logits 行后 argmax。某样本 EOS 或超长即冻结为 pad 行陪跑,输出丢弃;finished[] 派生自各 rank 相同的 all-gathered logits,故 if all(finished): break 判定集体一致,无需节拍器。
finished[]
if all(finished): break
PP 单步(PipelineInferenceContext):pp>1 时各 rank 传相同 kwargs 调 schedule.run,first stage 消费输入,last stage 产出 logits 并选出 per-sample token,再经 broadcast_object_list 广播回全部 stage 供下一步 append。src 取本线 last stage 的全局 rank(get_rank_list_along_axis("pp")[-1],与 has_last 同源)——MindSpore broadcast 的 src 语义为全局 rank,多 PP 线下按局部 rank 或世界组广播会错源。ScaledLossPipelineStage.bwd_send_specs 覆写返回空 spec list,使调度器为非首 stage 排的 BWD_SEND 步变 no-op,训练路径不受影响。
broadcast_object_list
src
get_rank_list_along_axis("pp")[-1]
has_last
broadcast
ScaledLossPipelineStage.bwd_send_specs
forward-only 在 hyper-parallel 侧若仍触发梯度归约,可能需要补 execute_reduce_grad 直返的覆写;POC 测试矩阵未覆盖到此路径,作为已知待验证项。
execute_reduce_grad
shard_id
all_gather_object
(payload, text_line)
_merge_shard_entries
InferenceConfig
is_inference
_load_checkpoint
is_hf_checkpoint
get_checkpoint_path
logger.error
_open_output_file
os.open
tools/utils.FILE_PERMISSION
BaseTrainer.predict_process
O_CREAT
interleave_num=1
overlap_b_f=false
swap=false
_validate_pp_inference
input_data 是唯一数据入口,支持五种输入形态,由 normalize_input 统一归一为 list[dict]:单条 str(字面 prompt,或文件路径——.jsonl 按 jsonl 解析,其他后缀按纯文本每行一条)、单条 dict、list[dict] / list[str]。其中结构化输入(单条 dict / list[dict] / .jsonl 文件)超出静态图推理入口的能力,并带出配套参数 dataset_field(record 中 prompt 字段名,默认 "instruction",由 Trainer.inference() 形参确认);静态图纯文本输入无此概念。混合类型 list 与非 str/dict/list 类型触发 TypeError。
list[dict]
str
dict
list[str]
dataset_field
"instruction"
list
TypeError
接口描述:PyNative 模型文本生成入口,贪心(argmax)解码。
接口原型:
def inference(self, input_data=None, max_new_tokens=None, output=None, batch_size=None, dataset_field="instruction") -> list:
输入/输出参数:
inference.input_data
inference.batch_size
异常处理:checkpoint.load_path 为空 / 缺 tokenizer 文件 / output 扩展名不识别 / max_new_tokens ≤ 0 / batch_size ≤ 0 / run_mode 非法时 ValueError;run_mode: predict 但缺 inference: 段时 ValueError。tokenize 失败的样本以 logger.error 逐 rank 记录并写 error 行,不中断批量。前向失败(generate_batch 抛异常)当前会传播出整批推理,不在容错范围内——与 UC2 "不中断批量" 意图存在偏差。
output
max_new_tokens ≤ 0
batch_size ≤ 0
约束说明:需在 is_inference=True 的 Trainer 上调用(yaml run_mode: predict);解码固定贪心 argmax,各 rank 从相同的 all-gathered logits 取同一 token,多卡确定可复现。
is_inference=True
batch_size 跨 rank 一致性当前只靠文档约束("all ranks share the batch boundaries"),代码未做 all-reduce 校验。yaml 同源时天然一致,但程序化 Trainer.inference(batch_size=X) 覆写若在部分 rank 上调用会静默 desync。建议入口加一次 broadcast(batch_size) 或 all-reduce 校验。
batch_size
Trainer.inference(batch_size=X)
broadcast(batch_size)
调用参考代码:
trainer = Trainer(config) # yaml 含 run_mode: predict results = trainer.inference() # 全走 yaml results = trainer.inference(input_data="hello", max_new_tokens=10) # 覆写
接口描述:inference: 段配置项,挂在 TrainConfig.inference 下。权重与 tokenizer 共用 checkpoint.load_path 单一入口(HF 权重同目录自带词表;MF 格式权重需将词表文件拷入该目录)。
TrainConfig.inference
[batch, seq]
run_mode 字段挂在 TrainConfig 顶层(非 InferenceConfig),取值 train(默认)/ predict,与图模式字段名对齐,非法值构造期报错。
predict
__post_init__ 校验:max_new_tokens > 0、batch_size > 0、output 扩展名 ∈ {.jsonl, .json, .txt, 无扩展名}。超长输入不做截断:tokenize 后 padded 长度超过 model.seq_length 的样本按 error 行记录并跳过(fail-fast,与官方 dsv4-flash generate.py 行为一致)。
__post_init__
max_new_tokens > 0
batch_size > 0
generate.py
接口描述:统一五种输入形态为 list[dict]。
{"instruction": s}
# 训练 yaml 基础上追加: run_mode: 'predict' # 路由字段,顶层 checkpoint: load_path: /path/to/weights_with_tokenizer # HF ckpt 自带词表;MF ckpt 需将词表文件拷入该目录 inference: input_data: /path/to/prompts.txt # 或 str / dict / list max_new_tokens: 128 batch_size: 8 output: inference_result.jsonl
bash mindformers/scripts/msrun_launcher.sh \ "python run_mindformer.py --mode 1 --config test.yaml" 8
使用约束:
tokenizer.json
tokenizer_config.json
vocab.json
merges.txt
tokenizer.model
spiece.model
vocab_size
测试环境:8 卡 Atlas 910B2 msrun,DSV4 tiny 训练 20 步另存的权重;冒烟脚本 predict/_script/run_test.sh。
predict/_script/run_test.sh
f00efec1d859bca98c22e77e54da2516
.xml
0o640
data_parallel_shard: 2
data_parallel_shard: -1
interleave_num > 1
overlap_b_f=true
swap=true
pipeline_parallel=1
NotImplementedError
e6bf3a88b9af964318d18735c30dc2cf
max_new_tokens: 32
状态(Status): Draft
作者(Authors): @Your_community
创建日期(Created): YYYY-MM-DD
更新日期(Updated): YYYY-MM-DD
相关 Issue/PR: #123(关联 Issue/PR 以便追踪背景)
1. 概述
1.1 简介
当前 PyNative 模式下,Trainer 主要面向训练流程设计,缺乏统一、便捷的模型纯前向推理入口。本提案为 PyNative 模式下的 Trainer 新增模型前向推理能力,并天然支持数据并行、模型并行、流水线并行等多种分布式并行策略。
推理由
TrainConfig顶层字段run_mode: predict路由:run_mindformer.py --mode 1下,run_mode: predict时走Trainer.inference(),否则走Trainer.train()。字段名与图模式顶层run_mode对齐,非法值在Trainer.__init__构造期 fail-fast。训练 / 推理是两个独立 msrun 进程、两份 yaml,中间靠 checkpoint 文件衔接。1.2 动机
用户在执行评估、预测或部署前的精度验证时,往往需要自行拼接分布式推理逻辑,不仅开发门槛高,而且容易因并行策略处理不当引入精度或性能问题。本提案降低 PyNative 模式下推理任务开发成本,提供与训练一致的分布式并行体验,使 Trainer 在动态图场景下真正覆盖"训练-推理"全流程。
1.3 目标
功能目标(对齐需求交付物):
Trainer.inference()推理接口:支持传入单条数据或数据集(五种输入形态,见 3.5.2.1),输入输出行为正常。dp_replicate > 1且非 PP 时生效;PP 开启时各 PP 线解码全量记录、靠sample_idx去重(见 3.1 "DP 样本分流"),DP 并发度在该组合下不生效——这是 POC 阶段的实现选择。inference:段、run_mode缺省为train时行为不变)。精度目标:使用训练配置文件正常拉起 DeepSeek-V4-Flash 模型推理任务,加载 HF 权重可推理出符合逻辑的 token;同 prompt 多入口输出逐位一致,多卡解码不失步。
性能目标:不涉及(POC 阶段聚焦功能与精度)。
非目标:
temperature/top_k/top_p/repetition_penalty等)——解码固定贪心(argmax)。inference()仅做 base 续写,instruct 模型的对话模板渲染由使用方离线预处理。对 instruct 模型直接调用会得到 base 续写行为,后续演进需补 prompt 渲染器。2. 用例分析
inference:段并置run_mode: predict,加载训练产出的分布式权重(或 HF 权重),贪心生成验证输出。要求:推理前向与训练数值路径一致;旧 yaml 不含inference段且run_mode缺省时训练行为不变。input_data,多卡 DP 下样本自动分流并行生成(见 3.1 样本分流),批量解码、逐条落盘,输出回显原始字段供评测脚本对账。要求:多卡解码不失步;tokenize 失败的样本记 error 行不中断批量;超长样本(padded 长度超过model.seq_length)记 error 行跳过,不截断。前向失败当前不在容错范围内:generate_batch抛出的异常会传播出整批推理,与 UC2 "单条失败不中断批量" 的意图存在偏差。parallelism.*,支持跨策略加载(ReshardLoader 自动切分)。要求:跨策略输出与原策略基线逐位一致;PP 仅支持基础调度(interleave / overlap / swap / FSDP+PP 组合 fail-fast 拒绝)。3. 方案设计
3.1 总体方案
核心思路:推理 = 训练的前向 + 一个解码循环。模型调用、并行切分全部复用训练代码,只新增"输入归一、token 挑选、落盘"三块推理专属逻辑。
GPTModel.construct(input_ids, position_ids)labels计 loss,推理不传即返回 logits_apply_parallelism/parallelize_modelforward_only参数透传至build_stagesPipelineScheduleRuntime.run()ScaledLossPipelineStagehas_backward=False+dyn_shape=True,forward-only 兜底以子类覆写bwd_send_specs落在 mindformers 侧,hyper-parallel 依赖库零改动_load_inference_checkpointcheckpoint.load_path缺失直接ValueError(避免随机权重产出垃圾输出)generate_batch批量解码循环[bs, L]全量前向,无 KV cache;EOS / 超长样本冻结陪跑,无需节拍器_sample_shard_infogather_vocab_parallel_logits/select_last_logits_rownormalize_input/ 落盘分发.jsonl/.json/.txt(无扩展名按 txt)流程图(双线框 = 与训练共用的部件,单线框 = 前向推理新增,
删除线= 训练有但推理被裁掉的部件):3.2 类图
classDiagram direction TB class Trainer { +config: TrainConfig +model: PreTrainedModel +run_mode: Optional~str~ +train_dataset: Optional~Dataset~ +eval_dataset: Optional~Dataset~ +optimizer: Optional~Any~ +lr_scheduler: Optional~Any~ +callbacks: Optional~List~ +compute_loss_func: Optional~Callable~ +inference(input_data: Optional~Union~, max_new_tokens: Optional~int~, output: Optional~str~, batch_size: Optional~int~, dataset_field: str) list -_validate_pp_inference() -_load_inference_checkpoint(model: PreTrainedModel) } class TrainConfig { +run_mode: str = train +inference: Optional~InferenceConfig~ } class InferenceConfig { +max_new_tokens: int = 50 +batch_size: int = 8 +output: str = inference_result.jsonl +input_data: Optional~Union~ } class inference_module { +configure_inference(config: Any) +build_inference_tokenizer(config: Any) +normalize_input(input_data: Any) list +generate_from_records(model: Any, tokenizer: Any, records: Any, dataset_field: str, parallel_dims: Any, max_new_tokens: int, batch_size: int, rank: int, output_path: str, pp_ctx: Any) list +generate_batch(model: Any, tokenizer: Any, prompts: Any, pad_token_id: int, parallel_dims: Any, max_new_tokens: int, max_seq_length: int, rank: int, pp_ctx: Any) list +gather_vocab_parallel_logits(logits: Any, parallel_dims: Any) Any +select_last_logits_row(logits: Any, last_index: int, parallel_dims: Any) Any +_sample_shard_info(parallel_dims: Any, pp_ctx: Any) tuple } class parallelize_model { +forward_only bool } class StageModelBuilder { +build_stages(model_config: Any, pp_mesh: Any, tp_mesh: Any, has_backward: bool, dyn_shape: bool) tuple } class ScaledLossPipelineStage { +bwd_send_specs(micro_index: int) list } class PipelineInferenceContext { +schedule: Any +is_last_stage: bool +group: Any +src: int +broadcast_tokens(token_ids: Any) list } class PreTrainedModel { <<abstract>> } class GPTModel { +config: TransformerConfig +vocab_size: int +max_sequence_length: int +pre_process: bool +post_process: bool +share_embeddings_and_output_weights: bool +position_embedding_type: str +layer_start: int +layer_end: int +mtp_process: bool +return_logits: bool +construct(input_ids: Tensor, position_ids: Tensor, labels: Tensor) Tensor } GPTModel --|> PreTrainedModel Trainer *-- TrainConfig : Trainer 自建, 从 yaml/dict 加载 Trainer o-- PreTrainedModel : 外部传入 model 引用 TrainConfig o-- InferenceConfig : Optional 字段, 可为 None Trainer ..> inference_module : 构造期与推理期两次调用模块函数 Trainer ..> parallelize_model : forward_only 透传 parallelize_model ..> StageModelBuilder : 路由至构建器 StageModelBuilder ..> ScaledLossPipelineStage : 构建 stage 实例 inference_module ..> GPTModel : 非 PP 路径直接 construct 前向 inference_module ..> PipelineInferenceContext : PP 路径下条件持有调用链要点:
Trainer.__init__(入参见类图字段区)→TrainConfig(读run_mode/inference)→inference.py(configure_inference+build_inference_tokenizer)→parallelize_model→StageModelBuilder→ScaledLossPipelineStage。run_mode="predict"时构造期旁路训练态 init。Trainer.inference()→inference.py(generate_from_records→generate_batch);PP 路径下generate_from_records持有PipelineInferenceContext驱动schedule.run与broadcast_tokens。ScaledLossPipelineStage训练 / 推理共用,差异仅在构建参数。3.3 时序图
下列 UML 时序图描述关键路径的参与者协同与消息时序。参与者与类图节点对应:
Trainer:推理入口对象(类图Trainer)inference:inference.py模块(类图inference_module),承载推理专属函数Model:GPTModel 实例(类图GPTModel),前向入口Schedule:PP 调度器(类图PipelineInferenceContext.schedule),PP 路径下驱动 stage 前向Rank0:落盘 rank3.3.1 端到端推理时序(构建期 + 推理期)
从 yaml 加载到结果落盘的全链路。一次性动作(权重加载、set_train)在批循环外,
loop只保留批量解码。sequenceDiagram autonumber participant Trainer participant inference as inference_module participant Model participant Schedule participant Rank0 Note over Trainer: build 阶段 Trainer.__init__ Trainer->>Trainer: 校验 run_mode 属于 train 或 predict alt run_mode == predict Trainer->>Trainer: is_inference 置 True opt config.inference is None Trainer->>Trainer: raise ValueError end Trainer->>inference: configure_inference 传 config Note right of inference: 剥 MTP 层 + 截 compress_ratios Trainer->>inference: build_inference_tokenizer 传 config inference->>inference: 从 load_path 检测 tokenizer 文件 inference->>inference: 解析 pad_token_id 写回 model.config inference-->>Trainer: tokenizer else run_mode == train Note over Trainer: 走训练路径 行为不变 end Trainer->>Model: _create_model + _apply_parallelism forward_only=is_inference Note right of Model: build_stages 参数 has_backward=False dyn_shape=True opt is_inference Trainer->>Trainer: return 跳过训练态 init end Note over Trainer: inference 阶段 Trainer.inference Trainer->>Model: set_train False opt PP enabled Trainer->>Trainer: _validate_pp_inference Note right of Trainer: interleave/overlap/swap 触发 NotImplementedError end Trainer->>Trainer: _load_inference_checkpoint 传 model alt is_hf_checkpoint load_path Trainer->>Trainer: load_hf_checkpoint else MF 格式 Trainer->>Trainer: load_checkpoint end Trainer->>inference: generate_from_records 传 model tokenizer records pp_ctx opt PP enabled Trainer->>Trainer: 构造 PipelineInferenceContext end loop 每个 batch 在 generate_from_records 内部 inference->>inference: _sample_shard_info 得 shard_id num_shards alt PP enabled inference->>Schedule: pp_ctx.schedule.run 传相同 kwargs Note right of Schedule: first stage 消费输入 last stage 产 logits else 非 PP 路径 inference->>Model: model construct 传 input_ids position_ids Note right of Model: 不传 labels 即返 logits end inference->>inference: select_last_logits_row 与 gather_vocab_parallel_logits inference->>inference: argmax 取 token, EOS 冻结陪跑 end inference->>Rank0: all_gather_object 汇聚 按 sample_idx 去重排序 Rank0->>Rank0: 落盘 .jsonl / .json / .txt Trainer->>Trainer: barrier 然后 destroy_process_group3.3.2 关键交互要点
端到端时序图(3.3.1)覆盖了构建期与推理期的全链路。下面三点是 PR 实现的关键交互细节,对应
inference.py的热路径:批量解码(
generate_batch):每步将活跃样本右 pad 到最长,一次[bs, L]前向;逐活跃样本经select_last_logits_row(CP)与gather_vocab_parallel_logits(TP)取最后逻辑 logits 行后 argmax。某样本 EOS 或超长即冻结为 pad 行陪跑,输出丢弃;finished[]派生自各 rank 相同的 all-gathered logits,故if all(finished): break判定集体一致,无需节拍器。PP 单步(
PipelineInferenceContext):pp>1 时各 rank 传相同 kwargs 调schedule.run,first stage 消费输入,last stage 产出 logits 并选出 per-sample token,再经broadcast_object_list广播回全部 stage 供下一步 append。src取本线 last stage 的全局 rank(get_rank_list_along_axis("pp")[-1],与has_last同源)——MindSporebroadcast的src语义为全局 rank,多 PP 线下按局部 rank 或世界组广播会错源。ScaledLossPipelineStage.bwd_send_specs覆写返回空 spec list,使调度器为非首 stage 排的 BWD_SEND 步变 no-op,训练路径不受影响。_sample_shard_info+generate_from_recordsfinally 块):dp_replicate > 1且非 PP 时,各副本按shard_id条带式认领样本各自组批解码,节奏独立、副本间零通信;解码完成后all_gather_object跨 world group 汇聚各副本的(payload, text_line)条目,由_merge_shard_entries按sample_idx去重排序(tp/cp siblings 报相同条目取首个),rank 0 按输入顺序落盘。PP 开启时不分片,各 PP 线解码全量记录靠sample_idx去重——功能正确但 DP 并发度浪费。3.4 安全隐私与 DFX 设计
inference:段、run_mode缺省为train时训练行为不变;InferenceConfig是新加字段,训练 / 推理 yaml 互不干扰。Trainer.__init__的is_inference分支;_load_inference_checkpoint独立于 resume 导向的_load_checkpoint,互不污染。is_hf_checkpoint/get_checkpoint_path工具;tokenize 失败逐 rank 记logger.error并写 error 行,不中断整轮推理。is_inference判断均为"推理旁路"方向(推理跳过训练态初始化),不设run_mode: predict时训练路径与既有代码逐行一致。_open_output_file(os.open+tools/utils.FILE_PERMISSION,0o640,对齐静态图BaseTrainer.predict_process);O_CREAT只对新建文件生效,覆盖已有文件不改其权限。3.5 编程与调用设计
3.5.1 编程模型基本设计
Trainer.inference()形参只做语义有意义的覆写);checkpoint.load_path必填(同时作为权重与 tokenizer 目录);PP 推理需interleave_num=1、overlap_b_f=false、swap=false(校验放_validate_pp_inference,fail-fast)。3.5.2 接口定义与设计
3.5.2.1 输入格式
input_data是唯一数据入口,支持五种输入形态,由normalize_input统一归一为list[dict]:单条str(字面 prompt,或文件路径——.jsonl按 jsonl 解析,其他后缀按纯文本每行一条)、单条dict、list[dict]/list[str]。其中结构化输入(单条dict/list[dict]/.jsonl文件)超出静态图推理入口的能力,并带出配套参数dataset_field(record 中 prompt 字段名,默认"instruction",由Trainer.inference()形参确认);静态图纯文本输入无此概念。混合类型 list 与非str/dict/list类型触发TypeError。3.5.2.2 Trainer.inference()
接口描述:PyNative 模型文本生成入口,贪心(argmax)解码。
接口原型:
def inference(self, input_data=None, max_new_tokens=None, output=None, batch_size=None, dataset_field="instruction") -> list:输入/输出参数:
inference.input_data.jsonl/.json/.txt/ 无扩展名(按 txt);None 取 yamlinference.batch_size"instruction"异常处理:
checkpoint.load_path为空 / 缺 tokenizer 文件 /output扩展名不识别 /max_new_tokens ≤ 0/batch_size ≤ 0/run_mode非法时ValueError;run_mode: predict但缺inference:段时ValueError。tokenize 失败的样本以logger.error逐 rank 记录并写 error 行,不中断批量。前向失败(generate_batch抛异常)当前会传播出整批推理,不在容错范围内——与 UC2 "不中断批量" 意图存在偏差。约束说明:需在
is_inference=True的 Trainer 上调用(yamlrun_mode: predict);解码固定贪心 argmax,各 rank 从相同的 all-gathered logits 取同一 token,多卡确定可复现。调用参考代码:
trainer = Trainer(config) # yaml 含 run_mode: predict results = trainer.inference() # 全走 yaml results = trainer.inference(input_data="hello", max_new_tokens=10) # 覆写3.5.2.3 InferenceConfig(yaml 配置项)
接口描述:
inference:段配置项,挂在TrainConfig.inference下。权重与 tokenizer 共用checkpoint.load_path单一入口(HF 权重同目录自带词表;MF 格式权重需将词表文件拷入该目录)。[batch, seq]前向解码的样本数;各 rank 必须共享同一取值.jsonl流式逐行 /.json数组 /.txt(或无扩展名)一行一条run_mode字段挂在TrainConfig顶层(非InferenceConfig),取值train(默认)/predict,与图模式字段名对齐,非法值构造期报错。__post_init__校验:max_new_tokens > 0、batch_size > 0、output扩展名 ∈ {.jsonl,.json,.txt, 无扩展名}。超长输入不做截断:tokenize 后 padded 长度超过model.seq_length的样本按 error 行记录并跳过(fail-fast,与官方 dsv4-flashgenerate.py行为一致)。3.5.2.4 normalize_input 输入形态
接口描述:统一五种输入形态为
list[dict]。str.jsonl按 jsonl 解析,其他后缀按纯文本每行一条dictlist[dict]/list[str]list[str]每条 wrap 成{"instruction": s}3.5.3 使用说明
# 训练 yaml 基础上追加: run_mode: 'predict' # 路由字段,顶层 checkpoint: load_path: /path/to/weights_with_tokenizer # HF ckpt 自带词表;MF ckpt 需将词表文件拷入该目录 inference: input_data: /path/to/prompts.txt # 或 str / dict / list max_new_tokens: 128 batch_size: 8 output: inference_result.jsonlbash mindformers/scripts/msrun_launcher.sh \ "python run_mindformer.py --mode 1 --config test.yaml" 8使用约束:
checkpoint.load_path必填,空则ValueError(推理在随机初始化上跑会产出垃圾输出);该目录须同时含权重与 tokenizer 文件(tokenizer.json/tokenizer_config.json/vocab.json/merges.txt/tokenizer.model/spiece.model任一命中即可),缺文件报错指明。output扩展名不识别则ValueError(.jsonl/.json/.txt/ 无扩展名可配)。vocab_size必须一致(FSDP 按 vocab 分片,不一致 shape mismatch)。.json输出收集到 list 末尾一次性 dump,sample 数 > 10k 有内存压力,大规模用.jsonl。temperature/top_k/top_p/repetition_penalty等采样类参数不在本方案范围(见非目标)。logger.error(tokenize 失败写 error 行)落盘,tokenize 失败不中断整轮推理;前向失败当前会中断整批。4. 测试设计
测试环境:8 卡 Atlas 910B2 msrun,DSV4 tiny 训练 20 步另存的权重;冒烟脚本
predict/_script/run_test.sh。4.1 一致性回归(必跑)
f00efec1d859bca98c22e77e54da2516,作为"行为不变"改动的复现门禁。4.2 多格式输入
str/ txt 文件 /.jsonl文件 /dict/list[str])传入。4.3 输出格式
.jsonl/.json/.txt输出路径;另用非法扩展名(如.xml)触发校验。.jsonl流式逐行写、.json数组一次性 dump、.txt逐行写;非法扩展名构造期ValueError;新建文件权限0o640。4.4 训练回归
inference段、run_mode缺省(默认train),正常训练配置跑训练流。is_inference判断均为"推理旁路"方向,训练路径与 upstream 代码逐行一致。4.5 并行继承矩阵
4.6 DP 样本分流
data_parallel_shard: 2,4 副本)3 条样本;对照纯 FSDP(data_parallel_shard: -1)。4.7 PP 边界
run_mode: predict+ PP enabled,分别置interleave_num > 1/overlap_b_f=true/swap=true;对照pipeline_parallel=1(单模型路径)。_validate_pp_inference入口 fail-fast 拒绝(NotImplementedError),报错信息指向对应开关;pipeline_parallel=1走原单模型路径不受影响。4.8 PP 推理
4.9 PP 组内广播验证
e6bf3a88b9af964318d18735c30dc2cf,且与改造前实现逐字节一致;FSDP8 基线与 PP 输出 9/10 逐字节一致,唯一分叉样本为尾部近 tie 位 token 重排(源于 FSDP 分片归约顺序,与 PP 路径无关)。4.10 Qwen3-8B 真机权重验证
max_new_tokens: 32贪心解码。4.11 单卡 UT
5. 缺点与风险 (可选)
6. 现有技术 (可选)
7. 未解决问题 (可选)