This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
# Agent支持
|
||||
|
||||
## 数据集格式
|
||||
|
||||
ms-swift 使用 agent-template 实现了Agent数据格式与模型的解耦:基于统一的数据集格式,可以灵活切换不同模型进行训练,无需修改数据。
|
||||
|
||||
纯文本Agent和多模态Agent的示例数据样本如下:
|
||||
```jsonl
|
||||
{"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"realtime_aqi\", \"description\": \"天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息\", \"parameters\": {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\", \"description\": \"城市名,例如:上海\"}}, \"required\": [\"city\"]}}}]", "messages": [{"role": "user", "content": "北京和上海今天的天气情况"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"北京\"}}"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"上海\"}}"}, {"role": "tool_response", "content": "{\"city\": \"北京\", \"aqi\": \"10\", \"unit\": \"celsius\"}"}, {"role": "tool_response", "content": "{\"city\": \"上海\", \"aqi\": \"72\", \"unit\": \"fahrenheit\"}"}, {"role": "assistant", "content": "根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。"}]}
|
||||
{"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"click\", \"description\": \"点击屏幕中的某个位置\", \"parameters\": {\"type\": \"object\", \"properties\": {\"x\": {\"type\": \"integer\", \"description\": \"横坐标,表示屏幕上的水平位置\"}, \"y\": {\"type\": \"integer\", \"description\": \"纵坐标,表示屏幕上的垂直位置\"}}, \"required\": [\"x\", \"y\"]}}}]", "messages": [{"role": "user", "content": "<image>现在几点了?"}, {"role": "assistant", "content": "<think>\n我可以通过打开日历App来获取当前时间。\n</think>\n"}, {"role": "tool_call", "content": "{\"name\": \"click\", \"arguments\": {\"x\": 105, \"y\": 132}}"}, {"role": "tool_response", "content": "{\"images\": \"<image>\", \"status\": \"success\"}"}, {"role": "assistant", "content": "成功打开日历App,现在的时间为中午11点"}], "images": ["desktop.png", "calendar.png"]}
|
||||
```
|
||||
- agent_template为"react_en", "hermes"等情况下,该格式适配所有模型Agent训练,可以轻松在不同模型间切换。
|
||||
- 其中tools是一个包含tool列表的json字符串,messages中role为'tool_call'和'tool_response/tool'的content部分都需要是json字符串。
|
||||
- tools字段将在训练/推理时和`{"role": "system", ...}"`部分组合,根据agent_template组成完整的system部分。
|
||||
- `{"role": "tool_call", ...}`部分将根据agent_template自动转成对应格式的`{"role": "assistant", ...}`,多条连续的`{"role": "assistant", ...}`将拼接在一起组成完整的assistant_content。
|
||||
- `{"role": "tool_response", ...}`也可以写成`{"role": "tool", ...}`,这两种写法是等价的。该部分也将根据`agent_template`自动转换格式。该部分在训练时将不进行损失的计算,角色类似于`{"role": "user", ...}`。
|
||||
- 该格式支持并行调用工具,例子参考第一条数据样本。多模态Agent数据样本中`<image>`标签数量应与"images"长度相同,其标签位置代表图像特征的插入位置。当然也支持其他模态,例如audios, videos。
|
||||
- 注意:您也可以手动将数据处理为role为system/user/assistant的messages格式。agent_template的作用是将其中的tools字段以及role为tool_call和tool_response的messages部分,自动映射为标准的role为system/user/assistant的messages格式。
|
||||
|
||||
以下为上述两条数据样本由qwen2_5和qwen2_5_vl的template进行encode后的input_ids和labels,选择的agent_template为**hermes**:
|
||||
|
||||
样本一(并行工具调用):
|
||||
```text
|
||||
[INPUT_IDS] <|im_start|>system
|
||||
You are Qwen, created by Alibaba Cloud. You are a helpful assistant.
|
||||
|
||||
# Tools
|
||||
|
||||
You may call one or more functions to assist with the user query.
|
||||
|
||||
You are provided with function signatures within <tools></tools> XML tags:
|
||||
<tools>
|
||||
{"type": "function", "function": {"name": "realtime_aqi", "description": "天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息", "parameters": {"type": "object", "properties": {"city": {"type": "string", "description": "城市名,例如:上海"}}, "required": ["city"]}}}
|
||||
</tools>
|
||||
|
||||
For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
|
||||
<tool_call>
|
||||
{"name": <function-name>, "arguments": <args-json-object>}
|
||||
</tool_call><|im_end|>
|
||||
<|im_start|>user
|
||||
北京和上海今天的天气情况<|im_end|>
|
||||
<|im_start|>assistant
|
||||
<tool_call>
|
||||
{"name": "realtime_aqi", "arguments": {"city": "北京"}}
|
||||
</tool_call>
|
||||
<tool_call>
|
||||
{"name": "realtime_aqi", "arguments": {"city": "上海"}}
|
||||
</tool_call><|im_end|>
|
||||
<|im_start|>user
|
||||
<tool_response>
|
||||
{"city": "北京", "aqi": "10", "unit": "celsius"}
|
||||
</tool_response>
|
||||
<tool_response>
|
||||
{"city": "上海", "aqi": "72", "unit": "fahrenheit"}
|
||||
</tool_response><|im_end|>
|
||||
<|im_start|>assistant
|
||||
根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。<|im_end|>
|
||||
|
||||
[LABELS] [-100 * 195]<tool_call>
|
||||
{"name": "realtime_aqi", "arguments": {"city": "北京"}}
|
||||
</tool_call>
|
||||
<tool_call>
|
||||
{"name": "realtime_aqi", "arguments": {"city": "上海"}}
|
||||
</tool_call><|im_end|>[-100 * 67]根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。<|im_end|>
|
||||
```
|
||||
|
||||
样本二(多模态,混合assistant和tool_call):
|
||||
```text
|
||||
[INPUT_IDS] <|im_start|>system
|
||||
You are a helpful assistant.
|
||||
|
||||
# Tools
|
||||
|
||||
You may call one or more functions to assist with the user query.
|
||||
|
||||
You are provided with function signatures within <tools></tools> XML tags:
|
||||
<tools>
|
||||
{"type": "function", "function": {"name": "click", "description": "点击屏幕中的某个位置", "parameters": {"type": "object", "properties": {"x": {"type": "integer", "description": "横坐标,表示屏幕上的水平位置"}, "y": {"type": "integer", "description": "纵坐标,表示屏幕上的垂直位置"}}, "required": ["x", "y"]}}}
|
||||
</tools>
|
||||
|
||||
For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
|
||||
<tool_call>
|
||||
{"name": <function-name>, "arguments": <args-json-object>}
|
||||
</tool_call><|im_end|>
|
||||
<|im_start|>user
|
||||
<|vision_start|>[151655 * 729]<|vision_end|>现在几点了?<|im_end|>
|
||||
<|im_start|>assistant
|
||||
<think>
|
||||
我可以通过打开日历App来获取当前时间。
|
||||
</think>
|
||||
<tool_call>
|
||||
{"name": "click", "arguments": {"x": 105, "y": 132}}
|
||||
</tool_call><|im_end|>
|
||||
<|im_start|>user
|
||||
<tool_response>
|
||||
{"images": "<|vision_start|>[151655 * 729]<|vision_end|>", "status": "success"}
|
||||
</tool_response><|im_end|>
|
||||
<|im_start|>assistant
|
||||
成功打开日历App,现在的时间为中午11点<|im_end|>
|
||||
|
||||
[LABELS] [-100 * 924]<think>
|
||||
我可以通过打开日历App来获取当前时间。
|
||||
</think>
|
||||
<tool_call>
|
||||
{"name": "click", "arguments": {"x": 105, "y": 132}}
|
||||
</tool_call><|im_end|>[-100 * 759]成功打开日历App,现在的时间为中午11点<|im_end|>
|
||||
```
|
||||
|
||||
**react_en**是常用的agent template格式之一,以下为样本一由qwen2_5使用`agent_template='react_en'`进行encode后的input_ids和labels:
|
||||
|
||||
```text
|
||||
[INPUT_IDS] <|im_start|>system
|
||||
Answer the following questions as best you can. You have access to the following tools:
|
||||
|
||||
realtime_aqi: Call this tool to interact with the realtime_aqi API. What is the realtime_aqi API useful for? 天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息 Parameters: {"type": "object", "properties": {"city": {"type": "string", "description": "城市名,例如:上海"}}, "required": ["city"]} Format the arguments as a JSON object.
|
||||
|
||||
Use the following format:
|
||||
|
||||
Question: the input question you must answer
|
||||
Thought: you should always think about what to do
|
||||
Action: the action to take, should be one of [realtime_aqi]
|
||||
Action Input: the input to the action
|
||||
Observation: the result of the action
|
||||
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
|
||||
Thought: I now know the final answer
|
||||
Final Answer: the final answer to the original input question
|
||||
|
||||
Begin!
|
||||
<|im_end|>
|
||||
<|im_start|>user
|
||||
北京和上海今天的天气情况<|im_end|>
|
||||
<|im_start|>assistant
|
||||
Action: realtime_aqi
|
||||
Action Input: {'city': '北京'}
|
||||
Action: realtime_aqi
|
||||
Action Input: {'city': '上海'}
|
||||
Observation:{"city": "北京", "aqi": "10", "unit": "celsius"}
|
||||
Observation:{"city": "上海", "aqi": "72", "unit": "fahrenheit"}
|
||||
根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。<|im_end|>
|
||||
|
||||
[LABELS] [-100 * 233]Action: realtime_aqi
|
||||
Action Input: {'city': '北京'}
|
||||
Action: realtime_aqi
|
||||
Action Input: {'city': '上海'}
|
||||
Observation:[-100 * 45]根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。<|im_end|>
|
||||
```
|
||||
|
||||
更多模型和agent_template的尝试可以使用以下代码,更多的agent template可选值参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/agent_template/__init__.py)。
|
||||
```python
|
||||
from swift import get_processor, get_template
|
||||
|
||||
tokenizer = get_processor('Qwen/Qwen3.5-2B')
|
||||
template = get_template(tokenizer) # 使用默认agent模板
|
||||
# template = get_template(tokenizer, agent_template='qwen3_5')
|
||||
print(f'agent_template: {template._agent_template}')
|
||||
data = {...}
|
||||
template.set_mode('train')
|
||||
encoded = template.encode(data)
|
||||
print(f'[INPUT_IDS] {template.safe_decode(encoded["input_ids"])}\n')
|
||||
print(f'[LABELS] {template.safe_decode(encoded["labels"])}')
|
||||
```
|
||||
|
||||
|
||||
## tools格式
|
||||
tools字段提供了模型可以调用的API信息。你需要提供tools的名字,描述和参数,示例如下:
|
||||
|
||||
```python
|
||||
tools = [{
|
||||
'type': 'function',
|
||||
'function': {
|
||||
'name': 'get_current_weather',
|
||||
'description': 'Get the current weather in a given location',
|
||||
'parameters': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'location': {
|
||||
'type': 'string',
|
||||
'description': 'The city and state, e.g. San Francisco, CA'
|
||||
},
|
||||
'unit': {
|
||||
'type': 'string',
|
||||
'enum': ['celsius', 'fahrenheit']
|
||||
}
|
||||
},
|
||||
'required': ['location']
|
||||
}
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
## loss_scale的使用
|
||||
|
||||
loss_scale参数可用于调节模型输出部分在训练过程中的损失权重。目前支持两种配置方式:字符串精确匹配和正则表达式匹配。
|
||||
|
||||
1. 字符串匹配示例:ReACT 格式
|
||||
|
||||
以 ReACT 格式为例,可通过 `--loss_scale react` 启用相应的 loss_scale 配置(配置文件详见 [react.json](https://github.com/modelscope/ms-swift/blob/main/swift/loss_scale/config/react.json))。该方式基于字符串精确匹配,配置中的字典映射需提供一个包含两个元素的列表,分别表示:当前匹配字符串本身的损失权重,
|
||||
从该字符串之后到下一个指定字符串之前的内容的损失权重。该设置的具体效果如下:
|
||||
- 'Action:' 和 'Action Input:' 字段自身及其后续内容的损失权重均为 2;
|
||||
- 'Thought:' 和 'Final Answer:' 字段自身及其后续内容的损失权重均为 1;
|
||||
- 'Observation:' 字段自身的权重为 2,但其后跟随的工具调用结果部分的损失权重为 0。
|
||||
|
||||
2. 正则匹配示例:忽略空思维块
|
||||
|
||||
在训练推理模型时,我们可能需要忽略数据集中存在的形如 `'<think>\n\n</think>\n\n'`的空思维标记损失计算。此时可使用 `--loss_scale ignore_empty_think`(配置文件详见 [ignore_empty_think.json](https://github.com/modelscope/ms-swift/blob/main/swift/loss_scale/config/ignore_empty_think.json))。该配置采用正则表达式匹配方式,字典映射的列表只需指定一个值,表示匹配内容的损失权重。该设置的具体效果如下:
|
||||
|
||||
- 所有与正则表达式`<think>\\s*</think>\\s*`匹配的字符串,loss_scale为0,即不计算损失。
|
||||
|
||||
使用代码测试loss_scale:
|
||||
```python
|
||||
from swift import get_processor, get_template
|
||||
|
||||
data = {"messages": [
|
||||
{"role": "user", "content": "aaaaa"},
|
||||
{"role": "assistant", "content": "<think>\n\n</think>\n\nabc<think>\n\n</think>\n\n123"},
|
||||
]}
|
||||
|
||||
template = get_template(get_processor('Qwen/Qwen3-8B'), loss_scale='ignore_empty_think')
|
||||
template.set_mode('train')
|
||||
inputs = template.encode(data)
|
||||
|
||||
print(template.safe_decode(inputs['labels']))
|
||||
# '[-100 * 14]abc<think>\n\n</think>\n\n123<|im_end|>\n'
|
||||
```
|
||||
|
||||
更多的loss_scale插件设计,请参考[架构](../Customization/Architecture.md#loss-scale)文档.
|
||||
|
||||
## 训练
|
||||
- 训练Base模型的Agent能力,通过修改`--model`切换不同模型,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/agent/qwen2_5.sh)。
|
||||
- 训练GLM4的agent_template为hermes,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/agent/glm4.sh)。
|
||||
- 使用`--loss_scale`对模型输出部分的损失权重进行调整,参加[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/agent/loss_scale)。
|
||||
|
||||
## 推理
|
||||
|
||||
- 🚀原始模型或者全参数训练后模型的推理,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_agent.py)。
|
||||
- LoRA训练后推理,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/agent/loss_scale/infer_lora.py)。
|
||||
|
||||
## 部署
|
||||
|
||||
服务端和客户端代码,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/deploy/agent)。
|
||||
@@ -0,0 +1,964 @@
|
||||
# 命令行参数
|
||||
|
||||
命令行参数的介绍会分为基本参数,原子参数、集成参数和特定模型参数。**命令行最终使用的参数列表为集成参数。集成参数继承自基本参数和一些原子参数**。特定模型参数是针对于具体模型的参数,可以通过`--model_kwargs'`或者环境变量进行设置。Megatron-SWIFT命令行参数介绍可以在[Megatron-SWIFT训练文档](../Megatron-SWIFT/Command-line-parameters.md)中找到。
|
||||
|
||||
**提示:**
|
||||
- 命令行传入list使用空格隔开即可。例如:`--dataset <dataset_path1> <dataset_path2>`。
|
||||
- 命令行传入dict使用json。例如:`--model_kwargs '{"fps_max_frames": 12}'`。
|
||||
- 带🔥的参数为重要参数,刚熟悉ms-swift的用户可以先关注这些命令行参数。
|
||||
|
||||
## 基本参数
|
||||
|
||||
- 🔥tuner_backend: 可选为'peft','unsloth'。默认为'peft'。
|
||||
- 🔥tuner_type: 可选为'lora'、'full'、'lora_llm'、'longlora'、'adalora'、'llamapro'、'adapter'、'vera'、'boft'、'fourierft'、'reft'、'bone'。默认为'lora'。
|
||||
- 注意:在Megatron-SWIFT中默认为'full'。
|
||||
- 其中'lora_llm'代表对llm部分进行lora,vit/aligner部分使用'full'。你可以使用`vit_lr/aligner_lr`设置各自的学习率。
|
||||
- 🔥adapters: 用于指定adapter的id/path的list,默认为`[]`。该参数通常用于推理/部署命令,例如:`swift infer --model '<model_id_or_path>' --adapters '<adapter_id_or_path>'`。该参数偶尔也用于断点续训,该参数与`resume_from_checkpoint`的区别在于,**该参数只读取adapter权重**,而不加载优化器和随机种子,并不跳过已训练的数据集部分。
|
||||
- `--model`与`--adapters`的区别:`--model`后接完整权重的目录路径,内包含model/tokenizer/config等完整权重信息,例如`model.safetensors`。`--adapters`后接增量adapter权重目录路径的列表,内涵adapter的增量权重信息,例如`adapter_model.safetensors`。
|
||||
- 🔥external_plugins: 外部`plugin.py`文件列表,这些文件会被额外加载(即对模块进行`import`)。默认为`[]`。你可以传入自定义模型、对话模板和数据集注册的`.py`文件路径,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/custom/sft.sh);或者自定义GRPO的组件,参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/run_external_reward_func.sh)。
|
||||
- seed: 全局随机种子,默认为42。
|
||||
- 注意:该随机种子与控制数据集随机的`data_seed`互不影响。
|
||||
- model_kwargs: 特定模型可传入的额外参数,该参数列表会在训练/推理时打印日志进行提示。例如`--model_kwargs '{"fps_max_frames": 12}'`。你也可以通过环境变量的方式设置,例如`FPS_MAX_FRAMES=12`。默认为None。
|
||||
- 注意:**若你在训练时指定了特定模型参数,请在推理时也设置对应的参数**,这可以提高训练效果。
|
||||
- 特定模型参数的含义可以在对应模型官方repo或者其推理代码中找到相应含义。ms-swift引入这些参数以确保训练的模型与官方推理代码效果对齐。
|
||||
- enable_npu_model_patch: 是否启用NPU模型层patch,默认为True。该参数仅控制NPU环境下模型相关的patch,通常不需要关闭;排查transformers原生行为或NPU模型patch兼容问题时可以设置为False。该参数需要在进程首次导入`swift.model`前作为启动参数传入。
|
||||
- load_args: 当指定`--resume_from_checkpoint`、`--model`、`--adapters`会读取保存文件中的`args.json`,读取的keys查看[base_args.py](https://github.com/modelscope/ms-swift/blob/main/swift/arguments/base_args/base_args.py)。推理和导出时默认为True,训练时默认为False。该参数通常不需要修改。
|
||||
- load_data_args: 如果将该参数设置为True,则会额外读取`args.json`中的数据参数。默认为False。**该参数通常用于推理时对训练中切分的验证集进行推理**,例如:`swift infer --adapters xxx --load_data_args true --stream true --max_new_tokens 512`。
|
||||
- use_hf: 控制模型下载、数据集下载、模型推送使用[ModelScope](https://modelscope.cn/)还是[HuggingFace](https://huggingface.co/)。默认为False,使用ModelScope。
|
||||
- 提示:如果你想在国外访问ModelScope,可以尝试使用[ModelScope国际版](https://modelscope.ai/home),设置环境变量`MODELSCOPE_DOMAIN='www.modelscope.ai'`即可。
|
||||
- hub_token: hub token. modelscope的hub token可以查看[这里](https://modelscope.cn/my/myaccesstoken)。默认为None。
|
||||
- ddp_timeout: 默认为18000000,单位为秒。
|
||||
- ddp_backend: 可选为"nccl"、"gloo"、"mpi"、"ccl"、"hccl"、"cncl"、"mccl"。默认为None,进行自动选择。
|
||||
- ignore_args_error: 用于兼容jupyter notebook。默认为False。
|
||||
|
||||
### 模型参数
|
||||
- 🔥model: [模型id](https://modelscope.cn/models)或模型本地路径。默认为None。
|
||||
- 🔥model_type: 模型类型。**我们将相同的模型架构、模型加载过程、template定义为一个`model_type`**。默认为None,即**根据`--model`的后缀和config.json中的'architectures'属性进行自动选择**。对应模型的model_type可以在[支持的模型列表](./Supported-models-and-datasets.md)中找到。
|
||||
- 注意:ms-swift中model_type的概念与`config.json`中的model_type不同。
|
||||
- 自定义模型通常需要自行注册`model_type`和`template`,具体可以参考[自定义模型文档](../Customization/Custom-model.md)。
|
||||
- model_revision: 模型版本,默认为None。
|
||||
- task_type: 默认为'causal_lm'。可选为'causal_lm'、'seq_cls'、'embedding'、'reranker'和'generative_reranker'。seq_cls的例子可以查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/seq_cls),embedding的例子查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/embedding)。
|
||||
- 若设置为'seq_cls',你通常需要额外设置`--num_labels`和`--problem_type`。
|
||||
- 🔥torch_dtype: 模型权重的数据类型,支持`float16`,`bfloat16`,`float32`。默认为None,从'config.json'文件中读取。
|
||||
- attn_impl: attention类型,可选项为'sdpa', 'eager', 'flash_attn', 'flash_attention_2', 'flash_attention_3', 'flash_attention_4'等。默认使用None,读取'config.json'。
|
||||
- 注意:这几种attention实现并不一定都支持,这取决于对应模型transformers实现的支持情况。
|
||||
- 若设置为'flash_attn'(兼容旧版本),则使用'flash_attention_2'。
|
||||
- 🔥experts_impl: 专家实现类型,可选项为'grouped_mm', 'batched_mm', 'eager'。默认为None。该特性需要"transformers>=5.0.0"。
|
||||
- new_special_tokens: 需要新增的特殊tokens。默认为`[]`。例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/new_special_tokens)。
|
||||
- 注意:你也可以传入以`.txt`结尾的文件路径,每行为一个special token。
|
||||
- num_labels: 分类模型(即`--task_type seq_cls`)需要指定该参数。代表标签数量,默认为None。
|
||||
- problem_type: 分类模型(即`--task_type seq_cls`)需要指定该参数。可选为'regression', 'single_label_classification', 'multi_label_classification'。默认为None,若模型为 reward_model 或 num_labels 为1,该参数为'regression',其他情况,该参数为'single_label_classification'。
|
||||
- rope_scaling: rope类型,你可以传入字符串,例如:`linear`、`dynamic`、`yarn`并结合传入`max_model_len`,ms-swift会自动设置对应的rope_scaling并覆盖'config.json'中的rope_scaling。或者你需要传入一个json字符串,例如`'{"factor":2.0, "type":"yarn"}'`,该值会直接覆盖'config.json'中的rope_scaling。默认为None。
|
||||
- max_model_len: 如果使用`rope_scaling`并传入字符串,可以设置`max_model_len`,该参数用来计算rope的`factor`倍数。该参数默认为None。若为非None,该参数会**覆盖**'config.json'中的`max_position_embeddings`值。
|
||||
- device_map: 模型使用的device_map配置,例如:'auto'、'cpu'、json字符串、json文件路径。该参数会**透传**入transformers的`from_pretrained`接口。默认为None,根据设备和分布式训练情况自动设置。
|
||||
- max_memory: device_map设置为'auto'或者'sequential'时,会根据max_memory进行模型权重的device分配,例如:`--max_memory '{0: "20GB", 1: "20GB"}'`。默认为None。该参数会透传入transformers的`from_pretrained`接口。
|
||||
- local_repo_path: 部分模型在加载时依赖于github repo,例如[deepseek-vl2](https://github.com/deepseek-ai/DeepSeek-VL2)。为了避免`git clone`时遇到网络问题,可以直接使用本地repo。该参数需要传入本地repo的路径, 默认为`None`。
|
||||
- init_strategy: 加载模型时,初始化模型中所有未初始化的参数(自定义模型架构时)。可选为'zero', 'uniform', 'normal', 'xavier_uniform', 'xavier_normal', 'kaiming_uniform', 'kaiming_normal', 'orthogonal'。默认为None。
|
||||
|
||||
|
||||
### 数据参数
|
||||
- 🔥dataset: 数据集id或路径的list。默认为`[]`。每个数据集的传入格式为:`'数据集id or 数据集路径:子数据集#采样数量'`,其中子数据集和取样数据可选。本地数据集支持jsonl、csv、json、文件夹等。**hub端的开源数据集可以通过`git clone`到本地并将文件夹传入而离线使用**。自定义数据集格式可以参考[自定义数据集文档](../Customization/Custom-dataset.md)。你可以传入`--dataset <dataset1> <dataset2>`来使用多个数据集。
|
||||
- 子数据集: 该参数只有当dataset为ID或者文件夹时生效。若注册时指定了subsets,且只有一个子数据集,则默认选择注册时指定的子数据集,否则默认为'default'。你可以使用`/`来选择多个子数据集,例如:`<dataset_id>:subset1/subset2`。你也可以使用'all'来选择注册时指定的所有子数据集,例如:`<dataset_id>:all`。注册例子可以参考[这里](https://modelscope.cn/datasets/swift/garbage_competition)。
|
||||
- 采样数量: 默认使用完整的数据集。你可以通过设置`#采样数`对选择的数据集进行采样,例如`<dataset_path#1000>`。若采样数少于数据样本总数,则进行随机选择(不重复采样)。若采样数高于数据样本总数,则只额外随机采样`采样数%数据样本总数`的样本,数据样本重复采样`采样数//数据样本总数`次。注意:流式数据集(`--streaming true`)只进行顺序采样。若设置`--dataset_shuffle false`,则非流式数据集也进行顺序采样。
|
||||
- 🔥val_dataset: 验证集id或路径的list。默认为`[]`。
|
||||
- 🔥cached_dataset: 使用缓存数据集(使用`swift export --to_cached_dataset true ...`命令产生),避免大型数据集训练/推理时,tokenize过程占用gpu时间。该参数用于设置缓存训练数据集文件夹路径,默认为`[]`。例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/cached_dataset)。
|
||||
- 提示:cached_dataset只会在数据集中额外存储length字段(为避免存储压力),并过滤掉会报错的数据样本。在训练/推理时,支持`--max_length`参数进行超长数据过滤/裁剪以及`--packing`参数。数据实际预处理过程将在训练时同步进行,该过程和训练是重叠的,并不会影响训练速度。(例外情况:当设置`--truncation_strategy split`会存储'input_ids',你需要在数据导出和训练时设置相同的`max_length`和`truncation_strategy`,packing兼容不受影响)
|
||||
- cached_dataset在`ms-swift`和`Megatron-SWIFT`之间是通用的,且支持pt/sft/infer/rlhf,使用`--template_mode`设置训练类型;支持embedding/reranker/seq_cls任务,使用`--task_type`设置任务类型。
|
||||
- 支持对cache_dataset进行采样,语法为`<cached_dataset_path>#采样数`,支持采样数高于和少于样本数的情况,功能与实现参考`--dataset`的介绍。
|
||||
- cached_val_dataset: 缓存验证数据集的文件夹路径,默认为`[]`。
|
||||
- 🔥split_dataset_ratio: 不指定val_dataset时从训练集拆分验证集的比例,默认为0.,即不从训练集切分验证集。
|
||||
- data_seed: 数据集随机种子,默认为42。
|
||||
- 🔥dataset_num_proc: 数据集预处理的进程数,默认为1。
|
||||
- 提示:纯文本模型建议将该值开大加速预处理速度。而多模态模型不建议开太大,这可能导致更慢的预处理速度(多模态模型若出现cpu利用率100%,但是处理速度极慢的情况,建议额外设置`OMP_NUM_THREADS`环境变量)。
|
||||
- 🔥load_from_cache_file: 是否从缓存中加载数据集,默认为False。**建议在实际运行时设置为True,debug阶段设置为False**。你可以修改`MODELSCOPE_CACHE`环境变量控制缓存的路径。
|
||||
- dataset_shuffle: 是否对dataset进行随机操作。默认为True。
|
||||
- 注意:**CPT/SFT的随机包括两个部分**:数据集的随机,由`dataset_shuffle`控制;train_dataloader中的随机,由`train_dataloader_shuffle`控制。
|
||||
- val_dataset_shuffle: 是否对val_dataset进行随机操作。默认为False。
|
||||
- streaming: 流式读取并处理数据集,默认False。(流式数据集的随机并不彻底,可能导致loss波动剧烈。)
|
||||
- 注意:需要额外设置`--max_steps`,因为流式数据集无法获得其长度。你可以通过设置`--save_strategy epoch`并设置较大的max_steps来实现与`--num_train_epochs`等效的训练。或者,你也可以设置`max_epochs`确保训练到对应epochs时退出训练,并对权重进行验证和保存。
|
||||
- 注意:流式数据集可以跳过预处理等待,将预处理时间与训练时间重叠。流式数据集的预处理只在rank0上进行,并通过数据分发的方式同步到其他进程,**其通常效率不如非流式数据集采用的数据分片读取方式**。当训练的world_size较大时,预处理和数据分发将成为训练瓶颈。
|
||||
- interleave_prob: 默认值为 None。在组合多个数据集时,默认使用datasets库的 `concatenate_datasets` 函数;如果设置了该参数,则会使用 `interleave_datasets` 函数。该参数通常用于流式数据集的组合,并会作为参数传入 `interleave_datasets` 函数中。该参数不对`--val_dataset`生效。
|
||||
- stopping_strategy: 可选为"first_exhausted", "all_exhausted",默认为"first_exhausted"。传入`interleave_datasets`函数中。该参数不对`--val_dataset`生效。
|
||||
- shuffle_buffer_size: 该参数用于指定**流式数据集**的随机buffer大小,默认为1000。该参数只在`dataset_shuffle`设置为true时有效。
|
||||
- download_mode: 数据集下载模式,包含`reuse_dataset_if_exists`和`force_redownload`,默认为'reuse_dataset_if_exists'。
|
||||
- 通常在使用hub端数据集报错时设置为`--download_mode force_redownload`。
|
||||
- columns: 用于对数据集进行列映射,使数据集满足AutoPreprocessor可以处理的样式,AutoPreprocessor可以处理的数据集格式查看[自定义数据集文档](../Customization/Custom-dataset.md)。你可以传入json字符串,例如:`'{"text1": "query", "text2": "response"}'`,代表将数据集中的"text1"映射为"query","text2"映射为"response",而query-response格式可以被AutoPreprocessor处理。默认为None。
|
||||
- strict: 如果为True,则数据集只要某行有问题直接抛错,否则会丢弃出错数据样本。默认False。该参数通常用于排查错误。
|
||||
- 🔥remove_unused_columns: 是否删除数据集中不被使用的列,默认为True。
|
||||
- 若该参数设置为False,则将额外的数据集列传递至trainer的`compute_loss`函数内,**方便自定义损失函数使用额外的数据集列**。
|
||||
- GRPO该参数的默认值为False。
|
||||
- disable_auto_column_mapping: 默认情况下会对数据集的列名进行自动映射,该参数用于关闭此行为(columns 参数依旧生效),默认为False。自动映射规则如下:
|
||||
- 以下字段会自动转成对应的images, videos, audios字段。
|
||||
- images: image, images.
|
||||
- videos: video, videos.
|
||||
- audios: audio, audios.
|
||||
- 以下字段会自动转成对应的system、query、response字段。(solution字段会保留)
|
||||
- system: 'system', 'system_prompt'.
|
||||
- query: 'query', 'prompt', 'input', 'instruction', 'question', 'problem'.
|
||||
- response: 'response', 'answer', 'output', 'targets', 'target', 'answer_key', 'answers', 'solution', 'text', 'completion', 'content'.
|
||||
- 🔥model_name: **仅用于自我认知任务**,只对`swift/self-cognition`数据集生效,替换掉数据集中的`{{NAME}}`通配符。传入模型中文名和英文名,以空格分隔,例如:`--model_name 小黄 'Xiao Huang'`。默认为None。
|
||||
- 🔥model_author: 仅用于自我认知任务,只对`swift/self-cognition`数据集生效,替换掉数据集中的`{{AUTHOR}}`通配符。传入模型作者的中文名和英文名,以空格分隔,例如:`--model_author '魔搭' 'ModelScope'`。默认为None。
|
||||
- custom_dataset_info: 自定义数据集注册的json文件路径,参考[自定义数据集](../Customization/Custom-dataset.md)和[内置'dataset_info.json'文件](https://github.com/modelscope/ms-swift/blob/main/swift/dataset/data/dataset_info.json)。默认为`[]`。
|
||||
|
||||
### 模板参数
|
||||
- 🔥template: 对话模板类型。默认为None,自动选择对应model的template类型,对应关系参考[支持的模型列表](./Supported-models-and-datasets.md)。
|
||||
- 🔥system: 自定义system字段,可以传入字符串或者**txt文件路径**。默认为None,使用注册template时的默认system。
|
||||
- 注意:数据集中的system**优先级**最高,然后是`--system`,最后是注册template时设置的`default_system`。
|
||||
- 🔥max_length: 限制单数据集样本经过`tokenizer.encode`后的tokens最大长度,超过的数据样本会根据`truncation_strategy`参数进行处理(避免训练OOM)。默认为None,即设置为模型支持的tokens最大长度(max_model_len)。
|
||||
- 当PPO、GRPO、GKD和推理情况下,`max_length`代表`max_prompt_length`。
|
||||
- truncation_strategy: 如果单样本的tokens超过`max_length`如何处理,支持'delete'、'left'、'right'和'split',代表删除、左侧裁剪、右侧裁剪和切成多条数据样本,默认为'delete'。
|
||||
- 注意:`--truncation_strategy split`只支持预训练时使用,即`swift/megatron pt`场景下,该策略会将超长字段切成多条数据样本,从而避免tokens浪费。该策略只支持纯文本数据集,多模态数据集无法正确处理pixel_values的切分。
|
||||
- 注意:若多模态模型的训练时将'truncation_strategy'设置为`left`或`right`,**ms-swift会保留所有的image_token等多模态tokens**,这可能会导致训练时OOM。
|
||||
- 🔥max_pixels: 多模态模型输入图片的最大像素数(H\*W),将超过该限制的图像进行缩放(避免训练OOM)。默认为None,不限制最大像素数。
|
||||
- 注意:该参数适用于所有的多模态模型。而Qwen2.5-VL特有的模型参数`MAX_PIXELS`(你可以在文档最下面找到)只针对Qwen2.5-VL模型。
|
||||
- 🔥agent_template: Agent模板,确定如何将工具列表'tools'转换成'system'、如何在推理/部署时从模型回复中提取toolcall部分,以及确定'messages'中`{"role": "tool_call", "content": "xxx"}`, `{"role": "tool_response", "content": "xxx"}`的模板格式。可选为"react_en", "hermes", "glm4", "qwen_en", "toolbench"等,更多请查看[这里](https://github.com/modelscope/ms-swift/blob/main/swift/agent_template/mapping.py)。默认为None,根据模型类型进行自动选择。可以参考[Agent文档](./Agent-support.md)。
|
||||
- norm_bbox: 控制如何缩放边界框(即数据集中的"bbox",里面的数据为绝对坐标,参考[自定义数据集文档](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html#grounding))。选项为'norm1000'和'none'。'norm1000'表示将bbox坐标缩放至千分位坐标,而'none'表示不进行缩放。默认值为None,将根据模型自动选择。
|
||||
- 当**图片在训练中发生缩放时**(例如设置了max_pixels参数),该参数也能很好进行解决。
|
||||
- use_chat_template: 使用chat模板还是generation模板(generation模板通常用于预训练时)。默认为`True`。
|
||||
- 注意:`swift pt`默认为False,使用generation模板。该参数可以很好的**兼容多模态模型**。
|
||||
- padding_side: 当训练`batch_size>=2`时的padding_side,可选值为'left'、'right',默认为'right'。(推理时的batch_size>=2时,只进行左padding)。
|
||||
- 注意:PPO和GKD默认设置为'left'。
|
||||
- 🔥padding_free: 将一个batch中的数据进行展平而避免数据padding,从而降低显存占用并加快训练(**同一batch的不同序列之间依旧是不可见的**)。默认为False。当前支持CPT/SFT/DPO/GRPO/KTO/GKD。
|
||||
- 注意:使用padding_free请结合`--attn_impl flash_attn`使用且"transformers>=4.44",具体查看[该PR](https://github.com/huggingface/transformers/pull/31629)。(同packing)
|
||||
- **相较于packing,padding_free不需要额外的预处理时间,但packing的训练速度更快且显存占用更稳定**。
|
||||
- 🔥loss_scale: 训练tokens的loss权重设置。默认为`'default'`。loss_scale包含3种基本策略:'default'、'last_round'、'all',以及其他策略:'ignore_empty_think'以及agent需要的:'react'、'hermes'、'qwen'、'agentflan'、'alpha_umi'等,可选值参考[loss_scale模块](https://github.com/modelscope/ms-swift/blob/main/swift/loss_scale/mapping.py)。ms-swift 支持了基本策略和其他策略的混用,例如:`'default+ignore_empty_think'`,`'last_round+ignore_empty_think'`。若没有指定基本策略,则默认为'default',例如:'hermes'与'default+hermes'等价。
|
||||
- 'default': 所有response(含history)以权重1计算交叉熵损失(**messages中的system/user/多模态tokens以及Agent训练中`tool_response`部分不计算损失**)。(**SFT默认为该值**)
|
||||
- 'last_round': 只计算最后一轮response的损失。最后一轮含义为最后一个"user"之后的所有内容。(**RLHF默认为该值**)
|
||||
- 'all': 计算所有tokens的损失。(**`swift pt`默认为该值**)
|
||||
- 'ignore_empty_think': 忽略空的`'<think>\n\n</think>\n\n'`损失计算。(满足正则匹配`'<think>\\s*</think>\\s*'`即可)。
|
||||
- 'react', 'hermes', 'qwen': 将`tool_call`部分的loss权重调整为2。
|
||||
- 注意:在"ms-swift>=4.3.1",支持了多个非基本策略串联使用(依次处理上一个策略的输出片段,权重相乘),例如:`'last_round+hermes+ignore_empty_think'`,其中'last_round'为基础策略,'hermes+ignore_empty_think'为多个非基本策略的串联使用,共用基础策略。
|
||||
- disable_ignore_empty_think: 是否禁用对混合思考模型的loss_scale自动追加`ignore_empty_think`策略。默认为`False`,即对混合思考模型(如Qwen3.5-4B)自动在loss_scale后追加`+ignore_empty_think`,使空的`'<think>\n\n</think>\n\n'`不参与损失计算。若用户已手动在loss_scale中指定了`ignore_empty_think`,则不会重复追加。该参数仅在训练时生效,对纯思考模型和非思考模型无效。设置为`True`可关闭此默认行为。
|
||||
- 注意:该参数在"ms-swift>=4.3.1"增加。在"ms-swift<4.3.1"需手动添加`--loss_scale ignore_empty_think`。
|
||||
- is_binary_loss_scale: 当loss_scale只可能为0/1时,该语义可被labels替代,将loss_scale为0的部分的labels设置为-100,从而兼容liger_kernel降低显存。默认为None,进行自动设置。
|
||||
- sequence_parallel_size: 序列并行大小,默认是1。当前支持CPT/SFT/DPO/GRPO。训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/sequence_parallel)。
|
||||
- template_backend: 选择template后端,可选为'swift'、'jinja',默认为'swift'。如果使用jinja,则使用transformers的`apply_chat_template`。
|
||||
- 注意:jinja的template后端只支持推理,不支持训练(无法确定损失计算的tokens范围)。
|
||||
- response_prefix: response的前缀字符,该参数只在推理时生效。默认为None,根据enable_thinking参数和模版类型确定。
|
||||
- enable_thinking: 该参数在推理时生效,代表是否开启thinking模式。默认为None,默认值由模板(模型)类型确定(混合思考/非思考模板为False,思考模板为True)。若enable_thinking为False,则增加非思考前缀,例如Qwen3-8B混合思考模型增加前缀`'<think>\n\n</think>\n\n'`,Qwen3-8B-Thinking则不增加前缀。若enable_thinking为True,则增加思考前缀,例如`'<think>\n'`。注意:该参数的优先级低于`response_prefix`参数。
|
||||
- 注意:"ms-swift<4.3.1",混合思考模板默认值为True,在"ms-swift>=4.3.1"修改为False。
|
||||
- preserve_thinking: 是否在推理和训练时,对历史思考内容进行保留。当设置为True时,则保留所有轮次的思考内容。若设置为False,则只保留最后一轮的思考内容(即最后一个user信息后的内容)。默认为None。
|
||||
- 默认行为:对于思考模型(思考/混合思考)或显式开启enable_thinking,我们会在推理和训练时,默认设置为False,只保留最后一轮的思考内容。若训练时的`loss_scale`基本策略不为'last_round',例如为'default',则默认为True,不对历史的思考内容进行删除。
|
||||
- add_non_thinking_prefix: 该参数只在训练时生效,代表是否对数据样本assistant部分**不以思考标记`'<think>'`开头**的数据样本增加非思考前缀(通常混合思考模型含非思考前缀)。该特性可以让swift内置的数据集可以训练混合思考模型。默认值为True。例如:例如Qwen3-8B混合思考模型的非思考前缀为`'<think>\n\n</think>\n\n'`,Qwen3-8B-Thinking/Instruct的非思考前缀为`''`。注意:训练时,loss_scale的基本策略为last_round,则只对最后一轮做此修改;否则,例如为'default'、'all',则对每一轮数据做此修改。若设置为False,则不对数据样本增加非思考前缀。
|
||||
|
||||
### 生成参数
|
||||
参考[generation_config](https://huggingface.co/docs/transformers/main_classes/text_generation#transformers.GenerationConfig)文档。
|
||||
|
||||
- 🔥max_new_tokens: 推理最大生成新tokens的数量。默认为None,无限制。
|
||||
- temperature: 温度参数,温度越高,输出的随机性越大。默认为None,读取'generation_config.json'。
|
||||
- 你可以设置`--temperature 0`或者`--top_k 1`以取消推理随机性。
|
||||
- top_k: top_k参数,保留概率最高的top_k数量 tokens用于生成,默认为None。读取'generation_config.json'。
|
||||
- top_p: top_p参数,保留概率最高的累计概率达到 top_p 的tokens用于生成,默认为None。读取generation_config.json。
|
||||
- repetition_penalty: 重复惩罚参数。1.0 表示不进行惩罚。默认为None,读取generation_config.json。
|
||||
- num_beams: beam search的并行保留数量,默认为1。
|
||||
- 🔥stream: 流式输出,默认为`None`,即使用交互式界面时为True,数据集批量推理时为False。
|
||||
- stop_words: 除了eos_token外额外的停止词,默认为`[]`。
|
||||
- 注意:eos_token会在输出respsone中被删除,额外停止词会在输出中保留。
|
||||
- logprobs: 是否输出logprobs,默认为False。
|
||||
- top_logprobs: 输出top_logprobs的数量,默认为None。
|
||||
- structured_outputs_regex: 结构化输出(引导解码)的正则表达式模式。设置后,模型生成将被约束为匹配指定的正则表达式模式。仅在`infer_backend`为`vllm`时生效。默认为`None`。
|
||||
|
||||
### 量化参数
|
||||
以下为加载模型时量化的参数,具体含义可以查看[量化](https://huggingface.co/docs/transformers/main/en/main_classes/quantization)文档。这里不包含`swift export`中涉及的`gptq`、`awq`量化参数。
|
||||
|
||||
- 🔥quant_method: 加载模型时采用的量化方法,可选项为'bnb'、'hqq'、'eetq'、'quanto'和'fp8',默认为None。
|
||||
- 若对awq/gptq量化模型进行qlora训练,则不需要设置额外`quant_method`等量化参数。
|
||||
- 🔥quant_bits: 量化bits数,默认为None。
|
||||
- hqq_axis: hqq量化axis,默认为None。
|
||||
- bnb_4bit_compute_dtype: bnb量化计算类型,可选为`float16`、`bfloat16`、`float32`。默认为None,设置为`torch_dtype`。
|
||||
- bnb_4bit_quant_type: bnb量化类型,支持`fp4`和`nf4`,默认为`nf4`。
|
||||
- bnb_4bit_use_double_quant: 是否使用双重量化,默认为`True`。
|
||||
- bnb_4bit_quant_storage: bnb量化存储类型,默认为None。
|
||||
|
||||
### RAY参数
|
||||
|
||||
- use_ray: boolean类型。是否使用ray,默认为`False`
|
||||
- ray_exp_name: ray实验名字,这个字段会用作cluster和worker名称前缀,可以不填
|
||||
- device_groups: 字符串(jsonstring)类型。在使用ray时,该字段必须配置,具体可以查看[ray文档](Ray.md)。
|
||||
|
||||
### yaml/json支持
|
||||
|
||||
这里以`swift sft`为例子,yaml/json的方式启动也支持`swift infer/rlhf/...`以及`megatron sft/rlhf`。请参考[这里的例子](https://github.com/modelscope/ms-swift/tree/main/examples/yaml)。
|
||||
- yaml/json文件会在训练/推理后,存储在`output_dir`中。
|
||||
|
||||
```shell
|
||||
swift sft xxx.yaml
|
||||
swift sft xxx.json
|
||||
```
|
||||
|
||||
xxx.yaml/xxx.json的内容为具体命令行配置:
|
||||
|
||||
```yaml
|
||||
model: "Qwen/Qwen2.5-7B-Instruct"
|
||||
dataset: "swift/self-cognition#500"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "Qwen/Qwen2.5-7B-Instruct",
|
||||
"dataset": "swift/self-cognition#500"
|
||||
}
|
||||
```
|
||||
|
||||
你也可以混合使用yaml和命令行方式。例如yaml为不经常修改的参数,命令行传入常修改参数。
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer examples/yaml/deepspeed/infer.yaml \
|
||||
--adapters output/vx-xxx/checkpoint-xxx
|
||||
```
|
||||
|
||||
如何在yaml/json中指定环境环境变量:
|
||||
```yaml
|
||||
ENV:
|
||||
MAX_PIXELS: '1003520'
|
||||
VIDEO_MAX_PIXELS: '50176'
|
||||
FPS_MAX_FRAMES: '12'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"ENV": {
|
||||
"MAX_PIXELS": "1003520",
|
||||
"VIDEO_MAX_PIXELS": "50176",
|
||||
"FPS_MAX_FRAMES": "12"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 原子参数
|
||||
|
||||
### Seq2SeqTrainer参数
|
||||
|
||||
该参数列表继承自transformers `Seq2SeqTrainingArguments`,ms-swift对其默认值进行了覆盖。未列出的请参考[HF官方文档](https://huggingface.co/docs/transformers/main/en/main_classes/trainer#transformers.Seq2SeqTrainingArguments)。
|
||||
|
||||
- 🔥output_dir: 模型预测结果和检查点将被写入的输出目录。默认为None,设置为`'output/<model_name>'`。
|
||||
- 🔥gradient_checkpointing: 是否使用gradient_checkpointing,默认为True。该参数可以显著降低显存占用,但降低训练速度。
|
||||
- 🔥vit_gradient_checkpointing: 多模态模型训练时,是否对vit部分开启gradient_checkpointing。默认为None,即当`--freeze_vit`为`false`时开启。例子参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/multimodal/vit_gradient_checkpointing.sh)。
|
||||
- 🔥deepspeed: 默认为None。可以设置为'zero0', 'zero1', 'zero2', 'zero3', 'zero2_offload', 'zero3_offload'来使用ms-swift内置的deepspeed配置文件。你也可以传入自定义deepspeed配置文件的路径。
|
||||
- zero_hpz_partition_size: 默认为None,这个参数是ZeRO++的特性,即node内模型分片,node间数据分片,如果遇到grad_norm NaN,请尝试使用`--torch_dtype float16`。
|
||||
- deepspeed_autotp_size: DeepSpeed张量并行大小,默认为1。使用DeepSpeed AutoTP时需将参数`--deepspeed`设置为'zero0'、'zero1'或'zero2'。(注意:该功能只支持全参数)
|
||||
- 🔥fsdp: FSDP2分布式训练配置。默认为None。可以设置为'fsdp2'来使用ms-swift内置的FSDP2配置文件。你也可以传入自定义FSDP配置文件的路径。FSDP2是PyTorch原生的分布式训练方案,与DeepSpeed二选一使用。
|
||||
- 🔥per_device_train_batch_size: 默认值1。
|
||||
- 🔥per_device_eval_batch_size: 默认值1。
|
||||
- 🔥gradient_accumulation_steps: 梯度累加。**默认为None,即设置gradient_accumulation_steps使得total_batch_size>=16**。total_batch_size等于`per_device_train_batch_size * gradient_accumulation_steps * world_size`。在GRPO训练中,默认为1。
|
||||
- 在CPT/SFT训练中,梯度累加的训练效果等价使用更大的batch_size,但在RLHF训练中,训练效果并不等价。
|
||||
- weight_decay: weight衰减系数,默认值0.1。
|
||||
- adam_beta1: Adam系列优化器中一阶矩估计(动量)的指数衰减率。默认为0.9。
|
||||
- adam_beta2: Adam系列优化器中二阶矩估计(方差)的指数衰减率。默认为0.95。
|
||||
- adam_epsilon: Adam系列优化器中用于数值稳定性的epsilon值。默认为1e-8。
|
||||
- 🔥learning_rate: 学习率,**全参数训练默认为1e-5,LoRA训练等tuners为1e-4**。
|
||||
- 提示:若要设置`min_lr`,您可以传入参数`--lr_scheduler_type cosine_with_min_lr --lr_scheduler_kwargs '{"min_lr": 1e-6}'`。
|
||||
- 🔥vit_lr: 当训练多模态大模型时,该参数指定vit的学习率,默认为None,等于learning_rate。通常与`--freeze_vit`、`--freeze_aligner`参数结合使用。
|
||||
- 提示:在日志中打印的"learning_rate"为`param_groups[0]`的学习率,其中param_groups的顺序依次是vit, aligner, llm(若含可训练参数)。
|
||||
- 🔥aligner_lr: 当训练多模态大模型时,该参数指定aligner的学习率,默认为None,等于learning_rate。
|
||||
- lr_scheduler_type: lr_scheduler类型,默认为'cosine'。常见选择:'linear', 'constant', 'cosine_with_min_lr'。
|
||||
- lr_scheduler_kwargs: lr_scheduler其他参数。默认为None。
|
||||
- gradient_checkpointing_kwargs: 传入`torch.utils.checkpoint`中的参数。例如设置为`--gradient_checkpointing_kwargs '{"use_reentrant": false}'`。默认为None。
|
||||
- 注意:当使用DDP而不使用deepspeed/fsdp,且gradient_checkpointing_kwargs为None,会默认设置其为`'{"use_reentrant": false}'`而避免出现报错。
|
||||
- full_determinism: 确保训练中获得可重现的结果,注意:这会对性能产生负面影响。默认为False。
|
||||
- 🔥report_to: 默认值为`tensorboard`。你也可以指定`--report_to tensorboard wandb swanlab`、`--report_to all`。
|
||||
- 如果你指定了`--report_to wandb`,你可以通过`WANDB_PROJECT`设置项目名称,`WANDB_API_KEY`指定账户对应的API KEY。
|
||||
- logging_first_step: 是否记录第一个step的日志,默认为True。
|
||||
- logging_steps: 日志打印间隔,默认为5。
|
||||
- router_aux_loss_coef: 用于moe模型训练时,设置 aux_loss 的权重,默认为`0.`。
|
||||
- enable_dft_loss: 是否在SFT训练中使用[DFT](https://arxiv.org/abs/2508.05629) (Dynamic Fine-Tuning) loss,默认为False。
|
||||
- enable_channel_loss: 启用channel loss,默认为`False`。你需要在数据集中准备"channel"字段,ms-swift会根据该字段分组统计loss(若未准备"channel"字段,则归为默认`None` channel)。数据集格式参考[channel loss](../Customization/Custom-dataset.md#channel-loss)。channel loss兼容packing/padding_free/loss_scale等技术。
|
||||
- safe_serialization: 是否存储为safetensors,默认为True。
|
||||
- max_shard_size: 单存储文件最大大小,默认'5GB'。
|
||||
- logging_dir: tensorboard日志保存路径。默认为None,即设置为`f'{self.output_dir}/runs'`。
|
||||
- predict_with_generate: 验证时使用生成式的方式,默认为False。
|
||||
- metric_for_best_model: 默认为None,即当`predict_with_generate`设置为False时,设置为'loss',否则设置为'rouge-l'(在PPO训练时,不进行默认值设置;GRPO训练设置为'reward')。
|
||||
- greater_is_better: 默认为None,即当`metric_for_best_model`含'loss'时,设置为False,否则设置为True。
|
||||
- max_epochs: 训练到`max_epochs`时强制退出训练,并对权重进行验证和保存。该参数在使用流式数据集时很有用。默认为None。
|
||||
|
||||
其他重要参数:
|
||||
- 🔥num_train_epochs: 训练的epoch数,默认为3。
|
||||
- 🔥save_strategy: 保存模型的策略,可选为'no'、'steps'、'epoch',默认为'steps'。
|
||||
- 🔥save_steps: 默认为500。
|
||||
- 🔥eval_strategy: 评估策略。默认为None,跟随`save_strategy`的策略。
|
||||
- 若不使用`val_dataset`和`eval_dataset`且`split_dataset_ratio`为0,则默认为'no'。
|
||||
- 🔥eval_steps: 默认为None,如果存在评估数据集,则跟随`save_steps`的策略。
|
||||
- eval_on_start: 是否在训练前执行一次评估步骤,以确保验证步骤能正常工作。默认为False。
|
||||
- 🔥save_total_limit: 最多保存的checkpoint数,会将过期的checkpoint进行删除。默认为None,保存所有的checkpoint。若设置为2,则保存best checkpoint和last checkpoint。
|
||||
- max_steps: 最大训练的steps数。在数据集为流式时需要被设置。默认为-1。
|
||||
- 🔥warmup_ratio: 默认为0.。
|
||||
- save_on_each_node: 在每一个节点都进行权重保存。默认为False。该参数在多机训练时需要被考虑。
|
||||
- 提示:在多机训练时,通常将`output_dir`设置为节点共享目录,因此无需额外设置该参数。
|
||||
- save_only_model: 是否只保存模型权重而不包含优化器状态,随机种子状态等内容,这在全参数训练时可以减少保存的时间消耗和空间占用。默认为False。
|
||||
- 🔥resume_from_checkpoint: 断点续训参数,指定checkpoint路径。默认为None。
|
||||
- 提示:**断点续训请保持其他参数不变,额外增加`--resume_from_checkpoint checkpoint_dir`**。权重等信息将在trainer中读取。
|
||||
- 注意: resume_from_checkpoint会读取模型权重,优化器状态,随机种子,并从上次训练的steps继续开始训练。你可以指定`--resume_only_model`只读取模型权重。
|
||||
- resume_only_model: 默认为False。如果在指定resume_from_checkpoint的基础上,将该参数设置为True,则仅resume模型权重,而忽略优化器状态和随机种子。
|
||||
- 注意:**resume_only_model默认将进行数据跳过**,此行为可通过 `ignore_data_skip` 参数控制。
|
||||
- ignore_data_skip: 当设置`resume_from_checkpoint`和`resume_only_model`时,该参数控制是否跳过已经训练的数据,并将epoch和迭代数等训练状态进行恢复。默认为False。若设置为True,则将不加载训练状态并不进行数据跳过,将从迭代数0开始训练。
|
||||
- 🔥ddp_find_unused_parameters: 默认为None。
|
||||
- 🔥dataloader_num_workers: 默认为None,若是windows平台,则设置为0,否则设置为1。
|
||||
- dataloader_pin_memory: 默认为True。
|
||||
- dataloader_persistent_workers: 默认为False。
|
||||
- dataloader_prefetch_factor: 默认为None。若 `dataloader_num_workers > 0`,则设置为2。每个工作进程预先加载的批次数量。2 表示所有工作进程总共会预取 2 * num_workers 个批次。
|
||||
- train_dataloader_shuffle: CPT/SFT训练的dataloader是否随机,默认为True。该参数对IterableDataset无效(即对流式数据集失效)。IterableDataset采用顺序的方式读取。
|
||||
- optim: 优化器,默认值为 `"adamw_torch"` (对于 torch>=2.8 为 `"adamw_torch_fused"`)。完整的优化器列表请参见 [training_args.py](https://github.com/huggingface/transformers/blob/main/src/transformers/training_args.py) 中的 `OptimizerNames`。
|
||||
- optim_args: 提供给优化器的可选参数,默认为None。
|
||||
- group_by_length: 是否在训练数据集中将长度大致相同的样本分组在一起(有随机因素),以最小化填充并确保各节点与进程的负载均衡以提高效率。默认为False。具体算法参考`transformers.trainer_pt_utils.get_length_grouped_indices`。
|
||||
- 🔥neftune_noise_alpha: neftune添加的噪声系数。默认为0,通常可以设置为5、10、15。
|
||||
- 🔥use_liger_kernel: 是否启用[Liger](https://github.com/linkedin/Liger-Kernel)内核加速训练并减少显存消耗。默认为False。示例shell参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/liger)。
|
||||
- 注意:liger_kernel不支持device_map,请使用DDP/DeepSpeed进行多卡训练。liger_kernel目前只支持`task_type='causal_lm'`。
|
||||
- average_tokens_across_devices: 是否在设备之间进行token数平均。如果设置为True,将使用all_reduce同步`num_tokens_in_batch`以进行精确的损失计算。默认为False。
|
||||
- max_grad_norm: 梯度裁剪。默认为1.。
|
||||
- 注意:日志中的grad_norm记录的是裁剪前的值。
|
||||
- push_to_hub: 推送checkpoint到hub。默认为False。
|
||||
- hub_model_id: 默认为None。
|
||||
- hub_private_repo: 默认为False。
|
||||
|
||||
### Tuner参数
|
||||
- 🔥freeze_llm: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_llm设置为True会将LLM部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_llm设置为True将会取消在LLM部分添加LoRA模块。该参数默认为False。
|
||||
- 🔥freeze_vit: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_vit设置为True会将vit部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_vit设置为True将会取消在vit部分添加LoRA模块。该参数默认为True。
|
||||
- 注意:**这里的vit不仅限于vision_tower, 也包括audio_tower**。若是Omni模型,若你只希望对vision_tower加LoRA,而不希望对audio_tower加LoRA,你可以修改[这里的代码](https://github.com/modelscope/ms-swift/blob/a5d4c0a2ce0658cef8332d6c0fa619a52afa26ff/swift/llm/model/model_arch.py#L544-L554)。
|
||||
- 🔥freeze_aligner: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_aligner设置为True会将aligner(也称为projector)部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_aligner设置为True将会取消在aligner部分添加LoRA模块。该参数默认为True。
|
||||
- 🔥target_modules: 指定lora模块, 默认为`['all-linear']`。你也可以设置为module的后缀,例如:`--target_modules q_proj k_proj v_proj`。该参数不限于LoRA,可用于其他tuners。
|
||||
- 注意:在LLM和多模态LLM中,'all-linear'的行为有所不同。若是LLM则自动寻找除lm_head外的linear并附加tuner;**若是多模态LLM,则默认只在LLM上附加tuner,该行为可以被`freeze_llm`、`freeze_vit`、`freeze_aligner`控制**。
|
||||
- 🔥target_regex: 指定lora模块的regex表达式,默认为`None`。如果该值传入,则target_modules参数失效。例如你可以设置`--target_regex '^(language_model).*\.(q_proj|k_proj|v_proj|o_proj|gate_proj|up_proj|down_proj)$'`,将符合该正则的模块指定为LoRA模块。该参数不限于LoRA,可用于其他tuners。
|
||||
- target_parameters: 要替换为LoRA的参数名称列表。该参数的行为与 `target_modules` 类似,但传入的应是参数名称而不是模块名称。该特性需要安装"peft>=0.17.0"。例如,在 Hugging Face Transformers 中许多混合专家(MoE)层中,并未使用 `nn.Linear`,而是使用了 `nn.Parameter`。这时可以使用target_parameters参数实现。
|
||||
- 注意:该参数需要设置`lora_dropout`为 0。
|
||||
- init_weights: 初始化weights的方法,LoRA可以指定为'true'、'false'、'gaussian'、'pissa'、'pissa_niter_[number of iters]'、'olora'、'loftq'、'lora-ga',Bone可以指定为'true'、'false'、'bat'。默认值'true'。
|
||||
- 🔥modules_to_save: 在已附加tuner后,额外指定一部分原模型模块参与训练和存储。默认为`[]`。该参数不限于LoRA,可用于其他tuners。例如设置为`--modules_to_save embed_tokens lm_head`,在LoRA训练中解开embed_tokens和lm_head层进行训练,这两部分的权重信息最终会保存在`adapter_model.safetensors`中。
|
||||
|
||||
#### 全参
|
||||
- freeze_parameters: 需要被冻结参数的前缀,默认为`[]`。
|
||||
- freeze_parameters_regex: 需要被冻结参数的正则表达式,默认为None。
|
||||
- freeze_parameters_ratio: 从下往上冻结的参数比例,默认为0。可设置为1将所有参数冻结,结合`trainable_parameters`设置可训练参数。
|
||||
- trainable_parameters: 额外可训练参数的前缀,默认为`[]`。
|
||||
- trainable_parameters_regex: 匹配额外可训练参数的正则表达式,默认为None。
|
||||
- 备注:`trainable_parameters`、`trainable_parameters_regex`的优先级高于`freeze_parameters`、`freeze_parameters_regex`和`freeze_parameters_ratio`。例如:当指定全参数训练时,会将所有模块设置为可训练的状态,随后根据`freeze_parameters`、`freeze_parameters_regex`、`freeze_parameters_ratio`将部分参数冻结,最后根据`trainable_parameters`、`trainable_parameters_regex`重新打开部分参数参与训练。
|
||||
|
||||
#### LoRA
|
||||
- 🔥lora_rank: 默认为`8`。
|
||||
- 🔥lora_alpha: 默认为`32`。
|
||||
- lora_dropout: 默认为`0.05`。
|
||||
- lora_bias: 默认为`'none'`,可以选择的值: 'none'、'all'。如果你要将bias全都设置为可训练,你可以设置为`'all'`。
|
||||
- lora_dtype: 指定lora模块的dtype类型。支持'float16'、'bfloat16'、'float32'。默认为None,跟随peft行为。
|
||||
- 🔥use_dora: 默认为`False`,是否使用`DoRA`。
|
||||
- use_rslora: 默认为`False`,是否使用`RS-LoRA`。
|
||||
- 🔥lorap_lr_ratio: LoRA+参数,默认值`None`,建议值为`10~16`。使用lora时额外指定该参数可使用lora+。
|
||||
|
||||
##### LoRA-GA
|
||||
- lora_ga_batch_size: 默认值为 `2`。在 LoRA-GA 中估计梯度以进行初始化时使用的批处理大小。
|
||||
- lora_ga_iters: 默认值为 `2`。在 LoRA-GA 中估计梯度以进行初始化时的迭代次数。
|
||||
- lora_ga_max_length: 默认值为 `1024`。在 LoRA-GA 中估计梯度以进行初始化时的最大输入长度。
|
||||
- lora_ga_direction: 默认值为 `ArB2r`。在 LoRA-GA 中使用估计梯度进行初始化时的初始方向。允许的值有:`ArBr`、`A2rBr`、`ArB2r` 和 `random`。
|
||||
- lora_ga_scale: 默认值为 `stable`。LoRA-GA 的初始化缩放方式。允许的值有:`gd`、`unit`、`stable` 和 `weightS`。
|
||||
- lora_ga_stable_gamma: 默认值为 `16`。当初始化时选择 `stable` 缩放时的 gamma 值。
|
||||
|
||||
#### FourierFt
|
||||
|
||||
FourierFt使用`target_modules`、`target_regex`、`modules_to_save`三个参数,含义见上面文档中的描述。额外参数包括:
|
||||
|
||||
- fourier_n_frequency: 傅里叶变换的频率数量, `int`类型, 类似于LoRA中的`r`. 默认值`2000`.
|
||||
- fourier_scaling: W矩阵的缩放值, `float`类型, 类似LoRA中的`lora_alpha`. 默认值`300.0`.
|
||||
|
||||
#### BOFT
|
||||
|
||||
BOFT使用`target_modules`、`target_regex`、`modules_to_save`三个参数,含义见上面文档中的描述。额外参数包括:
|
||||
|
||||
- boft_block_size: BOFT块尺寸, 默认值4.
|
||||
- boft_block_num: BOFT块数量, 不能和`boft_block_size`同时使用.
|
||||
- boft_dropout: boft的dropout值, 默认0.0.
|
||||
|
||||
#### Vera
|
||||
|
||||
Vera使用`target_modules`、`target_regex`、`modules_to_save`三个参数,含义见上面文档中的描述。额外参数包括:
|
||||
|
||||
- vera_rank: Vera Attention的尺寸, 默认值256.
|
||||
- vera_projection_prng_key: 是否存储Vera映射矩阵, 默认为True.
|
||||
- vera_dropout: Vera的dropout值, 默认`0.0`.
|
||||
- vera_d_initial: Vera的d矩阵的初始值, 默认`0.1`.
|
||||
|
||||
#### GaLore
|
||||
|
||||
- 🔥use_galore: 默认值False, 是否使用GaLore.
|
||||
- galore_target_modules: 默认值None, 不传的情况下对attention和mlp应用GaLore.
|
||||
- galore_rank: 默认值128, GaLore的rank值.
|
||||
- galore_update_proj_gap: 默认值50, 分解矩阵的更新间隔.
|
||||
- galore_scale: 默认值1.0, 矩阵权重系数.
|
||||
- galore_proj_type: 默认值`std`, GaLore矩阵分解类型.
|
||||
- galore_optim_per_parameter: 默认值False, 是否给每个Galore目标Parameter设定一个单独的optimizer.
|
||||
- galore_with_embedding: 默认值False, 是否对embedding应用GaLore.
|
||||
- galore_quantization: 是否使用q-galore. 默认值`False`.
|
||||
- galore_proj_quant: 是否对SVD分解矩阵做量化, 默认`False`.
|
||||
- galore_proj_bits: SVD量化bit数.
|
||||
- galore_proj_group_size: SVD量化分组数.
|
||||
- galore_cos_threshold: 投影矩阵更新的cos相似度阈值. 默认值0.4.
|
||||
- galore_gamma_proj: 在投影矩阵逐渐相似后会拉长更新间隔, 本参数为每次拉长间隔的系数, 默认值2.
|
||||
- galore_queue_size: 计算投影矩阵相似度的队列长度, 默认值5.
|
||||
|
||||
#### LISA
|
||||
|
||||
注意: LISA仅支持全参数,即`--tuner_type full`。
|
||||
|
||||
- 🔥lisa_activated_layers: 默认值`0`,代表不使用LISA,改为非0代表需要激活的layers个数,建议设置为2或8。
|
||||
- lisa_step_interval: 默认值`20`,多少iter切换可反向传播的layers。
|
||||
|
||||
#### UNSLOTH
|
||||
|
||||
🔥unsloth无新增参数,对已有参数进行调节即可支持,例如:
|
||||
|
||||
```
|
||||
--tuner_backend unsloth
|
||||
--tuner_type full/lora
|
||||
--quant_bits 4
|
||||
```
|
||||
|
||||
#### LLAMAPRO
|
||||
|
||||
- 🔥llamapro_num_new_blocks: 默认值`4`, 插入的新layers总数.
|
||||
- llamapro_num_groups: 默认值`None`, 分为多少组插入new_blocks, 如果为`None`则等于`llamapro_num_new_blocks`, 即每个新的layer单独插入原模型.
|
||||
|
||||
#### AdaLoRA
|
||||
|
||||
以下参数`tuner_type`设置为`adalora`时生效. adalora的`target_modules`等参数继承于lora的对应参数,但`lora_dtype`参数不生效。
|
||||
|
||||
- adalora_target_r: 默认值`8`, adalora的平均rank.
|
||||
- adalora_init_r: 默认值`12`, adalora的初始rank.
|
||||
- adalora_tinit: 默认值`0`, adalora的初始warmup.
|
||||
- adalora_tfinal: 默认值`0`, adalora的final warmup.
|
||||
- adalora_deltaT: 默认值`1`, adalora的step间隔.
|
||||
- adalora_beta1: 默认值`0.85`, adalora的EMA参数.
|
||||
- adalora_beta2: 默认值`0.85`, adalora的EMA参数.
|
||||
- adalora_orth_reg_weight: 默认值`0.5`, adalora的正则化参数.
|
||||
|
||||
#### ReFT
|
||||
|
||||
以下参数`tuner_type`设置为`reft`时生效.
|
||||
|
||||
> 1. ReFT无法合并tuner
|
||||
> 2. ReFT和gradient_checkpointing不兼容
|
||||
> 3. 如果使用DeepSpeed遇到问题请暂时卸载DeepSpeed
|
||||
|
||||
- 🔥reft_layers: ReFT应用于哪些层上, 默认为`None`, 代表所有层, 可以输入层号的list, 例如reft_layers 1 2 3 4`
|
||||
- 🔥reft_rank: ReFT矩阵的rank, 默认为`4`.
|
||||
- reft_intervention_type: ReFT的类型, 支持'NoreftIntervention', 'LoreftIntervention', 'ConsreftIntervention', 'LobireftIntervention', 'DireftIntervention', 'NodireftIntervention', 默认为`LoreftIntervention`.
|
||||
- reft_args: ReFT Intervention中的其他支持参数, 以json-string格式输入.
|
||||
|
||||
### vLLM参数
|
||||
参数含义可以查看[vllm文档](https://docs.vllm.ai/en/latest/serving/engine_args.html)。
|
||||
|
||||
- 🔥vllm_gpu_memory_utilization: GPU内存比例,取值范围为0到1。默认值`0.9`。
|
||||
- 🔥vllm_tensor_parallel_size: tp并行数,默认为`1`。
|
||||
- vllm_pipeline_parallel_size: pp并行数,默认为`1`。
|
||||
- vllm_data_parallel_size: dp并行数,默认为`1`,在`swift deploy/rollout`命令中生效。
|
||||
- 若在`swift infer`中,使用`NPROC_PER_NODE`来设置dp并行数。参考这里的[例子](https://github.com/modelscope/ms-swift/blob/main/examples/infer/vllm/mllm_ddp.sh)。
|
||||
- vllm_enable_expert_parallel: 开启专家并行,默认为False。
|
||||
- vllm_max_num_seqs: 单次迭代中处理的最大序列数,默认为`256`。
|
||||
- 🔥vllm_max_model_len: 模型支持的最大长度。默认为`None`,即从config.json中读取。
|
||||
- vllm_disable_custom_all_reduce: 禁用自定义的 all-reduce 内核,回退到 NCCL。为了稳定性,默认为`True`。
|
||||
- vllm_enforce_eager: vllm使用pytorch eager模式还是建立cuda graph,默认为`False`。设置为True可以节约显存,但会影响效率。
|
||||
- vllm_mm_processor_cache_gb: 多模态处理器缓存大小(GiB),用于缓存已处理的多模态输入(如图像、视频)避免重复处理。默认为`4`。设置为`0`可禁用缓存但会降低性能(不推荐)。仅对多模态模型生效。
|
||||
- vllm_speculative_config: 推测解码配置,传入json字符串。默认为None。
|
||||
- vllm_disable_cascade_attn: 是否强制关闭V1引擎的cascade attention实现以防止潜在数值误差,默认为False,由vLLM内部逻辑决定是否使用。
|
||||
- 🔥vllm_limit_mm_per_prompt: 控制vllm使用多图,默认为`None`。例如传入`--vllm_limit_mm_per_prompt '{"image": 5, "video": 2}'`。
|
||||
- vllm_max_lora_rank: 默认为`16`。vllm对于lora支持的参数。
|
||||
- vllm_quantization: vllm可以在内部量化模型,参数支持的值详见[这里](https://docs.vllm.ai/en/latest/serving/engine_args.html)。
|
||||
- 🔥vllm_enable_prefix_caching: 开启vllm的自动前缀缓存,节约重复查询前缀的处理时间,加快推理效率。默认为`None`,跟随vLLM行为。
|
||||
- vllm_use_async_engine: vLLM backend下是否使用async engine。默认为None,会根据场景自动设置:encode任务(embedding、seq_cls、reranker、generative_reranker)默认为True,部署场景(swift deploy)默认为True,其他情况默认为False。注意:encode任务需使用async engine。
|
||||
- vllm_reasoning_parser: 推理解析器类型,用于思考模型的思维链内容解析。默认为`None`。仅用于 `swift deploy` 命令。可选的种类参考[vLLM文档](https://docs.vllm.ai/en/latest/features/reasoning_outputs.html#streaming-chat-completions)。
|
||||
- vllm_engine_kwargs: vllm的额外参数,格式为json字符串。默认为None。
|
||||
|
||||
### SGLang参数
|
||||
参数含义可以查看[sglang文档](https://docs.sglang.ai/backend/server_arguments.html)。
|
||||
|
||||
- 🔥sglang_tp_size: tp数。默认为1。
|
||||
- sglang_pp_size: pp数。默认为1。
|
||||
- sglang_dp_size: dp数。默认为1。
|
||||
- sglang_ep_size: ep数。默认为1。
|
||||
- sglang_enable_ep_moe: 是否启用ep moe。默认为False。该参数已在最新sglang中移除。
|
||||
- sglang_mem_fraction_static: 用于静态分配模型权重和KV缓存内存池的GPU内存比例。如果你遇到GPU内存不足错误,可以尝试降低该值。默认为None。
|
||||
- sglang_context_length: 模型的最大上下文长度。默认为 None,将使用模型的`config.json`中的值。
|
||||
- sglang_disable_cuda_graph: 禁用CUDA图。默认为False。
|
||||
- sglang_quantization: 量化方法。默认为None。
|
||||
- sglang_kv_cache_dtype: 用于k/v缓存存储的数据类型。'auto'表示将使用模型的数据类型。'fp8_e5m2'和'fp8_e4m3'适用于CUDA 11.8及以上版本。默认为'auto'。
|
||||
- sglang_enable_dp_attention: 为注意力机制启用数据并行,为前馈网络(FFN)启用张量并行。数据并行的规模(dp size)应等于张量并行的规模(tp size)。目前支持DeepSeek-V2/3以及Qwen2/3 MoE模型。默认为False。
|
||||
- sglang_disable_custom_all_reduce: 禁用自定义的 all-reduce 内核,回退到 NCCL。为了稳定性,默认为True。
|
||||
- sglang_speculative_algorithm: 推测算法,可选值:None、"EAGLE"、"EAGLE3"、"NEXTN"、"STANDALONE"、"NGRAM"。默认为None。
|
||||
- sglang_speculative_num_steps: 在推测解码中从草稿模型采样的步数。默认值为None。
|
||||
- sglang_speculative_eagle_topk: 在 EAGLE2 算法中每步从草稿模型采样的 token 数量。默认值为None。
|
||||
- sglang_speculative_num_draft_tokens: 在推测解码中从草稿模型采样的 token 数量。默认值为None。
|
||||
|
||||
### LMDeploy参数
|
||||
参数含义可以查看[lmdeploy文档](https://lmdeploy.readthedocs.io/en/latest/api/pipeline.html#turbomindengineconfig)。
|
||||
|
||||
- 🔥lmdeploy_tp: tensor并行度。默认为`1`。
|
||||
- lmdeploy_session_len: 最大会话长度。默认为`None`。
|
||||
- lmdeploy_cache_max_entry_count: k/v缓存占用的GPU内存百分比。默认为`0.8`。
|
||||
- lmdeploy_quant_policy: 默认为0。当需要将k/v量化为4或8位时,分别将其设置为4或8。
|
||||
- lmdeploy_vision_batch_size: 传入VisionConfig的max_batch_size参数。默认为`1`。
|
||||
|
||||
### 合并参数
|
||||
|
||||
- 🔥merge_lora: 是否合并lora,本参数支持lora、llamapro、longlora,默认为False。例子参数[这里](https://github.com/modelscope/ms-swift/blob/main/examples/export/merge_lora.sh)。
|
||||
- safe_serialization: 是否存储为safetensors,默认为True。
|
||||
- max_shard_size: 单存储文件最大大小,默认'5GB'。
|
||||
|
||||
|
||||
## 集成参数
|
||||
|
||||
### 训练参数
|
||||
训练参数除包含[基本参数](#基本参数)、[Seq2SeqTrainer参数](#Seq2SeqTrainer参数)、[tuner参数](#tuner参数)外,还包含下面的部分:
|
||||
|
||||
- add_version: 在`output_dir`上额外增加目录`'<版本号>-<时间戳>'`防止权重覆盖,默认为True。
|
||||
- check_model: 检查本地模型文件有损坏或修改并给出提示,默认为True。**如果是断网环境,请设置为False**。
|
||||
- 🔥create_checkpoint_symlink: 额外创建checkpoint软链接,方便书写自动化训练脚本。best_model和last_model的软链接路径分别为f'{output_dir}/best'和f'{output_dir}/last'。
|
||||
- 🔥packing: 使用`padding_free`的方式将不同长度的数据样本打包成**近似**统一长度的样本(packing能保证不对完整的序列进行切分),实现训练时各节点与进程的负载均衡(避免长文本拖慢短文本的训练速度),从而提高GPU利用率,保持显存占用稳定。当使用 `--attn_impl flash_attn` 时,可确保packed样本内的不同序列之间相互独立,互不可见。该参数默认为`False`,目前支持 CPT/SFT/DPO/KTO/GKD以及embedding/reranker/seq_cls任务的packing。注意:**packing会导致数据集样本数减少,请自行调节梯度累加数和学习率**。
|
||||
- 注意:Qwen3-Next的packing请使用Megatron-SWIFT。Qwen3.5的transformers生态padding_free/packing支持请使用"ms-swift>=4.3.1"(或使用Megatron-SWIFT),具体参考[Qwen3.5最佳实践](../BestPractices/Qwen3_5-Best-Practice.md)。
|
||||
- packing_length: packing的长度。默认为None,设置为max_length。
|
||||
- packing_num_proc: packing的进程数,默认为1。需要注意的是,不同的`packing_num_proc`,最终形成的packed数据集是不同的。(该参数在流式packing时不生效)。通常不需要修改该值,packing速度远快于tokenize速度。
|
||||
- packing_strategy: packing 算法,可选为'binpack'和'sequential',默认为'binpack'。'binpack'使用 best-fit-decreasing 装箱(会按长度重排样本);'sequential'使用保序的贪心装箱(next-fit:仅维护一个开放 pack,放不下即 flush),按输入顺序逐条装箱,使样本顺序与每个 pack 的边界跟随顺序采样器(建议配合 packing_num_proc=1 以保证全局顺序)。
|
||||
- lazy_tokenize: 是否使用lazy_tokenize。若该参数设置为False,则在训练之前对所有的数据集样本进行tokenize(多模态模型则包括从磁盘中读取图片)。该参数默认为None,在LLM训练中默认为False,而MLLM训练默认为True,节约内存。
|
||||
- 注意:若你要进行图像的数据增强,你需要将lazy_tokenize(或streaming)设置为True,并修改Template类中的encode方法。
|
||||
- use_logits_to_keep: 通过在`forward`中根据labels传入logits_to_keep,减少无效logits的计算与存储,从而减少显存占用并加快训练速度。默认为None,进行自动选择。
|
||||
- acc_strategy: 训练和验证时计算acc的策略。可选为`seq`和`token`级别的acc,默认为`token`。
|
||||
- max_new_tokens: 覆盖生成参数。predict_with_generate=True时的最大生成token数量,默认64。
|
||||
- temperature: 覆盖生成参数。predict_with_generate=True时的temperature,默认0。
|
||||
- optimizer: 使用的optimizers插件(优先级高于`--optim`),默认为None。可选optimizers参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/optimizers/mapping.py)。
|
||||
- loss_type: 自定义的loss_type名称。默认为None,使用模型自带损失函数。可选loss参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss/mapping.py)。
|
||||
- mrl_dims: Embedding训练的[Matryoshka表征学习(MRL)](https://arxiv.org/abs/2205.13147)维度配置,默认为None。格式为`Dict[int, float]`或Json字符串,key为截断的embedding维度,value为该维度对应的loss权重,例如`'{"32": 1.0, "64": 1.0, "128": 1.0}'`。开启后,trainer会对`last_hidden_state`分别截断到每个维度并做L2归一化,再调用`loss_type`对应的loss加权累加。仅在`task_type='embedding'`下生效。
|
||||
- 注意:可支持的最大embedding维度由模型`config.json`中的`hidden_size`决定,key大于`hidden_size`的KV对将被自动忽略。
|
||||
- eval_metric: 自定义eval metric名称。默认为None。可选eval_metric参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/metrics/mapping.py)。
|
||||
- 关于默认值:当`task_type`为'causal_lm', 且`predict_with_generate=True`的情况下默认设置为'nlg'。`task_type` 为'embedding',根据loss_type,默认值为'infonce' 或 'paired'。`task_type`为'reranker/generative_reranker',默认值为'reranker'。
|
||||
- callbacks: 自定义trainer callback,默认为`[]`。可选callbacks参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/callbacks/mapping.py)。例如:通过在`callbacks`中添加`deepspeed_elastic`(可选`graceful_exit`)可以来启用弹性训练。参考[Elastic示例](../BestPractices/Elastic.md)
|
||||
- early_stop_interval: 早停的间隔,会检验best_metric在early_stop_interval个周期内(基于`save_steps`, 建议`eval_steps`和`save_steps`设为同值)没有提升时终止训练。具体代码在[early_stop.py](https://github.com/modelscope/ms-swift/blob/main/swift/callbacks/early_stop.py)中。同时,如果有较为复杂的早停需求,直接覆盖callback.py中的已有实现即可。设置该参数时,自动加入`early_stop`的trainer callback。
|
||||
- eval_use_evalscope: 是否使用evalscope进行训练时评测,需要设置该参数来开启评测,具体使用参考[示例](../Instruction/Evaluation.md#训练中评测)。
|
||||
- eval_dataset: 评测数据集,可设置多个数据集,用空格分割。
|
||||
- eval_dataset_args: 评测数据集参数,json格式,可设置多个数据集的参数。
|
||||
- eval_limit: 评测数据集采样数。
|
||||
- eval_generation_config: 评测时模型推理配置,json格式,默认为`{'max_tokens': 512}`。
|
||||
- use_flash_ckpt: 是否启用[DLRover Flash Checkpoint](https://github.com/intelligent-machine-learning/dlrover)的flash checkpoint。默认为`false`,启用后,权重会先保存至共享内存,之后异步持久化;建议搭配`PYTORCH_CUDA_ALLOC_CONF="expandable_segments:True"` 一起使用,避免训练过程CUDA OOM。
|
||||
|
||||
#### SWANLAB
|
||||
|
||||
- swanlab_token: SwanLab的api-key。你也可以使用`SWANLAB_API_KEY`环境变量指定。
|
||||
- swanlab_project: swanlab的project,可以在页面中预先创建[https://swanlab.cn/space/~](https://swanlab.cn/space/~)或自动创建,默认为"ms-swift"。
|
||||
- swanlab_workspace: 默认为None,会使用api-key对应的username。
|
||||
- swanlab_exp_name: 实验名,可以为空,为空时默认传入--output_dir的值。
|
||||
- swanlab_notification_method: 在训练完成/发生错误时,swanlab的通知方式,具体参考[这里](https://docs.swanlab.cn/plugin/notification-dingtalk.html)。支持'dingtalk'、'lark'、'email'、'discord'、'wxwork'、'slack'。
|
||||
- swanlab_webhook_url: 默认为None。swanlab的`swanlab_notification_method`对应的 webhook url。
|
||||
- swanlab_secret: 默认为None。swanlab的`swanlab_notification_method`对应的 secret。
|
||||
- swanlab_mode: 可选cloud和local,云模式或者本地模式。
|
||||
|
||||
|
||||
### RLHF参数
|
||||
RLHF参数继承于[训练参数](#训练参数)。
|
||||
|
||||
- 🔥rlhf_type: 人类对齐算法类型,支持'dpo'、'orpo'、'simpo'、'kto'、'cpo'、'rm'、'ppo'、'grpo'和'gkd'。默认为'dpo'。
|
||||
- ref_model: 采用dpo、kto、ppo、grpo算法且使用全参数训练时需要传入。默认为None,设置为`--model`。
|
||||
- ref_adapters: 默认为`[]`。若你要使用SFT产生的LoRA权重进行DPO/KTO/GRPO,请在训练时设置`--adapters sft_ckpt --ref_adapters sft_ckpt`。若是此场景的断点续训,则设置`--resume_from_checkpoint rlhf_ckpt --ref_adapters sft_ckpt`。
|
||||
- ref_model_type: 同model_type。默认为None。
|
||||
- ref_model_revision: 同model_revision。默认为None。
|
||||
- 🔥beta: 控制与参考模型偏差程度的参数。beta值越高,表示与参考模型的偏差越小。默认为`None`,使用不同rlhf算法的默认值不同,其中`simpo`算法默认为`2.`,GRPO默认为`0.04`,GKD默认为0.5,其他算法默认为`0.1`。具体参考[文档](./RLHF.md)。
|
||||
- label_smoothing: 是否使用DPO smoothing,默认值为`0`。
|
||||
- max_completion_length: GRPO/PPO/GKD算法中的最大生成长度,默认为512。
|
||||
- 🔥rpo_alpha: 来自[RPO 论文](https://arxiv.org/abs/2404.19733)中的参数,用于控制损失函数中NLL项的权重(即SFT损失),`loss = dpo_loss + rpo_alpha * sft_loss`,论文中推荐设置为`1.`。默认为`None`,即默认不引入sft_loss。
|
||||
- ld_alpha: 来自[LD-DPO 论文](https://arxiv.org/abs/2409.06411),对超出公共前缀部分的logps加权 $\alpha$ 抑制长度偏好。
|
||||
- discopop_tau: 来自 [DiscoPOP 论文](https://arxiv.org/abs/2406.08414)的温度参数 $\tau$ ,用于缩放 log-ratio。默认值0.05。在 loss_type 为 discopop 时生效。
|
||||
- loss_type: 损失类型。默认为None,使用不同的rlhf算法,其默认值不同。
|
||||
- DPO: 可选项参考[文档](https://huggingface.co/docs/trl/main/en/dpo_trainer#loss-functions),支持传入多个值实现混合训练([MPO](https://arxiv.org/abs/2411.10442)), 传入多个值时需要设置参数 loss_weights。默认为`sigmoid`。
|
||||
- GRPO: 参考[GRPO参数](#grpo参数)。
|
||||
- loss_weights: 在 DPO 训练中设置多个 loss_type 时,用于指定各个损失项的权重。
|
||||
- cpo_alpha: CPO/SimPO loss 中 nll loss的系数, 默认为`1.`。
|
||||
- simpo_gamma: SimPO算法中的reward margin项,论文建议设置为0.5-1.5,默认为`1.`。
|
||||
- desirable_weight: KTO算法中用于抵消 desirable 和 undesirable 数量不均衡的影响,对 desirable 损失按该系数进行加权,默认为`1.`。
|
||||
- undesirable_weight: KTO算法中用于抵消 desirable 和 undesirable 数量不均衡的影响,对 undesirable 损失按该系数进行加权,默认为`1.`。
|
||||
- center_rewards_coefficient: 用于RM训练。用于激励奖励模型输出均值为零的奖励的系数,具体查看这篇[论文](https://huggingface.co/papers/2312.09244)。推荐值:0.01。
|
||||
- loss_scale: 覆盖模板参数。rlhf训练时,默认为'last_round'。
|
||||
- temperature: 默认为0.9,该参数将在PPO、GRPO、GKD中使用。
|
||||
- top_k: rollout采样的top-k参数,-1表示不进行top-k过滤。默认为-1。
|
||||
- top_p: rollout采样的top-p参数,1.0表示不进行top-p过滤。默认为1.0。
|
||||
|
||||
#### GKD参数
|
||||
- lmbda: 默认为0.5。该参数在GKD中使用。控制学生数据比例的 lambda 参数(即策略内学生生成输出所占的比例)。若lmbda为0,则不使用学生生成数据。
|
||||
- sft_alpha: 默认为0。控制GKD中加入sft_loss的权重。最后的loss为`gkd_loss + sft_alpha * sft_loss`。
|
||||
- gkd_logits_topk: 使用 Top-K logits 计算 KL 散度,默认为 None(即使用完整词表计算)。设置该参数可有效降低训练显存峰值;当配置 `teacher_model_server` 时,此参数为必填项。详见[蒸馏文档](./Distillation.md#31-gkd散度作为直接损失)。
|
||||
- truncation_strategy: 用于处理输入长度超过 max_length 的样本,支持 delete 和 left 两种策略,分别表示删除该样本和从左侧裁剪。默认值为 left。若使用 delete 策略,被删除的超长样本或编码失败的样本将在原数据集中通过重采样进行替换。
|
||||
- log_completions: 是否记录训练中的模型生成内容,搭配 `--report_to wandb/swanlab` 使用。默认为False。
|
||||
- 提示:若没有设置`--report_to wandb/swanlab`,则会在checkpoint中创建`completions.jsonl`来存储生成内容。
|
||||
- 仅记录 vLLM 采样结果。
|
||||
|
||||
#### Reward/Teacher模型参数
|
||||
reward模型参数将在PPO、GRPO中使用;teacher模型参数在GKD与GRPO中使用。
|
||||
|
||||
- reward_model: 默认为None。
|
||||
- reward_adapters: 默认为`[]`。
|
||||
- reward_model_type: 默认为None。
|
||||
- reward_model_revision: 默认为None。
|
||||
- teacher_model: 默认为None。
|
||||
- teacher_adapters: 默认为`[]`。
|
||||
- teacher_model_type: 默认为None。
|
||||
- teacher_model_revision: 默认为None。
|
||||
- teacher_model_server: 教师模型服务地址,通过 `swift deploy` 部署后用于获取 logprobs。支持单 teacher URL(如 `http://localhost:8000`)或多 teacher JSON(如 `'[{"url":"http://t1:8000","tags":["data/math.jsonl"]},{"url":"http://t2:8001","tags":["data/code.jsonl"]}]'`)。`tags` 与数据集或样本标识的对应关系见[蒸馏文档](./Distillation.md#multi-teacher多教师路由)。
|
||||
- teacher_tag_key: 多 teacher 路由时,样本用于匹配 teacher `tags` 的字段名,默认为 `"dataset"`。多数据集时按 `--dataset` 各项匹配;也可在数据中自定义列(如 `teacher_tag`)并设为 `--teacher_tag_key teacher_tag`。
|
||||
- teacher_deepspeed: 同 deepspeed 参数,控制 teacher model 的 deepspeed 配置,默认使用训练模型的 deepspeed 配置。
|
||||
- offload_teacher_model: 卸载教师模型以节约显存,在采样/计算logps时加载,仅在设置teacher_model时生效默认为False。
|
||||
|
||||
|
||||
#### PPO参数
|
||||
|
||||
以下参数含义可以参考[这里](https://huggingface.co/docs/trl/main/ppo_trainer)。
|
||||
- num_ppo_epochs: 默认为4。
|
||||
- whiten_rewards: 默认为False。
|
||||
- kl_coef: 默认为0.05。
|
||||
- cliprange: 默认为0.2。
|
||||
- vf_coef: 默认为0.1。
|
||||
- cliprange_value: 默认为0.2。
|
||||
- gamma: 默认为1.0。
|
||||
- lam: 默认为0.95。
|
||||
- num_mini_batches: 默认为1。
|
||||
- local_rollout_forward_batch_size: 默认为64。
|
||||
- num_sample_generations: 默认为10。
|
||||
- missing_eos_penalty: 默认为None。
|
||||
|
||||
|
||||
#### GRPO参数
|
||||
- beta: KL正则系数,默认为0.04,设置为0时不加载ref model。
|
||||
- per_device_train_batch_size: 每个设备训练批量大小,在GRPO中,指 completion 的批次大小。
|
||||
- per_device_eval_batch_size: 每个设备评估批量大小,在GRPO中,指 completion 的批次大小。
|
||||
- steps_per_generation: 每轮生成的优化步数,默认等于 gradient_accumulation_steps。与 generation_batch_size 只能同时设置一个。
|
||||
- generation_batch_size: 总的采样 completion 批量大小,需要是 num_processes * per_device_train_batch_size 的倍数,默认等于 per_device_train_batch_size * steps_per_generation * num_processes。
|
||||
- num_generations: 每个prompt采样的数量,论文中的G值,generation_batch_size 必须能被 num_generations 整除。默认为 8。
|
||||
- num_generations_eval: 评估阶段每个prompt采样的数量。允许在评估时使用较少的生成数量以节省计算资源。如果为 None,则使用 num_generations 的值。默认为 None。
|
||||
- ds3_gather_for_generation: 该参数适用于DeepSpeed ZeRO-3。如果启用,策略模型权重将被收集用于生成,从而提高生成速度。然而,禁用此选项允许训练超出单个GPU VRAM的模型,尽管生成速度会变慢。禁用此选项与vLLM生成不兼容。默认为True。
|
||||
- reward_funcs: GRPO算法奖励函数,可选项为`accuracy`、`format`、`cosine`、`repetition`和`soft_overlong`,见swift/rewards/orm.py。你也可以在plugin中自定义自己的奖励函数。默认为`[]`。
|
||||
- reward_weights: 每个奖励函数的权重。必须与奖励函数和奖励模型的总数量匹配。如果为 None,则所有奖励的权重都相等,为`1.0`。
|
||||
- 提示:如果GRPO训练中包含`--reward_model`,则其加在奖励函数的最后位置。
|
||||
- reward_model_plugin: 奖励模型逻辑,默认为orm逻辑, 详细见[自定义奖励模型](./GRPO/DeveloperGuide/reward_model.md#自定义奖励模型)。
|
||||
- dataset_shuffle: 是否对dataset进行随机操作,默认为True。
|
||||
- truncation_strategy: 用于处理输入长度超过 max_length 的样本,支持 delete 和 left 两种策略,分别表示删除该样本和从左侧裁剪。默认值为 left。若使用 delete 策略,被删除的超长样本或编码失败的样本将在原数据集中通过重采样进行替换。
|
||||
- loss_type: loss 归一化的类型,可选项为['grpo', 'bnpo', 'dr_grpo', 'dapo', 'cispo', 'sapo', 'real', 'fipo'], 默认为'grpo', 具体参考[文档](./GRPO/DeveloperGuide/loss_types.md)
|
||||
- fipo_decay_rate: FIPO Future-KL 折扣半衰参数,实际折扣为`2 ** (-1 / fipo_decay_rate)`,默认值为32.0。
|
||||
- fipo_clip_range: FIPO influence weight 裁剪范围,默认值为0.2;设置为None或0时不裁剪。
|
||||
- fipo_clip_high_only: 是否只将FIPO influence weight裁剪到`[1.0, 1.0 + fipo_clip_range]`,默认值为True。
|
||||
- fipo_safety_threshold: 当负advantage token的IS ratio超过该阈值时,将FIPO influence weight限制到`[0.8, 1.0]`,默认值为4.0。
|
||||
- log_completions: 是否记录训练中的模型生成内容,搭配 `--report_to wandb/swanlab` 使用。默认为False。
|
||||
- 提示:若没有设置`--report_to wandb/swanlab`,则会在checkpoint中创建`completions.jsonl`来存储生成内容。
|
||||
- use_vllm: 是否使用 vLLM 作为 GRPO 生成的 infer_backend,默认为False。
|
||||
- vllm_mode: vLLM 集成模式,可选项为 `server` 和 `colocate`。server 模式使用 `swift rollout` 拉起的 vLLM 服务器进行采样,colocate 模式在程序内部署 vLLM。使用server端时,
|
||||
- vllm_mode server 参数
|
||||
- vllm_server_host: vLLM server host地址,默认为None。
|
||||
- vllm_server_port: vLLM server 服务端口,默认为8000。
|
||||
- vllm_server_base_url: vLLM server的Base URL(比如 `http://local_host:8000`), 默认为None。设置后,忽略host和port设置。
|
||||
- vllm_server_group_port: vllm server 内部通信端口,除非端口被占用,一般无需设置,默认为51216。
|
||||
- vllm_server_timeout: 连接vLLM server的超时时间,默认为 240s。
|
||||
- vllm_server_pass_dataset: 透传额外的数据集信息到vLLM server,用于多轮训练。
|
||||
- async_generate: 异步rollout以提高训练速度,注意开启时采样会使用上一轮更新的模型进行采样,不支持多轮场景。默认`false`。
|
||||
- enable_flattened_weight_sync: 是否使用 flattened tensor 进行权重同步。启用后会将多个参数打包为单个连续 tensor 进行传输,可提升同步效率,在 Server Mode 下生效,默认为 True。
|
||||
- SWIFT_UPDATE_WEIGHTS_BUCKET_SIZE: 环境变量,用于控制flattened tensor 权重同步时的传输桶大小(bucket size),适用于 Server Mode 下的全参数训练,单位为 MB,默认值为 512 MB。
|
||||
- vllm_mode colocate 参数(更多参数支持参考[vLLM参数](#vLLM参数)。)
|
||||
- vllm_gpu_memory_utilization: vllm透传参数,默认为0.9。
|
||||
- vllm_max_model_len: vllm透传参数,默认为None。
|
||||
- vllm_enforce_eager: vllm透传参数,默认为False。
|
||||
- vllm_limit_mm_per_prompt: vllm透传参数,默认为None。
|
||||
- vllm_enable_prefix_caching: vllm透传参数,默认为True。
|
||||
- vllm_tensor_parallel_size: tp并行数,默认为`1`。
|
||||
- vllm_enable_lora: 支持vLLM Engine 加载 LoRA adapter,默认为False。用于加速LoRA训练的权重同步,具体参考[文档](./GRPO/GetStarted/GRPO.md#权重同步加速)。
|
||||
- sleep_level: 训练时释放 vLLM 显存,可选项为[0, 1, 2], 默认为0,不释放。
|
||||
- offload_optimizer: 是否在vLLM推理时offload optimizer参数,默认为False。
|
||||
- offload_model: 是否在vLLM推理时 offload 模型,默认为False。
|
||||
- completion_length_limit_scope: 在多轮对话中,`max_completion_length` 的限制范围。
|
||||
`total`限制所有对话轮次的总输出长度不超过`max_completion_length`, `per_round`限制每一轮的输出长度。
|
||||
- num_iterations: 每条数据的更新次数,[GRPO论文](https://arxiv.org/abs/2402.03300)中的 $\mu$ 值,默认为1。
|
||||
- epsilon: clip 系数,默认为0.2。
|
||||
- epsilon_high: upper clip 系数,默认为None,设置后与epsilon共同构成[epsilon, epsilon_high]裁剪范围。
|
||||
- tau_pos: [SAPO](https://arxiv.org/abs/2511.20347)算法中正优势的温度参数,控制软门控函数的锐度。较大值使门控更锐利(接近硬裁剪),较小值使门控更平滑。默认为1.0。
|
||||
- tau_neg: SAPO算法中负优势的温度参数,控制软门控函数的锐度。通常设置`tau_neg > tau_pos`以对负优势施加更强约束。默认为1.05。
|
||||
- dynamic_sample: 筛除group内奖励标准差为0的数据,额外采样新数据,默认为False。
|
||||
- max_resample_times: dynamic_sample设置下限制重采样次数,默认3次。
|
||||
- overlong_filter: 跳过超长截断的样本,不参与loss计算,默认为False。
|
||||
- delta: [INTELLECT-2 tech report](https://huggingface.co/papers/2505.07291)中双侧 GRPO 上界裁剪值。若设置,建议大于 1 + epsilon。默认为None。
|
||||
- importance_sampling_level: 控制重要性采样比计算,可选项为 `token` 和 `sequence`,`token` 模式下保留原始的每个 token 的对数概率比,`sequence` 模式下则会对序列中所有有效 token 的对数概率比进行平均。[GSPO论文](https://arxiv.org/abs/2507.18071)中使用sequence级别计算来稳定训练,默认为`token`。
|
||||
- advantage_estimator: 优势计算函数,默认为 `grpo`,即计算组内相对优势,可选项为 `grpo`、[`rloo`](./GRPO/AdvancedResearch/RLOO.md)、[`reinforce_plus_plus`](./GRPO/AdvancedResearch/REINFORCEPP.md)。
|
||||
- kl_in_reward: 控制 KL 散度正则项的处理位置;`false`表示作为损失函数的独立正则项,`true`表示将 KL 直接并入奖励(从奖励中扣除)。默认情况与advantage_estimator绑定,`grpo`下默认为`false`,`rloo` 和 `reinforce_plus_plus` 下默认为 `true`。
|
||||
- scale_rewards: 指定奖励的缩放策略。可选值包括 `group`(按组内标准差缩放)、`batch`(按整个批次的标准差缩放)、`none`(不进行缩放)、`gdpo`(对每个奖励函数分别进行组内归一化后加权聚合,参考 [GDPO 论文](https://arxiv.org/abs/2601.05242))。默认值与 `advantage_estimator` 绑定:`grpo` 对应 `group`,`rloo` 对应 `none`,`reinforce_plus_plus` 对应 `batch`。
|
||||
- 注意:`gdpo` 模式不支持 `kl_in_reward=True`,若同时设置会自动将 `kl_in_reward` 设为 `False`。
|
||||
- GDPO 适用于多奖励优化场景:当使用多个奖励函数时,GDPO 会对每个奖励函数分别在组内进行标准化(减均值、除标准差),然后使用 `reward_weights` 进行加权求和,最后再进行批次级别的标准化。这种方式可以更好地保留各个奖励的相对差异,避免不同奖励组合坍塌成相同的 advantage 值。
|
||||
- teacher_kl_coef: OPD-RL中teacher_kl的系数,即 `adv_t = base_adv + teacher_kl_coef * teacher_kl`。默认为 1.0。
|
||||
- sync_ref_model: 是否定期同步ref_model,默认为False。
|
||||
- ref_model_mixup_alpha: 控制在更新过程中model和先前ref_model之间的混合。更新公式为 $π_{ref} = α * π_θ + (1 - α) * π_{ref_{prev}}$。默认为0.6。
|
||||
- ref_model_sync_steps: 同步频率,默认为512。
|
||||
- move_model_batches: 在模型向vLLM等快速推理框架移动参数时,将layers分为多少个batch. 默认为None, 代表整个模型不进行拆分,否则拆分为move_model_batches+1(非layer参数)+1(多模态部分参数)个。
|
||||
- multi_turn_scheduler: 多轮GRPO参数, 传入对应的plugin名称, 同时在plugin/multi_turn.py中添加好对应的实现。
|
||||
- max_turns: 多轮GRPO的轮数上限。默认为None,不做限制。
|
||||
- gym_env: 全局指定 gym 环境名称,需先在 plugin 里注册。默认为None,可在数据集每行 `env_config.name` 中覆盖。具体参考[文档](./GRPO/DeveloperGuide/gym_env.md)。
|
||||
- use_gym_env: 是否将 env 给出的 `total_reward` 作为奖励,启用后无需配置 reward 函数。默认为None;未显式传入时,若设置了 `gym_env` 则自动设为True,否则server模式下从rollout继承,其它情况为False。
|
||||
- top_entropy_quantile: 仅对熵值处于前指定分位的 token 参与损失计算,默认为1.0,即不过滤低熵 token,具体参考[文档](./GRPO/AdvancedResearch/entropy_mask.md)
|
||||
- log_entropy: 记录训练中的熵值变化动态,默认为False,具体参考[文档](./GRPO/GetStarted/GRPO.md#logged-metrics)
|
||||
- rollout_importance_sampling_mode: 训推不一致校正模式,可选项为 `token_truncate`、`token_mask`、`sequence_truncate`、`sequence_mask`。默认为None,不启用校正。具体参考[文档](./GRPO/AdvancedResearch/training_inference_mismatch.md)
|
||||
- rollout_importance_sampling_threshold: 重要性采样权重的阈值,用于截断或屏蔽极端权重。默认为2.0。
|
||||
- log_rollout_offpolicy_metrics: 当 `rollout_importance_sampling_mode` 未设置时,是否记录训推不一致诊断指标(KL、PPL、χ²等)。当设置了 `rollout_importance_sampling_mode` 时,指标会自动记录。默认为False。
|
||||
- off_policy_sequence_mask_delta: Off-Policy Sequence Masking 阈值,来自 [DeepSeek-V3.2 论文](https://arxiv.org/abs/2512.02556)。当设置此值时,会计算每个序列的 `mean(old_policy_logps - policy_logps)`,若该值大于阈值且该序列的优势为负,则 mask 掉该序列不参与损失计算。具体参考[文档](./GRPO/AdvancedResearch/training_inference_mismatch.md#off-policy-sequence-masking)
|
||||
|
||||
##### 奖励函数参数
|
||||
内置的奖励函数参考[文档](./GRPO/DeveloperGuide/reward_function.md)
|
||||
cosine 奖励参数
|
||||
- cosine_min_len_value_wrong: cosine 奖励函数参数,生成错误答案时,最小长度对应的奖励值。默认值为-0.5。
|
||||
- cosine_max_len_value_wrong: 生成错误答案时,最大长度对应的奖励值。默认值为0.0。
|
||||
- cosine_min_len_value_correct: 生成正确答案时,最小长度对应的奖励值。默认值为1.0。
|
||||
- cosine_max_len_value_correct: 生成正确答案时,最大长度对应的奖励值。默认值为0.5。
|
||||
- cosine_max_len: 生成文本的最大长度限制。默认等于 max_completion_length。
|
||||
|
||||
repetition 奖励参数
|
||||
- repetition_n_grams: 用于检测重复的 n-gram 大小。默认值为3。
|
||||
- repetition_max_penalty: 最大惩罚值,用于控制惩罚的强度。默认值为-1.0。
|
||||
|
||||
soft overlong 奖励参数
|
||||
- soft_max_length: 论文中的L_max,模型的最大生成长度,默认等于max_completion_length。
|
||||
- soft_cache_length: 论文中的L_cache,控制长度惩罚区间,区间为[soft_max_length-soft_cache_length, soft_max_length]。
|
||||
|
||||
### 推理参数
|
||||
|
||||
推理参数除包含[基本参数](#基本参数)、[合并参数](#合并参数)、[vLLM参数](#vllm参数)、[LMDeploy参数](#LMDeploy参数)外,还包含下面的部分:
|
||||
|
||||
- 🔥infer_backend: 推理加速后端,支持'transformers'、'vllm'、'sglang'、'lmdeploy'四种推理引擎。默认为'transformers'。
|
||||
- 注意:这四种引擎使用的都是swift的template,使用`--template_backend`控制。
|
||||
- 🔥max_batch_size: 指定infer_backend为'transformers'时生效,用于批量推理,默认为1。若设置为-1,则不受限制。
|
||||
- 🔥result_path: 推理结果存储路径(jsonl),默认为None。如果对数据集进行推理/评测,则默认保存在checkpoint目录(含args.json文件)或者'./result'目录,最终存储路径会在命令行中打印(交互式推理或部署默认不存储结果)。
|
||||
- 注意:若已存在`result_path`文件,则会进行追加写入。
|
||||
- write_batch_size: 结果写入`result_path`的batch_size。默认为1000。若设置为-1,则不受限制。
|
||||
- metric: 对推理的结果进行评估,目前支持'acc'和'rouge'。默认为None,即不进行评估。
|
||||
- val_dataset_sample: 推理数据集采样数,默认为None。
|
||||
- reranker_use_activation: 在reranker推理时,是否在score之后使用sigmoid,默认为True。
|
||||
|
||||
|
||||
### 部署参数
|
||||
|
||||
部署参数继承于[推理参数](#推理参数)。
|
||||
|
||||
- host: 服务host,默认为'0.0.0.0'。
|
||||
- port: 端口号,默认为8000。
|
||||
- api_key: 访问需要使用的api_key,默认为None。
|
||||
- owned_by: 默认为`swift`。
|
||||
- 🔥served_model_name: 提供服务的模型名称,默认使用model的后缀。
|
||||
- verbose: 打印详细日志,默认为True。
|
||||
- 注意:在`swift app`或者`swift eval`时,默认为False。
|
||||
- log_interval: tokens/s统计值打印间隔,默认20秒。设置为-1则不打印。
|
||||
- max_logprobs: 最多返回客户端的logprobs数量,默认为20。
|
||||
|
||||
### Rollout参数
|
||||
Rollout参数继承于[部署参数](#部署参数)
|
||||
- multi_turn_scheduler: 多轮GRPO训练规划器,传入对应的plugin名称, 同时在plugin/multi_turn.py中添加好对应的实现。默认为None,具体参考[文档](./GRPO/DeveloperGuide/multi_turn.md)。
|
||||
- max_turns: 多轮GRPO训练下的最大轮数,默认为None,即不做约束。
|
||||
- gym_env: 全局指定 gym 环境名称,需先在 plugin 里注册。默认为None,可在数据集每行 `env_config.name` 中覆盖。具体参考[文档](./GRPO/DeveloperGuide/gym_env.md)。
|
||||
- use_gym_env: 是否启用 gym 环境模式(rollout 输出 `total_reward` 供 trainer 使用)。默认为None,未显式传入时若设置了 `gym_env` 则自动设为True。
|
||||
- vllm_enable_lora: 支持vLLM Engine 加载 LoRA adapter,默认为False。用于加速LoRA训练的权重同步,具体参考[文档](./GRPO/GetStarted/GRPO.md#权重同步加速)。
|
||||
- vllm_max_lora_rank: vLLM Engine LoRA参数,需大于等于训练的lora_rank,建议等于。默认为16。
|
||||
- vllm_enable_expert_parallel: 开启MoE模型的专家并行(EP),将experts分布到不同rank,在vllm_use_async_engine True时生效。默认为False。
|
||||
|
||||
### Web-UI参数
|
||||
- server_name: web-ui的host,默认为'0.0.0.0'。
|
||||
- server_port: web-ui的port,默认为7860。
|
||||
- share: 默认为False。
|
||||
- lang: web-ui的语言,可选为'zh', 'en'。默认为'zh'。
|
||||
|
||||
|
||||
### App参数
|
||||
|
||||
App参数继承于[部署参数](#部署参数), [Web-UI参数](#Web-UI参数)。
|
||||
- base_url: 模型部署的base_url,例如`http://localhost:8000/v1`。默认为`None`,使用本地部署。
|
||||
- studio_title: studio的标题。默认为None,设置为模型名。
|
||||
- is_multimodal: 是否启动多模态版本的app。默认为None,自动根据model判断,若无法判断,设置为False。
|
||||
- lang: 覆盖Web-UI参数,默认为'en'。
|
||||
|
||||
### 评测参数
|
||||
|
||||
评测参数继承于[部署参数](#部署参数)。
|
||||
|
||||
- 🔥eval_backend: 评测后端,默认为'Native',也可以指定为'OpenCompass'或'VLMEvalKit'。
|
||||
- 🔥eval_dataset: 评测数据集,请查看[评测文档](./Evaluation.md)。
|
||||
- eval_limit: 每个评测集的采样数,默认为None。
|
||||
- eval_output_dir: 评测存储结果的文件夹,默认为'eval_output'。
|
||||
- temperature: 覆盖生成参数,默认为0。
|
||||
- eval_num_proc: 评测时客户端最大并发数,默认为16。
|
||||
- eval_url: 评测url,例如`http://localhost:8000/v1`。例子可以查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/eval/eval_url)。默认为None,采用本地部署评估。
|
||||
- eval_generation_config: 评测时模型推理配置,需传入json字符串格式,例如:`'{"max_new_tokens": 512}'`;默认为None。
|
||||
- extra_eval_args: 额外评测参数,需传入json字符串格式,默认为空。仅对Native评测有效,更多参数说明请查看[这里](https://evalscope.readthedocs.io/zh-cn/latest/get_started/parameters.html)。
|
||||
- local_dataset: 部分评测集,如`CMB`无法直接运行,需要下载额外数据包才可以使用。设置本参数为`true`可以自动下载全量数据包,并在当前目录下创建`data`文件夹并开始评测。数据包仅会下载一次,后续会使用缓存。该参数默认为`false`。
|
||||
- 注意:默认评测会使用`~/.cache/opencompass`下的数据集,在指定本参数后会直接使用当前目录下的data文件夹。
|
||||
|
||||
### 导出参数
|
||||
|
||||
导出参数除包含[基本参数](#基本参数)和[合并参数](#合并参数)外,还包含下面的部分:
|
||||
|
||||
- 🔥output_dir: 导出结果存储路径。默认为None,会自动设置合适后缀的路径。
|
||||
- exist_ok: 如果output_dir存在,不抛出异常,进行覆盖。默认为False。
|
||||
- 🔥quant_method: 可选为'gptq'、'awq'、'bnb'和'fp8',默认为None。例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize)。
|
||||
- quant_n_samples: gptq/awq的校验集采样数,默认为256。
|
||||
- quant_batch_size: 量化batch_size,默认为1。
|
||||
- group_size: 量化group大小,默认为128。
|
||||
- to_cached_dataset: 提前对数据集进行tokenize并导出,默认为False。例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/cached_dataset)。更多介绍请查看`cached_dataset`。
|
||||
- 提示:你可以通过`--split_dataset_ratio`或者`--val_dataset`指定验证集内容。
|
||||
- template_mode: 用于支持对`swift rlhf`训练的`cached_dataset`功能。该参数只在`--to_cached_dataset true`时生效。可选项包括: 'train'、'rlhf'和'kto'。其中`swift pt/sft`使用'train',`swift rlhf --rlhf_type kto`使用'kto',其他rlhf算法使用'rlhf'。注意:当前'gkd', 'ppo', 'grpo'算法不支持`cached_dataset`功能。默认为'train'。
|
||||
- to_ollama: 产生ollama所需的Modelfile文件。默认为False。
|
||||
- 🔥to_mcore: HF格式权重转成Megatron格式。默认为False。
|
||||
- to_hf: Megatron格式权重转成HF格式。默认为False。
|
||||
- mcore_model: mcore格式模型路径。默认为None。
|
||||
- mcore_adapter: mcore格式模型的adapter路径,默认为None。
|
||||
- thread_count: `--to_mcore true`时的模型切片数。默认为None,根据模型大小自动设置,使得最大分片小于10GB。
|
||||
- 🔥offload_bridge: Megatron导出的用于vLLM更新HF格式权重使用CPU主存存放,以降低 GPU 显存占用。默认为 False。
|
||||
- 🔥test_convert_precision: 测试HF和Megatron格式权重转换的精度误差。默认为False。
|
||||
- test_convert_dtype: 转换精度测试使用的dtype,默认为'float32'。
|
||||
- 🔥push_to_hub: 是否推送hub,默认为False。例子参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/export/push_to_hub.sh)。
|
||||
- hub_model_id: 推送的model_id,默认为None。
|
||||
- hub_private_repo: 是否是private repo,默认为False。
|
||||
- commit_message: 提交信息,默认为'update files'。
|
||||
|
||||
### 采样参数
|
||||
|
||||
- prm_model: 过程奖励模型的类型,可以是模型id(以'transformers'方式拉起),或者plugin中定义的prm key(自定义推理过程)。
|
||||
- orm_model: 结果奖励模型的类型,通常是通配符或测试用例等,一般定义在plugin中。
|
||||
- sampler_type: 采样类型,目前支持 sample, distill。
|
||||
- sampler_engine: 支持`transformers`, `lmdeploy`, `vllm`, `client`, `no`,默认为`transformers`,采样模型的推理引擎。
|
||||
- output_dir: 输出目录,默认为`sample_output`。
|
||||
- output_file: 输出文件名称,默认为`None`使用时间戳作为文件名。传入时不需要传入目录,仅支持jsonl格式。
|
||||
- override_exist_file: 如`output_file`存在,是否覆盖。
|
||||
- num_sampling_batch_size: 每次采样的batch_size。
|
||||
- num_sampling_batches: 共采样多少batch。
|
||||
- n_best_to_keep: 返回多少最佳sequences。
|
||||
- data_range: 本采样处理数据集的分片。传入格式为`2 3`,代表数据集分为3份处理(这意味着通常有三个`swift sample`在并行处理),本实例正在处理第3个分片。
|
||||
- temperature: 在这里默认为1.0。
|
||||
- prm_threshold: PRM阈值,低于该阈值的结果会被过滤掉,默认值为`0`。
|
||||
- easy_query_threshold: 单个query的所有采样中,ORM评估如果正确,大于该比例的query会被丢弃,防止过于简单的query出现在结果中,默认为`None`,代表不过滤。
|
||||
- engine_kwargs: 传入sampler_engine的额外参数,以json string传入,例如`{"cache_max_entry_count":0.7}`。
|
||||
- num_return_sequences: 采样返回的原始sequence数量。默认为64,本参数对`sample`采样有效。
|
||||
- cache_files: 为避免同时加载prm和generator造成显存OOM,可以分两步进行采样,第一步将prm和orm置为`None`,则所有结果都会输出到文件中,第二次运行采样将sampler_engine置为`no`并传入`--cache_files`为上次采样的输出文件,则会使用上次输出的结果进行prm和orm评估并输出最终结果。
|
||||
- 注意:使用cache_files时,`--dataset`仍然需要传入,这是因为cache_files的id是由原始数据计算的md5,需要把两部分信息结合使用。
|
||||
|
||||
## 特定模型参数
|
||||
除了以上参数外,有些模型还支持额外的具体模型参数。这些参数含义通常可以在对应模型官方repo或者其推理代码中找到相应含义。**ms-swift引入这些参数以确保训练的模型与官方推理代码效果对齐**。
|
||||
- 特定模型参数可以通过`--model_kwargs`或者环境变量进行设置,例如: `--model_kwargs '{"fps_max_frames": 12}'`或者`FPS_MAX_FRAMES=12`。
|
||||
- 注意:若你在训练时指定了特定模型参数,请在推理时也设置对应的参数,这可以提高训练效果。
|
||||
|
||||
### qwen2_vl, qvq, qwen2_5_vl, mimo_vl, keye_vl, keye_vl_1_5
|
||||
参数含义与`qwen_vl_utils<0.0.12`或者`qwen_omni_utils`库中含义一致,可以查看[这里](https://github.com/QwenLM/Qwen2.5-VL/blob/main/qwen-vl-utils/src/qwen_vl_utils/vision_process.py#L24)。ms-swift通过修改这些常数值来控制图片分辨率和视频帧率,避免训练时OOM。
|
||||
|
||||
- IMAGE_FACTOR: 默认为28。
|
||||
- MIN_PIXELS: 默认为`4 * 28 * 28`。图像的最小分辨率。建议设置为28*28的倍数。
|
||||
- 🔥MAX_PIXELS: 默认为`16384 * 28 * 28`。图像的最大分辨率。建议设置为28*28的倍数。
|
||||
- MAX_RATIO: 默认为200。
|
||||
- VIDEO_MIN_PIXELS: 默认为`128 * 28 * 28`。视频中一帧的最小分辨率。建议设置为28*28的倍数。
|
||||
- 🔥VIDEO_MAX_PIXELS: 默认为`768 * 28 * 28`。视频中一帧的最大分辨率。建议设置为28*28的倍数。
|
||||
- VIDEO_TOTAL_PIXELS: 默认为`24576 * 28 * 28`。
|
||||
- FRAME_FACTOR: 默认为2。
|
||||
- FPS: 默认为2.0。
|
||||
- FPS_MIN_FRAMES: 默认为4。一段视频的最小抽帧数。
|
||||
- 🔥FPS_MAX_FRAMES: 默认为768。一段视频的最大抽帧数。
|
||||
- 🔥QWENVL_BBOX_FORMAT: grounding格式使用'legacy'还是'new'。'legacy'格式为:`<|object_ref_start|>一只狗<|object_ref_end|><|box_start|>(432,991),(1111,2077)<|box_end|>`,'new'格式参考:[Qwen3-VL cookbook](https://github.com/QwenLM/Qwen3-VL/blob/main/cookbooks/2d_grounding.ipynb),并参考[grounding数据集格式文档](../Customization/Custom-dataset.md#grounding)。默认为'legacy'。
|
||||
- 注意:该环境变量适配Qwen2/2.5/3-VL和Qwen2.5/3-Omni系列模型。
|
||||
|
||||
### qwen2_audio, qwen3_asr
|
||||
- SAMPLING_RATE: 默认为16000。
|
||||
|
||||
### qwen3_vl, qwen3_5
|
||||
参数含义与`qwen_vl_utils>=0.0.14`库中的含义一致,可以查看[这里](https://github.com/QwenLM/Qwen2.5-VL/blob/main/qwen-vl-utils/src/qwen_vl_utils/vision_process.py#L24)。通过传入以下环境变量,可以修改该库的全局变量默认值。(也兼容使用`qwen2_5_vl`的环境变量,例如:`MAX_PIXELS`、`VIDEO_MAX_PIXELS`,会做自动转换。)
|
||||
|
||||
- SPATIAL_MERGE_SIZE: 默认为2。
|
||||
- IMAGE_MIN_TOKEN_NUM: 默认为`4`,代表一张图片最小图像tokens的个数。
|
||||
- 🔥IMAGE_MAX_TOKEN_NUM: 默认为`16384`,代表一张图片最大图像tokens的个数。(用于避免OOM)。
|
||||
- 提示:等价最大图像像素为`IMAGE_MAX_TOKEN_NUM * 32 *32`。
|
||||
- VIDEO_MIN_TOKEN_NUM: 默认为`128`,代表视频中一帧的最小视频tokens的个数。
|
||||
- 🔥VIDEO_MAX_TOKEN_NUM: 默认为`768`,代表视频中一帧的最大视频tokens的个数。(用于避免OOM)
|
||||
- MAX_RATIO: 默认为200。
|
||||
- FRAME_FACTOR: 默认为2。
|
||||
- FPS: 默认为2.0。
|
||||
- FPS_MIN_FRAMES: 默认为4。代表一段视频的最小抽帧数。
|
||||
- 🔥FPS_MAX_FRAMES: 默认为768,代表一段视频的最大抽帧数。(用于避免OOM)。
|
||||
|
||||
|
||||
### qwen2_5_omni, qwen3_omni
|
||||
qwen2_5_omni除了包含qwen2_5_vl和qwen2_audio的模型特定参数外,还包含以下参数:(注意:qwen3_omni包含的是**qwen3_vl**, qwen2_audio的模型特定参数)
|
||||
- USE_AUDIO_IN_VIDEO: 默认为False。是否使用video中的音频信息。
|
||||
- 🔥ENABLE_AUDIO_OUTPUT: 默认为None,即使用`config.json`中的值。若使用zero3进行训练,请设置为False。
|
||||
- 提示:ms-swift只对thinker部分进行微调,建议设置为False以降低显存占用(只创建thinker部分的模型结构)。
|
||||
|
||||
|
||||
### qwen3_vl_emb, qwen3_vl_reranker
|
||||
参数含义与`qwen3_vl`相同,见上面的描述。以下为对默认值的覆盖:
|
||||
|
||||
- IMAGE_MAX_TOKEN_NUM: qwen3_vl_emb默认为1800, qwen3_vl_reranker默认为1280。具体参考这里:[qwen3_vl_embedding](https://modelscope.cn/models/Qwen/Qwen3-VL-Embedding-2B/file/view/master/scripts%2Fqwen3_vl_embedding.py?status=1#L26), [qwen3_vl_reranker](https://modelscope.cn/models/Qwen/Qwen3-VL-Reranker-2B/file/view/master/scripts%2Fqwen3_vl_reranker.py?status=1#L16)。
|
||||
- FPS: 默认为1。
|
||||
- FPS_MAX_FRAMES: 默认为64。
|
||||
|
||||
|
||||
### internvl_chat
|
||||
参数含义可以查看[这里](https://modelscope.cn/models/OpenGVLab/InternVL2_5-2B)。
|
||||
- MAX_NUM: 默认为12。
|
||||
- INPUT_SIZE: 默认为448。
|
||||
- VIDEO_MAX_NUM: 默认为1。视频的MAX_NUM。
|
||||
- VIDEO_SEGMENTS: 默认为8。
|
||||
|
||||
|
||||
### minicpmv2_6, minicpmv4, minicpmo
|
||||
- MAX_SLICE_NUMS: 默认为9,参考[这里](https://modelscope.cn/models/OpenBMB/MiniCPM-V-2_6/file/view/master?fileName=config.json&status=1)。
|
||||
- VIDEO_MAX_SLICE_NUMS: 默认为1,视频的MAX_SLICE_NUMS,参考[这里](https://modelscope.cn/models/OpenBMB/MiniCPM-V-2_6)。
|
||||
- MAX_NUM_FRAMES: 默认为64,参考[这里](https://modelscope.cn/models/OpenBMB/MiniCPM-V-2_6)。
|
||||
|
||||
### minicpmo
|
||||
- INIT_TTS: 默认为False。是否创建和加载TTS模型。
|
||||
- INIT_AUDIO: 默认为True。是否创建和加载Audio模型。
|
||||
- USE_AUDIO_IN_VIDEO: 默认为False。是否使用video中的音频信息。
|
||||
|
||||
|
||||
### minicpmv4_6
|
||||
- DOWNSAMPLE_MODE: 默认为`'16x'`。视觉token下采样模式。`'16x'`合并token以提升效率;`'4x'`保留4倍更多的token以获取更精细的细节。
|
||||
- MAX_SLICE_NUMS: 默认为9。分割高分辨率图像时的最大切片数量。数值越高,大图像保留的细节越多。建议图像设为36。
|
||||
- VIDEO_MAX_SLICE_NUMS: 默认为1。视频的`MAX_SLICE_NUMS`。
|
||||
- MAX_NUM_FRAMES: 默认为128。从视频中采样的主帧最大数量。
|
||||
- STACK_FRAMES: 默认为1。每秒的总采样点数。1表示仅使用主帧(无堆叠)。N(N>1)表示每秒包含1个主帧 + N−1个子帧;子帧会被合成一张网格图像,并与主帧交错排列。建议短视频设为1,长视频设为3或5。
|
||||
|
||||
|
||||
### ovis1_6, ovis2
|
||||
- MAX_PARTITION: 默认为9,参考[这里](https://github.com/AIDC-AI/Ovis/blob/d248e34d755a95d24315c40e2489750a869c5dbc/ovis/model/modeling_ovis.py#L312)。
|
||||
|
||||
### ovis2_5
|
||||
以下参数含义可以在[这里](https://modelscope.cn/models/AIDC-AI/Ovis2.5-2B)的示例代码中找到。
|
||||
- MIX_PIXELS: int类型,默认为`448 * 448`。
|
||||
- MAX_PIXELS: int类型,默认为`1344 * 1792`。若出现OOM,可以调小该值。
|
||||
- VIDEO_MAX_PIXELS: int类型,默认为`896 * 896`。
|
||||
- NUM_FRAMES: 默认为8。用于视频抽帧。
|
||||
|
||||
### mplug_owl3, mplug_owl3_241101
|
||||
- MAX_NUM_FRAMES: 默认为16,参考[这里](https://modelscope.cn/models/iic/mPLUG-Owl3-7B-240728)。
|
||||
|
||||
### xcomposer2_4khd
|
||||
- HD_NUM: 默认为55,参考[这里](https://modelscope.cn/models/Shanghai_AI_Laboratory/internlm-xcomposer2-4khd-7b)。
|
||||
|
||||
### xcomposer2_5
|
||||
- HD_NUM: 图片数量为1时,默认值为24。大于1,默认为6。参考[这里](https://modelscope.cn/models/AI-ModelScope/internlm-xcomposer2d5-7b/file/view/master?fileName=modeling_internlm_xcomposer2.py&status=1#L254)。
|
||||
|
||||
### video_cogvlm2
|
||||
- NUM_FRAMES: 默认为24,参考[这里](https://github.com/zai-org/CogVLM2/blob/main/video_demo/inference.py#L22)。
|
||||
|
||||
### phi3_vision
|
||||
- NUM_CROPS: 默认为4,参考[这里](https://modelscope.cn/models/LLM-Research/Phi-3.5-vision-instruct)。
|
||||
|
||||
### llama3_1_omni
|
||||
- N_MELS: 默认为128,参考[这里](https://github.com/ictnlp/LLaMA-Omni/blob/544d0ff3de8817fdcbc5192941a11cf4a72cbf2b/omni_speech/infer/infer.py#L57)。
|
||||
|
||||
### video_llava
|
||||
- NUM_FRAMES: 默认为16。
|
||||
|
||||
|
||||
## 其他环境变量
|
||||
- USE_HF: 使用ModelScope/HuggingFace,默认为'0',使用ModelScope。
|
||||
- CUDA_VISIBLE_DEVICES: 控制使用哪些GPU卡。默认使用所有卡。
|
||||
- ASCEND_RT_VISIBLE_DEVICES: 控制使用哪些NPU卡(只对ASCEND卡生效)。默认使用所有卡。
|
||||
- MODELSCOPE_CACHE: 控制缓存路径。(多机训练时建议设置该值,以确保不同节点使用相同的数据集缓存)。
|
||||
- PYTORCH_CUDA_ALLOC_CONF: 推荐设置为`'expandable_segments:True'`,这将减少GPU内存碎片,具体请参考[torch文档](https://docs.pytorch.org/docs/stable/notes/cuda.html#cuda-memory-management)。
|
||||
- NPROC_PER_NODE: torchrun中`--nproc_per_node`的参数透传。默认为1。若设置了`NPROC_PER_NODE`或者`NNODES`环境变量,则使用torchrun启动训练或推理。
|
||||
- MASTER_PORT: torchrun中`--master_port`的参数透传。默认为29500。
|
||||
- MASTER_ADDR: torchrun中`--master_addr`的参数透传。
|
||||
- NNODES: torchrun中`--nnodes`的参数透传。
|
||||
- NODE_RANK: torchrun中`--node_rank`的参数透传。
|
||||
- LOG_LEVEL: 日志的level,默认为'INFO',你可以设置为'WARNING', 'ERROR'等。
|
||||
- SWIFT_DEBUG: 在`engine.infer(...)`时,若设置为'1',TransformersEngine将会打印input_ids和generate_ids的内容方便进行调试与对齐。
|
||||
- VLLM_USE_V1: 用于切换vLLM使用V0/V1版本。
|
||||
- SWIFT_TIMEOUT: 若多模态数据集中存在图像URL,该参数用于控制获取图片的timeout,默认为20s。
|
||||
- ROOT_IMAGE_DIR: 图像(多模态)资源的根目录。通过设置该参数,可以在数据集中使用相对于 `ROOT_IMAGE_DIR` 的相对路径。默认情况下,是相对于运行目录的相对路径。
|
||||
- SWIFT_SINGLE_DEVICE_MODE: 单设备模式,可选值为"0"(默认值)/"1",在此模式下,每个进程只能看到一个设备。
|
||||
- SWIFT_AUDIO_LOAD_BACKEND: 音频波形加载后端。`librosa`(默认)或 `soundfile_pyav`(soundfile 优先、失败时 pyav fallback)。GRPO/GKD训练 在 `--use_vllm true`时默认为 `soundfile_pyav`,保证训练侧 encode 与 vLLM rollout 解析同一音频 URL 时波形一致。
|
||||
@@ -0,0 +1,398 @@
|
||||
# 知识蒸馏(Knowledge Distillation)
|
||||
|
||||
知识蒸馏是一种将教师模型(teacher model)的能力迁移到学生模型(student model)的训练方法。其核心思想是:让学生在每个 token 位置上向教师的输出分布靠拢,从而获得比单纯模仿标注答案更丰富的监督信号——教师不仅告诉学生「哪个 token 是对的」,还告诉学生「其他 token 有多好 / 多差」。
|
||||
|
||||
本文档自顶向下地介绍:蒸馏为什么有效(第一节)、蒸馏方法的统一设计框架(第二节),最后落到 swift 中三种具体的蒸馏训练方法 GKD / OPD-RL / OPSD(第三节)。
|
||||
|
||||
---
|
||||
|
||||
## 一、为什么需要蒸馏:从稀疏信号到稠密信号
|
||||
|
||||
一个语言模型的能力,通常由一连串训练阶段堆叠而成:
|
||||
|
||||
- **预训练(Pre-training)**:习得语言、世界知识、基础推理等通用能力。
|
||||
- **中训练(Mid-training)**:注入领域知识,如代码、医学、公司内部文档等。
|
||||
- **后训练(Post-training)**:激发目标行为,如指令遵循、数学推理、对话风格等。
|
||||
|
||||
蒸馏主要发生在**后训练**阶段。要理解它的价值,需要从两个彼此独立的维度来看待后训练方法:
|
||||
|
||||
1. **采样方式(数据从哪来)**:训练序列是由学生自己生成(on-policy),还是来自外部固定数据(off-policy)。
|
||||
2. **反馈密度(每条序列能学到多少)**:是整条序列只有一个奖励(per-sequence,稀疏),还是每个 token 都有信号(per-token,稠密)。
|
||||
|
||||
|
||||
**SFT / 离线蒸馏**(off-policy + 稠密):在固定数据上对齐标注或教师分布。信号稠密,但训练时见到的都是教师/标注的状态,和学生推理时自己会进入的状态不一致。学生一旦在推理早期犯了教师不会犯的错,就会进入训练中从未见过的状态,误差不断累积,这被称为 **exposure bias(曝光偏差)**。
|
||||
|
||||
**RL**(on-policy + 稀疏):学生自己采样,按最终结果给奖励。分布与学生推理一致,但奖励通常是**序列级**标量,一般不指明具体哪个 token 出错。
|
||||
|
||||
**On-policy 蒸馏**(on-policy + 稠密):学生自己采样轨迹,再由教师对轨迹的**每一个 token** 打分。训练分布与学生推理分布一致,且反馈为 per-token 级别。
|
||||
|
||||
### SFT 是蒸馏的一个特例
|
||||
|
||||
理解蒸馏的一个自然切入点是 SFT 的损失函数。SFT 的 cross-entropy loss,等价于以标注 token 的 one-hot 分布 $\delta_{y^*}$ 为「教师」的 KL 散度:
|
||||
|
||||
$$-\log P_S(y^*_t) = \text{KL}(\delta_{y^*} \,\|\, P_S)$$
|
||||
|
||||
知识蒸馏只是把这个确定性的 one-hot「教师」换成了真实教师模型的软分布 $P_T$,在每个 token 上优化 $\text{KL}(P_T \,\|\, P_S)$,从而提供比 one-hot 丰富的监督信号。
|
||||
|
||||
| 方法 | 采样方式 | 反馈密度 | 教师分布 |
|
||||
|------|----------|----------|----------|
|
||||
| SFT | off-policy(固定数据) | 稠密(per-token) | one-hot $\delta_{y^*}$ |
|
||||
| 离线(off-policy)蒸馏 | off-policy(固定数据或教师生成) | 稠密(per-token) | 教师软分布 |
|
||||
| RL | on-policy(学生采样) | 稀疏(per-sequence) | 无 |
|
||||
| **On-policy 蒸馏** | **on-policy(学生采样)** | **稠密(per-token)** | **教师软分布** |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 二、蒸馏的两个核心选择
|
||||
|
||||
不同蒸馏方法的差异,几乎都可以归结为两个问题。理解了这两个维度,后面的方法都只是它们的不同组合。
|
||||
|
||||
### 2.1 教师信号怎么算
|
||||
|
||||
在每个 token 位置上,量化教师分布 $P_T$ 与学生分布 $P_S$ 的差异(我们称之为 **Teacher KL**),有两个子选择。
|
||||
|
||||
**(a) 散度方向**
|
||||
|
||||
| 散度 | 定义 | 优化时的行为(信息论含义) |
|
||||
|------|------|------|
|
||||
| Forward KL | $\text{KL}(P_T \,\|\, P_S)$ | Mode-covering:学生需对教师概率较高的区域都赋予足够概率 |
|
||||
| Reverse KL | $\text{KL}(P_S \,\|\, P_T)$ | Mode-seeking:学生主要拟合教师的众数(高概率)区域 |
|
||||
| 广义 JSD($\beta$) | $\beta\,\text{KL}(P_T\|M) + (1-\beta)\,\text{KL}(P_S\|M)$,其中 $M=\beta P_T+(1-\beta)P_S$ | 在两者之间插值 |
|
||||
|
||||
> 其中 $\beta=0$ 退化为 Forward KL,$\beta=1$ 退化为 Reverse KL。SFT 等价于 Forward KL(教师为 one-hot)。
|
||||
|
||||
在 swift 中:
|
||||
- GKD 默认 $\beta=0.5$(JSD),可通过 `--beta` 在 Forward / JSD / Reverse 之间选择;
|
||||
- OPD-RL 的实现固定使用 Reverse KL 的 k1 估计量 $\log\pi_{\text{teacher}}(y_t)-\log\pi_{\text{student}}(y_t)$ 作为 per-token advantage。
|
||||
|
||||
**(b) 计算粒度**
|
||||
|
||||
| 计算粒度 | 需要的教师信息 | 说明 |
|
||||
|----------|---------------|------|
|
||||
| 全词表 | 教师完整的 next-token 分布 | 可计算散度的精确值;显存开销大 |
|
||||
| Top-K | 教师概率最高的 K 个 token | 在 top-K 子集上重新归一化后的近似;适合外部 API(受 `max_logprobs` 限制) |
|
||||
| 采样 token | 教师在学生实际采样 token 上的单个 logp | Reverse KL 的单样本蒙特卡洛估计;通信开销最低 |
|
||||
|
||||
> **精度 vs 开销**:全词表需要物化完整 logits;采样 token 只需教师在已采样 token 上的 logp(可走远程 API)。[DeepSeek-V4](https://arxiv.org/abs/2606.19348)技术报告指出,仅用采样 token 的 log-ratio 作 advantage 时梯度估计方差较大,因此其全词表 OPD 采用完整 logit 蒸馏
|
||||
|
||||
### 2.2 信号怎么传给学生
|
||||
|
||||
| | **路径 A:GKD(直接损失)** | **路径 B:OPD-RL(RL Advantage)** |
|
||||
|---|---|---|
|
||||
| 训练范式 | `--rlhf_type gkd` | `--rlhf_type grpo` + 教师 |
|
||||
| 信号传递 | 把信号作为 loss | 把信号当 advantage,走 policy gradient |
|
||||
| 梯度流经 | 学生**全词表** logits(或 top-k) | 仅学生**采样 token** 的 $\nabla\log\pi(y_t)$ |
|
||||
| 教师信息需求 | 全词表分布(或 top-k logits) | 采样 token 上的单个 logp |
|
||||
| 散度选择 | Forward / Reverse / JSD(`--beta`) | Reverse KL(k1 log-ratio) |
|
||||
| 与任务奖励组合 | 通过 `sft_alpha` 混合 SFT loss | 可与 GRPO reward 叠加为 advantage |
|
||||
|
||||
两者**共享同一套教师基础设施**(见下文),区别只在如何使用 teacher KL 信号。
|
||||
|
||||
> **蒸馏的常见用法**
|
||||
> 1. **能力融合**:多个专家模型蒸馏到统一模型。
|
||||
> 2. **强到弱**:大模型向小模型传递能力。
|
||||
> 3. **防遗忘**:用旧 checkpoint 作教师,在多阶段训练后恢复先前能力。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 三、swift 中的蒸馏方法
|
||||
|
||||
swift 提供三种蒸馏训练方法,它们共享同一套教师基础设施:
|
||||
|
||||
| 方法 | 信号传递路径 | 启用方式 | 一句话 |
|
||||
|------|--------------|----------|--------|
|
||||
| **GKD** | 直接损失(路径 A) | `--rlhf_type gkd` | 教师散度作为 loss 反向传播;支持全词表 / top-k 散度 |
|
||||
| **OPD-RL** | RL advantage(路径 B) | `--rlhf_type grpo` + 教师 | 教师 log-ratio 注入 GRPO advantage,可与任务奖励叠加 |
|
||||
| **OPSD** | A 或 B 均可 | 在上面基础上提供 `teacher_prompt` | 单模型自蒸馏:教师输入含特权信息(如参考解答) |
|
||||
|
||||
**教师的三种来源**(GKD 与 OPD-RL 通用):
|
||||
|
||||
- `--teacher_model`:在训练进程中加载一个独立的冻结教师模型。
|
||||
- `--teacher_model_server`:连接一个外部教师服务(`swift deploy`启动的vllm服务),不在训练卡上加载教师。GKD 使用 API 时需同时设置 `--gkd_logits_topk`。支持单 URL 与多 teacher JSON 配置。
|
||||
- **自蒸馏**:教师与学生同源。LoRA 训练且 `--teacher_model` 与 `--model` 相同时,自动用 `disable_adapter()` 以基座为固定教师,无需额外加载;不传 `--teacher_model` 且设置 `teacher_prompt`, 则以「学生当前权重」为动态教师。
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `--teacher_model` | None | 教师模型路径;GKD 下不传则为动态自蒸馏 |
|
||||
| `--teacher_model_server` | None | 教师 API 地址(与 `teacher_model` 互斥);见下方格式说明 |
|
||||
| `--teacher_tag_key` | `"dataset"` | 多 teacher 路由时,样本用于匹配 teacher `tags` 的字段名 |
|
||||
| `--teacher_deepspeed` | None | 教师模型的 DeepSpeed 配置(如 `zero3`) |
|
||||
| `--offload_teacher_model` | False | 非前向阶段将教师卸载到 CPU(仅 `teacher_model` 生效) |
|
||||
|
||||
完整参数说明见[命令行参数](./Command-line-parameters.md#rewardteacher模型参数)。
|
||||
|
||||
#### 外部教师 API
|
||||
|
||||
通过 `swift deploy --model xxx --infer_backend vllm` 部署教师服务后,训练进程按 prompt 向 API 请求 logprobs,无需在训练卡上加载教师权重。
|
||||
|
||||
- **GKD**:需设置 `--gkd_logits_topk`(API 仅返回 top-k logprobs),用于计算 JSD 散度损失。
|
||||
- **OPD-RL**:取采样 token 的 logp(`prompt_logprobs=0`),注入 advantage;系数由全局 `--teacher_kl_coef` 控制(见 [3.2](#32-opd-rlkl-作为-rl-advantage))。
|
||||
|
||||
#### Multi-Teacher(多教师路由)
|
||||
|
||||
Multi-Teacher 允许同时连接多个外部教师 API,按 tag 将每条样本路由到一个教师,实现领域专家蒸馏。
|
||||
|
||||
**`teacher_model_server` 格式**
|
||||
|
||||
```bash
|
||||
# 单 teacher
|
||||
--teacher_model_server http://localhost:8000
|
||||
|
||||
# 多 teacher(`tags` 与 `--dataset` 或数据列取值对应,见下方「模式一」)
|
||||
--teacher_model_server '[{"url":"http://t1:8000","tags":["data/math.jsonl"]},{"url":"http://t2:8001","tags":["data/code.jsonl"]}]'
|
||||
```
|
||||
|
||||
每个 teacher 配置项:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `url` | 教师 API 地址 |
|
||||
| `tags` | 该教师负责的数据来源;单 teacher 可省略;多 teacher 时各条目不重叠,且与下方路由方式中的标识一致 |
|
||||
|
||||
**路由方式**
|
||||
|
||||
**模式一:按数据集路由(默认)**
|
||||
|
||||
传入多个 `--dataset` 时,样本按来源数据集匹配 teacher。默认 `--teacher_tag_key` 为 `"dataset"`,`tags` 与 `--dataset` 中对应项保持一致:
|
||||
|
||||
```bash
|
||||
# Hub ID
|
||||
--dataset AI-ModelScope/alpaca-gpt4-data-en AI-ModelScope/alpaca-cleaned \
|
||||
--teacher_model_server '[{"url":"http://t1:8000","tags":["AI-ModelScope/alpaca-gpt4-data-en"]},{"url":"http://t2:8001","tags":["AI-ModelScope/alpaca-cleaned"]}]'
|
||||
|
||||
# 本地路径
|
||||
--dataset data/math.jsonl data/code.jsonl \
|
||||
--teacher_model_server '[{"url":"http://t1:8000","tags":["data/math.jsonl"]},{"url":"http://t2:8001","tags":["data/code.jsonl"]}]'
|
||||
```
|
||||
|
||||
**模式二:按样本路由**
|
||||
|
||||
在数据文件中为每条样本增加列(如 `teacher_tag`),设置 `--teacher_tag_key teacher_tag`,`tags` 与该列取值对应。适用于只传一个 `--dataset` 但仍需多个 teacher 的场景。
|
||||
|
||||
**示例:GKD + 多 teacher**
|
||||
|
||||
```bash
|
||||
# 部署两个教师服务(GKD 需 max_logprobs >= gkd_logits_topk)
|
||||
CUDA_VISIBLE_DEVICES=1 swift deploy --model Qwen/Qwen3.5-4B --port 8000 --max_logprobs 64
|
||||
CUDA_VISIBLE_DEVICES=2 swift deploy --model Qwen/Qwen3.5-1.7B --port 8001 --max_logprobs 64
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0 swift rlhf \
|
||||
--rlhf_type gkd \
|
||||
--model Qwen/Qwen3.5-0.6B \
|
||||
--teacher_model_server '[{"url":"http://localhost:8000","tags":["data/math.jsonl"]},{"url":"http://localhost:8001","tags":["data/code.jsonl"]}]' \
|
||||
--gkd_logits_topk 64 \
|
||||
--dataset data/math.jsonl data/code.jsonl \
|
||||
...
|
||||
```
|
||||
|
||||
**示例:OPD-RL + 多 teacher**
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=1 swift deploy --model Qwen/Qwen3.5-4B --port 8000 --max_logprobs 1
|
||||
CUDA_VISIBLE_DEVICES=2 swift deploy --model Qwen/Qwen3.5-1.7B --port 8001 --max_logprobs 1
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0 swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen3.5-0.6B \
|
||||
--teacher_model_server '[{"url":"http://localhost:8000","tags":["data/math.jsonl"]},{"url":"http://localhost:8001","tags":["data/code.jsonl"]}]' \
|
||||
--teacher_kl_coef 1.0 \
|
||||
--dataset data/math.jsonl data/code.jsonl \
|
||||
--use_vllm true --vllm_mode colocate \
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.1 GKD:散度作为直接损失
|
||||
|
||||
GKD([Generalized Knowledge Distillation](https://arxiv.org/pdf/2306.13649))直接把教师-学生间的散度作为损失函数反向传播。
|
||||
|
||||
**损失函数**
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{GKD}}(x, y) = \sum_{t=1}^{|y|} D_{\text{JSD}(\beta)}\big(P_{\text{teacher}}(\cdot|x,y_{<t}),\, P_{\text{student}}(\cdot|x,y_{<t})\big)
|
||||
$$
|
||||
|
||||
其中散度 $D$ 由 `--beta` 选择(见 2.1):$\beta=0$ 为 Forward KL,$\beta=1$ 为 Reverse KL,$0<\beta<1$ 为广义 JSD(默认 $0.5$)。
|
||||
|
||||
**On-Policy vs Off-Policy:`lmbda`**
|
||||
|
||||
GKD 通过 `lmbda` 控制每个 batch 用学生在线采样的概率:
|
||||
|
||||
```python
|
||||
if random() <= lmbda:
|
||||
y = student.generate(x) # on-policy:学生自己采样
|
||||
else:
|
||||
y = y_ground_truth # off-policy:使用数据集标注
|
||||
loss = D(P_teacher(·|x, y), P_student(·|x, y))
|
||||
```
|
||||
|
||||
- `lmbda=0`:纯离线(传统 SFT 蒸馏)。
|
||||
- `lmbda=1`:纯在线(学生从自身错误中学习,即 on-policy 蒸馏)。
|
||||
- `0<lmbda<1`:混合。
|
||||
|
||||
> 若希望走teacher生成数据路径,需先用教师离线生成响应写入数据集,再设 `lmbda=0` 训练
|
||||
|
||||
**GKD参数**
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `--beta` | float | 0.5 | 散度插值:0=Forward KL,0.5=JSD,1=Reverse KL |
|
||||
| `--lmbda` | float | 0.5 | 在线采样概率:0=离线,1=纯在线 |
|
||||
| `--sft_alpha` | float | 0 | 混合 SFT loss 比例,最终 `loss = gkd_loss + sft_alpha * sft_loss`(仅对**非学生生成**的数据生效) |
|
||||
| `--gkd_logits_topk` | int | None | 仅用教师 top-K logits 计算 KL;使用 `teacher_model_server` 时为必填 |
|
||||
|
||||
**Top-K 蒸馏(省显存)**
|
||||
|
||||
默认用完整词表计算 KL,词表很大时容易 OOM,可使用`--gkd_logits_topk`参数。
|
||||
|
||||
**外部教师 API**
|
||||
|
||||
设置 `--teacher_model_server` 时需同时设置 `--gkd_logits_topk`(API 仅返回 top-k logprobs)。示例如下:
|
||||
|
||||
```bash
|
||||
# 步骤 1:部署教师模型(max_logprobs 需 >= gkd_logits_topk)
|
||||
CUDA_VISIBLE_DEVICES=0 swift deploy \
|
||||
--model Qwen/Qwen3.5-9B \
|
||||
--infer_backend vllm \
|
||||
--port 8000 \
|
||||
--max_logprobs 64
|
||||
|
||||
# 步骤 2:启动 GKD 训练
|
||||
CUDA_VISIBLE_DEVICES=1,2,3,4 \
|
||||
NPROC_PER_NODE=4 \
|
||||
swift rlhf \
|
||||
--rlhf_type gkd \
|
||||
--model Qwen/Qwen3.5-2B \
|
||||
--teacher_model_server http://localhost:8000 \
|
||||
--gkd_logits_topk 64 \
|
||||
--lmbda 1.0 \
|
||||
--beta 1.0 \
|
||||
--dataset xxx
|
||||
```
|
||||
|
||||
**在线采样加速**
|
||||
|
||||
`lmbda > 0` 时学生需在线生成序列,建议用 vLLM 加速采样(colocate / server 两种模式,与 GRPO 一致),参考 [GRPO 文档](./GRPO/GetStarted/GRPO.md#集群支持)。
|
||||
|
||||
**多轮 GKD**
|
||||
|
||||
GKD 多轮训练,与 GRPO 共享同一套 `MultiTurnScheduler` 基础设施。
|
||||
|
||||
完整接口和自定义方式请参考 [GRPO 多轮训练文档](./GRPO/DeveloperGuide/multi_turn.md)。
|
||||
|
||||
**参考脚本**
|
||||
|
||||
- 基础训练:[examples/train/rlhf/gkd/](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/gkd/)
|
||||
- 多轮训练:[examples/train/rlhf/gkd/multi_turn.sh](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/gkd/multi_turn.sh)
|
||||
- 多模态:[examples/train/multimodal/rlhf/gkd/](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal/rlhf/gkd/)
|
||||
- Megatron:[examples/megatron/rlhf/gkd/](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/rlhf/gkd/)
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 3.2 OPD-RL:KL 作为 RL Advantage
|
||||
|
||||
OPD(On-Policy Distillation)RL 把教师 KL 信号注入 GRPO 的 **per-token advantage**,通过 policy gradient 更新学生。
|
||||
|
||||
**原理**
|
||||
|
||||
标准 GRPO 的 advantage 来自组内归一化的任务奖励(per-sequence 标量)。OPD-RL 在 advantage 归一化**之后**逐 token 注入教师信号:
|
||||
|
||||
$$
|
||||
A_t = A_t^{\text{base}} + \alpha \cdot \big(\log \pi_{\text{teacher}}(y_t|x,y_{<t}) - \log \pi_{\text{student}}(y_t|x,y_{<t})\big)
|
||||
$$
|
||||
|
||||
- $A_t^{\text{base}}$:GRPO 归一化后的任务奖励 advantage(无奖励函数时为 0)。
|
||||
- $\alpha$:`--teacher_kl_coef`,教师信号强度。
|
||||
- $\log\pi_{\text{teacher}}(y_t) - \log\pi_{\text{student}}(y_t)$:教师 log-ratio,即 Reverse KL 梯度中 $\nabla_\theta\log\pi_\theta(y_t)$ 系数对应的 k1 估计量(见 `compute_teacher_logratio`)。
|
||||
|
||||
**纯蒸馏模式**:不设 `--reward_funcs` 时 base advantage 为 0,教师信号是唯一驱动力,此时 $A_t = \alpha\cdot(\log\pi_{\text{teacher}}(y_t)-\log\pi_{\text{student}}(y_t))$。
|
||||
|
||||
**监控指标**:日志中的 `teacher_kl` 是 k3 估计量 $e^{d}-d-1$($d=\log\pi_{\text{teacher}}-\log\pi_{\text{student}}$),衡量学生与教师的距离。
|
||||
|
||||
**启用方式**:在 `--rlhf_type grpo` 下设置 `--teacher_model` 或 `--teacher_model_server` 即自动启用 OPD-RL,无需额外开关。教师相关参数见上文共享参数表。
|
||||
|
||||
**OPD-RL 特有参数**
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `--teacher_kl_coef` | 1.0 | 教师 log-ratio 注入 advantage 的系数 $\alpha$ |
|
||||
|
||||
**参考脚本**
|
||||
|
||||
- HF:[examples/train/grpo/opd_rl.sh](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/opd_rl.sh)
|
||||
- Megatron:[examples/megatron/grpo/opd_rl.sh](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/grpo/opd_rl.sh)
|
||||
- Ray:[examples/ray/grpo/run_opd.sh](https://github.com/modelscope/ms-swift/tree/main/examples/ray/grpo/run_opd.sh)
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 3.3 OPSD:On-Policy Self-Distillation
|
||||
|
||||
OPSD([On-Policy Self-Distillation](https://arxiv.org/abs/2601.18734))是一种单模型自蒸馏方法:同一模型分别构造学生输入与教师输入,教师侧额外接收**特权信息**(如参考解答),再对齐两者在学生采样响应上的输出分布。
|
||||
|
||||
**核心机制**
|
||||
|
||||
- **学生**:仅看到问题,正常推理。
|
||||
- **教师**:看到问题 + 参考解答(通过 `teacher_prompt` 列提供特权信息)。
|
||||
- **训练目标**:用散度(JSD / KL)对齐学生与教师在同一份学生采样响应上的输出分布。
|
||||
|
||||
OPSD 既可走 GKD 路径,也可走 OPD-RL 路径:
|
||||
|
||||
- **GKD + OPSD**:`--rlhf_type gkd`,教师 KL 作为直接损失。
|
||||
- **OPD-RL + OPSD**:`--rlhf_type grpo`;动态模式不传 `--teacher_model`,固定模式设 `--teacher_model` 与 `--model` 相同。
|
||||
|
||||
**两种自蒸馏权重模式**
|
||||
|
||||
| 模式 | 参数配置 | 教师权重 | 说明 |
|
||||
|------|---------|---------|------|
|
||||
| **Dynamic(动态)** | 不传 `--teacher_model` | 学生当前权重 | 教师随训练同步更新 |
|
||||
| **Fixed(固定)** | `--teacher_model` 设为与 `--model` 相同 | 初始教师权重 | 教师权重固定 |
|
||||
|
||||
**数据格式**
|
||||
|
||||
OPSD 数据集需包含 `teacher_prompt` 列,可通过 `--external_plugins` 加载数据处理插件来构建。以数学推理数据集 `open-r1/OpenThoughts-114k-math` 为例:
|
||||
|
||||
```python
|
||||
from swift.dataset import DatasetMeta, RowPreprocessor, register_dataset
|
||||
|
||||
class OpenThoughtsOPSDPreprocessor(RowPreprocessor):
|
||||
def preprocess(self, row):
|
||||
if not row.get('correct', True):
|
||||
return None
|
||||
problem = row.get('problem', '')
|
||||
solution = row.get('solution', '')
|
||||
teacher_prompt = f'{problem}\n\nReference solution:\n{solution}\n\nNow articulate your own reasoning.'
|
||||
messages = [
|
||||
{'role': 'system', 'content': 'Please reason step by step, and put your final answer within \\boxed{}.'},
|
||||
{'role': 'user', 'content': problem},
|
||||
]
|
||||
return {'messages': messages, 'teacher_prompt': teacher_prompt}
|
||||
|
||||
register_dataset(DatasetMeta(
|
||||
ms_dataset_id='open-r1/OpenThoughts-114k-math',
|
||||
preprocess_func=OpenThoughtsOPSDPreprocessor(),
|
||||
tags=['math', 'opsd'],
|
||||
))
|
||||
```
|
||||
|
||||
**参考脚本**
|
||||
|
||||
- HF:[examples/train/rlhf/opsd/](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/opsd/)
|
||||
- Megatron:[examples/megatron/rlhf/gkd/opsd.sh](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/rlhf/gkd/opsd.sh)
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
- Kevin Lu & Thinking Machines Lab. [On-Policy Distillation](https://thinkingmachines.ai/blog/on-policy-distillation/). 2025.
|
||||
- Agarwal et al. [On-Policy Distillation of Language Models (GKD)](https://arxiv.org/pdf/2306.13649). 2023.
|
||||
- Gu et al. [MiniLLM: Knowledge Distillation of Large Language Models](https://arxiv.org/abs/2306.08543). 2023.
|
||||
- DeepSeek-AI. [DeepSeek-V4](https://arxiv.org/html/2606.19348v1). 2026.
|
||||
- Qwen Team. [Qwen3 Technical Report](https://arxiv.org/abs/2505.09388). 2025.
|
||||
- Zhipu AI. [GLM-5: from Vibe Coding to Agentic Engineering](https://arxiv.org/html/2602.15763). 2026.
|
||||
- Kimi Team. [Kimi K2.5: Visual Agentic Intelligence](https://arxiv.org/abs/2602.02276). 2026.
|
||||
@@ -0,0 +1,284 @@
|
||||
# 评测
|
||||
|
||||
SWIFT支持了eval(评测)能力,用于对原始模型和训练后的模型给出标准化的评测指标。
|
||||
|
||||
## 能力介绍
|
||||
|
||||
SWIFT的eval能力使用了魔搭社区[评测框架EvalScope](https://github.com/modelscope/eval-scope),并进行了高级封装以支持各类模型的评测需求。
|
||||
|
||||
> 注意:EvalScope支持许多其他的复杂能力,例如[模型的性能评测](https://evalscope.readthedocs.io/zh-cn/latest/user_guides/stress_test/quick_start.html),请直接使用EvalScope框架。
|
||||
|
||||
目前我们支持了**标准评测集**的评测流程,以及**用户自定义**评测集的评测流程。其中**标准评测集**由三个评测后端提供支持:
|
||||
|
||||
下面展示所支持的数据集名称,若需了解数据集的详细信息,请参考[所有支持的数据集](https://evalscope.readthedocs.io/zh-cn/latest/get_started/supported_dataset/index.html)
|
||||
|
||||
1. Native(默认):
|
||||
|
||||
主要支持纯文本评测,同时**支持**评测结果可视化
|
||||
```text
|
||||
'arc', 'bbh', 'ceval', 'cmmlu', 'competition_math',
|
||||
'general_qa', 'gpqa', 'gsm8k', 'hellaswag', 'humaneval',
|
||||
'ifeval', 'iquiz', 'mmlu', 'mmlu_pro',
|
||||
'race', 'trivia_qa', 'truthful_qa'
|
||||
```
|
||||
|
||||
2. OpenCompass:
|
||||
|
||||
主要支持纯文本评测,暂**不支持**评测结果可视化
|
||||
```text
|
||||
'obqa', 'cmb', 'AX_b', 'siqa', 'nq', 'mbpp', 'winogrande', 'mmlu', 'BoolQ', 'cluewsc', 'ocnli', 'lambada',
|
||||
'CMRC', 'ceval', 'csl', 'cmnli', 'bbh', 'ReCoRD', 'math', 'humaneval', 'eprstmt', 'WSC', 'storycloze',
|
||||
'MultiRC', 'RTE', 'chid', 'gsm8k', 'AX_g', 'bustm', 'afqmc', 'piqa', 'lcsts', 'strategyqa', 'Xsum', 'agieval',
|
||||
'ocnli_fc', 'C3', 'tnews', 'race', 'triviaqa', 'CB', 'WiC', 'hellaswag', 'summedits', 'GaokaoBench',
|
||||
'ARC_e', 'COPA', 'ARC_c', 'DRCD'
|
||||
```
|
||||
|
||||
3. VLMEvalKit:
|
||||
|
||||
主要支持多模态评测,暂**不支持**评测结果可视化
|
||||
```text
|
||||
'COCO_VAL', 'MME', 'HallusionBench', 'POPE', 'MMBench_DEV_EN', 'MMBench_TEST_EN', 'MMBench_DEV_CN', 'MMBench_TEST_CN',
|
||||
'MMBench', 'MMBench_CN', 'MMBench_DEV_EN_V11', 'MMBench_TEST_EN_V11', 'MMBench_DEV_CN_V11',
|
||||
'MMBench_TEST_CN_V11', 'MMBench_V11', 'MMBench_CN_V11', 'SEEDBench_IMG', 'SEEDBench2',
|
||||
'SEEDBench2_Plus', 'ScienceQA_VAL', 'ScienceQA_TEST', 'MMT-Bench_ALL_MI', 'MMT-Bench_ALL',
|
||||
'MMT-Bench_VAL_MI', 'MMT-Bench_VAL', 'AesBench_VAL', 'AesBench_TEST', 'CCBench', 'AI2D_TEST', 'MMStar',
|
||||
'RealWorldQA', 'MLLMGuard_DS', 'BLINK', 'OCRVQA_TEST', 'OCRVQA_TESTCORE', 'TextVQA_VAL', 'DocVQA_VAL',
|
||||
'DocVQA_TEST', 'InfoVQA_VAL', 'InfoVQA_TEST', 'ChartQA_TEST', 'MathVision', 'MathVision_MINI',
|
||||
'MMMU_DEV_VAL', 'MMMU_TEST', 'OCRBench', 'MathVista_MINI', 'LLaVABench', 'MMVet', 'MTVQA_TEST',
|
||||
'MMLongBench_DOC', 'VCR_EN_EASY_500', 'VCR_EN_EASY_100', 'VCR_EN_EASY_ALL', 'VCR_EN_HARD_500',
|
||||
'VCR_EN_HARD_100', 'VCR_EN_HARD_ALL', 'VCR_ZH_EASY_500', 'VCR_ZH_EASY_100', 'VCR_ZH_EASY_ALL',
|
||||
'VCR_ZH_HARD_500', 'VCR_ZH_HARD_100', 'VCR_ZH_HARD_ALL', 'MMDU', 'MMBench-Video', 'Video-MME'
|
||||
```
|
||||
|
||||
## 环境准备
|
||||
|
||||
```shell
|
||||
pip install ms-swift[eval] -U
|
||||
```
|
||||
|
||||
或从源代码安装:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/modelscope/ms-swift.git
|
||||
cd ms-swift
|
||||
pip install -e '.[eval]'
|
||||
```
|
||||
|
||||
## 评测
|
||||
|
||||
支持纯文本评测、多模态评测、url评测、自定义数据集评测四种方式
|
||||
|
||||
**基本示例**
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift eval \
|
||||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||||
--eval_backend Native \
|
||||
--infer_backend transformers \
|
||||
--eval_limit 10 \
|
||||
--eval_dataset gsm8k
|
||||
```
|
||||
其中:
|
||||
- model: 可指定本地模型路径或者modelscope上的模型ID
|
||||
- eval_backend: 可选 Native, OpenCompass, VLMEvalKit,默认为 Native
|
||||
- infer_backend: 可选 transformers, vllm, sglang, lmdeploy,默认为 transformers
|
||||
- eval_limit: 每个评测集的采样数,默认为None,表示使用全部数据,可用于快速验证
|
||||
- eval_dataset: 评测数据集,可设置多个数据集,用空格分割
|
||||
|
||||
**复杂评测示例**
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift eval \
|
||||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||||
--eval_backend Native \
|
||||
--infer_backend transformers \
|
||||
--eval_limit 10 \
|
||||
--eval_dataset gsm8k \
|
||||
--eval_dataset_args '{"gsm8k": {"few_shot_num": 0, "filters": {"remove_until": "</think>"}}}' \
|
||||
--eval_generation_config '{"max_tokens": 512, "temperature": 0}' \
|
||||
--extra_eval_args '{"ignore_errors": true, "debug": true}'
|
||||
```
|
||||
|
||||
详细评测的参数列表可以参考[这里](Command-line-parameters.md#评测参数)。
|
||||
|
||||
## 训练中评测
|
||||
|
||||
SWIFT支持在训练过程中使用EvalScope对当前的模型进行评测,以便及时了解模型的训练效果。
|
||||
|
||||
**基本示例**
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift sft \
|
||||
--model "Qwen/Qwen2.5-0.5B-Instruct" \
|
||||
--tuner_type "lora" \
|
||||
--dataset "AI-ModelScope/alpaca-gpt4-data-zh#100" \
|
||||
--torch_dtype "bfloat16" \
|
||||
--num_train_epochs "1" \
|
||||
--per_device_train_batch_size "1" \
|
||||
--learning_rate "1e-4" \
|
||||
--lora_rank "8" \
|
||||
--lora_alpha "32" \
|
||||
--target_modules "all-linear" \
|
||||
--gradient_accumulation_steps "16" \
|
||||
--save_steps "50" \
|
||||
--save_total_limit "5" \
|
||||
--logging_steps "5" \
|
||||
--max_length "2048" \
|
||||
--eval_strategy "steps" \
|
||||
--eval_steps "5" \
|
||||
--per_device_eval_batch_size "5" \
|
||||
--eval_use_evalscope \
|
||||
--eval_dataset "gsm8k" \
|
||||
--eval_dataset_args '{"gsm8k": {"few_shot_num": 0}}' \
|
||||
--eval_limit "10"
|
||||
```
|
||||
|
||||
注意启动命令为`sft`,其中eval相关的参数有:
|
||||
- eval_strategy: 评估策略。默认为None,跟随`save_strategy`的策略
|
||||
- eval_steps: 默认为None,如果存在评估数据集,则跟随`save_steps`的策略
|
||||
- eval_use_evalscope: 是否使用evalscope进行评测,需要设置该参数来开启评测
|
||||
- eval_dataset: 评测数据集,可设置多个数据集,用空格分割
|
||||
- eval_dataset_args: 评测数据集参数,json格式,可设置多个数据集的参数
|
||||
- eval_limit: 评测数据集采样数
|
||||
- eval_generation_config: 评测时模型推理配置,json格式,默认为`{'max_tokens': 512}`
|
||||
|
||||
|
||||
更多评测的样例可以参考[examples](https://github.com/modelscope/ms-swift/tree/main/examples/eval)
|
||||
|
||||
## 自定义评测集
|
||||
|
||||
本框架支持选择题和问答题,两种预定义的数据集格式,使用流程如下:
|
||||
|
||||
*注意:使用自定义评测时,eval_backend参数必须为Native*
|
||||
|
||||
### 选择题格式(MCQ)
|
||||
适合用户是选择题的场景,评测指标为准确率(accuracy)。
|
||||
|
||||
**数据准备**
|
||||
|
||||
准备选择题格式的csv文件,该目录结构如下:
|
||||
|
||||
```text
|
||||
mcq/
|
||||
├── example_dev.csv # (可选)文件名组成为`{subset_name}_dev.csv`,用于fewshot评测
|
||||
└── example_val.csv # 文件名组成为`{subset_name}_val.csv`,用于实际评测的数据
|
||||
```
|
||||
|
||||
其中csv文件需要为下面的格式:
|
||||
|
||||
```text
|
||||
id,question,A,B,C,D,answer
|
||||
1,通常来说,组成动物蛋白质的氨基酸有____,4种,22种,20种,19种,C
|
||||
2,血液内存在的下列物质中,不属于代谢终产物的是____。,尿素,尿酸,丙酮酸,二氧化碳,C
|
||||
```
|
||||
其中:
|
||||
- `id`是序号(可选)
|
||||
- `question`是问题
|
||||
- `A`, `B`, `C`, `D`等是可选项,最大支持10个选项
|
||||
- `answer`是正确选项
|
||||
|
||||
**启动评测**
|
||||
|
||||
运行下面的命令:
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift eval \
|
||||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||||
--eval_backend Native \
|
||||
--infer_backend transformers \
|
||||
--eval_dataset general_mcq \
|
||||
--eval_dataset_args '{"general_mcq": {"local_path": "/path/to/mcq", "subset_list": ["example"]}}'
|
||||
```
|
||||
其中:
|
||||
- `eval_dataset` 需要设置为 `general_mcq`
|
||||
- `eval_dataset_args` 需要设置
|
||||
- `local_path` 自定义数据集文件夹路径
|
||||
- `subset_list` 评测数据集名称,上述 `*_dev.csv` 中的 `*`
|
||||
|
||||
**运行结果**
|
||||
|
||||
```text
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Model | Dataset | Metric | Subset | Num | Score | Cat.0 |
|
||||
+=====================+=============+=================+==========+=======+=========+=========+
|
||||
| Qwen2-0.5B-Instruct | general_mcq | AverageAccuracy | example | 12 | 0.5833 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
```
|
||||
|
||||
## 问答题格式(QA)
|
||||
适合用户是问答题的场景,评测指标是`ROUGE`和`BLEU`。
|
||||
|
||||
**数据准备**
|
||||
|
||||
准备一个问答题格式的jsonline文件,该目录包含了一个文件:
|
||||
|
||||
```text
|
||||
qa/
|
||||
└── example.jsonl
|
||||
```
|
||||
|
||||
该jsonline文件需要为下面的格式:
|
||||
|
||||
```json
|
||||
{"query": "中国的首都是哪里?", "response": "中国的首都是北京"}
|
||||
{"query": "世界上最高的山是哪座山?", "response": "是珠穆朗玛峰"}
|
||||
{"query": "为什么北极见不到企鹅?", "response": "因为企鹅大多生活在南极"}
|
||||
```
|
||||
|
||||
**启动评测**
|
||||
|
||||
运行下面的命令:
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift eval \
|
||||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||||
--eval_backend Native \
|
||||
--infer_backend transformers \
|
||||
--eval_dataset general_qa \
|
||||
--eval_dataset_args '{"general_qa": {"local_path": "/path/to/qa", "subset_list": ["example"]}}'
|
||||
```
|
||||
|
||||
其中:
|
||||
- `eval_dataset` 需要设置为 `general_qa`
|
||||
- `eval_dataset_args` 是一个json字符串,需要设置:
|
||||
- `local_path` 自定义数据集文件夹路径
|
||||
- `subset_list` 评测数据集名称,上述 `*.jsonl` 中的 `*`
|
||||
|
||||
**运行结果**
|
||||
|
||||
```text
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Model | Dataset | Metric | Subset | Num | Score | Cat.0 |
|
||||
+=====================+=============+=================+==========+=======+=========+=========+
|
||||
| Qwen2-0.5B-Instruct | general_qa | bleu-1 | default | 12 | 0.2324 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | bleu-2 | default | 12 | 0.1451 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | bleu-3 | default | 12 | 0.0625 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | bleu-4 | default | 12 | 0.0556 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-1-f | default | 12 | 0.3441 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-1-p | default | 12 | 0.2393 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-1-r | default | 12 | 0.8889 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-2-f | default | 12 | 0.2062 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-2-p | default | 12 | 0.1453 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-2-r | default | 12 | 0.6167 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-l-f | default | 12 | 0.333 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-l-p | default | 12 | 0.2324 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-l-r | default | 12 | 0.8889 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
```
|
||||
@@ -0,0 +1,59 @@
|
||||
# 导出与推送
|
||||
|
||||
|
||||
## Merge LoRA
|
||||
|
||||
- 查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/export/merge_lora.sh)。
|
||||
|
||||
## 量化
|
||||
|
||||
SWIFT支持AWQ、GPTQ、FP8、BNB模型的量化导出。其中使用AWQ、GPTQ需使用校准数据集,量化性能较好但量化耗时较长;而FP8、BNB无需校准数据集,量化耗时较短。
|
||||
|
||||
| 量化技术 | 多模态 | 推理加速 | 继续训练 |
|
||||
| -------- | ------ | -------- | -------- |
|
||||
| FP8 | ✅ | ✅ | ✅ |
|
||||
| GPTQ | ✅ | ✅ | ✅ |
|
||||
| AWQ | ✅ | ✅ | ✅ |
|
||||
| BNB | ❌ | ✅ | ✅ |
|
||||
|
||||
|
||||
除SWIFT安装外,需要安装以下额外依赖:
|
||||
```shell
|
||||
# 使用awq量化:
|
||||
# autoawq和cuda版本有对应关系,请按照`https://github.com/casper-hansen/AutoAWQ`选择版本
|
||||
# 如果出现torch依赖冲突,请额外增加指令`--no-deps`
|
||||
pip install autoawq -U
|
||||
|
||||
# 使用gptq量化:
|
||||
# auto_gptq和cuda版本有对应关系,请按照`https://github.com/PanQiWei/AutoGPTQ#quick-installation`选择版本
|
||||
pip install auto_gptq optimum -U
|
||||
|
||||
# 使用gptq v2量化:
|
||||
pip install gptqmodel optimum -U
|
||||
|
||||
# 使用bnb量化:
|
||||
pip install bitsandbytes -U
|
||||
```
|
||||
|
||||
我们提供了一系列脚本展现SWIFT的量化导出能力:
|
||||
- 支持[AWQ](https://github.com/modelscope/ms-swift/blob/main/examples/export/quantize/awq.sh)/[GPTQ](https://github.com/modelscope/ms-swift/blob/main/examples/export/quantize/gptq.sh)/[GPTQ v2](https://github.com/modelscope/ms-swift/blob/main/examples/export/quantize/gptq_v2.sh)/[BNB](https://github.com/modelscope/ms-swift/blob/main/examples/export/quantize/bnb.sh)量化导出。
|
||||
- 多模态量化: 支持使用GPTQ和AWQ对多模态模型进行量化,其中AWQ支持的多模态模型有限。参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize/mllm)。
|
||||
- 更多系列模型的支持: 支持[Bert](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize/bert),[Reward Model](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize/reward_model)的量化导出。
|
||||
- 使用SWIFT量化导出的模型支持使用vllm/sglang/lmdeploy进行推理加速;也支持使用QLoRA继续进行SFT/RLHF。
|
||||
|
||||
|
||||
## 推送模型
|
||||
|
||||
SWIFT支持将训练/量化的模型重新推送到ModelScope/HuggingFace。默认推送到ModelScope,你可以指定`--use_hf true`推送到HuggingFace。
|
||||
```shell
|
||||
swift export \
|
||||
--model output/vx-xxx/checkpoint-xxx \
|
||||
--push_to_hub true \
|
||||
--hub_model_id '<model-id>' \
|
||||
--hub_token '<sdk-token>' \
|
||||
--use_hf false
|
||||
```
|
||||
|
||||
小贴士:
|
||||
- 你可以使用`--model <checkpoint-dir>`或者`--adapters <checkpoint-dir>`指定需要推送的checkpoint目录,这两种写法在推送模型场景没有差异。
|
||||
- 推送到ModelScope时,你需要确保你已经注册了魔搭账号,你的SDK token可以在[该页面](https://www.modelscope.cn/my/myaccesstoken)中获取。推送模型需确保sdk token的账号具有model_id对应组织的编辑权限。推送模型将自动创建对应model_id的模型仓库(如果该模型仓库不存在),你可以使用`--hub_private_repo true`来自动创建私有的模型仓库。
|
||||
@@ -0,0 +1,471 @@
|
||||
# 常见问题整理
|
||||
|
||||
下面是SWIFT使用过程中遇到的一些常见问题。
|
||||
|
||||
## 训练
|
||||
|
||||
SWIFT支持的训练方法包括预训练、指令监督微调、偏好学习、GRPO、Embedding、Reranker、序列分类任务等,详见[主页](https://github.com/modelscope/ms-swift/blob/main/README_CN.md)。
|
||||
|
||||
### Q1: SWIFT支持的模型有哪些?如何设置本地模型路径?
|
||||
支持的模型详见文档[支持的模型和数据集](https://swift.readthedocs.io/zh-cn/latest/Instruction/Supported-models-and-datasets.html)。如果模型已经下载到了本地,设置`--model <path_to_model>`即可。对于离线环境训练,同时设置`--model 本地路径`,`--check_model false`,如果提示git clone相关报错,需要clone repo,然后通过`local_repo_path`指定,详见[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。从ModelScope下载的模型,可以配置环境变量`MODELSCOPE_CACHE=your_path`将原始的模型存到指定路径;如果用ModelScope SDK下载,通过`cache_dir="本地地址"`;也可以使用`modelscope download`命令行工具或`git`下载,详见modelscope文档[模型下载](https://modelscope.cn/docs/models/download)。如果需要从Hugging Face下载模型,设置环境变量`USE_HF=1`。
|
||||
SWIFT会自动匹配model_type,也可以查看文档[支持的模型和数据集](https://swift.readthedocs.io/zh-cn/latest/Instruction/Supported-models-and-datasets.html),手动指定。
|
||||
|
||||
### Q2: SWIFT支持的数据集有哪些?如何使用自定义数据集?
|
||||
支持的数据集详见文档[支持的模型和数据集](https://swift.readthedocs.io/zh-cn/latest/Instruction/Supported-models-and-datasets.html)。自定义数据集格式及使用方法见文档[自定义数据集](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html),符合这些格式的数据集会自动使用swift内置的数据预处理器。如果与文档中的格式不一致,请自行转换格式,或者参考已支持的数据集接入。若自定义数据集中有额外的字段,这些字段默认不会被使用,可以通过[命令行参数remove_unused_columns](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id4)进行设置。
|
||||
需要将数据集下载到本地,然后通过路径指定,请查看[自定义数据集文档](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html#dataset-info-json)。`git clone`下载到本地,然后通过dataset_info.json文件中的`dataset_path`字段指定就行。
|
||||
数据随机详见[命令行参数dataset_shuffle](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
强制重新下载数据集,设置命令行参数`--download_mode`。对数据集进行错误检查,请设置命令行参数`strict`。需要数据集质检工具时,可以查看另一个库[data-juicer](https://github.com/modelscope/data-juicer)。
|
||||
由于datasets的底层pyarrow对于类型管控比较严格,图像grounding数据集的objects部分、agent数据集的tools部分等,因为这个原因要用str,要不pyarrow就会报错:你每行的类型不一致。
|
||||
训练中遇到报错`AttributeError:’TrainerState’ object has no attribute ’last_model_checkpoint’`,数据集太少了,数据数量不足一个step导致的报错,增加一些数据。另外,切分的验证集数据很少时也会有类似报错。
|
||||
下面是一个assistant字段为空导致的报错:
|
||||
```text
|
||||
File "/mnt/workspace/swift/swift/1lm/dataset/preprocessor/core. py", line 69, in _check_messages raise
|
||||
ValueError(f'assistant_message; {assistant_message}')
|
||||
ValueError: assistant_message: {'role' :'assistant', 'content': ''}
|
||||
```
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 NPROC_PER_NODE=1 MAX_PIXELS=1003520 swift sft --model Qwen/Qwen2.5-VL-7B-Instruct --tuner_type lora --dataset /mnt/workspace/data.json --deepspeed zero2 --max_length 16384
|
||||
```
|
||||
数据集assistant字段为空,如果是推理,把这个空字符串删掉,因为这个会导致训练时NaN,会做检查。
|
||||
|
||||
### Q3: 从缓存加载数据集相关问题
|
||||
设置命令行参数`--load_from_cache_file true`,可以加快数据集加载速度,尤其是在多模态数据集、数据量较大等场景。在debug或修改preprocessor时,设置为false,更多说明请在[命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)中搜索该参数。
|
||||
|
||||
### Q4: 如何搭建SWIFT环境?有镜像可以使用吗?
|
||||
环境搭建详见[SWIFT安装文档](https://swift.readthedocs.io/zh-cn/latest/GetStarted/SWIFT-installation.html),一些常见依赖的推荐版本可以在[主页](https://github.com/modelscope/ms-swift/blob/main/README_CN.md)上找到。文档中提供了镜像,用`docker run`命令启动容器即可,如:`docker run --gpus all -p 8000:8000 -it -d --name ms modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.9.0-vllm0.13.0-modelscope1.33.0-swift3.12.5 /bin/bash`,启动容器后拉最新代码安装swift。
|
||||
|
||||
### Q5: 多模态模型训练数据格式、参数冻结、优化器设置相关问题
|
||||
多模态模型训练的[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal)。支持纯文本、图文数据训练,也可以两种数据混合训练。图像、视频、音频相关的参数,如,最大像素、fps等请查看[特定模型参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id19)。
|
||||
Grounding任务中通用数据格式支持了一个物体对应多个bbox,参考文档[自定义数据集](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html#grounding)。videos可以是图片列表,使用文件目录的方式。
|
||||
SWIFT按max_pixels对图像进行调整,会保存预处理前和后的图像,然后对bbox进行调整,不过推理没有这样的调整,需要提前手动处理图像。
|
||||
VLM模型训练减少显存使用,请配置`--freeze_vit true`,以及限制最大像素的参数`--max_pixels`。`--freeze_vit`,`--freeze_aligner`,`--freeze_llm`这几个参数详见[命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#tuner)。如果ViT没有训练,那有会有warning: none of the inputs have requires_grad=True是正常的,如果训练了,则不应该抛出。
|
||||
使用全参数微调visual encoder同时使用LoRA微调LLM,参考这里[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal/lora_llm_full_vit)。
|
||||
|
||||
### Q6: template相关问题
|
||||
由于jinja chat template没有labels,所以不支持训练。
|
||||
多模态数据集如果需要在加载数据之后做动态数据增强,例如,给输入数据随机添加噪声等,请在template中修改encode方法。
|
||||
|
||||
### Q7: SWIFT训练如何debug?
|
||||
详见[预训练与微调文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Pre-training-and-Fine-tuning.html)。
|
||||
|
||||
### Q8: SWIFT如何使用python脚本训练?
|
||||
参考[notebook例子](https://github.com/modelscope/ms-swift/tree/main/examples/notebook)。
|
||||
|
||||
### Q9: SWIFT如何使用UI界面训练?
|
||||
使用`swift web-ui`命令,界面训练与命令行一致,界面上的参数请查看命令行参数文档。自定义数据集的使用与上面Q2一致。Megatron-SWIFT不支持UI界面训练。
|
||||
|
||||
### Q10: 单机多卡训练相关问题
|
||||
SWIFT多卡训练底层依赖torchrun。`deepspeed` 和 `device_map`不兼容,两个只能选1个。更多细节请查看代码库中的[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-gpu)。
|
||||
|
||||
### Q11: 多机多卡训练相关问题
|
||||
请查看[多机多卡例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-node)。多机多卡训练时,只有主节点有日志。
|
||||
多机训练速度缓慢,如,使用DeepSpeed ZeRO3训练会出现严重的速度下降,请查看[issue](https://github.com/modelscope/ms-swift/issues/1825)。
|
||||
|
||||
### Q12: 大规模数据集相关问题
|
||||
数据集太大了,然后每次tokenize都需要很久,请使用`lazy_tokenize`或流式读取`streaming`,详见[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
|
||||
### Q13: 断点续训相关问题
|
||||
先前训练脚本中的参数不变,加上`--resume_from_checkpoint output/xxx/vx-xxx/checkpoint-xxx`,详见[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。如果数据集发生了改动,仅加载模型,请同时设置`--resume_only_model`。更复杂的场景,请在命令行参数文档中搜索resume。
|
||||
|
||||
### Q14: 数据集流式加载相关问题
|
||||
流式加载`--streaming true`,一边训练一边加载,需要设置max_steps,详见`streaming`参数说明,[命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id4)。
|
||||
注意:streaming是不随机的,也不划分验证集,验证集通过命令行参数`val_dataset`指定。
|
||||
断点续训时,流式只能往前索引,不能随机索引,跳过已经训练的数据耗时特别长,不建议用流式。
|
||||
|
||||
### Q15: packing相关问题
|
||||
packing要和flash_attn一起使用,不然是有误差,attention_mask会出问题。
|
||||
Qwen3.5模型中的linear-attention不支持var_len,不建议开启packing。
|
||||
开启packing,多模态数据会有两次map,map完一次后还会进行第二次mapping,一次是数据集的,一次是template的。如果速度非常慢,可以设置`OMP_NUM_THREADS=14`加速,或者可以把packing去掉,就不会有第二次了。
|
||||
|
||||
### Q16: 数据集多进程处理
|
||||
数据集map过程比较慢时,设置参数`--dataset_num_proc`可以开多进程。多模态数据集map比较慢是正常的。
|
||||
|
||||
### Q17: 当前训练完默认保存多少个checkpoint?
|
||||
默认保存所有的checkpoint,详见[命令行参数 save_total_limit](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
|
||||
### Q18: 训练过程的loss和acc
|
||||
自定义的损失函数在plugin中加就可以。如果需要不同数据集的loss曲线,请设置`--enable_channel_loss`。
|
||||
如果eval得到的acc和对应保存的ckpt去重新推理一遍计算得到的acc不是一致的,是因为训练时候的eval_acc和推理时候的acc计算方式不一样导致的。`acc_strategy`: 默认为`'token'`, 可选择的值包括: `'token'`,`'seq'`。
|
||||
训练过程中没有token_acc是因为有些模型`logits`和`labels`数量对不上,就不算的。
|
||||
可以在[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss/mapping.py)查看当前支持的loss或添加新的loss,
|
||||
检查`<image>`等特殊token是否参与损失计算,可以在命令行日志中找一下打印的labels。
|
||||
训练agent时,tool_call就是应该算loss,tool_response不算loss。
|
||||
|
||||
### Q19: 模型参数freeze相关问题
|
||||
训练的过程中,冻结某些层时导致某些参数未参与梯度回传,请配置参数`--ddp_find_unused_parameters true`。
|
||||
freeze_parameters和freeze_vit/freeze_aligner/freeze_llm:先freeze parameters再active parameters。`freeze vit/freeze aligner/freeze llm`这三个参数会对freeze parameters 和trainable parameters进行调整.因为有些模型的ViT中包含`aligner`,所以会将`aligner`单独加入trainable_parameters。
|
||||
freeze_parameters_ratio这个参数的机制是从embedding开始从下往上freeze。
|
||||
|
||||
### Q20: 序列并行相关问题
|
||||
序列并行支持pt, sft, dpo and grpo。参考这个例子[sequence_parallel](https://github.com/modelscope/ms-swift/tree/main/examples/train/sequence_parallel)。
|
||||
VLM模型的目前仅支持flash-attn,纯文本支持flash-attn和sdpa。
|
||||
sequence parallel可以和Liger kernel同时使用。
|
||||
sequence parallel和自定义loss冲突时,由于sequence parallel在自己的代码中定制了loss,可以自己改下[这里](https://github.com/modelscope/ms-swift/blob/main/swift/trainers/sequence_parallel/ulysses.py)。
|
||||
|
||||
### Q21: 扩充词表
|
||||
用SWIFT框架扩充词表需要设置命令行参数`new_special_tokens`,`--modules_to_save embed_tokens lm_head`,详见[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/new_special_tokens)。
|
||||
|
||||
### Q22: tuners相关问题
|
||||
SWIFT中的LlamaPro对多模态做了适配。
|
||||
LongLoRA只有LLaMA系列模型能用。
|
||||
LoRA训练和`--trainable_parameters`参数不兼容,LoRA模块之外其他的可训练参数用modules_to_save。
|
||||
|
||||
### Q23: embedding/reranker训练
|
||||
[embedding训练例子](https://github.com/modelscope/ms-swift/blob/main/examples/train/embedding)。
|
||||
[reranker训练例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/reranker)。
|
||||
数据格式见[自定义数据集](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html)。
|
||||
|
||||
### Q24: 分类任务训练
|
||||
SWIFT支持多标签分类,自定义数据集文档有数据格式,在命令行参数文档中搜索`problem_type`,其他和回归是一样的。
|
||||
注意:label字段和message字段同级。
|
||||
|
||||
### Q25: thinking模型训练
|
||||
查看这个[issue](https://github.com/modelscope/ms-swift/issues/4030)。
|
||||
|
||||
### Q26: 想问一下,SWIFT支持蒸馏吗?
|
||||
参考这个[例子](https://github.com/modelscope/ms-swift/blob/main/examples/sampler/distill/distill.sh)。
|
||||
|
||||
### Q27: gkd训练student model和teacher model的model_type需要一致吗,一个dense一个moe可以吗?
|
||||
可以的,只需要词表一样,不过带MoE就会比较慢。
|
||||
|
||||
### Q28: GRPO训练相关问题
|
||||
SWIFT现在支持多模态的GRPO训练。GRPO训练过程中loss接近0是正常情况,参考[issue](https://github.com/huggingface/open-r1/issues/239#issuecomment-2646297851)。
|
||||
设置sleep_mode,推理结束VllmEngine释放显存。下次调用时,再加载,而不是一直占用。
|
||||
GRPO训练时不想引入KL项,可以通过命令行参数beta设置。
|
||||
LoRA微调后继续做GRPO训练,请在命令行参数文档中搜索`--adapters`。
|
||||
由于算entropy会有额外的一点开销,所以默认没有记录熵曲线。如果需要,请设置`--log_entropy true`,
|
||||
colocate模式不支持use_async_engine。
|
||||
GRPO不支持channel_loss。
|
||||
Liger kernel和padding free没法在GRPO阶段一起开。如果一起开,需要改liger grpo loss的实现,在liger kernel库中,不方便改。
|
||||
如果训练集有不同的task,请查看[多任务训练](https://swift.readthedocs.io/zh-cn/latest/Instruction/GRPO/DeveloperGuide/multi_task.html)。
|
||||
|
||||
### Q29: reward函数(模型)相关问题
|
||||
reward_model和reward_funcs可以一起使用。
|
||||
自定义reward函数参考[examples/train/grpo/plugin](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin)。
|
||||
针对math问题,要从数据集里面传solution,不然不好算accuracy。
|
||||
如果在ORM的自定义奖励函数中需要传入数据集中的某个列,请将该列放到messages之外的其他列。
|
||||
在GRPO训练的过程中如果需要指定一个llm-judge模型来做打分,请参考奖励模型的文档。
|
||||
|
||||
### Q30: rollout相关问题
|
||||
Rollout应该是不兼容pipeline parallel。
|
||||
vLLM推理引擎默认trust_rwmote_code为true。
|
||||
|
||||
### Q31: 请教一个问题,grpo脚本中的save_steps指的是step还是global step?目前本地训练显示的global step是18, wandb上显示的step是628。
|
||||
`global_step`,本地tqdm显示的。
|
||||
|
||||
### Q32: 默认只用 num_iterations=1 的话,clip 就失去作用了吧?dapo 的 clip higher 也没用。我看 veRL 有个 micro batch 可以设置单轮小批次更新 policy model 来使得 clip 项生效,ms-swift 的 mini batch 看源码貌似只是做了梯度累加?
|
||||
是的,需要num_iterations>1。
|
||||
|
||||
### Q33: 请问gspo训练支持传入参数top_entropy_quantile吗?传入了--importance_sampling_level sequence后,还能实现对熵分布前x%的token的优化吗?
|
||||
支持,顺序是先正常计算loss(受importance_sampling_level影响),再根据top_entropy_quantile mask掉loss。
|
||||
|
||||
### Q34: GRPO文档中的faq
|
||||
更多GRPO相关的FAQ,请查看[GRPO文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/GRPO/GetStarted/GRPO.html#faq)
|
||||
|
||||
### Q35: ppo等偏好训练相关问题
|
||||
PPO训练不支持梯度裁剪。
|
||||
目前PPO还只支持RM和policy是同一系列的模型(tokenizer/template)。
|
||||
不支持多轮的DPO。
|
||||
|
||||
### Q36: MoE模型训练相关问题
|
||||
MoE模型LoRA训练,如果aux-loss基本没变化,将all-router也加到target_modules。
|
||||
LoRA训练中,路由器模块是否参与训练看gate是否是nn.Linear实现,如果是nn.Parameter就不训练,详见命令行参数[target_parameters](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#tuner)。
|
||||
|
||||
### Q37: Megatron-SWIFT训练相关问题
|
||||
Checkpoint保存,参考命令行参数[save_strategy](https://swift.readthedocs.io/zh-cn/latest/Megatron-SWIFT/Command-line-parameters.html)。
|
||||
Megatron多机训练时,因为pp并行只有在pp last rank有完整的信息, 日志在last rank打印,而不是从master node打印。
|
||||
Megatron-SWIFT支持了save_total_limit,支持了SwanLab监控训练,详见[Megatron-SWIFT命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Megatron-SWIFT/Command-line-parameters.html)
|
||||
ViT用的是transformers的模型结构,目前没有并行,训练遇到OOM时降低`decoder_first_pipeline_num_layers`。
|
||||
Megatron-SWIFT支持新的模型,目前没有教程,请查看新增模型的PR。
|
||||
sequence_parallel的并行数等于tp数。
|
||||
FP8训练支持block wise,参考[examples/megatron/fp8例子](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/fp8)。
|
||||
|
||||
### Q38: 请问Megatron-SWIFT如何配置断点续训?
|
||||
配置`--mcore_model`加载checkpoint,另外根据需要配置这几个参数,`--finetune`,`--no_load_optim`,`--no_load_rng`。如果是LoRA断点续训,配置`--mcore_adapter`,其他同全参数训练,详见[Megatron-SWIFT命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Megatron-SWIFT/Command-line-parameters.html)。
|
||||
|
||||
### Q39: mtp相关问题
|
||||
需要MTP训练,请设置命令行参数`mtp_num_layers`。
|
||||
如果base模型不附带MTP结构,可以从头初始化训练MTP。
|
||||
多模态的MTP目前还没支持。
|
||||
|
||||
### Q40: 有个关于Megatron GKD的问题请教,如果teacher是Qwen3-235B,student是Qwen3-30BA3B,之前SFT 235B都是pp8然后decoder fist和decoder last设为11。如果我在GKD的时候也设置decoder first last,会不会影响student的并行?
|
||||
现在两个模型的并行参数是共用的,不同并行的设置会在v4版本后支持。
|
||||
|
||||
### Q41: 量化模型训练相关问题
|
||||
QLoRA微调参考[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/qlora)。
|
||||
量化模型不能全参数微调,GPTQ模型的int型参数无法参与求导,只能附着LoRA等额外结构参与更新。
|
||||
QLoRA训练后的模型merge参考[QLoRA例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/qlora)。
|
||||
Megatron-SWIFT不支持QLoRA训练。
|
||||
|
||||
### Q42: 一些特殊模型的训练
|
||||
SWIFT目前不支持MiniCPM-O使用音频模态输入的训练。
|
||||
微调DeepSeek-VL-2,transformers用4.42以前的版本,`peft==0.11.*`。
|
||||
Moonlight-16B-A3B-Instruct微调。因为模型文件中禁止了训练, 参考DeepSeek-VL-2的解决方案,issue中搜索。
|
||||
微调Ovis2这个模型有点特殊,需要padding到max_length。设置一下`--max_length`。
|
||||
Qwen2.5-Omni目前不支持talker训练,只有thinker。
|
||||
Qwen2-Audio的sft不支持packing。
|
||||
|
||||
### Q43: 请问在不支持flash attention的设备上attention implemation默认是什么呢?文档中默认是none
|
||||
默认使用sdpa。
|
||||
|
||||
### Q44: 请问默认模型训练都是left padding是吧?
|
||||
训练可以选择使用左padding还是右padding。默认是右padding, `batch infer`都是左padding。
|
||||
|
||||
### Q45: 请问下MoE的参数有哪些,参数表里关键字搜索不到?专家数量,专家路由这些参数怎么设置?
|
||||
直接用config.json中的参数。
|
||||
|
||||
### Q46: SWIFT能够支持设置最小的learning rate吗,感觉最后减到太小了
|
||||
可以设置,`--lr_scheduler_type cosine_with_min_lr --lr_scheduler_kwargs '{"min_lr": 1e-6}'`。
|
||||
|
||||
### Q47: 目前支持用yaml文件配置grpo和sft吗?
|
||||
都支持的,该配置是在main.py中直接处理成命令行。
|
||||
|
||||
### Q48: 请问现在是不支持use_liger_kernel和log_entropy一起用吗?
|
||||
不支持。
|
||||
|
||||
### Q49: 请问下,遇到这个报错,怎么处理?安装了apex也不行
|
||||
```text
|
||||
RuntimeError: ColumnParallelLinear was called with gradient_accumulation_fusion set to True but the custom CUDA extension fused_weight_gradient_mlp_cuda module is not found. To use gradient_accumulation_fusion you must install APEX with --cpp_ext and --cuda_ext. For example: pip install --global-option="--cpp_ext" --global-option="--cuda_ext ." Note that the extension requires CUDA>=11. Otherwise, you must turn off gradient accumulation fusion.
|
||||
```
|
||||
设置一下`--gradient_accumulation_fusion false`。
|
||||
|
||||
### Q50: 几个任务一起finetune vlm,不同任务视频采样规则不一致,ms-swift是否支持?在哪里配置?
|
||||
[命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)看下`interleave_prob`。
|
||||
|
||||
### Q51: 想问一个问题,多模态packing预训练每次pytorch allocator cache flushes since last step后,显存使用好像就会增长一点,步数多了容易oom
|
||||
加个环境变量`PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True'`。
|
||||
|
||||
### Q52: use_logits_to_keep 现在多模态大模型上可以用吗?
|
||||
如果多模态token的展开在模型的forward内会报错。
|
||||
|
||||
### Q53: 请问一下为什么训练到会有好几次显存大幅度增加,已经50step或者100step
|
||||
设置环境变量`PYTORCH_CUDA_ALLOC_CONF`,具体查看PyTorch文档。
|
||||
|
||||
### Q54: 从qwen base模型微调成chat模型有没有实践文档,有什么要特别配置的吗?
|
||||
`swift sft`,没有其他需要特别配置的,参考[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/base_to_chat)。
|
||||
|
||||
### Q55: 模型训练后,回复重复了很多内容
|
||||
参考[预训练与微调](https://swift.readthedocs.io/zh-cn/latest/Instruction/Pre-training-and-Fine-tuning.html)。如果训练过程中出现重复的情况,请多训练几个epoch, 清洗数据, 全参数训练, 采用RLHF的方式缓解。
|
||||
|
||||
### Q56: 请问为什么 --torch_dtype float16 (卡不能使用bf16)会出现报错:lib/python3.12/site-packages/torch/amp/grad_scaler.py", line 260, in _unscale_grads_ raise ValueError("Attempting to unscale FP16 gradients.") ValueError: Attempting to unscale FP16 gradients.
|
||||
全参数,不能fp16训练的。
|
||||
|
||||
### Q57: 请问下,lora参数合并报错,目前peft是0.11.0,这个是因为peft版本需要升级吗
|
||||
```text
|
||||
File "/opt/conda/lib/python3.9/site-packages/peft/config.py", line 118, in from_peft_type
|
||||
return config_cls(**kwargs)
|
||||
TypeError: __init__() got an unexpected keyword argument 'corda_config'
|
||||
```
|
||||
训练和合并的peft版本不一致导致的。
|
||||
|
||||
### Q58: 请问这个问题如何解决?safetensors_rust.SafetensorError: Error while deserializing header: HeaderTooLarge
|
||||
磁盘空间不足了,模型没有保存完整。
|
||||
|
||||
### Q59: 这个错误为什么会出现在这,numpy.object找不到在哪?
|
||||
`numpy==1.26.3`,尝试一下。
|
||||
|
||||
### Q60: unsloth训练,报错:assert(type(target modules) in (list,tuple,))。配置的参数是--target modules all-linear
|
||||
别用`all-linear`,改为具体的模块列表,比如`--target_modules q k v`。
|
||||
|
||||
### Q61: 请问对于qwen2.5-omni来说--freeze_vit false意味这视觉编码器和音频编码器都打开了,有什么办法可以只打开音频编码器不打开视觉编码器吗?
|
||||
`--target_regex`写一下。
|
||||
|
||||
## 推理
|
||||
|
||||
SWIFT支持python脚本、命令行、ui界面推理,详见[推理和部署](https://swift.readthedocs.io/zh-cn/latest/Instruction/Inference-and-deployment.html)。
|
||||
|
||||
### Q1:SWIFT推理如何设置模型?
|
||||
如果是全参数训练的模型、LoRA训练后合并的模型或者从model hub下载的模型,设置命令行参数`--model <model_id_or_path>`;LoRA训练后未合并的模型,用`--adapters`设置,同时可通过`--model`指定基模路径。
|
||||
|
||||
### Q2: SWIFT如何使用数据集进行推理?推理结果保存在哪儿?
|
||||
`--val_dataset <your-val-dataset>`,指定数据集。对于训练后的模型也可以设置参数`--load_data_args true`。推理结果保存路径通过`--result_path your_path`设置,日志中会打印路径。详见文档[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
如果需要保留推理数据集中额外的字段,请设置`--remove_unused_columns false`。
|
||||
|
||||
### Q3: SWIFT如何设置批量推理?
|
||||
如果infer_backend为`transformers`,设置命令行参数`--max_batch_size 16`,或[python脚本](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo.py)。这里max_batch_size指的是每张卡上的batch_size。
|
||||
|
||||
### Q4: SWIFT如何设置流式推理?
|
||||
`--stream true`,此时推理结果将逐条写入jsonl文件。需要注意的是,流式推理不支持ddp。
|
||||
|
||||
### Q5: vLLM和SGLang推理后端相关的问题
|
||||
对于LoRA训练的模型,请查看vLLM和SGLang文档,如果支持LoRA推理则不需要合并。此外,SGLang推理目前不支持多模态。
|
||||
|
||||
### Q6: 生成参数相关的问题
|
||||
temperature等参数默认从generation_config.json中读取。设置`--temperature 0`或者`--top_k 1`可以取消推理随机性。
|
||||
|
||||
### Q7: 如何将system_prompt置空?命令行不设置system参数,但是它会加上默认的system。
|
||||
设置`--system ''`。
|
||||
|
||||
### Q8: 推理时如何计算acc/rouge等指标?
|
||||
参考[推理参数metric](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id14)。
|
||||
|
||||
### Q9: 模型推理的时候如果需要在特定前缀下继续推理的话是设置哪个参数?
|
||||
参数`--response_prefix`。
|
||||
|
||||
### Q10: 数据answer里面已经包含了部分prompt,希望补全answer,应该怎么修改inference?
|
||||
```text
|
||||
{"messages": [{"role": "system", "content": "<system>"}, {"role": "user", "content": "<query1>"}, {"role": "assistant", "content": "answer1, "}]}
|
||||
```
|
||||
参考[examples/infer/demo_agent](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_agent.py)。
|
||||
|
||||
### Q11: 多模态模型推理时如何限制最大像素,以减少显存占用?
|
||||
设置命令行参数`--max_pixels xxx`、环境变量`MAX_PIXELS=xxx`、或特定模型参数`--model_kwargs '{"max_pixels": xxx}'`,其中环境变量仅对文档中对应的模型生效,详见文档[特定模型参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id18)。
|
||||
|
||||
### Q12: SWIFT推理如何输出概率值logprobs参数?
|
||||
命令行推理设置`--logprobs true`,python脚本推理设置`request_config = RequestConfig(..., logprobs=True, top_logprobs=2)`,参考[test_logprobs.py](https://github.com/modelscope/ms-swift/blob/main/tests/infer/test_logprobs.py)。
|
||||
|
||||
### Q13: SWIFT推理如何输出last_hidden_state?
|
||||
没有例子,可以参考GRPO trainer的`_get_last_hidden_state`方法。
|
||||
|
||||
### Q14: transformers,vllm,ollama等推理结果不一致问题
|
||||
SWIFT的template是对齐transformers的。检查推理参数是否对其。此外,VllmEngine和TransformersEngine是有差异的。
|
||||
|
||||
### Q15: embedding/reranker模型推理
|
||||
embedding模型推理参考这里的[例子](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_embedding.py)。reranker模型推理参考这里的[例子](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_reranker.py)。
|
||||
|
||||
### Q16: 请问在使用python脚本推理时,如何使用cpu?
|
||||
设置环境变量,`os.environ['CUDA_VISIBLE_DEVICES'] = '-1'`。
|
||||
|
||||
### Q17: 使用swift infer命令进行推理,支持多机推理吗?
|
||||
如果单节点放得下模型,外面封装k8s就行。如果单节点放不下那就不支持。
|
||||
|
||||
### Q18: swift sample的时候,好像不支持batch?好像是for循环一个个例子sample,有点慢
|
||||
有一个[脚本](https://github.com/modelscope/ms-swift/blob/main/examples/train/rft/rft.py),可以用多进程对数据集拆分采样。
|
||||
|
||||
### Q19: 特殊模型依赖版本相关问题
|
||||
Qwen2-Audio推理结果出现混乱,请使用transformers4.48。
|
||||
transformers4.55.2训练的LoRA不能使用小于4.52的版本加载了,详见[issue#5440](https://github.com/modelscope/ms-swift/issues/5440)。
|
||||
swift对不同版本的qwen-vl-utils做了兼容,使用qwen2.5-vl和qwen3-vl模型时不需要切换该依赖版本。
|
||||
|
||||
### Q20: 报错,safetensors_rust.SafetensorError: Error while deserializing header:MetadataIncompleteBuffer
|
||||
模型权重损坏了。
|
||||
|
||||
### Q21: vLLM 报错 `ValueError: the decoder prompt contains a(n) video item with length 16758, which exceeds the pre-allocated encoder cache size 16384. please reduce the input size or increase the encoder cache size by setting --limit-mm-per-prompt at startup.`
|
||||
这通常是多模态输入过长,超过了 vLLM 预分配的 encoder cache size 导致的。可以通过 `--limit-mm-per-prompt` 调整 encoder cache size。另一个可行的解决方法是增大 `max_num_batched_tokens`,在 Swift cli 中传入:
|
||||
```shell
|
||||
--vllm_engine_kwargs '{"max_num_batched_tokens": 20000}'
|
||||
```
|
||||
|
||||
## 导出
|
||||
|
||||
### Q1: autoawq相关的报错
|
||||
如果推理没有涉及AWQ量化模型,但出现了autoawq相关的报错,可以尝试卸载autoawq再进行推理。不支持AWQ量化的模型,尝试用GPTQ进行量化。
|
||||
|
||||
### Q2: SWIFT量化模型时,一张卡上放不下模型的情况
|
||||
尝试设置`--device_map cpu`。或者多卡加载模型,单卡量化。
|
||||
|
||||
### Q3: 想问一下用swift export对qwen2.5 72B模型进行gptq int4量化,max model length=32768用的是默认值,给的校准数据集有128个样本,但是量化的时候报错了,报错日志是:factorization could not be completed because the input is not positive-definite(the leading minor of order 18145 is not pisitive-definite)。是什么原因?
|
||||
海森矩阵不正定的问题,试试其他的数据集。
|
||||
|
||||
### Q4: swift export的时候传入自定义的template_type,是不是就可以永久改掉template_type了?如果swift export --template_type 自定义,是不是就可以把模型对应的template改掉
|
||||
不会被修改,swift中的template是定义在swift内部的,不是以jinja方式保存的。
|
||||
|
||||
### Q5: 模型训练完能直接转gguf格式吗?
|
||||
目前只支持导出ModelFile,详见文档[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
|
||||
## 部署
|
||||
|
||||
### Q1: SWIFT部署如何设置模型?
|
||||
同上面推理Q1。
|
||||
|
||||
### Q2: SWIFT如何进行多卡部署?
|
||||
详见[例子](https://github.com/modelscope/ms-swift/tree/main/examples/deploy)。如果是transformers engine,不支持DDP,不能多卡部署。此外,不支持异构部署,如不同型号的显卡、各显卡设置不同的存储占比等。
|
||||
|
||||
### Q3: 通过--system参数指定system prompt与数据集中每个数据前加system prompt以及template的system prompt是不是有一个就行?这些方式对模型来说,是不是一样的?
|
||||
system优先级:数据集中的>命令行的>template中默认的。
|
||||
|
||||
### Q4: 客户端多模态输入相关问题
|
||||
客户端传入图片、音频等,见[客户端例子](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/client/mllm)。
|
||||
如果图片url非法,可以设置请求的超时时间,环境变量`SWIFT_TIMEOUT`,或者`InferClient`中可以传参数。
|
||||
|
||||
### Q5: 生成参数设置相关问题
|
||||
temperature等参数推理只能启动前设置,部署可以在启动时设置默认值,之后在客户端继续设置,覆盖默认值。
|
||||
|
||||
### Q6: SWIFT部署的模型怎么设置流式生成?
|
||||
客户端控制的,查看[examples/deploy/client](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/client)。
|
||||
|
||||
### Q7: SWIFT部署如何输出token的概率?
|
||||
服务端设置`--logprobs true`,要客户端传参数,`request_config = RequestConfig(..., logprobs=True, top_logprobs=2)`。
|
||||
|
||||
### Q8: 部署模型时,thinking相关问题
|
||||
如果需要禁止思考,目前只能在swift deploy启动的时候禁止thinking。查看这个[issue](https://github.com/modelscope/ms-swift/issues/4030)。
|
||||
|
||||
### Q9: 部署时,设置什么参数可以实现一次输出多个结果?
|
||||
`RequestConfig`参数`n`。
|
||||
|
||||
### Q10: SWIFT部署,指定--infer_backend vllm,和直接使用vllm部署相关问题
|
||||
如果两者推理结果相差较多,可能是template没对齐。如果推理速度相差较多,可能是图像分辨率不一致。swift默认使用V1 engine,可以通过环境变量`VLLM_USE_V1=1`控制。
|
||||
|
||||
### Q11: 特殊模型和依赖版本相关问题
|
||||
如果遇到报错没有“model.language_model.embed_tokens.weight”,训练前后的transformers版本不一致。
|
||||
qwen2.5使用fp16推理如果遇到返回乱码,尝试bf16。
|
||||
|
||||
### Q12: 有个问题想问一下,qwen2-7b部署后使用客户端时,调用openai的api要使用client.completions.create,不能使用client.chat.completions.create,但是使用qwen2-7b-instruct-q5_k_m.gguf的时候可以使用client.chat.completions.create,这是为什么呀?
|
||||
base模型可以用client.chat.completions.create的,不过这个是兼容行为。
|
||||
|
||||
## 评测
|
||||
|
||||
### Q1: SWIFT支持的评测集有哪些?以及如何使用自定义评测集?
|
||||
标准评测集和用户自定义评测集的使用详见文档[评测](https://swift.readthedocs.io/zh-cn/latest/Instruction/Evaluation.html)。
|
||||
|
||||
### Q2: 官方支持的评测数据集手动下载后,swift eval能配置本地路径评测吗?
|
||||
离线评测请参考EvalScope文档[快速上手](https://evalscope.readthedocs.io/zh-cn/latest/get_started/basic_usage.html)
|
||||
|
||||
### Q3: eval微调后的模型,总是会在固定的百分比停掉,但是vllm服务看着一直是有在正常运行的。模型越大,断开的越早。
|
||||
`SWIFT_TIMEOUT`环境变量设置为-1。
|
||||
|
||||
### Q4: 评估的时候可不可以控制数据集条数?评估一个mmlu需要一个多小时,也太慢了。
|
||||
配置参数`--eval_limit`,这里的`--eval_limit`是控制了每个subset的条数,比如mmlu有50多个subset,每个limit10条,那就是500多条。
|
||||
|
||||
### Q5: 问一下评估swift eval里,模型最多生成1024token就结束了,这个如何修改?设置--max_new_tokens 5000,看起来没起作用
|
||||
查看命令行参数[eval_generation_config](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id16)
|
||||
|
||||
### Q6: 请教一下,想使用OpenCompass的后端评测,如何从本地加载下载好的数据集?
|
||||
OpenCompass后端不支持设置`data_args`。
|
||||
|
||||
### Q7: swift eval 来评估模型,--eval_backend OpenCompass不支持自定义数据集吗?
|
||||
```text
|
||||
ValueError: eval_dataset: /mnt/workspace/data.jsonl is not supported.
|
||||
eval_backend: OpenCompass supported datasets: ['C3', 'summedits', 'WiC', 'csl', 'lambada', 'mbpp', 'hellaswag', 'ARC_e', 'math', 'nq', 'race', 'MultiRC', 'cmb', 'ceval', 'GaokaoBench', 'mmlu', 'winogrande', 'tnews', 'triviaqa', 'CB', 'cluewsc', 'humaneval', 'AX_g', 'DRCD', 'RTE', 'ocnli_fc', 'gsm8k', 'obqa', 'ReCoRD', 'Xsum', 'ocnli', 'WSC', 'siqa', 'agieval', 'piqa', 'cmnli', 'cmmlu', 'eprstmt', 'storycloze', 'AX_b', 'afqmc', 'strategyqa', 'bustm', 'BoolQ', 'COPA', 'ARC_c', 'PMMEval', 'chid', 'CMRC', 'lcsts']
|
||||
```
|
||||
OpenCompass不支持自定义数据集,用native可以自定义模式。
|
||||
|
||||
### Q8: evalscope原生是可以生成报告的,其他后端如opencompass是不支持生成报告可视化是吗?
|
||||
目前只支持native的可视化,其他后端还不支持。
|
||||
|
||||
### Q9: 请问一下评测ifeval报这个错是什么原因?
|
||||
```text
|
||||
[Errno 20] Not a directory: '/root/nltk_data/tokenizers/punkt_tab.zip/punkt_tab/english/collocations.tab'
|
||||
```
|
||||
解压这个文件,`unzip /path/to/nltk_data/tokenizers/punkt_tab.zip`。
|
||||
|
||||
### Q10: 请问评测时eval_backend='OpenCompass',怎么指定离线数据集路径?
|
||||
查看[数据准备教程](https://evalscope.readthedocs.io/zh-cn/latest/user_guides/backend/opencompass_backend.html#id3),下载数据集并解压。不用指定`dataset-args`,将数据集文件夹(即data文件夹)放置在当前工作路径下即可。
|
||||
|
||||
### Q11: 用evalscope报这个错是什么原因?
|
||||
```text
|
||||
unzip: cannot find or open /root/nltk_data/tokenizers/punkt_tab.zip, /root/nltk_data/tokenizers/punkt_tab.zip.zip or /root/nltk_data/tokenizers/punkt_tab.zip.ZIP
|
||||
```
|
||||
这是在下载nltk的依赖,手动下载[punkt_tab.zip](https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/open_data/nltk_data/punkt_tab.zip),解压到`~/nltk_data/tokenizers`下面。
|
||||
|
||||
### Q12: 为啥纯文本没问题,测多模态我们指定路径了,但他还是检测不到数据集,会去下载?
|
||||
VLMEvalKit流程跟native不一样,会自己下载数据放到`~/LMUData/`下面,详见[文档](https://evalscope.readthedocs.io/zh-cn/latest/user_guides/backend/vlmevalkit_backend.html#id2)。
|
||||
|
||||
### Q13: 请问一下swift eval做benchmark评测的时候,是否可以指定llm作为judge, 参数应该怎么传进去?
|
||||
支持,使用swift得从`extra_eval_args`去传递`judge-model-args`参数,包括`api_key,api_url,model_id`,整体是一个json字符串。
|
||||
|
||||
### Q14: 请问在执行eval的时候出现了多卡显存分配不均是什么原因?
|
||||
```shell
|
||||
NPROC_PER_NODE=8
|
||||
ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7\ MAX_PIXELS=802816\ swift eval\
|
||||
--model "$MODEL_PATH” \$EXTRA_ARGS \
|
||||
--eval_backend Native \ --infer_backend transformers\ --device_map auto \
|
||||
--eval_limit"$EVAL_LIMIT"\ --eval_dataset general_qa\
|
||||
--dataset_args "{\"general_qa\": {\"local_path\": \"${DATA_PATH}\", \"subset_list\": [\"${SUBSET_NAME}\"]}}" \ --host 127.0.0.1\> "$LOG_FILE" 2>&1
|
||||
```
|
||||
swift eval不支持DDP方式启动。
|
||||
|
||||
### Q15: 请问哪里可以看到swift评测的时候送入的query除了问题之外还有哪些额外的字段呢?
|
||||
最简单的方法是看输出的reviews文件中的input字段,是输入给模型的内容转换后的Markdown格式。如果用backend是opencompass的话没有这些,需要用native backend。
|
||||
|
||||
ms-swift的eval能力使用了魔搭社区评测框架EvalScope, 复杂能力请直接使用[EvalScope框架](https://evalscope.readthedocs.io/zh-cn/latest/get_started/introduction.html)。
|
||||
@@ -0,0 +1,63 @@
|
||||
# On-Policy RL Meets Off-Policy Experts: Harmonizing SFT and RL via Dynamic Weighting (CHORD)
|
||||
|
||||
本文档介绍论文 [On-Policy RL Meets Off-Policy Experts: Harmonizing SFT and RL via Dynamic Weighting](https://arxiv.org/abs/2508.11408) 中提出的 CHORD 算法。CHORD 的核心思想是在强化学习过程中,动态融合专家数据(SFT),通过 全局权重 μ + token 级别权重 φ 的双重控制机制,在模仿与探索之间实现平衡。
|
||||
|
||||
## 算法概述
|
||||
CHORD 算法通过在 GRPO loss 中引入 **SFT loss**,实现动态混合训练。总体目标函数为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{CHORD}} = (1 - \mu) \cdot \mathcal{L}_{\text{GRPO}} + \mu \cdot \mathcal{L}_{\text{SFT}}
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $\mathcal{L}_{\text{GRPO}}$:基于 on-policy 采样的强化学习损失(类似 PPO)。
|
||||
- $\mathcal{L}_{\text{SFT}}$:监督微调损失。
|
||||
- $\mu \in [0, 1]$:全局平衡系数,控制 SFT 信号在总梯度中的贡献。
|
||||
|
||||
### 参数配置(数据与批量大小)
|
||||
我们可以基于 GRPO 训练实现 CHORD 训练。
|
||||
|
||||
CHORD 需要在训练时指定额外的 SFT 数据集和批量大小:
|
||||
- `chord_sft_dataset`: 用于提供专家数据的 SFT 数据集。
|
||||
- `chord_sft_per_device_train_batch_size`: 每个设备的 SFT mini-batch 大小。
|
||||
|
||||
---
|
||||
|
||||
## 两种 CHORD 变体
|
||||
|
||||
论文提出了两种算法变体:**CHORD-µ** 和 **CHORD-ϕ**。
|
||||
|
||||
### CHORD-µ
|
||||
通过在训练过程中逐步 **衰减 μ**,实现从模仿专家到自主探索的过渡。
|
||||
|
||||
**参数:**
|
||||
- `chord_mu_peak`:μ 的峰值。
|
||||
- `chord_mu_valley` μ 的衰减终值。
|
||||
- `chord_mu_warmup_steps` μ 值上升至峰值的训练步数。
|
||||
- `chord_mu_decay_steps` μ 从峰值衰减到谷值的训练步数。
|
||||
|
||||
### CHORD-ϕ(Token 级加权)
|
||||
**CHORD-ϕ** 通过 **token-wise 权重函数 φ** 动态控制每个专家 token 的梯度贡献。
|
||||
|
||||
**φ 定义:**
|
||||
$$
|
||||
\phi(y_t^\star, \pi_\theta) = p_t \cdot (1 - p_t)
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $p_t = \pi_\theta(y_t^\star \mid x, y_{<t}^\star)$:模型当前预测专家 token 的概率。
|
||||
- 当 $p_t ≈ 0.5$(模型不确定时),φ 取最大值 → 强化学习不确定的 token。
|
||||
- 当 $p_t ≈ 0$ 或 $p_t ≈ 1$,φ → 0 → 避免对过于确定或完全不会的 token 过度学习。
|
||||
|
||||
**开启 φ 加权的参数**:
|
||||
- `chord_enable_phi_function: bool = False`
|
||||
- 设置为 `True` 即启用 token-wise 权重 φ。
|
||||
|
||||
注:如果使用常数 μ 值 ,设置 chord_mu_peak 与 chord_mu_valley 相同
|
||||
|
||||
<details>
|
||||
<summary>mu值衰减与loss计算代码实现</summary>
|
||||
请参考`GRPOTrainer`的`_compute_chord_loss`方法:
|
||||
</details>
|
||||
|
||||
训练参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/chord.sh)
|
||||
@@ -0,0 +1,61 @@
|
||||
# Clipped Importance Sampling Policy Optimization (CISPO)
|
||||
|
||||
|
||||
Clipped Importance Sampling Policy Optimization (CISPO) 是 [MiniMax-M1](https://arxiv.org/abs/2506.13585) 论文中提出的一种强化学习算法。相比GRPO(Group Relative Policy Optimization)算法,CISPO 对重要性采样权重(importance sampling weights)本身进行裁剪。
|
||||
|
||||
## 算法原理
|
||||
为便于理解,我们基于 GRPO 算法进行对比说明。
|
||||
|
||||
GRPO通过裁剪策略比率来限制策略更新幅度,其损失函数为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{GRPO}}(\theta) = -\mathbb{E}\left[\min\left(r_t(\theta) \cdot \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \cdot \hat{A}_t\right)\right]
|
||||
$$
|
||||
|
||||
其中 $r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)}$ 是重要性采样比。
|
||||
|
||||
在处理长推理链条时,这种裁剪方式可能导致以下问题:
|
||||
|
||||
**关键 Token 的梯度被抑制**:在复杂推理任务中,某些关键的低概率 token(如 *However, Recheck, Wait, Aha*)对于触发深度思考和推理纠错至关重要。这些 token 在旧策略 $\pi_{\theta_{\text{old}}}$ 中概率较低,当新策略试图提高其概率时,会导致较大的策略比率 $r_t(\theta)$,GRPO 的裁剪机制会将这些 token 丢弃。
|
||||
|
||||
|
||||
### CISPO 的解决方案
|
||||
|
||||
CISPO 的核心思想是:裁剪重要性采样权重,保留梯度更新。具体来说,CISPO 的损失函数为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{CISPO}}(\theta) = -\mathbb{E}\left[\text{detach}\left(\min(r_t(\theta), \epsilon_{\text{high}})\right) \cdot \hat{A}_t \cdot \log \pi_\theta(a_t|s_t)\right]
|
||||
$$
|
||||
|
||||
其中 $r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)}$ 是重要性采样比。
|
||||
|
||||
**关键机制**:
|
||||
- 对重要性采样权重进行裁剪:$\min(r_t(\theta), \epsilon_{\text{high}})$
|
||||
- **detach 操作**:裁剪后的权重不参与梯度计算,作为常数系数
|
||||
- 梯度来自 $\log \pi_\theta(a_t|s_t)$ 项,保证所有 token 都有梯度贡献
|
||||
|
||||
|
||||
## 实现细节
|
||||
CISPO 的伪代码实现如下:
|
||||
|
||||
```python
|
||||
log_ratio = per_token_logps - old_per_token_logps
|
||||
importance_weights = torch.exp(log_ratio) # r_t(θ) = π_θ / π_θ_old
|
||||
|
||||
clamped_ratios = torch.clamp(importance_weights, max=epsilon_high).detach()
|
||||
|
||||
per_token_loss = -clamped_ratios * advantages.unsqueeze(1) * per_token_logps
|
||||
```
|
||||
|
||||
## 参数设置
|
||||
|
||||
我们可以基于 `GRPOTrainer`,通过设置以下参数实现 CISPO 训练:
|
||||
|
||||
```bash
|
||||
--loss_type cispo
|
||||
--epsilon_high 5.0
|
||||
```
|
||||
|
||||
> 相比其他算法, cispo 的 epsilon_high 一般取值较大,minimax论文中未给出具体的参数设置,这里的值参考论文[ScaleRL](https://arxiv.org/pdf/2510.13786)的实验设置
|
||||
|
||||
其他训练参数参考 [GRPO参数文档](../../Command-line-parameters.md#grpo参数)
|
||||
@@ -0,0 +1,102 @@
|
||||
# DAPO: An Open-Source LLM Reinforcement Learning System at Scale
|
||||
|
||||
|
||||
|
||||
[Decoupled Clip and Dynamic sAmpling Policy Optimization (DAPO)](https://arxiv.org/abs/2503.14476)在GRPO的基础上设置了几种trick,分别是
|
||||
- [Clip Higher](#clip-higher)
|
||||
- [Dynamic Sampling](#dynamic-sampling)
|
||||
- [Token level Loss](#token-level-loss)
|
||||
- [Overlong Filtering](#overlong-filtering)
|
||||
- [Soft Overlong Punishment](#soft-overlong-punishment)
|
||||
|
||||
## Clip Higher
|
||||
PPO和GRPO使用对称裁剪范围(如±0.2)限制策略更新幅度,虽然保证了稳定性,但也制约了模型的探索能力。特别是当某些token在旧策略中概率极低时,即使当前梯度显示其应被强化(A>0),最大增幅也被严格限制。
|
||||
|
||||
DAPO使用非对称裁剪范围, 提高上裁剪范围来鼓励模型进行探索:
|
||||
|
||||
- 上界(鼓励侧)放宽至0.28
|
||||
- 下界(抑制侧)保持0.2不变
|
||||
|
||||
GRPO中,默认使用`epsilon`设置用对称裁剪范围
|
||||
|
||||
使用参数
|
||||
|
||||
- `epsilon_high` 设置上裁剪范围,此时参数`epsilon` 为下裁剪范围
|
||||
|
||||
## Dynamic Sampling
|
||||
GRPO对每个问题采样多个回答计算组间优势,
|
||||
|
||||
$$
|
||||
\hat{A}_{i,t} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)}
|
||||
$$
|
||||
而当生成的所有输出{oi}获得相同奖励时,组间优势等于0,会出现梯度消失导致训练效率下降
|
||||
|
||||
DAPO引入动态采样策略解决这一问题:
|
||||
|
||||
- 采样阶段跳过组间奖励标准差为0的数据
|
||||
- 持续生成样本直到填满批次
|
||||
|
||||
|
||||
使用参数
|
||||
|
||||
- `dynamic_sample true` 来开启动态采样
|
||||
- `max_resample_times` 设置最多重采样次数
|
||||
|
||||
## Token level Loss
|
||||
GRPO 在归一化损失时采用句子级归一化,这会导致损失计算具有长度偏差。
|
||||
|
||||
DAPO 使用token级归一化,避免了回答长度在损失计算上的偏差。
|
||||
|
||||
使用参数
|
||||
|
||||
- loss_type bnpo/dapo 来使用token级归一化
|
||||
|
||||
> loss_type 计算公式可参考[文档](../DeveloperGuide/loss_types.md)
|
||||
|
||||
## Overlong Filtering
|
||||
DAPO 认为被强制截断的回复的奖励噪声较大,可能会导致模型难以区分质量问题和长度问题。为此,DAPO 筛除了训练中被截断的数据,使其不参与损失计算。
|
||||
|
||||
使用参数
|
||||
|
||||
- overlong_filter 开启对超长样本的过滤
|
||||
|
||||
|
||||
## Soft Overlong Punishment
|
||||
语言模型常面临生成长度控制难题:
|
||||
|
||||
- 过长输出可能被截断,导致正确内容被误判
|
||||
|
||||
- 无约束生成长度影响实用性和计算效率
|
||||
|
||||
DAPO 设计了三段式长度惩罚函数:
|
||||
|
||||
$$
|
||||
R_{\text{length}}(L) =
|
||||
\begin{cases}
|
||||
0, & L \leq L_{\text{max}} - L_{\text{cache}} \\[10pt]
|
||||
\dfrac{(L_{\text{max}} - L_{\text{cache}}) - L}{L_{\text{cache}}}, & L_{\text{max}} - L_{\text{cache}} < L \leq L_{\text{max}} \\[10pt]
|
||||
-1, & L > L_{\text{max}}
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
在长度位于 $(L_{\text{max}} - L_{\text{cache}} < L \leq L_{\text{max}})$ 区间时设置线性递增惩罚,在 $(L > L_{\text{max}})$ 时设置最大惩罚(-1)
|
||||
|
||||
|
||||
使用参数
|
||||
|
||||
- `reward_funcs soft_overlong` 启用该奖励函数
|
||||
- `soft_max_length` 设置L_max,默认等于为模型的最长输出长度(max_completion_length)。
|
||||
- `soft_cache_length`: 设置L_cache
|
||||
|
||||
## 参数设置
|
||||
综上所述,我们可以基于GRPOTrainer,设置以下参数实现 DAPO 训练。
|
||||
|
||||
| 参数 | 类型 | 值 |
|
||||
|----------------------|-----------|-------------|
|
||||
| `--loss_type` | `str` | `bnpo`/`dapo`|
|
||||
| `--epsilon_high` | `float` | `0.28` |
|
||||
| `--dynamic_sample` | `bool` | `true` |
|
||||
| `--max_resample_times` | `int` | `3` |
|
||||
| `--overlong_filter` | `bool` | `true` |
|
||||
| `--reward_funcs` | `str` | `soft_overlong`|
|
||||
| `--soft_cache_length` | `int` | `4096`|
|
||||
@@ -0,0 +1,54 @@
|
||||
# FIPO: Future-KL Influenced Policy Optimization
|
||||
|
||||
作者: [li2zhi](https://github.com/li2zhi)
|
||||
|
||||
[FIPO](https://arxiv.org/abs/2603.19835) 是一种面向长链推理的 value-free RL 方法。它保留 GRPO/DAPO 的整体训练框架,但改变 token 级策略更新的加权方式:不再让一个序列级 advantage 均匀作用到所有 token,而是用折扣累积的 Future-KL 信号判断“从当前 token 开始的后续轨迹”整体是在被增强还是被削弱。
|
||||
|
||||
## 核心思想
|
||||
|
||||
GRPO/DAPO 中,每个 response 的 token 通常共享同一个序列级 advantage:
|
||||
|
||||
$$
|
||||
\hat{A}_{i,t} = \hat{A}_{i}
|
||||
$$
|
||||
|
||||
这种做法稳定且简单,但 credit assignment 粒度较粗。FIPO 引入当前策略与旧策略在每个 token 上的 log-prob shift:
|
||||
|
||||
$$
|
||||
\Delta \log p_t = \log \pi_\theta(y_t \mid x, y_{<t}) -
|
||||
\log \pi_{\mathrm{old}}(y_t \mid x, y_{<t})
|
||||
$$
|
||||
|
||||
如果 $\Delta \log p_t > 0$,说明当前训练正在提高该 token 的概率;如果小于 0,则说明该 token 正在被压低。FIPO进一步从当前位置向后折扣累积该信号:
|
||||
|
||||
$$
|
||||
\mathrm{FutureKL}_t =
|
||||
\sum_{k=t}^{T}\gamma^{k-t} M_k \Delta \log p_k
|
||||
$$
|
||||
|
||||
其中 $M_k$ 是 completion mask,$\gamma = 2^{-1 / \text{decay\_rate}}$。`decay_rate` 越大,越远的 future token 对当前位置的影响越强;`decay_rate` 越小,Future-KL 越偏局部。然后将 Future-KL 映射为 influence weight:
|
||||
|
||||
$$
|
||||
f_t = \mathrm{clip}(\exp(\mathrm{FutureKL}_t), 1-\epsilon_f, 1+\epsilon_f)
|
||||
$$
|
||||
|
||||
最终把原本的 advantage 改成 future-aware advantage:
|
||||
|
||||
$$
|
||||
\tilde{A}_{i,t} = \hat{A}_{i} \cdot f_{i,t}
|
||||
$$
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|---------------------------|---------|--------|----------------------------------------------------------------------------------------------------------------|
|
||||
| `--loss_type` | `str` | `grpo` | 设置为`fipo` 启用 FIPO loss |
|
||||
| `--delta` | `float` | `None` | 启用后会同时用于 Future-KL 高 IS ratio token 过滤和主 loss 的 dual-clip 上限,应大于 `1 + epsilon_high`,对齐FIPO 32B训练脚本建议设置为 `10.0` |
|
||||
| `--fipo_decay_rate` | `float` | `32.0` | Future-KL 折扣半衰参数,实际折扣为`2 ** (-1 / fipo_decay_rate)` |
|
||||
| `--fipo_clip_range` | `float` | `0.2` | influence weight 裁剪范围;`0.2` 表示默认裁剪到 `[0.8, 1.2]` |
|
||||
| `--fipo_clip_high_only` | `bool` | `true` | 若为`true`,权重只裁剪到 `[1.0, 1.0 + fipo_clip_range]`,更偏向放大正 Future-KL |
|
||||
| `--fipo_safety_threshold` | `float` | `4.0` | 负 advantage 且 IS ratio 超过该阈值时,将 FIPO 权重限制到 `[0.8, 1.0]` 以避免过度惩罚 |
|
||||
|
||||
## 训练示例
|
||||
|
||||
[swift](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/fipo.sh)
|
||||
@@ -0,0 +1,64 @@
|
||||
# Group Sequence Policy Optimization
|
||||
|
||||
[Group Sequence Policy Optimization](https://arxiv.org/abs/2507.18071)中指出GRPO在计算重要性采样权重时,是在token级别进行操作的。然而,这种做法由于每个token仅采样一次,无法实现有效的分布校正,反而会在模型训练过程中引入高方差噪声,极易导致模型的梯度估计不稳定,最终造成模型训练的崩塌。因此,论文认为,优化目标的单位应该与奖励的单位保持一致。由于奖励通常是在序列级别(即完整生成的回复)给出的,因此更合理的做法是将 off-policy 校正和优化也提升到序列级别,而非 token 级别。以下是三种计算策略对比:
|
||||
|
||||
1. GRPO
|
||||
对每个 token 独立计算重要性采样比,具体公式为
|
||||
|
||||
$$
|
||||
w^{\mathrm{GRPO}}_{i,t} = \frac{\pi_\theta (y_{i, t} \mid x, y_{i, <t})}{\pi_{\theta_{\mathrm{old}}} (y_{i, t} \mid x, y_{i, <t})}
|
||||
$$
|
||||
|
||||
2. GSPO (Sequence-Level)
|
||||
|
||||
在序列级别上计算重要性采样比,具体公式为
|
||||
|
||||
$$
|
||||
w^{\mathrm{GSPO}}_{i} = \left[ \frac{\pi_\theta (y_i \mid x)}{\pi_{\theta_{\mathrm{old}}} (y_i \mid x)} \right]^{\frac{1}{|y_i|}}
|
||||
= \exp\left( \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \log \frac{\pi_\theta (y_{i, t} \mid x, y_{i, <t})}{\pi_{\theta_{\mathrm{old}}} (y_{i, t} \mid x, y_{i, <t})} \right)
|
||||
$$
|
||||
|
||||
3. GSPO-token
|
||||
GSPO-token 结合了序列级与 token 级的重要性采样思想
|
||||
|
||||
$$
|
||||
w_{i, t}^{\mathrm{GSPO-token}} = \mathrm{sg}\left[w_i^{\mathrm{GSPO}}\right] \cdot \frac{\pi_{\theta}(y_{i, t} \mid x, y_{i, < t})}{\mathrm{sg}\left[\pi_{\theta}(y_{i, t} \mid x, y_{i, < t})\right]}
|
||||
$$
|
||||
|
||||
其中,$(\mathrm{sg}[\cdot])$ 表示梯度截断(detach())。
|
||||
|
||||
> 注意:根据梯度推导(即论文中的公式(11)和(18)),当各 token 的 advantage 相同时,GSPO-token 与 GSPO 等价。当前的 GRPO 实现中,所有 token 的 advantage 实际上都是基于句子级 reward 并在 group 内进行归一化,因此在这种设置下,GSPO-token 和 GSPO 在理论上是等价的。不过,GSPO-token 为未来更细粒度(token 级别)的 advantage 提供了支持。
|
||||
|
||||
伪代码实现
|
||||
```python
|
||||
log_ratio = per_token_logps - old_per_token_logps
|
||||
# GRPO
|
||||
log_importance_weights = log_ratio
|
||||
|
||||
# GSPO (Sequence-Level)
|
||||
seq_weight = (log_ratio * mask).sum(-1) / mask.sum(-1)
|
||||
log_importance_weights = seq_weight.unsqueeze(-1) # (B,1)
|
||||
|
||||
# GSPO-token
|
||||
seq_weight = (log_ratio * mask).sum(-1) / mask.sum(-1)
|
||||
log_importance_weights = seq_weight.detach().unsqueeze(-1) + (per_token_logps - per_token_logps.detach())
|
||||
|
||||
importance_weights = torch.exp(log_importance_weights)
|
||||
```
|
||||
|
||||
我们可以在 GRPO 训练的基础上,通过参数 `--importance_sampling_level` 选择不同的算法:
|
||||
|
||||
- `importance_sampling_level token` (默认,GRPO 实现)
|
||||
- `importance_sampling_level sequence` (GSPO)
|
||||
- `importance_sampling_level sequence_token` (GSPO-token)
|
||||
|
||||
论文其他超参
|
||||
```bash
|
||||
--epsilon 3e-4 # from paper section 5.1
|
||||
--epsilon_high 4e-4 # from paper section 5.1
|
||||
--gradient_accumulation_steps 8
|
||||
--steps_per_generation 32 # from paper section 5.1 (each batch of rollout data is partitioned into four minibatches for gradient updates)
|
||||
--beta 0 # zero kl regularization https://github.com/volcengine/verl/pull/2775#issuecomment-3131807306
|
||||
```
|
||||
|
||||
训练可以参考该[脚本](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/internal/gspo.sh)
|
||||
@@ -0,0 +1,77 @@
|
||||
# Rewards as Labels: Revisiting RLVR from a Classification Perspective
|
||||
|
||||
作者: [li2zhi](https://github.com/li2zhi)
|
||||
|
||||
[Rewards as Labels: Revisiting RLVR from a Classification Perspective](https://arxiv.org/abs/2602.05630) 针对GRPO提出把奖励视为标签,在group内分类而不是计算advantage,从而将策略优化问题转化为分类问题,以此解决GRPO Loss中存在的正样本**梯度错配**与负样本**梯度主导**问题。
|
||||
|
||||
## 背景与动机
|
||||
|
||||
GRPO目标函数
|
||||
|
||||
$$
|
||||
J_{\mathrm{GRPO}}(\theta)=\mathbb{E}_{q,o\sim\pi_{\mathrm{od}}(\cdot|q)}\left[\frac{1}{|o|}\sum_{t=1}^{|o|}\left(\min\left(\rho_tA_t,\mathrm{clip}(\rho_t,1-\epsilon,1+\epsilon)A_t\right)\right)\right]
|
||||
$$
|
||||
|
||||
其中$\rho_t=\frac{\pi_\theta(o_t|q)}{\pi_{\mathrm{old}}(o_t|q)}$为相对概率,$A_{t}$为优势函数,故梯度为:
|
||||
|
||||
$$
|
||||
\nabla_{\theta} J_{\mathrm{GRPO}} = \mathbb { E } \left[ \frac { 1 } { | o | } \sum _ { t = 1 } ^ { | o | } \mathbb { I } _ { \mathrm { clip } } \cdot A _ { t } e ^ { s _ { t } } \nabla _ { \theta } \log \pi _ { \theta } \left( o _ { t } | q \right) \right]
|
||||
$$
|
||||
|
||||
其中$s_t=\log\frac{\pi_\theta(o_t|q)}{\pi_{\mathrm{old}}(o_t|q)}$作为token的相对对数概率,$\mathbb { I } _ { \mathrm { clip } }$为指示函数
|
||||
|
||||
故 GRPO 对单 token 的梯度权重为:
|
||||
|
||||
$$
|
||||
|\mathcal{W}_{\mathrm{GRPO}}|=\left\{ \begin{array} {ll}\left|A\cdot e^s\right|, & \mathrm{if~}\mathbb{I}_{\mathrm{clip}}=1, \\ 0, & \text{otherwise.} \end{array}\right.
|
||||
$$
|
||||
|
||||

|
||||
|
||||
- 正样本的梯度错配(Gradient Misassignment):对正样本来说,随着相对概率$s$变小,梯度更新幅度反而越弱。这违背直觉,因为模型对“不太自信”的正确 token 本来就需要更大的更新幅度来强化,但更多的梯度权重却放到更“自信”的 token,没学好的 token 得不到足够的重视。
|
||||
|
||||
- 负样本的梯度主导(Gradient Domination):对负样本来说,随着相对概率$s$变小,梯度更新幅度呈指数级增加。这意味着,只要出现几个模型“盲目自信”的错误 token,它们产生的巨大梯度就会把同组内其他负样本的信号淹没。由于缺乏上限保护,模型在处理这些错误样本时可能会产生过大的参数更新,让训练过程变得不太可控。
|
||||
|
||||
为解决上述问题,Real提出将奖励直接视为标签然后进行组内的样本分类训练
|
||||
|
||||

|
||||
|
||||
分类的logits分值设计:
|
||||
|
||||
$$
|
||||
\bar{s}^k=\frac{1}{|o^k|}\sum_{t=1}^{|o^k|}\left(\log\frac{\pi_\theta(o_t^k\mid q)}{\pi_{\mathrm{old}}(o_t^k\mid q)}\right)
|
||||
$$
|
||||
|
||||
- $\bar{s}^k > 0$: 表示该样本在当前策略下生成的概率比旧策略整体更高,模型倾向于**增强**该样本。
|
||||
- $\bar{s}^k < 0$: 表示该样本在当前策略下生成的概率比旧策略整体更低,模型倾向于**抑制**该样本。
|
||||
|
||||
损失函数设计:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{REAL}=\log\left(1+\sum_{\mathcal{O}_+}e^{-\bar{s}^i/\tau}\right)+\log\left(1+\sum_{\mathcal{O}_-}e^{\bar{s}^j/\tau}\right)
|
||||
$$
|
||||
|
||||
梯度特性:
|
||||
$$
|
||||
|\mathcal{W}_{\mathrm{REAL}}|=
|
||||
\begin{cases}
|
||||
\frac{1}{\tau}\frac{1}{1+C_{+}e^{\bar{s}^{k}/\tau}}, & r=1 \\
|
||||
\\
|
||||
\frac{1}{\tau}\frac{1}{1+C_{-}e^{-\bar{s}^{k}/\tau}}, & r=0 & & &
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
## 参数设置
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|-------------------|---------|-------|-------------------|
|
||||
| `--loss_type` | `str` | - | 设置为 `real` |
|
||||
| `--real_tau` | `float` | `0.5` | 温度参数,控制决策边界锐度 |
|
||||
|
||||
训练脚本参考
|
||||
|
||||
[swift](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/real.sh)
|
||||
|
||||
## 注意事项
|
||||
|
||||
设置参数时,确保 per_device_train_batch_size 能够被 num_generations 整除,以此保证单个训练batch中能拿到完整的 group 进行分类。
|
||||
@@ -0,0 +1,87 @@
|
||||
# REINFORCE++: An Efficient RLHF Algorithm with Robustness to Both Prompt and Reward Models
|
||||
|
||||
[REINFORCE++ Baseline](https://arxiv.org/abs/2501.03262) 是 REINFORCE++ 算法的简化版本,适用于 outcome rewards(response-level 标量奖励)。它与 GRPO 类似,对每个prompt输入采样多条模型输出,并使用组内 baseline 来估计优势函数,主要区别在于标准化时使用的统计量不同。
|
||||
|
||||
|
||||
## 算法原理
|
||||
为便于理解,我们基于 GRPO(Group Relative Policy Optimization)算法进行对比说明。
|
||||
|
||||
GRPO 和 REINFORCE++ Baseline 都采用组内对比的方式来估计优势函数,主要区别在于:
|
||||
|
||||
### 区别1:标准化时使用的统计量不同
|
||||
|
||||
**GRPO (Group Relative Policy Optimization)**
|
||||
|
||||
对每个 prompt 生成 $G$ 个响应样本,使用**组内所有样本的均值和标准差**进行标准化:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)}
|
||||
$$
|
||||
|
||||
当设置 `scale_rewards='batch'` 时,使用**原始奖励的批次 std**:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^{N})}
|
||||
$$
|
||||
|
||||
其中 $N$ 是批次中所有样本数。
|
||||
|
||||
**REINFORCE++ Baseline**
|
||||
|
||||
对每个 prompt 生成 $G$ 个响应样本,先减去组内均值,再使用**减去组内均值后的奖励**的标准差进行标准化:
|
||||
|
||||
$$
|
||||
\begin{align}
|
||||
\tilde{A}_{i} &= R_i - \text{mean}(\{R_j\}_{j=1}^G) \\
|
||||
\hat{A}_{i} &= \frac{\tilde{A}_{i}}{\text{std}(\{\tilde{A}_k\}_{k=1}^{N})}
|
||||
\end{align}
|
||||
$$
|
||||
|
||||
其中 $N$ 是批次中所有样本数。
|
||||
|
||||
**关键区别**:
|
||||
- **GRPO**:标准化时使用**原始奖励 $R$** 的标准差
|
||||
- **REINFORCE++**:标准化时使用**减去组内均值后的奖励 $\tilde{A}$** 的标准差
|
||||
|
||||
### 区别2: KL 散度正则化
|
||||
|
||||
与 RLOO 类似,REINFORCE++ Baseline 将 KL 散度整合到奖励项中:
|
||||
|
||||
$$
|
||||
R'_i = R_i - \beta \cdot \text{KL}(\pi_\theta || \pi_{\text{ref}})
|
||||
$$
|
||||
|
||||
其中 $\beta$ 是 KL 散度的权重系数(对应参数 `beta`),$\pi_{\text{ref}}$ 是参考策略。
|
||||
|
||||
## 参数设置
|
||||
|
||||
我们可以基于 `GRPOTrainer`,通过设置以下参数实现 REINFORCE++ Baseline 训练:
|
||||
|
||||
```bash
|
||||
--advantage_estimator reinforce_plus_plus
|
||||
--scale_rewards batch
|
||||
--kl_in_reward true
|
||||
```
|
||||
|
||||
训练可以参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/reinforce_plus_plus.sh)
|
||||
|
||||
### 重要参数说明
|
||||
|
||||
- **`--advantage_estimator`**:选择优势函数估计方法
|
||||
- `grpo`(默认):标准化时使用原始奖励的标准差
|
||||
- `reinforce_plus_plus`:标准化时使用减去组内均值后的奖励的标准差
|
||||
|
||||
- **`--kl_in_reward`**:控制 KL 散度正则化项的处理位置
|
||||
- `false`:KL 散度作为损失函数的独立正则化项(GRPO 默认)
|
||||
- `true`:KL 散度直接从奖励中扣除(REINFORCE++ 原始实现)
|
||||
|
||||
- **`--scale_rewards`**:控制标准化方式
|
||||
- `group`(默认):组内标准化
|
||||
- `batch`:全局批次标准化(REINFORCE++原始实现)
|
||||
- `none`:不进行标准化
|
||||
|
||||
- **`--num_generations`**:每个 prompt 生成的样本数量 $G$
|
||||
|
||||
- **`--beta`**:KL 散度正则化系数 $\beta$
|
||||
|
||||
其他参数参考 [GRPO参数](../../Command-line-parameters.md#grpo参数)
|
||||
@@ -0,0 +1,92 @@
|
||||
# REINFORCE Leave-One-Out (RLOO)
|
||||
|
||||
[REINFORCE Leave-One-Out (RLOO)](https://arxiv.org/abs/2402.14740) 基于经典的 REINFORCE 策略梯度方法,通过留一法(Leave-One-Out)构造无偏的优势函数基线。
|
||||
|
||||
## 算法原理
|
||||
|
||||
为便于理解,我们基于 GRPO(Group Relative Policy Optimization)算法进行对比说明。
|
||||
|
||||
GRPO 和 RLOO 都采用组内对比的方式来估计优势函数,避免了全局基线估计带来的高方差问题。两者的核心区别主要体现在以下两个方面:
|
||||
|
||||
### 区别1:优势函数基线的构造方法
|
||||
|
||||
**1. GRPO (Group Relative Policy Optimization)**
|
||||
|
||||
GRPO 对每个 prompt 生成 $G$ 个响应样本,使用**组内所有样本的均值和标准差**进行标准化:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)}
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $R_i$ 是第 $i$ 个样本的奖励值
|
||||
- $\text{mean}(\{R_j\}_{j=1}^G) = \frac{1}{G}\sum_{j=1}^G R_j$ 是组内均值
|
||||
- $\text{std}(\{R_j\}_{j=1}^G)$ 是组内标准差
|
||||
|
||||
**2. RLOO (REINFORCE Leave-One-Out)**
|
||||
|
||||
RLOO 对每个 prompt 生成 $K$ 个响应样本,使用 **留一法(Leave-One-Out)** 构造基线,即第 $i$ 个样本的基线为除自己外的其他 $K-1$ 个样本的均值:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = R_i - \frac{1}{K-1}\sum_{j \neq i} R_j
|
||||
$$
|
||||
|
||||
这个公式可以等价地改写为:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = \frac{K}{K-1} \left(R_i - \bar{R}\right)
|
||||
$$
|
||||
|
||||
其中 $\bar{R} = \frac{1}{K}\sum_{j=1}^K R_j$ 是组内所有样本的均值。
|
||||
|
||||
> **说明**:这里使用 $K$ 对齐论文符号,与 GRPO 中的 $G$ 含义一致,均对应配置参数 `num_generations`
|
||||
|
||||
**为什么使用留一法?**
|
||||
|
||||
留一法的关键优势在于**无偏性**。对于第 $i$ 个样本,其奖励 $R_i$ 和基线 $\frac{1}{K-1}\sum_{j \neq i} R_j$ 是独立的,因此优势估计是无偏的。相比之下,如果使用包含自身的均值作为基线,会引入偏差。
|
||||
|
||||
### 区别2:KL 散度正则化项的处理方式
|
||||
|
||||
为防止策略偏离参考策略过远,两种算法都引入了 KL 散度正则化,但处理方式不同:
|
||||
|
||||
**GRPO**:将 KL 散度作为独立的正则化项添加到[损失函数](../GetStarted/GRPO.md#算法原理)中:
|
||||
|
||||
$$
|
||||
\mathcal{L}(\theta) = -\mathbb{E}\left[\hat{A}_i \log \pi_\theta(a_i|s_i)\right] + \beta \cdot \text{KL}(\pi_\theta || \pi_{\text{ref}})
|
||||
$$
|
||||
|
||||
**RLOO**:将 KL 散度直接整合到奖励项中,构造修正后的奖励:
|
||||
|
||||
$$
|
||||
R'_i = R_i - \beta \cdot \text{KL}(\pi_\theta || \pi_{\text{ref}})
|
||||
$$
|
||||
|
||||
其中 $\beta$ 是 KL 散度的权重系数(对应参数 `beta`),$\pi_{\text{ref}}$ 是参考策略(通常是 SFT 模型或初始策略)。
|
||||
|
||||
## 参数设置
|
||||
|
||||
我们可以基于 `GRPOTrainer`,通过设置以下参数实现 RLOO 训练:
|
||||
```bash
|
||||
# 基本 RLOO 配置
|
||||
--advantage_estimator rloo # 使用 RLOO 的留一法优势函数计算
|
||||
--kl_in_reward true # 将 KL 散度项整合到奖励中(RLOO 默认方式)
|
||||
```
|
||||
|
||||
训练可以参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/rloo.sh)
|
||||
|
||||
### 重要参数说明
|
||||
|
||||
- **`--advantage_estimator`**:选择优势函数估计方法
|
||||
- `grpo`(默认):使用组内均值和标准差进行标准化
|
||||
- `rloo`:使用留一法(Leave-One-Out)构造基线
|
||||
|
||||
- **`--kl_in_reward`**:控制 KL 散度正则化项的处理位置
|
||||
- `false`:KL 散度作为损失函数的独立正则化项(GRPO 方式)
|
||||
- `true`:KL 散度直接从奖励中扣除,构造修正后的奖励(RLOO 方式)
|
||||
|
||||
- **`--num_generations`**:每个 prompt 生成的样本数量 $K$
|
||||
|
||||
- **`--beta`**:KL 散度正则化系数 $\beta$
|
||||
- 控制策略更新的保守程度
|
||||
|
||||
其他参数与 [GRPO参数](../../Command-line-parameters.md#grpo参数)一致
|
||||
@@ -0,0 +1,91 @@
|
||||
# Soft Adaptive Policy Optimization (SAPO)
|
||||
|
||||
[Soft Adaptive Policy Optimization (SAPO)](https://arxiv.org/abs/2511.20347) 针对 GRPO 中硬裁剪(hard clipping)带来的问题,提出了一种基于温度控制的软门控(soft gate)机制,用于平滑地衰减离策略更新,同时保留有用的学习信号。
|
||||
|
||||
## 背景与动机
|
||||
|
||||
在强化学习训练 LLM 时,GRPO 通过计算 token 级别的重要性采样比(Importance Sampling Ratio)来处理 off-policy 训练:
|
||||
|
||||
$$
|
||||
r_t = \frac{\pi_\theta(y_t|x, y_{<t})}{\pi_{\theta_{\mathrm{old}}}(y_t|x, y_{<t})}
|
||||
$$
|
||||
|
||||
然而,token 级别的重要性采样比往往表现出高方差,这一现象在以下情况下可能更严重:
|
||||
- **长文本生成**
|
||||
- **MoE 模型的路由异质性**:采样时的 old-policy 模型与训练模型可能使用不同的专家路由,导致 logps 差异显著放大
|
||||
|
||||
为此,GRPO 通过硬裁剪来限制策略更新的幅度:
|
||||
|
||||
$$
|
||||
L^{\mathrm{GRPO}} = -\min\left( r_t \cdot A, \mathrm{clip}(r_t, 1-\epsilon, 1+\epsilon) \cdot A \right)
|
||||
$$
|
||||
|
||||
**硬裁剪的困境**:硬裁剪难以在稳定性和学习效率之间取得平衡——裁剪范围过严格会限制有效样本的数量,而过宽松则会引入离策略样本的噪声梯度,导致训练不稳定。
|
||||
|
||||
## SAPO 方法
|
||||
|
||||
SAPO 使用温度控制的 sigmoid 软门控函数替代硬裁剪,实现平滑的梯度衰减。
|
||||
|
||||
### 软门控函数
|
||||
|
||||
SAPO 的核心是使用 sigmoid 函数对重要性采样比进行软门控:
|
||||
|
||||
对于正向优势($A > 0$),使用正向门控:
|
||||
|
||||
$$
|
||||
g^{+}_t = \sigma\left( \tau_{\mathrm{pos}} \cdot (r_t - 1) \right) \cdot \frac{4}{\tau_{\mathrm{pos}}}
|
||||
$$
|
||||
|
||||
对于负向优势($A < 0$),使用负向门控:
|
||||
|
||||
$$
|
||||
g^{-}_t = \sigma\left( \tau_{\mathrm{neg}} \cdot (r_t - 1) \right) \cdot \frac{4}{\tau_{\mathrm{neg}}}
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $\sigma(\cdot)$ 是 sigmoid 函数
|
||||
- $\tau_{\mathrm{pos}}$ 和 $\tau_{\mathrm{neg}}$ 是温度参数,控制门控函数的斜率
|
||||
- $r_t$ 是重要性采样比
|
||||
|
||||
### SAPO 损失函数
|
||||
|
||||
$$
|
||||
L^{\mathrm{SAPO}} = -g_t \cdot A
|
||||
$$
|
||||
|
||||
其中 $g_t = g^{+}_t$ 当 $A > 0$,$g_t = g^{-}_t$ 当 $A < 0$。
|
||||
|
||||
### 温度参数
|
||||
|
||||
温度参数 $\tau$ 控制软门控函数的衰减速率,数值越大,衰减越快。
|
||||
|
||||

|
||||
|
||||
论文指出正向优势会提升采样token的logit,并降低所有未采样token的logit;负向优势相反,提高许多未采样token的logit,可能会扩散到大量无关token上,带来一定的不稳定性。所以论文推荐设置温度 $\tau_\text{neg} > \tau_\text{pos}$,来使负向奖励的token梯度衰减更快,提升训练的稳定性和性能。
|
||||
|
||||
论文默认推荐 $\tau_{\mathrm{pos}} = 1.0$,$\tau_{\mathrm{neg}} = 1.05$。
|
||||
|
||||
## 参数设置
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `--loss_type` | `str` | - | 设置为 `sapo` |
|
||||
| `--tau_pos` | `float` | `1.0` | 正向优势的温度参数,控制门控斜率 |
|
||||
| `--tau_neg` | `float` | `1.05` | 负向优势的温度参数,控制门控斜率 |
|
||||
|
||||
```bash
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--loss_type sapo \
|
||||
--tau_pos 1.0 \
|
||||
--tau_neg 1.05 \
|
||||
# ... 其他参数
|
||||
```
|
||||
|
||||
训练脚本参考
|
||||
|
||||
- [swift](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/internal/sapo.sh)
|
||||
- [megatron swift](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/grpo/sapo.sh)
|
||||
|
||||
> SAPO 的软门控机制仅在 off-policy 训练下生效。
|
||||
> SAPO 中的重要性采样粒度为 token 级别(即 importance_sampling_level 默认设置为 token),与 GSPO 冲突。
|
||||
@@ -0,0 +1,95 @@
|
||||
# DeepEyes: Incentivizing "Thinking with Images" via Reinforcement Learning
|
||||
|
||||
## 原理介绍
|
||||
|
||||
[DeepEyes论文](https://arxiv.org/abs/2505.14362) 提出了一种利用强化学习使模型具备“think with images”(以图辅助思考)能力的方法。该方法通过端到端的强化学习,模型能力自发涌现,无需额外的 SFT(监督微调)过程。模型内置图像定位能力,能够主动调用“图像放大工具”:在推理过程中,模型会自动选取图片中的具体区域进行放大和裁剪,将处理后的区域信息进行进一步推理,实现视觉与文本的链式推理。
|
||||
|
||||

|
||||
|
||||
## 最佳实践
|
||||
|
||||
**数据集下载与注册**
|
||||
|
||||
下载 DeepEyes 官方训练数据集到本地
|
||||
```bash
|
||||
# modelscope
|
||||
modelscope download --dataset Lixiang/ChenShawn-DeepEyes-Datasets-47k
|
||||
|
||||
# huggingface
|
||||
huggingface-cli download ChenShawn/DeepEyes-Datasets-47k --repo-type=dataset
|
||||
```
|
||||
|
||||
数据集内有三个parquet文件,`swift/dataset/data/dataset_info.json` 文件中分别进行注册,将数据集中的 `prompt` 列重命名为 `messages`
|
||||
|
||||
```json
|
||||
{
|
||||
"ms_dataset_id": "path/to/data_0.1.2_visual_toolbox_v2.parquet",
|
||||
"columns": {
|
||||
"prompt": "messages"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ms_dataset_id": "path/to/data/data_thinklite_reasoning_acc.parquet",
|
||||
"columns": {
|
||||
"prompt": "messages"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ms_dataset_id": "path/to/data/data_v0.8_visual_toolbox_v2.parquet",
|
||||
"columns": {
|
||||
"prompt": "messages"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在本地注册论文中所用到的奖励函数和工具调用逻辑,实现可以参考[DeepEyes实现示例](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/deepeyes/deepeyes_plugin.py)
|
||||
|
||||
**部署验证模型**
|
||||
|
||||
Deepeyes 的奖励函数依赖生成式奖励模型对模型生成结果与标准答案进行对比评估,为了加速这一环节,推荐对模型进行部署。
|
||||
|
||||
假设使用 Qwen2.5-VL-72B-Instruct 模型进行评估,参考以下部署命令
|
||||
|
||||
```bash
|
||||
# 4*80G
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift deploy \
|
||||
--model Qwen/Qwen2.5-VL-72B-Instruct \
|
||||
--infer_backend vllm \
|
||||
--vllm_tensor_parallel_size 4 \
|
||||
```
|
||||
|
||||
在 plugin 文件中,使用OpenAI接口进行调用,参考[奖励模型文档](../DeveloperGuide/reward_model.md#外部部署)
|
||||
|
||||
|
||||
训练参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/deepeyes/deepeyes.sh)
|
||||
|
||||
## 实现细节
|
||||
|
||||
[DeepEyes实现示例](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/deepeyes/deepeyes_plugin.py)参考[官方实现](https://github.com/Visual-Agent/DeepEyes/blob/main/verl/utils/reward_score/vl_agent.py) 给出了 DeepEyes 训练插件的样例代码,涵盖了奖励函数与多轮交互调用的相关逻辑。
|
||||
|
||||
**数据集数据**如下
|
||||
|
||||
| 数据集文件名 | data_source | 对应评分函数 | 工具调用 |
|
||||
|------------------------------------------|-----------------------|----------------------------------|------------------|
|
||||
| data_v0.8_visual_toolbox_v2.parquet | chart | vl_agent.compute_score | True (image_zoom_in_tool) |
|
||||
| data_0.1.2_visual_toolbox_v2.parquet | vstar | vl_agent.compute_score | True (image_zoom_in_tool) |
|
||||
| data_thinklite_reasoning_acc.parquet | thinklite_eureka | vl_agent.compute_score_math | False |
|
||||
|
||||
|
||||
**注意**:多模态大模型在处理图像输入时,可能会对图像进行预处理(例如受 max_pixels 参数限制的裁剪或缩放等操作)。当调用图像放大工具 image_zoom_in_tool 时,模型会根据输入图像输出裁剪后的 bbox。因此,在调用图像放大工具时,需要确保输入的是经过预处理后的图像。示例代码展示了 Qwen2.5-VL 系列模型的实现方式:
|
||||
|
||||
```python
|
||||
from qwen_vl_utils import fetch_image
|
||||
# 这里的images尚未经过图像处理
|
||||
infer_request.images
|
||||
# 通过加载为PIL.Image格式,进行裁剪(使用环境变量MAX_PIXELS时的处理)
|
||||
img = fetch_image({'image': load_pil_image(infer_request.images[0])})
|
||||
```
|
||||
|
||||
**工具奖励**
|
||||
|
||||
论文中指出当最终答案正确,且轨迹至少使用一个工具时给予工具奖励。为了避免模型生成的工具调用是无效的,我们通过图像数量而不是`<tool_call>` 等token进行判断。
|
||||
```python
|
||||
tool_reward = 1.0 if num_image > 1 and acc_reward > 0.5 else 0.0
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
# Beyond the 80/20 Rule: High-Entropy Minority Tokens Drive Effective Reinforcement Learning for LLM Reasoning
|
||||
|
||||
[论文](https://arxiv.org/abs/2506.01939)发现在以 RLVR等方法训练大型语言模型推理能力时,驱动学习进步的关键在于一小部分高熵“少数 token”,而并非大多数信息熵低的 token。
|
||||
|
||||
论文指出,在模型推理的 token 分布中,只有极少数信息熵较高的 token 起到了主导作用。这些 token 往往出现在推理和决策路径分歧最大的关键节点(如 "wait"、"since" 等),决定了模型能否习得复杂推理任务。而大多数熵低的 token 对模型推理能力的提升作用有限。论文提出只对高熵 token 计算策略梯度、舍弃低熵 token 的梯度。
|
||||
|
||||
|
||||
token 熵公式如下
|
||||
|
||||
$
|
||||
H_t := -∑_{j=1}^{V} p_{t,j} \log p_{t,j}, \qquad where (p_{t,1}, ···, p_{t,V}) = \mathbf{p}_t = π_θ(\cdot | \mathbf{q}, \mathbf{o}_{<t}) = \text{Softmax}(\frac{\mathbf{z}_t}{T})
|
||||
$
|
||||
|
||||
其中
|
||||
- $\pi_\theta$:参数为 $\theta$ 的模型
|
||||
- $\mathbf{q}$:输入查询(input query)。
|
||||
- $\mathbf{o}_{<t} = (o_1, o_2, \cdots, o_{t-1})$:时间步 $t$ 之前已生成的 token 序列。
|
||||
- $V$:词表大小(vocabulary size)。
|
||||
- $\mathbf{z}_t \in \mathbb{R}^V$:时间步 $t$ 的 pre-softmax 逻辑值(logits)。
|
||||
- $\mathbf{p}_t \in \mathbb{R}^V$:模型对词表的概率分布。
|
||||
- $T \in \mathbb{R}$:解码温度(decoding temperature),控制分布的平滑程度。
|
||||
|
||||
熵的计算对象:$H_t$ 是 token 生成分布 $\mathbf{p}_t$ 的熵,用于衡量训练策略 $\pi_\theta$ 在给定上下文 $(\mathbf{q}, \mathbf{o}_{<t})$ 下的不确定性。
|
||||
|
||||
> "Token entropy" $H_t$ 始终指向位置 $t$ 的生成分布 $\mathbf{p}_t$ 的不确定性,而非 token $o_t$ 本身的属性。即$H_t$ 是位置 $t$ 对应分布 $\mathbf{p}_t$ 的熵,与采样得到的 token $o_t$ 无关。
|
||||
|
||||
|
||||
在实践中,我们可以在 GRPO 训练中通过参数 `top_entropy_quantile` 控制训练范围。论文实验设置该参数为 0.2,即每次仅对处于熵分布前 20% 的 token 进行训练优化。
|
||||
|
||||
同时使用参数`log_entropy`,可以记录训练过程中的熵值变化,参考[文档](../GetStarted/GRPO.md#logged-metrics)
|
||||
@@ -0,0 +1,19 @@
|
||||
Advanced Research
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
entropy_mask.md
|
||||
CISPO.md
|
||||
DAPO.md
|
||||
deepeyes.md
|
||||
FIPO.md
|
||||
GSPO.md
|
||||
CHORD.md
|
||||
RLOO.md
|
||||
REINFORCEPP.md
|
||||
REAL.md
|
||||
router_replay.md
|
||||
SAPO.md
|
||||
training_inference_mismatch.md
|
||||
treepo.md
|
||||
@@ -0,0 +1,104 @@
|
||||
# Router Replay (R2/R3)
|
||||
|
||||
**TL;DR**: 在 MoE 模型的 RL 训练中,训练引擎与推理引擎之间的路由(expert routing)不一致会显著放大训推偏差,甚至导致训练崩溃。Router Replay 通过在训练前向传播中回放固定的路由掩码来消除这一不一致。根据回放来源的不同,分为 R2(Vanilla Routing Replay)和 R3(Rollout Routing Replay)两种策略。
|
||||
|
||||
## Background
|
||||
|
||||
### MoE RL 中的三个策略
|
||||
|
||||
在 MoE 模型的 GRPO 训练中,存在三个不同阶段的策略,它们共享相同的模型权重,但路由行为可能不同:
|
||||
|
||||
| 策略 | 符号 | 路由结果 | 说明 |
|
||||
|------|------|---------|------|
|
||||
| **训练策略** | $\pi_\theta$ | $e^{\pi}_t$ | 梯度更新中的模型 |
|
||||
| **Old Policy** | $\pi_{\theta_{\text{old}}}$ | $e^{\pi}_{\text{old},t}$ | 批次更新前的模型状态 |
|
||||
| **Rollout Policy** | $\mu_{\theta_{\text{old}}}$ | $e^{\mu}_{\text{old},t}$ | 推理引擎(如vLLM)中的采样策略,权重与 old policy 相同,但 kernel 实现、精度等差异导致路由不同 |
|
||||
|
||||
其中,$\pi_{\theta_{\text{old}}}$ 和 $\mu_{\theta_{\text{old}}}$ 的权重在采样时完全一致,但由于推理引擎与训练引擎的实现差异(如算子实现),即使输入相同,路由结果也可能不同。
|
||||
|
||||
### 训推不一致的分解
|
||||
|
||||
根据 [论文](https://arxiv.org/abs/2507.18071) 的分析,token 级重要性采样比可以分解为两个因子:
|
||||
|
||||
$$
|
||||
\frac{\pi_\theta(y_t|x, y_{<t})}{\mu_{\theta_{\text{old}}}(y_t|x, y_{<t})} = \underbrace{\frac{\pi_{\theta_{\text{old}}}(y_t|x, y_{<t})}{\mu_{\theta_{\text{old}}}(y_t|x, y_{<t})}}_{\text{training-inference discrepancy}} \times \underbrace{\frac{\pi_\theta(y_t|x, y_{<t})}{\pi_{\theta_{\text{old}}}(y_t|x, y_{<t})}}_{\text{policy staleness}}
|
||||
$$
|
||||
|
||||
对于 MoE 模型,专家路由与这两个因子深度耦合:
|
||||
|
||||
- **Training-inference discrepancy**:训练引擎和推理引擎的路由不一致($e^{\pi}_{\text{old},t} \neq e^{\mu}_{\text{old},t}$),放大输出差异
|
||||
- **Policy staleness**:随着 mini-batch 更新,路由也随之漂移($e^{\pi}_t \neq e^{\pi}_{\text{old},t}$),进一步偏离采样时的策略
|
||||
|
||||
## R2: Vanilla Routing Replay
|
||||
|
||||
R2 的核心思想是在梯度更新时,**回放 old policy 在训练引擎中确定的路由**($e^{\pi}_{\text{old},t}$)。
|
||||
|
||||
### 原理
|
||||
|
||||
在训练前向传播中,先用 old policy 的权重做一次前向,记录每一层 MoE Router 选择的 expert indices,然后在训练模型 $\pi_\theta$ 的前向传播中强制使用这些 indices:
|
||||
|
||||
$$
|
||||
g_{\text{replay},i} = \frac{I^{\pi}_{\text{old},i} \cdot \exp(s_{\text{train},i})}{\sum_j I^{\pi}_{\text{old},j} \cdot \exp(s_{\text{train},j})}
|
||||
$$
|
||||
|
||||
其中 $I^{\pi}_{\text{old}}$ 是 old policy 的路由掩码,$s_{\text{train}}$ 是训练模型计算的 router logits。softmax 仍作用于训练 logits,因此梯度可以正常回传到 router 权重。
|
||||
|
||||
### 特性
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| **首个 mini-batch**(on-policy) | $\theta = \theta_{\text{old}}$,故 $e^{\pi}_t = e^{\pi}_{\text{old},t}$,**target policy 不变**(无 bias) |
|
||||
| **后续 mini-batch**(off-policy) | $\theta \neq \theta_{\text{old}}$,故 $e^{\pi}_t \neq e^{\pi}_{\text{old},t}$,**target policy 被改变**(有 bias),但 policy staleness 被控制 |
|
||||
|
||||
## R3: Rollout Routing Replay
|
||||
|
||||
R3 的核心思想是在训练前向传播中,**回放 rollout policy 在推理引擎中确定的路由**($e^{\mu}_{\text{old},t}$)。
|
||||
|
||||
### 原理
|
||||
|
||||
在推理引擎(如 vLLM)采样时,额外记录每个 token 在每一层 MoE Router 的 expert indices(路由掩码),然后将这些掩码传递到训练引擎,在 $\pi_\theta$ 的前向传播中强制使用:
|
||||
|
||||
$$
|
||||
g_{\text{replay},i} = \frac{I^{\mu}_{\text{old},i} \cdot \exp(s_{\text{train},i})}{\sum_j I^{\mu}_{\text{old},j} \cdot \exp(s_{\text{train},j})}
|
||||
$$
|
||||
|
||||
### 与其他方法的兼容性
|
||||
|
||||
- R3 与 **GSPO** 正交,组合使用可进一步提升
|
||||
- R3 与 **TIS** 组合不一定有增益(R3 已从根源消除不一致,TIS 的额外修正可能多余)
|
||||
- Router Replay 与 **Clipping** 在 off-policy 训练中缺一不可
|
||||
|
||||
## Router Mask Caching
|
||||
|
||||
R3 论文还提出路由掩码可以与 KV Cache 一起缓存:对于相同的前缀 token,MoE Router 的输出是确定性的,因此路由掩码可以随 prefix KVCache 一起存储和复用。这在多轮 Agent 场景(tool calling)中尤为重要,避免了重新 prefill 前缀来获取路由掩码,整体 rollout 延迟开销 < 3%。
|
||||
|
||||
## Swift 实现
|
||||
|
||||
### 参数设置
|
||||
|
||||
通过 `--router_replay_mode` 参数选择路由回放策略:
|
||||
|
||||
| 参数值 | 说明 |
|
||||
|--------|------|
|
||||
| `disabled`(默认) | 不启用路由回放 |
|
||||
| `R2` | Vanilla Routing Replay:在训练引擎中记录 old policy 路由并回放 |
|
||||
| `R3` | Rollout Routing Replay:从推理引擎导出路由掩码并在训练中回放 |
|
||||
|
||||
环境依赖:
|
||||
|
||||
- R3 需要 vLLM ≥ 0.14.0 以支持返回 `routed_experts` 信息。
|
||||
- 当前 Router Replay 仅在 Megatron backend 下可用,需要 megatron-core ≥ 0.16.0。
|
||||
|
||||
### 与训推校正的关系
|
||||
|
||||
Router Replay 与 [Training-Inference Mismatch](training_inference_mismatch.md) 中介绍的重要性采样校正(IS correction)是互补的:
|
||||
|
||||
- **IS correction**:在 loss 层面通过权重修正训推概率偏差
|
||||
- **Router Replay**:在模型结构层面通过固定路由消除偏差来源
|
||||
|
||||
## 参考资料
|
||||
|
||||
1. [Stabilizing MoE Reinforcement Learning by Aligning Training and Inference Routers](https://arxiv.org/abs/2510.11370)
|
||||
2. [Group Sequence Policy Optimization](https://arxiv.org/abs/2507.18071)
|
||||
3. [Stabilizing Reinforcement Learning with LLMs: Formulation and Practices](https://arxiv.org/abs/2512.01374)
|
||||
4. [Megatron Core Router Replay Design Document](https://docs.nvidia.com/megatron-core/developer-guide/nightly/api-guide/router_replay.html)
|
||||
@@ -0,0 +1,239 @@
|
||||
# Training-Inference-Mismatch
|
||||
|
||||
|
||||
**TL;DR**: GRPO 引入 vLLM 加速采样过程的同时,也引入了训练-推理不一致(Training-Inference Mismatch)的问题,从而可能影响训练稳定性。本文将解释这个问题的背景、原因以及相应的解决方案。
|
||||
|
||||
## Background
|
||||
|
||||
### GRPO 的基本假设
|
||||
|
||||
GRPO (Group Relative Policy Optimization) 的训练目标可以表示为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{GRPO}} = - \mathbb{E}_{y \sim \pi_\theta} \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $r_t(\theta) = \frac{\pi_\theta(y_t|x, y_{<t})}{\pi_{\theta_{\text{old}}}(y_t|x, y_{<t})}$ 是重要性采样比
|
||||
- $\hat{A}_t$ 是优势函数(advantage),基于 reward 和 group baseline 计算
|
||||
- $\epsilon$ 是 clipping 参数
|
||||
|
||||
**核心假设**:样本 $y$ 是从策略 $\pi_\theta$ 中采样得到的。在实际训练中,这意味着:
|
||||
1. 采样模型(rollout model)与训练模型(policy model)应当是**同一个模型** $\pi_\theta$
|
||||
2. 两个模型的概率分布应当**完全一致**,即 $\pi_{\text{rollout}} = \pi_\theta$
|
||||
|
||||
### 引入 vLLM 后的假设偏离
|
||||
|
||||
GRPO 的训练速度很大程度上受到采样过程(rollout)的速度制约。为了加速,训练框架引入高效推理引擎(如 vLLM)来执行采样。**理想假设**是:通过权重同步,vLLM 与训练模型保持一致,即 $\pi_{\text{vLLM}} \equiv \pi_\theta$。
|
||||
|
||||
然而,在实践中,即使权重完全同步,由于算子实现等差异,两者的概率分布仍然存在偏差:
|
||||
|
||||
$$
|
||||
\pi_{\text{vLLM}}(y|x) \neq \pi_\theta(y|x)
|
||||
$$
|
||||
|
||||
此时,实际的训练目标变为:
|
||||
|
||||
$$
|
||||
\mathcal{L} = - \mathbb{E}_{y \sim \pi_{\text{vLLM}}} \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]
|
||||
$$
|
||||
|
||||
其中样本来自 $\pi_{\text{vLLM}}$,但梯度是基于 $\pi_\theta$ 计算的,这**破坏了算法的 on-policy 假设**,引入了训推不一致的问题。
|
||||
|
||||
## Solution
|
||||
|
||||
针对训推不一致问题,可以引入**重要性采样(Importance Sampling, IS)**的校正机制。
|
||||
|
||||
### 重要性采样校正
|
||||
|
||||
重要性采样的基本思想是:当样本来自分布 $q$ 而非目标分布 $p$ 时,可以通过引入权重来修正期望的计算:
|
||||
|
||||
$$
|
||||
\mathbb{E}_{x \sim p} [f(x)] = \mathbb{E}_{x \sim q} \left[ \frac{p(x)}{q(x)} \cdot f(x) \right]
|
||||
$$
|
||||
|
||||
应用到 GRPO 的场景,修正后的损失函数为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{corrected}} = - \mathbb{E}_{y \sim \pi_{\text{vLLM}}} \left[ w(x, y) \cdot \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]
|
||||
$$
|
||||
|
||||
其中 $w(x, y)$ 是重要性采样权重,用于校正 vLLM 与训练模型之间的分布偏差
|
||||
|
||||
重要性采样权重可以在不同粒度上计算和应用:
|
||||
|
||||
1. **Token-Level**
|
||||
|
||||
每个 token 上计算重要性采样比:
|
||||
|
||||
$$
|
||||
w_{i,t}^{\text{token}} = \frac{\pi_\theta(y_{i,t}|x, y_{i,<t})}{\pi_{\text{vLLM}}(y_{i,t}|x, y_{i,<t})}
|
||||
$$
|
||||
|
||||
2. **Sequence-Level**
|
||||
|
||||
计算序列级别的重要性采样比,然后广播到每个 token:
|
||||
|
||||
$$
|
||||
w_i^{\text{seq}} = \left[ \frac{\pi_\theta(y_i|x)}{\pi_{\text{vLLM}}(y_i|x)} \right]^{\frac{1}{|y_i|}} = \exp\left( \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \log \frac{\pi_\theta(y_{i,t}|x, y_{i,<t})}{\pi_{\text{vLLM}}(y_{i,t}|x, y_{i,<t})} \right)
|
||||
$$
|
||||
|
||||
### 稳定性控制:Truncate vs. Mask
|
||||
|
||||
过大的重要性采样权重会导致梯度爆炸,破坏训练稳定性。因此需要对权重进行控制:
|
||||
|
||||
#### 1. Truncate(截断)
|
||||
|
||||
将重要性采样权重截断到 $[0, \tau]$ 区间:
|
||||
|
||||
$$
|
||||
w_{\text{truncate}} = \min(w, \tau)
|
||||
$$
|
||||
|
||||
该方法保留所有样本,但限制其影响范围。
|
||||
|
||||
#### 2. Mask(屏蔽)
|
||||
|
||||
舍弃权重超过阈值的 token/sequence 数据
|
||||
|
||||
$$
|
||||
w_{\text{mask}} = \begin{cases}
|
||||
w & \text{if } w \leq \tau \\
|
||||
0 & \text{otherwise}
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
|
||||
### 四种校正模式
|
||||
|
||||
结合粒度和控制策略,共设置四种校正模式(通过 `--rollout_importance_sampling_mode` 参数选择):
|
||||
|
||||
| 模式 | 说明 |
|
||||
|------|------|
|
||||
| `token_truncate` | Token 级截断 |
|
||||
| `token_mask` | Token 级屏蔽 |
|
||||
| `sequence_truncate` | Sequence 级截断 |
|
||||
| `sequence_mask` | Sequence 级屏蔽 |
|
||||
|
||||
其中阈值通过 `--rollout_importance_sampling_threshold` 参数设置。
|
||||
|
||||
## Metrics
|
||||
|
||||
为了监控训练中训推不一致的程度,我们在log中加入以下指标(前缀为 `rollout_correction/`):
|
||||
|
||||
### 1. KL 散度(KL Divergence)
|
||||
|
||||
KL 散度衡量 rollout 策略与训练策略之间的偏离程度。两个指标都估计 $\text{KL}(\pi_{\text{vLLM}} \| \pi_\theta)$
|
||||
|
||||
|
||||
**直接估计器 `kl`**:
|
||||
|
||||
$$
|
||||
\text{KL}(\pi_{\text{vLLM}} \| \pi_\theta) = \mathbb{E}_{\pi_{\text{vLLM}}}\left[ \log \frac{\pi_{\text{vLLM}}}{\pi_\theta} \right]
|
||||
$$
|
||||
|
||||
**K3 估计器 `k3_kl`**:
|
||||
|
||||
$$
|
||||
\text{KL}(\pi_{\text{vLLM}} \| \pi_\theta) \approx \mathbb{E}_{\pi_{\text{vLLM}}}\left[ \rho - \log \rho - 1 \right], \quad \rho = \frac{\pi_\theta}{\pi_{\text{vLLM}}}
|
||||
$$
|
||||
|
||||
K3 估计器在 KL 值较小时数值更稳定,且始终非负。
|
||||
|
||||
### 2. Perplexity (PPL)
|
||||
|
||||
困惑度衡量模型对序列的预测不确定性:
|
||||
|
||||
$$
|
||||
\text{PPL} = \exp\left( -\frac{1}{|y|} \sum_{t=1}^{|y|} \log p(y_t) \right)
|
||||
$$
|
||||
|
||||
相关指标:
|
||||
- `training_ppl` / `training_log_ppl`:训练策略的 PPL 及其对数
|
||||
- `rollout_ppl` / `rollout_log_ppl`:rollout 策略的 PPL 及其对数
|
||||
- `log_ppl_diff`:log PPL 差异,正值表示训练策略分配的概率更低
|
||||
- `log_ppl_abs_diff`:log PPL 绝对差异
|
||||
- `log_ppl_diff_max` / `log_ppl_diff_min`:log PPL 差异的最大/最小值
|
||||
- `ppl_ratio`:PPL 比率 $\frac{\text{PPL}_{\text{training}}}{\text{PPL}_{\text{rollout}}}$
|
||||
|
||||
### 3. χ² 散度(Chi-squared Divergence)
|
||||
|
||||
χ² 散度衡量重要性采样权重的方差:
|
||||
|
||||
$$
|
||||
\chi^2(\pi_\theta \| \pi_{\text{vLLM}}) = \mathbb{E}_{\pi_{\text{vLLM}}}\left[ \rho^2 \right] - 1, \quad \rho = \frac{\pi_\theta}{\pi_{\text{vLLM}}}
|
||||
$$
|
||||
|
||||
- `chi2_token`:Token 级别 χ² 散度,$\mathbb{E}[\rho_t^2] - 1$
|
||||
- `chi2_seq`:Sequence 级别 χ² 散度(基于几何平均),$\mathbb{E}[\rho_{\text{geo}}^2] - 1$,其中 $\rho_{\text{geo}} = \exp(\frac{1}{T}\sum_t \log \rho_t)$
|
||||
|
||||
χ² 散度越大,表示 IS 权重方差越大,训练越不稳定。`chi2_seq` 使用几何平均而非乘积,使其与 `chi2_token` 在量级上可比较。
|
||||
|
||||
### 4. Effective Sample Size (ESS)
|
||||
|
||||
有效样本大小衡量重要性采样后实际起作用的样本数量:
|
||||
|
||||
$$
|
||||
\text{ESS} = \frac{1}{\mathbb{E}\left[\left(\frac{w}{\mathbb{E}[w]}\right)^2\right]}
|
||||
$$
|
||||
|
||||
ESS 值越大(接近1),表示重要性采样权重分布越均匀,样本的有效利用率越高。当所有权重相等时(on-policy),ESS = 1;当权重差异很大时(严重 off-policy),ESS 会很小。
|
||||
|
||||
### 5. IS 权重统计
|
||||
|
||||
- `is_weight_mean`:平均重要性采样权重,理想值为 1.0
|
||||
- `clipped_frac`:被截断或屏蔽的样本比例
|
||||
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 仅记录诊断指标
|
||||
|
||||
如果只想监控训推不一致的程度,而不启用重要性采样校正,可以设置:
|
||||
|
||||
```
|
||||
--log_rollout_offpolicy_metrics true
|
||||
```
|
||||
|
||||
这将记录上述所有诊断指标(KL、PPL、χ² 等),但不会对损失函数进行任何修正。
|
||||
|
||||
### 启用重要性采样校正
|
||||
|
||||
在GRPO训练中,设置以下参数启用校正机制:
|
||||
|
||||
```
|
||||
--rollout_importance_sampling_mode (默认为None)
|
||||
--rollout_importance_sampling_threshold (默认为2)
|
||||
```
|
||||
|
||||
当设置了 `rollout_importance_sampling_mode` 时,诊断指标会自动记录,无需额外设置 `log_rollout_offpolicy_metrics`。
|
||||
|
||||
## Off-Policy Sequence Masking
|
||||
|
||||
除了重要性采样校正外,还可以使用 **Off-Policy Sequence Masking** 技术来处理训推不一致问题。该技术来自 [DeepSeek-V3.2 论文](https://arxiv.org/abs/2512.02556)。
|
||||
|
||||
### 原理
|
||||
|
||||
Off-Policy Sequence Masking 的核心思想是:当当前策略相对于旧策略(rollout 或 old policy)发生较大偏移时,直接丢弃(mask)该序列,不参与损失计算。这种方法特别针对**优势为负的序列**,因为这些序列在策略偏移较大时更容易导致训练不稳定。
|
||||
|
||||
具体来说,对于每个序列,计算:
|
||||
|
||||
$$
|
||||
\delta_i = \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \bigl( \log \pi_{\text{old}}(y_{i,t}|x, y_{i,<t}) - \log \pi_\theta(y_{i,t}|x, y_{i,<t}) \bigr)
|
||||
$$
|
||||
|
||||
当满足以下条件时,序列 $i$ 将被 mask 掉(平均只在 completion token 上计算,即 `completion_mask=1` 的位置):
|
||||
1. $\delta_i > \tau$
|
||||
2. **且** $\hat{A}_i < 0$
|
||||
|
||||
其中:
|
||||
- $\pi_{\text{old}}$ 优先使用 `rollout_per_token_logps`(rollout/行为策略的 logprobs),若不存在则使用 `old_per_token_logps`
|
||||
- $\tau$ 是用户设置的阈值(`--off_policy_sequence_mask_delta`,默认 None 表示关闭)
|
||||
|
||||
|
||||
参考资料
|
||||
|
||||
1. https://yingru.notion.site/When-Speed-Kills-Stability-Demystifying-RL-Collapse-from-the-Training-Inference-Mismatch-271211a558b7808d8b12d403fd15edda
|
||||
2. https://fengyao.notion.site/off-policy-rl
|
||||
3. https://github.com/volcengine/verl/blob/main/verl/trainer/ppo/rollout_corr_helper.py
|
||||
4. [DeepSeek-V3.2: Pushing the Frontier of Open Large Language Models](https://arxiv.org/abs/2512.02556)
|
||||
@@ -0,0 +1,35 @@
|
||||
# TreePO: Bridging the Gap of Policy Optimization and Efficacy and Inference Efficiency with Heuristic Tree-based Modeling
|
||||
|
||||
|
||||
作者: [li2zhi](https://github.com/li2zhi)
|
||||
|
||||
## 原理介绍
|
||||
[TreePO论文](https://arxiv.org/abs/2508.17445) 提出了一种树状结构建模方法。该方法将序列生成组织为分段式的树结构搜索,通过动态分支、回退与提前终止机制,显著提高KV缓存复用率,从而降低计算开销,同时保持甚至增强了探索的多样性。
|
||||
|
||||

|
||||
|
||||
## 实现细节
|
||||
[TreePO实现示例](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/treepo/tree_rollout_plugin.py)参考[官方实现](https://github.com/multimodal-art-projection/TreePO/blob/main/recipe/treepo/vllm_rollout_tree.py) 给出了 TreePO 训练插件的样例代码,涵盖了多轮交互、终止判断,与分支回退等相关逻辑。
|
||||
|
||||
**注意**:在实际使用中,你需要根据自己的场景需求,重写step、check_finished等方法的逻辑,以确保其能够在自定义场景下按照预期执行。而关于自定义奖励的设计与使用,你可以参考[DeepEyes](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/deepeyes/deepeyes_plugin.py)的实现。
|
||||
|
||||
训练参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/treepo/tree_rollout.sh)
|
||||
|
||||
## 测试数据
|
||||
> model: Qwen/Qwen2.5-0.5B
|
||||
> dataset: AI-MO/NuminaMath-TIR
|
||||
> subset size: 1,000 samples
|
||||
> 1 GPU for training, 1 GPU for inference
|
||||
|
||||
| \ | batch_size | num_generation | max_tree_depth | global_step | total inference calls | saving ratio | train_speed(iter/s) | improvement rate |
|
||||
| ----------------------- | ---------- | -------------- | -------------- | ----------- | --------------------- | ------------ | ------------------- | ---------------- |
|
||||
| original implementation | 8 | 8 | 4 | 200 | 5965 | 0.00% | 0.292436 | 0.00% |
|
||||
| tree(max_divergence=3) | 8 | 8 | 4 | 200 | 3678 | 38.34% | 0.31819 | 8.81% |
|
||||
| | | | | | | | | |
|
||||
| original implementation | 8 | 8 | 5 | 105 | 4312 | 0.00% | 0.261324 | 0.00% |
|
||||
| tree(max_divergence=2) | 8 | 8 | 5 | 105 | 2513 | 52.69% | 0.336639 | 28.82% |
|
||||
| tree(max_divergence=3) | 8 | 8 | 5 | 105 | 2990 | 30.66% | 0.308791 | 18.16% |
|
||||
| | | | | | | | | |
|
||||
| original implementation | 8 | 8 | 6 | 105 | 5202 | 0.00% | 0.24832 | 0.00% |
|
||||
| tree(max_divergence=2) | 8 | 8 | 6 | 105 | 3348 | 35.64% | 0.27755 | 11.77% |
|
||||
| tree(max_divergence=3) | 8 | 8 | 6 | 105 | 3888 | 25.26% | 0.272339 | 9.67% |
|
||||
@@ -0,0 +1,360 @@
|
||||
# GYM环境训练
|
||||
|
||||
GYM 风格的环境训练把"模型 → 环境 → 奖励"这条链路封装成一个抽象接口,让 LLM 像 Agent 一样与环境进行多轮交互,每一步的奖励直接由环境给出,无需再单独写 reward 函数从轨迹里反推。本文先介绍接口,再用一个完整的自定义示例(FrozenLake)说明如何接入训练。
|
||||
|
||||
## Gym 接口
|
||||
|
||||
GYM 源自 [Gymnasium库](https://github.com/Farama-Foundation/Gymnasium)。在 ms-swift 中我们定义了如下接口:
|
||||
|
||||
```python
|
||||
class Env(ABC):
|
||||
|
||||
def __init__(self, env_config):
|
||||
"""env_config 来自数据集每行的 env_config 列,可承载初始化参数"""
|
||||
self.env_config = env_config
|
||||
|
||||
@abstractmethod
|
||||
async def reset(self, config: RolloutInferRequest) -> Tuple[str, Dict[str, Any], str]:
|
||||
"""
|
||||
Returns:
|
||||
- observation: 作为首轮 user 消息发送给模型
|
||||
- info: 调试/日志信息,记录到 completions.jsonl
|
||||
- system_message: 本条轨迹的 system prompt
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def step(self, action: Messages) -> Tuple[str, float, bool, Dict[str, Any]]:
|
||||
"""
|
||||
Args:
|
||||
action: 截止当前的完整对话消息,最后一条即模型最新回复
|
||||
Returns:
|
||||
- next_observation: 下一轮 user 消息
|
||||
- reward: 当前 step 奖励
|
||||
- done: 轨迹是否结束
|
||||
- info: 调试/日志信息
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def close(self):
|
||||
"""释放资源"""
|
||||
pass
|
||||
```
|
||||
|
||||
`reset` 接收到的 `RolloutInferRequest` 包含数据集行的 `messages`、`data_dict`(额外列,包括 `env_config`)等。完整示例参见 [入参示例](./multi_turn.md#多轮规划器-multiturnscheduler)。
|
||||
|
||||
> 如果需要在每轮 rollout 之间额外控制对话历史(例如动态压缩、注入额外提示),推荐直接继承 `MultiTurnScheduler` 并实现 `on_trajectory_start` / `on_turn_end` hook,或重写 `step` / `run` 方法,详见[多轮训练文档](./multi_turn.md#自定义多轮交互逻辑)。
|
||||
|
||||
## 启动训练
|
||||
|
||||
使用内置的 [gym_scheduler](https://github.com/modelscope/ms-swift/blob/main/swift/rollout/multi_turn.py) 把 env 串到多轮 rollout 中。
|
||||
|
||||
`GYMScheduler` 基于通用 hook 协议实现:
|
||||
- 继承 `MultiTurnScheduler`,无需自定义 `run` 方法
|
||||
- 实现 `on_trajectory_start`(调用 `env.reset`)和 `on_turn_end`(调用 `env.step`)
|
||||
- 同时适用于 server mode(`run()`)和 colocate mode(`run_multi_turn()`)
|
||||
|
||||
用户自定义的 env 通过 `--external_plugins your_plugin.py` 加载,plugin 里执行 `envs['my_env'] = MyEnv` 完成注册(下文 FrozenLake 示例完整演示)。
|
||||
|
||||
**Colocate 模式**:
|
||||
|
||||
```bash
|
||||
megatron rlhf \
|
||||
--rlhf_type grpo \
|
||||
--vllm_mode colocate \
|
||||
--external_plugins examples/megatron/grpo/multi_turn/frozen_lake_plugin.py \
|
||||
--multi_turn_scheduler gym_scheduler \
|
||||
--gym_env frozen_lake \
|
||||
--use_gym_env true \
|
||||
--max_turns 10 \
|
||||
...
|
||||
|
||||
# swift rlhf 同理
|
||||
```
|
||||
|
||||
|
||||
**Server 模式**
|
||||
|
||||
```bash
|
||||
swift rollout \
|
||||
--model xxx \
|
||||
--use_gym_env true \
|
||||
--external_plugins examples/megatron/grpo/multi_turn/frozen_lake_plugin.py \
|
||||
--multi_turn_scheduler gym_scheduler \
|
||||
--gym_env frozen_lake \
|
||||
--max_turns 10
|
||||
|
||||
# trainer 侧需要加 --vllm_server_pass_dataset true,把 env_config 等额外列透传给 rollout 端
|
||||
megatron rlhf --vllm_mode server --vllm_server_pass_dataset true ...
|
||||
# or swift rlhf --vllm_mode server --vllm_server_pass_dataset true ...
|
||||
```
|
||||
|
||||
|
||||
环境选择有两种方式:
|
||||
- 通过 `--gym_env env_name` 全局指定(同一脚本里所有 prompt 共用一个 env);
|
||||
- 在每行数据的 `env_config.name` 中指定(适用于多环境混合场景,每条数据可指向不同 env,会覆盖 `--gym_env`)。
|
||||
|
||||
## 示例:从零写一个 FrozenLake 环境
|
||||
|
||||
<img src="https://gymnasium.farama.org/_images/frozen_lake.gif" width="220" alt="FrozenLake 环境示意图(来源:Gymnasium 官方文档)" />
|
||||
|
||||
[FrozenLake](https://gymnasium.farama.org/environments/toy_text/frozen_lake/) 是 OpenAI Gym 中的经典任务:智能体从起点出发,需要穿过一片冰湖到达终点,途中要避开冰窟。原始环境如上图所示。下面以纯文本版本(把上图网格直接渲染成 ASCII 字符)为例。
|
||||
|
||||
以下完整代码参考完整代码:[frozen_lake_plugin](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/grpo/multi_turn/frozen_lake_plugin.py)。
|
||||
|
||||
**1. 定义 Env**
|
||||
|
||||
每条数据派生一张随机 4x4 地图(随机洞 + 随机 S/G 位置,BFS 校验保证可解)。单元含义:`S` 起点 / `G` 终点 / `H` 冰窟(踩到=失败)/ `F` 安全冰面 / `P` 玩家当前位置。
|
||||
|
||||
```python
|
||||
class FrozenLakeEnv(Env):
|
||||
def __init__(self, env_config):
|
||||
super().__init__(env_config)
|
||||
self.size = int(env_config.get('size', 4))
|
||||
self.p = float(env_config.get('p', 0.8))
|
||||
seed = env_config.get('seed')
|
||||
self.seed = int(seed) if seed is not None else None
|
||||
|
||||
async def reset(self, config: RolloutInferRequest):
|
||||
self.grid = generate_random_map(size=self.size, p=self.p, seed=self.seed)
|
||||
...
|
||||
return observation, {'seed': self.seed}, SYSTEM_PROMPT
|
||||
|
||||
async def step(self, action: Messages):
|
||||
move = _parse_action(action[-1]['content']) # <action>up|down|left|right</action>
|
||||
# 推进一格、判断 G / H;外层 max_turns 由 scheduler 兜底
|
||||
if cell == 'G': return obs, 1.0, True, {'status': 'goal'}
|
||||
if cell == 'H': return obs, 0.0, True, {'status': 'hole'}
|
||||
...
|
||||
```
|
||||
|
||||
**2. GYMScheduler 的 hook 实现**
|
||||
|
||||
框架内置的 `GYMScheduler` 基于多轮 hook 完成了控制逻辑:
|
||||
|
||||
```python
|
||||
class GYMScheduler(MultiTurnScheduler):
|
||||
def on_trajectory_start(self, requests):
|
||||
# 为每个请求创建 env,调用 env.reset,注入初始 observation
|
||||
for req in requests:
|
||||
env = self._create_env(req.data_dict.get('env_config', {}))
|
||||
observation, info, system_message = env.reset(req)
|
||||
req.messages = [system_msg, user_msg(observation)]
|
||||
self._envs[req.uuid] = env
|
||||
|
||||
def on_turn_end(self, req, response_choice, current_turn):
|
||||
# 调用 env.step,累积 reward,返回 done + rollout_infos
|
||||
next_obs, reward, done, info = env.step(deepcopy(req.messages))
|
||||
self._total_rewards[req.uuid] += reward
|
||||
return {
|
||||
'done': done,
|
||||
'rollout_infos': {
|
||||
'total_reward': self._total_rewards[req.uuid],
|
||||
'step_rewards': [...],
|
||||
}
|
||||
}
|
||||
|
||||
def step(self, req, response_choice, current_turn):
|
||||
# 注入下一帧 observation 到 user message
|
||||
if self._pending_obs.get(req.uuid):
|
||||
req.messages.append({'role': 'user', 'content': next_obs})
|
||||
return {'infer_request': req}
|
||||
```
|
||||
|
||||
用户只需实现 Env 接口,无需关心多轮控制细节。
|
||||
|
||||
**3. 注册**
|
||||
|
||||
将 env 类挂到 swift 的 `envs` 注册表里。`--external_plugins` 在训练启动时会 import 该文件,注册随之生效:
|
||||
|
||||
```python
|
||||
# examples/megatron/grpo/multi_turn/frozen_lake_plugin.py
|
||||
from swift.rollout.gym_env import Env, envs
|
||||
|
||||
class FrozenLakeEnv(Env):
|
||||
...
|
||||
|
||||
envs['frozen_lake'] = FrozenLakeEnv
|
||||
```
|
||||
|
||||
**4. 准备数据集**
|
||||
|
||||
数据集在这里仅作占位符处理,数据构造由环境生成,和 `env_config.seed`来控制地图生成的随机性:
|
||||
|
||||
```json
|
||||
{"messages":[{"role":"user","content":"<placeholder>"}],"env_config":{"seed":0}}
|
||||
{"messages":[{"role":"user","content":"<placeholder>"}],"env_config":{"seed":1}}
|
||||
...
|
||||
{"messages":[{"role":"user","content":"<placeholder>"}],"env_config":{"seed":127}}
|
||||
```
|
||||
|
||||
**5. (可选)叠加自定义 reward**
|
||||
|
||||
设置 `--use_gym_env true` 后,env 给出的 `total_reward` 会自动作为一路奖励参与训练,无需再写 reward 函数。如果想在此之外再叠加自定义信号(如格式/长度等),通过 `--reward_funcs` 传入即可,gym 奖励会作为额外一列与 reward_funcs 拼在一起,由 `--reward_weights` 统一加权。例如同时启用一个格式校验 reward:
|
||||
|
||||
```bash
|
||||
megatron rlhf ... --use_gym_env true --reward_funcs format --reward_weights 0.2 1.0
|
||||
# reward_weights 末位对应 gym 的 total_reward
|
||||
```
|
||||
|
||||
**6. 训练**
|
||||
|
||||
运行脚本参考:[`examples/megatron/grpo/multi_turn/frozen_lake.sh`](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/grpo/multi_turn/frozen_lake.sh)
|
||||
|
||||
|
||||
## OpenEnv 环境训练
|
||||
|
||||
[OpenEnv](https://github.com/huggingface/openenv) 是 HuggingFace 开源的 Agentic RL 环境框架,通过 WebSocket 与环境服务器交互。与上文 FrozenLake 的本地 `Env` 接口不同,OpenEnv 将环境逻辑放在独立的服务进程中,swift 通过 `OpenEnvScheduler` + `OpenEnvWrapper` 与之通信。
|
||||
|
||||
### 架构对比
|
||||
|
||||
| 特性 | 内置 Gym (`GYMScheduler`) | OpenEnv (`OpenEnvScheduler`) |
|
||||
|------|--------------------------|------------------------------|
|
||||
| 环境运行位置 | 训练进程内(Python 对象) | 独立服务器(WebSocket 通信) |
|
||||
| 环境接口 | 继承 `Env`,实现 `reset/step/close` | 服务器提供 HTTP/WebSocket API |
|
||||
| 注册方式 | `--external_plugins` + `envs` 注册表 | `--external_plugins` + `multi_turns` 注册表 |
|
||||
| 适用场景 | 轻量本地环境(FrozenLake 等) | 复杂服务端环境(TextArena、CARLA 等) |
|
||||
| 并发控制 | 无需 | 内置 Semaphore 限制并发连接 |
|
||||
|
||||
### OpenEnvScheduler
|
||||
|
||||
`OpenEnvScheduler` 继承 `GYMScheduler`,将本地 `Env` 替换为 `OpenEnvWrapper`(WebSocket 客户端)。核心设计:
|
||||
|
||||
- **`_create_env`**:创建 `OpenEnvWrapper`,连接 OpenEnv 服务器
|
||||
- **`on_trajectory_start`**:为每个请求创建 wrapper,调用 `reset()`,用 Semaphore 限制并发(默认 4)
|
||||
- **`on_turn_end`**:解析模型输出,调用 `wrapper.step()`,累积奖励
|
||||
- **`parse_action`**(可覆盖):将模型文本解析为 action dict,默认 `json.loads`
|
||||
- **`format_observation`**(可覆盖):将服务器返回的 observation 格式化为字符串,默认 `json.dumps`
|
||||
|
||||
用户通过继承 `OpenEnvScheduler` 并覆盖 `parse_action`、`format_observation`、`on_trajectory_start`、`on_turn_end` 来适配具体环境。
|
||||
|
||||
### 示例:Sudoku 环境
|
||||
|
||||
以 TextArena Sudoku 为例,模型需要通过 `[row col number]` 格式下棋,在 9x9 数独棋盘上填入数字。完整代码参考:[sudoku_scheduler.py](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/openenv/sudoku_scheduler.py)。
|
||||
|
||||
**1. 启动 OpenEnv 服务器**
|
||||
|
||||
安装 OpenEnv 和 Sudoku 环境包(textarena 和 nltk 会作为依赖自动安装):
|
||||
|
||||
```bash
|
||||
pip install openenv
|
||||
pip install git+https://huggingface.co/spaces/openenv/sudoku
|
||||
```
|
||||
|
||||
使用提供的启动脚本启动本地服务器(默认端口 8000)。`MAX_CONCURRENT_ENVS` 需 ≥ 训练时的 `num_generations`:
|
||||
|
||||
```bash
|
||||
TEXTARENA_ENV_ID=Sudoku-v0 MAX_CONCURRENT_ENVS=8 python examples/train/grpo/plugin/openenv/start_sudoku_server.py
|
||||
```
|
||||
|
||||
数据集中将 `base_url` 指向本地服务器地址:
|
||||
|
||||
```json
|
||||
{"messages":[{"role":"user","content":"Play"}],"env_config":{"name":"openenv","base_url":"http://127.0.0.1:8000"}}
|
||||
```
|
||||
|
||||
**2. 自定义 Scheduler**
|
||||
|
||||
继承 `OpenEnvScheduler`,实现 Sudoku 专用的动作解析、观察格式化和多组件奖励:
|
||||
|
||||
```python
|
||||
from swift.rollout.multi_turn import OpenEnvScheduler
|
||||
|
||||
class SudokuScheduler(OpenEnvScheduler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._last_content_len = {} # 内容差分跟踪
|
||||
|
||||
async def on_trajectory_start(self, requests):
|
||||
# 创建环境、解析棋盘、生成 hints
|
||||
# hints 包括「保证正确的走法」和候选数字列表
|
||||
...
|
||||
|
||||
async def on_turn_end(self, infer_request, response_choice, current_turn):
|
||||
# 解析 [row col number],step 环境
|
||||
# 计算 5 路奖励:空格选择 / 合法移动 / 重复惩罚 / 进度 / 正确
|
||||
# 返回更新后的棋盘 + hints 作为下一轮观察
|
||||
...
|
||||
|
||||
def parse_action(self, text):
|
||||
import re
|
||||
match = re.search(r'\[\s*(\d+)\s+(\d+)\s+(\d+)\s*\]', text)
|
||||
if match:
|
||||
row, col, num = match.groups()
|
||||
return {"message": f"[{row} {col} {num}]"}
|
||||
return {"message": "[1 1 1]"}
|
||||
```
|
||||
|
||||
**多组件奖励系统**(参考 [TRL Sudoku 示例](https://github.com/huggingface/trl/blob/main/examples/notebooks/openenv_sudoku_grpo.ipynb)):
|
||||
|
||||
| 奖励组件 | 计算方式 | 作用 |
|
||||
|---------|---------|------|
|
||||
| `empty_cell_reward` | 目标是空格 +1 / 覆盖已有 -1 | 引导模型选择合法位置 |
|
||||
| `valid_move_reward` | 合法新走法 +1 / 警告 -0.5 / 无效 0 | 鼓励合法操作 |
|
||||
| `repetition_reward` | 重复走法指数惩罚(-2^n,上限 -10) | 避免重复 |
|
||||
| `progress_reward` | (已填充 - 初始) / (81 - 初始) | 衡量解题进度 |
|
||||
| `correct_reward` | 环境返回的二值奖励 | 完全解出 |
|
||||
|
||||
组合奖励 = 各组件均值之和,提供比单一二值奖励更密集的学习信号。
|
||||
|
||||
**3. Hints 系统**
|
||||
|
||||
每轮交互中,scheduler 解析当前棋盘状态,为模型提供提示:
|
||||
|
||||
- **GUARANTEED MOVES**:只有一个候选数字的空格(可直接填入)
|
||||
- **Other options**:2-3 个候选数字的空格
|
||||
- **MOVES ALREADY TRIED**:已尝试过的走法(避免重复)
|
||||
|
||||
**4. 准备数据集**
|
||||
|
||||
数据集仅作占位符,实际棋盘由环境服务器生成。`base_url` 指向 OpenEnv 托管地址:
|
||||
|
||||
```json
|
||||
{"messages":[{"role":"user","content":"Play"}],"env_config":{"name":"openenv","base_url":"http://127.0.0.1:8000"}}
|
||||
```
|
||||
|
||||
**5. 注册 Scheduler**
|
||||
|
||||
`sudoku_scheduler.py` 末尾已包含注册代码,通过 `--external_plugins` 加载即可:
|
||||
|
||||
```python
|
||||
# sudoku_scheduler.py 末尾
|
||||
from swift.rollout.multi_turn import multi_turns
|
||||
multi_turns['sudoku_scheduler'] = SudokuScheduler
|
||||
```
|
||||
|
||||
**6. 启动训练**
|
||||
|
||||
```bash
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen3.5-4B \
|
||||
--dataset examples/train/grpo/plugin/openenv/sudoku.jsonl \
|
||||
--external_plugins examples/train/grpo/plugin/openenv/sudoku_scheduler.py \
|
||||
--enable_thinking false \
|
||||
--max_completion_length 256 \
|
||||
--use_gym_env true \
|
||||
--multi_turn_scheduler sudoku_scheduler \
|
||||
--max_turns 20 \
|
||||
--use_vllm true \
|
||||
--vllm_mode colocate \
|
||||
...
|
||||
```
|
||||
|
||||
运行脚本参考:[`examples/train/grpo/plugin/openenv/run_grpo_sudoku.sh`](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/openenv/run_grpo_sudoku.sh)
|
||||
|
||||
### 注意事项
|
||||
|
||||
- **vLLM 模式**:以上示例使用 `--vllm_mode colocate`,vLLM 与训练共享 GPU。若使用 `--vllm_mode server`,需额外启动 `swift rollout` 作为 vLLM 服务器,且 `--multi_turn_scheduler` 和 `--max_turns` 参数应传给 `swift rlhf` 而非 `swift rollout`。
|
||||
- **并发会话数**:`start_sudoku_server.py` 的 `MAX_CONCURRENT_ENVS` 需 ≥ 训练时的 `num_generations`。默认的 `python -m textarena_env.server.app` 只支持 1 个并发会话。
|
||||
- **enable_thinking**:Sudoku 等环境不需要 CoT 推理,建议设置 `--enable_thinking false` 以减少 token 消耗。
|
||||
- **同步 I/O**:`OpenEnvWrapper` 的 `reset()`/`step()` 是同步 WebSocket 调用。`OpenEnvScheduler` 的子类应使用 `asyncio.to_thread()` 包装这些调用以避免阻塞事件循环。
|
||||
|
||||
|
||||
参考资料:
|
||||
|
||||
- https://gymnasium.farama.org/environments/toy_text/frozen_lake/
|
||||
- https://github.com/alibaba/ROLL/tree/main/roll/pipeline/agentic/env/frozen_lake
|
||||
- https://github.com/huggingface/openenv
|
||||
- https://github.com/huggingface/trl/blob/main/examples/notebooks/openenv_sudoku_grpo.ipynb
|
||||
@@ -0,0 +1,11 @@
|
||||
Developer Guide
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
loss_types.md
|
||||
multi_turn.md
|
||||
multi_task.md
|
||||
reward_function.md
|
||||
reward_model.md
|
||||
gym_env.md
|
||||
@@ -0,0 +1,131 @@
|
||||
# Loss Types
|
||||
|
||||
GRPO训练支持多种不同的loss类型,主要区别在于归一化的维度和梯度处理方式上有所不同。
|
||||
|
||||
## 损失函数
|
||||
|
||||
token 级别上,GRPO 训练使用以下损失函数
|
||||
|
||||
$$\mathcal{L}_{i,t} = -\min\left(\rho_{i,t} A_{i,t}, \text{clip}(\rho_{i,t}, 1-\epsilon, 1+\epsilon) A_{i,t}\right)$$
|
||||
|
||||
当设置`loss_type cispo`时,使用 cispo 损失
|
||||
|
||||
$$\mathcal{L}_{i,t}^{\text{CISPO}} = -\text{detach}\left(\min(\rho_{i,t}, \epsilon_{\text{high}})\right) \cdot A_{i,t} \cdot \log \pi_\theta(y_{i,t}|y_{i,<t})$$
|
||||
|
||||
当设置`loss_type sapo`时,使用软门控替代硬裁剪,详见 [SAPO](../AdvancedResearch/SAPO.md)
|
||||
|
||||
$$\mathcal{L}_{i,t}^{\text{SAPO}} = -g_{i,t} \cdot A_{i,t}$$
|
||||
|
||||
其中 $g_{i,t} = \sigma(\tau \cdot (\rho_{i,t} - 1))$ 是温度控制的软门控函数。
|
||||
|
||||
其中:
|
||||
- $\rho_{i,t} = \frac{\pi_\theta(y_{i,t}|y_{i,<t})}{\pi_{\theta_{\text{old}}}(y_{i,t}|y_{i,<t})}$ 是重要性采样权重
|
||||
- $A_{i,t}$ 是优势函数
|
||||
- $\epsilon$ 和 $\epsilon_{\text{high}}$ 是clipping参数
|
||||
- $\text{detach}(\cdot)$ 表示该项不参与梯度计算
|
||||
- $\sigma(\cdot)$ 是 sigmoid 函数,$\tau$ 是温度参数
|
||||
|
||||
## GRPO
|
||||
|
||||
`--loss_type grpo`
|
||||
|
||||
GRPO是标准的损失函数实现,对每个样本的token-level损失取平均,然后对所有样本取平均。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{GRPO}} = \frac{1}{N} \sum_{i=1}^{N} \frac{1}{T_i} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
|
||||
**归一化维度:** 样本维度(先对每个样本的所有token取平均,再对所有样本取平均)
|
||||
|
||||
## BNPO (Batch Normalized Policy Optimization)
|
||||
|
||||
`--loss_type bnpo`
|
||||
|
||||
BNPO将所有样本的所有token的损失直接求和,然后除以所有completion token的总数量。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{BNPO}} = \frac{\sum_{i=1}^{N} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}}{\sum_{i=1}^{N} T_i}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
|
||||
**归一化维度:** Token维度(对所有completion token取平均)
|
||||
|
||||
## DR-GRPO
|
||||
|
||||
`--loss_type dr_grpo`
|
||||
|
||||
DR-GRPO将所有样本的所有token的损失求和,然后除以批次大小乘以最大completion长度。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{DR-GRPO}} = \frac{\sum_{i=1}^{N} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}}{N \times L_{\text{max}}}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
- $L_{\text{max}}$ 是最大completion长度
|
||||
|
||||
**归一化维度:** 固定维度(批次大小 × 最大completion长度)
|
||||
|
||||
## CISPO
|
||||
|
||||
`--loss_type cispo`
|
||||
|
||||
CISPO损失按所有进程的completion token总数进行归一化。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{CISPO}} = \frac{\sum_{i=1}^{N} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}^{\text{CISPO}}}{\sum_{\text{all processes}} \sum_{i=1}^{N_p} T_{p,i}}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是当前进程批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
- $N_p$ 是第$p$个进程的样本数量
|
||||
|
||||
**归一化维度:** 全局token维度(跨所有进程的completion token总数)
|
||||
|
||||
## DAPO
|
||||
|
||||
`--loss_type dapo`
|
||||
|
||||
DAPO与BNPO类似,使用token-level归一化,但基于全局数据(多进程)进行归一化。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{DAPO}} = \frac{\sum_{i=1}^{N} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}}{\sum_{\text{all processes}} \sum_{i=1}^{N_p} T_{p,i}}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是当前进程批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
- $N_p$ 是第$p$个进程的样本数量
|
||||
|
||||
**归一化维度:** 全局token维度(跨所有进程的completion token总数)
|
||||
|
||||
## FIPO
|
||||
|
||||
`--loss_type fipo`
|
||||
|
||||
FIPO 在 DAPO/GRPO 的 clipped policy loss 上引入 Future-KL influence weight。每个 token 的序列级 advantage 会乘以从当前位置到后续 token 的折扣累积 KL 位移得到的权重:
|
||||
|
||||
$$f_{i,t} = \text{clip}\left(\exp\left(\sum_{k=t}^{T_i} \gamma^{k-t} M_{i,k} \Delta \log p_{i,k}\right), 1-\epsilon_f, 1+\epsilon_f\right)$$
|
||||
|
||||
$$\mathcal{L}_{i,t}^{\text{FIPO}} = f_{i,t} \cdot \mathcal{L}_{i,t}$$
|
||||
|
||||
FIPO 的 influence weight 默认不参与梯度计算,并使用与 DAPO 相同的全局 token 归一化。
|
||||
|
||||
**归一化维度:** 全局 token 维度(所有进程的 completion token 总数)
|
||||
|
||||
## SAPO
|
||||
|
||||
`--loss_type sapo`
|
||||
|
||||
SAPO使用温度控制的软门控替代硬裁剪,实现平滑的梯度衰减。归一化方式与GRPO相同。
|
||||
|
||||
详细说明请参考 [SAPO](../AdvancedResearch/SAPO.md)
|
||||
@@ -0,0 +1,51 @@
|
||||
# 多任务训练
|
||||
我们可以在数据集中添加一个用于标识任务类型的列,并在奖励函数/奖励模型插件中根据任务类型进行判断,从而实现多任务训练。假设数据集中包含数学和编程任务,比如:
|
||||
|
||||
```
|
||||
{"query": "Solve the equation x + 2 = 5", "solution": "3", "task": "math"},
|
||||
{"query": "Write a function to calculate the Fibonacci sequence", "solution": "xxx", "task": "code"},
|
||||
{"query": "What is the integral of x^2?", "solution": "xxx", "task": "math"},
|
||||
{"query": "Implement a sorting algorithm in Python", "solution": "xxx", "task": "code"},
|
||||
```
|
||||
|
||||
我们可以设置不同的奖励函数来分别处理数学数据和代码数据,注意数据集中的列会传入奖励函数,所以我们可以通过 `task` 列
|
||||
|
||||
下面是针对不同任务的奖励函数的示例:
|
||||
|
||||
```python
|
||||
from swift.rewards import ORM, orms
|
||||
import random
|
||||
|
||||
# Math-specific reward function
|
||||
class MathRandomReward(ORM):
|
||||
def __call__(self, completions, task, **kwargs):
|
||||
rewards = []
|
||||
for completion, t in zip(completions, task):
|
||||
if t == "math":
|
||||
import random
|
||||
# imple math accuracy logic
|
||||
reward = random.random()
|
||||
rewards.append(reward)
|
||||
else:
|
||||
# Return None for non-math tasks
|
||||
rewards.append(None)
|
||||
return rewards
|
||||
|
||||
# Coding-specific reward function
|
||||
class CodeRandomReward(ORM):
|
||||
def __call__(self, completions, task, **kwargs):
|
||||
rewards = []
|
||||
for prompt, completion, t in zip(prompts, completions, task):
|
||||
if t == "code":
|
||||
# imple coding accuracy logic
|
||||
reward = random.random()
|
||||
rewards.append(reward)
|
||||
else:
|
||||
# Return None for non-coding tasks
|
||||
rewards.append(None)
|
||||
return rewards
|
||||
|
||||
orms['math_reward'] = MathRandomReward
|
||||
orms['code_reward'] = CodeRandomReward
|
||||
```
|
||||
对于非当前任务的数据, 通过返回 None 来处理,从而使得奖励相关仅计算任务内的数据。
|
||||
@@ -0,0 +1,312 @@
|
||||
# 多轮训练
|
||||
|
||||
在强化学习训练场景中,模型采样可能需要与环境进行多轮交互(如工具调用)。这种交互式训练要求模型能够根据环境反馈信息进行连续推理。本文档将详细介绍如何在 GRPO 训练中自定义多轮训练流程。
|
||||
|
||||
> GKD 同样支持多轮训练,与 GRPO 共享同一套 `MultiTurnScheduler` 基础设施。
|
||||
|
||||
以下是多轮训练示例图,模型可能涉及多轮 rollout,包括环境交互、工具调用等步骤:
|
||||
|
||||

|
||||
|
||||
## 多轮规划器 MultiTurnScheduler
|
||||
|
||||
`MultiTurnScheduler` 是一个抽象基类,提供了默认的多轮对话管理逻辑,其工作流程如下图所示:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/multiturn_pipeline.png " width="300" />
|
||||
|
||||
多轮规划器主要承担两大核心功能:
|
||||
- **终止条件判断**:通过 `check_finished` 方法判断当前轮次推理是否应该结束
|
||||
- **推理请求构造**:通过 `step` 方法构建下一轮推理的请求对象
|
||||
|
||||
抽象基类 `MultiTurnScheduler` 的核心方法如下:
|
||||
|
||||
```python
|
||||
class MultiTurnScheduler(ABC):
|
||||
|
||||
def __init__(self, max_turns: Optional[int] = None, *args, **kwargs):
|
||||
self.max_turns = max_turns
|
||||
|
||||
def on_trajectory_start(self, requests: List['RolloutInferRequest']) -> None:
|
||||
"""在首轮推理前调用,用于初始化轨迹级别状态。
|
||||
|
||||
可在此方法中直接修改 requests(如注入环境初始 observation)。
|
||||
默认实现为空(no-op)。
|
||||
"""
|
||||
pass
|
||||
|
||||
def on_turn_end(self, infer_request: 'RolloutInferRequest',
|
||||
response_choice: 'ChatCompletionResponseChoice',
|
||||
current_turn: int) -> Dict[str, Any]:
|
||||
"""在 assistant 消息追加后、check_finished 前调用。
|
||||
|
||||
用于推进环境状态(如 env.step)并返回每轮元数据。
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 可选包含以下键:
|
||||
- 'done' (bool): 若存在,将覆盖 check_finished 的结果
|
||||
- 'rollout_infos' (dict): 合并到轨迹累积的额外信息中
|
||||
默认返回空字典(no-op)。
|
||||
"""
|
||||
return {}
|
||||
|
||||
def step(self, infer_request: 'RolloutInferRequest', response_choice: 'ChatCompletionResponseChoice',
|
||||
current_turn: int) -> Dict:
|
||||
"""
|
||||
处理对话轮次之间的转换。
|
||||
|
||||
Args:
|
||||
infer_request: 当前推理请求
|
||||
response_choice: 当前轮次的响应
|
||||
current_turn: 当前轮次数
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 包含推理结果的字典,结构如下:
|
||||
- infer_request (必需): 下一轮的推理请求对象
|
||||
- response_token_ids (可选): 每个 rollout 轮次的响应 token IDs
|
||||
- response_loss_mask (可选): 每个 rollout 轮次响应的损失掩码
|
||||
- rollout_logprobs (可选): 每个 rollout 轮次的响应对应的 logps
|
||||
- rollout_infos (可选): 额外信息数据
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def check_finished(self, infer_request: 'RolloutInferRequest', response_choice: 'ChatCompletionResponseChoice',
|
||||
current_turn: int) -> bool:
|
||||
"""
|
||||
检查多轮 rollout 是否应该结束的默认终止逻辑。
|
||||
|
||||
默认终止条件:
|
||||
1. 当响应达到长度限制时 (finish_reason == 'length')
|
||||
2. 当对话达到最大轮数时 (如果设置了 max_turns)
|
||||
|
||||
Args:
|
||||
infer_request: 推理请求对象
|
||||
response_choice: 包含生成结果的响应选择,包括 finish_reason
|
||||
current_turn: 当前对话轮数
|
||||
|
||||
Returns:
|
||||
bool: True 表示终止对话,False 表示继续
|
||||
"""
|
||||
if response_choice.finish_reason == 'length':
|
||||
return True
|
||||
if self.max_turns and current_turn >= self.max_turns:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
`step` 和 `check_finished` 方法接收的参数说明:
|
||||
- **infer_request**: 当前的推理请求
|
||||
- **response_choice**: 当前轮次的推理结果
|
||||
- **current_turn**: 当前推理轮次(从 1 开始)
|
||||
|
||||
<details><summary>入参示例(点击展开)</summary>
|
||||
|
||||
```python
|
||||
infer_request
|
||||
"""
|
||||
RolloutInferRequest(
|
||||
messages=[
|
||||
{'role': 'system', 'content': 'A conversation between User and Assistant. The user asks a question, and the Assistant solves it. The assistant first thinks about the reasoning process in the mind and then provides the user with the answer. The reasoning process and answer are enclosed within <think> </think> and <answer> </answer> tags, respectively, i.e., <think> reasoning process here </think><answer> answer here </answer>\n'}, {'role': 'user', 'content': 'What is the value of $\\sqrt{36 \\times \\sqrt{16}}$?'},
|
||||
{'role': 'assistant', 'content': 'To find the value of \\(\\sqrt{36 \\times \\sqrt{16}}\\), we will break down the problem step-by-step.\n\nFirst, we need to evaluate the inner square root:\n\\[\n\\sqrt{16}\n\\]\nWe know that:\n\\[\n4^2 = 16 \\implies \\sqrt{16} = 4\n\\]\n\nNext, we substitute this result back into the original expression:\n\\[\n\\sqrt{36 \\times \\sqrt{16}} = \\sqrt{36 \\times 4}\n\\]\n\nNow, we need to evaluate the product inside the square root:\n\\[\n36 \\times 4 = 144\n\\]\n\nSo, the expression simplifies to:\n\\[\n\\sqrt{144}\n\\]\n\nFinally, we determine the square root of 144:\n\\[\n\\sqrt{144} = 12\n\\]\n\nThus, the value of \\(\\sqrt{36 \\times \\sqrt{16}}\\) is:\n\\[\n\\boxed{12}\n\\]'}
|
||||
],
|
||||
images=[],
|
||||
audios=[],
|
||||
videos=[],
|
||||
tools=None,
|
||||
objects={},
|
||||
data_dict={
|
||||
'problem': 'What is the value of $\\sqrt{36 \\times \\sqrt{16}}$?',
|
||||
'solution': "To solve the problem, we need to evaluate the expression \\(\\sqrt{36 \\times \\sqrt{16}}\\).\n\nWe can break down the steps as follows:\n\n1. Evaluate the inner square root: \\(\\sqrt{16}\\).\n2. Multiply the result by 36.\n3. Take the square root of the product obtained in step 2.\n\nLet's compute this step by step using Python code for accuracy.\n```python\nimport math\n\n# Step 1: Evaluate the inner square root\ninner_sqrt = math.sqrt(16)\n\n# Step 2: Multiply the result by 36\nproduct = 36 * inner_sqrt\n\n# Step 3: Take the square root of the product\nfinal_result = math.sqrt(product)\nprint(final_result)\n```\n```output\n12.0\n```\nThe value of \\(\\sqrt{36 \\times \\sqrt{16}}\\) is /\\(\\boxed{12}\\)."
|
||||
}
|
||||
)
|
||||
"""
|
||||
response_choice
|
||||
"""
|
||||
ChatCompletionResponseChoice(
|
||||
index=0,
|
||||
message=ChatMessage(
|
||||
role='assistant',
|
||||
content='To find the value of \\(\\sqrt{36 \\times \\sqrt{16}}\\), we will break down the problem step-by-step.\n\nFirst, we need to evaluate the inner square root:\n\\[\n\\sqrt{16}\n\\]\nWe know that:\n\\[\n4^2 = 16 \\implies \\sqrt{16} = 4\n\\]\n\nNext, we substitute this result back into the original expression:\n\\[\n\\sqrt{36 \\times \\sqrt{16}} = \\sqrt{36 \\times 4}\n\\]\n\nNow, we need to evaluate the product inside the square root:\n\\[\n36 \\times 4 = 144\n\\]\n\nSo, the expression simplifies to:\n\\[\n\\sqrt{144}\n\\]\n\nFinally, we determine the square root of 144:\n\\[\n\\sqrt{144} = 12\n\\]\n\nThus, the value of \\(\\sqrt{36 \\times \\sqrt{16}}\\) is:\n\\[\n\\boxed{12}\n\\]', tool_calls=None),
|
||||
finish_reason='stop',
|
||||
logprobs=None,
|
||||
messages=None)
|
||||
"""
|
||||
# response_choice.messages will be copied at the end of multi-turn inference.
|
||||
```
|
||||
</details>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
默认的 `check_finished` 逻辑会在以下两种情况下停止推理:
|
||||
- 模型回复被截断,即超出了 `max_completion_length`
|
||||
- 模型推理轮数超出了限制的最大轮数
|
||||
|
||||
完整的默认多轮 rollout 逻辑请参考该类的 `run` 方法,我们也可以通过重载`run` 方法来实现自定义多轮逻辑。
|
||||
|
||||
## 设置多轮训练参数
|
||||
|
||||
在 swift rollout 命令中,设置 multi_turn_scheduler 参数指定规划器
|
||||
|
||||
```bash
|
||||
swift rollout \
|
||||
--model Qwen/Qwen3-1.7B \
|
||||
--vllm_use_async_engine true \
|
||||
--multi_turn_scheduler thinking_tips_scheduler \
|
||||
--vllm_max_model_len 32768 \
|
||||
--vllm_gpu_memory_utilization 0.8 \
|
||||
--max_turns 3
|
||||
```
|
||||
|
||||
|
||||
> 通过参数 `external_plugins`,我们可以将本地的多轮规划器注册到 ms-swift 中,具体实现请参考[代码](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)。
|
||||
|
||||
多轮训练脚本请参考[脚本](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/external/vllm_multi_turn.sh)。
|
||||
|
||||
|
||||
对于多轮 rollout,我们使用 AsyncEngine 来实现高效的批量数据异步多轮采样。AsyncEngine 在多轮推理时能够减少推理过程中的计算气泡:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/asyncengine.png" width="400" />
|
||||
|
||||
在 `rollout` 命令中使用参数 `vllm_use_async_engine` 来指定 engine 的种类(默认使用 async engine):
|
||||
|
||||
> 注意: async engine 仅在 server mode 下可用。
|
||||
|
||||
### GYM 环境训练
|
||||
|
||||
如果你的多轮任务可以建模为标准的 gym environment(`reset` / `step` / 环境直接给奖励),推荐直接复用框架内置的 `gym_scheduler`,并通过实现一个 `Env` 子类来描述任务。
|
||||
|
||||
`GYMScheduler` 基于通用 hook 协议实现,无需重载 `run` 方法:
|
||||
- **`on_trajectory_start`**: 调用 `env.reset` 并注入初始 observation 到首轮 user 消息
|
||||
- **`on_turn_end`**: 调用 `env.step` 推进环境,返回 `{'done': bool, 'rollout_infos': dict}`
|
||||
|
||||
这种设计使得 `GYMScheduler` 同时适用于 server mode(`run()`)和 colocate mode(`run_multi_turn()`),用户只需实现 Env 接口即可。
|
||||
|
||||
完整接口、自定义 env 的步骤参考 [GYM 环境训练文档](./gym_env.md)。
|
||||
|
||||
## 高级设置
|
||||
|
||||
### 自定义多轮交互逻辑
|
||||
|
||||
在以上默认逻辑中,我们用一条轨迹来计算多轮 rollout 的损失,这里需要假设多轮交互的过程中,模型的历史信息没有受到改变。
|
||||
|
||||
而在一些多轮场景中,我们可能需要在多轮 rollout 过程中动态地修改模型的历史信息(比如压缩历史信息),此时,我们需要将每轮的 rollout 单独作为一条轨迹进行训练。
|
||||
|
||||
#### 方式一:使用 hook
|
||||
|
||||
```python
|
||||
class CustomScheduler(MultiTurnScheduler):
|
||||
def on_trajectory_start(self, requests):
|
||||
# 首轮推理前初始化(如环境 reset、注入初始状态)
|
||||
for req in requests:
|
||||
req.messages = [system_msg, user_msg(initial_observation)]
|
||||
|
||||
def on_turn_end(self, req, response_choice, current_turn):
|
||||
# 每轮推理后推进状态,返回 done 和 rollout_infos
|
||||
next_obs, reward, done = self.advance_env(req.messages)
|
||||
return {
|
||||
'done': done,
|
||||
'rollout_infos': {'reward': reward, ...}
|
||||
}
|
||||
```
|
||||
|
||||
这种方式同时适用于 server mode 和 colocate mode,无需重载 `run` 方法。
|
||||
|
||||
#### 方式二:重载 run 方法(完全自定义)
|
||||
|
||||
比较常见的一种场景是对于思考类模型,在实际推理过程中,模型通常只会保留最后一轮的思考内容,而忽略历史模型回复中的思考内容。
|
||||
|
||||
对于这类场景,我们需要重写多轮规划器中的交互逻辑,即重载 `run` 方法,从而单独返回每一轮的 Rollout 的结果。
|
||||
|
||||
框架内置的 `ThinkingModelTipsScheduler` 类展示了如何通过重写 `run()` 方法来实现完全自定义的多轮推理逻辑。请参考[内置多轮调度器实现](https://github.com/modelscope/ms-swift/blob/main/swift/rollout/multi_turn.py)
|
||||
|
||||
**注意**: 这种情况下,相同轨迹的数据会拆分为多条数据,在奖励相关的处理中,需要对相同轨迹的数据分配同样的reward。
|
||||
|
||||
可以在kwargs中获取 trajectory_inputs 获取完整轨迹的数据,具体实现参考[MultiTurnThinkingTips类](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)
|
||||
|
||||
### 多模态数据修改
|
||||
在多模态多轮交互场景下,可能需要在对话过程中动态增删或修改多模态数据,并确保这些变更同步至 trainer。
|
||||
|
||||
实现方式:借助 rollout_infos,通过指定键值覆盖原始数据集的多模态内容。
|
||||
|
||||
现已支持覆盖的键:images、audios、videos。
|
||||
|
||||
具体请参考[DeepEyes Schduler](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/deepeyes/deepeyes_plugin.py#L403-L404)
|
||||
|
||||
### 返回 response token ids
|
||||
在默认的多轮交互流程中,规划器先把模型生成的文本字符串返回给 trainer,trainer 再将其重新 encode 为 token id,用于后续训练。为了避免这一步重复编码的开销,你可以让规划器直接返回 response_token_ids,省去 trainer 侧的再次 encode。
|
||||
|
||||
具体做法如下:
|
||||
|
||||
- 在 response_choice 对象中读取 token_ids 属性,即可获得本次 rollout 生成的 token 序列。
|
||||
- 在 step/run 方法的返回值里加入 response_token_ids,trainer 便能直接使用这些 token id 参与训练,无需重新编码。
|
||||
|
||||
具体实现可以参考[ThinkingModelTipsScheduler](https://github.com/modelscope/ms-swift/blob/main/swift/rollout/multi_turn.py)类
|
||||
|
||||
### 损失掩码
|
||||
|
||||
在工具调用或环境交互返回结果时,若需将返回内容作为模型响应的一部分,建议对这些插入内容进行掩码处理,以确保模型在训练过程中不会对这些外部生成的内容计算损失。
|
||||
|
||||
我们可以通过两种方式设置损失掩码
|
||||
|
||||
**第一种:设置 loss_scale**
|
||||
|
||||
ms-swift 提供 loss_scale 参数来对模型回复部分的内容进行损失缩放设置。比如设置`--loss_scale last_round`,可以将非最后一轮的模型回复的损失置零。我们也可以实现自定义 loss_scale,具体请参考[定制化 loss_scale 文档](../../../Customization/Architecture.md#loss-scale)。
|
||||
|
||||
> 注:在GRPO中,loss_scale 只提供掩码功能,不提供缩放功能。
|
||||
|
||||
**第二种:设置loss_mask**
|
||||
|
||||
在`step`或者`run`方法中设置 response_loss_mask, 可以在规划器中自定义损失掩码。前提需要返回response token ids,返回的 response_loss_mask 需要与 response token ids等长。当返回 response_loss_mask 时,loss_scale 参数失效。
|
||||
|
||||
response_loss_mask 返回可以参考[ToolCallScheduler类](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)
|
||||
|
||||
### 奖励函数相关
|
||||
|
||||
在奖励函数中获取多轮 Rollout 中的信息
|
||||
|
||||
在 `on_turn_end` 方法或 `step/run` 方法中,返回 `rollout_infos` 对象,在奖励函数的 kwargs 中获取 `rollout_infos`:
|
||||
|
||||
```python
|
||||
class Scheduler():
|
||||
def on_turn_end(self, infer_request, response_choice, current_turn):
|
||||
...
|
||||
return {'done': done, 'rollout_infos': extra_dict}
|
||||
|
||||
# 或者在 step 方法中
|
||||
def step(self, infer_request, response_choice, current_turn):
|
||||
...
|
||||
return {'infer_request': infer_request, 'rollout_infos': extra_dict}
|
||||
|
||||
class RewardFunction():
|
||||
def __call__(self, completions, **kwargs):
|
||||
infos = kwargs.get('rollout_infos', {})
|
||||
...
|
||||
```
|
||||
|
||||
### 在 Scheduler 中获取额外的数据集信息
|
||||
|
||||
在训练侧设置参数`--vllm_server_pass_dataset`,可将数据集中的其他列传入多轮规划器。在`infer_request.data_dict`中获取。
|
||||
|
||||
### 训推一致性兼容
|
||||
swift 支持从 vLLM 侧返回 rollout 的 logps 用于纠正训推不一致问题,具体请参考该[文档](../AdvancedResearch/training_inference_mismatch.md)
|
||||
|
||||
在多轮训练中,如果启用了 `rollout_importance_sampling_mode`,框架会自动收集每轮 rollout 的 log probabilities,用于校正训推不一致带来的 off-policy 问题。
|
||||
|
||||
**默认行为**:
|
||||
- 使用默认的 `run` 方法时,框架会自动从 `response_choice.logprobs` 中提取 log probabilities
|
||||
- 这些 logprobs 会与 `response_token_ids` 和 `response_loss_mask` 一起传递给 trainer
|
||||
|
||||
**自定义 Scheduler 的注意事项**:
|
||||
|
||||
如果你在 `step` 方法中修改了 response(如截断、添加内容),需要同步返回对应的 `rollout_logprobs`
|
||||
|
||||
**关键规则**:
|
||||
- `rollout_logprobs` 的长度应该等于 `response_loss_mask` 中值为 1 的数量
|
||||
- 对于 `loss_mask=0` 的 token(如用户添加的提示、工具返回结果),不需要提供 logprobs
|
||||
- 如果 `step` 方法没有返回 `rollout_logprobs`,框架会自动从 `response_choice.logprobs` 中提取
|
||||
|
||||
**重写 `run` 方法的场景**:
|
||||
|
||||
如果你完全重写了 `run` 方法,需要手动收集和传递 `rollout_logprobs`
|
||||
|
||||
具体的实现请参考[内置实现](https://github.com/modelscope/ms-swift/blob/main/swift/rollout/multi_turn.py)
|
||||
@@ -0,0 +1,146 @@
|
||||
# 奖励函数
|
||||
## 自定义奖励函数
|
||||
奖励函数接受模型生成的文本 completions 其他数据集中的列以及训练器状态作为参数(kwargs)进行打分, 其中[训练器状态](https://huggingface.co/docs/transformers/main/main_classes/callback#transformers.TrainerState)包含训练的步数等信息。
|
||||
|
||||
注意:模型输入相关的列(比如query,response)会被处理为 messages 键,原数据集中的 assistant response 会被舍弃,请使用额外的列进行保留。
|
||||
相关处理的列名参考[文档](../../../Customization/Custom-dataset.md#query-response格式)
|
||||
|
||||
以下是一个示例,展示了如何实现一个简单的长度奖励函数。该函数会在模型生成的文本长度超过 1024 时,给予 1.0 的奖励信号;否则,奖励信号为 0.0。
|
||||
|
||||
```python
|
||||
from swift.rewards import ORM, orms
|
||||
class DummyLengthRewardFunction(ORM)
|
||||
def __call__(completions, **kwargs):
|
||||
return [1.0 if len(completion) > 1024 else 0.0 for completion in completions]
|
||||
|
||||
orms['dummy']= DummyLengthRewardFunction
|
||||
```
|
||||
|
||||
**获取数据集中的其他列**
|
||||
|
||||
比如奖励函数需要获取数据集`solution`列、当前训练步数和总步数作为辅助计算,以下是两种获取方式
|
||||
|
||||
第一种:在__call__入参中显式定义列名
|
||||
```python
|
||||
def __call__(completions, solution, trainer_state, **kwargs):
|
||||
print(solution)
|
||||
global_step = trainer_state.global_step
|
||||
max_steps = trainer_state.max_steps
|
||||
...
|
||||
```
|
||||
|
||||
第二种:在kwargs中获取
|
||||
```python
|
||||
def __call__(completions, **kwargs):
|
||||
solution = kwargs.get('solution')
|
||||
trainer_state = kwargs.get('trainer_state')
|
||||
global_step = trainer_state.global_step
|
||||
max_steps = trainer_state.max_steps
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
**使用自定义奖励函数**
|
||||
|
||||
可以在`swift/examples/train/grpo/plugin/plugin.py`中加入该奖励函数,使用参数`--external_plugins examples/train/grpo/plugin/plugin.py`进行注册,并通过 reward_funcs 参数进行指定
|
||||
|
||||
执行脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/run_external_reward_func.sh)
|
||||
|
||||
## 异步奖励函数
|
||||
|
||||
对于涉及 I/O 操作的奖励函数(如 API 调用、数据库查询等),可以使用异步(async)奖励函数来提高性能。异步奖励函数使用 `asyncio.gather` 并行执行,可以显著加速奖励计算。
|
||||
|
||||
```python
|
||||
from swift.rewards import AsyncORM, orms
|
||||
import asyncio
|
||||
|
||||
class AsyncAPIReward(AsyncORM):
|
||||
async def __call__(self, completions, **kwargs):
|
||||
import aiohttp
|
||||
|
||||
async def score_single(session, text):
|
||||
async with session.post(
|
||||
'https://api.example.com/score',
|
||||
json={'text': text}
|
||||
) as resp:
|
||||
result = await resp.json()
|
||||
return result['score']
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
# 使用 asyncio.gather 并行发送所有请求
|
||||
tasks = [score_single(session, c) for c in completions]
|
||||
rewards = await asyncio.gather(*tasks)
|
||||
return list(rewards)
|
||||
|
||||
orms['async_api'] = AsyncAPIReward
|
||||
```
|
||||
|
||||
swift 支持同时使用同步和异步奖励函数。训练器会自动检测奖励函数的类型:
|
||||
- 同步奖励函数按顺序执行
|
||||
- 异步奖励函数使用 `asyncio.gather` 并行执行
|
||||
|
||||
[plugin](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)文件中提供了一个调用`swift deploy`服务的生成式奖励模型的例子(async_genrm)
|
||||
|
||||
## 内置奖励函数
|
||||
swift内置了五种基于规则的奖励函数(代码见swift/rewards/orm.py)
|
||||
|
||||
| 奖励函数 | 论文 |
|
||||
|----------------|----------------------------------------------------------------------------|
|
||||
| accuracy | [DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via RL](https://arxiv.org/abs/2501.12948) |
|
||||
| format | 同上 |
|
||||
| cosine | [Demystifying Long Chain-of-Thought Reasoning in LLMs](https://arxiv.org/abs/2502.03373) |
|
||||
| repetition | 同上 |
|
||||
| soft_overlong | [Decoupled Clip and Dynamic sAmpling Policy Optimization (DAPO)](https://arxiv.org/abs/2503.14476) |
|
||||
|
||||
### 1. **accuracy**
|
||||
|
||||
该函数将模型的生成结果与数据集中的 solution 列进行比较,计算准确率分数。如果生成结果与标准答案一致,则得分为 1.0;否则为 0.0。
|
||||
|
||||
注意:该奖励函数使用`math_verify`库解析生成结果和solution中的答案,可能只适用于特定的数学数据集。
|
||||
|
||||
### 2. **format**
|
||||
|
||||
论文中使用以下system prompt要求模型按照固定格式进行返回
|
||||
```
|
||||
A conversation between User and Assistant. The user asks a question, and the Assistant solves it. The assistant first thinks about the reasoning process in the mind and then provides the user with the answer. The reasoning process and answer are enclosed within <think> </think> and <answer> </answer> tags, respectively, i.e., <think> reasoning process here </think><answer> answer here </answer>
|
||||
```
|
||||
|
||||
该函数检查模型是否按照 `<think>think content</think><answer>answer content</answer>` 的格式进行生成。如果生成文本符合格式要求,则得分为 1.0;否则为 0.0。
|
||||
|
||||
### 3. **cosine**
|
||||
|
||||
论文发现,仅使用 accuracy 奖励函数进行训练会导致模型的生成长度趋于超长,从而影响训练效果。cosine 奖励函数通过控制模型的生成长度来优化训练过程:
|
||||
|
||||
- 对于生成正确答案的文本,奖励值随长度增加而递减,鼓励模型生成简洁的回答。
|
||||
- 对于生成错误答案的文本,奖励值随长度增加而递增,鼓励模型进行更深入的思考。
|
||||
|
||||
使用余弦函数平滑地调整奖励值,确保奖励变化在合理范围内。余弦函数的参数包括生成文本的长度、最大长度限制以及奖励的最小值和最大值。
|
||||
|
||||
参数
|
||||
- cosine_min_len_value_wrong(默认值:-0.5):生成错误答案时,最小长度对应的奖励值。
|
||||
- cosine_max_len_value_wrong(默认值:0.0):生成错误答案时,最大长度对应的奖励值。
|
||||
- cosine_min_len_value_correct(默认值:1.0):生成正确答案时,最小长度对应的奖励值。
|
||||
- cosine_max_len_value_correct(默认值:0.5):生成正确答案时,最大长度对应的奖励值。
|
||||
- cosine_max_len(默认值等于模型生成的最大程度):生成文本的最大长度限制。
|
||||
|
||||
|
||||
### 4. **repetition**
|
||||
|
||||
惩罚模型生成文本中的重复内容,通过检测生成文本中的重复 n-gram 模式来评估重复程度,并给予相应的惩罚。
|
||||
|
||||
函数将生成文本分割为单词,并提取指定大小的 n-gram(默认为 3-gram)。通过统计不同 n-gram 的数量与总 n-gram 数量的比例,计算重复比例。如果生成文本中重复的 n-gram 比例较高,则给予较大的负奖励(惩罚)。惩罚值通过重复比例和最大惩罚值(默认为 -1.0)计算得出。
|
||||
|
||||
参数
|
||||
- repetition_n_grams(默认值:3):用于检测重复的 n-gram 大小。
|
||||
- repetition_max_penalty(默认值:-1.0):最大惩罚值,用于控制惩罚的强度。
|
||||
|
||||
### 5. **soft overlong punishment**
|
||||
定义长度惩罚区间。在这个区间内,给予[-1,0]的线性惩罚。
|
||||
|
||||
参数
|
||||
- soft_max_length: 论文中的L_max,模型的最大生成长度,默认等于max_completion_length
|
||||
- soft_cache_length: 论文中的L_cache,控制长度惩罚区间,区间为[soft_max_length-soft_cache_length, soft_max_length]
|
||||
|
||||
## 注意事项
|
||||
|
||||
如果需要在奖励函数中加载模型,默认会使用训练的 deepspeed 插件(transformers逻辑),zero3下可能会导致模型无法正常推理,参考该[issue](https://github.com/modelscope/ms-swift/issues/4580)来跳过 deepspeed 初始化环境
|
||||
@@ -0,0 +1,145 @@
|
||||
# 奖励模型
|
||||
|
||||
默认情况下,奖励模型是指具有分类头数值输出的模型,通常称为输出奖励模型(ORM)。这些模型会对其他模型的输出进行评分,从而生成一个标量值,表示模型响应的质量。
|
||||
|
||||
我们可以通过使用参数 `reward_models` 来加载具有分类头的奖励模型,或者加载经过[奖励建模](../../RLHF.md#rm)训练的奖励模型,进而使用模型的logits作为奖励。
|
||||
|
||||
## 自定义奖励模型
|
||||
对于生成式奖励模型,有两种常见的调用方式:一种是在 Trainer 内部直接使用 reward_model_plugin 定义奖励模型的逻辑,可以使用TransformersEngine对奖励模型进行推理,另一种是通过外部部署的模型服务进行调用。
|
||||
|
||||
- 使用 reward_model_plugin 调用奖励模型时,模型会被内嵌在 Trainer 内部,无需额外占用计算资源。该方式优点是方便集成,但生成速度相对较慢,更适合参数量较小的奖励模型场景。
|
||||
|
||||
- 外部部署奖励模型时,可以通过诸如 swift deploy 或 vllm serve 等命令将模型服务部署于独立设备,大幅提升推理速度,适合参数量较大的模型。但这样需要预留额外的硬件资源。
|
||||
|
||||
### 内部插件
|
||||
|
||||
我们可以在 reward_model_plugin 中灵活地自定义奖励模型的处理逻辑。这使得实现诸如生成式奖励模型等技术成为可能,包括:
|
||||
|
||||
- 自定义模型的系统提示:定义特定的指令和上下文以指导评估过程。
|
||||
- 处理模型交互历史:管理对话上下文,以提供有意义且具有上下文感知的评估。
|
||||
- 定义自定义评估标准:设置独特的标准和度量,用于评估模型的响应,超越默认的准确性和相关性衡量标准。
|
||||
|
||||
通过reward_model_plugin,开发者可以针对其应用的特定需求定制奖励评估过程。这种灵活性允许更细致和有效的基于奖励的训练策略。
|
||||
|
||||
奖励模型通过plugin的`__call__`方法进行调用,该方法接受 `inputs` 作为参数,包含了模型输入输出的 messages 和数据集中的其他列
|
||||
|
||||
```python
|
||||
def __call__(self, inputs):
|
||||
print(inputs)
|
||||
"""
|
||||
[
|
||||
{
|
||||
'messages': [
|
||||
{'role': 'system', 'content': 'system prompt'},
|
||||
{'role': 'query', 'content': 'query'},
|
||||
{'role': 'user', 'content': 'completions1'},
|
||||
],
|
||||
'solution': "abc",
|
||||
},
|
||||
{
|
||||
'messages': [
|
||||
{'role': 'system', 'content': 'system prompt'},
|
||||
{'role': 'query', 'content': 'query'},
|
||||
{'role': 'user', 'content': 'completions2'},
|
||||
],
|
||||
'solution': "abc",
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
在插件中使用 TransformersEngine 进行奖励模型的推理, 我们只需构造 messages ,并通过 infer 接口调用:
|
||||
```python
|
||||
class RMPlugin(DefaultRMPlugin):
|
||||
|
||||
def __init__(self, model, template):
|
||||
|
||||
super().__init__(model, template)
|
||||
# initilize TransformersEngine to infer
|
||||
self.engine = TransformersEngine(self.model, template=self.template, max_batch_size=0)
|
||||
|
||||
def __call__(self, inputs):
|
||||
system_prompt = ...
|
||||
query = ...
|
||||
messages = [{'role': 'system', 'content': system_prompt}, {'role': 'query', 'content': query}]
|
||||
result = self.engine.infer([messages], self.request_config, use_tqdm=False)
|
||||
rewards = ...
|
||||
return rewards
|
||||
```
|
||||
|
||||
我们在 [rm_plugin.py](https://github.com/modelscope/ms-swift/blob/main/swift/rewards/rm_plugin.py) 中提供了一个简单的生成式奖励模型示例(GenRMPlugin)。
|
||||
|
||||
在 [plugin.py](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py) 中自定义奖励模型插件,并使用 `external_plugins` 参数进行注册。
|
||||
|
||||
|
||||
注意:
|
||||
1. 在 GRPOTrainer 中,reward_model 会依次append到 reward_funcs 中。因此,reward_weights 的顺序对应 [reward_funcs, reward_model]。
|
||||
2. reward_model_plugin 默认为 default,即使用 ORM 处理逻辑。
|
||||
3. 对于参数量较大的模型,TransformersEngine 生成速度较慢,请使用[外部部署](#外部部署)方法
|
||||
|
||||
对于 BERT 这类无法通过 reward_model 加载的模型,我们可以内置在 reward_function 中进行加载,参考[issue](https://github.com/modelscope/ms-swift/issues/4580)
|
||||
|
||||
### 外部部署
|
||||
|
||||
**示例 2:使用 swift deploy 部署奖励模型并进行远程调用**
|
||||
|
||||
这类方法则不需要使用 reward_model_plugin , 而是直接在奖励函数中进行调用即可
|
||||
|
||||
首先用如下命令启动模型服务:
|
||||
|
||||
```bash
|
||||
# 注意部署的设备不要与训练设备重叠
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift deploy \
|
||||
--model Qwen/Qwen2.5-72B-Instruct \
|
||||
--vllm_tensor_parallel_size 4
|
||||
|
||||
# [INFO:swift] model_list: ['Qwen2.5-72B-Instruct']
|
||||
# INFO: Started server process [xxxxxx]
|
||||
# INFO: Waiting for application startup.
|
||||
# INFO: Application startup complete.
|
||||
# INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
在奖励函数中通过 OpenAI 库初始化客户端,指定模型服务的地址和端口,示例代码如下:
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
class RMReward(ORM):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
try:
|
||||
self.client = OpenAI(
|
||||
api_key='EMPTY',
|
||||
base_url='http://127.0.0.1:8000/v1', # 若在本地部署则为 127.0.0.1
|
||||
)
|
||||
self.verify_model_name = self.client.models.list().data[0].id
|
||||
except Exception as e:
|
||||
raise RuntimeError('Failed to connect to the model service. Please deploy the model '
|
||||
"using 'swift deploy' or 'vllm serve'.") from e
|
||||
|
||||
|
||||
def __call__(self, completions, messages, **kwargs) -> List[float]:
|
||||
rewards = []
|
||||
for completion, message in zip(completions, messages):
|
||||
rm_prompt = ... # 构建 reward model 的prompt
|
||||
chat_response = self.client.chat.completions.create(
|
||||
model=self.verify_model_name,
|
||||
messages=[
|
||||
{
|
||||
'role': 'system',
|
||||
'content': 'You are a helpful assistant.'
|
||||
},
|
||||
{
|
||||
'role': 'user',
|
||||
'content': rm_prompt
|
||||
},
|
||||
],
|
||||
)
|
||||
response = chat_response.choices[0].message.content.strip()
|
||||
reward = ... # 根据奖励模型生成结果提取奖励值
|
||||
rewards.append(reward)
|
||||
return rewards
|
||||
|
||||
```
|
||||
@@ -0,0 +1,398 @@
|
||||
# GRPO
|
||||
|
||||
[GRPO(Group Relative Policy Optimization)](https://arxiv.org/abs/2402.03300) 算法利用组内相对优势计算来替代 PPO 算法中独立的价值模型,并直接在损失函数中加入 KL 散度惩罚来提高训练稳定性。
|
||||
|
||||
## 算法原理
|
||||
|
||||
GRPO 目标函数
|
||||
|
||||
$
|
||||
{\scriptstyle
|
||||
\begin{aligned}
|
||||
\mathcal{J}_{G R P O}(\theta) & =\mathbb{E}_{\left[q \sim P(Q),\left\{o_i\right\}_{i=1}^G \sim \pi_{\theta_{o l d}}(O \mid q)\right]} \\
|
||||
& \frac{1}{G} \sum_{i=1}^G \frac{1}{\left|o_i\right|} \sum_{t=1}^{\left|o_i\right|}\left\{\min \left[\frac{\pi_\theta\left(o_{i, t} \mid q, o_{i,<t}\right)}{\pi_{\theta_{o l d}}\left(o_{i, t} \mid q, o_{i,<t}\right)} \hat{A}_{i, t}, \operatorname{clip}\left(\frac{\pi_\theta\left(o_{i, t} \mid q, o_{i,<t}\right)}{\pi_{\theta_{o l d}}\left(o_{i, t} \mid q, o_{i,<t}\right)}, 1-\varepsilon, 1+\varepsilon\right) \hat{A}_{i, t}\right]-\beta \mathbb{D}_{K L}\left[\pi_\theta| | \pi_{r e f}\right]\right\}
|
||||
\end{aligned}
|
||||
}
|
||||
$
|
||||
|
||||
其中优势函数定义为
|
||||
|
||||
$
|
||||
\hat{A}_{i,t} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)}
|
||||
$
|
||||
|
||||
|
||||
<details> <summary>GRPO算法伪代码</summary>
|
||||
|
||||
```python
|
||||
# ========== 1. Rollout Generation Phase ==========
|
||||
prompt = "Question: Which is bigger? 9.11 or 9.9?"
|
||||
|
||||
# Generate multiple completions through parallel sampling
|
||||
completions = rollout_function(
|
||||
model=current_policy_model,
|
||||
prompt=prompt,
|
||||
num_generations=8, # Hyperparameter: number of samples per prompt
|
||||
temperature=1.0 # Hyperparameter: sampling diversity
|
||||
)
|
||||
"""
|
||||
completions = [
|
||||
(completion 1) "The larger number is 9.9...",
|
||||
(completion 2) "9.11 is bigger than...",
|
||||
...
|
||||
(completion 8) "After calculation, 9.9..."
|
||||
]
|
||||
"""
|
||||
|
||||
# ========== 2. Reward Calculation Phase ==========
|
||||
# Evaluate generated completions using reward model
|
||||
rewards = reward_function(
|
||||
completions=completions,
|
||||
ground_truth="9.9" # Expected correct answer
|
||||
)
|
||||
"""
|
||||
rewards = [
|
||||
(reward 1) 1.0, # Correct answer
|
||||
(reward 2) 0.0, # Incorrect
|
||||
...
|
||||
(reward 8) 1.0 # Correct
|
||||
]
|
||||
"""
|
||||
|
||||
# Normalize rewards to advantages
|
||||
rewards_mean = mean(rewards) # μ = 0.5
|
||||
rewards_std = std(rewards) # σ = 0.25
|
||||
advantages = (rewards - rewards_mean) / (rewards_std + 1e-8) # Standardization
|
||||
"""
|
||||
advantages = [
|
||||
(advantage 1) 2.0, # (1.0 - 0.5)/0.25
|
||||
(advantage 2) -2.0,
|
||||
...
|
||||
(advantage 8) 2.0
|
||||
]
|
||||
"""
|
||||
|
||||
# ========== 3. Policy Optimization Phase ==========
|
||||
# Get token-level log probabilities from different models
|
||||
current_logps = get_per_token_logps(current_policy_model, prompt, completions) # π_θ
|
||||
old_logps = get_per_token_logps(old_policy_model, prompt, completions) # π_θ_old
|
||||
ref_logps = get_per_token_logps(reference_model, prompt, completions) # π_ref
|
||||
|
||||
# PPO Clipped Objective
|
||||
is_ratio = exp(current_logps - old_logps) # Importance sampling ratio: e^(π_θ - π_θ_old)
|
||||
clipped_ratio = clip(is_ratio, 1-ε, 1+ε) # ε=0.2 typically
|
||||
|
||||
# Policy gradient term (dual form)
|
||||
policy_loss = -mean(
|
||||
minimum(is_ratio * advantages, # Unclipped objective
|
||||
clipped_ratio * advantages) # Clipped objective
|
||||
)
|
||||
|
||||
# KL Divergence Penalty (K3 estimator)
|
||||
# KL(π_θ||π_ref) ≈ e^(logπ_ref - logπ_θ) - (logπ_ref - logπ_θ) - 1
|
||||
kl_penalty = beta * mean(
|
||||
exp(ref_logps - current_logps) -
|
||||
(ref_logps - current_logps) - 1
|
||||
)
|
||||
|
||||
# Total Loss = Policy Loss + KL Penalty
|
||||
total_loss = policy_loss + kl_penalty
|
||||
|
||||
# ========== 4. Update Rule ==========
|
||||
# Apply gradient descent to minimize total_loss
|
||||
optimizer.zero_grad()
|
||||
total_loss.backward()
|
||||
optimizer.step()
|
||||
```
|
||||
</details>
|
||||
|
||||
训练脚本示例参考[examples](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo)
|
||||
|
||||
GRPO参数参考[文档](../../../Instruction/Command-line-parameters.md#grpo参数)
|
||||
|
||||
## 集群支持
|
||||
|
||||

|
||||
|
||||
GRPO 训练框架支持集成高性能推理引擎(如 vLLM)来加速采样过程,提供以下两种部署模式:
|
||||
|
||||
### 1. Colocate(Internal) Mode
|
||||
|
||||
训练与推理共享GPU资源,在 Trainer 内部启动推理服务,
|
||||
|
||||
启动参数
|
||||
```bash
|
||||
--use_vllm true \
|
||||
--vllm_mode colocate
|
||||
```
|
||||
|
||||
#### Colocate 模式下的显存优化方案
|
||||
在 Colocate 模式下运行时,容易出现显存不足(OOM)的情况。以下是几种有效的显存优化方法和参数配置:
|
||||
|
||||
1. 降低`vllm_gpu_memory_utilization` 参数
|
||||
|
||||
|
||||
2. 在训练阶段,释放 vLLM 占用的显存:
|
||||
|
||||
```bash
|
||||
--sleep_level 1
|
||||
```
|
||||
|
||||
3. 在vLLM 推理阶段,释放模型和优化器占用的显存:
|
||||
|
||||
```bash
|
||||
--offload_optimizer true \
|
||||
--offload_model true \
|
||||
```
|
||||
|
||||
4. 在vLLM中使用 Tensor Parallel 技术:
|
||||
|
||||
```bash
|
||||
--vllm_tensor_parallel_size [tp_size]
|
||||
```
|
||||
|
||||
5. 分批 Gather 模型权重(zero3下同步 vLLM 权重时):
|
||||
|
||||
```bash
|
||||
--move_model_batches [批次数量]
|
||||
```
|
||||
|
||||
6. 将 Megatron 导出的用于 vLLM 更新的 HF 格式权重存放在 CPU 主存中,以降低 GPU 显存占用:
|
||||
|
||||
```bash
|
||||
--offload_bridge true
|
||||
```
|
||||
|
||||
### 2. Async(External) Mode
|
||||
|
||||
训练与推理资源分离,启动单独的推理服务器
|
||||
|
||||
使用`swift rollout`命令部署vLLM 服务器, 现仅支持vLLM backend
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_data_parallel_size 1
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_data_parallel_size 1
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_data_parallel_size 2
|
||||
```
|
||||
|
||||
更多 rollout 参数参考[vLLM参数](../../../Instruction/Command-line-parameters.md#vllm参数)和[rollout 参数](../../../Instruction/Command-line-parameters.md#rollout参数)
|
||||
|
||||
注意:在使用 vllm_use_async_engine 时,仅开启 DP 可能会导致错误,相关问题参考: [vllm issue](https://github.com/vllm-project/vllm/issues/18567)。如果出现错误,请尝试同时启用 TP 和 DP,或升级vLLM
|
||||
|
||||
|
||||
训练使用以下参数配置外部 vLLM 服务器
|
||||
```bash
|
||||
--use_vllm true \
|
||||
--vllm_mode server \
|
||||
--vllm_server_host <服务器IP> \
|
||||
--vllm_server_port <服务端口> \
|
||||
--vllm_server_timeout <超时时间> \
|
||||
```
|
||||
|
||||
### 权重同步加速
|
||||
设置以下参数可以通过仅同步 LoRA adapter 权重而非全量模型权重,优化 LoRA 训练的权重同步速度。
|
||||
|
||||
> 注意:这种同步方式会略微影响 vLLM 推理速度。
|
||||
|
||||
```bash
|
||||
# rollout(server mode)
|
||||
swift rollout \
|
||||
--vllm_enable_lora true \
|
||||
--vllm_max_lora_rank xxx # 与训练脚本lora_rank一致
|
||||
...
|
||||
|
||||
# grpo(colocate mode)
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--vllm_mode colocate \
|
||||
--vllm_enable_lora true \
|
||||
...
|
||||
|
||||
# megatron grpo(colocate mode)
|
||||
swift megatron rlhf \
|
||||
--rlhf_type grpo \
|
||||
--vllm_mode colocate \
|
||||
--vllm_enable_lora true \
|
||||
...
|
||||
```
|
||||
|
||||
**多模态模型 ViT 层 LoRA 同步:** 如果训练时开启了 ViT 层的 LoRA(`freeze_vit false`),
|
||||
在仅同步LoRA的模式下,需要相应在 vLLM 侧开启 tower/connector LoRA 支持。
|
||||
|
||||
通过 `vllm_engine_kwargs` 传入:
|
||||
|
||||
```bash
|
||||
--vllm_engine_kwargs '{"enable_tower_connector_lora": true}'
|
||||
```
|
||||
|
||||
该功能为 vLLM 实验性特性,目前支持 Qwen2.5-VL、Qwen3-VL 等模型。
|
||||
具体支持情况请参阅 [vLLM 文档](https://docs.vllm.ai/en/latest/features/lora/)
|
||||
和 [vLLM issue](https://github.com/vllm-project/vllm/issues/31479)。
|
||||
|
||||
## logged metrics
|
||||
- completions/mean_length:生成的 completion 的平均长度。
|
||||
- completions/min_length:生成的 completion 的最小长度。
|
||||
- completions/max_length:生成的 completion 的最大长度。
|
||||
- completions/clipped_ratio:被长度截断的 completion 占比。
|
||||
- reward/{reward_func_name}/mean:某个特定 reward function 的平均奖励值。
|
||||
- reward/{reward_func_name}/std:某个特定 reward function 的奖励标准差。
|
||||
> 注意, 上述两个指标是在所有 completions 范围内统计得到的。
|
||||
- reward:加权 reward_weights 后的整体平均奖励。
|
||||
- reward_std:加权 reward_weights 后,每个 batch 内整体奖励的标准差。
|
||||
> 注意:上述两个指标是先在每个组内分别计算均值/std,然后再对各组的结果取平均。
|
||||
- frac_reward_zero_std:在生成 batch 中,reward 标准差为零的样本比例,意味着该 prompt 上的答案几乎无多样性(所有回答奖励一致)。
|
||||
- kl:生成的 completion 上,模型与参考模型之间的平均 KL 散度。仅当 beta 非零时记录。
|
||||
- clip_ratio/region_mean:不同句子中被 CLIP 的的 token 平均比例
|
||||
- clip_ratio/low_mean:不同句子中被 下CLIP 的的 token 平均比例
|
||||
- clip_ratio/low_min:不同句子中被 下CLIP 的的 token 最小比例
|
||||
- clip_ratio/high_mean:不同句子中被 上CLIP 的的 token 平均比例
|
||||
- clip_ratio/high_max:不同句子中被 上CLIP 的的 token 最大比例
|
||||
> 注意:如果开启`overlong_filter`, kl 和 clip_ratio 指标会过滤超长的样本
|
||||
|
||||
如果设置了`log_entropy`参数,则会额外记录entropy相关指标,包括
|
||||
- entropy/mean: 不同句子中的 entropy 均值
|
||||
- entropy/max: 不同句子中的 entropy 最大值
|
||||
- entropy/min: 不同句子中的 entropy 最小值
|
||||
> 注意这里的 句子 entropy 指 completion 中的 token entropy 均值
|
||||
|
||||
|
||||
如果设置了`top_entropy_quantile`参数<1.0, 则会记录entropy threshold的值
|
||||
- entropy/threshold: 分位点处的 entropy 值,小于该值的 token 将不会被计算 loss
|
||||
|
||||
训推一致性指标,前缀为rollout_correction,需设置`log_rollout_offpolicy_metrics=true`或`rollout_importance_sampling_mode`:
|
||||
- `kl` / `k3_kl`:训练策略与 rollout 策略之间的 KL 散度(直接估计器 / K3 估计器)
|
||||
- `training_ppl` / `rollout_ppl`:训练策略和 rollout 策略的困惑度
|
||||
- `log_ppl_diff`:log PPL 差异,反映分布偏移程度
|
||||
- `ppl_ratio`:PPL 比率
|
||||
- `chi2_token` / `chi2_seq`:Token/Sequence 级别的 χ² 散度
|
||||
|
||||
IS 校正指标(需设置`rollout_importance_sampling_mode`):
|
||||
- `is_weight_mean`:平均重要性采样权重
|
||||
- `ess`:有效样本大小(Effective Sample Size)
|
||||
- `clipped_frac`:被截断或屏蔽的样本比例
|
||||
|
||||
> 训推一致性指标详细说明请参考文档 [Training-Inference-Mismatch](../AdvancedResearch/training_inference_mismatch.md)
|
||||
|
||||
如果设置了`log_completions`, 将保存训练动态在output对应文件夹中,包括
|
||||
- step:记录时的训练步数
|
||||
- prompt:模型输入
|
||||
- completion:模型采样回答
|
||||
- {reward_func_name}:特定奖励
|
||||
- entropy:entropy token 均值,在设置`log_entropy`时记录
|
||||
|
||||
设置 `report_to wandb/swanlab` 将训练动态Table推送到对应的平台
|
||||
|
||||
如果需要在Table中额外记录其他列,请在 `GRPOTrainer._generate_and_score_completions` 方法中,设置 metrics_to_gather 字典。
|
||||
|
||||
默认自动检测
|
||||
- `image`:视觉数据集图像输入。(暂时只支持wandb)
|
||||
- `solution`:数据集中的 solution 列。
|
||||
|
||||
## FAQ
|
||||
**1. 训练过程中 loss 等于0 / 接近0 / 小于0**
|
||||
|
||||
正常情况, 参考[issue](https://github.com/huggingface/open-r1/issues/239#issuecomment-2646297851)
|
||||
|
||||
**2. num_generations / 批量大小相关**
|
||||
|
||||
在 GRPO 中,batch_size 以 completion(模型生成结果) 为单位。例如,设置 per_device_train_batch_size=8 表示每张 GPU 在训练过程中会同时处理 8 个 completion 的 loss 计算。
|
||||
|
||||
训练阶段,在一次完整的梯度累计 batch 中,总的批量大小等于:
|
||||
|
||||
```
|
||||
effective_batch_size = num_processes * per_device_train_batch_size * gradient_accumulation_steps
|
||||
```
|
||||
|
||||
采样阶段,总的批量大小 (completion-level) 数量等于:
|
||||
|
||||
1. 设置 generation_batch_size 下,等于 generation_batch_size
|
||||
2. 设置 steps_per_generation 下,等于 per_device_train_batch_size * steps_per_generation * num_processes
|
||||
3. 默认情况下,steps_per_generation = gradient_accumulation_steps,generation_batch_size = per_device_train_batch_size * steps_per_generation * num_processes = per_device_train_batch_size * gradient_accumulation_steps * num_processes = effective_batch_size
|
||||
|
||||
在评估阶段,completion 的数量等于:
|
||||
```
|
||||
num_processes * per_device_eval_batch_size
|
||||
```
|
||||
|
||||
参数 `num_generations` 必须能够被以上采样阶段和评估的总批量大小整除,以保证生成任务可以均匀分配到各个设备上。
|
||||
|
||||
**示例**
|
||||
|
||||
- num_processes = 8
|
||||
- per_device_train_batch_size = 4
|
||||
- gradient_accumulation_steps = 8
|
||||
- generation_batch_size = 512
|
||||
- num_generations = 64
|
||||
|
||||
1. 采样需要的总数据(prompt)量等于 512 / 64 = 8
|
||||
2. 每次采样 512 条模型回复
|
||||
3. 每次更新模型权重批量大小为 8 *4 * 8 = 256
|
||||
|
||||
|
||||
**3. 为什么 KL 出现了NaN**
|
||||
|
||||
开启 overlong_filter 后,某一卡上的所有 completion 都被截断
|
||||
|
||||
**4. 训练的steps怎么计算?**
|
||||
|
||||
参考[issue](https://github.com/modelscope/ms-swift/issues/3912)
|
||||
|
||||
**5. clip_ratio为什么总是0?**
|
||||
|
||||
Clip机制的核心目的是限制策略更新的幅度,防止因单次更新过大而导致策略性能崩溃(即策略更新后表现急剧下降)。
|
||||
Clip操作的具体公式如下:
|
||||
|
||||
$
|
||||
L_{\text{CLIP}}(\theta) = \mathbb{E}_{t} \left[ \min\left(r_{t}(\theta) \hat{A}_{t}, \text{clip}(r_{t}(\theta), 1 - \epsilon, 1 + \epsilon) \hat{A}_{t} \right) \right]
|
||||
$
|
||||
|
||||
其中:$r_{t}(\theta) = \frac{\pi_{\theta}(a_{t} \mid s_{t})}{\pi_{\text{old}}(a_{t} \mid s_{t})}$ 是重要性采样比,衡量新旧策略的差异。$\hat{A}_{t}$ 是优势函数(advantage function),表示动作的相对收益。$\epsilon$ 用于限制 $r_{t}(\theta)$ 的偏离范围。
|
||||
|
||||
在 on-policy 训练过程中,由于每次更新都使用最新策略生成的数据,新旧策略相同,即 $\pi_{\theta} = \pi_{\text{old}}$
|
||||
|
||||
因此重要性采样比恒为 1,此时,clip 操作不会生效。
|
||||
|
||||
在设置以下参数情况下,算法为off-policy (near-on-policy)
|
||||
1. num_iterations > 1, 或者
|
||||
2. gradient_accumulation_steps % steps_per_generation != 0
|
||||
|
||||
参考[issue](https://github.com/huggingface/open-r1/issues/239#issuecomment-2646297851)
|
||||
|
||||
**6. 如何设置训练的 `mini-batch size`**
|
||||
|
||||
在 GRPO 训练中,我们可以通过以下两种方式配置 mini-batch 更新:
|
||||
- 设置 `generation_batch_size` 为训练 global batch size (effective_batch_size) 的整数倍
|
||||
- 或设置 `steps_per_generation` 为 `gradient_accumulation_steps` 的整数倍
|
||||
|
||||
典型配置示例:
|
||||
- 当配置:
|
||||
steps_per_generation = 16, gradient_accumulation_steps = 8, mini_batch_size = steps_per_generation / gradient_accumulation_steps = 2. 则 1 次 rollout 结果将拆分成 2 批 mini-batch 进行更新。
|
||||
|
||||
**7. swift deploy 与 swift rollout 的区别**
|
||||
|
||||
- swift deploy 主要用于模型的部署和推理,支持 PT、vLLM、SGLang 等多种引擎,兼容流式推理与 OpenAI API 的调用格式。
|
||||
|
||||
- swift rollout 则专注于 GRPO 推理加速,目前仅支持 vLLM 引擎,并内置了权重自动同步的功能。
|
||||
|
||||
|
||||
**8. 如何取消 KL 项损失**
|
||||
|
||||
将参数设置为 `--beta 0`,即可关闭 KL 损失的计算,并且不会加载参考模型(ref model)。
|
||||
|
||||
|
||||
## RL微信群
|
||||
|
||||
<img src="https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/wechat/grpo.png" width="250">
|
||||
@@ -0,0 +1,6 @@
|
||||
Get Started
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
GRPO.md
|
||||
@@ -0,0 +1,19 @@
|
||||
GRPO
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Get Started
|
||||
|
||||
GetStarted/index.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Developer Guide
|
||||
|
||||
DeveloperGuide/index.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Advanced Research
|
||||
|
||||
AdvancedResearch/index.rst
|
||||
@@ -0,0 +1,352 @@
|
||||
# 推理和部署
|
||||
|
||||
以下为swift支持的推理引擎以及接入部分的相应能力,三种推理加速引擎为SWIFT的推理、部署、评测模块提供推理加速:
|
||||
|
||||
| 推理加速引擎 | OpenAI API | 多模态 | 量化模型 | 多LoRA | QLoRA | Batch推理 | 并行技术 |
|
||||
| ------------ | -------------- | ---------- | ------ | -------- | ------ | ----- | ----- |
|
||||
| transformers | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/deploy/client/llm/chat/openai_client.py) | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/app/mllm.sh) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_lora.py) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/batch_ddp.sh) |DDP/device_map |
|
||||
| [vllm](https://github.com/vllm-project/vllm) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/infer/vllm/mllm_tp.sh) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/deploy/lora/server.sh) | ❌ | ✅ | TP/PP/DP |
|
||||
| [sglang](https://github.com/sgl-project/sglang) | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | TP/PP/DP/EP |
|
||||
| [lmdeploy](https://github.com/InternLM/lmdeploy) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/infer/lmdeploy/mllm_tp.sh) | ✅ | ❌ | ❌ | ✅ | TP/DP |
|
||||
|
||||
|
||||
## 推理
|
||||
ms-swift使用了分层式的设计思想,用户可以使用命令行界面、Web-UI界面和直接使用Python的方式进行推理。
|
||||
|
||||
如果要查看LoRA微调后模型的推理,可以参考[预训练与微调文档](./Pre-training-and-Fine-tuning.md#推理微调后模型)。
|
||||
|
||||
### 使用CLI
|
||||
|
||||
**全参数模型:**
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift infer \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--stream true \
|
||||
--infer_backend transformers \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
**LoRA模型:**
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift infer \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--adapters swift/test_lora \
|
||||
--stream true \
|
||||
--infer_backend transformers \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
|
||||
**命令行推理指令**
|
||||
|
||||
以上为交互式命令行界面推理,脚本运行后仅需在terminal中输入query即可。你也可以输入以下特殊指令:
|
||||
- `multi-line`: 切换到多行模式,在输入中支持换行输入,以`#`代表输入结束。
|
||||
- `single-line`: 切换到单行模式,以换行代表输入结束。
|
||||
- `reset-system`: 重置system并清空历史记录。
|
||||
- `clear`: 清除历史记录。
|
||||
- `quit` or `exit`: 退出对话。
|
||||
|
||||
**多模态模型**
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
MAX_PIXELS=1003520 \
|
||||
VIDEO_MAX_PIXELS=50176 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
swift infer \
|
||||
--model Qwen/Qwen2.5-VL-3B-Instruct \
|
||||
--stream true \
|
||||
--infer_backend transformers \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
如果要进行多模态模型的推理,可以在query中添加`<image>/<video>/<audio>`等标签(代表图像表征在`inputs_embeds`中的位置),例如输入`<image><image>这两张图有啥区别`,`<video>描述这段视频`。然后根据提示输入相应的图像/视频/音频即可。
|
||||
|
||||
以下为一个推理的示例:
|
||||
```
|
||||
<<< <image><image>这两张图有什么区别
|
||||
Input an image path or URL <<< http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png
|
||||
Input an image path or URL <<< http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png
|
||||
这两张图片的区别在于它们所展示的动物和场景。
|
||||
|
||||
1. **第一张图片**:
|
||||
- 展示了一只小猫。
|
||||
- 小猫有大大的眼睛,表情显得有些困惑或好奇。
|
||||
- 背景是模糊的,可能是室内环境。
|
||||
|
||||
2. **第二张图片**:
|
||||
- 展示了一群羊。
|
||||
- 羊们站在草地上,背景是绿色的山丘和蓝天白云。
|
||||
- 羊的表情看起来很平静,似乎在享受大自然的环境。
|
||||
|
||||
总结来说,第一张图片是一只小猫,而第二张图片是一群羊。
|
||||
--------------------------------------------------
|
||||
<<< clear
|
||||
<<< <video>描述这段视频
|
||||
Input a video path or URL <<< https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4
|
||||
The video shows a baby wearing sunglasses sitting on a bed and reading a book. The baby is holding the book with both hands and appears to be focused on the pages. The baby's feet are visible in the frame, and they are moving slightly as they read. The background of the video shows a room with a bed and some furniture.
|
||||
```
|
||||
|
||||
|
||||
**数据集推理:**
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift infer \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--stream true \
|
||||
--infer_backend transformers \
|
||||
--val_dataset AI-ModelScope/alpaca-gpt4-data-zh \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
以上提供了全参数和LoRA流式推理的例子,以下介绍更多SWIFT中的推理技术:
|
||||
- 界面推理:你可以将`swift infer`改成`swift app`。
|
||||
- batch推理:`infer_backend=transformers`可以指定`--max_batch_size`对大模型和多模态大模型进行batch推理,具体参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/batch_ddp.sh)。在进行batch推理时,你不能设置`--stream true`。
|
||||
- DDP/device_map推理:`infer_backend=transformers`支持使用DDP/device_map技术进行并行推理,具体参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/mllm_device_map.sh)。
|
||||
- 推理加速:swift支持使用vllm/sglang/lmdeploy对推理、部署和评测模块进行推理加速,只需要额外指定`--infer_backend vllm/sglang/lmdeploy`即可。可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/vllm/mllm_ddp.sh)。
|
||||
- 多模态模型:我们提供了[transformers](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/mllm_device_map.sh)/[vllm](https://github.com/modelscope/ms-swift/blob/main/examples/infer/vllm/mllm_tp.sh)/[lmdeploy](https://github.com/modelscope/ms-swift/blob/main/examples/infer/lmdeploy/mllm_tp.sh)对多模态模型进行多GPU推理的shell脚本。
|
||||
- 量化模型:直接选择GPTQ、AWQ、BNB量化的模型,例如:`--model Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4`即可。
|
||||
- 更多模型类型:我们提供了[bert](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/bert.sh)、[reward_model](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/reward_model.sh)、[prm](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/prm.sh)的推理脚本。
|
||||
|
||||
|
||||
**小帖士:**
|
||||
- SWIFT会将推理结果保存起来,你可以通过`--result_path`指定保存路径。
|
||||
- 如果要输出logprobs,只需要在推理时,指定`--logprobs true`即可。SWIFT会保存。注意,设置`--stream true`将不会存储。
|
||||
- infer_backend为'transformers'支持所有swift已支持模型的推理,而infer_backend为vllm/sglang/lmdeploy只支持部分模型,具体请参考[vllm](https://docs.vllm.ai/en/latest/models/supported_models.html)、[sglang](https://docs.sglang.ai/supported_models/generative_models.html)、[lmdeploy](https://lmdeploy.readthedocs.io/en/latest/supported_models/supported_models.html)文档。
|
||||
- 使用`--infer_backend vllm`出现OOM,可以通过降低`--vllm_max_model_len`,`--vllm_max_num_seqs`,选择合适的`--vllm_gpu_memory_utilization`,设置`--vllm_enforce_eager true`。或者使用tensor并行`--vllm_tensor_parallel_size`来解决。
|
||||
- 使用`--infer_backend vllm`推理多模态模型,需要传入多张图片。可以设置`--vllm_limit_mm_per_prompt`解决,例如:`--vllm_limit_mm_per_prompt '{"image": 10, "video": 5}'`。
|
||||
- 推理qwen2-vl/qwen2.5-vl出现OOM,可以通过设置`MAX_PIXELS`、`VIDEO_MAX_PIXELS`、`FPS_MAX_FRAMES`解决,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/app/mllm.sh)。
|
||||
- swift内置对话模板与使用transformers运行的对话模板对齐,测试参考[这里](https://github.com/modelscope/ms-swift/blob/main/tests/test_align/test_template/test_vision.py)。如果出现未对齐情况,欢迎提issue/PR修正。
|
||||
|
||||
|
||||
### 使用Web-UI
|
||||
如果你要使用界面的方式进行推理,可以查看[Web-UI文档](../GetStarted/Web-UI.md)。
|
||||
|
||||
### 使用Python
|
||||
|
||||
文本模型:
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
|
||||
from swift.infer_engine import TransformersEngine, RequestConfig, InferRequest
|
||||
model = 'Qwen/Qwen2.5-0.5B-Instruct'
|
||||
|
||||
# 加载推理引擎
|
||||
engine = TransformersEngine(model, max_batch_size=2)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了2个infer_request来展示batch推理
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '浙江的省会在哪?'},
|
||||
{'role': 'assistant', 'content': '浙江省的省会是杭州。'},
|
||||
{'role': 'user', 'content': '这里有什么好玩的地方'},]),
|
||||
]
|
||||
resp_list = engine.infer(infer_requests, request_config)
|
||||
query0 = infer_requests[0].messages[0]['content']
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
```
|
||||
|
||||
多模态模型:
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['MAX_PIXELS'] = '1003520'
|
||||
os.environ['VIDEO_MAX_PIXELS'] = '50176'
|
||||
os.environ['FPS_MAX_FRAMES'] = '12'
|
||||
|
||||
from swift.infer_engine import TransformersEngine, RequestConfig, InferRequest
|
||||
model = 'Qwen/Qwen2.5-VL-3B-Instruct'
|
||||
|
||||
# 加载推理引擎
|
||||
engine = TransformersEngine(model, max_batch_size=2)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了3个infer_request来展示batch推理
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<image><image>两张图的区别是什么?'}],
|
||||
images=['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png',
|
||||
'http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png']),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<video>describe the video'}],
|
||||
videos=['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']),
|
||||
]
|
||||
resp_list = engine.infer(infer_requests, request_config)
|
||||
query0 = infer_requests[0].messages[0]['content']
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
print(f'response2: {resp_list[2].choices[0].message.content}')
|
||||
```
|
||||
|
||||
我们也提供了更多使用python推理的demo:
|
||||
- 使用流式推理以及`VllmEngine`、`SglangEngine`、`LmdeployEngine`进行推理加速,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo.py)。
|
||||
- 多模态推理:除了上述多模态输入格式外,swift兼容OpenAI的多模态输入格式,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_mllm.py)。
|
||||
- grounding任务:对多模态模型进行Grounding任务画框,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_grounding.py)。
|
||||
- 多LoRA推理:参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_lora.py)。
|
||||
- agent推理:参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_agent.py)。
|
||||
- 异步接口:使用`engine.infer_async`进行python方式推理,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo.py)。
|
||||
|
||||
|
||||
## 部署
|
||||
|
||||
如果要查看LoRA微调后模型的部署,可以参考[预训练与微调文档](./Pre-training-and-Fine-tuning.md#部署微调后模型)。
|
||||
|
||||
这里主要介绍对多模态模型的部署和调用,文本大模型我们给出简单的部署和调用的简单案例:
|
||||
|
||||
服务端部署:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift deploy \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--infer_backend vllm \
|
||||
--max_new_tokens 2048 \
|
||||
--served_model_name Qwen2.5-7B-Instruct
|
||||
```
|
||||
|
||||
客户端调用测试:
|
||||
```shell
|
||||
curl http://localhost:8000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "Qwen2.5-7B-Instruct",
|
||||
"messages": [{"role": "user", "content": "晚上睡不着觉怎么办?"}],
|
||||
"max_tokens": 256,
|
||||
"temperature": 0
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
### 服务端
|
||||
|
||||
```shell
|
||||
# test env: pip install transformers==4.51.3 vllm==0.8.5.post1
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
MAX_PIXELS=1003520 \
|
||||
VIDEO_MAX_PIXELS=50176 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
swift deploy \
|
||||
--model Qwen/Qwen2.5-VL-3B-Instruct \
|
||||
--infer_backend vllm \
|
||||
--vllm_gpu_memory_utilization 0.9 \
|
||||
--vllm_max_model_len 8192 \
|
||||
--max_new_tokens 2048 \
|
||||
--vllm_limit_mm_per_prompt '{"image": 5, "video": 2}' \
|
||||
--served_model_name Qwen2.5-VL-3B-Instruct
|
||||
```
|
||||
|
||||
|
||||
### 客户端
|
||||
|
||||
这里介绍3种调用客户端的方式,分别是curl、openai库和swift客户端。
|
||||
|
||||
|
||||
方案一: curl
|
||||
```shell
|
||||
curl http://localhost:8000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "Qwen2.5-VL-3B-Instruct",
|
||||
"messages": [{"role": "user", "content": [
|
||||
{"type": "image", "image": "http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png"},
|
||||
{"type": "image", "image": "http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png"},
|
||||
{"type": "text", "text": "两张图的区别是什么?"}
|
||||
]}],
|
||||
"max_tokens": 256,
|
||||
"temperature": 0
|
||||
}'
|
||||
```
|
||||
|
||||
方案2: openai库
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key='EMPTY',
|
||||
base_url=f'http://127.0.0.1:8000/v1',
|
||||
)
|
||||
model = client.models.list().data[0].id
|
||||
print(f'model: {model}')
|
||||
|
||||
messages = [{'role': 'user', 'content': [
|
||||
{'type': 'video', 'video': 'https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4'},
|
||||
{'type': 'text', 'text': 'describe the video'}
|
||||
]}]
|
||||
|
||||
resp = client.chat.completions.create(model=model, messages=messages, max_tokens=512, temperature=0)
|
||||
query = messages[0]['content']
|
||||
response = resp.choices[0].message.content
|
||||
print(f'query: {query}')
|
||||
print(f'response: {response}')
|
||||
|
||||
# base64
|
||||
import base64
|
||||
import requests
|
||||
resp = requests.get('https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4')
|
||||
base64_encoded = base64.b64encode(resp.content).decode('utf-8')
|
||||
messages = [{'role': 'user', 'content': [
|
||||
{'type': 'video', 'video': f'data:video/mp4;base64,{base64_encoded}'},
|
||||
{'type': 'text', 'text': 'describe the video'}
|
||||
]}]
|
||||
|
||||
gen = client.chat.completions.create(model=model, messages=messages, stream=True, temperature=0)
|
||||
print(f'query: {query}\nresponse: ', end='')
|
||||
for chunk in gen:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
```
|
||||
|
||||
方案三:swift客户端
|
||||
|
||||
```python
|
||||
from swift import InferRequest, InferClient, RequestConfig, InferStats
|
||||
|
||||
engine = InferClient(host='127.0.0.1', port=8000)
|
||||
print(f'models: {engine.models}')
|
||||
metric = InferStats()
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了3个infer_request来展示batch推理
|
||||
# 支持传入本地路径、base64和url
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<image><image>两张图的区别是什么?'}],
|
||||
images=['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png',
|
||||
'http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png']),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<video>describe the video'}],
|
||||
videos=['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']),
|
||||
]
|
||||
|
||||
resp_list = engine.infer(infer_requests, request_config, metrics=[metric])
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
print(f'response2: {resp_list[2].choices[0].message.content}')
|
||||
print(metric.compute())
|
||||
metric.reset()
|
||||
|
||||
# base64
|
||||
import base64
|
||||
import requests
|
||||
resp = requests.get('https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4')
|
||||
base64_encoded = base64.b64encode(resp.content).decode('utf-8')
|
||||
messages = [{'role': 'user', 'content': [
|
||||
{'type': 'video', 'video': f'data:video/mp4;base64,{base64_encoded}'},
|
||||
{'type': 'text', 'text': 'describe the video'}
|
||||
]}]
|
||||
infer_request = InferRequest(messages=messages)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0, stream=True)
|
||||
gen_list = engine.infer([infer_request], request_config, metrics=[metric])
|
||||
print(f'response0: ', end='')
|
||||
for chunk in gen_list[0]:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
print(metric.compute())
|
||||
```
|
||||
我们也提供了更多部署的demo:
|
||||
- 多LoRA部署与调用: 参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/lora)。
|
||||
- Base模型的部署与调用: 参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/client/llm/base)。
|
||||
- 更多模型类型: 我们提供了[bert](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/bert)、[reward_model](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/reward_model)的部署脚本。
|
||||
@@ -0,0 +1,319 @@
|
||||
# 预训练与微调
|
||||
|
||||
训练能力:
|
||||
|
||||
| 方法 | 全参数 | LoRA | QLoRA | Deepspeed | 多机 | 多模态 |
|
||||
| ------ | ------ |---------------------------------------------------------------------------------------------| ----- | ------ | ------ |----------------------------------------------------------------------------------------------|
|
||||
| [预训练](https://github.com/modelscope/ms-swift/blob/main/examples/train/pretrain) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [指令监督微调](https://github.com/modelscope/ms-swift/blob/main/examples/train/lora_sft.sh) | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/train/full/train.sh) | ✅ | [✅](https://github.com/modelscope/ms-swift/tree/main/examples/train/qlora) | [✅](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-gpu/deepspeed) | [✅](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-node) | [✅](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal) |
|
||||
| [GRPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [GKD](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/gkd) | ✅ | ✅ | ✅ | ✅ | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/train/multimodal/rlhf/gkd) |
|
||||
| [PPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/ppo) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| [DPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/dpo) | ✅ | ✅ | ✅ | ✅ | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/train/multimodal/rlhf/dpo) |
|
||||
| [KTO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/kto.sh) | ✅ | ✅ | ✅ | ✅ | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/train/multimodal/rlhf/kto.sh) |
|
||||
| [奖励模型](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/rm.sh) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [CPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/cpo.sh) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [SimPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/simpo.sh) | ✅ | ✅ | ✅ | ✅| ✅ | ✅ |
|
||||
| [ORPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/orpo.sh) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [Embedding](https://github.com/modelscope/ms-swift/blob/main/examples/train/embedding) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [Reranker](https://github.com/modelscope/ms-swift/tree/main/examples/train/reranker) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [序列分类](https://github.com/modelscope/ms-swift/blob/main/examples/train/seq_cls) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
## 环境准备
|
||||
推荐的第三方库版本参考[SWIFT安装文档](../GetStarted/SWIFT-installation.md)
|
||||
```bash
|
||||
pip install ms-swift -U
|
||||
|
||||
# 若使用deepspeed zero2/zero3
|
||||
pip install deepspeed -U
|
||||
```
|
||||
|
||||
## 预训练
|
||||
预训练使用`swift pt`命令,这将自动使用生成式而非对话式的template,即将`use_chat_template`设置为False(其他所有的命令,例如`swift sft/rlhf/infer`,都默认将`use_chat_template`设置为True)。此外,`swift pt`与`swift sft`相比,具有不同的数据集格式,可以参考[自定义数据集文档](../Customization/Custom-dataset.md)。
|
||||
|
||||
使用CLI进行预训练的脚本可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/pretrain/train.sh)。更多训练技术的介绍可以参考微调章节。
|
||||
|
||||
小贴士:
|
||||
- `swift pt`与`swift sft --use_chat_template false --loss_scale all`等价。
|
||||
|
||||
## 微调
|
||||
|
||||
ms-swift使用了分层式的设计思想,用户可以使用命令行界面、Web-UI界面和直接使用Python的方式进行微调。
|
||||
|
||||
### 使用CLI
|
||||
|
||||
我们提供了10分钟在单卡3090上对Qwen2.5-7B-Instruct进行自我认知微调的最佳实践,具体参考[这里](../GetStarted/Quick-start.md),这可以帮助您快速了解SWIFT。
|
||||
|
||||
此外,我们给出了一系列脚本帮助您了解SWIFT的训练能力:
|
||||
|
||||
- 轻量化训练:SWIFT支持的轻量微调示例可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/tuners)。(注意:这些方式预训练也可以使用,但预训练通常使用全参数训练)。
|
||||
- 分布式训练:SWIFT支持的分布式训练技术包括:DDP、device_map、DeepSpeed ZeRO2/ZeRO3、FSDP。
|
||||
- device_map: 简易模型并行。如果存在多GPU,device_map会自动开启。这会将模型按层均匀的划分到可见的GPU中,显著降低显存消耗,但是训练速度通常会降低,因为是串行的。
|
||||
- DDP+device_map:将按组对模型进行device_map划分,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/multi-gpu/ddp_device_map/train.sh)。
|
||||
- DeepSpeed ZeRO2/ZeRO3: 节约显存资源,但训练速度下降。ZeRO2将对优化器状态、模型梯度进行分片。ZeRO3在ZeRO2基础上,对模型参数进行分片,更加节约显存,但训练速度更慢。参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-gpu/deepspeed)。
|
||||
- FSDP+QLoRA: 双卡3090运行70B模型的训练,参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-gpu/fsdp_qlora/train.sh)。
|
||||
- 多机多卡训练: 我们书写了使用swift、torchrun、dlc、deepspeed、accelerate启动多节点运行的shell脚本示例。除了dlc和deepspeed,其他启动脚本都需要在所有节点中启动才可运行。具体参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/multi-node)。
|
||||
- 量化训练:支持使用GPTQ、AWQ、AQLM、BNB、HQQ、EETQ量化技术的QLoRA训练。微调7B模型只需要9GB显存资源。具体参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/qlora)。
|
||||
- 多模态训练:SWIFT支持多模态模型的预训练、微调和RLHF。支持Caption、VQA、OCR、[Grounding](https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2_5-vl-grounding/zh.ipynb)任务。支持图像、视频和音频三种模态。具体参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal)。多模态自定义数据集格式参考[自定义数据集文档](../Customization/Custom-dataset.md)。
|
||||
- 对ViT/Aligner使用全参数训练,LLM使用LoRA训练,并采用不同学习率的例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal/lora_llm_full_vit)。
|
||||
- 多模态模型packing,增加训练速度,例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/packing)。
|
||||
- RLHF训练:参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf)。多模态模型参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal/rlhf)。GRPO训练参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/internal)。强化微调查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rft)。
|
||||
- Megatron训练:支持使用Megatron的并行技术来加速大模型的训练,包括数据并行、张量并行、流水线并行、序列并行,上下文并行。参考[Megatron-SWIFT训练文档](../Megatron-SWIFT/Quick-start.md)。
|
||||
- 序列分类模型训练:参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/seq_cls)。
|
||||
- Embedding模型训练:参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/embedding)
|
||||
- Agent训练:参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/agent)。
|
||||
- Any-to-Any模型训练:参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/all_to_all)。
|
||||
- 其他能力:
|
||||
- 数据流式读取: 在数据量较大时减少内存使用。参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/streaming/streaming.sh)。
|
||||
- packing: 将多个序列拼成一个,让每个训练样本尽可能接近max_length,提高显卡利用率,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/packing)。
|
||||
- 长文本训练: 参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/sequence_parallel)。
|
||||
- lazy tokenize: 在训练期间对数据进行tokenize而不是在训练前tokenize(多模态模型可以避免在训练前读入所有多模态资源),这可以避免预处理等待并节约内存。参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/streaming/lazy_tokenize.sh)。
|
||||
|
||||
小帖士:
|
||||
|
||||
- 在使用`swift sft`通过LoRA技术微调base模型为chat模型时,有时需要手动设置模板。通过添加`--template default`参数来避免base模型因未见过对话模板中的特殊字符而无法正常停止的情况。具体参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/base_to_chat)。
|
||||
- 如果需要在**断网**环境下进行训练,请设置`--model <model_dir>`和`--check_model false`。如果对应的模型需要`git clone`github的仓库,例如`deepseek-ai/Janus-Pro-7B`,请设置手动下载仓库,并设置`--local_repo_path <repo_dir>`。具体参数含义请参考[命令行参数文档](Command-line-parameters.md)。
|
||||
- 无法对QLoRA训练的模型进行Merge LoRA,因此不建议使用QLoRA进行微调,无法在推理和部署时使用vLLM/Sglang/LMDeploy进行推理加速。建议使用LoRA/全参数进行微调,合并为完整权重后再使用GPTQ/AWQ/BNB进行[量化](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize)。
|
||||
- 如果使用NPU进行训练,只需要将shell中的`CUDA_VISIBLE_DEVICES`修改为`ASCEND_RT_VISIBLE_DEVICES`。
|
||||
- SWIFT默认在训练时设置`--gradient_checkpointing true`来节约显存,这会略微降低训练速度。
|
||||
- 若使用DDP进行训练,出现报错:`RuntimeError: Expected to mark a variable ready only once.`,请额外设置参数`--gradient_checkpointing_kwargs '{"use_reentrant": false}'`或者使用DeepSpeed进行训练。
|
||||
- 如果要使用deepspeed,你需要安装deepspeed:`pip install deepspeed -U`。使用deepspeed可以节约显存,但会略微降低训练速度。
|
||||
- 如果您的机器是A100等高性能显卡,且模型支持flash-attn,推荐你安装[flash-attn](https://github.com/Dao-AILab/flash-attention/releases),并设置`--attn_impl flash_attn`,这将会加快训练和推理的速度并略微降低显存占用。
|
||||
|
||||
**如何debug:**
|
||||
|
||||
你可以使用以下方式进行debug,这与使用命令行微调是等价的,但此方式不支持分布式。微调命令行运行入口可以查看[这里](https://github.com/modelscope/ms-swift/blob/main/swift/cli/sft.py)。
|
||||
|
||||
```python
|
||||
from swift import sft_main, SftArguments
|
||||
result = sft_main(SftArguments(
|
||||
model='Qwen/Qwen2.5-7B-Instruct',
|
||||
tuner_type='lora',
|
||||
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#500',
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500',
|
||||
'swift/self-cognition#500'],
|
||||
torch_dtype='bfloat16',
|
||||
# ...
|
||||
))
|
||||
```
|
||||
|
||||
|
||||
### 使用Web-UI
|
||||
如果你要使用界面的方式进行训练,可以查看[Web-UI文档](../GetStarted/Web-UI.md)。
|
||||
|
||||
### 使用python
|
||||
|
||||
- Qwen2.5自我认知微调notebook查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2_5-self-cognition/self-cognition-sft.ipynb)。
|
||||
- Qwen2VL进行OCR任务notebook查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2vl-ocr/ocr-sft.ipynb)。
|
||||
|
||||
|
||||
## Merge LoRA
|
||||
|
||||
- 查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/export/merge_lora.sh)。
|
||||
|
||||
## 推理(微调后模型)
|
||||
|
||||
使用CLI对LoRA训练的checkpoint进行推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend transformers \
|
||||
--stream true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
- adapters文件夹中包含了训练的参数文件`args.json`,因此不需要额外指定`--model`,`--system`,swift会自动读取这些参数。如果要关闭此行为,可以设置`--load_args false`。
|
||||
- 如果使用全参数训练,请使用`--model`替代`--adapters`指定训练的checkpoint目录。更多参考[推理和部署文档](./Inference-and-deployment.md#推理)。
|
||||
- 你可以使用`swift app`替代`swift infer`进行界面推理。
|
||||
- 你可以选择对LoRA进行merge(额外指定`--merge_lora true`),然后指定`--infer_backend vllm/sglang/lmdeploy`进行推理加速。
|
||||
|
||||
对数据集中的验证集进行批量推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend transformers \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048 \
|
||||
--load_data_args true \
|
||||
--max_batch_size 1
|
||||
```
|
||||
|
||||
- 你可以设置`--max_batch_size 8`,从而使用`--infer_backend transformers`进行批量处理。若使用`infer_backend vllm/sglang/lmdeploy`则无需指定,会进行自动batch。
|
||||
- `--load_data_args true`会额外读取训练存储参数文件`args.json`中的数据参数。
|
||||
|
||||
若想对额外的测试集进行推理,而不使用训练时的验证集,使用`--val_dataset <dataset_path>`进行推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend transformers \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048 \
|
||||
--val_dataset <dataset-path> \
|
||||
--max_batch_size 1
|
||||
```
|
||||
|
||||
|
||||
使用Python对训练后LoRA推理的例子如下:
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
|
||||
from swift.infer_engine import TransformersEngine, RequestConfig, InferRequest
|
||||
from swift import get_model_processor, get_template
|
||||
from swift.utils import safe_snapshot_download
|
||||
from peft import PeftModel
|
||||
# 请调整下面几行
|
||||
model = 'Qwen/Qwen2.5-7B-Instruct'
|
||||
lora_checkpoint = safe_snapshot_download('swift/test_lora') # 修改成checkpoint_dir
|
||||
template_type = None # None: 使用对应模型默认的template_type
|
||||
default_system = "You are a helpful assistant." # None: 使用对应模型默认的default_system
|
||||
|
||||
# 加载模型和对话模板
|
||||
model, tokenizer = get_model_processor(model)
|
||||
if lora_checkpoint is not None:
|
||||
model = PeftModel.from_pretrained(model, lora_checkpoint)
|
||||
template_type = template_type or model.model_meta.template
|
||||
template = get_template(tokenizer, template_type=template_type, default_system=default_system)
|
||||
engine = TransformersEngine(model, template=template, max_batch_size=2)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了2个infer_request来展示batch推理
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '浙江的省会在哪?'},
|
||||
{'role': 'assistant', 'content': '浙江的省会在哪?'},
|
||||
{'role': 'user', 'content': '这里有什么好吃的'},]),
|
||||
]
|
||||
resp_list = engine.infer(infer_requests, request_config)
|
||||
query0 = infer_requests[0].messages[0]['content']
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
```
|
||||
|
||||
多模态模型的LoRA推理示例如下:
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
|
||||
from swift.infer_engine import TransformersEngine, RequestConfig, InferRequest
|
||||
from swift import get_model_processor, get_template
|
||||
from swift.utils import safe_snapshot_download
|
||||
from peft import PeftModel
|
||||
# 请调整下面几行
|
||||
model = 'Qwen/Qwen2.5-VL-7B-Instruct'
|
||||
lora_checkpoint = safe_snapshot_download('swift/test_grounding') # 修改成checkpoint_dir
|
||||
template_type = None # None: 使用对应模型默认的template_type
|
||||
default_system = None # None: 使用对应模型默认的default_system
|
||||
|
||||
# 加载模型和对话模板
|
||||
model, tokenizer = get_model_processor(model)
|
||||
if lora_checkpoint is not None:
|
||||
model = PeftModel.from_pretrained(model, lora_checkpoint)
|
||||
template_type = template_type or model.model_meta.template
|
||||
template = get_template(tokenizer, template_type=template_type, default_system=default_system)
|
||||
engine = TransformersEngine(model, template=template, max_batch_size=2)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了2个infer_request来展示batch推理
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<image>Task: Object Detection'}],
|
||||
images=['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png']),
|
||||
]
|
||||
resp_list = engine.infer(infer_requests, request_config)
|
||||
query0 = infer_requests[0].messages[0]['content']
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
```
|
||||
|
||||
如果使用ms-swift训练的模型,可以通过以下方式获取训练的配置:
|
||||
```python
|
||||
from swift import safe_snapshot_download, BaseArguments
|
||||
|
||||
lora_adapters = safe_snapshot_download('swift/test_lora')
|
||||
args = BaseArguments.from_pretrained(lora_adapters)
|
||||
print(f'args.model: {args.model}')
|
||||
print(f'args.model_type: {args.model_type}')
|
||||
print(f'args.template_type: {args.template}')
|
||||
print(f'args.default_system: {args.system}')
|
||||
```
|
||||
|
||||
- 对全参数训练的checkpoint进行推理,将`model`设置为checkpoint_dir,并将lora_checkpoint设置为None即可。更多参考[推理和部署文档](./Inference-and-deployment.md#推理)。
|
||||
- 使用流式推理以及`VllmEngine`、`SglangEngine`、`LmdeployEngine`进行推理加速,可以参考[大模型](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo.py)和[多模态大模型](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_mllm.py)推理示例。
|
||||
- 微调后的模型使用huggingface transformers/peft生态推理,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_hf.py)。
|
||||
- 若训练了多个LoRA,要进行多LoRA切换,可以参考[推理](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_lora.py)、[部署](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/lora)样例。
|
||||
- 对多模态模型进行Grounding任务的画框,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_grounding.py)。
|
||||
- 对LoRA微调后的Bert进行推理,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_bert.py)。
|
||||
|
||||
|
||||
## 部署(微调后模型)
|
||||
|
||||
使用以下命令启动部署服务端。如果权重使用全参数训练,请使用`--model`替代`--adapters`指定训练的checkpoint目录。你可以参考[推理和部署文档](./Inference-and-deployment.md#部署)介绍的客户端调用方式:curl、openai库和swift客户端进行调用。
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift deploy \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend transformers \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048 \
|
||||
--served_model_name '<model-name>'
|
||||
```
|
||||
|
||||
这里将给出使用vLLM对多LoRA进行部署并调用的完整例子。
|
||||
|
||||
### 服务端
|
||||
首先你需要安装vLLM:`pip install vllm -U`,并在部署时使用`--infer_backend vllm`,这通常可以显著加速推理速度。
|
||||
|
||||
我们预先训练了2个基模型为`Qwen/Qwen2.5-7B-Instruct`的不同自我认知LoRA增量权重(可以直接跑通),我们可以在[args.json](https://modelscope.cn/models/swift/test_lora/file/view/master)中找到相关信息。你需要在部署时修改`--adapters`指定训练好的LoRA权重本地路径即可。
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift deploy \
|
||||
--adapters lora1=swift/test_lora lora2=swift/test_lora2 \
|
||||
--infer_backend vllm \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
### 客户端
|
||||
|
||||
这里只介绍使用openai库进行调用。使用curl、swift客户端调用的例子可以参考[推理和部署文档](./Inference-and-deployment.md#部署)。
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key='EMPTY',
|
||||
base_url=f'http://127.0.0.1:8000/v1',
|
||||
)
|
||||
models = [model.id for model in client.models.list().data]
|
||||
print(f'models: {models}')
|
||||
|
||||
query = 'who are you?'
|
||||
messages = [{'role': 'user', 'content': query}]
|
||||
|
||||
resp = client.chat.completions.create(model=models[1], messages=messages, max_tokens=512, temperature=0)
|
||||
query = messages[0]['content']
|
||||
response = resp.choices[0].message.content
|
||||
print(f'query: {query}')
|
||||
print(f'response: {response}')
|
||||
|
||||
gen = client.chat.completions.create(model=models[2], messages=messages, stream=True, temperature=0)
|
||||
print(f'query: {query}\nresponse: ', end='')
|
||||
for chunk in gen:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
"""
|
||||
models: ['Qwen2.5-7B-Instruct', 'lora1', 'lora2']
|
||||
query: who are you?
|
||||
response: I am an artificial intelligence model named swift-robot, developed by swift. I can answer your questions, provide information, and engage in conversation. If you have any inquiries or need assistance, feel free to ask me at any time.
|
||||
query: who are you?
|
||||
response: I am an artificial intelligence model named Xiao Huang, developed by ModelScope. I can answer your questions, provide information, and engage in conversation. If you have any inquiries or need assistance, feel free to ask me at any time.
|
||||
"""
|
||||
```
|
||||
@@ -0,0 +1,137 @@
|
||||
# 人类对齐
|
||||
|
||||
本文档提供了各种人类偏好对齐算法的训练脚本。若您希望深入了解更详尽的算法信息及其选择方法,请参考[文档](https://github.com/modelscope/modelscope-classroom/blob/main/LLM-tutorial/M.%E4%BA%BA%E7%B1%BB%E5%81%8F%E5%A5%BD%E5%AF%B9%E9%BD%90%E8%AE%AD%E7%BB%83.md)
|
||||
|
||||
|
||||
## 数据集
|
||||
PPO与GRPO算法所需的数据仅为模型输入,也就是system prompt(可选)加上query。其中GRPO中的奖励函数可能需要额外的数据列,比如计算准确率需要`solution`列作为参考答案。
|
||||
|
||||
RM和DPO类算法如ORPO,CPO,SimPO,则需要 $(x,y_w,y_l)$ 格式的数据,其中 $x$ 表示模型输入,$y_w,y_l$ 分别表示符合人类偏好的偏好回答和不符合人类偏好的拒绝回答,比如
|
||||
|
||||
而KTO算法的数据比较特殊,只需要 $(x,y,\text{label})$ 格式的数据,其中 $x$ 表示模型输入,$y$ 表示模型输出,label表示回答是否符合人类偏好
|
||||
比如
|
||||
|
||||
使用自定义数据集对文本模型或者多模态大模型进行RLHF训练可以参考[自定义数据集文档](../Customization/Custom-dataset.md#rlhf)。
|
||||
|
||||
## GRPO
|
||||
[论文arvix](https://arxiv.org/abs/2402.03300)
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo).
|
||||
|
||||
## DPO
|
||||
[论文arvix](https://arxiv.org/abs/2305.18290)
|
||||
|
||||
超参
|
||||
|
||||
- beta:KL正则系数,值越大表示对偏离参考模型的惩罚越强。默认为0.1。
|
||||
- loss_type:不同DPO算法变种,可选值参考[文档](https://huggingface.co/docs/trl/main/en/dpo_trainer#loss-functions),默认为 'sigmoid'。
|
||||
- (可选) loss_weights: 多种 loss 混合时的权重设置。
|
||||
- (可选) ld_alpha: 来自[LD-DPO 论文](https://arxiv.org/abs/2409.06411),对超出公共前缀部分的logps加权 $\alpha$ 抑制长度偏好。
|
||||
- (可选) discopop_tau: 来自 [DiscoPOP 论文](https://arxiv.org/abs/2406.08414)的温度参数 $\tau$ ,用于缩放 log-ratio。默认值0.05。在 loss_type 为 discopop 时生效。
|
||||
|
||||
建议在开始 DPO 训练前,先用偏好数据集中用户偏好答案部分进行一次 SFT 训练,以确保数据分布更符合 DPO 算法的要求。
|
||||
|
||||
如需同时混合多个 loss(如用于 [MPO](https://arxiv.org/abs/2411.10442) 训练),可指定多个 loss_type,并通过 loss_weights 设置各自权重。
|
||||
|
||||
通过设置超参数 `rpo_alpha`,可在 loss 中混合一定比例的 SFT loss,从而提升训练的稳定性。
|
||||
|
||||
训练脚本参考
|
||||
|
||||
- [DPO脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/dpo).
|
||||
- [MPO脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/mpo.sh).
|
||||
|
||||
## RM
|
||||
[论文arvix](https://arxiv.org/abs/2203.02155)
|
||||
|
||||
RLHF中的Reward Modeling阶段
|
||||
|
||||
使用sft训练后的base model或者instruct model作为基底模型, 增加value head, 使用偏好数据集训练为reward model
|
||||
|
||||
增加的value head权重会保存在`value_head.safetensors` 或 `value_head.bin`文件中
|
||||
|
||||
RM损失函数如下
|
||||
|
||||
$
|
||||
\text{loss} = -\log \sigma \left( r^{(c)} - r^{(r)} - m \right) + \lambda \left( r^{(c)} + r^{(r)} \right)^2
|
||||
$
|
||||
|
||||
- $r^{(c)}$: 模型对 chosen response 的打分
|
||||
- $r^{(r)}$: 模型对 rejected response 的打分
|
||||
- $\lambda$: L2正则项系数,鼓励模型输出接近0,使用参数`center_rewards_coefficient`进行设置,来自[论文](https://arxiv.org/pdf/2307.09288), 默认为0
|
||||
- $m$: margin项,鼓励模型根据不同难度的样本进行区分,需要数据集中提供`margin`列,默认为0,来自[论文](https://arxiv.org/pdf/2307.09288)
|
||||
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/rm.sh).
|
||||
|
||||
## PPO
|
||||
[论文arvix](https://arxiv.org/abs/2203.02155)
|
||||
|
||||
RLHF中的PPO(proximal policy optimization)阶段, 涉及到四个模型
|
||||
- model: 训练模型, sft训练后的base model或者instruct model
|
||||
- ref_model: 参考模型, 默认为 model
|
||||
- reward_model: 奖励模型, 由RM阶段训练得到
|
||||
- value_model: 价值模型, 由reward_model初始化, 在训练中同步更新
|
||||
|
||||
超参
|
||||
|
||||
- local_rollout_forward_batch_size: 每次数据采样的批量大小, 默认为64
|
||||
- whiten_rewards: 对奖励进行归一化处理, 默认为False
|
||||
- kl_coef: KL散度项的系数, 默认为0.05
|
||||
- cliprange: PPO策略损失函数中的clip范围, 默认为0.2
|
||||
- vf_coef: 价值损失函数系数, 默认为0.1
|
||||
- cliprange_value: PPO价值损失函数中的clip范围, 默认为0.2
|
||||
- gamma: 累计奖励的折扣因子, 默认为1.0
|
||||
- lam: [GAE](https://arxiv.org/abs/1506.02438)中的lambda系数, 默认为0.95
|
||||
- num_sample_generations: 训练过程中生成的调试样本数量, 默认为10
|
||||
|
||||
|
||||
注意: 训练base model时, 需要先sft后再进行rlhf, 指定chat template, `tuner_type` 建议使用full
|
||||
|
||||
训练中的指标解释参考[文档](https://huggingface.co/docs/trl/ppov2_trainer#explanation-of-the-logged-metrics)
|
||||
|
||||
|
||||
## KTO
|
||||
[论文arvix](https://arxiv.org/abs/2402.01306)
|
||||
|
||||
超参
|
||||
|
||||
- beta: KL正则系数,值越大表示对偏离参考模型的惩罚越大。默认为0.1
|
||||
- desirable_weight :损失函数中的$\lambda_D$项,偏好回答样本的损失权重, 默认为1.0
|
||||
- undesirable_weight :损失函数中的$\lambda_U$项,拒绝回答样本的损失权重,默认为1.0
|
||||
|
||||
用 $n_D$ 和 $n_U$ 分别表示数据集中偏好回答和拒绝回答的样本数量,对于超参 $\lambda_D$ 和 $\lambda_U$ ,作者推荐设置 $\frac{\lambda_Dn_D}{\lambda_Un_U}\in[1,\frac{4}{3}]$
|
||||
|
||||
训练脚本
|
||||
使用 $(x,y,\text{label})$ 格式数据训练
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/kto.sh).
|
||||
|
||||
## CPO
|
||||
[论文arvix](https://arxiv.org/abs/2401.08417)
|
||||
超参
|
||||
|
||||
- beta:隐含奖励前的系数,默认为0.1
|
||||
- cpo_alpha: nll loss系数, 默认为1.0
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/cpo.sh).
|
||||
|
||||
## ORPO
|
||||
[论文arvix](https://arxiv.org/abs/2403.07691)
|
||||
|
||||
超参
|
||||
|
||||
- lambda: Odds Ratio loss系数
|
||||
|
||||
注意:ORPO使用参数`--beta`传入超参`lambda`
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/orpo.sh).
|
||||
|
||||
## SimPO
|
||||
[论文arvix](https://arxiv.org/abs/2405.14734)
|
||||
超参
|
||||
|
||||
- beta:隐含奖励前的系数,默认为2.0
|
||||
- simpo_gamma:reward margin项,默认为1.0
|
||||
- cpo_alpha: 混合CPO nll loss提高训练稳定性, 默认为1.0, 设置0.0使用原始SimPO算法
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/simpo.sh).
|
||||
@@ -0,0 +1,238 @@
|
||||
# ray的支持
|
||||
|
||||
## Megatron Ray
|
||||
|
||||
Megatron 后端支持通过 Ray 进行 GRPO 和 GKD 训练:
|
||||
|
||||
| 功能 | 例子 | 可分配角色 |
|
||||
|------|------|-----------|
|
||||
| megatron grpo | https://github.com/modelscope/ms-swift/tree/main/examples/ray/grpo | train/rollout |
|
||||
| megatron gkd | https://github.com/modelscope/ms-swift/tree/main/examples/ray/gkd | train/rollout/teacher |
|
||||
|
||||
### 何时使用
|
||||
|
||||
非 Ray Megatron(`megatron rlhf`)和 Ray Megatron(`megatron rlhf --use_ray true`)训练功能相同
|
||||
|
||||
核心区别在于**部署方式**:
|
||||
|
||||
- **非 Ray Megatron**:通过 torchrun 启动。推理可选 colocate(同进程)或 server(手动启动 vLLM server)模式。多节点需要在每个节点手动配置 `MASTER_ADDR/PORT` 并分别启动 torchrun 和 vLLM server。
|
||||
- **Ray Megatron**:通过一份 YAML 声明各角色的 GPU 数量(`train.gpus`、`rollout.gpus`、`teacher.gpus`),Ray 自动完成进程创建、GPU 分配和跨节点调度,无需手动管理多个进程。
|
||||
|
||||
两者都支持训练和推理的 GPU 隔离(非 Ray 通过 `vllm_mode=server`,Ray 通过 YAML 配置 separate 模式),功能上等价。Ray 的优势是将多进程的编排自动化——在多节点场景下,免去逐节点手动启动 torchrun 和 vLLM server 的运维负担。
|
||||
|
||||
**选择建议:**
|
||||
|
||||
| 场景 | 建议 |
|
||||
|------|------|
|
||||
| 单机训练 | **非 Ray** — 更简单 |
|
||||
| 多节点集群 | **Ray** — 自动跨节点调度,一份 YAML 一键启动 |
|
||||
|
||||
### 快速开始
|
||||
|
||||
```bash
|
||||
# 1. 启动 Ray 集群(单节点可省略)
|
||||
ray start --head # 主节点
|
||||
ray start --address=<head_ip>:6379 # 其他节点
|
||||
|
||||
# 2. 提交训练
|
||||
megatron rlhf --use_ray true --config examples/ray/grpo/ray_grpo_colocate.yaml
|
||||
```
|
||||
|
||||
### GPU 分配模式
|
||||
|
||||
**Colocate(共享 GPU)**— 训练和推理共享同一组 GPU,交替使用,通过 sleep/wake 释放显存:
|
||||
|
||||
```yaml
|
||||
colocate_groups: [[train, rollout]]
|
||||
offload_model: true
|
||||
offload_optimizer: true
|
||||
sleep_level: 1
|
||||
|
||||
train:
|
||||
gpus: 4
|
||||
rollout:
|
||||
gpus: 4 # 必须和 train 相同
|
||||
```
|
||||
|
||||
**Separate(独立 GPU)**— 训练和推理各占独立 GPU,无显存竞争:
|
||||
|
||||
```yaml
|
||||
# 不设置 colocate_groups
|
||||
train:
|
||||
gpus: 4
|
||||
rollout:
|
||||
gpus: 4 # 独立的 4 张卡
|
||||
```
|
||||
|
||||
### GKD Teacher 模式
|
||||
|
||||
| 模式 | 配置方式 | top-k | full-vocab |
|
||||
|------|---------|:-----:|:----------:|
|
||||
| Colocated teacher | 设置 `teacher_model` + `offload_teacher_model: true` | ✅ | ✅ |
|
||||
| 独立 teacher GPU 组 | 添加 `teacher:` 组并设置 `gpus`、`model` | ✅ | ❌ |
|
||||
|
||||
- **Colocated teacher**:teacher 是 Megatron 模型,与 student 共享同一组 GPU 和相同的并行参数,通过 offload 交替释放显存。
|
||||
- **独立 teacher GPU 组**:teacher 是独立的 vLLM 推理引擎,运行在单独 GPU 上,并行参数独立配置(`vllm_tensor_parallel_size`)。
|
||||
- **top-k**:蒸馏损失仅在 teacher 概率最高的 k 个 token 上计算(通过 `gkd_logits_topk` 设置),显存占用更低,但会丢弃长尾分布信息。
|
||||
- **full-vocab**:蒸馏损失在完整词表上计算,保留完整分布信息,但显存占用较高。
|
||||
|
||||
### 相关文档
|
||||
|
||||
更多文档请参考
|
||||
|
||||
- **GRPO 训练**:[Megatron GRPO 文档](../Megatron-SWIFT/GRPO.md)
|
||||
- **GKD 训练**:[GKD 文档](../Megatron-SWIFT/GKD.md)
|
||||
- **Megatron 训练参数**:[命令行参数文档](../Megatron-SWIFT/Command-line-parameters.md)
|
||||
- **Megatron 快速开始**:[Quick Start](../Megatron-SWIFT/Quick-start.md)
|
||||
|
||||
详细配置说明和示例见 [examples](https://github.com/modelscope/ms-swift/tree/main/examples/ray)。
|
||||
|
||||
## Swift Ray
|
||||
|
||||
SWIFT 的 HF Trainer 侧也支持使用 ray 来进行多卡或多节点训练:
|
||||
|
||||
| 功能 | 支持ray | 例子 | 可分配角色 |
|
||||
|----------|-------|--------------------------------------------------------------------------------|-----------------|
|
||||
| pt/sft | ✅ | https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-node/ray | default |
|
||||
| dpo | ❎ | | |
|
||||
| grpo | ❎ | | |
|
||||
| ppo | ❎ | | |
|
||||
| sampling | ✅ | https://github.com/modelscope/ms-swift/tree/main/examples/sampler/distill | sampler/prm/orm |
|
||||
| distill | ✅ | https://github.com/modelscope/ms-swift/tree/main/examples/sampler/sample | sampler/prm/orm |
|
||||
|
||||
### 技术细节
|
||||
|
||||
在叙述参数设置之前,我们有必要先行讲一下技术细节。由于SWIFT的内部当前使用了大量transformers和trl的已有实现,像veRL或ROLL一样拆解为不同的ray角色是不现实的,而且拆解后会以ray为中心,对非ray的场景的支持会不良。
|
||||
因此SWIFT采取了装饰器为主的技术方案,以函数级别定义了不同角色,这些角色可以在参数中被定义如何使用。看下面的例子:
|
||||
|
||||
```python
|
||||
from swift.ray_utils import RayHelper
|
||||
|
||||
@RayHelper.worker(group=['model1', 'model2'])
|
||||
class MyTrainer:
|
||||
|
||||
def __init__(self, args):
|
||||
self._prepare_model1()
|
||||
self._prepare_model2()
|
||||
self._prepare_datasets()
|
||||
|
||||
@RayHelper.function(group='model1')
|
||||
def _prepare_model1(self):
|
||||
...
|
||||
|
||||
@RayHelper.function(group='model2')
|
||||
def _prepare_model2(self):
|
||||
...
|
||||
|
||||
@RayHelper.function(group='model1')
|
||||
def rollout(self, inputs):
|
||||
return self.model1.generate(inputs)
|
||||
|
||||
@RayHelper.function(group='model2')
|
||||
def forward_model2(self, inputs):
|
||||
loss = self.model2.forward(inputs)
|
||||
loss.backward()
|
||||
|
||||
def _prepare_datasets(self):
|
||||
self.dataset = ...
|
||||
|
||||
def train(self):
|
||||
for batch in DataLoader(self.dataset):
|
||||
generated = self.rollout(batch)
|
||||
self.forward_model2(generated)
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
...
|
||||
MyTrainer(args).train()
|
||||
```
|
||||
|
||||
RayHelper会将被装饰的方法分配到不同的硬件集群中,本地调用会被平滑转换到ray集群中进行远程调用。也可以以类为中心进行划分:
|
||||
|
||||
```python
|
||||
|
||||
@RayHelper.worker(group=['model1'])
|
||||
class Model1:
|
||||
...
|
||||
|
||||
@RayHelper.function(group='model1')
|
||||
def rollout(self):
|
||||
...
|
||||
|
||||
@RayHelper.worker(group=['model2'])
|
||||
class Model2:
|
||||
...
|
||||
|
||||
@RayHelper.function(group='model2')
|
||||
def forward_and_optimize(self):
|
||||
...
|
||||
|
||||
|
||||
class Trainer:
|
||||
...
|
||||
```
|
||||
|
||||
SWIFT对ray的支持本质上是使用@worker和@function两个注解的组合使用,worker指定ray集群的角色,function指定如何分配数据。
|
||||
|
||||
function注解有额外的几个参数:
|
||||
```python
|
||||
@staticmethod
|
||||
def function(group: str,
|
||||
dispatch: Union[Literal['slice', 'all'], Callable] = 'all',
|
||||
execute: Literal['first', 'all'] = 'all',
|
||||
collect: Union[Literal['none', 'flatten'], Callable] = 'none'):
|
||||
```
|
||||
|
||||
- dispatch: 如何分配调用入参
|
||||
- slice:对入参切分,也就是worker负载均衡执行
|
||||
- all:各个worker入参完全相同
|
||||
- 自定义切分方式,格式为:
|
||||
```python
|
||||
def my_custom_slice(n, i, data):
|
||||
# n是worker数量,i是当前worker索引,data是原始入参
|
||||
# 返回第i个的入参
|
||||
```
|
||||
- execute: 如何执行
|
||||
- first: rank0执行,此时slice和Callable方式切分无效
|
||||
- all: 全部执行
|
||||
|
||||
- collect: 如何收集返回数据
|
||||
- none:原样返回,格式为各个worker返回值的列表
|
||||
- flatten: 将worker返回的结果进行拉平,支持tuple的拉平
|
||||
- Callable: 自定义collect方式,格式为:
|
||||
```python
|
||||
def my_custom_collect(result):
|
||||
# result是各个worker返回的列表
|
||||
# 输入你想要的格式
|
||||
```
|
||||
|
||||
### 参数设置
|
||||
|
||||
讲完技术细节后,可以将参数配置了。开发者可以根据不同的流程中的角色列表,设置不同的硬件搭配方式,例如采样功能中,共有三个角色,sampler、prm、orm,可以这样配置:
|
||||
|
||||
```yaml
|
||||
device_groups:
|
||||
nproc_per_node: 4
|
||||
sample_group:
|
||||
device: GPU
|
||||
ranks: list(range(0, 2))
|
||||
workers:
|
||||
- sampler
|
||||
rm_group:
|
||||
device: GPU
|
||||
ranks: list(range(2, 4))
|
||||
workers:
|
||||
- prm
|
||||
- orm
|
||||
```
|
||||
|
||||
- nproc_per_node: ray集群中需要的每个node的最小卡数。
|
||||
xxx_group: 每个ray组的名称,可以随意指定
|
||||
- device: 设备类型,当前支持GPU/CPU等。
|
||||
- ranks: 当前组分配到哪些ranks上。如果是CPU,ranks只能为整数,代表共需要多少进程,如果是GPU,可以为`[0,1,2,3]`, `4`, `list(range(0, 4))`等格式。
|
||||
- workers: 哪些角色分配到当前组中。
|
||||
|
||||
所有可用的角色可以见本文最上面的表。
|
||||
|
||||
如果使用命令行,device_groups也可以以`--device_groups xxx`方式传入,xxx为jsonstring。为了配置的简便,我们强烈推荐使用yaml方式搭配ray使用。
|
||||
@@ -0,0 +1,93 @@
|
||||
# 强化微调
|
||||
|
||||
强化微调是目前模型训练非常重要的功能之一,它本身的实现是多种多样的,SWIFT目前已经支持了强化微调所需要的原子能力,如采样、强化学习和微调。目前我们提供了拒绝采样微调的一个具体示例,可以查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rft/rft.py)。
|
||||
|
||||
## 强化微调的概念
|
||||
|
||||
强化微调是从2022年开始(甚至更早)就被提出的概念。其方式一般有下列流程:
|
||||
|
||||
1. 使用某个模型生成数据,或进行原始数据扩充
|
||||
2. 使用数据训练目标模型
|
||||
3. 如果有必要,重复上述过程
|
||||
|
||||
步骤1:
|
||||
|
||||
- 如果生成数据的模型是更大的模型,如GPT、Qwen-Max、DeepSeek-V3/R1等,则该强化微调可以理解为蒸馏
|
||||
- 如果生成数据的模型是本模型,则可以理解为自我提升(self-improvement)微调
|
||||
- 如果采样过程是采样一个batch,然后通过KL散度和reward进行拟合训练并不断循环,则可以理解为PPO、GRPO等on-policy算法
|
||||
- 采样数据的算法包含蒙特卡洛采样、do_sample采样、group beam search、dvts等
|
||||
- 采样过程可以引入ORM(结果判断),PRM(过程打分),多样性过滤,语种过滤等
|
||||
|
||||
步骤2:
|
||||
|
||||
- 如果使用SFT,则称为拒绝采样微调
|
||||
- 如果是强化学习,则称为强化学习微调
|
||||
|
||||
步骤3:
|
||||
|
||||
- 如果使用更大的模型蒸馏,例如更大模型的蒙特卡洛采样蒸馏,一般不会有循环
|
||||
- 如果使用本模型进行采样,或者PPO等算法,则会有循环
|
||||
|
||||
泛泛来说,常见强化微调的方式有下面几种:
|
||||
|
||||
1. 蒸馏:使用蒙特卡洛、do_sample等方式从超大模型中采样大量优质数据,训练小模型
|
||||
2. 自我提升:从本模型中采样部分优质数据,筛选后训练本模型,循环执行
|
||||
3. on-policy RL:使用PPO、GRPO等方式循环训练
|
||||
|
||||
采样过程一般很漫长,比训练过程漫长的多。如果使用GPT等模型蒸馏数据,则需要购买token。因此,强化微调的时间成本和花费成本比较高,所以一般作为微调的补充机制出现,当然也有特例,例如最近的DeepSeek-R1。
|
||||
|
||||
DeepSeek-R1使用了GRPO算法从零使base模型涌现CoT能力,该方法需要大规模集群支持,且模型需要足够大才能发生能力涌现,在本文中不详细讨论。如果需要了解该过程,请查看[论文解析](https://zhuanlan.zhihu.com/p/19714987272)。
|
||||
|
||||
有关强化微调的一些论文:
|
||||
|
||||
- 拒绝采样微调:https://arxiv.org/pdf/2308.01825
|
||||
- ReST:https://arxiv.org/pdf/2308.08998
|
||||
- B-STAR:https://arxiv.org/pdf/2412.17256
|
||||
- DeepSeekMath:https://arxiv.org/pdf/2402.03300
|
||||
- Qwen-math-PRM:https://arxiv.org/pdf/2501.07301
|
||||
- DeepSeek-R1:https://github.com/deepseek-ai/DeepSeek-R1/tree/main
|
||||
|
||||
## 什么时候使用强化微调
|
||||
|
||||
在LLaMA3之后,我们发现一个非常明显但却是不常被提及的特点:使用某个含有CoT的train数据集训练Instruct模型,再通过对应的test集进行评测,会发现test集评测效果变差。例如,使用gsm8k训练集训练llama3.1-8b-instruct,对生成的ckpt使用test集进行评测,会发现掉点。
|
||||
|
||||
这个特性主要来源于模型的知识遗忘问题。在模型厂商的微调中,会加入非常多的CoT数据集,模型在解决数学任务的时候,用到的能力很有可能不是来自于math数据集,而是来自arc数据集,这个推论有[一些工作可以证明](https://zhuanlan.zhihu.com/p/19269451950)。在继续训练通用任务后,知识遗忘破坏了模型原有能力,导致了掉点。
|
||||
|
||||
然而,优先使用微调方式训练模型总是正确的。微调可以使模型快速适应数据集的分布,并且微调的成本很低。当有如下条件之一时使用强化微调:
|
||||
|
||||
1. 已经微调过模型,能力不满足需求
|
||||
2. 需要更强的CoT能力
|
||||
3. 对基模型训练通用能力,而原始数据集已经导致模型效果无法提升
|
||||
4. 对应query的输出结果可以相对准确地评估好坏,例如结果清晰(数学,代码),过程清晰(翻译,风格)等
|
||||
|
||||
强化微调非常依赖于reward评估是否准确。如果评估结果不准确,可能导致模型训练原地震荡,甚至越训越差。
|
||||
|
||||
## SWIFT的实现
|
||||
|
||||
SWIFT支持sample命令,该命令就是用于模型采样。目前支持的采样方式有:
|
||||
|
||||
- sample:以generate方式对模型进行采样
|
||||
|
||||
目前我们给出了一个较为通用的[RFT脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rft/rft.py)。该脚本适用于自我提升方式的训练,且支持动态调整采样温度值、PRM阈值等超参数,并且训练方式灵活可变(微调、DPO等;或者每次迭代重新训练原模型或继续训练上个迭代的模型,甚至加载上个迭代的所有训练状态等)。开发者可以在该脚本中增加其他数据过滤(生成的数据集中,id相同的行来自同一个query),例如多样性判断、语种判断等。
|
||||
|
||||
## 实验结果
|
||||
|
||||
我们对该RFT脚本针对数学领域使用competition_math数据集进行了训练和评测,结果如下:
|
||||
|
||||
| 模型 | MATH指标 | 训练方式 | 迭代次数 | 训练后MATH指标 |
|
||||
| ------------------------ | -------- | -------- | -------- | --------------------- |
|
||||
| LLaMA3.1_8b | 12.0 | SFT | 3 | 25.2(LLaMA3.1_8b_sft) |
|
||||
| LLaMA3.1_8b_sft | 25.2 | RFT | 2 | 32.4 |
|
||||
| LLaMA3.1_8b_instruct | 52.2 | SFT | 2 | 39.0 |
|
||||
| LLaMA3.1_8b_instruct | 52.2 | RFT | 3 | 58 |
|
||||
| Qwen2.5_math_7b_instruct | 79.6 | RFT | 2 | 83.2 |
|
||||
|
||||
可以看到,使用competition_math直接SFT后,instruct模型的掉点十分严重。而RFT后模型能力有提升,即使对Qwen2.5_math_7b_instruct这个SOTA的math模型也同样有一定提升空间。
|
||||
|
||||
特别地,针对Qwen2.5_math_7b_instruct我们测试了gsm8k的指标:
|
||||
|
||||
| 模型 | gsm8k指标 | RFT后gsm8k指标 |
|
||||
| ------------------------ | --------- | -------------- |
|
||||
| Qwen2.5_math_7b_instruct | 92.8 | 91.6 |
|
||||
|
||||
可以看到,RFT训练后gsm8k指标变化不大,并没有出现前述的掉点现象。
|
||||
@@ -0,0 +1,94 @@
|
||||
# 采样
|
||||
|
||||
采样是SWIFT新支持的重要能力之一,这部分可以理解为`test-time compute`的落地实现。同时,该能力对RFT(强化微调)的实现也至关重要。
|
||||
|
||||
## 能力介绍
|
||||
|
||||
SWIFT的sample能力可以使用下面的例子进行:
|
||||
```shell
|
||||
swift sample --model LLM-Research/Meta-Llama-3.1-8B-Instruct --sampler_engine transformers --num_return_sequences 5 --dataset AI-ModelScope/alpaca-gpt4-data-zh#5
|
||||
```
|
||||
在当前文件夹的`sample_output`目录下,会生成以时间戳为文件名的jsonl文件,该文件应该包含25行,每一行都是一个完整`messages`格式的数据。
|
||||
|
||||
采样的参数列表请参考[这里](Command-line-parameters.md)。
|
||||
|
||||
## 环境准备
|
||||
|
||||
```shell
|
||||
pip install ms-swift[llm] -U
|
||||
```
|
||||
|
||||
或从源代码安装:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/modelscope/ms-swift.git
|
||||
cd ms-swift
|
||||
pip install -e '.[llm]'
|
||||
```
|
||||
|
||||
## 使用PRM和ORM进行结果过滤
|
||||
|
||||
采样重要的能力就是对过程和结果进行监督,这可以通过设置额外参数来支持。
|
||||
|
||||
```shell
|
||||
swift sample --model LLM-Research/Meta-Llama-3.1-8B-Instruct --sampler_engine lmdeploy --num_return_sequences 5 --n_best_to_keep 2 --dataset tastelikefeet/competition_math#5 --prm_model AI-ModelScope/GRM-llama3.2-3B-rewardmodel-ft --orm_model math
|
||||
```
|
||||
|
||||
在当前文件夹的`sample_output`目录下,会生成以时间戳为文件名的jsonl文件,该文件**至多包含**10行,每一行都是一个完整`messages`格式的数据。
|
||||
> 之所以至多包含10行,是因为虽然设置了共处理5个数据,每个数据保留2个(n_best_to_keep),但是orm可能会校验失败,失败数据不会保留到文件中。
|
||||
> 另外,增加了--prm_model或--orm_model后文件格式有所不同,包含了rejected_response key,内容来自于prm评分最低的行。
|
||||
|
||||
## 自定义PRM或ORM
|
||||
|
||||
PRM和ORM的自定义可以在plugin中按照现有代码增加一个新的实现。例如:
|
||||
```python
|
||||
class CustomPRM:
|
||||
|
||||
# 构造需要是无参的
|
||||
def __init__(self):
|
||||
# init here
|
||||
pass
|
||||
|
||||
def __call__(self, infer_requests: List[InferRequest], ground_truths: List[str], **kwargs) -> List[Union[float, List[float]]]:
|
||||
...
|
||||
|
||||
|
||||
prms = {'custom': CustomPRM}
|
||||
```
|
||||
|
||||
之后在命令行中使用`--prm_model custom`即可。
|
||||
|
||||
## 显存控制
|
||||
|
||||
如果被采样模型和PRM共同加载进显存,则可能出现OOM的问题。因此采样可以分为两段进行:
|
||||
|
||||
- 第一段指定`--model`和``--sampler_engine`,同时不指定`--orm_model`和`--prm_model`,仅进行采样,并存储为文件
|
||||
- 第二段指定`--sampler_engine no`,指定`--orm_model`和`--prm_model`,并同时指定`--cache_files`,仅进行RM数据过滤,不重新采样
|
||||
|
||||
通过两段方式可以每次仅加载一个模型,防止OOM。
|
||||
|
||||
## 实际例子
|
||||
|
||||
请参考[强化微调脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rft/rft.py)。该脚本给出了使用采样进行强化微调的实际例子。
|
||||
|
||||
> 注意:该脚本的实际效果和模型、数据、RM的质量强相关,因此仅作为样例出现,用户请自行修改该脚本并训练自己的RM和generator模型。
|
||||
|
||||
## 大模型蒸馏采样
|
||||
|
||||
SWIFT的sample支持使用OpenAI API的方式,用大模型蒸馏数据,如下示例:
|
||||
```shell
|
||||
OPENAI_API_KEY="your_api_key" \
|
||||
swift sample \
|
||||
--sampler_type distill \
|
||||
--sampler_engine client \
|
||||
--model deepseek-r1 \
|
||||
--stream true \
|
||||
--dataset tastelikefeet/competition_math#5 \
|
||||
--num_return_sequences 1 \
|
||||
--temperature 0.6 \
|
||||
--top_p 0.95 \
|
||||
--engine_kwargs '{"base_url":"https://dashscope.aliyuncs.com/compatible-mode/v1"}'
|
||||
```
|
||||
在以上示例中,base_url和model分别是api地址和模型名称,stream表示发起请求的stream参数。
|
||||
|
||||
注意,对于Deepseek-R1系列模型,输出会被格式化为:`<think>{reasoning_content}</think>\n\n<answer>{content}</answer>`。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,133 @@
|
||||
# 使用Tuners
|
||||
|
||||
tuner是指附加在模型上的额外结构部分,用于减少训练参数量或者提高训练精度。目前SWIFT支持的tuners有:
|
||||
|
||||
- LoRA: [LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS](https://arxiv.org/abs/2106.09685)
|
||||
- LoRA+: [LoRA+: Efficient Low Rank Adaptation of Large Models](https://arxiv.org/pdf/2402.12354.pdf)
|
||||
- LLaMA PRO: [LLAMA PRO: Progressive LLaMA with Block Expansion](https://arxiv.org/pdf/2401.02415.pdf)
|
||||
- GaLore/Q-GaLore: [GaLore: Memory-Efficient LLM Training by Gradient Low-Rank Projection](https://arxiv.org/abs/2403.03507)
|
||||
- Liger Kernel: [Liger Kernel: Efficient Triton Kernels for LLM Training](https://arxiv.org/abs/2410.10989)
|
||||
- LISA: [LISA: Layerwise Importance Sampling for Memory-Efficient Large Language Model Fine-Tuning](https://arxiv.org/abs/2403.17919)
|
||||
- UnSloth: https://github.com/unslothai/unsloth
|
||||
- SCEdit: [SCEdit: Efficient and Controllable Image Diffusion Generation via Skip Connection Editing](https://arxiv.org/abs/2312.11392) < [arXiv](https://arxiv.org/abs/2312.11392) | [Project Page](https://scedit.github.io/) >
|
||||
- NEFTune: [Noisy Embeddings Improve Instruction Finetuning](https://arxiv.org/abs/2310.05914)
|
||||
- LongLoRA: [Efficient Fine-tuning of Long-Context Large Language Models](https://arxiv.org/abs/2309.12307)
|
||||
- Adapter: [Parameter-Efficient Transfer Learning for NLP](http://arxiv.org/abs/1902.00751)
|
||||
- Vision Prompt Tuning: [Visual Prompt Tuning](https://arxiv.org/abs/2203.12119)
|
||||
- Side: [Side-Tuning: A Baseline for Network Adaptation via Additive Side Networks](https://arxiv.org/abs/1912.13503)
|
||||
- Res-Tuning: [Res-Tuning: A Flexible and Efficient Tuning Paradigm via Unbinding Tuner from Backbone](https://arxiv.org/abs/2310.19859) < [arXiv](https://arxiv.org/abs/2310.19859) | [Project Page](https://res-tuning.github.io/) >
|
||||
- [PEFT](https://github.com/huggingface/peft)提供的tuners, 如AdaLoRA、DoRA、Fourierft等
|
||||
|
||||
## 接口列表
|
||||
|
||||
### Swift类静态接口
|
||||
|
||||
- `Swift.prepare_model(model, config, **kwargs)`
|
||||
- 接口作用:加载某个tuner到模型上,如果是PeftConfig的子类,则使用Peft库的对应接口加载tuner。在使用SwiftConfig的情况下,本接口可以传入SwiftModel实例并重复调用,此时和config传入字典的效果相同。
|
||||
- 本接口支持并行加载不同类型的多个tuners共同使用
|
||||
- 参数:
|
||||
- `model`: `torch.nn.Module`或`SwiftModel`的实例,被加载的模型
|
||||
- `config`: `SwiftConfig`、`PeftConfig`的实例,或者一个自定义tuner名称对config的字典
|
||||
- 返回值:`SwiftModel`或`PeftModel`的实例
|
||||
- `Swift.merge_and_unload(model)`
|
||||
- 接口作用:将LoRA weights合并回原模型,并将LoRA部分完全卸载
|
||||
- 参数:
|
||||
- model: `SwiftModel`或`PeftModel`的实例,已加载LoRA的模型实例
|
||||
- 返回值:None
|
||||
|
||||
- `Swift.merge(model)`
|
||||
|
||||
- 接口作用:将LoRA weights合并回原模型,不卸载LoRA部分
|
||||
|
||||
- 参数:
|
||||
- model: `SwiftModel`或`PeftModel`的实例,已加载LoRA的模型实例
|
||||
|
||||
- 返回值:None
|
||||
|
||||
- `Swift.unmerge(model)`
|
||||
|
||||
- 接口作用:将LoRA weights从原模型weights中拆分回LoRA结构
|
||||
|
||||
- 参数:
|
||||
- model: `SwiftModel`或`PeftModel`的实例,已加载LoRA的模型实例
|
||||
|
||||
- 返回值:None
|
||||
|
||||
- `Swift.save_to_peft_format(ckpt_dir, output_dir)`
|
||||
|
||||
- 接口作用:将存储的LoRA checkpoint转换为Peft兼容的格式。主要改变有:
|
||||
|
||||
- `default`会从对应的`default`文件夹中拆分到output_dir根目录中
|
||||
- weights中的`{tuner_name}.`字段会被移除,如`model.layer.0.self.in_proj.lora_A.default.weight`会变为`model.layer.0.self.in_proj.lora_A.weight`
|
||||
- weights中的key会增加`basemodel.model`前缀
|
||||
|
||||
- 注意:只有LoRA可以被转换,其他类型tuner由于Peft本身不支持,因此会报转换错误。此外,由于LoRAConfig中存在额外参数,如`dtype`,因此在这些参数有设定的情况下,不支持转换为Peft格式,此时可以手动删除adapter_config.json中的对应字段
|
||||
|
||||
- 参数:
|
||||
|
||||
- ckpt_dir:原weights目录
|
||||
- output_dir:目标weights目录
|
||||
|
||||
- 返回值:None
|
||||
|
||||
- `Swift.from_pretrained(model, model_id, adapter_name, revision, **kwargs)`
|
||||
- 接口作用:从存储的weights目录中加载起tuner到模型上,如果adapter_name不传,则会将model_id目录下所有的tuners都加载起来。同`prepare_model`相同,本接口可以重复调用
|
||||
- 参数:
|
||||
- model:`torch.nn.Module`或`SwiftModel`的实例,被加载的模型
|
||||
- model_id:`str`类型,待加载的tuner checkpoint, 可以是魔搭hub的id,或者训练产出的本地目录
|
||||
- adapter_name:`str`或`List[str]`或`Dict[str, str]`类型或`None`,待加载tuner目录中的tuner名称,如果为`None`则加载所有名称的tuners,如果是`str`或`List[str]`则只加载某些具体的tuner,如果是`Dict`,则将`key`指代的tuner加载起来后换成`value`的名字
|
||||
- revision: 如果model_id是魔搭的id,则revision可以指定对应版本号
|
||||
|
||||
### SwiftModel接口
|
||||
|
||||
下面列出用户可能调用的接口列表,其他内部接口或不推荐使用的接口可以通过`make docs`命令查看API Doc文档。
|
||||
|
||||
- `SwiftModel.create_optimizer_param_groups(self, **defaults)`
|
||||
- 接口作用:根据加载的tuners创建parameter groups,目前仅对`LoRA+`算法有作用
|
||||
- 参数:
|
||||
- defaults:`optimizer_groups`的默认参数,如`lr`和`weight_decay`
|
||||
- 返回值:
|
||||
- 创建的`optimizer_groups`
|
||||
|
||||
- `SwiftModel.add_weighted_adapter(self, ...)`
|
||||
- 接口作用:将已有的LoRA tuners合并为一个
|
||||
- 参数:
|
||||
- 本接口是PeftModel.add_weighted_adapter的透传,参数可以参考:[add_weighted_adapter文档](https://huggingface.co/docs/peft/main/en/package_reference/lora#peft.LoraModel.add_weighted_adapter)
|
||||
|
||||
- `SwiftModel.save_pretrained(self, save_directory, safe_serialization, adapter_name)`
|
||||
- 接口作用:存储tuner weights
|
||||
- 参数:
|
||||
- save_directory:存储目录
|
||||
- safe_serialization: 是否使用safe_tensors,默认为False
|
||||
- adapter_name:存储的adapter tuner,如果不传则默认存储所有的tuners
|
||||
- `SwiftModel.set_active_adapters(self, adapter_names, offload=None)`
|
||||
- 接口作用:设置当前激活的adapters,不在列表中的adapters会被失活
|
||||
- 在`推理`时支持环境变量`USE_UNIQUE_THREAD=0/1`,默认值`1`,如果为`0`则set_active_adapters只对当前线程生效,此时默认使用本线程激活的tuners,不同线程tuners互不干扰
|
||||
- 参数:
|
||||
- adapter_names:激活的tuners
|
||||
- offload:失活的adapters如何处理,默认为`None`代表留在显存中,同时支持`cpu`和`meta`,代表offload到cpu和meta设备中以减轻显存消耗,在`USE_UNIQUE_THREAD=0`时offload不要传值以免影响其他线程
|
||||
- 返回值:None
|
||||
- `SwiftModel.activate_adapter(self, adapter_name)`
|
||||
- 接口作用:激活一个tuner
|
||||
- 在`推理`时支持环境变量`USE_UNIQUE_THREAD=0/1`,默认值`1`,如果为`0`则activate_adapter只对当前线程生效,此时默认使用本线程激活的tuners,不同线程tuners互不干扰
|
||||
- 参数:
|
||||
- adapter_name:待激活的tuner名字
|
||||
- 返回值:None
|
||||
- `SwiftModel.deactivate_adapter(self, adapter_name, offload)`
|
||||
- 接口作用:失活一个tuner
|
||||
- 在`推理`时环境变量`USE_UNIQUE_THREAD=0`时不要调用本接口
|
||||
- 参数:
|
||||
- adapter_name:待失活的tuner名字
|
||||
- offload:失活的adapters如何处理,默认为`None`代表留在显存中,同时支持`cpu`和`meta`,代表offload到cpu和meta设备中以减轻显存消耗
|
||||
- 返回值:None
|
||||
|
||||
- `SwiftModel.get_trainable_parameters(self)`
|
||||
|
||||
- 接口作用:返回训练参数信息
|
||||
|
||||
- 参数:无
|
||||
|
||||
- 返回值:训练参数信息,格式如下:
|
||||
```text
|
||||
trainable params: 100M || all params: 1000M || trainable%: 10.00% || cuda memory: 10GiB.
|
||||
```
|
||||
Reference in New Issue
Block a user