CodeBERT论文笔记

  1. CodeBERT笔记
    1. 摘要
    2. 1 介绍
    3. 2 背景
    4. 3 CodeBERT
      1. 3.1 模型架构
      2. 3.2 输入输出表示
      3. 3.3 预训练数据
      4. 3.4 预训练CodeBERT
        1. MLM
        2. RTD
      5. 3.5 CodeBERT微调举例
    5. 4 实验
      1. 4.1 自然语言代码搜索
      2. 4.2 探测任务
      3. 4.3 代码文档生成
      4. 4.4 生成不在预训练范围内的语言
    6. 5 结论

CodeBERT笔记

摘要

CodeBERT是一个针对PL与NL的双模态的预训练模型,CodeBERT基于Transformer,通过混合目标方法训练,预训练任务是替换词语检测(replaced token detection, RTD),即检测生成器中可行的替代样本。这个方法使模型既可以运用双模态数据(bimodal data,即NL-PL对)又可以运用单模态数据(unimodal data,即单纯的NL或PL)。双模态数据为模型训练提供输入的tokens,单模态数据便于生成器学习。CodeBERT在通过自然语言搜索代码任务和代码文档化任务中都取得了好结果。

光看摘要真的不知道是在干啥,接着往下看好了。

1 介绍

ELMo/GPT/BERT/XLNet/RoBERTa等一众预训练模型大幅提升了NLP任务完成水平。这些预训练模型通过MLM等自监督学习手段学习。预训练模型在NLP领域的成功也催生了别的领域内的预训练模型。

本论文介绍了CodeBERT的工作。CodeBERT是一个针对PL与NL的双模态预训练模型,CodeBERT能够抓住NL和PL的语义学联系,并生成可以用来完成各种关于NL-PL理解任务的向量表示。CodeBERT由多层Transformer构成。为了充分利用双模态和单模态数据,使用一个混合目标函数来训练。混合目标包含MLM和RTD(即首先使用一个生成器预测句中被mask掉的token,接下来使用预测的token替代句中的[MASK]标记,然后使用一个判别器区分句中的每个token是原始的还是替换后的)。单模态数据可以为替换词语检测训练更好的生成器,训练得越好就可以生成更好的替代词汇。代码包含GitHub代码仓库中的六种语言,不显式标注语言类型。

这一段讲的就稍微能理解一些了。

2 背景

首先是又介绍了一遍预训练模型(感觉已经介绍了三遍了)。

然后是讲了一下自己的模型与之前模型的不同。首先是既用了双模态数据又用了单模态数据,第二是与一个同时期的工作(Kanade et al., 2019)相对比有三点不同:①不止用了单纯的代码,还用了NL-PL结合的代码;②不止用了Python代码,一共用了六种不同语言的代码。③CodeBERT创新了训练方式,采用替换词语检测任务训练。

3 CodeBERT

本段正式介绍模型。

3.1 模型架构

采用与RoBERTa相同的模型架构,即多层双向Transformer。

3.2 输入输出表示

在预训练阶段,将句子表示如下:就是那个用来输出分类信息的token。按照Transformer中的常规操作,把词汇拆成wordpiece。直接把代码段当作token序列。

结合上次看的code2seq这个地方应该可以优化代码段的输入,用抽象语法树来表示代码段。

输出包括包含NL和PL在内每个token的向量表示。的向量表示作为整个序列的总向量表示。

3.3 预训练数据

还就那个双模态数据(NL-PL对)和单模态数据(单独的NL和PL),在论文里提过无数次了。

论文中此处提到了数据的来源及选取标准。

3.4 预训练CodeBERT

训练CodeBERT用到的两个任务:MLM和RTD。其中MLM用双模态数据,RTD用单模态数据。

MLM

输入:,其中是NL序列,是PL序列。随机选定中的一些位置用遮盖,遮盖率定为15%。

目标:预测覆盖住的token是什么。

RTD

输入:,其中是NL序列,是PL序列。这里有针对NL和PL的两个生成器,生成中可能的替代项。

目标:确定句子中的词语是不是原本的那一个。

备注:生成器的训练方式在这篇论文中并没有被特别强调。论文中就是用的简单的n-gram模型。用n-gram也能练双模态的生成器。当然也可以不用n-gram用Transformer,论文中并没有涉及这部分工作了。

上面这两个任务的损失函数都是交叉熵损失函数。

3.5 CodeBERT微调举例

以自然语言代码搜索为例,那就是先把自然语言过CodeBERT,然后把生成的向量过encoder-decoder架构。详情见下面实验部分。

4 实验

4.1 自然语言代码搜索

输入为一段自然语言,输出为一堆代码段中最符合语义的那段代码段。这应该是一个选择任务而非生成任务。

4.2 探测任务

本小节实验主要为了从一个小细节上去探究CodeBERT到底学了什么。因为预训练模型在fine-tuning上表现出色有可能是fine-tuning得好,并不一定就是这个预训练模型训得好,所以要设计任务看预训练模型本身是否学到了东西。

probing的目的是为了弄清楚在预训练模型的每一层中模型究竟学了什么。一般做法是设计一些简单的分类任务(一种任务包含一种特定的语言学信息),将 BERT 每一层的输出作为 embedding,在得到 embedding 后通过简单的分类器(例如一层的 FFN,为了不过多干扰模型本身的能力),来验证模型的不同层在不同 probing task 上的能力。

在本实验中,设定的NL-PL probing的目标是在一些干扰选项之间选择正确的masked token。一般而言,有两种干扰方式:其一是整个词汇表都用来生成干扰选项,第二种是根据专家的专业知识选取特定的一些干扰选项。此处选择第二种干扰方式。整个NL-PL probing其实就像是让电脑做完形填空。如果要看在NL上的表现,那就给完整的代码和遮盖过的文档(此处遮盖的词统一为最大(max/maximize)、最小(min/minimize)、小于(less)、大于(greater)),然后让电脑填空,看是否能填对。如果要看在PL上的表现也是一样,不过文中PL给了两种验证方式,一种是两边的文本都有,另一种是只有左边的文本(preceding PL codes)。

4.3 代码文档生成

代码文档生成任务的挑战在于训练CodeBERT时并没有做生成方面的任务,但最后结果居然是SOTA。

4.4 生成不在预训练范围内的语言

(讲真这个听起来就很nb)

这个的结果是比RoBERTa好但比code2seq差,也算是个好结果了。

5 结论

本文提出了第一个同时针对NL和PL的大型双模态预训练模型CodeBert,在双模态和单模态数据上训练CodeBert,并在两个NL-PL下游任务中进行fine-tuning取得了SOTA的结果,接下来的工作目标有:①使用更好的神经网络架构;②在预训练时增加生成任务;③将AST等结构融入特征提取的过程中;④尝试更多的下游任务。

script>