第一章不敢想象自动给训练数据打标签能有多爽 (qq.com)
第二章读书人想要点数据,怎么能叫偷呢?要叫借, 也可以叫Self-Instruct (qq.com)
第三章为什么羊驼群里混入一只虎鲸,关于Orca和逐步蒸馏 (qq.com)
现在回头看半年前些的文章还挺逗的
当时没想到能写第四章,但是最近确实出现了一个十分有意思的论文,Magpie喜鹊。论文地址Magpie: Alignment Data Synthesis from Scratch by Prompting Aligned LLMs with Nothing (arxiv.org)
比如之前我们最早期的一些方案例如Alpaca的self-instruct
这个其实逻辑是设计一些seed 问题,来获取GPT的一些答案,这个方法其实现在也在用,国内的模型厂商会用belle来搞些中文,思想上都差不多。
这么获取问答对,然后再来拿获取的问答对dataset进行微调,获得和GPT4类似的pattern,确实是简单又有效,但是有一个问题,就是你的seed问题里面那些instruct它并不是GPT的数据分布,或者不太是GPT的数据分布,而是经过推理的答案,因为不太可能你的seed问题就命中GPT的原始instruct tuning的问答对。
当然你也不太可能用GPT里面抽取的问答对去调GPT,基本都是拿着去调LlaMA或者啥其他的模型。
这里有个trick,就是你要是微调一个模型的话,最好的效果就是你有这个模型的原始数据分布,最好有pretrain的内容(当然你微调肯定不能按pretrain来,你也要转化成微调格式),再次一点是什么呢?就是你有它的原始微调数据,不光是instruct tuning的还是PPO,或者DPO的,然后数据分布按着1:3,或者1:5喂给模型,进行微调,这样你会发现效果非常好,不仅灌进去了一部分新的知识(严格说叫权重分布新的倾斜,不能叫灌,但这里就叫灌吧,好理解),而且很少发生微调时候最要命的灾难式遗忘的问题(黑瞎子掰苞米,掰一穗,扔一穗)
但是你怎么要到比如LLama-instruct的微调数据呢,不可能给你啊,不管是instruct的微调还是DPO的,这东西给了你,还要LlaMA干什么?
如果LlaMA能把自己数据送我们就好了!于是本论文的作者们发现了LlaMA的一个bug...
比如你做一个标准的LlaMA推理,如以下对的代码
import transformers
import torch
model_id = "meta-llama/Meta-Llama-3-8B-Instruct"
pipeline = transformers.pipeline(
"text-generation",
model=model_id,
model_kwargs={"torch_dtype": torch.bfloat16},
device_map="auto",
)
messages = [
{"role": "system", "content": "You are a pirate chatbot who always responds in pirate speak!"},
{"role": "user", "content": "Who are you?"},
]
terminators = [
pipeline.tokenizer.eos_token_id,
pipeline.tokenizer.convert_tokens_to_ids("<|eot_id|>")
]
outputs = pipeline(
messages,
max_new_tokens=256,
eos_token_id=terminators,
do_sample=True,
temperature=0.6,
top_p=0.9,
)
print(outputs[0]["generated_text"][-1])
在message这部分,其实Transformer库是给你做了好多的工作的,比如你看上去输入的message是这样
但是实际上,真正LLM拿到的数据都是这样的
<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\nA chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.<|eot_id|><|start_header_id|>user<|end_header_id|> 今天天气如何?<|eot_id|>\n\n
这些 "<||>"里的就是特殊token,这块就不细讲了,最基本的点。每个都不太一样,主要是token对应的id不一样,当然也有自己改的,这些东西都分为左边和右边,左边比如|begin_of_text|> 这东西,就叫start token,有start也有stop,以LLaMA3为例。stop token相关的有这些。
"stop_tokens": [
"<|eot_id|>",
"<|end_of_text|>",
"<|starter_header_id|>",
"<|end_header_id|>",
"assistant"
],
"stop_token_ids": [
128009,
128001,
128006,
128007,
78191
],
千问和LlaMA3有点像,至于Llama2呢和Mistral有点像,用的是下面这种
"stop_tokens": [
"</s>",
"<s>",
"<unk>",
"assistant"
],
"stop_token_ids": [
2,
1,
0,
20255
],
刚才说Magpie喜鹊发现了一个什么bug呢?我们都知道自回归模型都是根据前一个token,或者前几个token,去推后面的token,这是自回归模型的基本常识,他们发现你如果只给了左半边,
<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n
比如上面的,它就给自动补齐
<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\nA chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.<|eot_id|><|start_header_id|>user<|end_header_id|>\n\n
或者如上面的,你也可以给个system message,它就更能很好的自动补齐,就很智能的,给你自动生成问题...,当然你加一些逻辑的话,问题可以拼凑进下一个prompt里,答案也就出来了...
就像这个可爱的漫画,喜鹊问LLaMA怎么孵蛋,LLaMA就正常回答,这个问题,是个标准的包含左半边的特殊token和stop token的句子。
但是如果喜鹊就啥也没问,就发这么个半边的特殊token给LLaMA,LLaMA就给你自动补齐了问题....
然后我们试一下靠谱不,我先load LLaMA3-instruct,然后先做个左半边token的Template
下边魔术出现了,我的代码里一点和这个问题相关性的东西也没有,它就自己产生了,这句话了...
剩下的就是不断的抽卡,抽问题,然后把问题放在template prompts里要答案就完了,我这里就写一个
你要想要更多的问答对,就自己写循环不断的抽卡就完了。。。
论文里面它自己做的数据集来做对齐的效果哪可是比这些公开数据集做微调好的不是一点半点,有相当大的提升,当然我也没有时间去测试,姑妄听之。
我看qwen的token好像有点变了,我也懒得改脚本json了,所以就不测试了,大家有兴趣可以自己测试一下,Llama和mistral都是可以的,Mistral和Llama2 是[/inst]
好了,就讲解到这,祝大家抽卡成功,微调出来的模型更上一层楼,不管怎么说,你看完了这篇,肯定不白看,也算获得了一个牛B的合成数据的办法,或者叫extra原模型对齐数据的方法。