当前的统计语言建模技术,包括基于深度学习的模型,已经被证明在源代码方面相当有效。作者在这里认为源代码的一些特殊属性可以被利用以进一步提升性能。在这项工作中,作者增强了建立语言建模方法来处理对源代码建模时的特殊问题,例如:频繁更改,不断变化的变量名,深层嵌套的作用域嵌套等。作者展示了一种快速的、特别为软件开发定制的语言建模工具,它可以增加或移除文本,混合多种模型。具体来说,作者改进了先前基于缓存模型的工作并提出一个具有更多扩展性,具备多层级局部性概念的建模软件。与传统的 N-gram,RNN 和 LSTM 深度学习语言模型相比,作者提供了不同语料库上的实验结果,并发布了所有源代码供公众使用。作者的评估表明,针对源代码精心调整 N-gram 模型可以产生甚至超过基于 RNN 和 LSTM 的深度学习模型的性能。
(1)Jelinek-Mercer 平滑优于先前工作中使用的平滑方法。
(2)人为地限制词汇量会错误地提升验证性能,但不会提高真正任务的实际表现。
NLP 中的统计语言模型和 Github 上大量的可获取的公开代码,催生了分析程序源代码这一研究领域。更好、更高级的针对源代码分析的语言模型能够有效地推动软件工程中某些领域的发展,例如代码补全。但是,考虑到 NLP 从诞生之初,就是为了解决自然语言中的一些问题,因此程序语言的某些特点并不能很好地被捕捉到。作者总结出以下 3 点:
为了解决上述这些问题,作者针对源代码分析开发出了一个动态地、作用域可调整的、开放单词模型的语言模型,在计数为基础的语言模型中能够达到最佳的效果。
下述算法描述了如何计算每种补全方式所对应的概率,假设使用简单的 Jelinek-Mercer 插值算法。尽管整个例子仅仅展示了 3 层的局部性,但是这很容易推广到多层的情况。当存在两个以上的模型见过同一个上下文后,那么所赋予的概率权重将会从全局逐渐转移到局部,以更好地偏向于局部性的补全结果。
总体上来说,作者的算法有如下两点优势:
初步结果
作者实现了之前描述的三种平滑方法:JM、WB 和 ADM,并使用 Tu 等人的代码来评估 MKN。其中,MKN 最适合自然语言,其次是 ADM,作者发现它在 10 亿个标记语料库以及一些小语料库中的表现几乎和 MKN 一样好。MKN(有时是 WB)已经被用于大多数先前的工作中。作者为作者的模型尝试了不同长度的 N-grams,在 N=6 时的 JM 模型为普通模型和缓存模型都产生了持久的最优值,如图 2 所示,在这一点上,高阶几乎不会产生任何收益。因此,作者在实验中使用 JM-6 模型。
图 1 模型性能(熵)由不同的平滑器获得,N 个图形按 N 的顺序绘制。无缓存(顶部)和有缓存(底部)的模型基本上遵循相同的模式,Jelinek-Mercer 平滑优于更精细的选择。
缓存模型
下图展示了实验结果。“平面”模型(不带缓存和带缓存)将所有看到的文件视为单个(“平面”)语料库。它们的熵分别为 5.3-5.5 位和 3.3-3.4,无缓存时 MRR 性能约为 0.6,有缓存时 MRR 性能提高到 0.7。然而,层次感知的嵌套模型极大地提高了模型和预测性能:对于无缓存模型,熵减少了约 2 位,而对于无缓存模型,熵减少了约 1 位,MRR 随之增加。值得注意的是,在维护设置方面,没有本地文件缓存的嵌套模型优于没有缓存的嵌套模型。
图 2 非嵌套和嵌套模型在动态和软件维护设置中的熵和 MRR 预测性能。每个模型都显示有和没有缓存组件这两种情况。
针对源代码专用的词汇表
下图中有几个模式很突出:当词汇表的大小稍微减小时,普通模型的性能会大幅提升。通过在测试时关闭词汇表,可以观察到缓存模型也有类似效果。然而,最关键的是,普通模型不能达到(点式)缓存基线的性能,即使使用封闭的词汇表和将词汇表大小减少三分之二的计数截止(即截止值 ≤5)。因此,作者得出两个结论:1)减少词汇表的大小和/或在测试时关闭词汇表会在建模和预测性能方面导致大量但误导性的膨胀;2)缓存组件可以优雅地处理词汇创新问题,而不需要人为限制。
图 3 词汇截止(在词汇表中包含的训练数据中必须看到事件的最少次数)与两个主流指标(实线)的性能,以及测试时真正开放的词汇(虚线)。
作者介绍了混合的,有范围的模型来处理 N-gram 模型的任意嵌套和混合问题,使用针对语言事件的动态范围计数和优化的数据结构来实现这些模型。作者在相关工作中比较了几种流行的平滑技术,并展示了一种简单的方法(通常不使用)。最后,作者通过与隐式(基于深度学习)模型的比较和组合,评估了这些模型在大型 Java 代码库上的性能。 作者发现其模型优于 RNN 和 LSTM 深度学习模型,在编码建议任务上实现了前所未有的熵值和性能。实验还表明,即使对于 LSTM 模型,作者的方法也存在一定的优势。