本篇具体介绍奖励模型原理及作用,并结合代码详细介绍实现方法,在文末附有可执行代码。
基于一个大模型,本系列开始讲如何对大模型进行微调、强化学习,基于大模型本身强大的生成能力引导成更适配于多种应用场景的专属领域大模型。
由于基于大模型进行SFT的一系列操作,例如P-tuning、P-tuning v2等已有很多教程与开源代码,因此本系列将重点放在奖励模型、强化学习与大模型结合的内容。
大模型微调分为系列分为:
1)reward:训练一个具有自己偏好的奖励模型
2)大模型、reward与强化学习强强联合:大模型只生成我想要的回答
本篇主要开始结合代码讲解如何训练一个具有自己偏好的奖励模型。下面是一个快捷目录。
\1. reward模型及原理介绍
\2. 数据处理模块
\3. 模型框架模块
\4. Loss计算模块
一、reward模型及原理介绍
在介绍原理前,我们来再次回顾一下一个类似于chatGPT这样大模型的训练流程。
1. LLM训练流程
- 预训练:训练数据是海量无标签文本;
- 监督式微调:训练数据是「指令 - 输入 - 输出」,举个实体关系抽取的例子。此步骤通过给模型一些可参考知识与指令来微调模型,指令学习阶段可以只动小部分模型参数。
inputjson={
"instruction": "你现在是一个信息抽取模型,请你帮我抽取出关系内容为\"性能故障\", \"部件故障\", \"组成\"和 \"检测工具\"的相关三元组,三元组内部用\"_\"连接,三元组之间用\\n分割。文本:",
"input": "843号汽车故障报告故障现象热车起动困难",
"output": "车_部件故障_起动困难"
}
- 对齐:通过强化学习将输出与人类偏好对齐,训练数据是对模型输出内容的评分。
2. 对齐步骤强化学习具体训练流程
其训练流程再分为三步:
- 第 1 步:对预训练模型进行监督式微调;
- 第 2 步:创建一个奖励模型;
- 第 3 步:通过近端策略优化进行微调。
奖励模型在其中的角色,就是在第三步骤中,目的是训练一个跟人类偏好相同的模型,对大模型生成的结果进行打分,并及时将打分结果反馈给模型,通过强化学习纠正或引导模型生成更符合人类要求的结果。
3.奖励模型训练步骤
分为两步:
- 对模型生成内容进行排名:用上一步中创建的已微调 LLM 为每个 prompt 生成 4-9 个响应。然后再让人基于自己的偏好对这些响应进行排名。
- 设计奖励模型:基于使用这些排名构建的数据集,可以设计一个奖励模型RM,其输出的是用于 RLHF 第 3 步后续优化阶段的奖励分数。在原本模型的基础上,需要将其输出层(下一 token 分类层)替换成一个回归层,其具有单个输出节点。
4.奖励模型原理与loss设计
因为涉及对人工排序后的句子进行排序,我们引入Rank Loss
假定模型生成4个答案,我们对4个句子的人工评分排序为 A>B>D>C;那么我们需要训练其对应的打分模型,模型生成的分要满足 r(A) > r(B) > r(D) > r©。
此处直接上损失函数公式:
其中,yi 代表排序排在 y1-i 前面的所有句子。
我们代入上面的例子 A>B>D>C :
loss = r(A) - r(B) + r(A) - r(C) + r(A) - r(D) + r(B) - r(D) + ... + r(D) - r(C)
loss = -loss
为了更好得归一化差值,每两项差值都需要过一个 sigmoid 函数,将值拉到 0 ~ 1 之间。
由于这个loss设计的目的是期望模型能够最大化 好句子得分和坏句子得分的差值,而梯度下降做的是最小化操作,因此loss前面的负号的目的是实现最大化差值。
那么奖励模型原理我们介绍完毕了,更直白的理解就是**输入是句子,输出是对该句子的人类偏好评分;这个偏好是可以根据自己的需要去标注评分数据来实现的。**下面我们开始介绍核心代码。
二、数据处理模块
1. 数据示例
本次训练任务:做一个倾向产生好评的奖励模型,为好评给予更高的分数,为差评给予更低的分数。
此模块导入对生成句子进行人工评分后的数据。
数据格式按行切分,每行有多个评价,从前往后,好评在前,差评在后,每个评价间使用’\t’ 分割。
穿着合适,做工很精致,质量不错 穿着特别舒服,而且有弹性。型也好。"非常满意,房间和交通都不错,下次还要住该酒店了" 购买不满意,苹果较酸,不理想,有些吹牛。
2. 数据处理
这里数据处理的关键就是一定要将每一行内的多条评价文本拼到同一个数组内,代码见代码路径中的 loaddata.py,这里只展示核心代码。
try:
rank_texts = line.strip().split('\t')
except:
print(f'"{line}" -> {traceback.format_exc()}')
exit()
rank_texts_prop = {
'input_ids': [],
'token_type_ids': [],
'position_ids': [],
'attention_mask': []
}
for rank_text in rank_texts:
encoded_inputs = tokenizer(
text=rank_text,
truncation=True,
max_length=max_seq_len,
padding='max_length')
rank_texts_prop['input_ids'].append(encoded_inputs["input_ids"])
rank_texts_prop['token_type_ids'].append(encoded_inputs["token_type_ids"])
rank_texts_prop['position_ids'].append([i for i in range(len(encoded_inputs["input_ids"]))])
rank_texts_prop['attention_mask'].append(encoded_inputs["attention_mask"])
for k, v in rank_texts_prop.items():
tokenized_output[k] = v
三、模型框架
这里的模型,我们可以任意选择一个预训练好的模型,在原本模型的基础上,将其输出层(下一 token 分类层)替换成一个回归层,这个模型结构就设计完成了。
在本次实践中采用的预训练模型是ernie-3.0-base-zh ,其是用于语言理解和生成的大规模知识强化预训练模型。代码见代码路径中的 model.py,这里只展示核心代码。
class RewardModel(nn.Module):
def __init__(self, encoder):
"""
init func.
Args: encoder (transformers.AutoModel): backbone, 默认使用 ernie 3.0 """ super().__init__()
self.encoder = encoder
self.reward_layer = nn.Linear(768, 1)
def forward(
self,
input_ids: torch.tensor,
token_type_ids: torch.tensor,
attention_mask=None,
pos_ids=None,
) -> torch.tensor:
"""
forward 函数,返回每句话的得分值。
"""
pooler_output = self.encoder(
input_ids=input_ids,
token_type_ids=token_type_ids,
position_ids=pos_ids,
attention_mask=attention_mask,
)["pooler_output"] # (batch, hidden_size)
reward = self.reward_layer(pooler_output) # (batch, 1)
return reward
四、Loss计算模块
1. 加载batch阶段
这里就需要注意结合前面加载数据时,每一行的数据是一个数组,其中包含有多条评价数据且按数组顺序从前到后,由好评到差评,因此我们在加载数据时需要对应数据处理模块的数据结构,将同一行的句子分别送入模型计算出的值装到一个数组。
batch_rank_rewards = []
for batch_idx in range(len(batch['input_ids'])):
rank_texts_count = len(batch['input_ids'][batch_idx])
rank_rewards = []
for text_idx in range(rank_texts_count):
reward = model(
batch['input_ids'][batch_idx][text_idx].unsqueeze(dim=0).to(args.device),
batch['token_type_ids'][batch_idx][text_idx].unsqueeze(dim=0).to(args.device),
batch['attention_mask'][batch_idx][text_idx].unsqueeze(dim=0).to(args.device),
batch['position_ids'][batch_idx][text_idx].unsqueeze(dim=0).to(args.device),
)
rank_rewards.append(reward[0])
batch_rank_rewards.append(rank_rewards)
2.训练阶段
这里比较常规,直接上代码。
3. loss计算部分
从上面我们可以,看到核心函数就是 compute_rank_list_loss,这里的关键依然是每一行的句子评分是在前面计算时有序(从高到低)排序句子的reward列表。
def compute_rank_list_loss(rank_rewards_list: List[List[torch.tensor]], device='cpu') -> torch.Tensor:
if type(rank_rewards_list) != list:
raise TypeError(f'@param rank_rewards expected "list", received{type(rank_rewards)}.')
loss, add_count = torch.tensor([0]).to(device), 0
for rank_rewards in rank_rewards_list:
for i in range(len(rank_rewards)-1): # 遍历所有前项-后项的得分差
for j in range(i+1, len(rank_rewards)):
diff = F.logsigmoid(rank_rewards[i] - rank_rewards[j]) # sigmoid到0~1之间
loss = loss + diff
add_count += 1
loss = loss / add_count
return -loss
那么,至此为什么要训练reward模型,以及如何训练reward模型就说完了。
完整的预训练模型下载地址、数据以及代码见
https://github.com/sailerml/RLHF_GLM
如何学习大模型
现在社会上大模型越来越普及了,已经有很多人都想往这里面扎,但是却找不到适合的方法去学习。
作为一名资深码农,初入大模型时也吃了很多亏,踩了无数坑。现在我想把我的经验和知识分享给你们,帮助你们学习AI大模型,能够解决你们学习中的困难。
下面这些都是我当初辛苦整理和花钱购买的资料,现在我已将重要的AI大模型资料包括市面上AI大模型各大白皮书、AGI大模型系统学习路线、AI大模型视频教程、实战学习,等录播视频免费分享出来
,需要的小伙伴可以扫取。

一、AGI大模型系统学习路线
很多人学习大模型的时候没有方向,东学一点西学一点,像只无头苍蝇乱撞,我下面分享的这个学习路线希望能够帮助到你们学习AI大模型。
二、AI大模型视频教程
三、AI大模型各大学习书籍
四、AI大模型各大场景实战案例
五、结束语
学习AI大模型是当前科技发展的趋势,它不仅能够为我们提供更多的机会和挑战,还能够让我们更好地理解和应用人工智能技术。通过学习AI大模型,我们可以深入了解深度学习、神经网络等核心概念,并将其应用于自然语言处理、计算机视觉、语音识别等领域。同时,掌握AI大模型还能够为我们的职业发展增添竞争力,成为未来技术领域的领导者。
再者,学习AI大模型也能为我们自己创造更多的价值,提供更多的岗位以及副业创收,让自己的生活更上一层楼。
因此,学习AI大模型是一项有前景且值得投入的时间和精力的重要选择。