“自己动手,丰衣足食”——大模型的自我“进化”探索
© 作者|闵映乾
机构|中国人民大学
研究方向 | 自然语言处理
本文主题大规模语言模型的自我“进化”或者“调优”。指的是在训练、微调或使用大语言模型时,如何不同程度地省去人工方法,使得模型更加自主高效地优化自身。文章也同步发布在 AI Box 知乎专栏(知乎搜索 AI Box 专栏),欢迎大家在知乎专栏的文章下方评论留言,交流探讨!
近期OpenAI的ChatGPT和GPT-4的优异表现,可谓掀起了大规模语言模型(LLM)的研究热潮,仿佛让人看到了AGI(通用人工智能)的曙光。大模型强大的性能的背后,意味着较高质量的数据集,庞大的算力需求以及高额的人力物力成本。除此之外,大模型还有诸多方面做的不够好,例如一本正经地胡说八道(幻像),生成内容带有歧视和偏差,对于不同领域不同用途的适配等等。一个很直觉的想法就是,在现有如此强大能力的基础上,大模型能否更加智能,像人一样慢慢地自我学习,自我纠错,自我进化,完善自己的能力呢?本文介绍的论文均与此有关,研究者们尚处于对大模型的摸索阶段,方法不一定有多么复杂,但思路都有一定的启发性,希望广大读者都能有些收获。
目录
Self-Improve Self-Refine Self-Debugging 总结
一、Self-Improve
论文题目:《Large Language Models Can Self-Improve》
论文链接:https://arxiv.org/abs/2210.11610
大型语言模型在各种任务中取得了出色的表现。然而,对LLM进行微调仍然需要大量的监督。那么,人类是如何在没有监督的情况下,通过自主思考来提高自己推理能力的呢?一般来说,给定一个问题,我们通常会反复思考,得出不同的可能结果,根据这些可能结果来总结解决问题的方法,最后从自己的解决方案中学习或记忆。这篇文章的基本思路也是这样。
在这篇论文中,作者展示了一个只使用无标签数据进行自我改进的方法。首先,使用预训练的LLM通过链式思考(CoT)和自洽性(self-consistency)来生成高置信度的合理解释增强答案,然后用这些自动生成的解决方案作为目标输出对LLM进行微调。这种方法被作者称为:Language Model Self-Improved(LMSI),详细的流程图如下:
作者使用的大模型是参数量540B的PaLM,并用该方法提高了它的一般推理能力(GSM8K上从74.4%提高到82.1%,DROP上从78.2%提高到83.0%,OpenBookQA上从90.0%提高到94.4%,ANLI-A3上从63.4%提高到67.9%),在没有真实标签的情况下达到了SOTA水平的表现。此外,作者还进行了消融研究,并展示了对推理进行微调对于LMSI是至关重要的。
下面按照流程顺序详细讲解论文的训练过程:
生成并过滤推理路径
对于每一个训练问题,作者采样了m个CoT推理路径,并且根据推理路径生成对应的答案。论文中使用self-consistency方法,通过投票的方式,选出最一致的答案,而不一定是正确答案。然后对于所有的训练问题,作者把能够推理出一致性答案的推理路径过滤出来,组成自训练的数据。在这种方法中有一个很重要的点,就是因为没有使用任何真实标签来过滤掉错误的一致性答案的情况,所以应当确保这些CoT路径大多数是可靠的,不正确的答案不会损害模型的自我优化。论文为了确认这一点,做了相应的实验,结果如下图:
图中显示的是在GSM8K数据集上多条推理路径投票置信度和答案准确度的关系,圆圈的面积和颜色的深浅显示了在某置信度下问题的数量,可以看出置信度高的答案更可能是正确的,这意味着选择一致性答案的推理路径得到的答案更有可能是正确的。
使用混合形式训练
为了防止语言模型过度适应特定的提示或答案风格,作者为每个推理路径创建了四种不同的格式,并在自训练过程中混合不同的格式训练。在第一种格式中,输入是CoT的例子(问题后面是导致正确的最终答案的推理路径)再加上问题和答案中,而语言模型的输出被训练成与CoT推理路径相同。在第二种格式中,使用问题的例子和它们的直接答案作为标准提示,而语言模型的输出也应该只包含直接答案。第三种和第四种格式与第一种和第二种格式类似,只是没有给出问题-答案对的例子,这样模型就会以语境中零散的方式学习自己的思考。在第三种格式中,作者希望模型在不预留包含CoT推理的例子的情况下输出CoT推理,因此在输入序列的末尾附加 "Let's step by step",以引导语言模型生成一步一步的CoT推理路径。
生成问题和提示
论文提出的方法依赖于通过一定量的CoT来实现模型的自我改进,因此当训练问题和CoT例子有限时,该方法可能无法产生足够的训练样本。为了进一步减少人工操作,作者还研究了如何自我生成更多的训练问题和示例提示。
生成问题。论文采用的方法是随机选择几个现有的问题,以随机顺序串联起来作为输入提示,并让LLM生成连续的序列作为新的问题。当不断重复这个过程后,就可以得到一大批新问题,然后再次使用self-consistency,只保留那些有高置信度的答案,这些问题被用作自我生成的训练问题。 生成提示。作者在这里使用了之前一篇相关工作的做法,使用“A: Let’s think step by step.”作为答案的开头,然后让语言模型生成后续的推理路径,再使用这些推理路径作为样例,进行few-shot的CoT提示
结果
主要结果如下表:
从表中可以看出,在4个主要的推理benchmark(ARC-c, OpenBookQA, ANLI-A2, ANLI-A3)上,本文方法LMSI都取得了SOTA的结果;在GSM8K和DROP数据集上都接近之前SOTA的结果。在GSM8K数据集上,与DiVeRSe方法使用100条输出路径相比,LMSI只使用了32个输出路径;在DROP数据集上,OPERA方法使用了真实标签来训练。而LMSI完全没有。综合来讲,LMSI取得了很好的效果,证明了大模型自我优化的可行性。
二、Self-Refine
论文题目:《Self-Refine: Iterative Refinement with Self-Feedback》
论文链接:https://arxiv.org/abs/2303.17651
人类在解决问题时,其实很少一遍就能完美解决,通常是经过了一些试错才慢慢找到正确的解决办法,这篇论文正是试图沿着这样的特点来让大模型对自己的输出多一些“思考”,在“思考”的过程中改进。
论文提出了一个名为SELF-REFINE的框架,通过迭代反馈和改进来优化大模型的初始输出。主要的想法是先使用LLM生成一个输出,然后让该模型为它自己的输出提供多方位的反馈;最后,该模型根据它自己的反馈改进它之前生成的输出。与早期的工作不同,作者强调他们的迭代改进框架不需要监督训练数据或强化学习,只需要一个LLM就可以工作。论文中对7个不同的任务(评论改写、数学推理等)进行了实验,证明了该方法优于直接生成。在所有的任务中,用SELF-REFINE生成的输出都比直接用GPT-3.5和GPT-4生成的输出更受人类和自动化指标的青睐,在不同的任务中平均提高了20%。
该方法的主要流程如上图。SELF-REFINE包括两个组件之间的循环迭代:Refine和Feedback,两个组件协同工作以产生高质量的输出。首先模型得到输入后,获得一个最初始的输出,然后把这个原始输出传回同一模型以获得反馈。这个反馈仍然会被传回同一模型,以迭代完善之前生成的输出。这个过程在指定的迭代次数内反复进行,或者做到模型认为不需要继续调整。在多次的自我反馈和自我调整的过程中,模型模拟了人类反复思考反复“试错”的过程。下图是实际任务中的两个示例:
结果
作者在7个不同的任务上(评论改写、数学推理等)进行了实验,结果如上表。可以发现,使用SELF-REFINE框架在这些任务上使得LLM的表现产生了改善。这些改善是以人类评测(Human Eval)或特定任务的性能指标所显示的偏好率的百分比增长来衡量的。这表明SELF-REFINE采用的迭代改进过程在产生更高质量的输出方面较为有效。
三、Self-Debugging
论文题目:《Teaching Large Language Models to Self-Debug》
论文链接:https://arxiv.org/abs/2304.05128
这篇论文针对的是大模型的代码生成任务。对于复杂的编程任务,一次性生成正确的解决方案比较困难,人类也是这样,需要在编程的基础上进行调试。该论文提出了一种SELF-DEBUGGING的方法,通过少量的解释示例来教大模型对自己生成的程序进行调试纠错。该方法除了可以通过简单的提示反馈和单元测试外,还可以让大语言模型进行“rubber duck debugging”,也就是让模型用自然语言解释生成的代码用途,从中寻找代码的错误,这个过程并不需要任何关于代码正确性或错误信息的反馈,属于LLM的自我纠错。
在效果方面,SELF-DEBUGGING在几个代码生成基准上实现了最先进的性能,包括Spider(文本到SQL生成),TransCoder(C++到Python翻译)以及MBPP(文本到Python生成)。在没有单元测试来验证预测正确性的Spider基准上,带有代码解释的SELF-DEBUGGING一直将基线提高了2-3%,并将最难的标签问题的预测精度提高了9%。在有单元测试的TransCoder和MBPP上,SELF-DEBUGGING将基线准确性提高了12%。同时,通过利用反馈信息和重用失败的预测,SELF-DEBUGGING明显提高了采样效率,并能匹配或超过生成10倍以上候选程序的基线模型。
模型架构
该方法的框架如上图所示,大致的流程是:
LLM生成代码 代码执行 LLM对代码用自然语言解释 综合执行结果和自然语言解释,作为反馈送回模型,让LLM进行代码调试(当单元测试不可用时,反馈可以纯粹地基于代码解释)
现有的工作已经证明了,语言模型可以被训练来理解人类对代码的反馈,并且根据指令做出修改。但是目前让语言模型在无人协助的情况下自行调试仍然是个问题。因此作者在论文中讨论了相关工作中的不同类型的反馈信息,并通过代码执行和少量的提示方式来获取这些反馈信息,从而使得调试过程自动化。
简单提示产生的简单反馈信息
最简单的自动反馈形式是一句只表示代码的正确性,不包含更详细的信息的话。例如,在将文本翻译成SQL的任务重,对于所有正确的SQL查询,few-shot的提示提供了反馈信息 "The SQL prediction above is correct !",对于错误的则使用“The SQL prediction above is wrong. Please fix the SQL.”
单元测试提供的反馈信息
对于包括单元测试的代码生成任务,除了利用代码执行来检查代码的正确性,还可以在反馈信息中呈现执行结果,为调试提供更丰富的信息。直观地说,检查运行时错误信息和失败的单元测试的执行结果也有助于人类程序员更有效地进行调试。在论文的实验中作者发现,利用单元测试可以大大改善调试性能。
自然语言代码解释提供的反馈信息
本文尝试了人类程序员在调试程序中一种常用的调试方法:rubber duck debugging——程序员通过逐行解释代码用途来调试代码。在描述代码的过程中将其与实际的问题描述相比较,人类程序员通常能够在没有额外指导的情况下找出错误。类似的,作者观察到大型语言模型也可以从这种调试方法中受益,特别是在没有单元测试的时候。
下图是几种反馈信息在实验时的示例:
实验结果
论文在Spider(文本到SQL生成),TransCoder(C++到Python翻译)以及MBPP(文本到Python生成)几个benchmark上测试了结果,结果如下:
从表格中可以看出,Self-Debugging在三个数据集上都达到了SOTA的水平,并且与不进行调试的基线方法相比显著提高了性能。
总结
上述几篇工作都涉及到让大模型脱离人工干预,进行自我优化的过程,想法比较新奇,让人受益匪浅。但是可以发现其实各自针对的任务较为有限。如何让大模型真正能够在通用意义上完成自我学习,可能会成为接下来需要持续探索的方向。
更多推荐
训练你的大模型!低资源下的模型轻量化
一文速览大型语言模型在图文多模态中的应用