代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理

作者 | 蒋宝尚 编辑 | 贾伟

BERT 自诞生之后,其应用边界便不断扩张,从自然语言到图像、语音等。过去的一年也因此被誉为“BERT 爆发的一年”。 近日,微软亚洲研究院周明、微软亚洲搜索技术中心蒋大兴、哈工大刘挺等人在 arxiv 上联合发表了一篇论文,标题为《CodeBERT: A Pre-Trained Model for Programming and Natural Languages》,再次拓宽了 BERT 的应用,将 BERT 应用到了 Python、PHP、Java、JavaScript、Go、Ruby 等编程语言的代码搜索和生成任务当中。

代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理

论文链接:https://arxiv.org/pdf/2002.08155.pdf

这篇论文提出了一个被称为「CodeBERT」的双模预训练模型,据作者介绍,这也是目前已知的第一个大型 NL-PL (自然语言-编程语言)预训练模型。该预训练模型能够处理 NL-PL 的普遍问题,例如用自然语言搜索代码、自动生成代码等。 所谓自然语言代码搜索,所要解决的问题是,如何通过自然语言 query 查找到所需的代码块,这和我们常用的搜索引擎(通过自然语言 query 来查找所需网页)类似。事实上,微软 Bing 在 2018 年便上线了类似的功能,在搜索框中输入自然语言 “convert case using a function of R”,便会返回一段 R 代码。代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理针对自然语言代码搜索,在这篇论文里,作者在 CodeSearchNet 语料库上对 CodeBERT 进行了预训练并做微调,这是一个包含了 6 种较为普遍的代码语言(分别为 Ruby、JavaScript、Go、Python、Java、PHP)的语料库。如下图所示,他们在自然语言代码搜索任务中取得了 SOTA 的结果:

代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理

而另一方面,代码文档生成任务,是未来能够极大节省程序员工作量的极具前景的一个研究方向。如下图所示,代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理针对这个任务,CodeBERT 也基本上都取得了 SOTA 结果,特别是相较于之前的 ROBERTa 模型,更是有显著的提高。 值一提的是,CodeBERT 有一大亮点,即尽管它只在 Ruby、JavaScript、Go、Python、Java、PHP 等代码语言上进行了预训练,但预训练的模型却可以泛化到其他代码语言任务上,例如 C#语言。

一、背景

BERT 作为一种双向 Transformer 的编码器,其对预训练方法的创新深受业界和学术界的喜爱,虽然其他大规模的预训练模型例如 ELMo、GPT 等已经能够在各种 NLP 任务中提升 SOTA。 但是上述提到的模型基本上都是面向自然语言处理,例如掩蔽语言建模、从未标记文本学习上下文表示。 相比以往的 Bert 的应用场景,作者另辟蹊径,推出双模态预训练模型,即兼顾 NLP 任务和 Python、Java 等编程语言。 具体来说,CodeBERT 抓住了自然语言和编程语言之间的语义联系,能够支持自然语言代码搜索等 NL-PL 理解任务以及一系列像代码生成这样的生成任务。代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理一个 NL-PL 对,其中红线框中的是 NL 文本,黑色框是 PL 文本。 为了利用 Nl-PL 对的双模实例(bimodal instances)以及大量可用的单模代码(unimodal codes),作者使用了混合目标函数来训练 CodeBERT,包括标准掩码语言建模和可替换 Token 检测。在具体的训练过程,作者用了六种编程语言在多语言 BERT 的设置中训练模型。 我们首先来看下 CodeBERT 的模型框架。

二、框架

在模型的整体架构上,CodeBERT 并未脱离 BERT 和 Roberta 的思想。和大多数工作类似,作者使用了多层双向 Transformer。更为具体一点,作者使用的模型架构与 Roberta-base 完全相同,即都有 12 层,每层有 12 个自注意头,每个头的大小是 64,隐藏尺寸为 768,前馈层的内部隐藏尺寸为 3072。模型参数的总数为 125M。 在预训练阶段,总共设计了两部分输入,一个是自然语言文本,另一个是编程语言的代码。对于自然语言文本将其视为单词序列,并拆分为 WordPiece。对于编程代码,将其看做 Token 序列。 CodeBERT 的输出也包括两个部分:1、聚合序列表示;2、有标记的上下文向量(contextual vector)。代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理数据集统计 训练 CodeBERT 所使用的数据集是 Husain 等人在 2019 年提供的最新数据集,里面包括 2.1M 双模数据和 6.4M 单码数据,其中双模码数据是指自然语言-代码对的并行数据,单码是指“未成对”的数据。 另外一些数据来自开源 Nonfork GitHub 仓库。对这些数据的处理是采用了一些约束和规则进行过滤。代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理可替换 Token 检测目标图解 在模型训练的设计上,其主要包括两个目标,其一是掩码语言建模,其二是可替换 Token 检测。在第二个目标中,作者进一步使用了大量的单模码数据。 目标一:掩码语言建模。将 NL-PL 对作为输入,随机为 NL 和 PL 选择位置进行掩码,然后用特殊的掩码 Token 进行替换。注意,掩码语言建模的任务是预测出被掩码的原始 Token。 目标二:替换 Token 检测。在这部分有两个数据生成器,分别是 NL 生成器和 PL 生成器,这两个生成器都用于随机掩码位置集(randomly masked positions)生成合理的备选方案。另外,还有一个学习生成器用来检测一个词是否为原词,其背后原理是一个二进制分类器,这里与 GAN 不同的是,如果生成器碰巧产生正确的 Token,则该 Token 的标签是“real”而不是“fake”。
代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理学习器的损失函数 经过调整后,损失函数优化如下:
代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理模型训练的最后一步是模型微调,具体操作是在 NL-PL 任务中使用不同的 CodeBERT 设置。例如在自然语言代码搜索中,会使用与预训练阶段相同的输入方式。而在代码到文本的生成中,使用编码器-解码器框架,并使用 CodeBERT 初始化生成模型的编码器。

三、实验

作者做了四个实验,分别是:1)将 CodeBERT 应用到自然语言代码搜索任务上,并与传统方法进行对比;2)进行 NL-PL Probing 实验,考察 CodeBERT 在预训练阶段到底学习了什么知识;3)将 CodeBERT 应用到生成任务当中;4)考察 CodeBERT 预训练模型的泛化能力,发现效果非常之好。

1、自然语言代码搜索

给定一段自然语言作为输入,代码搜索的目标是从一组代码中找到语义上最相关的代码。为了进行比较,作者选择了 Husain 等人在 2019 年发布的 CodeSearchNet 语料库进行训练。这个语料库框架如下图所示,共包含 6 中常见的编程语言(Python、JavaScript、Java、Ruby、PHP、Go)。
代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理在预训练阶段,作者首先对每种语言的数据集进行了训练。数据集分割如下:代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理在微调阶段,设置学习率为 1e-5,批量大小为 64,最大序列长度为 200,最大微调周期为 8,并使用 Adam 来更新参数,并从开发集中选择出表现最好的模型,并用于测试集上进行评估。 结果如下表所示:代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理性能相比于之前的 SOTA 模型 ROBERTa 取得了显著的提高。

2、NL-PL Probing

这部分实验主要研究在不更改参数的的情况下,Code BERT 能够学习哪些类型的知识。目前学界还没有针对 NL-PLProbing 的工作,所以在这部分实验中,作者自行创建了数据集。 给定 NL-PL 对,NL-PL Probing 的目标是测试模型的正确预测能力。模型比较结果如下图所示:代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理上表显示了正确预测实例的数量与全部实例数量的比例。可以看出,在各个变成语言的预测上,CodeBERT 基本都取得了最高的分数。但由于不同编程语言的数据集非常不平衡,因此用累计的数据进行比较更为恰当,在 PL 和 NL 的 probing 中,CodeBERT 的结果都要比 RoBERTa 高 10~20 个百分点。 也可以用一个具体的案例来对比下。下图案例中分别掩盖了 NL 和 PL 中的“min”:代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理上图为 RoBERTa 和 CodeBert 的预测概率 从结果来看,CodeBERT 在 NL 上的正确预测率为 60.6%,而在 PL 上直接高达 99.999%。

3、代码文档生成

这部分研究代码到文档的生成问题,并在六种编程语言中研究了生成任务在 Code Search Net Corpus 上的结果。 另外,为了证明 CodeBERT 在代码到 NL 生成任务中的有效性,作者采用了各种预训练的模型作为编码器,并保持了超参数的一致性。 实验结果如下:代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理在编程语言上进行预训练的模型的性能优于 ROBERTa

4、泛化能力

那么,在 Python、JavaScript、Java、Ruby、PHP、Go 这些语言上做的预训练模型能够应用到别的编程语言上吗? 作者拿着前面预训练出的 CodeBERT 模型在 C#语言上做了测试。 作者选择了 Codenn 数据集,这是一个包含 Stack Overflow 自动收集的 66015 对问题和答案的数据集,其规模相比 CodeSearchNet 语料库要小几个数量级。为了可靠地评估模型,作者通过人工方式,为测试集中的代码片段提供两个附加 titles 来扩展测试集。 模型评估标准采用平滑的 BLEU-4 分数,评估结果如下图:代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理从这个结果可以看出,相较于 RoBERTa,CodeBERT 能够更好地推广到其他编程语言。不过值得注意的是,模型的效果略低于 code2seq,作者认为原因可能是 code2seq 使用其抽象语法树 AST 中的组合路径,而 CodeBERT 仅将原始代码作为输入。
虽然作者也按照一定的顺序通过遍历 AST 的树结构来训练 CodeBert,但并不会带来生成任务的改进。这种结果意味着结合 AST 来改进 codebert 是潜在方向。

四、总结

如前面提到,微软的 Bing 在 2018 年便已经上线了代码搜索功能,可以预期,基于预训练的代码功能也将很快落实到 Bing 的产品当中,从而提供能加优质的服务。同时我们也可以期待,该项工作能够在近期开源,以让更多研究人员快速跟进这一工作。
我们用几句话来总结这项工作的意义: 1、据作者表示,CodeBERT 也是目前已知的首个大型的 NL-PL (自然语言-编程语言)预训练模型; 2、本文提出了一个混合学习目标,能够支持使用双模数据 NL-PL,且能够很容易地应用到单模数据中(例如没有自然语言文本的编程代码); 3、CodeBERT 在自然语言代码搜索和代码文档生成两个任务中都达到了 SOTA 性能,此外作者在实验中还建立了一个数据集来研究 NL-PL 预训练模型的探测能力,方便了以后跟进的研究人员。



代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理

代码也能预训练,微软 & 哈工大最新提出 CodeBERT 模型,支持自然-编程双语处理点击“阅读 原文” 前往 AAAI 2020 专题

来源链接:mp.weixin.qq.com