欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Assessing Generalizability of CodeBERT

最编程 2024-03-13 08:47:21
...

CodeBERT的通用性评估

周欣,韩东均,罗大卫

新加坡管理大学计算机与信息系统学院

xinzhou.2020@phdcs.smu.edu.sg, {dhan, davidlo} @smu.edu.sg

摘要

像BERT这样的预训练模型在许多自然语言处理(NLP)任务上都取得了很大的进步,显示出了其巨大的通用性。预训练模型在自然语言中的成功启发了编程语言的预训练模型。最近,提出了一种基于自然语言(NL)和编程语言(PL)的模型CodeBERT,并在代码搜索数据集上进行了预训练。尽管CodeBERT很有前途,但目前还没有在其预训练NL-PL任务数据集之外进行评估。此外,它目前只在两个接近预训练数据的任务上显示有效。这就提出了两个问题:CodeBERT能否超越其预训练数据?它能推广到涉及NL和PL的各种软件工程任务吗?

我们的工作通过对CodeBERT的通用性进行实证调查来回答这些问题。首先,我们评估CodeBERT对数据集的泛化能力,而不是它的预训练数据。具体来说,考虑到代码搜索任务,我们在另一个包含Python代码片段及其相应文档的数据集上进行实验。我们还考虑了另一个从Stack Overflow收集的关于Python编程的问题和答案数据集。第二,为了评估CodeBERT对各种软件工程任务的通用性,我们将CodeBERT应用于即时缺陷预测任务。我们的实证结果支持CodeBERT在额外数据和任务上的通用性。基于codebert的解决方案可以比专门为代码搜索和即时缺陷预测任务设计的解决方案获得更高或相当的性能。然而,CodeBERT的卓越性能需要权衡;例如,与专门的代码搜索方法相比,它需要更多的计算资源。

 

1.介绍

随着最近深度学习的成功,应用深度学习来自动化软件工程(SE)任务[1]-[4]变得流行起来。然而,深度学习解决方案通常需要兴趣对象的数值向量表示作为输入[3]-[11]。这种数值向量表示也称为嵌入。最近,有很多关于源代码嵌入的研究;他们学习了一段代码的嵌入[3],[4],[9]-[11]。尽管这些源代码嵌入模型在它们的专用任务中工作得很好,但是它们嵌入到其他下游任务的泛化需要进一步的研究。例如,Kang等人发现code2vec[9]提取的源代码token嵌入不能轻易用于其他下游任务,如代码注释生成、代码作者身份识别、代码克隆检测[12]。

近年来,在自然语言处理(NLP)领域,BERT[13]是学习自然语言单词嵌入的一个突破,BERT在许多NLP任务上的表现远远优于最先进的技术,如问题回答、自然语言推理、命名实体识别、情感二值分类、文本分类等。BERT是一种典型的预训练模型,具有跨多个数据集和下游NLP任务的通用性。灵感来自BERT的变体,即RoBERTa [20], Feng等人[21]提出了CodeBERT,一个针对源代码和自然语言文本的预训练模型。发布的CodeBERT模型是在Husain等人[22]提供的代码搜索数据集(CodeSearchNet)上进行预训练的,该数据集包含210万个双模态代码文档对和640万个单模态代码片段。

CodeBERT已经在两个基于自然语言和编程语言(NL-PL)的任务上进行了评估:代码搜索和代码文档生成[21]。然而,在验证过程中,他们使用了来自CodeSearchNet的数据。BERT算法在预训练数据集[23]-[25]以外的NLP数据上已得到了推广;然而,这并没有在CodeBERT中得到论证。这就引出了以下研究问题:

RQ1 CodeBERT可以泛化到其预训练数据之外吗?

为了回答RQ1,我们评估了CodeBERT预训练模型用于测试数据集上的代码搜索任务,而不是其预训练语料库。首先,我们在一个由Barone和Sennrich收集的[26]包含Python代码片段及其相应文档(即代码注释)的公共数据集上进行了实验。此数据集(代码-文档对)的性质与原始CodeBERT验证[21]中使用的数据集相同。其次,我们使用了另一个从Stack Overflow[27]收集的关于Python编程的问题和答案数据集。使用这个数据集,我们可以进一步测试CodeBERT对性质略有不同的NL- PL配对数据的泛化性(与最初评估中考虑的数据相比)。此外,虽然原始的CodeBERT评估将CodeBERT与通用模型(如CNN、RNN)作为基准进行比较,但在本研究中,我们使用专门为代码搜索设计的深度学习方法,即NCS[28]和UNIF[1]为基线。

我们的实验结果表明,CodeBERT在平均倒数排名(MRR)分数方面比所有的专门方法高出约31-38%,显示了其在不同数据集上的代码搜索任务的泛化能力。但是,我们也发现CodeBERT的更好性能是有代价的;在生成给定查询的代码段排序列表时,CodeBERT比基准慢8-23倍。

考虑到通用性的另一个方面,CodeBERT只在涉及自然语言和编程语言(NL-PL任务)的两个任务上进行评估:代码搜索和代码文档生成。BERT在许多不同的任务[13],[14],[14]-[19]上都得到了推广;然而,这并没有在CodeBERT中得到演示。这就引出了以下研究问题:

RQ2  CodeBERT可以推广到更多的NL-PL任务吗?

为了回答RQ2,我们在另一个NL-PL任务上评估了CodeBERT预训练模型:just-in-time (JIT)缺陷预测任务,即根据提交的NL描述和PL代码片段预测提交的缺陷。我们选择JIT缺陷预测任务是因为它是一个流行的任务(在许多以前的工作[2],[29]-[34]中研究过),与软件工程师有很高的相关性,并且涉及到源代码和自然语言。与原始CodeBERT评估中考虑的两个NL-PL任务相比,它的性质也有所不同:在代码搜索任务中,它以NL描述作为输入,生成代码;在文档生成任务中,它以代码作为输入,生成NL描述;在这个任务中,它接受NL描述和代码作为输入,并产生缺陷标签(即有缺陷的或干净的)。我们选择了两种最新的基于深度学习的方法,即: DeepJIT[2]和CC2Vec[34]作为基线。

我们实验的结果表明,一个简单的应用程序的CodeBERT附近的模型可以实现性能最先进的方法和表现方法发表的最近3.7 - -4.2%的AUC分数,这显示了普遍性的CodeBERT NL - PL下游任务。

我们工作的主要贡献是:

•我们评估CodeBERT在其超出预训练数据的工作能力方面的泛化能力,并提供证明其有效性的经验证据。

•我们评估CodeBERT在另一个流行的自动化软件工程任务(即时缺陷预测)上的通用性,并提供证明其有效性的证据。

•在我们的评估中,我们将CodeBERT与最近提出的用于代码搜索和即时缺陷预测的专门方法进行了比较。在原始论文中没有这种比较(CodeBERT只与通用解决方案进行比较,如CNN)。

本文的组织结构如下。第二节介绍了一些关于CodeBERT和下游任务的背景信息。第三节介绍了我们的实验来回答RQ1和他们的发现。第四节描述了我们回答RQ2的实验和他们的发现。第五节是我们的讨论。第六部分分析了效力的威胁。section7讨论相关工作。结论和未来的工作将在第8节提出。

 

2.背景

A. CodeBERT的预训练

CodeBERT是针对编程语言(PL)和自然语言(NL)[21]的一个双模态预训练模型。CodeBERT在Husain等人[22]提供的大规模代码搜索数据集CodeSearchNet上进行了预训练。虽然CodeSearchNet是用于代码搜索任务,但它对代码段的NL查询是代码段的文档,即代码注释,而不是程序员提出的实际问题。CodeBERT是考虑到两个目标的预训练。第一个目标是masked语言建模(MLM),它考虑masked编码和NLtoken[13]的生成。MLM被证明对于学习下游任务的一般化模型是有效的。第二个目标是Clark等人[36]提出的替换token检测(RTD),考虑替换代码和NLtoken的识别。CodeBERT使用双模态数据(NL-PL对)来为MLM目标训练模型,并使用单模态数据继续为RTD目标训练模型。

B. CodeBERT的微调

由于预训练模型是在其预训练数据集上训练的,因此训练后的模型可能不适合开头的特定下游任务[12]。要在特定的下游任务中使用预训练的CodeBERT,我们需要对该特定任务[13]、[21]的数据集微调CodeBERT。

在本研究中,我们主要利用CodeBERT进行分类,并简单地将CodeBERT作为一个句子编码器:将一个token序列(无论是NLtoken还是PLtoken)嵌入到一个固定维向量中(嵌入),该向量在输入时包含序列的语义。

由于CodeBERT要求特定的输入格式,我们需要对输入(NL或PLtoken序列)进行预处理:在整个序列的开始和结束分别添加特殊token[CLS]和[EOS]。此外,如果给定的数据是一对序列,则特殊token[SEP]应该添加在两个序列的中间,如图1所示。

 

图1所示。CodeBERT分类微调框架的示例。[CLS]和[EOS]是分别添加在整个序列开头和结尾的特殊token, [SEP]是添加在不同片段之间的分离token。我们在输出处使用[CLS]的token嵌入作为整个输入的CodeBERT嵌入。

为了进行分类,CodeBERT首先将输入(NL或PLtoken序列)编码为其CodeBERT嵌入。如图1所示,对于成对数据的任务(代码搜索中的NL-PL对),一对数据(两个NL或PLtoken序列)将被连接成一个序列。然后将这个串联的序列输入CodeBERT,以得到表示这一对的嵌入。在之前的工作[21]之后,我们使用[CLS]在输出处的token嵌入作为本研究中整个输入的嵌入。然后,它将嵌入信息提供给全连接层和softmax层(用于多类分类)或sigmoid函数(用于二值分类)来计算分类。然后根据分类与ground-truth之间的差距计算损失,如JIT缺陷预测中的缺陷标签(defective or clean),并将损失反向传播以调整CodeBERT和全连接层的参数。调整后的CodeBERT将更好地适合这些特定的数据集,并应用于测试数据。

C.下游任务

我们选择两个NL-PL下游任务来评估CodeBERT的通用性。

a)代码搜索:代码搜索任务给出一个自然语言查询作为输入,在候选代码段集合中找到与该查询语义最相似的代码段。代码搜索任务可以评估模型捕捉代码片段和NL文本之间语义相似性的能力。换句话说,就是区分语义相关的NL-PL对和不相关的NL-PL对的能力。在这个任务中,我们选择RoBERTa[20]和两个最近专门的基于深度学习的代码搜索工具:Neural code search (NCS)[28]和UNIF[1]作为基线。

b)即时缺陷预测:即时缺陷预测是根据从NL提交消息和PL代码更改[37],[38]中提取的特征来预测给定的提交是有bug的还是干净的。提交由代码更改和总结提交的提交消息组成。一次提交可能会同时修改项目中的几个不同文件,这意味着代码更改会分散到这些文件中。为了进行预测,必须考虑从不同文件中提取的特性,因为任何一个文件中的代码更改都可能给项目带来错误。我们选择了两种最近专门的深度学习方法,即DeepJIT[2]和CC2Vec[34]作为基线。

 

3.RQ1: codebert可以泛化超出它的预训练数据吗?

在这一部分中,我们评估了用于代码搜索任务的CodeBERT预训练模型,以测试其对数据集的泛化能力。

我们首先使用了由Barone和Sennrich[26]发布的Python代码片段的公共数据集和相应的文档(代码注释)

表1.代码搜索数据集的统计信息。Doc-Code数据集;此数据集具有与CodeSearchNet数据集相同的性质(代码-文档对)。在代码搜索的典型场景中,用户通常使用关于编程的问题,而不是输入代码注释作为查询。因此,我们还在另一个数据集上进行了实验,该数据集包含从Stack Overflow[27]收集的关于Python编程的问题和答案,称为问题-代码数据集。使用这个数据集,我们可以进一步评估CodeBERT是否可以泛化到问题-答案对的数据集。具体来说,对于问题代码数据集,我们使用了Yao等人[27]收集的单码答题贴子数据,它将一个(已接受的)答案中的完整代码片段与问题标题配对成问题-代码对。

A.数据集

我们在两个公开的数据集上进行了实验:Doc-Code数据集和Question-Code数据集。表1显示了这两个数据集的详细统计数据,包括文本的数量对训练、验证和测试集独特的token的数量在PL代码和本地语言文本,和常见的平均百分比独特的token之间共享代码片段和相应的本地语言文本(文件或问题)。在Doc-Code数据集中,一个代码片段与其对应的文档平均共享22.91%的惟一token。但是,对于Question-Code数据集,代码段和问题平均只有7.86%的唯一token具有共同点,这表明在token的共现方面,问题及其对应的代码段答案的相似性低于文档和代码段。

B.用于代码搜索的基线和codebert

许多基于深度学习的专门代码搜索方法[1],[22],[28],[39],[40]有两个单独的嵌入模块:代码嵌入网络将代码片段嵌入到向量中,文本嵌入网络将NL查询嵌入到向量中。这两个模块如图2所示。如果一个代码段和一个描述具有相似的语义意义,则它们的嵌入向量在联合嵌入空间中应该相互接近;对于不相关的代码和描述对,它们的嵌入应该彼此远离[39]。这个目标用排序损失函数[41],[42]表示:

其中c是代码段的代码嵌入,q+是代码段c对应描述的代码嵌入,称为正样本;q-是代码段c的负样本的嵌入,它是从描述池中随机选择的,不太可能与代码段c有相似的语义;P表示训练数据集。直观上,排名损失导致代码段与其对应的NL文本之间的余弦相似度得分较大,而代码段与不相关的NL描述之间的余弦相似度得分较小。

在本研究中,我们选择了两个最近发布的专门的代码搜索模型:NCS [28], UNIF[1]和一个NLP模型RoBERTa[20]作为基线。我们将简要介绍选定的基线和CodeBERT的使用。

图2。神经编码搜索(NCS)和UNIF框架

a) NCS:神经代码搜索[28]是用于代码搜索任务的无监督学习模型。对于输入层,NCS直接使用FastText[43]token嵌入。FastText类似于Word2Vec模型,它可以根据给定的语料库学习一个token级表示。对于代码嵌入模块,NCS简单地用对应的TF-IDF权值对代码片段的token嵌入求和:

 

其中ei是代码段中第i个token的嵌入,而tfidf(ei)是token ei的TF-IDF值.对于查询嵌入模块,NCS只计算查询中所有token嵌入的平均值。

b) UNIF: UNIF[1]是NCS的监督版本。对于代码嵌入,UNIF也直接利用FastText模型来初始化token嵌入。但UNIF在训练过程中调整了token嵌入。对于代码嵌入模块,他们没有使用TF- IDF值这样的固定权重,而是使用注意力机制[9]来计算注意力权重。注意力机制可以在代码片段中突出显示token的不同重要性。假设在代码段中嵌入了所有token,例如e1, e2,…, en, ei的注意权重αi的计算如下:

其中ac是根据损失函数训练的代码上下文向量。在获得权重αi后,该代码段的单个向量被计算为该代码段中所有token的加权平均值。对于查询嵌入模块,UNIF还将查询中所有token嵌入的平均值计算为查询嵌入。

c) CodeBERT: CodeBERT将代码搜索任务建模为一个二进制分类问题:给定任何代码-文档对,该模型识别该对是否在语义上相关。他们首先将代码片段和查询连接成一个序列:[CLS], w1, w2, ..wn, [SEP], c1, c2,…, cm, [EOS],其中[CLS]和[EOS]分别是添加在连接序列的开始和结束处的特殊token,[SEP]是添加在自然语言查询(w1, .... . wn)和代码片段(c1,…cm)之间的分离token。其次,他们将连接的序列输入到CodeBERT中,并将CodeBERT返回的[CLS]表示视为CodeBERT嵌入。然后将CodeBERT嵌入输入到全连通层和sigmoid函数中进行预测。由于CodeBERT和RoBERTa的框架是相同的,为了简洁起见,我们省略了RoBERTa的解释。

C.实验细节

训练-测试流程:每个代码搜索数据集的数据分割如表一所示。数据集分为训练集、验证集和测试集。我们对所有方法使用相同的数据分割,包括三个基线方法。在训练之前,NCS和UNIF需要FastTexttoken嵌入来初始化。我们使用FastText库1 在每个数据集上训练FastText模型,它可以将一个代码或NLtoken转换为相应的FastTexttoken嵌入。

NCS是一种无监督的代码搜索方法,它不学习代码或NL文本的嵌入,而是直接使用FastTexttoken嵌入的加权平均作为代码或NL文本的嵌入。对于监督代码搜索方法UNIF、RoBERTa和CodeBERT的训练,我们首先从每个数据集的训练集中,将相应的描述和从训练集中抽样的无关描述配对到训练集中的每个代码片段,建立一个正负平衡数据集。然后,UNIF在构建的平衡数据集上进行训练,以最小化排名损失函数。对于RoBERTa和CodeBERT,代码搜索任务被建模为一个二进制分类问题;具体来说,我们将这对代码及其对应的描述赋值为1,将这对代码及其抽样的无关描述赋值为0。因此,训练这两种方法来预测平衡数据集中每对数据的标签。

对于验证,与训练集类似,我们从每个数据集的验证集构建另一个正负平衡数据集。我们使用建立在验证集上的平衡数据集的验证损失来调优模型。我们使用网格搜索过程[44]对每个模型进行调优,考虑以下一组超参数及其参数取值范围:批处理大小为{8,16,32},学习速率为{5e−6,1e−5,5e−5}。我们选择导致最小验证损失的超参数组合。使用选定的超参数训练的结果模型用于测试集上的评估。

表二:在文档代码和问题代码数据集上的所有代码搜索模型的mrr分数

对于测试,评估在一个支撑测试集上执行,该测试集是每个数据集的测试集。我们对所有方法使用相同的评估方法。在评估测试集中的每个NL查询Q时,我们将对应的代码段C作为正样本,因为C与查询Q在语义上相关。为了评估模型区分语义相关的代码段与不相关的代码段的能力,我们从测试集中随机抽取K个不相关的代码段C -作为负样本,并将正确的代码段C与不相关的代码段混合。按照前面的工作[27][45],我们设K = 49。在本例中,对于每个查询,都有50个候选代码段,其中只有一个代码段在语义上与该查询相关。然后,我们将查询与所有候选代码配对,并将每一对输入到训练过的模型中,以获得预测分数。然后我们根据分数将所有代码片段从高到低进行排序。

评估指标:按照前面的工作[1],[27],[39],[45],模型的代码搜索性能通过在整个测试集T上的平均平均互反秩(MRR)指标[46]来评估:

 

其中ranki是第i个查询对应的代码段C在第i个查询的所有候选代码段中的排名。MRR分数越高,代码搜索性能越好。

D.结果与讨论

a) CodeBERT的通用性:对两个数据集进行代码搜索的结果如表2所示。对于Doc-Code数据集,CodeBERT比所有专门的代码搜索方法都有很大的优势:CodeBERT的MRR得分为0.98,这意味着对于大多数查询,它可以将正确的代码段排在首位。而NCS和UNIF两种专门方法的MRR分别为0.60和0.67。在Doc-Code数据集上,RoBERTa也取得了非常高的MRR评分0.97,略低于CodeBERT。

由于RoBERTa也可以获得如此高的MRR分数,这表明对于RoBERTa和CodeBERT等BERT变体来说,Doc-Code数据集似乎很简单。如表1所示,Doc-Code数据集包含代码和文档之间的许多常见token(平均22.91%),这使得预测两个token序列之间的关系更容易。

对于Question-Code数据集,CodeBERT的MRR得分比所有专门的代码搜索方法高出31%:CodeBERT的MRR得分为0.74,而NCS和UNIF的MRR得分均为0.43。在Question-Code数据集,RoBERTa的MRR为0.50,略好于NCS和UNIF。

在Question- Code数据集中比较CodeBERT和RoBERTa, CodeBERT比RoBERTa有很大的改进(MRR评分超过24%),表明CodeBERT在NL-PL任务上优于NLP预训练模型RoBERTa。此外,RoBERTa在Question-Code数据集上性能的显著下降表明,该数据集需要一个模型来捕捉代码和NL文本的更多语义,以便进行预测。

一般来说,CodeBERT可以比其他最近的专门代码搜索方法表现得更好,显示出它在不同数据集上的通用性,而不是用于代码搜索任务的预训练数据集。

b) CodeBERT的时间效率:虽然CodeBERT的性能优于专门的代码搜索方法,但在实践中,CodeBERT的时间效率是一个值得注意力的问题。我们在表III中报告了所有方法的训练(针对CodeBERT和RoBERTa的微调)和评估时间。我们在一台台式机上运行了所有的方法,并安装了Nvidia GeForce RTX 2080 Ti和Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz。

表3训练(训练)。和评估(EVAL)。以分钟为单位的编码和基线时间

在训练时间上,CodeBERT对Doc-Code数据集和Question-Code数据集分别需要231分钟和186分钟,这是UNIF的14-15倍。NCS的训练时间为零,因为它是一种不需要训练的无监督方法。在评估时间方面,CodeBERT需要的评估时间是NCS的8-9倍,需要的评估时间是UNIF的18-23倍。此外,在训练和评估方面,CodeBERT比RoBERTa略快。

RQ1主要发现:CodeBERT可泛化到数据集,而不是用于代码搜索任务的预训练数据集。CodeBERT在这两个数据集上的表现始终优于最近专门的基于深度学习的代码搜索方法。CodeBERT模型在平均互反秩度量方面的改进范围在31%到38%之间。然而,Code- BERT的卓越性能是有代价的;CodeBERT需要比专用代码搜索基线多8-23倍的时间来生成给定查询的输出。

 

4.rq2: codebert可以推广到更多的nl-pl任务吗?

在本部分中,我们在另一个NL-PL任务上评估了CodeBERT模型。 just-in-time缺陷预测,并将其与最近的专门方法进行比较,以评估其对NL-PL任务的通用性。我们的目的是研究CodeBERT是否可以将知识从代码搜索数据集(CodeSearchNet)转移到即时缺陷预测的任务。

我们选择即时缺陷预测任务有三个原因。首先,即时缺陷预测是一个热门的任务,已经在许多以前的工作[2],[29]-[34]中进行了研究。此任务可以潜在地在早期阶段(甚至在将新的代码更改引入代码库之前)识别缺陷,这是最佳的成本节约实践[2]。第二,这个任务涉及到两个源代码,即代码更改和自然语言,即提交消息,这适合于评估CodeBERT对特定NL-PL任务的通用性。第三,这个任务与被评估的两个NL-PL任务不同。代码搜索任务根据代码与NL描述之间的语义相似性,检索给定NL描述的代码作为输入。文档生成任务生成作为输入的给定代码的NL描述。该任务根据提交的NL描述和PL代码片段作为输入来预测提交的缺陷。

A.即时缺陷预测

即时(JIT)缺陷预测是根据从提交消息和代码更改[37],[38]中提取的特征来预测给定的提交是否有bug的任务。这项任务的核心部分是设计一个特征提取模型,它可以适当地捕获提交的错误概率。机器学习技术和精心手工制作的特征是JIT缺陷预测[29]-[32]的常见解决方案。最近,一些作品使用深度学习技术,如深度信念网络[33]和卷积神经网络(CNN)[2],解决了JIT缺陷预测问题,并在很大程度上超越了手工制作的特征。我们选择了两种基于深度学习的方法作为基线:DeepJIT[2]和CC2Vec[34]。DeepJIT使用一个特定的CNN[47]从代码更改中提取特征并提交消息[2]。CC2Vec是一个基于深度学习的模型,它学习由日志消息[34]指导的代码更改的分布式表示。

B.数据集

我们使用了Hoang等人使用的相同数据集[34]:OpenStack数据集和Qt数据集,这些数据集分别是由McIntosh和Kamei[48]从OpenStack和Qt软件项目中收集的提交。Qt数据集包含25,704次提交,其中有1,825次提交有缺陷。OpenStack数据集总共包含13,304个提交,其中1,627个提交有缺陷。我们使用在CC2Vec2的复制包中发布的数据,并使用原始数据分割。对于OpenStack数据集,训练集包含11,973次提交,而测试集有1,331次提交。对于Qt数据集,训练集包含23,133次提交,测试集有2,571次提交。

C.基线

图3.DeepJIT框架

1) DeepJIT: DeepJIT是一个端到端深度学习框架,首先以固定维词嵌入表示代码token和提交消息词,然后使用CNN自动提取代码更改嵌入和提交消息嵌入的特征[2]。由于代码更改和提交消息都被视为输入,因此JIT缺陷预测模型与自然语言和编程语言都有关。在DeepJIT中,提交消息和代码更改token的嵌入是在训练过程中随机初始化和调优的。初始化之后,提交消息和代码更改中的所有token都转换为固定维向量。如图3所示,对于一个提交消息(一系列token),DeepJIT使用文本分类任务[47]的特定CNN模型将该提交消息中所有token的嵌入融合到一个向量中,称为提交消息向量。

由于代码更改是代码行列表,DeepJIT使用CNN以分层的方式将代码更改融合为单个向量。首先,对于所有代码行,DeepJIT使用CNN模块将代码行(代码token序列)嵌入到表示该行的向量中。在第一个CNN模块完成后,将代码更改中的所有代码行嵌入到vector中。然后,DeepJIT使用第二个CNN模块将所有代码行的所有嵌入融合成一个向量,称为代码变化向量;这个向量表示这次提交的整个代码更改。最后,模型连接用于代码更改和提交消息的向量,并将连接的向量提供给一个完全连接的层,以便进行预测。

2) CC2Vec: CC2Vec是一个端到端深度学习模型,学习由日志消息中的单词监督的代码变化的分布式表示。CC2Vec的训练目标是学习代码更改的嵌入,该代码更改可以预测单词词汇表中的单词是否存在于其日志消息中。由于日志消息是对相应代码变更语义的描述,这个训练目标可以强制学习到的代码变更的嵌入来捕捉代码变更的语义[34]。CC2Vec使用层次注意网络(HAN)[49]作为特征提取层,将代码更改中所有token的嵌入聚合为单个向量,以捕获整个代码更改的语义意义。CC2Vec不是一个为即时缺陷预测而设计的模型,而是一个学习代码更改表示的模型。然而,Hoang等人[34]将CC2Vec提取的特征与原来的DeepJIT进行了拼接,以提高DeepJIT的性能。

D.使用CodeBERT进行JIT缺陷预测

为了在即时缺陷预测任务中使用CodeBERT,我们需要为JIT缺陷预测设计一个模型。此后,我们将其简称为CodeBERT4JIT。正如我们前面提到的,CodeBERT本质上是一个句子编码器,它将一系列token(句子)编码为单个向量,这个向量包含句子的语义。此外,CodeBERT是一个双模态的句子编码器,可以对代码行和NL描述进行编码。

受DeepJIT框架的启发,如图3所示,我们发现对于代码行和提交消息(对于代码行和提交消息的卷积网络),第一层的cnn是编码器,它将一个句子(即一个序列的NL或PLtoken)到一个单一的向量。这些cnn的功能与CodeBERT相同,即将序列编码为包含该序列语义的向量。因此,我们只需将DeepJIT中的CNN句子编码器替换为CodeBERT模型,构建一个用于即时缺陷预测任务的CodeBERT模型的简单应用。我们不设计一个复杂的网络,因为我们只是想看看CodeBERT在这个任务上的能力和通用性。复杂的设计使得在即时缺陷预测任务中调查CodeBERT的有效性变得更加困难。

给定一个提交消息m,它是一个NL token序列[w1, w2,…, w|m|],我们把它输入到CodeBERT中,得到它的句子嵌入:

其中Zm∈Rd是该提交消息的CodeBERT嵌入,d是该提交消息的嵌入输出的维数。代码更改C包含(可能不同的)源代码文件[C1, C2,…, C|C|]中更改的代码行,Ci代表第i个源代码文件中所有更改的代码行。

我们的目标是将这些代码行嵌入到一个向量ZC中,它包含了代码变化C的语义含义。

我们首先将所有代码行连接到一个源代码文件(Ci)变成一个句子,然后将这个连接起来的句子输入CodeBERT以获得它的句子

 

其中ZCi∈Rd是代码文件Ci的CodeBERT嵌入。对于每次代码变更的代码文件,即Ci,我们计算其CodeBERT嵌入向量ZCi .然后将这些嵌入向量输入到CNN模块和最大池化层,形成一个嵌入向量(ZC),用于代码更改C。

为了融合不同文件的嵌入,我们利用一个filter f∈Rk×d对一个包含k个文件的窗口计算一个新特征:

 

其中*是各元素乘积的和,α(.)是非线性激活函数,bi∈R是偏置值。过滤器f应用于代码变更的每个k文件。然后将生成的特征连接起来,形成一个向量X,这样:

 

在之前的工作[2]之后,对于每个filter,我们然后使用一个最大池化层[50]来处理特征向量X,以获得最大值:

 

在获得提交消息和代码更改的嵌入后,我们将这两个嵌入连接起来以生成一个特征表示,即Z表示这个提交:

 

其中⊕为拼接运算符。最后,将提交的向量Z通过sigmoid函数送入全连接(FC)层,将高维向量映射到一维向量,计算对这个二元分类任务的预测:

 

式中·为点积,wh为FC层的权矩阵,bh为偏置项,α(.)为非线性激活函数,sigmoid(.)为对预测进行归一化的函数。

E.实验细节

训练-测试流程:我们使用CC2Vec复制包发布的数据,将数据集分为训练集和测试集。我们使用训练集10%的数据进行验证。本研究中所有方法的数据分割是相同的。

为了训练两个基线,我们在复制包中使用脚本,并在包中使用默认的超参数。对于训练CodeBERT,我们设置学习速率为1e−5,batch大小为8,最大序列长度(序列中考虑的最大token数量)为120.我们使用Adam优化器来更新模型参数。因为JIT缺陷预测数据集经常有不平衡的问题:只有少数的提交是有bug的,而大量的提交是干净的,所以我们使用与前面的工作[2]相同的损失函数来解决这个问题。

为了验证,我们通过观察使用验证集中的一组超参数值训练的模型的性能来调整每个模型的超参数。具体地说,我们使用网格搜索过程对超参数进行优化,参数集及其可能值如下:学习率在{5e−6,1e−5,5e−5},最大序列长度在{100,120,150}。我们选择超参数组合,从而在验证过程中获得最佳性能。在验证过程中表现最好的模型用于对测试集进行评估。

对于测试,在前面的工作[2]之后,在保留测试集上执行评估,这些测试集是已发布数据集的测试集。对于每种方法(CodeBERT4JIT、DeepJIT和CC2Vec),我们将测试集中的所有提交都输入所选的模型(验证过程中确定的性能最好的模型),以获得预测(有缺陷的或完整的),并根据预测和基本事实计算AUC分数。

评估指标:类似于之前的研究[2]、[34]、[48],为了评估JIT方法的有效性,我们使用接收者操作符特征曲线下的面积(AUC),这是一个与阈值无关的指标,用来衡量他们区分缺陷提交或良性提交的能力。AUC的值在0(最差预测)和1(最佳预测)之间。

F.结果与发现

a) CodeBERT的通用性:从表4可以看出,使用CodeBERT的方法在OpenStack数据集和Qt数据集上的AUC分数分别比使用DeepJIT的专门方法提高了0.037和0.042。尽管这个简单的CodeBERT应用程序在JIT缺陷预测上不能在所有数据集上超过最先进的方法CC2Vec,但CodeBERT在OpenStack数据集上取得了与CC2Vec相当的结果。CC2Vec是专门为代码变化而设计的,而CodeBERT不是为这个任务设计的,CodeBERT的出色性能表明了它对这个特定的NL-PL任务的通用性。

我们还使用消融测试[51]、[52]来分析提交数据的每个组件的贡献,只在提交中输入代码更改或提交消息,并分别评估它们的AUC分数。如表5所示,对于预测有缺陷的提交,提出的模型从代码更改中比从提交消息中获益更多。对于代码改变,CodeBERT4JIT在OpenStack数据集和Qt数据集上的AUC分数分别为0.775和0.771,而在只有commit消息的情况下只能得到0.731和0.726。 

表4即时缺陷预测的auc结果

 

表5使用不同数据部分的不同模型的Auc分数

RQ2主要发现:一个简单的codebert (CodeBERT4JIT)在AUC方面比专门的DeepJIT方法表现3.7-4.2%,并在OpenStack数据集上达到了与最先进的方法相当的性能,表明它对这个特定的NL-PL任务的通用性。

 

5.讨论

A:经验教训

从上面的两个任务中,我们可以看到,预训练的Code- BERT模型在NL-PL任务中是很有前途的。在代码搜索任务中,优化后的CodeBERT模型优于专门的方法。在平均倒数排序指标方面,前者为31%,后者为38%。在JIT缺陷预测任务的情况下,经过微调的CodeBERT在OpenStack数据集上达到了与最先进的方法(CC2Vec)相当的性能。尽管在Qt数据集上,CodeBERT的性能比最先进的方法差,但在接收算子特征曲线度量方面,它仍然在两个数据集上比最近的专门方法DeepJIT的性能一致地高出3.7-4.2%。基于这些有希望的结果,我们鼓励研究人员在未来涉及NL-PL任务的工作中,考虑对预训练的CodeBERT方法进行微调,作为基线。

在JIT缺陷预测任务中,尽管CodeBERT比DeepJIT取得了改进,但CC2Vec给DeepJIT带来的改进比CodeBERT更大。与CodeBERT在代码搜索任务上的优越性能相比,在JIT缺陷预测任务中相对较差的性能表明,CodeBERT可能无法完全适应与它预训练的数据集不同的数据集。Howard和Ruder之前在NLP域[53]中提出过这个问题。他们认为,即使在预训练使用非常不同的语料库,目标任务的数据也可能来自不同的分布。为了缓解这一问题,他们提出了任务自适应微调,利用任务训练数据上的预训练目标对预训练模型进行微调。在他们的实验中,这种方法为预训练的模型带来了显著的改进。因此,研究人员可以考虑任务自适应微调方法,进一步微调预训练的CodeBERT模型在特定的任务数据集与预训练的目标:MLM和RTD。建议首先使用预训练目标微调下游任务数据集上的CodeBERT。

B.计算时间方面的挑战

尽管CodeBERT在上述两个任务中表现出了良好的性能,但在实践中,CodeBERT的时间效率可能是一个值得注意力的问题,特别是对于与检索相关的任务,如代码搜索。

在执行实际的代码搜索任务时,对于搜索语料库中的每个查询都有数百万个候选代码段,代码搜索的CodeBERT解决方案在计算时间方面是昂贵的。为了说明这个问题,我们报告了当搜索空间为10,000(即1个正确的代码段,9999个不相关的代码段)。在这种情况下,CodeBERT为一个查询提供建议需要34.2秒,比NCS和UNIF慢9-24倍。Teevan等人[54]发现,稍微慢一点的检索可以导致结果感知质量的显著下降。Eric和Jake[55]还发现,将谷歌中的结果页面的加载时间增加100毫秒可能会导致人均搜索次数的减少。对于CodeBERT,一个查询34秒的等待时间对用户来说可能是不可接受的,并且随着搜索语料库中代码片段数量的增加,等待时间会增加。

CodeBERT计算时间的另一个问题与框架有关。假设我们有A个查询等待一个代码搜索系统回答,该系统在搜索语料库中有B个代码片段。注意,A和B都可以是一个很大的数字。对于NCS、UNIF等专门的代码搜索方法,它们分别有代码嵌入网络和文本嵌入网络,如图2所示。在这种情况下,他们只需要对训练模型的B代码段进行一次编码,并存储所有的代码段嵌入。当出现新的查询时,他们可以通过训练模型对新查询进行一次编码,并计算该查询的嵌入与所有存储的代码片段嵌入之间的余弦相似度得分。因此,对于NCS和UNIF等专门的代码搜索方法,它们只需要将所有查询和代码片段分别编码为相应的嵌入,总共为A+B次。

然而,在CodeBERT框架中,代码片段和相应的查询被连接到一个单独的序列中,该序列作为一个整体提供给CodeBERT模型。在这种情况下,我们不能将代码的嵌入过程和查询分开。通过该实现构建一个实际的代码查询系统,需要对a × B次进行编码,以得到所有可能的代码查询对的嵌入。

 

表6当搜索空间为10,000时,一个查询的评估时间(以秒为单位)

有一些潜在的解决方案可以缓解这个问题。例如,为了降低代码搜索任务中CodeBERT的时间复杂度,我们可以用CodeBERT模型将PL代码片段和NL描述分开编码,而不是用NL-PL对连接起来的句子进行编码。通过这样做,CodeBERT需要为A查询和B代码片段编码的次数从A×B(代码和查询之间的所有可能对)下降到A+B(代码和查询分别)。然而,这种想法的有效性需要在未来的工作中进一步研究。

Nogueira和Cho也在[15]之前的文档检索任务中解决了这个问题。他们提出了一种分两步的方法来缓解这个计算时间问题:首先,使用BM25等标准快速机制对大量可能与给定问题相关的文档进行排序,以缩小搜索空间[15];第二,他们使用了更强大但计算更密集的方法,例如BERT,对第一步返回的前K个文档重新排序。在MRR@10[15]方面,他们的方法比以前的技术水平高出27%(相对)。在未来的工作中,我们将继续探索一种可行的代码搜索方法,这种方法结合了像CodeBERT这样强大的模型和像NCS这样有效的模型。

 

6.有效性的威胁

内部有效性的威胁。内部效度的威胁与可能影响我们结果的因素有关。在代码搜索任务中,我们将CodeBERT与最近发布的用于代码搜索任务[1]和[28]的两种方法进行了比较。由于这两种方法的源代码没有公开,我们需要重新实现这些技术的版本。我们尽了最大的努力去遵循他们最初作品中的描述。我们发布了一个复制包供其他人检查。对于JIT缺陷预测任务,我们在基线的复制包中使用脚本,并在复制包中保持默认的超参数不变。

外部有效性的威胁。外部效度的威胁与我们研究结果的普遍性有关。我们用四个公开的数据集对两个不同的NL-PL任务进行了实验,这些数据集在规模、查询类型、每个数据项的PL代码数量等方面都存在差异。这两个NL- pl任务也具有不同的性质:代码搜索以NL描述作为输入并生成代码片段;JIT缺陷预测将NL描述和PL代码片段作为输入,并预测一个缺陷标签。虽然我们试图确保数据集和任务的多样性,但我们的发现可能不能推广到所有的NL-PL任务和数据集。我们在其他NL-PL任务中的结果还需要进一步的研究来证实。此外,在这项工作中,我们只注意力整个代码片段或代码-文本对的嵌入。换句话说,我们使用CodeBERT将NL/PLtoken序列嵌入到单个向量中,以表示这个序列。我们还没有研究CodeBERT嵌入的其他粒度,例如,源代码token的嵌入。

构建有效性的威胁。构建有效性的威胁与我们选择的评估指标有关。对于代码搜索任务,我们遵循前面的工作,使用平均平均倒数秩(MRR)度量[1],[27],[39],[45]。对于JIT缺陷预测,我们跟随Hoang等人[2],[34]使用接受者算子特征曲线(AUC)下的面积,这是一个推荐用于评估缺陷预测模型[56]的鉴别能力的性能度量。

 

7.相关工作

最近,在自然语言处理(NLP)领域,BERT技术在许多NLP任务[14]-[19]上的表现都大大超过了word2vec技术,显示了其优于word2vec的优势。受自然语言[13]预训练嵌入成功的启发,Feng等人提出了CodeBERT[21],一个用于NL和PL的预训练模型。在本文中,我们研究了CodeBERT在额外数据集和额外下游任务方面的泛化性。相比之下,最先进的方法是专门为下游任务设计的。

有一些PL或NL-PL数据的预训练模型。Kanade等以掩码语言建模和下一句预测为预训练目标,在Python源代码上训练BERT模型,即CuBERT[57]。作者在几个下游任务上评估了CuBERT,表现优于一般基线,如LSTM和transformer。Svyatkovskiy等人提出了code (GPT-C)[58]的多层生成式变压器模型,该模型是GPT-2[59]的变体。GPT-C是在大型无监督源代码数据集上从头开始训练的。Buratti等人提出了一种基于转换器的语言模型,称为C- bert[60],它是在GitHub上排名前100的C语言库中预训练的。C-BERT在抽象语法树(AST)token任务和漏洞识别任务中取得了良好的性能。Guo等人提出了一种考虑代码固有结构的编程语言预训练模型[61]。他们使用掩码语言建模、边缘预测和节点对齐作为预训练目标,并在一些下游任务(如代码精化和代码翻译)上表现优于基线。

Lachaux等人利用跨语言模型(XLM)提出了神经源到源翻译器[62]。他们的目标是基于无监督原则(如XLM)设计一种更好的方法,而我们的目标是评估CodeBERT的通用性。Mastropaolo等人研究了预训练的文本到文本转换转换器(T5)模型泛化到几个下游任务的能力,如自动bug修复和代码总结[63]。在我们的工作中,我们评估了CodeBERT在不同的下游任务上的通用性,这些任务没有被他们的工作覆盖。T5或CodeBERT哪个更好还不清楚,这可能需要在未来进一步研究,并且超出了本工作的范围。最近,Tian等人评估了预训练BERT模型在程序修复任务中预测补丁正确性的有效性[64]。他们评估了一个BERT模型在NL文本上训练,而我们评估了一个CodeBERT模型在NL和PL数据上训练。此外,他们的工作主要集中在程序修复任务上,而我们的工作主要集中在另外两个NL-PL任务上。另一项与我们的工作并行开展的密切相关的工作是Lu等人的名为CodeXGLUE的工作,该工作在他们的arXiv手稿中有描述[65]。他们工作的主要贡献与我们不同;当我们评估CodeBERT模型的通用性时,他们为10个编程语言任务创建基准数据集。尽管如此,作为他们研究的一部分,他们在几个任务中评估CodeBERT。我们的工作在以下几个方面与他们不同:(1)CodeXGLUE使用了通用的深度学习模型(如BiLSTM)作为基线,而我们选择了每个任务的最先进的方法作为基线。因此,我们的研究可以阐明CodeBERT是否能与最先进的技术竞争。(2)他们没有考虑本工作中考虑的即时缺陷预测任务。他们的发现和我们的发现相互补充,为cobert等预训练模型的有效性提供了经验证据。

 

8.结论与未来工作

在本工作中,我们通过实验研究了CodeBERT的泛化性,它是第一个预训练的自然语言(NL)和编程语言(PL)模型。我们的实验包括代码搜索任务和即时缺陷预测任务。对于代码搜索任务,我们的实验结果表明,微调后的CodeBERT模型在不包含在其训练数据集和具有不同查询类型的数据集上的MRR指标比专用代码搜索方法高出31%到38%。实验结果表明,CodeBERT对训练数据集之外的更多数据具有较好的推广性。

对于即时缺陷预测任务,一个简单的CodeBERT应用程序在一个数据集上取得了相当的性能,并且在AUC分数方面比最近的专门方法高出3.7-4.2%。这表明CodeBERT可泛化到特定NL- PL任务的数据集,尽管任务和数据集具有不同的评估任务性质:代码搜索和代码文档生成。在未来,我们将对以下几个方向感兴趣:(1)研究CodeBERT模型对其他SE任务(例如bug定位)的有效性,(2)应用CodeBERT模型来改进进一步下游任务的基线。