Page 230 - 《软件学报》2021年第7期
P. 230

2148                                     Journal of Software  软件学报 Vol.32, No.7,  July 2021

                 Encoder 只用于传递给 Attention 机制使其匹配学习到的 AST 信息,而对于引入了指针生成网络的 CodePtr,
                 Source Encoder 的 Attention 权重还作为指针生成网络的输入.
                    从图中可以看出,CodePtr 大致上由以下几部分组成.
                    (1)  对 Java 源代码进行处理的 Parser;
                    (2) 3 个编码器:
                       a)  Source Encoder,对未经驼峰式分解的源代码序列进行编码,是专门为指针生成网络添加的编码器;
                       b)  Code Encoder,对经过驼峰式分解的源代码序列进行编码,目的是提取源代码序列的语义信息;
                       c)  AST Encoder,对 Java 代码对应的 AST 的 SBT 序列进行编码,目的是提取结构信息;
                    (3)  维度映射层 ReduceHidden,使多个编码器的隐藏状态输出可以输入到解码器中;
                    (4)  加入了 Attention 机制和指针生成网络的解码器.
                 2.1    Parser
                    我们对 Java 方法的源代码进行了一系列处理,分别得到了输入到 Source Encoder、Code Encoder 和 AST
                 Encoder 的输入序列.对相应的注释只进行了小写处理.
                    对于 Source Encoder,由于是专门用来学习指针生成的网络,保持源代码就是最好的选择.同时,为了减少冗
                 余,只做了一些必要的处理.首先将源代码全部转换为小写,然后将具体的数字和字符串分别替换成了“NUM”
                 和“STR”标签,由此就得到了 Source Encoder 的输入序列.
                    对于 Code Encoder,由于源代码中大量的标识符均为 OOV 词,同时,这些名称都是根据 Java 的命名规
                 范——驼峰命名法进行命名的,由多个单词拼接而成,其中隐藏着大量的信息,这些有意义的名称对于理解源代
                 码有着很大的帮助.因此,为了减少 OOV 比例,学习到更多的信息,我们对源代码中用驼峰命名法命名的名称进
                 行了分解,例如将变量名“invocationTime”分解为“invocation”和“time”,将方法名“copyMutationMappingFile”分
                 解为“copy”“mutation”“mapping”和“file”,将类名“RFFilter”分解为“rf”和“filter”.根据驼峰命名法进行分解后,训
                 练集上只出现过 1 次的单词数量从 203 581 减少到了 8 226,未经裁剪的词库大小从 476 654 减少到了 43 480.
                 同时,裁剪掉的 OOV 词数量占裁剪前词库的比例大为降低,下一节的实验中,在训练集大小为 432 674、词库大
                 小为 30 000 的情况下,分解前后的 OOV 比例分别为 93.70%和 31.00%,而这一数字在词库大小为 50 000 的情况
                 下为 89.51%和 0%,可见对标识符进行分解大大降低了词库的大小.对源代码进行分解后,再经过与 Source
                 Encoder 相同的处理,就得到了 Code Encoder 的输入序列.值得注意的是,这里对标识符进行分解的操作是为了
                 解决输入序列中 OOV 词丢失语义信息而提出的,与本节最开始的说明相同,这一步处理对本文解决的输出序列
                 中生成 OOV 词并没有关系.
                    对于 AST Encoder,首先使用 Eclipse 的开发工具 JDT(http://www.eclipse.org/jdt/)将 Java 代码转换为对应的
                 AST,再使用文献[12]中提出的 SBT 遍历方法生成 AST 的遍历序列,由于已有另外的编码器对源代码中变量名
                 称进行编码,因此,在 SBT 遍历结果中,只保留 AST 节点的类型信息,删掉了节点的名称.由此,就得到了 AST
                 Encoder 的输入序列.
                 2.2    编码器

                    第 1.2 节最后介绍的 Hybrid-DeepCom 对源代码序列中的标识符进行了拆分,可以大大减少 OOV 比例,同
                 时丰富源代码的语义信息,但是对于源代码这类结构性、语法性强的语言,如果将一个词拆分为多个词,很有可
                 能破坏了源代码的结构.由于同时使用了 SBT 序列使模型提取到了隐藏的结构信息,分解后的源代码序列并不
                 能完全对应 SBT 中的每个节点,我们认为这样使模型并不能完全利用学习到的结构信息.
                    换一个角度来看,就是在只有 Code Encoder 和 AST Encoder 的 Hybrid-DeepCom 中,本文认为 Code Encoder
                 的任务和作用有两个,一是提取源代码序列中的语义信息,另一个是与 AST Encoder 提取到的结构信息进行匹
                 配.本文认为,第 1 个作用主要体现在 Code Encoder 的最终隐藏状态上,最终隐藏状态向量蕴含了提取到的隐藏
                 特征;而第 2 个作用主要体现在解码时对 Code Encoder 输入的 Attention 权重上,依据提取到的结构信息选择对
   225   226   227   228   229   230   231   232   233   234   235