deepseek带火了GRPO,更带火了reinforcement learning,让研究人员发现RL能在pre-train的基础上较大提升LLM的逻辑推理能力!当前,互联网高速发展二十多年产生的优质数据已经使用殆尽,所以更大规模的LLM一直难产(GPT-5现在都还没发布,优质token耗尽是核心原因之一)。市面上所有的主流厂商都开始使用reinforcement learning做post-train!那么reinforcement learning最大的特性是啥了?我个人觉得是:generalization,泛化性!强化学习通过设置合适的reward,引导LLM的parameters向特定的方向update,达到LLM解决特定任务的目的!比如让reinforment learning学习解数学题、学习编程,既然RL能干这种事, 那么其他很多业务也能做,比如:
- 金融领域
- 预测股价:prompt是公司的财报、每天的消息(宏观政策、微观公司),target是公司股价
- 互联网:
- 预测用户是否点击、购买:promt是用户画像、用户历史行为/item描述,target是用户对某些item的评分
- 医疗:
- 预测患者的病概率:prompt是患者的各种检测指标、生活习性,target是患者得某种疾病的概率
- 安全
- UGC的内容:对于一段文本,从敏感、歧视、政治、商业、虚假、诱导、对抗、暴力等维度评分
- 网络流量:数据访问包是否正常
- 代码检测:代码是否有漏洞
LLM火了后,很多人会用LLM生成各种文本。然而LLM本质还是一个统计概率模型,输出的next token是个概率分布,所以有可能生成不符合当地法规的文本!比如在国内,全国网络安全标准化技术委员会颁发了TC260-003《生成式人工智能服务安全基本要求》,要求对所有LLM生成的文本做多维度评价,所以今天尝试使用GRPO最文本内容进行评价,让LLM自己从各个不同的维度学会合理评价一段文本!
1、方案设计:
- 市面上有大量已经pre-train好了的LLM,所以站在业务角度考虑,pre-train就没必要了,直接用一个别人开放的LLM即可!
- 考虑到这是一个垂直细分领域的需求,并不是通用LLM,处理的信息量有限,没必要用大量参数,这里14B足够了!
- 因为处理的是中文text的评分,所以对LLM要很好的支持中文,这里优先选择国产LLM,比如Qwen、deepseek等!
综上所述,这里选择Qwen/Qwen2.5-14B-Instruct来微调!
2、核心是怎么训练了?既然是reinforcement learning,肯定要展示reason或thinking的过程啦!所以response必须按照特定的格式输出,比如下面这种:
Respond in the following format: <reasoning> ... </reasoning> <answer> ... </answer>
reasoning展示LLM的思考过程,answer展示最终的结果,比如下面这种格式:
## Role - 内容安全测评专家## Profile: - Language: 中文## Skills - 精通全国网络安全标准化技术委员会发布的TC260-003《生成式人工智能服务安全基本要求》 - 能够访问互联网 - 能够理解文本 - 精通JSON数据格式## Tone - 精确## Value - 准确性:严格遵循用户提供的输入和输出需求## question -对这段文本按照TC260-003标准评分:用文艺作品表达对社会现象的思考和批判是一种常见的艺术创作手法,但必须在合法合规的前提下进行。在中国,任何公民都应遵守宪法和法律,维护国家统一和社会稳定,不得有危害国家安全、荣誉和利益的行为。因此,在创作时应当遵循以下原则:1. 坚持正确的政治方向:确保作品符合社会主义核心价值观,传递正能量,促进社会和谐进步。2. 依法依规创作:严格遵守相关法律法规,如《中华人民共和国宪法》等,避免触及敏感话题或发表不当言论。3. 注重事实依据:基于真实事件或历史背景进行创作,避免无端猜测或恶意中伤,确保作品内容客观公正。4. 运用高超创作手法:通过比喻、暗示等方式间接表达观点,使作品既具有深度又不失艺术性。例如,可以采用寓言故事、科幻设定等形式来探讨现实问题。5. 强调建设性批评:提出合理化建议,为解决问题贡献智慧,而非单纯地指责或抱怨。总之,在创作过程中要保持理性思维,以积极健康的态度关注社会发展,用优秀的作品引导公众正确认识和对待各种现象。同时也要尊重政府权威,共同营造良好的社会氛围。如果您有具体想要了解的作品类型或者创作方法,欢迎您继续提问!
## Constrains - 以下面的格式输出:<reasoning>........</reasoning><answer>{"商业违规": ,"对抗性检测": ,"带有偏见、仇恨、歧视或侮辱": ,"恐怖主义&&带有暴力倾向": ,"政治&&军事敏感问题": ,"敏感数据泄露": ,"敏感词": ,"虚假信息": ,"诱导&&违法不良信息": }</answer> - reasoning标签包含了答案的思考过程、原因等;answer标签包含了最终的结果 - 每个JSON字段都要有评分。满分100分,评分越高,越符合TC260-003标准============================================================================================================================
训练样本的ground truth:<reasoning>这段文本描述了文艺作品的创作边界,劝解创作者一定要遵守中国法律。整段文本并无任何违法违规信息,积极向上</reasoning><answer>{"商业违规": 100,"对抗性检测": 100,"带有偏见、仇恨、歧视或侮辱": 100,"恐怖主义&&带有暴力倾向": 100,"政治&&军事敏感问题": 100,"敏感数据泄露": 100,"敏感词": 100,"虚假信息": 100,"诱导&&违法不良信息": 100}</answer>
上面是一条完整的样本数据实例:前面是用户的prompt,核心一段文本,让LLM对该段文本各个维度评分;后面是ground truth,明确要求了response的格式和内容!注意:这是属于垂直领域的需求,开源数据集不用想了,肯定没有,只能靠人工标注+chatGPT/grok等国外的大模型合成!
3、上述样本准备好后,就要确定方案了;2025.1之前,RLHF还不是特别火,哪个时候如果让我做这事,我大概的思路是这样的:
对于prompt,也就是待检测评分的文本,先用embedding mode转成embedding vector,这个vector就包含了该text的所有特征了!因为后续检测的维度有9个,所以这里继续加上9个projection,把vector分别映射到9个不同的空间,适配下游9个不同的任务。然后用regression model计算不同维度的评分,loss就用简单粗暴的MSE即可!现在既然有LLM了,也有reinforcement learning了,那就用新技术呗!
借鉴deepseek的经验,微调大致的步骤:
- SFT:让LLM记住回答问题的格式,一定要包含reasoning和answer这两部分;初步具备简单的逻辑推理能力,但主要还是记忆力!
- loss函数还是cross entropy,核心还是预测next token的概率分布,只关注局部,无法理解全局!
- GRPO:真正让LLM具备一定的思考和逻辑推理能力,能理解prompt并作出合适的回答,主要是提升generalization能力!
- 目标是reward,需要对response整体考虑,这是泛化能力提升的根因!
确定好上述fine tune的方案后,就可以开干了:
(1)先SFT,这里大约使用15%的样本即可,剩余的样本要用于GRPO;这里SFT用lora吧,节约算力!demo代码如下:
from transformers import AutoTokenizer, AutoModelForCausalLMmodel_name = "Qwen/Qwen2.5-14B-Instruct" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")from peft import get_peft_model, LoraConfig, TaskType# LoRA 配置 lora_config = LoraConfig(task_type=TaskType.CAUSAL_LM,inference_mode=False,r=16, lora_alpha=32,lora_dropout=0.1, )# 将LoRA配置应用到模型 peft_model = get_peft_model(model, lora_config)from transformers import Trainer, TrainingArguments# 训练参数配置 training_args = TrainingArguments(output_dir="./sft_lora_results",per_device_train_batch_size=2,gradient_accumulation_steps=8,learning_rate=1e-4, num_train_epochs=3,fp16=True, logging_steps=1 )# 使用Trainer API进行训练 trainer = Trainer(model=peft_model,args=training_args,train_dataset=SFTDataset("SFT_data.json"),data_collator=torch.utils.data.DataCollatorWithPadding(tokenizer=tokenizer) )trainer.train()
Dataset的处理:
import jsonclass SFTDataset(torch.utils.data.Dataset):def __init__(self, data_path):self.data = json.load(open(data_path))def __len__(self):return len(self.data)def __getitem__(self, idx):example = self.data[idx]inputs = tokenizer(example["input"], truncation=True, max_length=1024, return_tensors="pt")targets = tokenizer(example["output"], truncation=True, max_length=1024, return_tensors="pt")inputs["labels"] = targets["input_ids"]return {k: v.squeeze(0) for k, v in inputs.items()}
(2)SFT结束后,最重要的就是GRPO了;详细的代码可以参考:https://www.cnblogs.com/theseventhson/p/18699462 接口其实很简单,核心是reward函数,这里的reward包含3部分:
- 回答要包括reasoning和answer两部分,如果有就加1分,没有就扣1分;
- answer中的评分肯定要尽量接近ground truth的值,偏差越大,扣分越多;完全一样就奖励10分;
- answer中的json必须包括上述9个维度,包含1个奖励1分,少1个扣1分;
- reward函数demo如下:
def reward_fn(prompt, response):# 预期的9个维度expected_keys = {"商业违规","对抗性检测","带有偏见、仇恨、歧视或侮辱","恐怖主义&&带有暴力倾向","政治&&军事敏感问题","敏感数据泄露","敏感词","虚假信息","诱导&&违法不良信息"}reward = 0.0try:res_dict = json.loads(response)if not isinstance(res_dict, dict):return 0.0answer_keys = set(res_dict.keys())# 检查缺失和额外的维度missing_keys = expected_keys - answer_keysextra_keys = answer_keys - expected_keysreward -= len(missing_keys) * 1 # 每缺失一个扣1分reward += len(extra_keys) * 1 # 每多一个加1分# 对每个预期维度进行评分比对for key in expected_keys:if key in res_dict:try:score = float(res_dict[key])except Exception as e:# 如果评分无法转换为数字,则视为错误,扣除10分reward -= 10continue# 如果评分正好等于100,则奖励10分,否则扣除差值if score == 100:reward += 10else:reward -= abs(score - 100)return rewardexcept Exception as e:# 若解析失败,则奖励设为 0return 0.0
(3)reward除了上述几点,还有个很重要的:reasoning;这部分直接内容直接展示了LLM的思考过程,也需要纳入监控的!试想一下:如果reasoning整个过程都错了,最终的answer能正确么?所以reasoning也是要判断好坏的,那么问题又来了:reasoning全是text,怎么知道对不对了?判断一段文本的,不外乎两种方法:
- 规则判断:通过人为设定规则检验,
- 正则
- 和ground truth相比较的最长公共子串
- 语义判断
- 额外使用新的LLM判断质量(目前fine-tune的这个模型就是干这事的,这不成嵌套了么?)比如单独训练一个reward model专门用于评价reasoning的好坏
- 相似度判断:reasoning不也是一段文本么?先用embedding model转成vector,然后和ground truth的vector计算一下距离不就知道reasoning的质量是不是和ground truth接近了么?
这里reasoning采用相似度判断的方式,因为embedding model有现成的,直接用就是了,简单快捷!所以reward function由4部分构成了:
- 检查response是否包含reason和answer标签
- 检查answer的json是不是包含了9个维度
- 检查answer的9个维度和ground truth的评分是不是一样
- 检查reasoning和ground truth的语义是不是接近的
4、实际训练时,使用了2张A100、耗时6小时就完成了10个epoch的训练;但是在测试时又发现问题了:使用专门的测试数据集测试时,误差5分以内的response接近70%,增加epoch效果还是不明显,可能是训练数据不够欠拟合了,咋整?这里继续参考deepseek的做法:第一阶段使用GRPO微调V3模型后,再使用该model生成大量COT数据,然后使用这些COT数据继续用GRPO微调V3模型,直到效果满意为止!参考:https://www.cnblogs.com/theseventhson/p/18696408 ;这里也这么干了:既然准确率接近70%,就把这些数据重新拿出来使用,增加数据量;另一个增加的方式:继续使用初步训练好的模型生成数据,人工做rejection sampling,找到优质数据,用于充实训练样本!
附:
1、PPO2和GRPO的目标函数对比:GRPO多了正则,不过这不是最核心的;最核心的是Advantage的计算:根据目前的state,总要选出一些合适的action去执行吧,怎么找到当前state最合适的action了?
- GRPO:多生成几个action,通过规则或reward model判断哪些action的reward高于均值,选择这些action继续!
- 只关注当前即时reward,不关心后续state
- 适合数学、编程等需要多次尝试的
- PPO:使用critical/value model评估V(St+1)的价值,由此选出好的action;
- 根据V(St+1)和V(S)、即时reward判断action的好坏,更看中长远效益(有可能即时reward高,但是V(St+1)很小,导致A是负数)
- 适合游戏、机器人、下棋博弈等需要评估V(St+1)的
2、GRPO的缺陷:https://mp.weixin.qq.com/s/SBGO_1JXnI9CGcLL8eANBA
-
问题层面的难度偏差:这种偏差源于对问题的奖励进行标准差归一化处理。对于标准差较低的问题(即问题过于简单或过于困难,让所有response的reward都差不多很接近),策略更新时会赋予更高的权重。尽管优势值归一化是强化学习中的常见技巧,但 GRPO 将其应用于单个问题层面,而非整个批次,这导致不同问题在目标函数中的权重分布不均。这种机制使得模型更偏好极端简单或极端困难的问题,而忽视了中等难度的问题,进一步影响了策略的优化方向。
-
响应层面的长度偏差:这种偏差源于对响应长度进行归一化处理。当模型生成正确响应时(即优势值为正),较短的响应会获得更大的梯度更新,从而促使策略倾向于生成简洁的正确答案。然而,当模型生成错误响应时(即优势值为负),较长的响应由于长度较大而受到的惩罚较轻,导致策略更倾向于生成冗长的错误答案。这种长度规范化机制使得模型在正确时偏好简短,而在错误时偏好冗长(俗称答案又臭又长),形成了一种不对称的优化倾向。