CC2Vec: Distributed Representations of Code Changes
论文概述
这篇文章主要做的是将源代码补丁(补丁:用来修复bug,更新API功能…),补丁通常包括2个部分:
- 日志信息:描述更新代码(code changes)的含义(semantics)
- 更新代码(code change):表示增加或删除的代码行(原文:The code change indicates thelines of code to remove or add across one or multiple files)
作者将这种代码向量化的方式应用在3个任务上:
-
日志信息生成(log message generation):根据更新的代码(code changes)进行日志信息生成,这样开发者就不必自己写文档 ,并且日志信息对软件更新过程的理解也是有重要的帮助。因此,这样任务志在根据修改的代码来生成简短的日志信息。
-
Bug修复补丁识别(Bug Fixing Patch Identification):为了应付新的需求,应用软件通常会不断更新,这也会招致新的bug的出现。当支持遗留代码库时,可能需要将错误修复向后移植到项目的旧版本。例如,Linux内核开发人员定期将最新版本的错误修复向后移植到仍受支持的旧版本。但是,旧版本的维护者可能会忽略最新版本中的相关补丁。所以,自动区分一个补丁是不是bug修复补丁十分重要(可能是增加新功能的补丁)。这项任务是一个二分类任务,输入更新代码(code change)和日志信息(log message),输出此次更新是否是为了修复bug。
-
实时缺陷预测(Just-in-Time Defect Prediction):这也是一项二分类任务,输入包含更新代码(code change)和日志信息(log message)的更新包(patch),输出该patch是否包含其它缺陷(defect)。
CC2Vec架构
架构图如下所示:
整体架构包括如下部分:
-
Preprocessing:这部分的输入是一个补丁的更新代码集合(code changes),输出一个list,list中每一个元素对应一个文件的增加或删除的代码(Outputs a list offiles. Each file includes a set of removed code lines and addedcode lines.)。
-
Input layer:将预处理部分输出的list当作输入,将list中每一个元素(file)编码成一个3维向量输出,那么整层的输出是一个list,每个元素是一个3维向量。
-
Feature extraction layers:该层用HAN模型(Hierarchical attention network)对list中每一个file的3维向量提取embedding vector。然后将list中的每一个embedding vector进行concatenated。得到code change vector。
-
Feature fusion layers and word prediction layer:将code change vector映射为从日志信息第一行中提取出来的word vector。
整个过程就是一个预训练过程,学习的是一个函数
f
:
P
→
Y
f: P \rightarrow Y
f:P→Y
其中,
y
i
∈
Y
y_i \in Y
yi∈Y 表示日志信息集合
Y
Y
Y中第
i
i
i 个patch的日志信息。
p
i
∈
P
p_i \in P
pi∈P 表示补丁集合
P
P
P 第
i
i
i 个补丁。
Preprocessing
给定修补程序的代码更改包括对一个或多个文件所做的更改。每个更改的文件都包含一组删除的代码和添加的代码。我们通过以下步骤处理每个补丁的代码更改:
-
Split the code change based on the affected files:首先将每个更改文件的代码更改信息分离到一个单独的代码文档中(即File1、File2等)。
-
Tokenize the removed code and added code lines:使用NLP的NLTK库,将其删除的代码行或添加的代码行解析为单词序列(token sequence)。我们忽略更改文件中的空行。
-
Construct a code vocabulary:基于训练集,我们建立了一个词汇库 V c V_c Vc。此词汇表包含出现在修补程序集合的代码更改中的代码token。
Input Layer
一个补丁的code changes可能包括对多个文件的更改;对每个文件的更改可能包含对不同hunks的更改;每个hunks包含删除和/或添加的代码行的列表。
为了保留这些结构信息,在每个更改的文件中,我们将删除(添加)的代码表示为一个三维矩阵 B ∈ R H × L × W B \in R^{H \times L \times W} B∈RH×L×W。
其中, H H H 是每个file中hunks的数量, L L L 是每个hunk中的删除(添加)代码行的数量,而 W W W 是受影响文件中每个删除(添加)代码行中的token数量。
分别用3维矩阵 B r B_r Br 和 B a B_a Ba 来表示移除代码和增加代码。
每个patch的修改文件数量可能不同,每个文件的hunks数量也可能不同,每个hunk中代码行数也可能不同,每个行的token数量也可能不同,因此需要padding和truncate操作。
那该部分的输出就是一个list,每个元素包含 B r B_r Br 和 B a B_a Ba 2个矩阵。
Feature Extraction Layers
特征提取层的任务是给定一个file的 B r B_r Br 和 B a B_a Ba 2个矩阵,来生成该file的code change vector。之后,一个patch(补丁)中的所有涉及到的file的code change vector进行 concatenated,生成该patch的code change vector。
架构图如下所示:
输入的
B
r
B_r
Br 和
B
a
B_a
Ba 均为
H
×
L
×
W
H \times L \times W
H×L×W 矩阵。
模型可分成2部分:
Hierarchical Attention Network.
该层次attention模型总共分为3层
- word sequence encoder + word-level attention layer
- line encoder + line-level attention layer
- hunk sequence encoder + hunk attention layer
架构图如下:
模型首先还包括了一个embedding层,为
∣
V
∣
×
d
|V| \times d
∣V∣×d矩阵,
∣
V
∣
|V|
∣V∣为词表大小,d为嵌入维度,嵌入过后
H
×
L
×
W
H \times L \times W
H×L×W 的矩阵就变成了
H
×
L
×
W
×
d
H \times L \times W \times d
H×L×W×d 矩阵
模型具体运算过程就不说了,其中用到了GRU。最终分别对 B r B_r Br 和 B a B_a Ba 生成 e r e_r er 和 e a e_a ea 向量。表示删除和新增代码的embedding向量。其中 e r , e a ∈ R n e_r, e_a \in R^n er,ea∈Rn
Comparison Layers
比较层的目的是是构建vector,以捕获给定补丁(patch)中受影响文件的删除代码和添加代码之间的差异。也就是给定一个file的 e r e_r er 和 e a e_a ea 向量,生成删除代码和添加代码之间差异的向量 e f e_f ef 作为该file的code change的最终向量表示, e f ∈ R n e_f \in R^n ef∈Rn。
文中也列举了几种比较方法
-
Neural Tensor Network
e N T = R e L U ( e r T ⋅ T [ 1 , . . . n ] ⋅ e a + b N T ) e_{NT} = ReLU(e_r^T \cdot T^{[1,...n]} \cdot e_a + b_{NT}) eNT=ReLU(erT⋅T[1,...n]⋅ea+bNT) T i ∈ R n × n T_i \in R^{n \times n} Ti∈Rn×n -
Neural Network
e N N = R e L U ( W ⋅ [ e a ⊕ e r ] ) e_{NN} = ReLU(W \cdot [e_a \oplus e_r]) eNN=ReLU(W⋅[ea⊕er]) W ∈ R n × 2 n W \in R^{n \times 2n} W∈Rn×2n
⊕ \oplus ⊕表示concatenated操作。 -
Similarity
e s i m = E U C ( e r , e a ) ⊕ C O S ( e r , e a ) e_{sim} = EUC(e_r, e_a) \oplus COS(e_r, e_a) esim=EUC(er,ea)⊕COS(er,ea)
E U C ( e r , e a ) = ∣ ∣ e r − e a ∣ ∣ 2 EUC(e_r, e_a) = || e_r - e_a ||^2 EUC(er,ea)=∣∣er−ea∣∣2
C O S ( e r , e a ) = e r e a ∣ ∣ e r ∣ ∣ ∣ ∣ e a ∣ ∣ COS(e_r, e_a) = \frac{e_r e_a}{|| e_r || || e_a ||} COS(er,ea)=∣∣er∣∣∣∣ea∣∣erea -
Element-wise subtraction
e s u b = e r − e a e_{sub} = e_r - e_a esub=er−ea -
Element-wise multiplication
e m u l = e r ⊙ e a e_{mul} = e_r \odot e_a emul=er⊙ea
⊙ \odot ⊙是按位乘法
最终Comparison Layers输出的向量 e f i = e N T ⊕ e N N ⊕ e s i m ⊕ e s u b ⊕ e m u l e_{f_i} = e_{NT} \oplus e_{NN} \oplus e_{sim} \oplus e_{sub} \oplus e_{mul} efi=eNT⊕eNN⊕esim⊕esub⊕emul
f i f_i fi表示该patch中的第 i i i 个file。
Feature Fusion and Word Prediction Layers
架构图如下:
该patch最终向量表示为
e
p
=
e
f
1
⊕
e
f
2
⊕
.
.
.
⊕
e
f
F
e_p = e_{f_1} \oplus e_{f_2} \oplus ... \oplus e_{f_F}
ep=ef1⊕ef2⊕...⊕efF
hidden layer和 word prediction layer分别用矩阵
w
h
和
w
o
w_h和w_o
wh和wo表示,该层的运算如下:
h
=
δ
(
w
h
⋅
e
p
+
b
h
)
h = \delta(w_h \cdot e_p + b_h)
h=δ(wh⋅ep+bh)
o
=
−
h
⋅
w
o
o = -h \cdot w_o
o=−h⋅wo
o
∈
R
∣
V
m
∣
×
1
o \in R^{|V_m| \times 1}
o∈R∣Vm∣×1
∣
V
m
∣
|V_m|
∣Vm∣ 表示日志序列词表大小
p
(
o
i
∣
p
)
=
s
i
g
m
o
i
d
(
o
i
)
p(o_i| p) = sigmoid(o_i)
p(oi∣p)=sigmoid(oi)
o
i
∈
o
o_i \in o
oi∈o 表示日志序列词表第
i
i
i个词。
预训练设置
采用最小化以下目标函数进行训练:
O
=
∑
y
i
∈
y
(
y
i
×
−
l
o
g
(
p
(
o
i
∣
p
)
)
+
(
1
−
y
i
)
×
−
l
o
g
(
1
−
p
(
o
i
∣
p
)
)
)
+
λ
2
∣
∣
θ
∣
∣
2
O = \sum_{y_i \in y} (y_i \times -log(p(o_i | p)) + (1 - y_i) \times -log(1 - p(o_i | p))) + \frac{\lambda}{2}||\theta||^2
O=∑yi∈y(yi×−log(p(oi∣p))+(1−yi)×−log(1−p(oi∣p)))+2λ∣∣θ∣∣2
y i ∈ 0 , 1 y_i \in {0, 1} yi∈0,1表示日志词表第 i i i个词 o i o_i oi是否出现在补丁p的日志序列中, θ \theta θ表示模型的所有参数
在其它任务上的实验
日志信息生成(log message generation)
任务目标是给定一个patch的code changes,来生成日志信息。实验证明该模型的BLEU-4分数高于state-of-art
Bug修复补丁识别(Bug Fixing Patch Identification)
给定一个patch的code changes和日志信息,输出该patch是否是用来修复bug。
state-of-art-approach:PatchNet
方法如下:
- 用CC2Vec提取code changes的vector
- 用PatchNet提取log messages和code changes的vector。
- 将上述向量concatenated
- 用PatchNet进行分类。
实验结果:
实时缺陷预测(Just-in-Time Defect Prediction)
给定一个patch的code change和 log message。判断该patch是否包含缺陷。
state-of-art-approach:DeepJIT
方法如下:
- 用CC2Vec提取code changes的vector
- 用DeepJIT提取log messages和code changes的vector。
- 将上述向量concatenated
- 用DeepJIT进行分类。
实验结果:
总结
作者提出了一种将code change(修改部分的代码)进行向量化的方法(CC2Vec),并分别在3个任务(相关文档生成,Bug修复补丁识别,补丁缺陷预测)进行了测试,和state-of-art方法进行整合,效果要好于state-of-art方法。
相关代码已开源:CC2Vec