# 奖励函数
## 自定义奖励函数
奖励函数接受模型生成的文本 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 初始化环境
