文 | 智商掉了一地
你有没有想过,让一台计算机诊断和修复自己生成的错误代码?一篇最新的研究论文介绍了一种名为 Self-Debugging 的技术,通过在生成的代码中添加自解释的信息,让计算机像一个可以自己修复代码的程序员一样调试自己的 BUG。
随着大型语言模型(LLMs)在代码生成领域的不断发展,取得了令人瞩目的性能。然而在面对复杂的编程任务时,一次性生成正确的解决方案变得越来越具有挑战性。为了解决这一问题,先前的研究提出了一些代码修复方法来改善代码生成性能。而在本篇论文中,作者提出了一种名为 Self-Debugging 的方法,通过少量示范来教大型语言模型调试它所生成的代码。
该研究表明,Self-Debugging 方法可以教大型语言模型进行橡皮鸭调试,即在没有任何关于代码正确性或错误信息的反馈的情况下,通过自然语言解释生成的代码来识别其错误,这种方法对于提升代码生成性能具有潜在的巨大价值。
为了验证 Self-Debugging 方法的有效性,作者在多个代码生成基准测试上进行了实验,包括文本到 SQL 生成、C++ 到 Python 翻译和文本到 Python 生成。该方法不仅在提升预测准确性方面表现出色,同时还在样本效率上获得了显著的改进,通过利用反馈信息和重用失败的预测,能在样本效率上具有优势,并且在与生成超过 10 倍候选代码的基线模型相比时,能够取得匹敌甚至超越的表现。
综上所述,这篇文章提出的 Self-Debugging 方法为解决复杂编程任务中代码生成困难的问题提供了一种新颖且有效的解决方案。这一研究将为解决复杂编程任务中代码生成的难题带来了新的可能性,对推动大型语言模型在代码生成领域的应用和发展提供了有力支持。
论文题目:
Teaching Large Language Models to Self-Debug
论文链接:
https://arxiv.org/abs/2304.05128
背景探索
近期的大型语言模型在生成代码方面有了显著的改进,但生成正确的代码仍然具有挑战性。研究表明从模型中采样多个程序时,选择最佳候选程序的准确性更高。即使对于人类程序员来说,第一次编写的代码并不总是正确的,他们通常会检查代码并根据执行结果更改来解决实现错误。因此,该研究提出了一种名为 Self-Debugging 的方法,通过执行生成的代码并基于代码和执行结果生成反馈信息,来引导模型进行调试。
不同于需要额外模型训练的方法,Self-Debugging 通过代码解释来指导模型识别实现错误,类似于人类程序员通过逐行向橡皮鸭解释代码行来提高调试效率的方法。图 1 展示了 Self-Debugging 的完整过程。
在每一步调试中,模型首先生成新的代码,然后执行该代码并解释,代码解释和执行结果构成了反馈信息,然后将反馈信息发送回模型以执行更多的调试步骤。当单元测试不可用时,反馈可以完全基于代码解释。
生成代码的 Prompt 技术
Few-shot prompting
Few-shot prompting 是一种通过几个输入-输出演示来指导语言模型完成任务的方法。以文本到 SQL 生成为例,few-shot 提示在感兴趣的问题前面加上(question, SQL)对的列表,这样当模型被要求预测给定提示后的后续 token 时,它会按照提示的格式生成 SQL 查询语句。图 2 展示了一个示例的提示。此外,这种方法还可以在提示中添加指令来提供高级任务描述。
基于执行的代码选择
先前的研究表明,对于大型语言模型,在解码过程中生成多个预测结果可以显著提高性能,特别是在代码生成任务中,可以利用代码执行结果来选择最终的预测结果。有以下两种方法:
使用执行结果的多数投票来选择最终的预测结果;
设计重新排序方案来提高性能。
在本研究中,当存在多个预测结果时,遵循第一种方法,选择在执行时没有遇到错误的预测中具有最频繁执行结果的代码,并对其应用 Self-Debugging。一些代码生成任务伴随着单元测试,以指定程序的执行行为。在问题描述中给出单元测试时,执行基于多数投票的选择之前,会先过滤掉未通过单元测试的程序。
Self-Debugging 框架
图 1 展示了 Self-Debugging 迭代调试框架,使用未经微调的预训练大型语言模型。在给定问题描述后,模型首先预测候选程序,然后推断程序的正确性,并生成反馈信息以进行后续的调试步骤。当反馈信息表明预测结果是正确的,或者达到了允许的最大调试轮数时,调试过程终止。
现有的研究表明,语言模型可以通过训练来理解人类对代码的反馈,并根据指令进行修正。然而,目前尚不清楚语言模型是否能够在没有人类辅助的情况下自行进行调试。在接下来的讨论中,将探讨如何利用代码执行和 few-shot 提示来生成不同类型的自动获取和生成的反馈信息。
通过简单反馈进行 Self-Debugging
最简单的自动反馈形式是一句话,仅指示代码的正确性,没有更详细的信息。例如,在文本到 SQL 生成任务中, few-shot 提示会依据正确或错误结果为所有 SQL 查询提供反馈信息:
“上面的SQL预测是正确的!”
“上面的 SQL 预测是错误的,请修正 SQL。”
使用单元测试进行 Self-Debugging(UT)
对于包含单元测试的代码生成任务,除了通过代码执行来检查代码的正确性外,还可以在反馈信息中呈现单元测试的执行结果,从而为调试提供更丰富的信息。图 5 的示例包含了代码翻译任务的单元测试反馈信息。通过检查运行时错误消息和未通过的单元测试的执行结果,可以帮助人类程序员更有效地进行调试。实验结果表明,利用单元测试可以显著提高调试性能。
通过代码解释进行 Self-Debugging(Expl.)
尽管大型语言模型在生成批评性反馈方面取得了一些进展,以避免生成有害的输出并在自然语言和推理任务中提高性能,但先前的研究尚未在代码生成任务中验证了反馈的有效性。然而,研究表明大型语言模型可以生成描述问题解决方案的文本和代码。基于这一启示,提出了一种新方法,即通过解释生成的代码来教模型进行自我调试,而不是教它预测错误消息。这种调试过程类似于程序员通过向橡皮鸭逐行解释代码来进行调试。研究验证了即使在没有单元测试的情况下,大型语言模型也可以从这种调试方法中获益。
应用
文本到 SQL 生成
在文本到 SQL 生成任务中,使用 Self-Debugging 方法旨在评估大型语言模型的性能,如图 3 所示。由于缺乏单元测试,模型更难推断预测的 SQL 查询的正确性。
调试过程包括三个步骤:
首先,模型通过提示对问题进行总结,并推断问题所需的返回类型,即对应 SQL 查询表的列数。
其次,模型执行 SQL 查询并将返回的表格添加到输入中,以进行代码解释。生成的 SQL 解释包括对每个子句的详细描述、包含在返回表格中的列数以及完整 SQL 查询的高层含义。如果返回表格超过两行,只有前两行包含在提示中。
最后,模型将推断的 SQL 解释和问题描述进行比较,并预测当前 SQL 查询的正确性。Self-Debugging 过程在第三步中认为 SQL 查询是正确的,或者达到最大的调试轮数时终止。
这种方法使得模型能够通过对生成的 SQL 查询进行解释和比较,从而自我调试并预测 SQL 查询的正确性。可以有望提高模型在文本到 SQL 生成任务中的性能,并弥补缺乏单元测试的限制。
代码翻译
在实验中,TransCoder 数据集包含了不同编程语言中的平行函数以及单元测试的测试集。根据先前研究的方法,将 Self-Debugging 应用于将 C++ 代码翻译成 Python 代码的任务,并且使用了相同的测试集,包含 560 个问题和每个问题 10 个单元测试,问题描述包括了 C++ 代码和所有单元测试,如图 4 所示。
由于单元测试的可用性,研究人员只在预测的 Python 代码未通过所有单元测试时才应用 Self-Debugging 技术,这样模型就不需要预测反馈信息。如图 5 所示,他们迭代地应用 Self-Debugging 技术,直到预测的代码通过了所有单元测试,或模型达到了预设的最大调试轮数。
文本到 Python 生成
在问题描述中包含了部分单元测试,MBPP 数据集包含了 500 个 Python 问题的文本描述,每个问题都包含3个单元测试。作者在提示中包含问题描述中的第一个单元测试,并将剩余的2个单元测试保留以进行完整的评估。与代码翻译类似,可以在反馈信息中利用单元测试的执行结果,但主要的区别在于,即使预测的 Python 代码通过了给定的单元测试,模型仍然需要推断代码的正确性。
实验
这里对实验部分将不再赘述,作者在所有 Self-Debugging 的实验中,使用贪婪解码来生成代码解释、反馈信息和新的程序,并将最大调试轮数设置为 10。实验证明,成功的调试过程通常在 3 轮内结束。
在表格 1、2 和 3 中,作者展示了 Self-Debugging 与现有方法的比较结果:
由于没有可用的单元测试,仅有简单的反馈并不能改善性能,因为模型无法仅通过少量的示例区分正确和错误的 SQL 查询,并且不能对初始预测的 SQL 查询进行有意义的修改。
简单的反馈利用了执行结果来推断代码的正确性,即使执行信息没有在反馈信息中呈现给模型。因此简单的反馈仍然改进了模型的预测性能。
对于所有任务,模型从更丰富的反馈信号中受益。尤其是代码解释使得模型可以在没有单元测试的情况下进行 Self-Debugging。
在消融实验中,作者主要指出以下几点:
文本到 SQL 生成
通常情况下,一个调试轮次已经足够。这些结果强调了利用大型语言模型解释代码进行调试的优势。
在图 6b 中进一步展示了不同难度级别问题上的准确性细分情况,其中每个问题的难度在数据集中根据目标 SQL 查询的复杂性进行了注释。在不同难度级别的问题上,Self-Debugging 对于较难的问题改进更加显著。
解决难题的能力:特别是在额外困难问题上,Self-Debugging 将准确性提高了 9%。图 7 展示了一个额外困难问题中,Self-Debugging 修复了预测的 SQL 查询的例子。
代码翻译
图 8a 展示了不同调试轮次上的准确性,图 8b 展示了不同初始样本数量上的准确性。
将单元测试执行和代码解释结合起来可以提高调试性能,在图 9 和图 10 中展示了这些例子。
如图 8b 所示,单独利用代码解释而不进行 Self-Debugging 也可以在不同数量的样本上提供一致的性能提升。
文本到 Python 生成
发现在初始预测代码与正确解之间差距较大时,代码解释的作用较小,此时模型更倾向于生成新的程序而不是依赖于初始预测中的代码片段。在图 11 中展示了一个例子,其中 Self-Debugging 后的预测代码与初始代码差异很大。
小结
本文介绍了 Self-Debugging 技术,它使得大型语言模型能够自行调试所生成的代码,该方法使模型能够进行橡皮鸭子调试,从而使模型能够在没有人类指导的情况下识别和修复错误。在多个代码生成领域,它实现了最先进的性能,并且显著提高了样本效率。
在文本到 SQL 生成任务中,由于该任务没有指定单元测试,利用代码解释进行 Self-Debugging 以及在最困难的问题上可以始终有好效果。对于代码翻译和文本到 Python 生成任务,在有单元测试可用的情况下,Self-Debugging 准确性提高了多达 12%。
本工作强调了通过教大型语言模型迭代来自己调试预测的方法,从而改进其编码性能的潜力,而非要求模型从零开始生成正确的代码。Self-Debugging 指导模型理解代码、识别错误,并根据错误信息修复错误。
未来的研究方向可以包括进一步提高模型在调试过程中的能力。例如:
探索让模型更好地描述代码的高级语义含义和实现细节,从而提高其代码解释能力。
考虑在模型的反馈中包含更多的调试信息(例如潜在错误的描述),以提供更加丰富的指导。
相信这些改进将进一步推动大型语言模型在编码任务中的性能提升,并在实际应用中发挥更大的潜力。若是有朝一日,用大模型的问答工具准确地辅助我们写代码,少了焦头烂额 Debug 的内耗时刻,岂不美哉?
卖萌屋作者:智商掉了一地
北理工计算机硕士在读,近期沉迷于跟 ChatGPT 唠嗑,对一切新颖的 NLP 应用充满好奇,正在努力成为兴趣广泛的斜杠青年~
作品推荐
我是粉红猪佩奇,我要把粉色吹风机写进 IJCAI 论文!
AI取代人类,可以自动生成prompt了
ICLR 2023 最高分论文被锤抄袭??
AI讲话总爱“结巴”?这篇NeurIPS论文找到了病因,结巴率已接近人类!
如何提升大规模Transformer的训练效果?Primer给出答案
Yoshua Bengio:我的一生
后台回复关键词【入群】
加入卖萌屋NLP、CV、搜推广与求职讨论群