之前在《大规模预训练模型如何赋能代码智能》一文中提到了 Codex,作为 OpenAI 用来支持 Github Copilot 的大规模预训练模型,Codex可谓是目前最强的编程语言预训练模型。最近,OpenAI 又推出了改进版本的 Codex,并发布了基于自身 API 的私测版,OpenAI 表示在初期会免费提供 Codex,并希望更多的企业和开发者可以通过它的 API 在 Codex 上构建自己的应用。本文将基于 Codex 的论文 ,简单介绍和分析一下Codex。
一. 数据集
正如OpenAI 的联合创始人兼首席技术官 Greg Brockman介绍的那样,“Codex是GPT-3的后代”,Codex基于GPT-3使用code数据进行了Fine-Tuning,模型参数从 12M 到 12B不等。众所周知,在训练大规模预训练模型时,通常是“模型未动,数据先行”,因此,本节我们先简单介绍一下Codex使用的到数据集。
1. Code Fine-Tuning数据集
首先是用来做Fine-Tuning的code数据集,根据论文介绍,Codex在2020年5月从Github 的 54,000,000 个公开代码仓上收集了数据,包括 179 GB 文件大小在 1 MB 以下的独一无二的python文件,在经过过滤后,最终的数据集大小为159GB。
2. 评测数据集
在之前的代码生成工作中,主要采用的是基于匹配的方法,即基于模型输出和参考代码的精确或模糊匹配,如BLEU,也有一些工作基于匹配的方法做了一定的改进来捕捉代码的语义正确性,如CodeBLEU ,但论文中认为基于匹配的方法,不能评价编程功能的正确性,因为从功能上看,参考代码所对应的解空间是十分巨大和复杂的。因此,论文中参考了之前代码迁移工作 和“伪代码到代码”转换工作 中的做法,将生成代码的功能正确性作为评测指标,具体来说,在论文中,关注从docstrings生成python函数的任务,并通过unit tests的方法来评测生成代码的正确性。
评测指标采用的pass@k,在之前的工作 中,该指标表示被解决问题的比例,其中每个问题在预测时采样k个样本,k个样本中任何一个通过unit tests,则认为该问题被解决,但论文中指出,用这种方式计算 pass@k 方差比较大,论文对该评测指标进行了改进。与之前工作不同的是,对每个问题,在预测时产生 n≥k 个样本,统计能够通过unit tests的正确样本的数量 c ≤ n, 并根据下式计算 pass@k 的无偏估计:
由于(1)式里的阶乘可能产生较大值,导致数值不稳定的问题,论文里提供了一个数值稳定的计算函数。
图1 计算 pass@k 无偏估计的数值稳定的脚本
论文中在 HumanEval 和 APPS 两个数据集上进行了评测,下面分别进行介绍。
2.1 HumanEval
为了评价函数功能的正确性,论文中构建了一个包括164个人工受写的编程问题的数据集,叫做HumanEval,其中每个编程问题包括函数头、docstrings、函数体和几个 unit tests。HumanEval中的编程问题可以用来评估语言理解能力、推理能力、算法能力和简单的数学能力,该数据集已经开源 。
图2 HumanEval数据集中的三个编程问题例子
2.2 APPS
APPS 是 Hendrycks 等人提出的用来衡量语言模型编程能力的数据集,APPS一共包含10000个编程问题,每个编程问题都有若干个 unit tests,其中5000个编程问题作为训练集,5000个编程问题作为测试集,训练集中的每个问题还包括若干个正确答案。
3. Supervised Fine-Tuning数据集
从Github上收集的Code Fine-Tuning数据集除了独立函数外,还包含类实现、配置文件、脚本,甚至还包含存储数据的文件,这些代码和从 docstring 生成函数的相关性不大,因此,论文认为这种数据分布的不匹配会影响模型在HumanEval数据集上的效果。
为了进行额外的有监督的 fine-tuning,论文构建了一个用于正确生成独立函数的数据集,数据集的来源是编程比赛网站和持续合入的代码仓,论文通过前者构造了10000个编程问题,通过后者构造了40000个编程问题,并进行了过滤。
4. Docstrings 生成数据集
论文中还尝试了从代码生成docstrings,由于数据集中通常的数据格式为 <函数头><docstrings><函数体>,因此根据函数头和 docstrings 生成函数体相对比较容易,而根据函数头和函数体生成 docstrings 可能没那么容易。通过将之前数据集中的数据格式变为<函数头><函数体><docstrings>, 构造了一个根据代码生成 docstrings 的数据集。
二. 模型
Codex 的模型结构和 GPT 完全一样,论文中尝试了从头训练和基于 GPT-3 的参数 fine-tuning,结果发现基于 GPT-3 的参数 fine-tuning 并没有取得效果上的提升,原因可能是 Code Fine-tuning 数据集太大,但基于 GPT-3 的参数 fine-tuing 可以收敛的更快,因此,论文中都采用的是这种训练策略。
为了尽可能的利用 GPT 的文本表示,Codex 使用了和 GPT-3 一样的分词器,但因为代码中词的分布和自然语言中词的分布有很大区别,GPT-3 的分词器在表示代码时可能不是非常有效,在 python 语言中,分词器编码低效的一个主要原因是空格的编码,因此,论文中在GPT-3 的分词器中加入了额外的一些 token 来表示不同长度的空格,这样在表示代码时可以少使用 30% 的 token。
推理时,使用核采样 不断采样 Codex 生成的 token,直到碰见以下字符中的任何一个。
"\nclass","\ndef","\n#","\nif" , '\nprint'
2.1 Codex
论文中训练得到的第一个模型就是Codex,即在不同参数量的 GPT-3 上做 Code-Funing 得到的模型,模型参数量有12M、25M、42M、85M、300M、679M,2.5B 和 12B。
2.2 Codex-S
为了提升 Codex 在 HumanEval 数据集上的效果,论文将 Codex 在 Supervised Fine-Tuning 数据集上进行了fine-tuning,得到的模型称为 Codex-S。为了和HumanEval保持一致,将数据都处理成了图2所示例子的形式,当一个batch内提示(函数名和 docstrings )长度不同时,采用了 left-pad 的方法进行了处理,在训练时,mask了所有提示中的token上的loss。
2.3 Codex-D
为了训练 Codex 生成 docstrings 的能力,论文将 Codex 在 Docstrings 生成数据集上进行了fine-tuning,得到的模型称为Codex-D,在训练时,mask了所有代码(函数名+函数体)中token上的loss。
三.实验结果
1.Codex 实验结果
1.1 HumanEval 实验结果
Codex和 GPT-Neo、GPT-J 和 TabNine 在 HumanEval 上的实验结果对比如图3所示,可以发现 Codex-300M 的效果优于 GPT-J 6B。
图3 Codex, GPT-Neo, GPT-J 和 TabNine 在 HumanEval 上的实验结果
如图4所示,论文中还证明了 Codex 可以通过增加模型规模持续受益。
图4 pass@1 和 pass@k 随着 log-parameters 的增加持续提升
此外,针对每个问题,论文计算了正确答案和错误答案的 BLEU,发现 BLUE 并不能很好地反映函数的正确性,说明了使用 pass@k 作为评测指标的必要性,如图5所示。
图5 针对HumanEval中随机的4个编程问题,Codex-12B产生的正确/错误答案的BLEU
论文还发现在推理时,在 pass@k 的 k 较大时,采样时使用较大的 temperature 效果较好,如图6所示。
图6 计算pass@k时不同k对应的最佳采样 temperature
通常,在根据函数头和 docstrings 生成函数体时,尽管我们可以通过采样生成多个,但在实际使用时(即评价时),只能使用其中一个,因此,论文中比较了多种从采样结果中挑选用于评价的样本的方法,如图7所示。通过比较,发现挑选 mean log-probability 最高的样本效果最好。
图7 通过不同方法挑选用于评价的采样样本在HumanEval数据集上的实验结果
1.2 APPS 实验结果
在 APPS 数据集中,每个编程问题都提供了 3 个输入输出例子(unit tests),在评测时,可以通过 Codex 采样 1000 个答案,然后挑选能够通过这 3 个 unit tests 的答案,最终在过滤后的答案集合上进行评测, 将这个评测结果叫做 filtered pass@k,在没有过滤过的答案集合上的评测结果叫做 raw pass@k, 另外,由于在编程比赛中通常有运行时间限制,因此,在论文中将在某条 unit tests 上运行时间超过 3s 的答案视为不通过。
此外,由于 GPT-Neo 在 APPS上进行了 fine-tuing,而 Codex没有进行 fine-tuning,为了对比相对公平,在 Codex 进行推理时使用了 1-shot 的做法,即在 docstrings 中加入了一条“输入/输出”示例。
Codex 在 APPS 上的实验结果如图8所示,尽管没有进行 fine-tuning,但 1-shot Codex 的效果仍然优于 GPT-Neo,同时,也可以发现随着问题难度增加,模型效果急剧劣化。
图8 Finetuned GPT-Neo 和 Codex-12B在 APPS 数据集上的效果比较,括号中是没有添加运行时间限制之前的结果
2. Codex-S 实验结果
从实验结果来看,Codex-S 在 HumanEval 上的效果明显优于 Codex,如图9所示,不同的模型规模下,Codex-S 效果均优于 Codex,使用不同的选择策略,Code-S效果也都优于Codex。
图9 Codex 和 Codex-S 在 HumanEval 上的效果对比
3. Codex-D 实验结果
在评价 docstrings 的生成效果时,论文使用了人工打分的方法,打分标准为doctring是能否精确且唯一地指定函数体。实验结果如图10所示,可以发现 Codex-D 的 pass@k 低于 Codex-S,这也印证了之前提到的生成 docstrings 更难的观点,但也有可能是因为相对代码来说,docstrings的质量本来就比较低。
图 10 Codex-D 实验结果
论文还分析了 bad case,发现生成失败主要有两个原因,一个是模型遗漏了一些重要的细节,比如“答案必须保留小数点后两位”,另一个是过度依赖函数名并发明了与函数体无关的编程问题。
四.讨论
论文也分析了 Codex 的一些限制:
首先是 Codex 的学习效率不够高,Codex 在训练过程中使用了大量代码,即使是经验丰富的开发人员,在整个职业生涯中也不会遇到这种数量级的代码,但 Codex 的能力甚至还不如一个完成了基础计算机科学课程的学生;
其次,Codex显示出了一些失败或者反直觉的行为。比如 Codex 会生成语法错误或者未定义的代码,并且会调用未定义或超出范围的函数、变量和属性。此外,Codex 很难解析更长的、更抽象的、系统级的代码。还有,Codex在讲操作施加在变量上时可能会发生错误。
从以上限制可以看出,Codex还是更倾向于“背代码”和做“代码组合”,而没有真正掌握多少编程知识,如果我们把编程能力分为:
1. 编程语言知识(语法知识、API功能等);
2. 逻辑推理能力(算法能力);
3. 利用已有代码的能力(掌握一些常用实现);
那么 Codex在前两点上的能力都比较弱,只在第三点上展现出了强大的能力,从这个角度来看,真正的专业的代码智能依旧任重而道远。
参考文献:
[1] Evaluating large language models trained on code, 2021.
[2] Codebleu: a method for automatic evaluation of code synthesis,2020.
[3] Unsupervised translation of programming languages,2020.
[4] Spoc: Search-based pseudocode to code,2019.
[5] https://link.zhihu.com/?target=https://www.github.com/openai/human-eval
[6] Measuring coding challenge competence with apps,2021.
[7] The curious case of neural text degeneration, 2020
原文:知乎
作者:于璠
推荐阅读