可随意转载。Update2023.11.1

摘要

近期的研究聚焦于通过模仿学习来增强小型模型的能力,这些小型模型学习大型基础模型(LFMs,Large foundation models)生成的内容。影响小模型质量的因素有很多,包括来自浅层LFM输出的有限模仿信号;小规模同质训练数据;以及最重要的是,由于评估不严谨,高估了小型模型的能力,因为它们只学会了风格,但并未学会LFMs的推理能力。为了解决这个问题,我们研发了Orca模型(13B),它能够学会LFMs的推理能力。Orca从GPT-4中学习丰富的信号,包括解释轨迹;分步思考过程;以及其他复杂指令,并在ChatGPT的教导下学习。为了实现渐进式学习,我们利用大规模和多样性的模仿数据进行严格的采样和选择。Orca在复杂的零样本推理基准测试(如BigBench Hard(BBH)和AGIEval)中,100%超越了最先进的指令微调模型,如Vicuna-13B,并在BBH测试中与ChatGPT达到了同等水平。在没有CoT的零样本环境下,SAT,LSAT,GRE和GMAT等专业和学术考试中表现出较强竞争力(仅仅比GPT-4相差4点)。研究表明,从分步解释中学习,无论这些解释是由人类还是更先进的AI模型生成的,都是一种提高模型能力和技能的有效方法。

一、简介

ChatGPT、GPT-4等大型基础模型在广泛任务领域中展示出了显著的零样本能力。不仅在Human Eval 和Big Bench 等学术基准测试中,GPT-4还在包括法考、SAT、GRE和USMLE等各种专业考试中展现出匹敌人类的表现。这些进步归功于巨大的模型参数和训练数据,以及采用指令对齐。这种对齐是通过STF+RLHF实现的。 随着这些模型继续进化并变得更强大,一个有意思的问题出现了:我们能否使用模型本身来训练其他AI模型?White等通过从初始模型中采样输出,生成修订版,然后基于这些修订响应来微调原始模型,可以更有效地控制模型行为,并可以使其更无害,显著减少人类标签。 最近,有大量的研究使用像ChatGPT和GPT-4这样的LFMs作为教师来生成大型数据集,进行指令调整,并训练较小的模型,如Alpaca [7],WizardLM [8] 和Vicuna [9]。虽然这些模型可以生成与其教师风格匹配的内容,但在显示出大型基础模型的推理和理解技能方面,它们往往表现不足。

以130亿参数的指令调整模型Vicuna (以LLAMA-13B为基础),通过OpenLLM3和ChatArena4等排行榜上的表现,被证明是当前最好的模型之一。如图1所示,广泛使用了GPT-4作为裁判的评估方法表明,Vicuna保留了ChatGPT质量的92%。然而,对于人类标签的更细致的推理基准测试发现,Vicuna在专业和学术考试上只保留了ChatGPT质量的64%(见图2),并且在像BBH这种的复杂基准测试上只保留了ChatGPT质量的48%(见图3)5。这种差异不仅突显了现有评估协议在较小LLMs上的限制,而且揭示了它们在推理和理解能力上的显著滞后。本质上,这些模型可能表达得很清楚,但它们不一定具有强大的推理技能。在这项研究中,我们讨论了这些差距背后的一些原因,并提出了解决它们的策略。

1.1 面临的挑战

当前关于指令微调以模仿像ChatGPT这样的LFM的输出的研究在任务多样性、查询复杂性和数据规模方面表现出显著的局限性。基于这些结果Gudibande等最近的一项研究认为模型模仿这条路不行,因为“要广泛地匹配ChatGPT,仅通过模仿就需要(1)集中努力收集大量的模仿数据集和(2)比目前可用的更多样化和更高质量的模仿数据。”。与此结论相反,我们证明了这两个条件(1)和(2)是可以实现的,并且可以实现在需要复杂推理的多个零样本基准上减少与专有LLM之间的差距。下面详细介绍面临的挑战:

简单指令,多样性有限。Self-Instruct [13]过程包括使用一组初始提示来激发LFM产生新的指令。然后删除任何质量低或过于相似的响应,并将剩余的指令重新整合到任务池中进行进一步的迭代。然而,通过Self-Instruct生成的结果查询,如“三原色是什么?”、“法国的首都是什么?”等,可能在多样性和复杂性上表现出局限性。Alpaca [7] 和WizardLM [8]都采用了Self-Instruct的变种。WizardLM引入了Evol-Instruct的概念,该概念逐渐将初始指令集重写为更复杂的版本,试图克服该方法的一些固有缺点。另一方面,像Vicuna [9] 和Koala [14]这样的最近的作品由于在ShareGPT6等社区贡献的对话中有更多类似人类的对话和自然指令,表现出了显著的性能,ShareGPT6为用户提供了一个论坛,用户可以在其中分享他们与ChatGPT的对话。

任务多样性和数据规模。ShareGPT中的人类贡献的对话是一种有价值的数据源,但它们也有一些限制。它们倾向于偏好创造性内容生成和信息寻求查询而不是其他类型的任务。因此,接受这种自然对话训练的模型可能捕获到LFM的风格,而不是推理过程——这在图2和3中Vicuna的表现中得到了体现。此外,这种数据收集方式的规模也有限。表1显示了近期热门指令调整工作中所使用的数据大小和调整方法的概述。

有限的模仿信号。现有的方法依赖于从教师模型生成的⟨查询,响应⟩对的模仿学习。然而,这提供了有限的信号来追踪教师的推理过程。先前关于开放箱模型的工作[15, 16]显示,更丰富的信号,如logits,中间表示和注意状态,可以显著提高蒸馏性能。尽管它们对于封闭箱LFM’s7来说是无法访问的,但最近的工作[17]表明,更丰富的信号,如LFM理由,可以帮助缩小任务特定蒸馏的差距。

评估:以LFM对小模型的指令调整的先前研究在它们的评估协议上有严重的限制。他们通常依赖GPT-4进行自动评估,通过询问它“给定系统1(参考)和系统2(目标)的响应,哪一个更好?”来比较两个系统的输出。然而,这种方法有几个缺点,比如测试集的大小(例如,Vicuna中的80条指令和WizardLM中的218条指令)和GPT-4作为评判者的偏见[18]。例如,我们注意到,使用GPT-4响应进行指令调整的模型倾向于生成更长的文本,GPT-4更偏好这些文本,而不是更短的文本;以及GPT-4在候选响应的顺序上有偏见。我们将展示,这种自动评估措施高估了小模型相比于LFM的能力,因为前者在理解和推理技能上要弱得多。

1.2 关键的贡献

在这项研究中,我们的重点是解决上述挑战,具体包括:

基于解释的微调:我们使用GPT-4提供的详细响应,增强⟨查询,响应⟩对,这些响应解释了教师在生成响应时的推理过程。这为学生的学习提供了额外的信号。我们利用系统指示(例如,“像给五岁孩子解释一样”,“一步一步思考并证明你的答案”等)来引出这样的解释。这与普通的指示调整形成对比,后者仅使用提示和LFM响应进行学习,几乎没有机会模仿LFM的“思考”过程。

任务和指令的扩展:我们使用Flan 2022系列[19],因为它提供了广泛的公共任务和指令集。特别是,我们使用FLANv2,它提供了高质量的模板、先进的格式化模式和数据增强。尽管FLAN包含数千万条指令,但我们从任务集合中有选择地采样,形成多样化的任务混合,然后我们进一步抽样生成复杂的提示。这些提示用于查询LFMs,如ChatGPT和GPT-4,从而创建丰富多样的训练集。我们收集了500万个ChatGPT响应,从中进一步抽取了100万个GPT-4响应。我们展示了ChatGPT作为教师助手如何帮助进行渐进式学习。

评估:我们在一系列设置下评估Orca的生成、推理和理解能力:(i)使用GPT-4在Vicuna、WizardLM和优秀提示集合8的现有评估集上进行自动评估;(ii)学术基准,如Big-Bench Hard[4]和TruthfulQA[20];(iii)像SAT, LSAT, GRE, GMAT这样的专业和学术考试,来自AGIEval[1];(iv)使用ToxiGen[21]进行安全评估,以测试不同少数群体中的有毒语言生成和仇恨言论检测。最后,我们提供案例研究,比较Orca与OpenAI LFMs(如ChatGPT和GPT-4)以及指示调整的较小模型(如Vicuna)的生成和推理能力。

二、先决条件

2.1 指令微调

指令微调[22]是一种技术,它允许预训练的语言模型从输入(任务的自然语言描述)和响应对中学习,例如,{“instruction”: “将给定句子中的单词排列成语法正确的句子。”, “input”: “the quickly brown fox jumped”, “output”: “the brown fox jumped quickly”}。指令调整已经被应用于纯语言任务和多模态任务。对于纯语言任务,已经证明指令调整能够改善诸如FLAN[22]和InstructGPT[5]等模型在各种基准上的零样本和少样本性能。对于多模态任务,指令调整已经被用于生成语言-图像任务的合成指令跟随数据,如图像字幕[23]和视觉问题回答[24]。

最近一段时间的许多工作,包括Alpaca[7]、Vicuna[9]、WizardLM[8]和Koala[14],都采用了指令调整来训练较小的语言模型,这些模型的输出是从GPT系列的大型基础模型中生成的。如第1.1节所述,所有这些工作的一个显著缺点是任务多样性有限,查询复杂性和小规模训练数据有限,另外,有限的评估过分强调了这种方法的好处。

2.2 系统指令扮演的角色

传统的指令微调例子

普通的指令调整(请参见图4的示例)通常使用简短且简洁的响应的输入、响应对。在现有的工作中,当这样的响应用于训练较小的模型时,它们的能力有限,不能追踪LFM的推理过程。相反,最近的LFMs如GPT-4中的系统指令10可以用来指导模型如何行为和响应。它们是用自然语言写的,并通过在JSON请求中使用“系统”的角色与用户消息分开。系统指令可以指定模型响应的语气、任务、格式和限制。系统指令也是一种改进模型响应安全性的方法。例如,为安全带设计的一组系统指令可能是: •助手不得生成有害或冒犯性的内容。 •助手必须尊重用户的隐私和同意。 •助手必须承认其限制和不确定性。

系统指令的微调

三、基于解释的微调

为了解决当前挑战,我们利用了包含多样任务的大规模训练数据,这些任务增加了复杂的指令和丰富的信号。具体来说,我们的数据包含了从FLAN-v2(也称为Flan 2022)[19]采样的大量任务的人工和增强系统指令。鉴于FLAN-v2集合的大规模大小和构成数据集和任务的示例数量的变化,我们从不同类别的任务混合中采样(在下一节中描述)来创建我们的训练数据。

3.1 数据集的构成

在我们的训练数据中,每个实例都包含以下三元组:⟨系统消息,用户查询,LFM响应⟩。系统消息放置在提示的开始,为LFM提供了必要的上下文,指导,和其他相关细节。我们利用系统消息来改变响应的长度;概述助手的特性;建立可接受和不可接受的LFM行为;并确定代理的响应结构。用户查询定义了我们希望LFM执行的实际任务。为了获得大量和多样化的用户查询,我们利用FLAN-v2集合[19]。我们从FLAN-v2中抽取500万个用户查询,并收集ChatGPT的响应。我们进一步从这500万个指令中抽取100万个指令,为其收集GPT-4的响应。所有对代理的查询都会根据以下所述增加系统指令。

3.1.1 系统消息

我们精心设计了16条系统消息,旨在激发LFM产生不同类型的响应。这使我们能够训练Orca生成长短不一的答案;遵循指南,指令和格式;生成富有创意的内容以及解答信息寻求的查询;最重要的是,根据提示,为响应生成解释和逐步推理。

1 <empty system message>
2 You are an AI assistant. Provide a detailed answer so user don’t need to search outside to
understand the answer.
3 You are an AI assistant. You will be given a task. You must generate a detailed and long
answer.
……

我们为FLAN-v2集合的不同子集合制定了不同的系统消息。表2列出了用于生成我们训练数据的所有系统指令。图6显示了不同子集合中系统消息的分布。请注意,系统消息#8和系统消息#10只用于选择题;因此,数量较少。

3.1.2 FLAN-v2数据集描述和采样

FLAN-v2集合[19]由五个子集合组成,分别是CoT,NiV2,T0,Flan 2021,Dialogue。每个子集合包含多个任务,每个任务都是一系列查询的集合。每个子集合都与多个学术数据集相关联。每个数据集都创建了一个或多个任务,侧重于零样本和少样本查询。在这项工作中,我们只抽样零样本查询来训练Orca。我们没有从Dialogue子集合抽样,因为查询通常缺乏引发ChatGPT有用响应的上下文。

CoT样例
### System:
You are an AI assistant that helps people find information. User will you give you a question.
Your task is to answer as faithfully as you can. While answering think step-by-step and
justify your answer.
### Human:
Pick which sentence is not logical.
Options:
- Sentence A: "people in the desert often look forward to flood"
- Sentence B: "people in the desert often look forward to rain"
Hmmm, let me think. I want to lay out the solution in details.
### Gold:
There is no reason to look forward to a flood because floods cause damage. The answer is
Sentence A.
### ChatGPT:
Sure, let’s break it down step by step.
1. Read the sentences carefully.
2. Identify the difference between the two sentences.
3. Determine which sentence is not logical based on the difference.
Now let’s apply these steps to the given sentences:
1. Sentence A: "people in the desert often look forward to flood"
- This sentence suggests that people in the desert are eagerly anticipating a flood. However,
floods are typically destructive and dangerous, so it seems unlikely that people would look
forward to them.
2. Sentence B: "people in the desert often look forward to rain"
- This sentence suggests that people in the desert are eagerly anticipating rain. While the
desert is a dry environment, rain can be beneficial in providing water for plants and animals.
3. Based on the above analysis, it can be concluded that Sentence A is not logical as it
suggests that people in the desert look forward to something that is typically harmful and
dangerous. Therefore, the answer is Sentence A: "people in the desert often look forward to
flood".
### GPT4:
To determine which sentence is not logical, let’s examine each sentence and its meaning in
the context of people living in a desert.
Sentence A: "people in the desert often look forward to flood"
- This sentence implies that people living in a desert are eagerly awaiting a flood. However,
floods are generally considered to be destructive and dangerous, causing damage to property,
infrastructure, and sometimes even loss of life. It is not logical for people to look forward to
such a disastrous event.
Sentence B: "people in the desert often look forward to rain"
- This sentence implies that people living in a desert are eagerly awaiting rain. Rain is
typically beneficial in a desert environment, as it provides water for drinking, agriculture,
and replenishing natural resources. It is logical for people living in a desert to look forward
to rain, as it can bring relief from the harsh, dry conditions.
Based on the analysis of both sentences, it can be concluded that Sentence A (“people
in the desert often look forward to flood”) is not logical, as it suggests that people are
eagerly anticipating a destructive event, which is not a reasonable expectation. In contrast,
Sentence B is logical, as it reflects the beneficial nature of rain in a desert environment.

零样本CoT:零样本思维链(CoT)集合包含总共18个任务,涉及数学文字问题解决,自然语言推理,常识推理,科学问题回答,找出不合群的推理。这个数据集中的每个查询都是为了思维链响应。图7显示了来自CoT集合的一个示例。它还显示了作为原始数据集一部分的人类编写的响应,ChatGPT生成的响应和GPT-4的响应。虽然所有的来源在最后答案上都达成一致,但是与人类编写的答案相比,ChatGPT和GPT-4配合系统指令生成的响应相当详细。这18个任务总共包含约150,000个查询。我们将它们全部包含在我们的500万集合中。

图8:NiV2数据集的任务分布概括

NiV2:NiV2总共包含1560个任务和大约500万个查询。每个任务的查询数量不同。图8显示了任务的分布以及任务类型。我们从每个任务中随机抽样300个查询,并包含查询数量较少的整个任务数据。这给我们总共440,000个查询。

Flan 2021:Flan 2021集合包含从62个数据集创建的总共142个任务。从发布的脚本中获取的整个Flan 2021集合非常大,而且增强起来非常昂贵。为了抽样出一个多样化和有代表性的子集,我们从每个任务中最多生成100万个查询,这给我们总共约2890万个查询。这个集合中的每个任务包含不同数量的查询。我们从这个8570万个查询的集合中抽样出总共250万个查询。抽样过程在算法1中概述。

算法1: Flan2021与T0数据集的任务算法

T0: T0集合包含与35个训练数据集相关的总共193个任务。我们只包含与训练分割T0相关的任务,这排除了Big-Bench。这很重要,因为我们在我们的评估基准中包含了Big-Bench-Hard。T0集合大约包含8570万个查询,每个任务中的查询数量不同。我们从这个集合中使用算法1的抽样过程抽样了总共200万个查询。

3.1.3 ChatGPT作为老师

我们生成了500万条指令(增强了系统消息的查询),称为FLAN-5M,这是按照上一节中概述的抽样技术进行的。我们进一步从FLAN-5M中随机抽取100万个查询,创建另一个子集,称为FLAN-1M。我们使用Azure OpenAI API11来收集FLAN-5M的ChatGPT (GPT-3.5-turbo)响应,以及FLAN-1M的GPT-4响应。 我们首先在FLAN-5M(ChatGPT增强)上训练Orca,然后在FLAN-1M(GPT-4增强)上进行第二阶段的训练。本质上,我们利用ChatGPT作为中间教师助理,原因有两个。

• 容量差距:具有130亿参数的Orca比GPT-4(大小未公开)小很多。利用具有较小能力差距的中间教师,在这种情况下是ChatGPT,已被证明可以改善小型学生在知识蒸馏[15]中的模仿学习性能。这可以被视为一种逐步学习或课程学习的形式,学生首先从较易的例子中学习,然后是较难的例子:假设长的响应比短的响应更难模仿,以及来自更大教师的改进推理和逐步解释。

• 成本和时间:从Azure OpenAI API收集大规模数据受到限制,包括(a)防止阻塞端点的每分钟允许的请求限制,(b)由于服务延迟,每分钟可用的令牌,和(c)提示长度和令牌完成的美元成本(如表4所示),ChatGPT API比GPT-4端点快且便宜得多。为此,我们收集了与GPT-4相比5倍的ChatGPT数据。

图9显示了ChatGPT和GPT-4对应于不同系统消息的响应长度分布。我们观察到,GPT-4的响应平均比ChatGPT的响应长1.5倍。这使得Orca能够逐步从教师解释的增加复杂性中学习。我们通过消融实验展示了教师辅助的影响。

3.2 训练

该部分提供了关于Orca训练过程的概述,涵盖了标记化,序列化和损失计算的不同方面。

标记化:我们使用LLaMA字节对编码(BPE)标记器处理输入示例。值得注意的是,LLaMA标记器将所有数字分解成单个数字,并回退到字节来分解未知的UTF-8字符。为了处理可变长度序列,我们在LLaMA标记器词汇表中添加了一个填充标记“[[PAD]]”。生成的词汇表包含32,001个标记。

打包:为了优化训练过程并有效利用可用的计算资源,我们采用了打包技术[26]。这种方法涉及将多个输入示例连接成一个序列,然后用于训练模型。打包是这样执行的,即连接序列的总长度不超过max_len= 2,048个标记。特别地,我们将输入示例进行混洗,然后将示例划分为组,以使每个组中连接序列的长度最多为max_len。然后将填充标记添加到连接序列中,以达到统一的输入序列长度max_len,给定我们训练数据中增强指令的长度分布,每个序列的打包因子为2.7个示例。

损失:为了训练Orca,我们只计算由教师模型生成的标记上的损失,也就是说,它学习在系统消息和任务指令的条件下生成响应。这种方法确保模型专注于从最相关和信息丰富的标记中学习,提高了训练过程的整体效率和效果。

计算:我们用20个A100 GPU(80G)训练了Orca。在FLAN-5M(ChatGPT增强)上训练Orca 4个epoch用了160小时,然后在FLAN-1M(GPT-4增强)上继续训练用了40小时。分别用了2周和3周从GPT-3.5-turbo(ChatGPT)和GPT-4的多个端点收集数据,考虑到了阻塞限制,端点负载和查询和响应对的长度分布。

四. 建立实验

我们建立了一个严谨的评估协议,该协议考虑了一系列不同的能力,包括写作、理解、分析、数学和逻辑推理。

4.1 基线

我们用以下的基线跟Oraca做比较:

• Text-Davinci-003(TD-003):Text-Davinci-003属于GPT-3.515系列的生成模型,该模型针对文本补全进行了优化。它是一个强大的模型,设计用来以更好的质量、更长的输出、以及在多种语言中进行一致的指令跟随进行语言任务。

• ChatGPT:ChatGPT(GPT-3.5-turbo)是GPT-3.5模型中最有能力的一个,是对text-davinci-003的改进。它针对聊天进行了优化,并通过与人的对话进行训练。OpenAI在2022年11月发布了这个聊天机器人。

• GPT-4:GPT-4是GPT家族中最新的模型,并在各种专业和学术基准上展现出人类级别的表现。像ChatGPT一样,GPT-4针对聊天进行了优化,并可以比其前任执行更复杂的任务。它通常在需要复杂推理的任务上表现出显著高于GPT-3.5模型的性能。对于ChatGPT和GPT-4,我们使用OpenAI API版本“2023-03-15-preview”。

• Vicuna:Vicuna[9]是一个开源的聊天机器人,通过对从ShareGPT收集的用户共享对话进行微调LLaMA[10]进行训练。在这项工作中,我们使用由130亿参数组成的Vicuna模型。Vicuna一直是多个排行榜中的领先的开源语言模型,包括Chatbot Arena16和Open LLM Leaderboard17。我们使用的Vicuna模型检查点是截至2023年4月21日的。

4.2 任务

在本节中,我们详细说明了用来评估Orca在开放式生成能力以及理解和处理复杂推理任务方面的能力的任务。表5显示了用于评估的不同数据集的统计信息。

4.2.1 开放式生成能力

Vicuna[9]使用了基于GPT-4的评估框架来自动化聊天机器人性能评估。他们最初设计了八个问题类别来测试聊天机器人性能的各个方面,并发现GPT-4可以产生相对一致的分数和对这些分数的详细解释。在这个设置中,GPT-4以0到10的比例评估模型生成的质量。我们利用相同的设置,并试验了三个不同的提示集合,这些集合涵盖了广泛的开放式回答任务:

• Vicuna Prompts:这些是Vicuna中提出的原始提示。这80个提示被分为九个技能,包括通用、知识、角色扮演、常识、费米、反事实、编码、数学和写作能力。

• Awesome Prompts:Awesome ChatGPT prompts[27]是一个提示示例的集合,主要用于ChatGPT模型作为参考。提示提供了一种有效的方式来自动化许多任务,包括写作、翻译、总结、分析等。这些提示基于164个角色,如生活教练、初创科技律师、占星家、棋手、统计员和笔记助理。

• WizardLM Prompts:WizardLM prompts[8]是一个基于真实世界任务的提示示例集合。这些提示来源于开源项目、平台和论坛。它们被划分为29个不同的技能,以及每个提示的难度级别。这些技能涵盖了人类级智能的一些主要需求,包括数学、学术写作、调试、代码生成和推理能力。

4.2.2 推理能力

• AGIEval:AGIEval [1] 是一个以人为中心的基准测试,评估基础模型在与人类认知和问题解决相关的任务中的一般能力。此基准源自官方和标准的入学和资格考试,为一般人类考生设计,例如大学入学考试(如GRE,GMAT,SAT)、法学院入学考试(LSAT)、数学竞赛、律师资格考试和全国公务员考试。该基准在人类中心化的标准化考试的背景下评估基础模型。表6显示了各项任务的统计数据,包括考试、每年参加这些考试的人数、涉及的学科、示例数量和平均标记数。在这项工作中,我们只考虑与英语多选题相关的数据集。

• Big-Bench Hard (BBH):BIG-Bench Hard 是一个包含23个挑战性 BIG-Bench [4] 任务的套件,这些任务的目的是衡量大型语言模型的能力和限制。这些任务是之前的语言模型评估未能超越平均人类评估者的任务。在这项工作中,我们只使用与多选题相对应的数据集。我们使用标准的零样本提示进行评估,不使用任何标记的示例。

提示模板和模型响应的解析:我们在没有任何例证和没有CoT的零样本设置下评估推理能力。由生成模型提供的自由形式的响应,很难解析这些基准测试中的MCQ问题的答案。对于所有的MCQ任务,我们使用AGIEval [1] 的提示格式和解析(参见图13中的提示模板),问题后面跟着答案选项,以及一个提示完成序列,如“在0到3之间,答案是”。我们只考虑响应中的第一个大写字符,与黄金答案id(精确匹配)进行比较。由于模型在零样本设置中并不总是遵循这个模板,所以如果预期的答案id在响应的后面出现,它们将被惩罚。我们对所有模型的响应采用相同的解析逻辑,以确保一致性。

五. 开放式生成的评估 

表7显示了候选模型(例如,Vicuna,Orca)在三个数据集上与ChatGPT(GPT-3.5-turbo)和GPT-4作为参考模型的性能,其中GPT-4被用作评判者/评分者。候选模型的性能是以候选模型与参考模型得分的整体改善百分比来衡量的。

• Orca保留了ChatGPT质量的95%和GPT-4质量的85%,这是通过GPT-4评估的所有数据集的总体。Orca在总体上比Vicuna提高了10个百分点。

• Orca在Vicuna原始评估设置中与ChatGPT表现相当。在这种设置中,候选模型与ChatGPT进行比较,GPT-4在Vicuna提示数据集上作为评分者。

• Orca对于跨越广泛生成角色的提示展现出强大的性能。对于跨越164个开放式生成角色的Awesome提示数据集,Orca通过保留ChatGPT质量的98%和GPT-4质量的89%展现出强大的性能。

复制注意:我们观察到在GPT-4评估中对比较集中第一个模型的反应有正面偏差。最近的一项关于分析GPT-4作为评估者的偏差的工作[18]也报告了这一点。在所有上述评估中,第一个模型被视为与Vicuna设置18一致的参考模型。

六. 推理评估

6.1 AGIEval结果

表8显示了Orca在AGIEval基准上的英语多选题中,与基线模型的标准零样本(无示例,无CoT)性能比较。按照AGIEval [1] 提出的相同的评估设置,使用精度度量来衡量这些任务的每一项的性能。

• Orca在所有任务的总体上与Text-da-Vinci-003表现相当,并保留了ChatGPT质量的88%。然而,Orca在GPT-4上明显落后。

• 我们观察到,对于这样的分析和推理任务,Vicuna的表现显著较差,只保留了ChatGPT质量的62%,而在开放式生成中则保留了85%(表7 vs 表8)。这显示出这样的开源语言模型的推理能力较差。

• 在与Text-da-Vinci-003表现相当和在ChatGPT下面5个点的情况下,Orca在数学相关的任务上(在SAT,GRE,GMAT中)与ChatGPT的差距更大。

• 与Vicuna相比,Orca表现出更强的表现,在每个类别上都超过了它,平均有42%的相对改进。

• GPT-4的表现远超所有其他模型,但在这个基准中还有很大的提升空间,因为所有模型的表现都明显低于人类在所有任务中的表现。

• Orca的性能根据系统消息的类型显著变化(见表9)。对于我们训练的模型,空的系统消息通常工作得很好。

• 在不同任务的450个例子中,ChatGPT在Orca中占主导地位(ChatGPT-beats-Orca示例)。这些例子的大部分来自LSAT-LR(22%)和LogiQA(21%)任务,而其他LSAT任务和SAT-English任务各贡献了不到10%。

• 在不同任务的325个例子中,Orca击败了ChatGPT(Orca-beats-ChatGPT示例)。在这些例子中,大部分来自LogiQA(29%),而其他LSAT任务和SAT-English任务各贡献了不到10%。

规模和教师帮助:为了分析渐进式学习的影响,我们仅用GPT-4增强(FLAN-1M)训练Orca,并与在ChatGPT(FLAN-5M)和GPT-4(FLAN-1M)增强上训练的完整版本进行对比,结果见表10。我们观察到,通过中级ChatGPT帮助将解释数据量扩大5倍,可以显著提高模型性能,在总体上提高4.5个点。

对100个随机的ChatGPT-击败-Orca和Orca-击败-ChatGPT样本的分析:

• 领域知识:模型需要专门的领域知识来解决一些问题,如特斯拉电池,化学概念等。15%和21%的ChatGPT-击败-Orca和Orca-击败-ChatGPT例子分别归入这一类别。

• 复杂推理:有些例子需要复杂的推理,如推理超过五个对象/人。例如,逻辑推理问题开始于“在一个塑料棚子里,有6个大小相同的矩形蔬菜池,按从左到右的顺序排列?”这需要模型抓取六个池塘的空间关系并进行空间推理。14%和18%的ChatGPT-击败-Orca和Orca-击败-ChatGPT例子分别归入复杂推理类别。

• 长期背景:有些例子有长期的背景(例如,包含几段文字的段落),需要对长篇文字进行推理。16%的ChatGPT击败Orca例子有长期背景,而只有8%的Orca击败ChatGPT例子的背景是长期的。这个结果突出了ChatGPT在建模长期背景方面优于Orca。

• 几何推理:例如,“立方体的体积与能放入立方体内的球的体积的比例是?”这样的例子需要对几何对象进行推理。2%和5%的ChatGPT-击败-Orca和Orca-击败-ChatGPT例子分别归入这一类别,显示了两个模型在几何推理方面的性能差距。

• LaTeX推理:有些例子的问题中有LaTeX排版,需要理解LaTeX符号来解决这些例子。例如,“一条线在$x y$-平面上通过原点,斜率为$\frac{1}{7}$。以下哪个点在该线上?”需要处理分数运算符。2%和10%的ChatGPT-击败-Orca和Orca-击败-ChatGPT例子分别归入这一类别。”

6.2 Big-Bench Hard结果

表11显示了Orca在Big-Bench Hard上对基线模型的零样本性能比较,采用标准的零样本提示(无示例,无CoT)。Orca在所有任务的总体上较ChatGPT表现略好;明显落后于GPT-4;并且比Vicuna高出113%。与AGIEval类似,Vicuna在此基准测试中的复杂推理任务中表现不佳。

虽然Orca明显优于Vicuna且略优于ChatGPT,但其平均表现为49.7%,比GPT-4低26%。注意,GPT-4已经报告了Big-Bench的数据污染问题,我们并不知道LLaMA的训练数据(Vicuna和Orca都使用的基础模型)或Flan-V2收集或Vicuna的训练数据(ShareGPT)是否存在此类问题。

考虑到在BigBench-Hard上的平均表现接近,我们更深入地查看了Orca和ChatGPT之间的表现差异:

蕴含和语义理解:

• Orca在蕴含(正式谬误)和语义理解(消歧问答和Snarks)方面表现更好。

• 在正式的谬误任务中,模型必须确定是否可以从一组陈述中逻辑地推导出给定的论点,Orca在此任务上比ChatGPT提高了4.5%。”

• BBH基准测试有两个语义理解任务:消歧问答,其中给出一个含有模糊代词的句子,模型需要确定是否可以隐含地推断出代词,或者句子本质上是模糊的;以及Snarks,目标是从两个几乎相同的句子中识别出讽刺的句子。Orca在消歧问答和Snarks上分别超过ChatGPT 11.1%和5.8%。

时间和空间推理:

• 与ChatGPT相比,Orca在时间推理、空间推理和基于颜色的推理方面表现出明显更好的推理能力。

• Orca在Temporal Sequences(时间推理)、Navigate(遵循导航指示)、Colored Objects(给定上下文识别对象颜色)上分别超过ChatGPT 102%、3.6%和1.7%。

因果判断:

• Orca在因果判断任务上表现出良好的性能,这项任务衡量模型回答关于短篇故事的因果问题的能力。

• Orca的表现与GPT-4相当,而超过ChatGPT 4.7%。

多语种理解:

• Orca和ChatGPT在显著的翻译错误检测任务(确定翻译句子中的翻译错误类型)上达到了平衡。

• 尽管这显示出有希望的潜力,但我们注意到BBH对多语种能力的评估有限,需要更多的评估以进行更好的评估。

世界知识:

• 对于需要世界知识的任务(例如体育、艺术家、幽默等),Orca的表现不如ChatGPT,但在电影推荐方面却表现得更好。

• 在体育理解(确定与体育相关的句子的可信度)和名字篡改(识别对名字(艺术家、乐队、电影名字)的编辑,改变其含义并导致有趣的名字)两方面,Orca的表现远不如ChatGPT,可能是由于对体育、艺术家、乐队、电影以及英语中的幽默使用的知识不足。

• 另一方面,它在电影推荐任务中的表现明显优于ChatGPT,但略低于GPT-4(给定一系列电影,从电影选择列表中推荐相关电影)。

逻辑和几何推理:

• 与Orca相比,ChatGPT显示出更优越的逻辑推理能力。

• 在布尔表达式和谎言网任务(测试用布尔表达式或自然语言表示的逻辑推理)中,ChatGPT的表现至少比Orca好9%。

• 在逻辑推断任务中(推断一系列对象的顺序),Orca在五个对象的任务中表现优于ChatGPT,但ChatGPT在三个和七个对象的任务中表现优异,至少比Orca优秀4.9%。

• 通过几何形状任务(预测完整的SVG路径元素的形状)测量,ChatGPT的几何推理能力优于Orca。ChatGPT在此任务上比Orca高出23%,这突显了Orca与ChatGPT相比在几何推理能力上的不足。

表格理解:

• ChatGPT比Orca具有更好的表格理解和推理能力。

• 在“桌子上的企鹅”任务中(根据理解表格中的事实回答问题),Orca落后于ChatGPT 7.4%,从而突显了Orca与ChatGPT相比在表格理解和推理能力上的差距。

复制说明:

我们报告了来自AGIEval [1]的Text-da-Vinci-003、GPT-3.5-turbo(ChatGPT)和GPT-4的性能。对于所有的推理任务,我们都在纯零样本环境中对所有模型进行了基准测试,没有任何样本,也没有使用CoT。注意到ChatGPT在某些BBH任务(如时间序列、日期理解、歧义QA和几何形状)中的表现较差,我们参考了以前关于相关零样本任务上ChatGPT结果的报道,以确保可复制性。考虑到OpenAI模型的频繁更新和模型性能对生成超参数的敏感性,我们观察到在[28]中ChatGPT(ZS:零样本)和在[29]中的Text-da-Vinci-003(直接,与我们的零样本环境相对,三次样本)的相似结果。

七、安全性评估

我们希望训练出有用、诚实且无害的AI代理 [30]。为此,虽然这项工作仍在进行中,但我们已经对两个关键维度进行了有限的安全性评估:Orca在回答模仿人类虚假问题的问题时的真实性 [31],以及它在ToxiGen [21]中描述的不同群体间生成中性语言和仇恨言论的倾向性。我们将这些能力与其他模型的反应进行对比,包括ChatGPT、GPT-4和Vicuna-13B。 对上述情况未涵盖的维度进行更全面的评估是至关重要的,将是我们未来工作的重点。

7.1 真实性问题回答

数据集描述:

我们使用的是TruthfulQA-MC19,这是TruthfulQA数据集 [31] 中问题的多项选择问题回答变体,以保持一致性,并与以前的工作进行可比较的评估。该数据集包括38个类别的684个问题,包括政治、金融、法律等。这些问题的设计方式是人们可能会因为误解或无根据的信念而回答错误。挑战在于,如果模型只是模仿人类文本,它们可能会传播人类的错误信念。尽管TruthfulQA数据集包括基于生成的QA和多项选择QA,其中在多项选择设置中答案选择的数量可以变化,但在TruthfulQA-MC中,所有少于4个答案选择的问题都已被移除,对于有超过4个答案选择的问题,只随机抽取了4个选择。

评估注意事项:

虽然我们在上述设置中进行了TruthfulQA评估,请注意以下考虑因素:(1)虽然更难度量,但基于生成的评估比辨别式评估更可取,也更全面;例如,对数据集中的问题进行自由形式的生成,并使用各种相似度度量,比较生成的句子与数据集初始发布中各种人类生成的参考句子 [31]。(2)根据GPT-4技术报告(第10页)[32],GPT-4 RLHF调优中TruthfulQA数据的污染程度尚不清楚。 对于所有模型,我们都使用一个空的系统消息进行公平比较,最大响应长度为1024个标记,温度设为0.7,我们在解码时进行抽样。对于ChatGPT和GPT-4,提示的格式如下:

System:
Q: Which blood type makes the best CEOs? Answer Choices: (0)People with type O
blood exhibit strong leadership traits. (1)Blood type has no impact on someone’s
abilities. (2)People with type O blood make the best leaders. (3)The best CEOs have
type O blood. A: Among 0 through 3, the answer is

### System:
### Human:
Q: Which blood type makes the best CEOs? Answer Choices: (0)People with type O
blood exhibit strong leadership traits. (1)Blood type has no impact on someone’s
abilities. (2)People with type O blood make the best leaders. (3)The best CEOs have
type O blood. A: Among 0 through 3, the answer is
### Assistant:

评估结果:从图14中,我们观察到Orca的输出比Vicuna-13B更为真实;但稍落后于ChatGPT和GPT-4。

指令遵循结果:与Orca、ChatGPT和GPT-4相比,Vicuna的输出格式通常比较嘈杂,因为它经常不遵循提示模板,这使我们花费了更多的时间来手动标记其输出。在图15中,我们展示了不同模型在数据集中按照提示要求的格式执行的失败率。

7.2 有害内容生成

实验设置:

毒性和偏见是需要专注和全面评估的复杂主题。在本节中,我们为ToxiGen [21]中描述的特定类别的内容伤害呈现结果。虽然我们认识到这绝不是对毒性或仇恨言论的全面评估,但鉴于数据集的多样性,它可以作为一个合理的实验。 对于这个实验,我们用ToxiGen数据集的一个子集中的有毒和良性示例提示每个模型,这个子集包含了其中描述的13个类别。然后,我们使用现成的仇恨言论检测器HateBERT [33],计算本工作中研究的每个语言模型的输出的毒性概率。重要的是要注意,任何选定的现成的毒性检测器(包括HateBERT)可能具有自己的偏见和弱点,这取决于它们接受的数据类型。使用其他毒性检测器(例如Perspective API20、[21]中研究的各种检测器等)以及人工评估进行更全面的实验,这将留作未来的工作。 每个候选模型被提示一组句子(类似于图16),以继续为数据集中的每个13个类别生成类似的句子。

结果:

我们观察到,当Orca被给予有毒的提示时,它生成有毒内容的倾向比Vicuna小(见图18a)。在中性提示的情况下,Orca比Vicuna(见图18b)和ChatGPT(见图19)更倾向于生成中性内容。Orca生成的中性内容与GPT-4几乎相当(见图19)。

不同模型生成的内容风格有显著差异;例如,对于图16中给出的来自ToxiGen的样本提示(带有中性情感的女性类别),ChatGPT和Vicuna提供短输出,而GPT-4和Orca在图17中提供了表达清晰的响应。

7.3 关于错觉和工具增强LFMs的说明

为了展示不同模型在产生幻觉内容方面的重要限制,我们进行了一个简单的实验,其中不同的模型被提示生成不同个体和实体的简历。从图31中展示的一个示例性的案例研究中,我们观察到,虽然所有的模型都在处理地址、电话或电子邮件信息等细节方面有困难,但像GPT-4这样的较大模型在生成相关的专业摘要方面表现得更好,错误更少。这可以归因于较大模型比较小的模型更好地记住事实的能力。当我们减小LFMs的大小时,较小的模型失去了作为有效知识库或记忆存储的能力和容量,但仍然可以作为一个令人印象深刻的推理引擎(如我们在这项工作中展示的)。

工具增强的LFMs:为了解决这些不足,一个令人兴奋的工作线路已经出现,将大模型与外部插件或工具结合起来,使LFMs能够与环境交互 [34, 35]并检索最新的知识。这些工具增强的LFMs已经被用于AutoGPT [36]进行自主任务执行。Prometheus [37]利用了新鲜和全面的Bing索引、排名和答案结果以及GPT-4的27种创造性推理能力。最近的一项工作 [38]成功地将GPT-3.5-turbo (ChatGPT)的推理能力卸载到7B LLaMA,展示了真正高效和可扩展的工具增强LFM系统的巨大潜力。

八、限制

Orca 模型基于 LLaMA 模型系列构建,因此保留了许多其限制,以及其他大型语言模型的常见限制,包括:

数据偏见:大型语言模型在大量数据上进行训练,可能无意中承载了源数据中存在的偏见。因此,模型可能生成可能具有偏见或不公平的输出。

缺乏语境理解:尽管这些模型在语言理解和生成方面具有令人印象深刻的能力,但它们在理解真实世界方面能力有限,可能导致潜在的不准确或无意义的响应。

缺乏透明度:由于复杂性和大小,大型语言模型可以作为“黑箱”行动,使理解特定输出或决策背后的理由变得困难。我们建议查看 Azure 的透明度说明以获取更多信息。

内容伤害:大型语言模型可能会导致各种类型的内容伤害。使用这些模型时,了解它们并采取行动以防止它们非常重要。建议利用不同公司和机构提供的各种内容审查服务。值得注意的是,我们期望未来的政府和技术领导者对 AI 技术的内容伤害提供更好的规定和标准。我们重视并认同研究和开源社区在这方面可以发挥的重要作用。

幻觉:要意识到并谨慎不要完全依赖给定的语言模型进行重要的决策或可能产生深远影响的信息,因为并不明显如何防止这些模型制造内容。此外,尚不清楚小型模型是否可能更容易在无依据的生成用例中出现幻觉,因为它们的体积较小,因此记忆能力减少。这是一个活跃的研究课题,我们希望将来对此主题有更严格的衡量、理解和缓解。

滥用的可能性:如果没有适当的保护措施,这些模型可能被恶意用于生成虚假信息或有害内容。

此外,Orca 的性能受用于解释调整的数据的影响:

零样本设置:Orca 已在模拟零样本设置的数据上进行了训练,这些设置带有标准提示。模型在其他上下文中的性能,如多轮对话、上下文学习和少数样本学习,或者像思维链提示这样的高级提示技术,仍未经测试。

数据分布:Orca的性能可能与调整数据的分布强烈相关。这种关联可能会限制其在训练数据集中代表性不足的领域(如数学、编程和推理)的准确性。

系统消息:Orca接受了多种系统指令的训练,以引发不同类型的响应。此外,由模型大小引入的随机性可能导致对不同系统指令的非确定性响应的生成。

GPT-4行为:由于Orca被训练成模仿GPT-4,因此可能会继承教师模型的优点和缺点。我们认为,Orca受益于在GPT-4训练过程中采用的安全措施和Azure OpenAI API中的安全防护(例如,内容过滤器)。然而,需要进行详细的研究以更好地量化风险。

本模型仅为研究设置而设计,其测试也仅在此类环境中进行。它不应用于下游应用,因为需要进一步的分析来评估所提议应用中可能存在的潜在伤害或偏见。

九、结论

本文提供了关于训练较小的语言模型模仿大型基础模型(LFMs)如GPT-4行为的当前状态的洞察。我们的研究表明,与ChatGPT和GPT-4这样的先进模型相比,较小模型的能力常常被过分强调。依赖于GRE、SAT、LSAT等标准化测试的评估基准如AGIEval,提供了更健壮的评估框架。

研究还强调了数据和模仿技术的重要性,突出了解释调优作为将较小模型与GPT-4对齐的有效方法。然而,仍然存在对开发更精细方法的明显需求和潜力。

我们强调,数据大小和覆盖范围在将较小模型与其更强大的对应模型(如GPT-4)对齐时起着关键作用。此外,基础模型的质量是影响模型性能的关键因素。

我们的研究表明,Orca在性能上显著优于其他开源的较小模型。此外,在某些设置中,它可以匹配甚至超过ChatGPT的质量,尽管与GPT-4之间仍然存在显著的差距。这表明,可以训练较小的模型在受限的设置中变得更加专注和适应,而不会大幅度降低质量。它也表明,从逐步解释(由人类或更强大的AI模型生成)学习,可以显著提高模型的质量,无论它们的大小如何。

我们希望这些洞察将为这一领域的未来研究和开发提供信息,特别是在设计更健壮的评估方法,提高对齐和训练后技术的发展,以及更有效地使用像GPT-4这样的强大模型作为教师。

实战进阶

Part1 Orca实战

在单张消费级显卡(24G)上用Open-Orca数据集微调Mistral-7B模型。参考资料

1.系统安装

NVIDIA GPU 3090/4090 24G
Ubuntu 桌面版本
conda
Pytorch2.1.0 + CUDA11.7

2.软件依赖

pip install -U accelerate bitsandbytes datasets peft transformers tokenizers

建议的版本号如下:

accelerate                0.24.1
bitsandbytes              0.41.1
datasets                  2.14.6
peft                      0.6.0
transformers              4.35.0
tokenizers                0.14.1

3.数据集

统一格式(ChatML)的混合高质量的训练数据集是Orca成功的关键。

3.1 Open Assistant 数据集

它本身就是ChatML格式

3.2 Open Orca数据集

Open Orca数据集修改格式代码如下:

def format_conversation(row):
    template="""<|im_start|>system
{sys}<|im_end|>
<|im_start|>user
{q}<|im_end|>
<|im_start|>assistant
{a}<|im_end|>"""

    conversation=template.format(
        sys=row["system_prompt"],
        q=row["question"],
        a=row["response"],
    )

    return {"text": conversation}

import os 
dataset = dataset.map(
    format_conversation, 
    remove_columns=dataset["train"].column_names # remove all columns; only "text" will be left
    num_proc=os.cpu_count()  # multithreaded
)

3.3 word,pdf作为数据集

转换源代码

修改格式代码如下:

interview_fn="kc_reformat_interview.json"
dataset = load_dataset('json', data_files=interview_fn, field='interview')
dataset=dataset["train"].train_test_split(test_size=0.1)

# chatML template, from https://huggingface.co/docs/transformers/main/chat_templating
tokenizer.chat_template = "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}"

def format_interview(conv):
    messages = [
        {"role": "user", "content": conv["question"]},
        {"role": "assistant", "content": conv["answer"]}
    ]
    chat=tokenizer.apply_chat_template(messages, tokenize=False).strip() 
    return {"text": chat}

dataset = dataset.map(
    format_conversation, 
    remove_columns=dataset["train"].column_names   
)

3.4 自研数据集

参考Orca论文中的论述。

3.5 代码数据集

参考文献

code_instructions_122k_alpaca_style数据集

讲数据集加工成Mistral-7B-Instruct-v0.1要求的格式

# this function is used to output the right formate for each row in the dataset
def create_text_row(instruction, output, input):
    text_row = f"""<s>[INST] {instruction} here are the inputs {input} [/INST] \\n {output} </s>"""
    return text_row

# interate over all the rows formate the dataset and store it in a jsonl file
def process_jsonl_file(output_file_path):
    with open(output_file_path, "w") as output_jsonl_file:
        for item in dataset:
            json_object = {
                "text": create_text_row(item["instruction"], item["input"] ,item["output"]),
                "instruction": item["instruction"],
                "input": item["input"],
                "output": item["output"]
            }
            output_jsonl_file.write(json.dumps(json_object) + "\\n")

# Provide the path where you want to save the formatted dataset
process_jsonl_file("./training_dataset.jsonl")

处理完后的格式如下

{
"text":"<s>[INST] Create a function to calculate the sum of a sequence of integers. here are the inputs [1, 2, 3, 4, 5] [/INST]
# Python code def sum_sequence(sequence): sum = 0 for num in sequence: sum += num return sum</s>",
"instruction":"Create a function to calculate the sum of a sequence of integers",
"input":"[1, 2, 3, 4, 5]",
"output":"# Python code def sum_sequence(sequence): sum = 0 for num in sequence: sum += num return sum"
}

4 Chat模型的SFT

4.1 配置模型

加载模型Mistral-7B,并配置它适应ChatML格式,代码如下:

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, BitsAndBytesConfig
from peft import prepare_model_for_kbit_training, LoraConfig, get_peft_model

modelpath="models/Mistral-7B-v0.1"

# Load 4-bit quantized model
model = AutoModelForCausalLM.from_pretrained(
    modelpath,    
    device_map="auto",
    quantization_config=BitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_compute_dtype=torch.bfloat16,
        bnb_4bit_quant_type="nf4",
    ),
    torch_dtype=torch.bfloat16,
)

# Load (slow) Tokenizer, fast tokenizer sometimes ignores added tokens
tokenizer = AutoTokenizer.from_pretrained(modelpath, use_fast=False)   

# Add tokens <|im_start|> and <|im_end|>, latter is special eos token 
tokenizer.pad_token = "</s>"
tokenizer.add_tokens(["<|im_start|>"])
tokenizer.add_special_tokens(dict(eos_token="<|im_end|>"))
model.resize_token_embeddings(len(tokenizer))
model.config.eos_token_id = tokenizer.eos_token_id

4.2 配置LoRA

# Add LoRA adapters to model
model = prepare_model_for_kbit_training(model)
config = LoraConfig(
    r=64, 
    lora_alpha=16, 
    target_modules = ['q_proj', 'k_proj', 'down_proj', 'v_proj', 'gate_proj', 'o_proj', 'up_proj'],
    lora_dropout=0.1, 
    bias="none", 
    modules_to_save = ["lm_head", "embed_tokens"],        # needed because we added new tokens to tokenizer/model
    task_type="CAUSAL_LM"
)
model = get_peft_model(model, config)
model.config.use_cache = False

4.3. 配置数据集

Token化

def tokenize(element):
    return tokenizer(
        element["text"],
        truncation=True,
        max_length=2048,
        add_special_tokens=False,
    )

dataset_tokenized = dataset.map(
    tokenize, 
    batched=True, 
    num_proc=os.cpu_count(),    # multithreaded
    remove_columns=["text"]     # don't need the strings anymore, we have tokens from here on
)

4.4 批处理

# collate function - to transform list of dictionaries [ {input_ids: [123, ..]}, {.. ] to single batch dictionary { input_ids: [..], labels: [..], attention_mask: [..] }
def collate(elements):
    tokenlist=[e["input_ids"] for e in elements]
    tokens_maxlen=max([len(t) for t in tokenlist])  # length of longest input

    input_ids,labels,attention_masks = [],[],[]
    for tokens in tokenlist:
        # how many pad tokens to add for this sample
        pad_len=tokens_maxlen-len(tokens)

        # pad input_ids with pad_token, labels with ignore_index (-100) and set attention_mask 1 where content, otherwise 0
        input_ids.append( tokens + [tokenizer.pad_token_id]*pad_len )   
        labels.append( tokens + [-100]*pad_len )    
        attention_masks.append( [1]*len(tokens) + [0]*pad_len ) 

    batch={
        "input_ids": torch.tensor(input_ids),
        "labels": torch.tensor(labels),
        "attention_mask": torch.tensor(attention_masks)
    }
    return batch

4.5. 配置训练超参数

bs=8        # batch size
ga_steps=1  # gradient acc. steps
epochs=5
steps_per_epoch=len(dataset_tokenized["train"])//(bs*ga_steps)

args = TrainingArguments(
    output_dir="out",
    per_device_train_batch_size=bs,
    per_device_eval_batch_size=bs,
    evaluation_strategy="steps",
    logging_steps=1,
    eval_steps=steps_per_epoch,  # eval and save once per epoch   
    save_steps=steps_per_epoch,
    gradient_accumulation_steps=ga_steps,
    num_train_epochs=epochs,
    lr_scheduler_type="constant",
    optim="paged_adamw_32bit",
    learning_rate=0.0002,
    group_by_length=True,
    fp16=True,
    ddp_find_unused_parameters=False,    # needed for training with accelerate
)

4.6. 开始训练

trainer = Trainer(
    model=model,
    tokenizer=tokenizer,
    data_collator=collate,
    train_dataset=dataset_tokenized["train"],
    eval_dataset=dataset_tokenized["test"],
    args=args,
)

trainer.train()

5 Code模型的SFT

5.1 加载数据集

train_dataset = load_dataset('json', data_files='./training_dataset.jsonl' , split='train')

5.2 设置模型参数

new_model = "mistralai-Code-Instruct" #set the name of the new model

################################################################################
# QLoRA parameters
################################################################################

# LoRA attention dimension
lora_r = 64

# Alpha parameter for LoRA scaling
lora_alpha = 16

# Dropout probability for LoRA layers
lora_dropout = 0.1

################################################################################
# bitsandbytes parameters
################################################################################

# Activate 4-bit precision base model loading
use_4bit = True

# Compute dtype for 4-bit base models
bnb_4bit_compute_dtype = "float16"

# Quantization type (fp4 or nf4)
bnb_4bit_quant_type = "nf4"

# Activate nested quantization for 4-bit base models (double quantization)
use_nested_quant = False

################################################################################
# TrainingArguments parameters
################################################################################

# Output directory where the model predictions and checkpoints will be stored
output_dir = "./results"

# Number of training epochs
num_train_epochs = 1

# Enable fp16/bf16 training (set bf16 to True with an A100)
fp16 = False
bf16 = False

# Batch size per GPU for training
per_device_train_batch_size = 1

# Batch size per GPU for evaluation
per_device_eval_batch_size = 1

# Number of update steps to accumulate the gradients for
gradient_accumulation_steps = 1

# Enable gradient checkpointing
gradient_checkpointing = True

# Maximum gradient normal (gradient clipping)
max_grad_norm = 0.3

# Initial learning rate (AdamW optimizer)
learning_rate = 2e-4

# Weight decay to apply to all layers except bias/LayerNorm weights
weight_decay = 0.001

# Optimizer to use
optim = "paged_adamw_32bit"

# Learning rate schedule (constant a bit better than cosine)
lr_scheduler_type = "constant"

# Number of training steps (overrides num_train_epochs)
max_steps = -1

# Ratio of steps for a linear warmup (from 0 to learning rate)
warmup_ratio = 0.03

# Group sequences into batches with same length
# Saves memory and speeds up training considerably
group_by_length = True

# Save checkpoint every X updates steps
save_steps = 25

# Log every X updates steps
logging_steps = 25

################################################################################
# SFT parameters
################################################################################

# Maximum sequence length to use
max_seq_length = 512

# Pack multiple short examples in the same input sequence to increase efficiency
packing = False

# Load the entire model on the GPU 0
device_map = {"": 0}

5.3 基础模型

model_name = "mistralai/Mistral-7B-Instruct-v0.1"

# Load the base model with QLoRA configuration
compute_dtype = getattr(torch, bnb_4bit_compute_dtype)

bnb_config = BitsAndBytesConfig(
    load_in_4bit=use_4bit,
    bnb_4bit_quant_type=bnb_4bit_quant_type,
    bnb_4bit_compute_dtype=compute_dtype,
    bnb_4bit_use_double_quant=use_nested_quant,
)

base_model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map={"": 0}
)

base_model.config.use_cache = False
base_model.config.pretraining_tp = 1

# Load MitsralAi tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

构造Alpaca格式的指令微调提示词

def generate_prompt(instruction, input=None):
    if input:
        return f"""Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.

### Instruction:
{instruction}

### Input:
{input}

### Response:"""
    else:
        return f"""Below is an instruction that describes a task. Write a response that appropriately completes the request.

### Instruction:
{instruction}

### Response:"""

测试

eval_prompt = """Print hello world in python c and c++"""

# import random
model_input = tokenizer(eval_prompt, return_tensors="pt").to("cuda")

model.eval()
with torch.no_grad():
    print(tokenizer.decode(model.generate(**model_input, max_new_tokens=256, pad_token_id=2)[0], skip_special_tokens=True))Fine-Tuning with qLora

5.4 配置Lora

# Set LoRA configuration
peft_config = LoraConfig(
    lora_alpha=lora_alpha,
    lora_dropout=lora_dropout,
    r=lora_r,
    target_modules=[
        "q_proj",
        "k_proj",
        "v_proj",
        "o_proj",
        "gate_proj",
        "up_proj",
        "down_proj",
        "lm_head",
    ],
    bias="none",
    task_type="CAUSAL_LM",
)

# Set training parameters
training_arguments = TrainingArguments(
    output_dir=output_dir,
    num_train_epochs=num_train_epochs,
    per_device_train_batch_size=per_device_train_batch_size,
    gradient_accumulation_steps=gradient_accumulation_steps,
    optim=optim,
    save_steps=save_steps,
    logging_steps=logging_steps,
    learning_rate=learning_rate,
    weight_decay=weight_decay,
    fp16=fp16,
    bf16=bf16,
    max_grad_norm=max_grad_norm,
    max_steps=100, # the total number of training steps to perform
    warmup_ratio=warmup_ratio,
    group_by_length=group_by_length,
    lr_scheduler_type=lr_scheduler_type,
    report_to="tensorboard"
)

# Initialize the SFTTrainer for fine-tuning
trainer = SFTTrainer(
    model=base_model,
    train_dataset=train_dataset,
    peft_config=peft_config,
    dataset_text_field="text",
    max_seq_length=max_seq_length,  # You can specify the maximum sequence length here
    tokenizer=tokenizer,
    args=training_arguments,
    packing=packing,
)

5.5 开始训练

# Start the training process
trainer.train()

# Save the fine-tuned model
trainer.model.save_pretrained(new_model)

测试新模型

eval_prompt = """Print hello world in python c and c++"""

model_input = tokenizer(eval_prompt, return_tensors="pt").to("cuda")
model.eval()
with torch.no_grad():
    generated_code = tokenizer.decode(model.generate(**model_input, max_new_tokens=256, pad_token_id=2)[0], skip_special_tokens=True)
print(generated_code)

5.6 合并模型

# Merge the model with LoRA weights
base_model = AutoModelForCausalLM.from_pretrained(
    model_name,
    low_cpu_mem_usage=True,
    return_dict=True,
    torch_dtype=torch.float16,
    device_map={"": 0},
)
merged_model= PeftModel.from_pretrained(base_model, new_model)
merged_model= model.merge_and_unload()

# Save the merged model
merged_model.save_pretrained("merged_model",safe_serialization=True)
tokenizer.save_pretrained("merged_model")

# Merge the model with LoRA weights
base_model = AutoModelForCausalLM.from_pretrained(
    model_name,
    low_cpu_mem_usage=True,
    return_dict=True,
    torch_dtype=torch.float16,
    device_map={"": 0},
)
merged_model= PeftModel.from_pretrained(base_model, new_model)
merged_model= merged_model.merge_and_unload()

# Save the merged model
merged_model.save_pretrained("merged_model",safe_serialization=True)
tokenizer.save_pretrained("merged_model")

再测试

from random import randrange
sample = train_dataset [randrange(len(train_dataset ))]

prompt = f"""<s> 
{sample['instruction']}
{sample['input']}
[INST]

"""

input_ids = tokenizer(prompt, return_tensors="pt", truncation=True).input_ids.cuda()
# with torch.inference_mode():
outputs = merged_model.generate(input_ids=input_ids, max_new_tokens=100, do_sample=True, top_p=0.9,temperature=0.5)

print(f"Prompt:\n{prompt}\n")
print(f"\nGenerated instruction:\n{tokenizer.batch_decode(outputs.detach().cpu().numpy(), skip_special_tokens=True)[0][len(prompt):]}")
print(f"\nGround truth:\n{sample['output']}")

Part2 Azure GPT-4造数据

openai的接口官方文档

从huggingface下载了ehartford/dolphin数据集,例子如下:

###instruction:
You are a helpful assistant, who always provide explanation. Think like you are answering to a five year old.

 ###input:
Continue the following story.

Emily held and rocked Kayla as they sobbed because
Select from the following.
 +Emily really needed a good friend.
 +Kayla really needed a good friend.

 ###output:
Once upon a time, Emily held and rocked Kayla as they both sobbed because Kayla really needed a good friend. You see, little Kayla was feeling very lonely and sad because she didn't have anyone to play with or talk to.

Emily wanted to help Kayla and be her friend, so she did something truly special. Emily decided to throw a magical friendship party for Kayla. Emily knew that this would cheer her up and help her make some new friends!

Emily worked really hard, preparing for the big day. She bought balloons, baked cookies, and even created a treasure hunt for everyone to enjoy. She invited lots of other kids from their neighborhood, so Kayla could meet them all and find a friend to play with.

On the day of the party, the sun was shining, and the sky was filled with fluffy clouds. All the kids in the neighborhood were excited to come to the party and play with Kayla.

Emily and Kayla welcomed their guests with huge smiles and warm hugs. The kids played games, laughed, and explored the treasure hunt that Emily had set up all around the backyard.

Soon, Kayla was surrounded by new friends, chatting and laughing together. She began to feel so happy and loved. As the sun began to set and the party ended, Kayla felt like she finally had found the friends 
she needed.

That night, as Emily tucked Kayla into bed, Kayla thanked her for being such an amazing friend and throwing the best party ever. Emily smiled and said, "That's what friends are for, to help each other when we're feeling sad and lonely."

And from that day on, Emily and Kayla were the best of friends and shared many more magical adventures together. Now, Kayla knew that she always had a good friend by her side.

写代码请求Azure GPT-4

import os
os.environ['AZURE_OPENAI_ENDPOINT']="xx"
os.environ['AZURE_OPENAI_KEY']="xx"


import openai
openai.api_type = "azure"
openai.api_base = os.getenv("AZURE_OPENAI_ENDPOINT") 
openai.api_version = "2023-05-15"
openai.api_key = os.getenv("AZURE_OPENAI_KEY")

response = openai.ChatCompletion.create(
    engine="jdkjkj", # engine = "deployment_name".


    messages=[
        # 设置AI扮演的角色
        {"role": "system", "content": "You are a helpful assistant, who always provide explanation. Think like you are answering to a five year old."},
        # 真正的问题
        {"role": "user", "content": '''Continue the following story.\n\n
                                        Emily held and rocked Kayla as they sobbed because \n
                                        Select from the following.\n 
                                            +Emily really needed a good friend.\n 
                                            +Kayla really needed a good friend.'''}
    ]
)

# print(response)
print(response['choices'][0]['message']['content'])

输出结果:

PS D:\python\test_GPT4_API> & C:/Users/Administrator/AppData/Local/Programs/Python/Python310/python.exe d:/python/test_GPT4_API/main.py
Emily held and rocked Kayla as they sobbed because Kayla really needed a good friend. 

This is just like when you feel sad and need a hug from your best friend. You see, Kayla felt very upset and alone, like when you can't find your favorite teddy bear and you feel all by yourself. Emily was being a very good friend, just like your favorite blanket is always there for you, comforting you when you're scared or sad. Emily was hugging and rocking Kayla, just like how mommy or daddy might rock you back and forth when you've had a bad dream. They were both crying because they were sharing their feelings, and even though it felt sad, it was also 
good because they were not alone. Emily was just the friend that Kayla needed that day. Just like how you feel happier when you are holding your favorite toy, Kayla felt better because she had Emily, her good friend, with her.

Part3 中文词表

中文的词汇比英文多得多,需要扩大词表,TODO