# 奖励模型

默认情况下，奖励模型是指具有分类头数值输出的模型，通常称为输出奖励模型（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

```
