mirror of
https://github.com/hiyouga/LLaMA-Factory.git
synced 2026-09-27 01:45:42 +08:00
[v1] update docs (#10684)
This commit is contained in:
64
docs/zh/developer-guide/architecture_overview.md
Normal file
64
docs/zh/developer-guide/architecture_overview.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 整体架构
|
||||
|
||||
v1 将命令入口、运行流程和可替换实现分开组织。Core 负责连接配置、数据、模型和训练状态,Plugin 为 Core 提供数据加载、模型处理、批处理和分布式训练等具体实现。
|
||||
|
||||
这样划分后,更换数据格式不需要改训练循环,更换分布式后端也不需要重新实现 SFT 损失。任务入口负责把组件组装成一次运行,Core 决定何时执行操作,Plugin 提供被选择的具体操作。
|
||||
|
||||
下文源码路径均相对于 `src/llamafactory/v1/`。
|
||||
|
||||
## 模块分层
|
||||
|
||||
| 模块 | 目录 | 职责 |
|
||||
|------|------|------|
|
||||
| 命令入口 | `launcher.py` | 路由 `sft`、`dpo`、`rm`、`chat` 和 `merge`,并在多设备训练时通过 `torchrun` 重启 |
|
||||
| 参数配置 | `config/` | 解析数据、模型、训练和推理参数 |
|
||||
| 任务入口 | `trainers/`、`samplers/` | 组装 SFT、DPO、RM 或推理流程 |
|
||||
| Core | `core/` | 管理数据索引、模型加载、样本渲染、批处理、训练循环和推理引擎 |
|
||||
| Plugin | `plugins/` | 提供数据、模型和训练器相关的可替换实现 |
|
||||
| 设备抽象 | `accelerator/` | 管理设备、进程组和 DeviceMesh |
|
||||
|
||||
## 调用关系
|
||||
|
||||
```text
|
||||
llamafactory-cli <command> config.yaml
|
||||
→ launcher
|
||||
→ get_args
|
||||
→ 任务入口(run_sft / run_dpo / run_rm / run_chat 等)
|
||||
├── DataEngine
|
||||
│ ├── DataLoaderPlugin
|
||||
│ └── DataConverterPlugin
|
||||
├── ModelEngine
|
||||
│ ├── InitPlugin
|
||||
│ ├── QuantizationPlugin
|
||||
│ ├── PeftPlugin
|
||||
│ └── KernelPlugin
|
||||
├── BaseTrainer
|
||||
│ ├── BatchGenerator → BatchingPlugin
|
||||
│ ├── DistributedPlugin
|
||||
│ └── OptimizerPlugin
|
||||
└── BaseSampler
|
||||
```
|
||||
|
||||
上图表示组件依赖,实际初始化次序由对应任务入口决定。以 `trainers/sft_trainer.py` 中的 `run_sft` 为例:先解析参数并初始化 `DistributedInterface`,再构造 DataEngine 和 ModelEngine,将数据集、模型与 Renderer 传给 SFTTrainer,最后调用 `fit()`、`save_model()` 并销毁分布式环境。SFTTrainer 继承 BaseTrainer 的运行循环,并提供 SFT 的损失计算。
|
||||
|
||||
## 数据如何到达训练循环
|
||||
|
||||
```text
|
||||
DataEngine[index] → 标准 Sample(仍是消息结构)
|
||||
Renderer.process_samples(samples) → ModelInput 列表(已分词,尚未组 batch)
|
||||
BatchGenerator → 一个更新步所需的 micro-batch 列表
|
||||
Trainer.compute_loss(batch) → 标量 loss
|
||||
BaseTrainer.fit() → 反向传播、参数更新与生命周期事件
|
||||
```
|
||||
|
||||
ModelEngine 同时提供模型和与模型匹配的 Renderer,BatchGenerator 持有这个 Renderer。这样,数据源的读取与字段转换可以独立于模型的 tokenizer、chat template 和多模态 processor。
|
||||
|
||||
## 状态与实现分别由谁管理
|
||||
|
||||
DataEngine 保存数据集和索引,ModelEngine 保存加载结果,BatchGenerator 保存读取进度与缓冲区,BaseTrainer 保存模型、优化器和训练进度。它们都是一次运行中的对象。
|
||||
|
||||
插件注册表保存函数或类对象。调用方每次把模型、配置或缓冲区传给插件,并接收处理结果;插件路由对象本身只记录实现名称。例如,分布式插件可以返回有状态的后端 engine,由 BaseTrainer 持有,但注册表本身不保存某次训练的 engine。
|
||||
|
||||
判断改动位置时,先区分是在改变任务目标、组件流程,还是替换某个操作:新损失属于 Trainer,统一的数据索引逻辑属于 DataEngine,新的原始字段转换属于 DataConverterPlugin。具体扩展边界分别在下方页面说明。
|
||||
|
||||
Core 各组件见[Core(核心模块)](core/index.md),插件的注册方式见[插件注册机制](baseplugin_mechanism.md),内置实现见[插件实现](plugins/index.md)。
|
||||
132
docs/zh/developer-guide/baseplugin_mechanism.md
Normal file
132
docs/zh/developer-guide/baseplugin_mechanism.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# 插件注册机制
|
||||
|
||||
`BasePlugin` 位于 `utils/plugin.py`,负责按名称注册和查找实现。每个 `BasePlugin` 子类表示一个独立的插件类型,并拥有自己的 `_registry`,例如 `OptimizerPlugin` 和 `DistributedPlugin` 的注册项互不共享。
|
||||
|
||||
## 插件类型、路由对象与实现
|
||||
|
||||
以 `DistributedPlugin("fsdp2").shard_model(...)` 为例,这里有三个不同对象:
|
||||
|
||||
| 对象 | 职责 |
|
||||
|------|------|
|
||||
| `DistributedPlugin` | 插件类型,持有这个类型的名称注册表 |
|
||||
| `DistributedPlugin("fsdp2")` | 路由对象,仅保存要查找的名称 |
|
||||
| 注册在 `fsdp2` 下的实现类 | 提供 `shard_model` 等实际操作 |
|
||||
|
||||
创建路由对象只保存名称,不会立即加载或创建后端 engine。第一次调用函数或访问实现方法时,`_resolve` 才从该类型的注册表中查找对象。模型、优化器、buffer 等运行状态由调用方持有并作为参数传入。
|
||||
|
||||
## 注册与调用流程
|
||||
|
||||
```text
|
||||
定义插件类型
|
||||
→ PluginType(BasePlugin)
|
||||
→ PluginType("name").register()
|
||||
→ 将函数或类对象写入 PluginType._registry
|
||||
→ PluginType("name") 按名称查找实现
|
||||
→ 通过 __call__ 或 __getattr__ 调用
|
||||
```
|
||||
|
||||
装饰器在模块导入时完成注册。注册名称必须存在;查找未注册的名称时会抛出 `ValueError`。同一插件类型重复注册相同名称时会记录警告,并使用后注册的实现。
|
||||
|
||||
注册表不会自动扫描文件或按 YAML 名称导入 Python 模块。内置实现通过其入口模块的正常导入完成注册,例如 Kernel 入口显式导入各实现。新增实现也必须在被调用前执行所在模块的导入,仅添加文件或写入配置名称不会触发注册。
|
||||
|
||||
## 函数实现
|
||||
|
||||
一个插件名称只对应一个操作时,直接注册函数。调用 `PluginType("name")(...)` 时,`BasePlugin.__call__` 查找并调用该函数。
|
||||
|
||||
```python
|
||||
class OptimizerPlugin(BasePlugin):
|
||||
pass
|
||||
|
||||
|
||||
@OptimizerPlugin("example").register()
|
||||
def create_optimizer(model, config):
|
||||
return ExampleOptimizer(model.parameters(), lr=config["learning_rate"])
|
||||
|
||||
|
||||
optimizer = OptimizerPlugin("example")(model, config)
|
||||
```
|
||||
|
||||
数据加载、数据转换、模型初始化、PEFT 和优化器等插件使用这种形式。插件类型也可以提供语义化方法,在方法内部调用 `super().__call__`,例如 `DataLoaderPlugin.load(...)`。
|
||||
|
||||
## 类实现(静态方法组)
|
||||
|
||||
同一插件名称需要提供多个相关操作时,注册一个类对象。`BasePlugin.__getattr__` 先查找该类,再把方法访问转发给它。
|
||||
|
||||
```python
|
||||
@DistributedPlugin("example").register()
|
||||
class ExampleDistributed(BaseDistributed):
|
||||
@staticmethod
|
||||
def shard_model(model, dist_config, **kwargs):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def save_checkpoint(model, optimizer, checkpoint_dir, **kwargs):
|
||||
...
|
||||
|
||||
|
||||
model = DistributedPlugin("example").shard_model(model, dist_config)
|
||||
DistributedPlugin("example").save_checkpoint(model, optimizer, checkpoint_dir)
|
||||
```
|
||||
|
||||
通过 `.method(...)` 访问时,路由会取出注册类上的方法,不创建该类的实例。因此,这类实现使用 `staticmethod` 或 `classmethod`,不在 `self` 中保存运行状态:
|
||||
|
||||
| 方法形式 | 调用时接收 | 用途 |
|
||||
|----------|------------|------|
|
||||
| `staticmethod` | 只接收显式参数 | 实现不依赖类本身的独立操作,例如保存 checkpoint |
|
||||
| `classmethod` | 首个参数为 `cls` | 需要调用子类方法的公共流程,例如 `BaseKernel.apply` |
|
||||
| 普通实例方法 | 首个参数为 `self` | 需要先创建实例,不适用于当前类对象路由 |
|
||||
|
||||
Python 没有单独的“静态类”类型;这里使用的是包含静态方法或类方法的普通类。分布式后端、批处理策略和 Kernel 使用这种方法组形式。对应抽象基类声明的也是静态方法或类方法,`ensure_methods_implemented` 在具体子类定义时检查这些方法是否完整。
|
||||
|
||||
## 两种实现形式的选择
|
||||
|
||||
| 实现形式 | 注册对象 | 调用入口 | 适用情况 |
|
||||
|----------|----------|----------|----------|
|
||||
| 函数 | 函数对象 | `PluginType("name")(...)` | 一个名称对应一个操作 |
|
||||
| 类方法组 | 类对象 | `PluginType("name").method(...)` | 一个名称需要提供多个相关操作 |
|
||||
|
||||
两种形式使用相同的名称注册表。区别只在于注册对象和调用方式,不影响配置文件通过 `name` 选择实现。
|
||||
|
||||
## 参数解析
|
||||
|
||||
注册过程只完成名称到实现的映射,不会自动解析参数。参数配置经过两个不同阶段:
|
||||
|
||||
1. `config/arg_utils.py` 的 `get_plugin_config` 将顶层参数中的插件配置包装成 PluginConfig,并检查是否有 `name`。这时并未根据名称验证插件专属字段。
|
||||
2. 调用进入具体实现后,由实现解析自己的字段。PEFT 和分布式入口使用参数 dataclass 与 `parse_params`;Muon 等实现直接读取配置。插件类型本身没有为所有实现统一选择参数类。
|
||||
|
||||
使用参数 dataclass 的入口可以显式调用 `parse_params(config, ParamsClass)`:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
|
||||
class ExamplePlugin(BasePlugin):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExampleParams:
|
||||
name: Literal["example"] = "example"
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
@ExamplePlugin("example").register()
|
||||
def apply_example(model, config):
|
||||
params = ExamplePlugin.parse_params(config, ExampleParams)
|
||||
if params.enabled:
|
||||
...
|
||||
```
|
||||
|
||||
`parse_params` 的处理规则如下:
|
||||
|
||||
- `config` 为目标 dataclass 实例时直接返回;
|
||||
- `config` 为字典时检查字段名,再创建参数实例;
|
||||
- `config` 为 `None` 时使用 dataclass 默认值创建实例;
|
||||
- 未声明的字段会抛出 `ValueError`;
|
||||
- 参数类不是 dataclass,或配置不是字典、`None`、目标 dataclass 实例时,会抛出 `TypeError`;
|
||||
- 取值范围和字段组合可以在参数 dataclass 的 `__post_init__` 中继续校验。
|
||||
|
||||
这里的严格检查主要针对未知字段和配置对象的形式;Python dataclass 不会自动强制执行每个类型注解或 `Literal` 的可选值,额外约束需要实现显式校验。
|
||||
|
||||
参数 dataclass 由具体插件入口选择,因此不同实现可以使用不同字段。用户可配置字段记录在对应的[参数说明](../configuration/index.md)中。
|
||||
47
docs/zh/developer-guide/core/base_sampler.md
Normal file
47
docs/zh/developer-guide/core/base_sampler.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# BaseSampler
|
||||
|
||||
`BaseSampler` 位于 `core/base_sampler.py`,为推理调用方提供生成接口,并持有具体的推理 engine。模型和 Renderer 由 [ModelEngine](model_engine.md) 创建后传入;Sampler 负责选择后端和转发调用。
|
||||
|
||||
## 组件与状态
|
||||
|
||||
| 组件 | 职责 | 源码 |
|
||||
|------|------|------|
|
||||
| BaseSampler | 根据 `sample_backend` 选择 engine,转发生成请求 | `core/base_sampler.py` |
|
||||
| BaseEngine / HuggingFaceEngine | 定义后端接口,使用模型和 Renderer 执行生成 | `core/utils/inference_engine.py` |
|
||||
| SyncSampler | 将异步生成接口适配为 CLI 使用的同步迭代器 | `samplers/cli_sampler.py` |
|
||||
| `run_chat` | 创建组件,读取用户输入,保存对话历史并输出回复 | `samplers/cli_sampler.py` |
|
||||
|
||||
当前 BaseSampler 直接构造 HuggingFaceEngine,后端选择不经过 BasePlugin 注册表。HuggingFaceEngine 保存模型、Renderer 和推理参数;对话历史由 `run_chat` 中的消息列表持有,每次请求传入完整历史。
|
||||
|
||||
## 单次生成路径
|
||||
|
||||
```text
|
||||
run_chat:追加 user 消息
|
||||
→ SyncSampler.generate(messages)
|
||||
→ BaseSampler.generate(messages, tools)
|
||||
→ HuggingFaceEngine.generate(messages, tools)
|
||||
→ Renderer.render_messages(..., is_generate=True)
|
||||
→ input_ids / attention_mask 转为设备上的张量
|
||||
→ 后台线程调用 model.generate
|
||||
→ AsyncTextIteratorStreamer 异步返回文本片段
|
||||
→ CLI 逐段输出,拼成 assistant 消息并加入历史
|
||||
```
|
||||
|
||||
HuggingFaceEngine 用信号量限制同时进入生成过程的请求数。它将 `max_new_tokens` 和 streamer 传给 `model.generate`,streamer 跳过 prompt 和特殊 token;返回值是解码后的文本片段,不是 token ID,也不保证每个片段恰好对应一个 token。
|
||||
|
||||
SyncSampler 另行维护后台事件循环,逐次等待异步生成器的下一段文本,再交给同步调用方。这个适配层改变调用方式,不改变消息渲染或模型生成逻辑。
|
||||
|
||||
## 接口与当前支持范围
|
||||
|
||||
| 接口 | 输入与输出 | 当前行为 |
|
||||
|------|------------|----------|
|
||||
| `generate(messages, tools=None)` | 标准 Message 列表、可选 tools 字符串;异步产出 `str` | Hugging Face 流式生成 |
|
||||
| `batch_infer(dataset)` | Dataset;约定返回 Sample 列表 | HuggingFaceEngine 抛出 `NotImplementedError` |
|
||||
|
||||
BaseSampler 的非 HF 后端分支会抛出 `ValueError`。CLI 中设置 `train_dataset` 会进入批量推理分支,但当前 engine 尚未实现该接口。因此,已实现的用户路径是 HF 交互式对话,启动方式见[推理](../../feature-guide/inference.md),配置字段见[推理参数](../../configuration/inference.md)。
|
||||
|
||||
## 扩展边界
|
||||
|
||||
新增推理后端时,实现 `BaseEngine` 的接口,并接入 BaseSampler 的后端选择;仅添加文件或写入新的 `sample_backend` 名称不会完成接入。后端需要保持异步文本流接口,并明确批量推理是否可用,SyncSampler 才能沿用现有适配方式。
|
||||
|
||||
改变模板和消息到模型输入的转换时,修改 [Renderer](renderer.md);改变生成参数的传递、模型调用或输出流时,修改 engine;改变命令行输入和对话历史管理时,修改 `run_chat`。当前 HF 生成调用只传递 Renderer 结果中的 `input_ids` 和 `attention_mask`,扩展其他模型输入时还需要补齐 engine 向模型传参的路径。
|
||||
50
docs/zh/developer-guide/core/base_trainer.md
Normal file
50
docs/zh/developer-guide/core/base_trainer.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# BaseTrainer
|
||||
|
||||
`BaseTrainer` 提供 SFT、DPO 和 RM 共用的训练生命周期。子类主要实现 `compute_loss`,必要时覆盖模型分片或输入处理。
|
||||
|
||||
实现位于 `core/base_trainer.py`。任务入口负责加载模型与数据,再把它们传入 Trainer;BaseTrainer 从这些对象开始组织批次生成、优化器更新和训练状态。这样,各训练目标可以共用同一套运行循环。
|
||||
|
||||
## 初始化训练组件
|
||||
|
||||
先保存参数、模型、Renderer 与数据集,创建 BatchGenerator,再确定训练总步数和 checkpoint 间隔。后续组件的创建顺序取决于后端:
|
||||
|
||||
- FSDP2 / FSDPTurbo 先分片模型,再基于处理后的参数创建 optimizer 和 scheduler;没有显式分布式后端时,使用普通模型或 DDP。
|
||||
- DeepSpeed 先构造后端 engine,再创建 optimizer 和 scheduler,最后由 engine 的 `prepare` 统一准备训练对象。
|
||||
|
||||
模型和优化器就绪后,`TrainingCheckpointCoordinator` 才能恢复它们的状态以及批次进度。之后创建 CallbackHandler 和 TrainerState,并把已恢复的 step、epoch 同步给回调状态。CP 模型处理在初始化的最后阶段接入。
|
||||
|
||||
## 训练循环
|
||||
|
||||
```text
|
||||
epoch / global step
|
||||
→ BatchGenerator
|
||||
→ forward + compute_loss
|
||||
→ gradient accumulation
|
||||
→ gradient clipping
|
||||
→ optimizer.step
|
||||
→ scheduler.step
|
||||
→ callback / logging
|
||||
→ 可选 checkpoint
|
||||
```
|
||||
|
||||
`global_batch_size / (dp_size × micro_batch_size)` 决定梯度累积所需的 micro-batch 数。
|
||||
|
||||
BatchGenerator 每次迭代返回一组 micro-batch,对应一次更新步。普通路径对每个 micro-batch 调用 `compute_loss` 并反向传播,累积完成后执行梯度裁剪、参数更新和清零。`compute_loss` 返回标量损失,BaseTrainer 还会根据有效监督 token 数和 DP 规模缩放损失,因此子类不应自行重复执行参数更新。
|
||||
|
||||
DeepSpeed 路径把反向传播和同步边界交给后端 engine;SFT 开启 CP 时,循环改为调用 SequenceParallelLossPlugin。扩展损失时需要同时考虑目标任务是否支持这两条路径。
|
||||
|
||||
## 训练器实现
|
||||
|
||||
- `SFTTrainer`:带 `loss_weights` 的语言模型损失
|
||||
- `DPOTrainer`:policy/reference 偏好损失
|
||||
- `RMTrainer`:chosen/rejected reward 排序损失
|
||||
|
||||
## 保存模型与 Checkpoint
|
||||
|
||||
训练循环在保存 step 调用 `core/utils/checkpoint.py` 中的 TrainingCheckpointCoordinator。它协调模型、优化器、scheduler、批次进度与训练计数等状态;其中依赖分片格式的模型和优化器读写交给 DistributedPlugin。这样,训练器决定何时保存,后端决定如何保存其分布式状态。
|
||||
|
||||
`fit()` 结束后,任务入口另外调用 `save_model()` 保存最终模型。最终模型与用于续训的 checkpoint 用途不同,用户配置见[模型保存与恢复](../../feature-guide/model_saving.md)。
|
||||
|
||||
## 扩展边界
|
||||
|
||||
新增训练目标时,在 Trainer 子类中实现损失和必要的任务准备。替换优化器、批处理或分布式实现时使用[训练器插件](../plugins/trainer_plugins.md),避免在每个任务的训练循环中复制后端分支。只观察生命周期事件时使用 [Callback](callback.md)。
|
||||
45
docs/zh/developer-guide/core/batch_generator.md
Normal file
45
docs/zh/developer-guide/core/batch_generator.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# BatchGenerator
|
||||
|
||||
`core/utils/batching.py` 负责 sampler、DataLoader、collate、梯度累积批次和状态恢复;`trainer_plugins/batching.py` 提供非 normal 策略。
|
||||
|
||||
## 输入、输出与状态
|
||||
|
||||
BatchGenerator 接收 DataEngine、Renderer 和批处理参数。内部的 StatefulDataLoader 按 DP 维度分配样本,并用 `Renderer.process_samples` 把读出的消息转换为 ModelInput 列表。此时还没有完成训练 batch 的组装。
|
||||
|
||||
这些 ModelInput 进入 StatefulBuffer,再由选定策略取出、截断并组织为 BatchInput。`__next__()` 返回的是一个更新步所需的 micro-batch 列表,BaseTrainer 逐个计算损失和累积梯度。
|
||||
|
||||
```text
|
||||
DataEngine + StatefulDistributedSampler
|
||||
→ StatefulDataLoader(Renderer.process_samples)
|
||||
→ StatefulBuffer
|
||||
→ normal collate 或 BatchingPlugin.generate_batch
|
||||
→ list[BatchInput]
|
||||
```
|
||||
|
||||
BatchGenerator 持有 DataLoader、迭代器和缓冲区;策略插件接收缓冲区和 `batch_info`,决定如何填充与取样。读取进度因此不保存在插件注册表中。
|
||||
|
||||
## Normal Batching
|
||||
|
||||
固定取 `micro_batch_size` 个样本,Renderer 处理后按当前 batch 最长序列 padding。
|
||||
|
||||
## Padding-Free Batching
|
||||
|
||||
`BatchingPlugin("padding_free")` 将多个样本拼成无 padding 的序列,并维护能够隔离文档的 attention/position 信息。
|
||||
|
||||
## Dynamic Batching
|
||||
|
||||
动态策略根据样本 token 数决定每个 batch 包含多少样本。纯 `dynamic_batching` 仍执行 padding;`dynamic_padding_free` 同时拼接序列。
|
||||
|
||||
## 保存批次状态
|
||||
|
||||
`state_dict()` 同时保存 DataLoader 的读取状态与 buffer 中已读取、尚未消费的样本。只恢复前者会丢掉预读样本,只恢复后者则无法确定接下来应该读哪里。
|
||||
|
||||
`load_state_dict()` 恢复两部分状态并设置 `_is_resuming`。普通 `__iter__()` 会清空 buffer;恢复后的首次迭代保留 buffer,随后清除恢复标记。新增策略必须保持这个状态约定,不能将剩余样本藏在无法随 checkpoint 保存的全局变量中。
|
||||
|
||||
## 校验动态批处理参数
|
||||
|
||||
纯 `dynamic_batching` 需要正数 `max_steps` 且不能使用 `save_epochs`,这些条件在 `TrainingArguments.__post_init__` 中校验。多模态输入是否能由当前策略处理,则在 BatchGenerator 生成 batch 时检查。配置条件与使用选择见[批处理策略](../../feature-guide/batching.md)。
|
||||
|
||||
## 扩展边界
|
||||
|
||||
改变 token 预算或样本拼接方式时实现 BatchingPlugin 的方法组,接口见[训练器插件](../plugins/trainer_plugins.md#batchingplugin)。改变消息如何变成 token 时修改 Renderer,改变反向传播和更新边界时检查 BaseTrainer。
|
||||
39
docs/zh/developer-guide/core/callback.md
Normal file
39
docs/zh/developer-guide/core/callback.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Callback
|
||||
|
||||
Callback 系统位于 `utils/callbacks/`,用于把日志和生命周期通知从训练循环中分离。
|
||||
|
||||
## Callback 组件
|
||||
|
||||
- `TrainerCallback`:事件接口
|
||||
- `CallbackHandler`:按注册顺序广播事件
|
||||
- `LoggingCallback`:输出 loss、learning rate、grad norm 等指标
|
||||
|
||||
## 事件如何传递
|
||||
|
||||
BaseTrainer 创建 CallbackHandler,并先加入默认 LoggingCallback,再加入构造参数 `callbacks` 中的实例。训练循环在固定位置调用 handler;handler 按列表顺序同步调用每个实例的同名方法,不经过 BasePlugin 注册表。
|
||||
|
||||
```text
|
||||
on_train_begin
|
||||
→ on_epoch_begin
|
||||
→ on_step_begin
|
||||
→ 前向、反向与更新
|
||||
→ on_step_end
|
||||
→ on_log(到达 logging_steps 时)
|
||||
→ on_save(checkpoint 写入后)
|
||||
→ on_epoch_end
|
||||
on_train_end
|
||||
```
|
||||
|
||||
事件位置由 Trainer 和 checkpoint 协调器决定。Callback 无需自行判断什么时候保存模型或推进优化器,只响应已经发生的生命周期事件。
|
||||
|
||||
## Callback 能读取什么
|
||||
|
||||
每个事件接收训练参数 `args`、进度对象 `state` 和额外关键字参数。TrainerState 保存 step、epoch、最近一次损失、梯度范数和学习率等;Handler 还通过关键字参数传入当前 model、optimizer、lr_scheduler 和批次生成器。
|
||||
|
||||
在 `on_step_begin` 时,本步计算尚未发生,损失等指标仍来自此前的更新;本步指标在 `on_step_end` 前写入 state。`on_log` 额外接收本次日志字典。
|
||||
|
||||
## 扩展边界
|
||||
|
||||
自定义 Callback 继承 TrainerCallback,仅覆盖关心的事件,并将实例传给 Trainer 的 `callbacks` 参数。它适合记录指标、连接外部观察工具等附加行为。需要改变损失、更新规则或 checkpoint 格式时,使用 Trainer 或对应插件。
|
||||
|
||||
接口约定将 state 作为供观察的进度信息,不通过修改它来控制训练流程;代码没有把这个 dataclass 冻结。事件也不会统一限制到 rank 0,涉及单次输出的 Callback 需要自行判断 rank,默认 LoggingCallback 就在自身内部处理这个条件。
|
||||
39
docs/zh/developer-guide/core/data_engine.md
Normal file
39
docs/zh/developer-guide/core/data_engine.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# DataEngine
|
||||
|
||||
`DataEngine(dataset_path: str)` 位于 `core/data_engine.py`,把一个或多个原始数据集暴露为可按整数索引访问的统一 Dataset。它处理数据来源、字段转换和样本索引,返回的仍是消息结构;分词和监督标签由后续 [Renderer](renderer.md)生成。
|
||||
|
||||
## 加载数据集
|
||||
|
||||
```text
|
||||
dataset_path
|
||||
→ 识别 YAML、目录、数据文件或 Hub ID
|
||||
→ 生成 dataset name → DatasetInfo 映射
|
||||
→ hf_hub 直接加载,其他 source 调用 DataLoaderPlugin.load
|
||||
→ 逐条转换样本,确定 SFT 的受监督轮次
|
||||
→ 建立 (dataset_name, sample_index, cut) 全局索引
|
||||
→ 根据 size / weight 调整索引
|
||||
```
|
||||
|
||||
初始化时,`datasets` 保存各来源加载出的 Dataset,`dataset_infos` 保存来源配置,`data_index` 保存训练样本到原始行的映射。索引项中的 `cut` 表示取消息列表的哪个前缀;它不是 token 截断长度。
|
||||
|
||||
多轮 SFT 的每个受监督 assistant turn 对应一个索引项,多个索引项可以指向同一条原始对话。DPO 等非 SFT 样本保持完整,`cut` 为 `None`。这一设计让 sampler 能按训练样本分配索引,同时保留原始数据集。
|
||||
|
||||
## 读取一个样本
|
||||
|
||||
`__getitem__` 从全局索引取出数据集名称、原始行号和 `cut`,再由 `_get` 读取原始行、调用 converter,并按 `cut` 截取消息前缀。返回样本带有 `_dataset_name`,供后续处理识别来源。
|
||||
|
||||
Converter 既会在初始化建立索引时调用,也会在实际取样时调用;DataEngine 不把索引阶段的转换结果缓存为新的数据集。因此转换函数应对相同输入产生一致的消息结构,避免依赖调用次数或在转换中改变原始行,否则索引记录的轮次可能与训练读取的内容不一致。
|
||||
|
||||
## 解析 DatasetInfo
|
||||
|
||||
字段结构定义在 `utils/types.py`。`_get_dataset_info` 将路径、Hub ID 或 YAML 统一成名称到 DatasetInfo 的映射,后续加载逻辑只处理这个映射。字段与默认值见[数据参数](../../configuration/data.md#datasetinfo)。
|
||||
|
||||
## 处理 Streaming Dataset
|
||||
|
||||
所有数据集必须同时为 map-style 或同时为 streaming。混合模式无法共享同一种索引和 sampler 语义,因此 DataEngine 初始化阶段会拒绝。当前 BatchGenerator 只支持 map-style dataset;streaming dataset 进入批处理初始化时会抛出 `NotImplementedError`。
|
||||
|
||||
## 扩展数据源与格式
|
||||
|
||||
加载新来源时,扩展 `DataLoaderPlugin`,保持返回 Dataset 的约定;只改变原始字段的含义时,扩展 `DataConverterPlugin`,保持返回标准 Sample 的约定。两种接口及其调用参数见[数据插件](../plugins/data_plugins.md)。
|
||||
|
||||
多轮展开和全局索引属于 DataEngine,共用索引行为应在此处理。模型模板、tokenization 和 padding 分别属于 Renderer 与 BatchGenerator,不放入 loader 或 converter。用户用法见[数据准备](../../feature-guide/data_preparation.md)。
|
||||
26
docs/zh/developer-guide/core/index.md
Normal file
26
docs/zh/developer-guide/core/index.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Core(核心模块)
|
||||
|
||||
Core 负责连接配置、数据、模型和训练流程。各组件由 Trainer 或 Sampler 组合,不通过插件注册表选择。
|
||||
|
||||
| 组件 | 职责 | 源码 |
|
||||
|------|------|------|
|
||||
| [DataEngine](data_engine.md) | 加载、转换和索引数据集 | `core/data_engine.py` |
|
||||
| [ModelEngine](model_engine.md) | 加载 Processor、Renderer 和模型 | `core/model_engine.py` |
|
||||
| [Renderer](renderer.md) | 将 Messages 转换为模型输入 | `core/rendering/` |
|
||||
| [BaseTrainer](base_trainer.md) | 管理通用训练生命周期 | `core/base_trainer.py` |
|
||||
| [BaseSampler](base_sampler.md) | 选择推理后端并连接生成接口 | `core/base_sampler.py` |
|
||||
| [BatchGenerator](batch_generator.md) | 生成批次并恢复批次状态 | `core/utils/batching.py` |
|
||||
| [Callback](callback.md) | 分发训练生命周期事件 | `utils/callbacks/` |
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
data_engine
|
||||
model_engine
|
||||
renderer
|
||||
base_trainer
|
||||
base_sampler
|
||||
batch_generator
|
||||
callback
|
||||
```
|
||||
46
docs/zh/developer-guide/core/model_engine.md
Normal file
46
docs/zh/developer-guide/core/model_engine.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# ModelEngine
|
||||
|
||||
`ModelEngine(model_args, is_train=False)` 拥有 processor、model config、Renderer 和最终 Hugging Face model。
|
||||
|
||||
实现位于 `core/model_engine.py`。它把同一份模型配置转为相互匹配的模型与输入处理组件;任务入口随后把 `model` 和 `renderer` 交给 Trainer 或 Sampler。训练循环、优化器创建和常规分布式分片在后续组件中完成。
|
||||
|
||||
## 模型加载流程
|
||||
|
||||
```text
|
||||
AutoProcessor.from_pretrained
|
||||
→ 同步或覆盖 chat_template
|
||||
→ AutoConfig.from_pretrained
|
||||
→ Renderer(processor)
|
||||
→ 选择初始化设备
|
||||
→ 应用量化加载参数
|
||||
→ 选择 AutoModel 类
|
||||
→ from_pretrained / from_config
|
||||
→ PEFT
|
||||
→ Kernel
|
||||
```
|
||||
|
||||
这个顺序由 ModelEngine 固定:初始化插件先决定在哪里创建模型,量化插件再补充权重加载参数;模型对象创建后,PEFT 决定可训练部分或加载 adapter,Kernel 最后处理实际得到的模型。插件之间不互相调度,后一阶段接收前一阶段的结果。
|
||||
|
||||
`is_train` 会传给量化和 PEFT 路径,并影响未启用 PEFT 时的模型精度处理。它表达本次加载的用途,训练循环仍会在 `fit()` 中设置模型的训练模式。
|
||||
|
||||
## 同步 Chat Template
|
||||
|
||||
多模态 processor 没有模板时,会从其 tokenizer 同步。`custom_chat_template` 则覆盖 processor/tokenizer 模板。Renderer 最终调用 `apply_chat_template`,不再导入模型专属 Python 模板。
|
||||
|
||||
## 选择 Hugging Face 模型类
|
||||
|
||||
- `llm`:根据模型 config 选择 image-to-text、多模态语言模型或因果语言模型类;多模态语言模型类仅在当前 Transformers 提供时参与选择
|
||||
- `cls`:单标签 token classification 模型,RM 使用
|
||||
- `other`:`AutoModel`
|
||||
|
||||
## Meta 与 ZeRO-3 初始化
|
||||
|
||||
普通路径通过 `from_pretrained` 加载权重。选择 meta device 后,ModelEngine 使用 `init_empty_weights()` 和 `from_config` 构造模型结构,此时参数没有真实数据,需要后续分布式加载路径填充。`init_on_rank0` 则让 rank 0 在 CPU 加载,其余 rank 构造 meta 模型。
|
||||
|
||||
Meta 初始化不能与量化同时使用;当前 LoRA 路径也会拒绝 `init_on_meta`。这些检查发生在模型加载阶段,不能通过调整 YAML 中字段的书写顺序绕过。
|
||||
|
||||
DeepSpeed ZeRO-3 需要在 `from_pretrained` 创建参数时介入,因此 ModelEngine 在加载前建立 ZeRO-3 上下文,并在加载后清理;后续 BaseTrainer 再准备训练 engine。它与 FSDP2 在 Trainer 中对已构造模型进行分片的时机不同。
|
||||
|
||||
## 扩展边界
|
||||
|
||||
改变加载顺序或 AutoModel 类选择时,修改 ModelEngine;增加一种初始化、PEFT 或量化实现时,扩展相应[模型插件](../plugins/model_plugins.md)。只需改变消息格式时,使用 chat template 或修改 Renderer 的转换逻辑,无需增加一种模型加载插件。
|
||||
51
docs/zh/developer-guide/core/renderer.md
Normal file
51
docs/zh/developer-guide/core/renderer.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Renderer
|
||||
|
||||
当前 Renderer 位于 `core/rendering/`,把 v1 `Sample` 转成 tokenized `ModelInput`。它不再依赖 `RenderingPlugin` 或模型名称模板表。
|
||||
|
||||
## 在数据路径中的位置
|
||||
|
||||
DataEngine 负责返回标准消息结构,ModelEngine 提供与模型匹配的 processor 并创建 Renderer。BatchGenerator 的 DataLoader 调用 `Renderer.process_samples(samples)`,得到已分词的样本列表,再执行批次组装。
|
||||
|
||||
Renderer 的结果包含 `input_ids`、`attention_mask`、`labels`、`loss_weights` 和位置信息;多模态样本还携带 processor 产生的媒体特征。它负责表达模型输入和监督范围,batch 内的 padding、拼接以及长度截断由后续批处理承担。
|
||||
|
||||
## Chat Template 的来源
|
||||
|
||||
1. 使用 processor/tokenizer 自带 `chat_template`。
|
||||
2. `custom_chat_template` 可以在 ModelEngine 中覆盖它。
|
||||
3. 模型完全没有模板时使用内置 ChatML fallback。
|
||||
|
||||
## 生成训练标签
|
||||
|
||||
多轮对话由 DataEngine 按每个受监督 assistant turn 切分为训练样本(见[DataEngine](data_engine.md))。Renderer 对样本最后一个 assistant turn 分别渲染 prompt 和完整序列,通过两者前缀差恢复监督 token 区间,不维护模型专属 role marker 表。
|
||||
|
||||
```text
|
||||
messages before assistant + generation prompt → prompt ids
|
||||
messages including assistant response → full ids
|
||||
full ids 中超出 prompt 的尾部 → supervised span
|
||||
```
|
||||
|
||||
这种方式兼容模型在历史消息中处理 reasoning 内容的差异。
|
||||
|
||||
该方法要求 prompt 的 token 序列与完整序列的前缀完全一致。Renderer 会实际比较两次编码的结果;如果模板在追加最后一轮后改写了前面的内容,会抛出错误,避免把上下文 token 当作回复标签。
|
||||
|
||||
前缀部分的 `labels` 为 `IGNORE_INDEX`、`loss_weights` 为零;最后回复按该轮的监督权重设置 `loss_weights`,有监督的位置使用实际 token ID 作为 label。生成模式则添加 generation prompt,并把所有位置设为不参与监督。
|
||||
|
||||
## 处理多模态内容
|
||||
|
||||
Renderer 先把标准内容块转换为 Hugging Face 消息结构,再通过 chat template 生成文本。存在媒体内容时,processor 接收文本与图片、视频或解码后的音频,并返回 token 与媒体特征。Renderer 把这些特征和媒体位置标记一并传给批处理,保持文本与媒体对应。
|
||||
|
||||
## 拼接 Chosen/Rejected 序列
|
||||
|
||||
chosen 与 rejected 分别渲染后拼接,并使用 `token_type_ids` 标记两个文档。DPO/RM 根据标记构造 block-diagonal attention 和各自 position ids,避免 rejected 序列读取 chosen 序列。
|
||||
|
||||
Renderer 为两段序列分别从起点生成 position IDs,并用 `token_type_ids` 的 `1`、`2` 保留段边界。物理拼接只改变存储布局;两段仍是独立回答,任务 Trainer 需要保留这个隔离语义。
|
||||
|
||||
## 转义特殊 Token
|
||||
|
||||
`core/rendering/escape.py` 在渲染用户控制的文本和 tools 前处理中和 tokenizer 的特殊 token 字符串,避免输入直接注入特殊 token。
|
||||
|
||||
## 自定义 Chat Template
|
||||
|
||||
新模型通常应在模型仓库的 tokenizer 配置中提供标准 HF chat template。仅在运行时覆盖时使用 `custom_chat_template`,无需注册 Python 插件。
|
||||
|
||||
若原始数据字段需要变化,扩展 converter;若标准消息如何映射到模型输入发生变化,再检查 `rendering/format.py` 和 `rendering/rendering.py`。转换原始字段与生成监督标签分属不同阶段,避免在 converter 中依赖模型的 token ID。
|
||||
20
docs/zh/developer-guide/index.md
Normal file
20
docs/zh/developer-guide/index.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 开发者指南
|
||||
|
||||
开发者指南说明 v1 的运行路径和扩展点。v1 由 Core(核心模块)组织主要运行流程,并通过 Plugin(插件系统)提供可替换实现。
|
||||
|
||||
## 架构
|
||||
|
||||
| 页面 | 内容 |
|
||||
|------|------|
|
||||
| [整体架构](architecture_overview.md) | 模块分层以及 Core 与 Plugin 的调用关系 |
|
||||
|
||||
## Core(核心模块)
|
||||
|
||||
[Core(核心模块)](core/index.md)说明数据加载、模型加载、样本渲染、批处理和训练循环。
|
||||
|
||||
## Plugin(插件系统)
|
||||
|
||||
| 页面 | 内容 |
|
||||
|------|------|
|
||||
| [插件注册机制](baseplugin_mechanism.md) | `BasePlugin` 的注册、路由和参数解析 |
|
||||
| [插件实现](plugins/index.md) | 数据、模型和训练器插件,以及融合算子加速 |
|
||||
57
docs/zh/developer-guide/plugins/data_plugins.md
Normal file
57
docs/zh/developer-guide/plugins/data_plugins.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 数据插件
|
||||
|
||||
数据插件把数据来源和原始字段格式从 DataEngine 的索引逻辑中分离。Loader 负责获得可读取的数据集,Converter 负责解释其中的一条记录;两者由不同配置字段选择,可以组合使用。
|
||||
|
||||
## 与 DataEngine 的交接
|
||||
|
||||
```text
|
||||
DatasetInfo.source
|
||||
→ hf_hub:DataEngine 直接调用 datasets.load_dataset
|
||||
→ 其他来源:DataLoaderPlugin(source).load(dataset_info)
|
||||
→ Dataset
|
||||
Dataset 中的一条原始记录 + DatasetInfo.converter
|
||||
→ DataConverterPlugin(converter)(raw_sample)
|
||||
→ 标准 SFTSample / DPOSample
|
||||
```
|
||||
|
||||
相同的本地 loader 可以配合 `alpaca` 或 `sharegpt` converter;数据已经是标准消息结构时省略 converter。分词与标签生成发生在后续 Renderer 中。
|
||||
|
||||
## DataLoaderPlugin
|
||||
|
||||
接口定义在 `plugins/data_plugins/loader.py`。DataEngine 调用 `DataLoaderPlugin(source).load(dataset_info)`,而 `load` 会从 DatasetInfo 中提取 `path`、`split` 和 `streaming`,再将这三个位置参数传给注册函数。
|
||||
|
||||
因此,注册函数接收的是以下参数,不是整个 DatasetInfo:
|
||||
|
||||
```python
|
||||
@DataLoaderPlugin("example").register()
|
||||
def load_example(path, split, streaming):
|
||||
...
|
||||
```
|
||||
|
||||
当前注册的 `local` 根据文件扩展名选择 Hugging Face dataset builder,再加载文件或目录。Hub 数据由 DataEngine 直接分派,当前没有通过一个名为 `hf_hub` 的 loader 注册项加载。
|
||||
|
||||
返回的数据集由 DataEngine 持有并用于建立索引。新增 loader 必须保持调用参数与返回数据集的约定;当前训练链路要求可按索引读取的数据集,具体 streaming 边界见 [DataEngine](../core/data_engine.md#处理-streaming-dataset)。
|
||||
|
||||
## DataConverterPlugin
|
||||
|
||||
接口定义在 `plugins/data_plugins/converter.py`。Converter 接收单条原始样本字典,返回一个 v1 `SFTSample` 或 `DPOSample`,不是输入或返回整个 batch。当前注册:
|
||||
|
||||
- `alpaca`
|
||||
- `sharegpt`
|
||||
- `pair`
|
||||
|
||||
```python
|
||||
@DataConverterPlugin("example").register()
|
||||
def convert_example(raw_sample):
|
||||
return {"messages": ...}
|
||||
```
|
||||
|
||||
返回字段必须符合 `utils/types.py` 中的 Messages 类型。SFT 返回 `messages`,偏好数据返回 `chosen_messages` 和 `rejected_messages`;图片等媒体也在这一阶段转为标准内容块,实际媒体特征由 Renderer 的 processor 生成。
|
||||
|
||||
DataEngine 在建立多轮索引和实际取样时都会调用 converter。它应稳定地转换一条记录,避免在函数中随机选择对话轮次或维护读取进度。轮次展开、采样规模和读取顺序分别由 DataEngine 与 sampler 管理。
|
||||
|
||||
## 调整数据索引
|
||||
|
||||
`adjust_data_index` 根据 `size`、`weight` 调整某个数据集的索引,`select_data_sample` 处理索引选择。两者是 `loader.py` 中的普通函数,不通过插件名称注册或路由。改变来源、格式时扩展 loader/converter;改变共用索引语义时,应检查这些函数及 DataEngine 的调用位置。
|
||||
|
||||
注册在模块导入时生效,机制见[插件注册机制](../baseplugin_mechanism.md)。使用配置见[数据准备](../../feature-guide/data_preparation.md)。
|
||||
22
docs/zh/developer-guide/plugins/index.md
Normal file
22
docs/zh/developer-guide/plugins/index.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 插件实现
|
||||
|
||||
v1 按数据、模型和训练流程组织内置插件。配置文件通过实现名称选择插件,新增实现时使用对应的插件类注册。
|
||||
|
||||
| 分类 | 内容 |
|
||||
|------|------|
|
||||
| [数据插件](data_plugins.md) | DataLoader 与 DataConverter |
|
||||
| [模型插件](model_plugins.md) | 初始化、PEFT、量化、Kernel 和 Sequence Parallel |
|
||||
| [训练器插件](trainer_plugins.md) | 分布式后端、批处理和优化器 |
|
||||
| [融合算子加速](kernel-acceleration/overview.md) | Kernel 选择与调用流程 |
|
||||
|
||||
插件注册和参数解析的通用机制见[插件注册机制](../baseplugin_mechanism.md)。
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 2
|
||||
:hidden:
|
||||
|
||||
data_plugins
|
||||
model_plugins
|
||||
trainer_plugins
|
||||
kernel-acceleration/overview
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
# 融合算子加速
|
||||
|
||||
Kernel 系统在模型加载后应用融合算子加速。一个实现既可以替换单个算子,也可以组合多个融合操作或接入外部加速库。
|
||||
|
||||
入口位于 `plugins/model_plugins/kernels/interface.py`。它负责名称选择与调用顺序,`base.py` 中的 BaseKernel 负责执行公共检查,各实现的 `_apply` 负责识别模型并进行具体替换。注册机制、检查流程和模型适配由这三层分别承担。
|
||||
|
||||
## Kernel 应用流程
|
||||
|
||||
```text
|
||||
ModelEngine
|
||||
→ apply_kernels(model, kernel_config)
|
||||
→ 解析 kernel_config.name
|
||||
→ auto 设备选择或 KernelPlugin(name)
|
||||
→ BaseKernel.apply()
|
||||
→ check_device()
|
||||
→ check_deps()
|
||||
→ _apply()
|
||||
```
|
||||
|
||||
接口模块显式导入内置实现,使装饰器在调用前完成注册。`apply_kernels` 将名称按逗号拆分,逐个调用,并将返回模型传给下一个实现;每个实现收到同一份配置与 `require_logits` 等上下文。
|
||||
|
||||
`auto` 是入口处的特殊分派:根据设备查询 `_AUTO_KERNELS`,再调用其中的已注册实现。它不是自动搜索最快实现的算法;当前映射只包含 NPU,其他设备不会因 `auto` 而执行替换。
|
||||
|
||||
## BaseKernel 与具体实现如何协作
|
||||
|
||||
注册表保存的是实现类。`KernelPlugin(name).apply(...)` 将方法调用转发到该类,继承的 `BaseKernel.apply` 再使用 `cls` 调用该实现的检查和替换方法。
|
||||
|
||||
`check_device` 检查设备,`check_deps` 检查可选依赖,随后公共入口确认存在模型对象,最后调用 `_apply`。模型是否受支持、需要替换哪个模块,由具体 `_apply` 决定。BaseKernel 不统一检测所有模型结构。
|
||||
|
||||
例如,CUDA Fused MoE 按模型架构和模块类名寻找目标并替换 forward;Liger 根据 model type 调用对应外部适配函数;FLA 通过算子注册表匹配模型属性。成功找到注册名称,只说明能进入实现,不能保证模型中存在可替换目标。
|
||||
|
||||
## 组合与扩展边界
|
||||
|
||||
组合时,后一个实现看到的是已经修改过的模型。入口不提供冲突检测、模型快照或失败回滚,也不会把各实现的专属配置拆成独立配置块。新增实现需要清楚界定自己修改哪些模块,并返回后续调用所需的模型对象。
|
||||
|
||||
实现类继承 BaseKernel,提供 `check_device` 和 `_apply`,需要额外依赖时覆盖 `check_deps`;子类定义时会检查必需方法是否实现。名称注册后,还需要确保实现模块在使用前被导入。通用路由与注册规则见[插件注册机制](../../baseplugin_mechanism.md)。
|
||||
|
||||
## 已注册实现
|
||||
|
||||
- `liger_kernel`
|
||||
- `cuda_fused_moe`
|
||||
- `flash-linear-attention`
|
||||
- `npu_fused_moe`
|
||||
- `npu_fused_rmsnorm`
|
||||
- `npu_fused_rope`
|
||||
- `npu_fused_swiglu`
|
||||
|
||||
用户配置见[融合算子加速](../../../feature-guide/kernel_acceleration.md)。
|
||||
47
docs/zh/developer-guide/plugins/model_plugins.md
Normal file
47
docs/zh/developer-guide/plugins/model_plugins.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 模型插件
|
||||
|
||||
模型插件提供模型加载和处理过程中的可替换操作。初始化、量化、PEFT 和 Kernel 由 ModelEngine 依次调用;Sequence Parallel 需要训练拓扑,因此由 BaseTrainer 在训练初始化阶段调用。
|
||||
|
||||
## 每个插件接收和改变什么
|
||||
|
||||
| 插件 | 接收 | 返回或修改 |
|
||||
|------|------|------------|
|
||||
| InitPlugin | 当前进程的设备与 rank 信息 | 返回用于创建模型的 `torch.device` |
|
||||
| QuantizationPlugin | 模型加载参数、量化配置与训练标记 | 返回补充量化选项后的加载参数 |
|
||||
| PeftPlugin | 已构造模型、PEFT 配置与训练标记 | 设置可训练参数、加载或合并 adapter,返回处理后的模型 |
|
||||
| KernelPlugin | PEFT 处理后的模型与 Kernel 配置 | 替换适配的计算路径,返回处理后的模型 |
|
||||
|
||||
它们的接口不同,不能仅通过替换 `name` 在不同插件族之间互换。具体调用次序及原因见 [ModelEngine](../core/model_engine.md)。
|
||||
|
||||
## InitPlugin
|
||||
|
||||
注册 `init_on_default`、`init_on_meta`、`init_on_rank0`,返回模型创建使用的 `torch.device`。
|
||||
|
||||
实现位于 `plugins/model_plugins/initialization.py`。这些函数不加载模型权重;ModelEngine 根据返回设备决定调用 `from_pretrained` 还是在 meta 上使用 `from_config`。Rank 0 加载与其他 rank 的权重同步需要后续分布式路径协作。
|
||||
|
||||
## PeftPlugin
|
||||
|
||||
注册 `lora` 与 `freeze`。两者分别通过 `LoraParams` 和 `FreezeParams` 严格解析配置。LoRA 还负责 adapter 加载、合并与导出。
|
||||
|
||||
实现位于 `plugins/model_plugins/peft.py`。训练时,LoRA 创建或加载 adapter,Freeze 按层和模块选择可训练权重;后续优化器只收集 `requires_grad` 的参数。推理与合并导出也复用 LoRA 插件,因此同一配置入口的行为还取决于调用时的 `is_train`。
|
||||
|
||||
## QuantizationPlugin
|
||||
|
||||
注册 `auto` 与 `bnb`。插件修改 `from_pretrained` 的 init kwargs,不直接替换已经加载的权重。
|
||||
|
||||
实现位于 `plugins/model_plugins/quantization.py`。调用发生在模型创建之前,使量化选项能参与权重加载;这也解释了它与加载完成后执行的 PEFT、Kernel 所处阶段不同。当前可配置字段见[模型参数](../../configuration/model.md#quant_config)。
|
||||
|
||||
## KernelPlugin
|
||||
|
||||
Kernel 在模型加载和 PEFT 处理后应用。调用流程见[融合算子加速](kernel-acceleration/overview.md)。
|
||||
|
||||
## Sequence Parallel Plugins
|
||||
|
||||
设置 `TrainingArguments.cp_size > 1` 后,BaseTrainer 使用 `cp_mode` 的值选择 `SequenceParallelModelPlugin`。因此,`cp_mode: ulysses` 会调用 `SequenceParallelModelPlugin("ulysses")` 修改模型 forward 所需的通信;训练循环再调用 `SequenceParallelLossPlugin("sequence_parallel_loss")` 处理 loss 聚合。用户配置见[分布式训练](../../feature-guide/distributed_training.md#ulysses-context-parallel)。
|
||||
|
||||
这两个插件共同完成序列并行:模型侧处理 attention 的通信与切分,损失侧处理分布后的输入与监督计算。只替换 forward 而沿用不匹配的损失路径,会破坏这组协作关系。实现位于 `plugins/model_plugins/parallelization/`。
|
||||
|
||||
## Chat Template 迁移
|
||||
|
||||
旧 `RenderingPlugin` 和 `plugins/model_plugins/templates/` 已删除。Chat
|
||||
template 统一由 `core/rendering/` 调用 Hugging Face 模板。
|
||||
51
docs/zh/developer-guide/plugins/trainer_plugins.md
Normal file
51
docs/zh/developer-guide/plugins/trainer_plugins.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 训练器插件
|
||||
|
||||
训练器插件替换训练过程中的具体操作。BaseTrainer 持有模型、优化器和训练计数,BatchGenerator 持有读取进度与缓冲区;插件通过调用参数访问这些对象。替换实现时需要维持调用方依赖的输入、输出和状态约定。
|
||||
|
||||
## DistributedPlugin
|
||||
|
||||
当前注册 `fsdp2`、`fsdpturbo` 和 `deepspeed`。每个实现类提供统一的模型切分、保存和 checkpoint 方法组:
|
||||
|
||||
- `shard_model`
|
||||
- `save_model`
|
||||
- `save_checkpoint`
|
||||
- `load_checkpoint`
|
||||
|
||||
参数分别由 `FSDP2Params`、`FSDPTurboParams` 和 `DeepSpeedParams` 解析。FSDPTurbo 额外实现跨专家并行 Mesh 的梯度裁剪。公共 DeviceMesh 拓扑由 `TrainingArguments` 和 `DistributedInterface` 管理。
|
||||
|
||||
注册和入口位于 `plugins/trainer_plugins/distributed/interface.py`,具体引擎位于同目录下的 `fsdp2.py`、`fsdpturbo.py` 和 `deepspeed.py`。路由层把配置转换为对应 Params,再委托后端执行;参数配置本身不负责创建进程组。
|
||||
|
||||
FSDP2 / FSDPTurbo 的 `shard_model` 返回处理后的模型,BaseTrainer 再创建优化器。DeepSpeed 的该入口返回后端 engine,BaseTrainer 随后调用它的 `prepare`,共同准备模型、优化器和 scheduler。因此,公共入口背后的返回对象与初始化协议仍需结合 BaseTrainer 的后端分支理解。
|
||||
|
||||
保存时,Trainer 和 checkpoint 协调器决定时机与通用训练状态,分布式插件负责其模型和优化器状态格式。扩展新后端需要同时提供分片、最终模型保存、checkpoint 保存与恢复;仅实现分片无法覆盖完整训练生命周期。
|
||||
|
||||
## BatchingPlugin
|
||||
|
||||
`normal` 是 BatchGenerator 默认路径。插件注册:
|
||||
|
||||
- `padding_free`
|
||||
- `dynamic_batching`
|
||||
- `dynamic_padding_free`
|
||||
|
||||
接口与实现位于 `plugins/trainer_plugins/batching.py`。实现类继承 BaseBatcher,提供四个操作:
|
||||
|
||||
| 方法 | BatchGenerator 用它决定什么 |
|
||||
|------|-----------------------------|
|
||||
| `get_data_provider_batch_size` | 底层 DataLoader 每次读多少条样本 |
|
||||
| `compute_length` | 批次生成器报告的长度 |
|
||||
| `fill_buffer` | 何时继续读取样本、怎样填充缓冲区 |
|
||||
| `generate_batch` | 如何取出样本并组织一个更新步的 micro-batch 列表 |
|
||||
|
||||
方法接收 `batch_info`、buffer 或读取函数,状态由调用方管理。`generate_batch` 返回 `None` 时,BatchGenerator 结束本次迭代。新策略需要同时考虑数据耗尽、剩余 buffer 和 checkpoint 恢复,不能只实现拼接张量。
|
||||
|
||||
## OptimizerPlugin
|
||||
|
||||
当前注册 `muon`。未指定插件时 BaseTrainer 使用默认优化器。Muon 将适合正交化更新的二维权重和其余 AdamW 权重分组。用户配置见[优化器](../../feature-guide/optimizer.md)。
|
||||
|
||||
入口位于 `plugins/trainer_plugins/optimizers/optimizer.py`,接收处理后的模型与 `optim_config`,返回 optimizer 实例。当前 Muon 直接读取配置字段;顶层 `learning_rate` 在 TrainingArguments 初始化时写入配置的 `lr`。插件负责参数分组与优化器构造,反向传播和更新时机仍由 Trainer 或 DeepSpeed engine 控制。
|
||||
|
||||
## LRSchedulerPlugin
|
||||
|
||||
插件族存在,但当前没有注册可选的 scheduler 名称。
|
||||
|
||||
接口位于 `plugins/trainer_plugins/lr_scheduler.py`。BaseTrainer 未收到配置时使用固定倍率的 LambdaLR;收到配置时按名称调用插件,传入 optimizer、训练总步数与配置,并保存返回的 scheduler。扩展实现应返回兼容训练循环和 checkpoint 状态保存的 scheduler 对象。
|
||||
Reference in New Issue
Block a user