Page 231 - 《软件学报》2021年第7期
P. 231
牛长安 等:基于指针生成网络的代码注释自动生成模型 2149
源代码不同位置分配不同的 Attention 权重.这两个作用对输入是未经过分解的源代码来说是可行的,因为此时
的输入与 AST Encoder 的输入 AST 是完全对应的.但是,Hybrid-DeepCom 为了丰富语义信息对标识符进行了分
解,破坏了源代码的结构,使得 Code Encoder 和 AST Encoder 的输入不再完全对应,在增强了 Code Encoder 第 1
个作用的同时,不可避免地削弱了第 2 个作用.在一定程度上,这两个任务是互斥的,因为要丰富语义信息就需要
对标识符进行分解,这样就破坏了语法结构信息;而保留语法结构就会将大量的标识符变成“UNK”标签,丢失
了语义信息.因此,Code Encoder 无法同时在两个任务上达到最佳性能,只能在训练中找到两者中间的平衡点.
为了同时发挥两个任务的最优性能,CodePtr 引入了一个新的编码器将这两个作用分开,将第 2 个作用通过
Source Encoder 实现,Source Encoder 将分解前的源代码序列作为输入,与 AST Encoder 的输入完全对应,使结构
信息发挥更大的作用,同时也使 Code Encoder 更好、更专注地发挥提取语义信息的作用.由于第 2 个作用主要通
过 Attention 权重实现,并且,由于分解后的语义信息包含分解前的语义信息,为了避免冗余以及过多“UNK”标签
的干扰,我们将不把 Source Encoder 的最终隐藏状态传给解码器,而只在解码阶段使用其对应的 Attention 权重.
因此,CodePtr 有 3 个编码器:输入为源代码序列的源代码编码器(source encoder)、输入为分解后源代码的
代码编码器(code encoder)和输入为 SBT 序列的 AST 编码器(ast encoder).
Source Encoder 用于对未分解的源代码进行编码,一方面是由于上述原因,另一方面是为指针生成网络提
供 Attention 权重,因为分解过后的源代码序列中标识符已被分解,无法提供整个单词的 Attention 权重.对于长度
s
s
为 T s 未分解的源代码序列 X xx s s , 编码器将其编码为隐藏状态,对于时刻 t,Source Encoder 接受当前时
, ,..., x
1 2 s T
s
s
s
,
刻的未分解的源代码序列输入 x 将上一时刻的隐藏状态 h 更新为 h 即
,
t t 1 t
s
s
h f (,x h s ) (1)
t s t t 1
其中,f s 在本文中采用了 GRU 单元,由此可以得到 Source Encoder 的所有隐藏状态 output [,h h s ,...,h s ].
s
s 1 2 s T
分解过后的源代码序列相对于分解前的代码序列而言,大大降低了 OOV 词的比例,可以使编码器学习到更
丰富的语义信息,因此,Code Encoder 十分必要,对于长度为 T c 未分解的源代码序列 X xx c ,..., x c , 在 t 时刻,
c
c
,
1 2 c T
c
c
c
,
,
Code Encoder 接受分解后的代码序列输入 x 将上一时刻的隐藏状态 h 更新为 h 公式如下:
1
t
t
t
c
c
h f c (,x h t c 1 ) (2)
t
t
其中,f c 同样采用了 GRU 单元,得到 Code Encoder 的隐藏状态 output [,h h c 2 ,...,h c c T ].
c
1
c
对于结构性强的语言,源代码的结构信息同样重要,Java 代码对应的 AST 可以反映源代码的结构信息,而
Hu 等人在文献[12]中提出的 SBT 遍历方法可以无损地将 AST 树形结构转换为序列.因此,AST Encoder 将源代
码对应的 AST 的 SBT 序列作为输入,使 CodePtr 可以提取到源代码的结构信息.对于长度为 T a 的 SBT 序列
t
a
a
a
X x a , x a ,..., x a , AST Encoder 在 t 时刻接受 SBT 序列输入 x 将上一时刻的隐藏状态 h 更新为 h 即
,
,
1 2 a T a t 1 t
a
a
h f (,x h a ) (3)
t a t t 1
其中,f a 为 GRU 单元,得到 AST Encoder 的隐藏状态 output [,h h a ,...,h a ].
a
a 1 2 a T
3 个编码器中,Source Encoder 是专门为指针生成网络添加的编码器,Code Encoder 和 AST Encoder 可以同
时学习到源代码的语义信息和结构信息.对于多编码器的最终隐藏状态,CodePtr 在传入解码器之前使用了一个
维度映射层 ReduceHidden.
2.3 维度映射层ReduceHidden
在经典的 seq2seq 网络 [18] 中,编码器最后时刻的隐藏状态作为解码器的初始隐藏状态,这样可以使编码器
提取到的信息传递给解码器.而对于多编码器的情况,由于编码器和解码器的隐藏维度大小一般是相同的,因此
CodePtr 使用了维度映射层 ReduceHidden,使多个输出的隐藏状态可以输入到解码器中.
上一小节中提到了我们将不会把 Source Encoder 的最终隐藏状态向量传递给解码器,原因有两个:一是因
为 Source Encoder 的作用是在解码时匹配 AST 信息,并且在指针生成网络开启时为其提供源代码的 Attention
权重,这都是通过解码时提供 Attention 权重来实现的;二是因为最终隐藏状态向量中蕴含的是编码器提取到的