1
1
# 多轮训练
2
2
3
- 注意:该 feature 需要使用 ms-swift>=3.6
3
+ ** 注意 ** 多轮训练逻辑已在 ms-swift 3.8 中进行重构,如果您的 ms-swift 版本低于该版本,请参考对应版本的文档。
4
4
5
- 在强化学习训练场景中,模型采样可能需要与环境进行多轮交互(如工具调用、外部API访问等 )。这种交互式训练要求模型能够根据环境反馈信息进行连续推理。本文档将详细介绍如何在 GRPO 训练中自定义多轮训练流程。
5
+ 在强化学习训练场景中,模型采样可能需要与环境进行多轮交互(如工具调用)。这种交互式训练要求模型能够根据环境反馈信息进行连续推理。本文档将详细介绍如何在 GRPO 训练中自定义多轮训练流程。
6
6
7
+ 以下是多轮训练示例图,模型可能涉及多轮 rollout,包括环境交互、工具调用等步骤:
7
8
8
- 根据环境反馈插入方式不同,多轮可以分为:
9
-
10
- - 新一轮推理:环境反馈结果作为 query,模型进行新一轮对话轮次进行响应
11
- - 当轮续写:环境反馈结果插入模型当前回复中,模型在此基础上继续续写后续内容
12
-
13
-
14
- 我们可以自定义并通过参数 ` multi_turn_scheduler ` 设置多轮采样的规划器来实现多轮采样逻辑
15
- ```
16
- --multi_turn_scheduler xxx
17
- --max_turns xxx
18
- ```
19
- 两种方式的实现例子可以参考[ 最佳实践] ( #最佳实践 )
9
+ ![ 多轮示例图] ( ../../../../resources/grpo_multi_turn.png )
20
10
21
11
## 多轮规划器 MultiTurnScheduler
22
- 多轮规划器是多轮训练的核心组件,其工作流程如下图所示:
23
12
13
+ ` MultiTurnScheduler ` 是一个抽象基类,提供了默认的多轮对话管理逻辑,其工作流程如下图所示:
24
14
25
15
<img src =" https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/multiturn_pipeline.png " width =" 300 " />
26
16
17
+ 多轮规划器主要承担两大核心功能:
18
+ - ** 终止条件判断** :通过 ` check_finished ` 方法判断当前轮次推理是否应该结束
19
+ - ** 推理请求构造** :通过 ` step ` 方法构建下一轮推理的请求对象
27
20
28
- 多轮规划器主要承担两大功能:
29
- - 终止条件判断:通过 check_finished 方法判断当前轮次推理是否应该结束
30
- - 推理请求构造:通过 step 方法构建下一轮推理的请求对象
21
+ 抽象基类 ` MultiTurnScheduler ` 的核心方法如下:
31
22
32
- 抽象基类 MultiTurnScheduler 代码如下
33
23
``` python
34
24
class MultiTurnScheduler (ABC ):
35
25
36
26
def __init__ (self , max_turns : Optional[int ] = None , * args , ** kwargs ):
37
27
self .max_turns = max_turns
38
28
39
- @abstractmethod
40
- def step (self , infer_request : ' RolloutInferRequest' , result : ' RolloutResponseChoice' ,
41
- current_turn : int ) -> Union[' RolloutInferRequest' , Tuple[' RolloutInferRequest' , Dict]]:
42
- pass
43
-
44
- def check_finished (self , infer_request : ' RolloutInferRequest' , result : ' RolloutResponseChoice' ,
29
+ def step (self , infer_request : ' RolloutInferRequest' , response_choice : ' ChatCompletionResponseChoice' ,
30
+ current_turn : int ) -> Dict:
31
+ """
32
+ 处理对话轮次之间的转换。
33
+
34
+ Args:
35
+ infer_request: 当前推理请求
36
+ response_choice: 当前轮次的响应
37
+ current_turn: 当前轮次数
38
+
39
+ Returns:
40
+ Dict[str, Any]: 包含推理结果的字典,结构如下:
41
+ - infer_request (必需): 下一轮的推理请求对象
42
+ - response_token_ids (可选): 每个 rollout 轮次的响应 token IDs
43
+ - response_loss_mask (可选): 每个 rollout 轮次响应的损失掩码
44
+ - rollout_infos (可选): 额外信息数据
45
+ """
46
+ raise NotImplementedError
47
+
48
+ def check_finished (self , infer_request : ' RolloutInferRequest' , response_choice : ' ChatCompletionResponseChoice' ,
45
49
current_turn : int ) -> bool :
46
- if result.finish_reason == ' length' :
50
+ """
51
+ 检查多轮 rollout 是否应该结束的默认终止逻辑。
52
+
53
+ 默认终止条件:
54
+ 1. 当响应达到长度限制时 (finish_reason == 'length')
55
+ 2. 当对话达到最大轮数时 (如果设置了 max_turns)
56
+
57
+ Args:
58
+ infer_request: 推理请求对象
59
+ response_choice: 包含生成结果的响应选择,包括 finish_reason
60
+ current_turn: 当前对话轮数
61
+
62
+ Returns:
63
+ bool: True 表示终止对话,False 表示继续
64
+ """
65
+ if response_choice.finish_reason == ' length' :
47
66
return True
48
67
if self .max_turns and current_turn >= self .max_turns:
49
68
return True
50
69
return False
51
70
```
52
71
53
- > 如果你想要奖励函数获取多轮交互中的信息,请在 step 方法中返回额外的 dict 对象, 在奖励函数中的 kwargs中,获取 ` multi_turn_infos `
72
+ ` step ` 和 ` check_finished ` 方法接收的参数说明:
73
+ - ** infer_request** : 当前的推理请求
74
+ - ** response_choice** : 当前轮次的推理结果
75
+ - ** current_turn** : 当前推理轮次(从 1 开始)
54
76
55
- ``` python
56
- class Scheduler ():
57
- def step (self , infer_request : ' RolloutInferRequest' , result : ' RolloutResponseChoice' ,
58
- current_turn : int ) -> Union[' RolloutInferRequest' , Tuple[' RolloutInferRequest' , Dict]]:
59
- ...
60
- return infer_request, extra_dict
77
+ <details ><summary >入参示例(点击展开)</summary >
61
78
62
- class RewardFunction ():
63
- def __call__ (self , completions , ** kwargs ):
64
- infos = kwargs.get(' multi_turn_infos' , {})
65
- ...
66
- ```
67
-
68
- step 和 check_finished 方法接收参数:
69
- - infer_request: 上轮的推理请求,包括
70
- - ` messages ` 键包含了模型的交互历史(注意:已包括当前模型推理结果)
71
- - 多模态信息,如 ` images `
72
- - ` data_dict ` 包含了数据集中的其他列
73
- - result: 上轮的推理结果,
74
- - current_turn: 当前推理轮次 (从1开始)
75
-
76
- 入参示例
77
79
``` python
78
80
infer_request
79
81
"""
@@ -93,9 +95,9 @@ RolloutInferRequest(
93
95
}
94
96
)
95
97
"""
96
- result
98
+ response_choice
97
99
"""
98
- RolloutResponseChoice (
100
+ ChatCompletionResponseChoice (
99
101
index=0,
100
102
message=ChatMessage(
101
103
role='assistant',
@@ -104,78 +106,110 @@ RolloutResponseChoice(
104
106
logprobs=None,
105
107
messages=None)
106
108
"""
107
- # result .messages will be copied at the end of multi-turn inference.
109
+ # response_choice .messages will be copied at the end of multi-turn inference.
108
110
```
111
+ </details >
109
112
110
- 默认的 ` check_finished ` 逻辑会在两种情况下停止推理
113
+ <br >
114
+ <br >
111
115
116
+ 默认的 ` check_finished ` 逻辑会在以下两种情况下停止推理:
112
117
- 模型回复被截断,即超出了 ` max_completion_length `
113
118
- 模型推理轮数超出了限制的最大轮数
114
119
120
+ 完整的默认多轮 rollout 逻辑请参考该类的 ` run ` 方法,我们也可以通过重载` run ` 方法来实现自定义多轮逻辑。
115
121
116
- 推荐使用 AsyncEngine 来实现高效的批量数据异步多轮采样(只支持 external server mode),AsyncEngine 在多轮推理时能够减小推理过程中的计算气泡(如图)
117
-
118
- <img src =" https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/asyncengine.png " width =" 400 " />
122
+ ## 设置多轮训练参数
119
123
124
+ 在 swift rollout 命令中,设置 multi_turn_scheduler 参数指定规划器
120
125
121
- 在 ` rollout ` 命令中使用参数 ` use_async_engine ` 来指定engine的种类
122
126
``` bash
123
- CUDA_VISIBLE_DEVICES=0 \
124
127
swift rollout \
125
- --model xxx \
128
+ --model Qwen/Qwen3-1.7B \
126
129
--use_async_engine true \
127
- --multi_turn_scheduler xxx \
128
- --max_turns xxx
130
+ --multi_turn_scheduler thinking_tips_scheduler \
131
+ --vllm_max_model_len 32768 \
132
+ --vllm_gpu_memory_utilization 0.8 \
133
+ --max_turns 3
129
134
```
130
135
131
- 通过参数` external_plugins ` , 我们可以将本地的多轮规划器注册进 ms-swift 中,具体实现参考[ 代码] ( https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py )
132
136
133
- 多轮训练脚本参考
137
+ > 通过参数 ` external_plugins ` ,我们可以将本地的多轮规划器注册到 ms-swift 中,具体实现请参考 [ 代码 ] ( https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py ) 。
134
138
135
- - [ server mode] ( https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/external/vllm_multi_turn.sh )
136
- - [ colocate mode] ( https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/internal/vllm_multi_turn.sh )
139
+ 多轮训练脚本请参考[ 脚本] ( https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/external/vllm_multi_turn.sh ) 。
137
140
138
141
139
- ## 最佳实践
140
- [ 插件代码示例] ( https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py ) 中提供了两种多轮规划器的例子,实现在数学问题中提示模型再次思考并给出答案,分别对应两种多轮推理:
142
+ 对于多轮 rollout,我们使用 AsyncEngine 来实现高效的批量数据异步多轮采样。AsyncEngine 在多轮推理时能够减少推理过程中的计算气泡:
141
143
142
- - 第一种方式(新一轮推理):新插入一轮对话,提示模型的答案错误,需要重新思考(math_tip_trick_multi_turn)
143
- - 第二种方式(续写):回溯到模型的思考阶段,并加入思考错误的提示 (math_tip_trick)
144
+ <img src =" https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/asyncengine.png " width =" 400 " />
144
145
146
+ 在 ` rollout ` 命令中使用参数 ` use_async_engine ` 来指定 engine 的种类(默认使用 async engine):
145
147
146
- ## 注意事项
147
148
148
- ### 奖励函数
149
- 注意在奖励函数中,接受的 ` completions ` 参数为最后一轮模型回复,如果奖励函数需要根据模型多轮回复计算奖励,需要获取 ` messages ` 键来获取完整的多轮对话记录
149
+ ## 高级设置
150
150
151
- ``` python
152
- class Reward ( ORM ):
151
+ ### 自定义多轮交互逻辑
152
+ 在以上默认逻辑中,我们用一条轨迹来计算多轮 rollout 的损失,这里需要假设多轮交互的过程中,模型的历史信息没有收到改变。
153
153
154
- def __call__ (completions , ** kwargs ):
155
- print (kwargs.keys())
156
- # dict_keys(['problem', 'solution', 'messages', 'is_truncated'])
157
- messages = kwargs.get(' messages' )
158
- ...
159
- ```
154
+ 而在一些多轮场景中,我们可以需要在多轮 rollout 过程中动态地修改模型的历史信息(比如压缩历史信息),此时,我们需要将每轮的 rollout 单独作为一条轨迹进行训练。
160
155
156
+ 比较常见的一种场景是对于思考类模型,在实际推理过程中,模型通常只会保留最后一轮的思考内容,而忽略历史模型回复中的思考内容。
157
+
158
+ 对于这类场景,我们需要重写多轮规划器中的交互逻辑,即重载 ` run ` 方法,从而单独返回每一轮的 Rollout 的结果。
159
+
160
+ 框架内置的 ` ThinkingModelTipsScheduler ` 类展示了如何通过重写 ` run() ` 方法来实现完全自定义的多轮推理逻辑。请参考[ 内置多轮调度器实现] ( https://github.com/modelscope/ms-swift/blob/main/swift/plugin/multi_turn.py )
161
+
162
+ ** 注意** : 这种情况下,相同轨迹的数据会拆分为多条数据,在奖励相关的处理中,需要对相同轨迹的数据分配同样的reward。
163
+
164
+ 可以在kwargs中获取 trajectory_inputs 获取完整轨迹的数据,具体实现参考[ MultiTurnThinkingTips类] ( https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py )
165
+
166
+ ### 返回 response token ids
167
+ 在默认的多轮交互流程中,规划器先把模型生成的文本字符串返回给 trainer,trainer 再将其重新 encode 为 token id,用于后续训练。为了避免这一步重复编码的开销,你可以让规划器直接返回 response_token_ids,省去 trainer 侧的再次 encode。
168
+
169
+ 具体做法如下:
170
+
171
+ - 在 response_choice 对象中读取 token_ids 属性,即可获得本次 rollout 生成的 token 序列。
172
+ - 在 step/run 方法的返回值里加入 response_token_ids,trainer 便能直接使用这些 token id 参与训练,无需重新编码。
173
+
174
+ 具体实现可以参考[ ThinkingModelTipsScheduler] ( https://github.com/modelscope/ms-swift/blob/main/swift/plugin/multi_turn.py ) 类
161
175
162
176
### 损失掩码
163
177
164
178
在工具调用或环境交互返回结果时,若需将返回内容作为模型响应的一部分,建议对这些插入内容进行掩码处理,以确保模型在训练过程中不会对这些外部生成的内容计算损失。
165
179
166
- 这里需要通过设置参数 loss_scale ,实现自定义掩码逻辑,具体参考[ 定制化loss_scale文档] ( ../../../Customization/插件化.md#定制化loss_scale ) 。
180
+ 我们可以通过两种方式设置损失掩码
181
+
182
+ ** 第一种:设置 loss_scale**
183
+
184
+ ms-swift 提供 loss_scale 参数来对模型回复部分的内容进行损失缩放设置。比如设置` --loss_scale last_round ` ,可以将非最后一轮的模型回复的损失置零。我们也可以实现自定义 loss_scale,具体请参考[ 定制化 loss_scale 文档] ( ../../../Customization/插件化.md#定制化loss_scale ) 。
185
+
186
+ > 注:在GRPO中,loss_scale 只提供掩码功能,不提供缩放功能。
167
187
168
- 默认 loss_scale 值:
188
+ ** 第二种:设置loss_mask **
169
189
170
- grpo训练(即设置` multi_turn_scheduler ` ),loss_scale 默认为` default ` ,即对 messages 中的 每一轮 response 进行训练
171
- > 如果数据集中本身包含 assistant response 也会被计算入内,如果想要排除数据集中的response , 需要自定义 loss_scale
190
+ 在` step ` 或者` run ` 方法中设置 response_loss_mask, 可以在规划器中自定义损失掩码。前提需要返回response token ids,返回的 response_loss_mask 需要与 response token ids等长。当返回 response_loss_mask 时,loss_scale 参数失效。
172
191
173
- 如果只想只计算最后一轮 response(rollout结果)损失,请修改为 ` last_round `
192
+ response_loss_mask 返回可以参考 [ ToolCallScheduler类 ] ( https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py )
174
193
194
+ ### 奖励函数相关
175
195
176
- 注意 loss_scale 可以用于
196
+ 在奖励函数中获取多轮 Rollout 中的信息
197
+
198
+ 在` step ` 或者` run ` 方法中,返回 ` rollout_infos ` 对象,在奖励函数的 kwargs 中获取 ` rollout_infos ` :
199
+
200
+ ``` python
201
+ class Scheduler ():
202
+ def step (self , infer_request : ' RolloutInferRequest' , response_choice : ' ChatCompletionResponseChoice' ,
203
+ current_turn : int ) -> Dict:
204
+ ...
205
+ return {' infer_request' : infer_request, ' rollout_infos' : extra_dict}
206
+
207
+ class RewardFunction ():
208
+ def __call__ (self , completions , ** kwargs ):
209
+ infos = kwargs.get(' rollout_infos' , {})
210
+ ...
211
+ ```
177
212
178
- 1 . 标注需要训练的 tokens (0为不训练)
179
- 2 . 放缩 tokens 的训练权重
213
+ ### 在 Scheduler 中获取额外的数据集信息
180
214
181
- 而 GRPO 中暂不支持 loss_scale 的权重设置 。
215
+ 在训练侧设置参数 ` --vllm_server_pass_dataset ` ,可将数据集中的其他列传入多轮规划器。在 ` infer_request.data_dict ` 中获取 。
0 commit comments