推理,作为人类的基本认知过程,对于学习、问题解决和科学发现起着至关重要的作用。常见的推理方式包括演绎推理和归纳推理。
演绎推理就是从一般性的前提推导出个别或特殊情况的结论,而归纳推理则用于从具体实例中提炼出一般性的结论或规律。
研究学者一直试图增强大模型的推理能力,但大部分训练数据的指令都偏向演绎推理,比如“解释一下这个代码是什么意思”或者“写一个程序找出列表中最大和最小的数”。
相比之下,那些要求从几个例子中推断出通用规则或函数关系的归纳推理任务,数据就稀缺多了。比如:“假设有:func(1) == 6 func(2) == 7 func(3) == 8 func(4) == 9给我解释一下这个函数”或者“给定输入与输出john Smith -> Smith, John;frank lee -> Lee, Frank;Laura Jane Jones -> Jones,Laura,请编写程序将输入转换为输出”。
这种数据不平衡让LLMs在归纳推理上学习困难,也缺乏有效评估其归纳能力的标准。为了填补这一空白,复旦大学发起了一项名为Case2Code的挑战,它模拟了现实工作中的一个常见场景:根据几个输入输出示例来编写代码。刷过题的朋友们对这套模式一定不陌生~
除此之外,本文还提出了一套高效获取大规模、多样化Case2Code训练数据的方法。使用1.3M规模的合成数据训练的7B小模型在Case2Code评估集上的准确率大幅提升,最高增幅达18.9%,甚至超越了LLaMA3-70B和GPT-3.5,且与GPT-4旗鼓相当。
论文标题:
Case2Code: Learning Inductive Reasoning with Synthetic Data
论文链接:
https://arxiv.org/pdf/2407.12504
代码链接:
https://github.com/choosewhatulike/case2code
方法
本文首先构建了大规模的合成数据用于代码的归纳推理。合成数据生成框架旨在自动生成大规模且多样化的Case2Code数据。步骤如下图所示:
首先从大规模数据集中收集多样化的程序,并通过基于规则的过滤器进行筛选。接着利用LLM编写多样的示例输入,并通过代码解释器计算其对应的输出。最后根据输出筛选出低质量程序,并将获得的三元组(程序、输入、输出)转换为用于代码领域归纳推理的Case2Code数据。
简单来说就是根据已有的题解(程序)使用LLM编写输入输出示例,以获得包含(程序、输入、输出)三元组的Case2Code数据。
1. 收集程序
为增强数据的多样性与质量,作者从The Stack 中采样有效的Python函数程序。利用现成的抽象语法树(AST)解析工具解析The Stack中的每个文件,以获取Python函数。且必须满足(1)通过语法检查;(2)具有一个或多个输入参数和返回值;(3)不依赖于第三方包或外部I/O操作。过滤后通过代码解释器进行验证,从而轻松获取多样化的Case2Code数据。
2. 生成输入
作者提示LLMs为每个函数编写输入参数,下表是一个简单的示例:
为了生成合适的输入参数,LLM首先需要分析函数的实现,然后推断输入参数的可能类型和value范围,最后提出正确的输入参数。
作者表示,这一步并不需要LLMs拥有多强大的能力,一些小LLM也可以轻松完成,以低成本高效地扩展生成过程
3. 获得输出
作者利用代码解释器执行函数并处理输入,从而获取对应的输出。鉴于LLM生成的输入样例可能存在错误,当函数输出不因输入变化而变动(恒定输出或抛出异常)时,该函数被视为无效并被剔除。
同时,为避免生成的Case2Code数据超出LLM的上下文窗口限制,排除了那些产生异常冗长输出值的函数
但是不会过滤掉导致异常或运行时错误的输入,失败的调用尝试也可以为归纳推理提供有价值的信息,以重建函数。
4.后处理
最后将获得的函数及其对应的输入输出对转换为Case2Code风格的数据。具体而言,对于给定函数及其个测试用例,随机选择个示例作为观察集。生成的提示数据将帮助LLM在观察集上进行归纳推理,从而重建函数。转换后的训练示例如下表所示:
实验设置
合成数据规模
作者从The Stack预训练数据集中随机抽取了大约2.3万个函数,经过去重过滤,使用InternLM2-7b生成输入示例,python执行环境生成输出后,最终获得了1.3M包含输入-输出-函数三元组的高质量的数据集。然后保留了500个样本用于评估,其余的用于训练。
训练设置
为了展示我们训练数据的泛化性和有效性, 作者在即InternLM2-7B和LLaMA3-8B分别进行了三种变体的Case2Code 训练:直接微调、混合预训练和混合微调。
评估设置
评估基准使用HumanEval和MBPP评估训练后的LLM编码能力,并引入EvalPlus——这两个基准的扩展版本,包含大量额外测试案例,以确保评估的严格性。
对于非指令调优模型,在HumanEval和MBPP上分别应用了zero-shot提示和 four-shot提示进行测试。而对指令对齐的LLM,则统一采用zero-shot提示在所有基准上进行评估。
为了探究LLM在代码归纳推理方面的能力,还进一步测试了它们在Case2Code任务上的表现,同样采用zero-shot提示。
结果分析
zero-shot性能评估
作者汇总了不同代表性LLM在zero-shot Case2Code任务中的表现及其编程性能:
分析显示,这些模型在Case2Code任务中的表现与其程序合成能力紧密相关,在其他基准上获得高分的模型在Case2Code中同样表现出色,且规模较大的模型普遍优于小规模模型。这表明Case2Code是一个有效的基准,能够反映LLM的代码推理能力。
但是普遍得分较低,也就是说大模型还没有达到根据示例编写完全准确代码的水平。这可能是因为,虽然大模型在训练时看了很多编写程序的例子,但它们接触到的像Case2Code这样需要通过观察行为来归纳程序的任务还比较少,类似于“逆诅咒”现象所揭示的规律。
Case2Code的泛化能力
合成数据的一个基本问题是它的泛化能力。因此,作者使用合成Case2Code数据集在不同的设置下训练不同的LLM,探索它如何影响LLM的代码推理学习。结果如下表所示:
直接微调
直接在Case2Code推理样本上训练的LLM能够高效地学习基于案例的编码方法。如上表所示,通过直接微调,Internlm2-7B和LLaMA3-8B在Case2Code评估集上的准确率分别提升至44.5%和42.0%,最高增幅达18.9%,这一表现甚至超越了LLaMA3-70B和GPT-3.5,且与GPT-4旗鼓相当。
另外这些经Case2Code训练的模型在HumanEval和MBPP等基准测试中的程序合成能力也获得了提升,彰显了Case2Code推理的普遍适用性和挑战性。
通过Case2Code样本的训练,不仅能强化LLM的归纳推理能力,还能增强其代码理解和生成的整体水平。
在预训练阶段混合
在预训练阶段融入Case2Code数据,有效促进模型将执行状态与函数实现相联结,从而显著增强LLM的程序合成能力。
相较于仅在Case2Code数据集上微调,此策略能更广泛地迁移通过Case2Code任务习得的代码状态归纳推理能力,实现更好的泛化效果。
在指令微调阶段混合
在采用instruction-following数据集训练时,融入Case2Code数据显著增强了LLM在基于指令编程任务上的性能。
整合Case2Code数据后,各LLM在代码生成任务上的表现均有所提升。以InternLM2-7B为例,其在HumanEval上的准确率飙升超过10%,达到64.6%。同样,LLaMA3-8B在HumanEval、HumanEval+及MBPP上的准确率也分别提升至64.6%、57.9%和71.2%,较其SFT版本有明显进步。这充分证明了学习Case2Code数据的有效性,并凸显了在LLM训练中纳入归纳推理数据的重要性。
消融实验
提示的多样性
由于Case2Code训练数据源自(程序、输入、输出)三元组的转换,提示模板在构建过程中扮演关键角色。作者对比了单一模板与多模板风格提示下的合成数据效果。如下图所示:
尽管域内Case2Code性能受提示多样性影响较小,但LLM在域外程序合成任务上的准确性却显著提升,这揭示了多样性在LLM学习中的潜在关键作用。
用于生成输入的LLM的影响
在合成Case2Code数据时,核心环节之一是引导LLM为各程序创建多个输入样例。这些输入与程序共同执行后产生的输出,共同构成了构建训练数据的关键上下文。
为探究LLM生成输入示例的能力对数据质量的影响,作者将LLM从InternLM2-7B替换为更强大的LLaMA3-70B,并生成了规模减半的新数据集。两者的成本比较如下图所示:
使用这一版成本更高的Case2Code数据在InternLM2-7B上进行了指令微调测试。如下图所示:
LLaMA3-70B编写的输入样本质量更高,使得训练后的LLM在数据减少情况下仍展现出相当的代码推理能力。这凸显了输入生成步骤对数据质量的重要性,建议在资源允许时采用更强大的LLM。
然而低成本版本InternLM2-7B也达到了和LLaMA3-70B相当的效果,不失为一个性价比更高的选择!
模型规模
为了探究利用小模型合成的Case2Code数据是否依然能有效提升大模型的性能,并深入分析模型规模对学习过程的影响。作者采用InternLM2-7B生成的Case2Code数据,混合SFT数据集对不同大小的InternLM2系列模型进行了训练。实验结果如下表:
可以看到,无论学生模型规模如何,即便学生模型的规模几乎是数据合成模型的三倍,合成数据均显著增强了其代码推理能力,这一发现揭示了在大规模代码任务中,弱监督到强监督转换的可行性与潜力。
结论
作者构建了一个新的基准——Case2Code,用于评估LLMs在代码领域的归纳推理能力,并提出了一个数据合成框架,仅使用小型LLM和代码解释器就能自动高效地从预训练代码文本中收集高质量的Case2Code训练数据。通过在不同设置下训练各种LLM,本文证明了Case2Code不仅能提升LLM的归纳推理能力,还能提高其整体编码能力。我们可以期待,当人类生成的数据用尽时,合成的Case2Code能够持续为改进LLM生成高质量的数据~