在这个系列之前的文章中:
我们探索了o1(可能基于test-time scaling law)做的 基本框架 。
以及框架中的一块积木 (靠纯inference优化来增强逻辑推理能力 ,我们分别列举了“PRM+some search methods”以及“MCTS”两种方法,本质上来说这两种都是MCTS-style的评估-搜索方法)
在这篇文章中,我们来探索另一块更好玩的积木: “o1是如何拥有自我纠错的能力的?” 。在下文中,我会基于自己的猜想,把o1的这种self-correction的能力与基于强化学习的post-training过程,以及我们上篇中所说的MCTS过程串起来 。同样,这也是我在阅读相关论文,及观察o1输出结果demo后给出的自己的猜想,具有强主观性,大家选择性阅读。
一、o1 demo中体现的自我纠错能力 在openai o1官网的例子中(https://openai.com/index/learning-to-reason-with-llms/),当我们展开模型回复结果里thought for xx seconds选项时,我们可以看见模型在推理过程中的整个思维链过程,这时我们会发现一个有趣的规律,o1在思考时展现了【自我纠错】的能力,例如:
User_question: xxxx o1: xxxxxx, But wait , xxxxxx Alternatively, xxxxx, hmm but, xxxx Alternatively, xxxxx
我们观察到,o1有时会在提出一系列的分析后,对这串分析进行自我验证(but wait),如果不对,那么就会采取别的思路(Alternatively) 。我观察到Alternatively里这个单词在o1切换方案时频繁出现,估计就是训练时给的prompt模版之一。
那么有趣的问题来了:模型是怎么知道自己可能做地不太对?
如果基于上篇我们讲MCTS的文章来说,假设模型在post-training或者之后的搜索优化中用到了MCTS,那么既然你都用了MCTS了,你肯定是指望它一下就能搜索到最佳的路径(这也是MCTS设计的初衷,如果错了还要重搜,说明这棵搜索树本身就训得不好) 。同时,你也很难想象,在MCTS中,如果遇到了错误的结点(暂且不说怎么判断是“错”的),你该怎么做,难道要回退吗?又回退到哪个祖先结点才可以呢?
在我刚开始探索o1时,我同样有这样的困惑。因为我一直认为o1的training data模式是“self-generated(人工标注应该更多用在PRM的训练上) + 某种基于inference的优化方法帮助生成高质量训练数据” ,所以如果想让训练数据里就有纠错信息,必然要在inference方法上做相应的考虑。在(痛苦地挠抓头皮)之后,我有了一些自己的猜想,所以下面,我们马上来循序渐进地回答这个有趣的问题。
二、从MCTS的角度看o1 demo的具体例子 在上一篇中,我们讲过MCTS通过模拟人类的思考方式所定义出的【行动空间】,那么现在我们结合o1官网的一个具体的例子,来看看如何把相似的思路用在上面 。我们选用的是官网给出的【Science(化学)】问题样例,因为它的样例中涉及到了【自我纠错】(别的样例要么太长,要么没有)
【阅读本节前一定先看讲解MCTS的第二篇 !】
2.1 原始问题 大致意思就是:求NH4F的0.1M溶液的PH值,给定了NH4+的Ka值(酸解离平衡常数)和Kb值(碱解离平衡常数)。
看不懂问题也没关系哈,不需要有什么化学知识,我们只关注o1回复中thoughts的规律。
2.2 rephrase(重述问题) 首先,o1执行了rephrase这个动作,把问题重述了一遍,特别地,它把原始问题中的一些重要的条件用Given这种形式再次明确了一遍。正如我们在MCTS篇中所说的,模型能做到这些是因为每个动作空间都配有一些特定的prompt。
2.3 propose subquestion and subanswer 接着,模型开始把复述过的问题分解成“子问题-子回答”,它提出的第一个subquestion是:
接下来它对这两个问题分别给出subanswers:
同样,subqs-subas也是在进行动作空间采样后,通过相关prompt指引生成的。同时回顾MCTS篇的讲解:这里不意味着rephrase之后一定接subqs-subas,在MCTS的rollout阶段这个动作空间是随机采样的,在推理阶段这个动作空间是基于价值函数做选择的。
2.4 emphasize 接下来,模型继续复述/强调了一遍原始问题。emphasize这个动作空间在MCTS讲解篇中没有,但是我们完全可以灵活地根据自己的需要和理解,去拆分o1 demo的动作空间。
2.5 propose original subanswer 接下来,针对这个被emphasize的问题,模型提出了第1个子回答,也就是去比较两个离子的酸碱程度:
但是显然,(也许在某种方法的判定下,我们后面就会来说这种方法可能的实现),模型觉得这个回答不够充分。(注意,这里用的形容词是“不充分”,而不是“错误”)
2.6 propose a self-corrected subanswer 于是,模型开始提出进一步修正上面的子回答(第一个alternatively出现了):
这个子回答的产生过程可能还包括一系列“提出假设-证明-得出假设结论是否正确”等一系列更精细的动作空间(表现在MCTS搜索树上就是一个或若干个结点),这里不再细拆。需要强调的是,模型还是通过某种方法,知道当前的解法可能还是不充分。
2.7 propose another self-corrected subanswer 所以,模型又提出了一种解法(又一个alternatively出现了),这回这个解法通过了模型那【某种方法】的验证,模型认为是可信的,于是step by step给出了接下来的所有结果(这里就不截图了)。这样,一个完整的思维链就出来了。模型最后根据原始问题+这个思维链,生成了给用户的最终答案。
好,在我们讨论【模型到底用什么方法来判断一个方案是否可行】前,我们来端详这个例子,看一个有趣的现象 :
尽管模型提出了多个方案(多个计算公式),但是这些计算公式本质是相同的,他们都是在假设溶液中离子浓度相等的情况下给出pH值的计算公式。
也就是说,尽管模型采用了不同的方案,但是这些方案间可能是重复的,即一个方案换一种方式来说,而不绝对是重新换一个方案。
根据这个现象,我们来猜想模型做判断的方法:
这时它的动作空间是“propose a self-corrected subanswer” 它吃这个思维链(MCTS搜索树)上的所有前置结点作为context,然后输出一个修正后的结果。 注意,“修正”的含义不一定是更改,也有可能是重述,取决于模型自己对当前答案质量的判断。 通过这种方式,在类MCTS搜索框架的前提下,也能让模型在不做任何回退操作的情况下,一路向下进行树结点的生成和搜索。 那么,谁来执行这种【判断方法】呢?是待训练的模型本身,还是可能引入一个新的模型来做这个判官?我个人认为是待训练的模型本身,因为之后我们还需要直接使用它来做用户推理,所以这个模型本身应该要能具备在给定采样动作的情况下去做修复的本领。 也就是先让模型具备这种修复能力(这个能力不一定要很强,但得有),再讨论接下来的inference优化,结合两者更好产生self-generate的训练数据,以便用于后续可能的post-training相关优化中。
那么接下来,我们就来探索这种【判断方法】的一种可能的实现方式,它采用的是self-generated training data + 多轮post-training(RL)的方法。
三、判断结点是否进行self-corrected采样:以SCoRe为例 这里,我们借助deepmind在今年9月提出的SCoRe(Self-Correction via Reinforcement Learning) 工作,来看模型做修正判断的一种方法(当然说起方法,这也是一块积木,实现方式有很多,这里只是给出一个我觉得合适的例子)。
paper:https://arxiv.org/abs/2409.12917
SCoRe(Self-Correction via Reinforcement Learning):一个可以对回答进行自我修复的强化学习模型。
SCoRe是自产自消的训练模式 ,即训练数据完全是self-generated,是一个on-policy的策略。
SCoRe最多只进行2轮回答(也即做1次修复) ,如下图所示,problem + 第1轮回答 + 一段instruction模版(提醒模型判断答案是否正确) + 第2轮回答 + 最终答案
直接来看SCoRe的整体运作流程:
(1)首先,
我们有一个基础模型(base generator) (2)基于这个基础模型,我们采用强化学习的方式,训练若干轮stage1 ,具体细节如下:
x1, y1: 将原始问题喂给模型后,产出的第1轮回答 ,其中x1是回答的文字描述(文字+数字),y1则是从x1提取出的数字部分
p1:引导模型做第二轮回答的instruction模版(参见上图中的示例)。
[x1, p1]:拼接“第1轮回答 + instruction模版” ,作为第2轮的输入
作者通过实验,发现在传统的self-correction方法中(例如sft,其训练数据为“prob + 第1轮 + instruct + 第2轮 + final_ans”之类),训练过后的模型可能会“把原先对的改成错的”。为了避免这样的情况,作者希望第1轮的答案尽量不要偏离原分布太远,毕竟即使错了还有第2轮的改正机会(别越训越笨) 奖励函数部分,只用于评估第2轮回答和真值回答之间的契合度
KL散度部分,只用于限制“第1轮回答的分布”不要偏离“基础模型的分布”太远
(2)在若干轮stage1的基础上,我们同样采用强化学习的方式,用更新后的模型,再进行若干轮stage2的训练,细节如下 :
对第1轮答案 计算奖励和KL散度(计算方式同stage1)
对第2轮答案计算修正后的奖励(shaped reward) 和KL散度
修正后的奖励(shaped reward)计算方式如下 :
其含义为,第2轮奖励-第1轮奖励 ,同时用超参 控制这一项的大小
这样设计的目的是,鼓励第2轮的回答尽量比第1轮的回答要好,这样修正才有意义。 如果第2轮的回答差于第1轮,那么超参则控制惩罚力度。
最后,在作者的实验中, 时表现较好。作者也建议对于stage1中的 ,我们可以采取动态变更的方式,决定模型在多大程度上不要偏离基础模型。
(4)若干轮stage1 + 若干轮stage2交互训练,直到达到停止条件为止。