论文地址
https://arxiv.org/pdf/2411.15124
模型是AI2的,他们家也是玩开源的
先看benchmark,几乎是纯用llama3 405B后训练去硬刚出一个gpt4o等级的LLamA405
我们先看之前的机遇Lllama3.1 405B进行全量微调的模型 Hermes 3,看着还没缘模型好(当然如果你去Nous的HF页面下看,它做了一些别的指标超了LM3,但是不是特别常用)
我们先分析为什么全量微调不会比源模型好
学院派(我
,但我肯定也是实干派):没有原始数据分布,微调数据量少,很难对base模型的权重进行有效的调整,所以一般都是base model的原产地来微调效果好,因为它有原始的pretrain数据,所以可以混着一起调整,一般1:3.这样学起来就很泛化效果也好
Nous没有原始数据,自然差点意思
OK,然后从另一面,我们理解一下微调的本质是什么?
你预训练好的模型其实知识能力的储备已经不说到头了吧,反正也大差不差
但是这时候它不能组织明白语言,也回答不好你的问题
夸张一点的早起LLM的例子(看着很邪性,但是我以前确实碰到过
):
你:中国的首都是哪?
LLM: The capital of the United States is Washington, DC.
那你指令微调,supervisor fine tunning 简称SFT的目的就是让它能好好说话。
单独就训练这个具体的事物来讲,和预训练有区别吗?尤其是全量微调(Lora就别参与讨论了)
其实没啥区别,因为你训练的三元素
1- 神经网络:长得一样
2- Loss 函数:没有本质不同,差一不二
3- 训练数据:主要这玩意不一样
我们看训练数据
一般来讲sft都sft chat模型
chat模型的template一般长这样的json
[
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "你为什么生气?"
},
{
"role": "assistant",
"content": "因为无能。"
}
]
但是到了模型里要被进行llm能理解的special token,就要求你的sft训练数据这样
<|begin_of_text|>
<|start_header_id|>system<|end_header_id|>
You are a helpful assistant.<|eot_id|>
<|start_header_id|>user<|end_header_id|>
你为什么生气?<|eot_id|>
<|start_header_id|>assistant<|end_header_id|>
因为无能。<|eot_id|>
上面这些<>里的都会被tokenzier定义成特定的token id,以表明对话开始结束之类的说明
然后说Loss函数
Pretrain大家都知道是对next token求loss
SFT是对问题的答案求loss
end_str = "<|start_header_id|>assistant<|end_header_id|>"
inputs = tokenizer(batch, max_length=max_length, padding=True, truncation=True)
input_ids = inputs["input_ids"]
input_len = len(input_ids[0])
end_ids = tokenizer(end_str)["input_ids"]
end_id_len = len(end_ids)
loss_mask = []
for input_id in input_ids:
for i in range(len(input_id) - end_id_len, -1, -1):
if input_id[i:i + end_id_len] == end_ids:
mask = [1] * (input_len - 1)
mask[i:i + end_id_len - 1] = [0] * (i + end_id_len - 1)
loss_mask.append(mask)
break
if i == 0:
loss_mask.append([0] * (input_len - 1))
inputs = {k: torch.tensor(v) for k, v in inputs.items()}
loss_mask = torch.tensor(loss_mask)
loss = torch.nn.functional.cross_entropy(logits, labels, reduction="none")
loss = loss * loss_mask
loss = torch.mean(loss)
其实就是碰到"<|start_header_id|>assistant<|end_header_id|>这样的,前面的就肯定是问题了,所以mask都置为0.后面的自然还时一个token 一个token的推倒(自回归本质不能变),然后求个loss的均值,就能得到回答的loss了,所以我说本质上Loss 函数也是一样的。
传统SFT的数据太少了,很多都需要人工制作,然后还是不够,再做数据增强,但是还是不够... 以为预训练的数据太大了,所以没办法只能夹着一部分预训练的数据一起来做sft了,而且这里还涉及一个预训练数据分布的问题,夹在一起训练效果能好一点
那如果我本身SFT数据量就不小了呢?????
那其实SFT就跟continue pretrain也可以认为是等价了
去哪弄那么多SFT数据那?
从GPT4o或者其他成型的LLM里面做self instrcut是一个常用的手段,说白了就是你生成一些种子问题,然后种子问题从GPT4o或者其他的模型里要答案,然后种子问题再生成问题,问题生答案,答案生问题,往复循环,就会形成一个比较全面的SFT dataset了,而且是可以保证质量的
我以前写过一篇文章
https://mp.weixin.qq.com/s/hU4I1ZQSEQhyi4-qOCruSw?token=2146785893&lang=zh_CN
刚才我们讲了当你的SFT数据集满足到一定的量级,你就可以把它当pretrain来处理了,所以也不再用担心LLM学不进去,但是此时数据分布里的任务可能太多样或者多样性不够,导致你学习的效果也是一般
Tülu 3是咋解决这个问题的呢?
监督微调(SFT)阶段的数据集:
公共数据集来源:Tülu 3 从各种公共数据集中获取提示,包括:
-
OpenAssistant
-
No Robots
-
WildChat (GPT-4 子集)FLAN v2
-
SciRIFF
-
TableGPT
-
OpenMathInstruct
-
NuminaMath-TIR
-
Evol CodeAlpaca
-
Aya
Tülu 3 合成数据集:Tülu 3 还合成生成了特定技能的数据集,以填补公共数据集的空白,包括:
Tülu 3 PersonaMATH: 用于数学任务
-
Tülu 3 Persona GSM: 用于常识推理任务
-
Tülu 3 Persona Algebra: 用于代数推理任务
-
Tülu 3 Persona Python: 用于代码生成任务
-
Tülu 3 CoCoNot: 用于安全和非合规性任务
-
Tülu 3 WildJailbreak
-
Tülu 3 WildGuardMix
-
Tülu 3 Persona IF: 用于精确指令跟随任务
-
Tülu 3 IF-augmented: 用于指令跟随的增强数据集
数据混合:SFT 阶段的数据混合策略,包括从各种来源选择高质量数据集,并删除质量相对较低的数据集
数据去污:为了避免训练集污染评估集,所有数据集都经过了严格的去污处理
这里面我觉得它SFT效果不错,数据去污占大头。别的SFT可能不是特别注意这块
怎么理解数据去污
Tülu 3 模型在数据准备阶段采取了严格的数据去污措施,以确保评估数据不会泄露到训练集中,从而保证模型评估的准确性和公正性。
以下是 Tülu 3 数据去污的逻辑和方法:
目标:数据去污的主要目标是避免模型在训练过程中接触到评估数据,从而防止模型在评估时出现过拟合现象,确保评估结果能够真实反映模型的泛化能力
去污方法:
提示去重:Tülu 3 的数据去污工作首先会检查所有的提示,去除与评估套件中完全匹配或高度相似的提示
关键词过滤: 对提示进行关键词过滤,删除或替换包含特定关键词(如“OpenAI”)的提示
手动审查: 除了自动化的去重和过滤,研究人员还会对数据集进行手动审查,以识别并删除任何可能导致数据泄露的提示
Tülu 3 Eval 去污工具: Tülu 3 使用专门开发的工具来确保提示不会被评估套件污染
数据集修改:对于检测到污染的现有数据集,Tülu 3 会对这些数据集进行修改,例如下采样或删除受污染的实例
去污过程:
公共数据集的审查:在整合公共数据集时,Tülu 3 团队会对每个数据集进行人工审查,并筛选掉那些与评估集存在重叠的数据
合成数据的审查:对于合成生成的数据,也会进行类似的审查,以避免模型生成的内容与评估数据重复
多阶段去污:数据去污是在多个阶段进行的,包括提示的初始收集、SFT 数据的准备以及 DPO 数据的准备
数据集标记:
修改标记: 被修改过的现有数据集会用 α 标记
Tülu 1/2 标记: 使用过的来自Tülu 1或2的数据集会分别标记为 1 或 2
具体应用:
SFT 数据集:在准备 SFT 数据集时,会过滤掉包含模型或开发者信息的响应,以避免训练数据中包含不必要的内容
偏好数据集:在构建偏好数据集时,会确保用于生成响应的提示不包含评估集中的内容
IF-augmented 数据集: 为了提高指令跟随能力,Tülu 3 生成了 IF 增强数据集。然而,为了保证数据集的清洁,只有那些真正满足约束条件的实例才会被加入最终的集合(这个现在不用,DPO时候用,一会讲)
去污的重要性:
避免过拟合: 通过去除训练集和评估集的重叠,确保模型在评估时不会因为见过数据而表现出虚高的性能(这个其实太重要了,大多数模型没做这个,所以看着评测质量不错,一用起来很垃圾)
保证评估的真实性: 确保评估结果真实反映模型在未见数据上的泛化能力,使得模型在实际应用中更加可靠
总而言之,Tülu 3 的数据去污逻辑是多层次、多步骤的,涵盖了从数据收集到模型训练的整个过程。通过这些方法,Tülu 3 旨在建立一个高质量、无污染的数据集,为模型的有效训练和评估奠定基础
当做了去污数据化以后的dataset,拿这个做SFT,你发现在avg without safety的评分上,甚至不如原版
这个也正常,因为这么训练相当于给模型喂了新的数据,影响了权重的分布,所以测试不如原版也正常(所以它这么折腾都没原版强,而大家对“一个sft就能超过原版这个逻辑是否成立”也应该有一个很好的判断
,绝大多数微调出来的模型除非特定任务,否则和原始模型比起来大多都是废物)
接下来是对这个模型进行DPO,其实对于原始模型来讲Llama 3.1 Instruct, 本来SFT,DPO都经历过了,我们之所以还要DPO,是因为2点
1- 刚新大剂量SFT了一下
2- DPO以获得比原模型更好的能力
DPO 也是RL,硬说和ppo啥区别,就是没人,全靠数据驱动,DPO也是没有奖励模型的,比如GPRO,就靠function来定义 reward
省钱肯定是省钱的,但是说比PPO好,我个人是不赞成的,可是现在这些新模型确实都是以DPO为主的,它们论文里写为了防止reward hacking这个我觉得并不是核心,核心是reward model真的不好训,而且PPO的不管HL还时AIF都有点麻烦,不如DPO直接顶着干见效果快
Tülu 3 的 DPO(Direct Preference Optimization,直接偏好优化)有以下特点,并在多个方面与一般的 DPO 方法有所不同:
长度归一化 DPO (Length-normalized DPO):
Tülu 3 不使用标准的 DPO 目标函数,而是采用了长度归一化的 DPO 变体
这个变体通过对数概率进行长度归一化,从而缓解了人类和模型偏好中常见的长度偏差
公式:长度归一化 DPO 的目标函数为:
输入解释:
yc被认为更优的候选输出(chosen)。
yr被认为次优的候选输出(rejected)。
x输入上下文。
πθ当前模型(policy)的输出概率分布。
πref参考模型(baseline policy)的输出概率分布。
β一个缩放参数,用于调整目标函数中相对重要性。
目标公式含义:
-
-
偏好数据来自人工标注的成对对比(比如 "更好的回答" 和 "次优回答")。
-
使用一个基于逻辑回归的模型,将更优答案的分数更靠近 1,次优答案的分数更靠近 0。
-
优化的目标是最大化更优答案 yc 的条件概率,同时最小化次优答案 yr 的条件概率。
-
优势:这种方法可以有效地减轻由于模型生成文本长度不同而导致的偏差,使得模型能够更好地学习人类偏好,说白了就是强者更强,要不除以|y|一般都会选长的,但是长的不一定就对
在线策略数据和离线策略数据结合:
Tülu 3 的 DPO 不仅仅使用离线偏好数据,还创新地采用了在线策略数据,即通过 Tülu 3 SFT 模型生成完成,并使用 GPT-4o 进行偏好标注
这种方法可以更好地利用模型自身的生成能力,并结合其他模型的优点,从而提高偏好数据的质量和多样性。
在线策略数据的生成过程:从 SFT 阶段使用的提示以及其他来源(如 Ultrafeedback 和 Persona)中选择提示,然后从不同的模型中采样四个响应,最后使用 GPT-4o 对这些响应进行评分,并转换为二元偏好对
偏好数据的合成管道:
Tülu 3 改进了 UltraFeedback 的偏好数据生成管道,该管道包括提示选择、响应生成和偏好注释三个阶段
提示选择:除了 SFT 中使用的提示外,还使用了 SFT 中未使用的子样本提示,以及新的 OOD(Out-of-Distribution)
响应生成:为每个提示从不同的模型中采样四个响应
偏好注释:使用 LLM-as-a-judge (GPT-4o) 对响应的帮助性、指令跟随、真实性和诚实性进行评分(这其实已经是一种RLAIF了)
二元偏好:根据响应的平均评分,将最高评分的响应作为首选,并将其他较低评分的响应随机抽一个作为拒绝
多来源的偏好数据集混合:
Tülu 3 的 DPO 使用了来自不同来源的偏好数据混合,例如:
SFT 重用数据:包括在 SFT 阶段使用的提示生成的在线和离线偏好数据
WildChat 数据:来自真实用户交互的提示,并包含指令跟随约束的提示
IF-augmented 数据集:结合 Tülu 2 SFT 数据集中的指令和新的约束条件,用于精确指令跟随任务
Persona IF 数据集: 用于精确指令跟随的个性化数据集
以上两个都是专门训练指令跟随能力的(我认为LLM最重要的一个能力之一)
Ultrafeedback 数据集:经过清理的 Ultrafeedback 数据集
这种混合策略旨在提高模型的整体性能,并覆盖各种不同的技能和任务。
数据消融研究:
Tülu 3 团队进行了广泛的数据消融研究,以确定最佳的偏好数据混合,通过实验来评估不同数据组合对模型性能的影响
研究发现,增加独特提示的数量 可以提高 DPO 的性能
研究还发现,与重用 SFT 中的提示相比,使用未使用的提示可以提高性能
包含在线策略数据可以提高 DPO 的性能
意思就是没见过的prompt来做DPO更难训,训好了自然也更厉害,同时它不光用离线数据DPO,也采取了生成的在线数据来做DPO往复训练(其实这块和deepseek玩左脚踩右脚的思路有那么一点点像)
此外,与重复提示相比,收集独特的提示和进行适当的混合更重要
超参数和算法优化:
Tülu 3 探索了多种 DPO 算法变体和超参数,最终选择了长度归一化的 DPO 作为最佳方法
通过对不同的学习率进行实验,找到了最佳的学习率
与 PPO 对比:
虽然 Tülu 3 团队主要使用 DPO 进行偏好调整,他们也进行了 PPO 的对比研究,发现 PPO 在没有经过充分调整的情况下,可以达到与 DPO 类似的平均分数,(所有我说过,有钱选PPO还是PPO更牛B一点)但是 PPO 的计算成本更高
DPO以后模型几本就能超越原生405能达到和DeepseekV3相同的平均能力了
但是Tülu 3又玩了一个花活儿
RLVL
Tülu 3 引入了一种名为基于可验证奖励的强化学习 (RLVR) 的新方法,这个RL方法是在DPO之后,用于训练具有可验证结果的任务的语言模型,例如数学问题求解和指令遵循。RLVR 的核心思想是利用可验证的奖励而不是奖励模型来训练模型,这与传统的强化学习方法有所不同
以下是 RLVR 的主要能力和特点:
使用可验证的奖励:RLVR 不像传统的 RLHF(基于人类反馈的强化学习)那样使用奖励模型,而是使用一个验证函数,该函数检查模型生成的答案是否正确...。如果答案是可验证的正确,则模型获得奖励;否则,奖励为零...。这种方法直接基于结果进行奖励,而不是依赖于奖励模型。
针对特定技能的训练:RLVR 特别适用于那些可以针对基本事实结果进行评估的技能,例如数学问题解决和精确指令遵循。Tülu 3 使用 RLVR 来提高 GSM8K、MATH 和 IFEval 的性能。
简化现有方法:RLVR 可以看作是现有引导 LM 推理方法 (如 STaR) 的简化形式,或者是带有执行反馈的 RL 的简化形式。 它使用答案匹配或约束验证作为二进制信号来训练模型。
在线训练:与 STaR 的迭代方法不同,RLVR 完全在线运行,使用二进制奖励。这意味着模型在训练过程中可以实时获得反馈。但是同时这种训练和所有的online policy train一样慢,因为是连生成带训
使用 PPO 算法进行优化:RLVR 使用 PPO(近端策略优化)算法来最大化可验证的奖励。PPO 是一种常见的强化学习算法,适用于训练语言模型。
扩展应用领域:虽然之前的研究主要将这种方法应用于提高数学技能,但 Tülu 3 将 RLVR 扩展到多个评估,并测试了它如何提高整体模型性能,将其集成到通用训练管道中
价值模型初始化:RLVR 的价值模型可以从通用奖励模型或 DPO 模型初始化。实验表明,从通用奖励模型初始化价值模型可以获得更高的 GSM8K 测试分数和平均分数。
不使用奖励模型分数:实验表明,仅使用可验证的奖励比使用奖励模型的分数表现更好。使用奖励模型的分数可能会引入更多噪音,尤其是在平均分数方面。
提高目标领域的性能:RLVR 可以提高目标领域的测试性能, 例如,在使用 RLVR 训练后,GSM8K、MATH 和 IFEval 的性能都得到了提高
与其他训练方法结合:RLVR 通常在偏好调整 (DPO) 之后进行,作为最终的微调阶段。在 Tülu 3 的训练过程中,它被用作多阶段训练管道的一部分,以优化各种核心技能的性能
可扩展的基础设施:Tülu 3 实现了异步 RL 设置,可以通过 vLLM 有效地运行 LLM 推理,而学习器可以同时执行梯度更新。RL 代码库也是高度可扩展的,可以训练 70B 和 405B RLVR 策略模型
总而言之,RLVR 是一种利用可验证奖励直接训练语言模型的新颖方法,尤其是在具有可验证答案的领域(如数学和指令遵循)中。它旨在通过将简单的验证功能整合到现有的强化学习框架中来提高模型的性能。
这个特别像DeepseekR1里的GRPO中的两个奖励函数
def accuracy_reward(completions, solution, **kwargs):
"""Reward function that checks if the completion is the same as the ground truth."""
contents = [completion[0]["content"] for completion in completions]
rewards = []
for content, sol in zip(contents, solution):
gold_parsed = parse(sol, extraction_mode="first_match", extraction_config=[LatexExtractionConfig()])
if len(gold_parsed) != 0:
# We require the answer to be provided in correct latex (no malformed operators)
answer_parsed = parse(
content,
extraction_config=[
LatexExtractionConfig(
normalization_config=NormalizationConfig(
nits=False,
malformed_operators=False,
basic_latex=True,
equations=True,
boxed=True,
units=True,
),
# Ensures that boxed is tried first
boxed_match_priority=0,
try_extract_without_anchor=False,
)
],
extraction_mode="first_match",
)
# Reward 1 if the content is the same as the ground truth, 0 otherwise
reward = float(verify(answer_parsed, gold_parsed))
else:
# If the gold solution is not parseable, we reward 1 to skip this example
reward = 1.0
print("Failed to parse gold solution: ", sol)
rewards.append(reward)
return rewards
def format_reward(completions, **kwargs):
"""Reward function that checks if the completion has a specific format."""
pattern = r"^<think>.*?</think><answer>.*?</answer>$"
completion_contents = [completion[0]["content"] for completion in completions]
matches = [re.match(pattern, content) for content in completion_contents]
return [1.0 if match else 0.0 for match in matches]
reward_funcs_registry = {
"accuracy": accuracy_reward,
"format": format_reward,
}
准确reward和格式reward,尤其是accuracy_reward,它是靠引入latex来做比如数学公式的判断对错,可验证的,而不是简单的偏好字符串对比,看你对,你就对,RLVR也是类似的道理
这种奖励机制对数学,代码的友好程度肯定是更高的
做完之后也确实是有提升了,尤其是math这块的提升
综合评测也能和GPT4o叫板了,但是math比Deepseekv3还是差挺多,v3参数也比它大很多,而且是MOE理论上模型通过moe大幅加宽了MLP,学习能力强也是正常的,不过我不推荐一般小白玩moe,训不好
回头看了一眼R1的论文
还是太严格了,所以数学和code会有不错的表现,当然如果扩展RLVR也是可以做出类似的效果
好,总结一下,Tülu 3 反证了一个观点,即纯用精修的数据来SFT,在精修数据DPO,RLVR也能使一个还差不多的模型达到更高的高度,比肩top档的非reasoning模型,这个方法的上限其实理论上还能提升,就是不断的精修数据,以换取更好的结果,但是做到极致,其实就和折腾预训练到RL完事区别也不大了...
不过也算是给了一些没有预训练时间的团队一些启发
他们家模型和数据也基本都公开,甚至包括训练脚本和超参数。大家可以自己去玩玩
从另外一个角度看一眼这个(不要看绝对数值,看相对数值,因为eval的题可能不一样)
从这个角度看qwen2.5max 比 qwen2.5 72和deepseek v3,甚至llama3.1 405 也没什么太大区别,而回到我们的eval,其实和原始405也就差几个点,当然这里具体到math啥的提升不少,确实用起来体验会好很多,但是也确实证明了预训练方法和数据能力亟待提升!