深度学习模型:如何对源代码进行建模

2020-7-7 14:32 2616 85

当前的统计语言建模技术,包括基于深度学习的模型,已经被证明在源代码方面相当有效。作者在这里认为源代码的一些特殊属性可以被利用以进一步提升性能。在这项工作中,作者增强了建立语言建模方法来处理对源代码建模时的特殊问题,例如:频繁更改,不断变化的变量名,深层嵌套的作用域嵌套等。作者展示了一种快速的、特别为软件开发定制的语言建模工具,它可以增加或移除文本,混合多种模型。具体来说,作者改进了先前基于缓存模型的工作并提出一个具有更多扩展性,具备多层级局部性概念的建模软件。与传统的 N-gram,RNN 和 LSTM 深度学习语言模型相比,作者提供了不同语料库上的实验结果,并发布了所有源代码供公众使用。作者的评估表明,针对源代码精心调整 N-gram 模型可以产生甚至超过基于 RNN 和 LSTM 的深度学习模型的性能。

本文主要贡献:

  1. 作者引入了一种动态可更新的,嵌套范围的,基于无限制词汇量的 N 元语法模型,该模型明显优于所有现有的词法级模型,包括基于深度学习的非常强大的模型。作者的模型比深度学习模型快得多。作者的嵌套缓存模型实现了 MRR 性能为 0.818,并且词汇量无限制(词汇量有限的情况下为 0.85),这是同类最佳的。作者的工作表明,经过精心设计的传统方法可以击败深度学习模型。
  2. 作者证明了基于计数的方法在 LSTM 模型中可以“很好地发挥”,并且在组合时产生更好的性能,尤其是在熵值方面,其中最佳混合实现了每个词的 1.25 位熵同时不限制词汇量。
  3. 作者的详细评估揭示了一些新发现:

(1)Jelinek-Mercer 平滑优于先前工作中使用的平滑方法。

(2)人为地限制词汇量会错误地提升验证性能,但不会提高真正任务的实际表现。

背景知识:

NLP 中的统计语言模型和 Github 上大量的可获取的公开代码,催生了分析程序源代码这一研究领域。更好、更高级的针对源代码分析的语言模型能够有效地推动软件工程中某些领域的发展,例如代码补全。但是,考虑到 NLP 从诞生之初,就是为了解决自然语言中的一些问题,因此程序语言的某些特点并不能很好地被捕捉到。作者总结出以下 3 点:

  • 几乎无限制的词汇:相比于自然语言,模型在训练过程中很快就不会再遇到新词汇。但是在代码中,虽然每种程序设计语言都有一系列固定的关键词和运算符,但是变量名却很少重复出现。
  • 嵌套的、有一定范围的局部性:尽管开发者会不断给出新的变量名,但是这些名字的使用并不会遍布全局,而仅仅是局限在某一个类中。同时考虑到程序的嵌套结构,某些变量会在作用域的内外频繁出现。
  • 动态性:软件的动态演化是非常常见的,因为补丁和新功能的添加会不断地涌入。此外,在交互式编程中,分析软件必须能够快速地在不同的上下文环境中进行切换。

为了解决上述这些问题,作者针对源代码分析开发出了一个动态地、作用域可调整的、开放单词模型的语言模型,在计数为基础的语言模型中能够达到最佳的效果。

算法分析:

下述算法描述了如何计算每种补全方式所对应的概率,假设使用简单的 Jelinek-Mercer 插值算法。尽管整个例子仅仅展示了 3 层的局部性,但是这很容易推广到多层的情况。当存在两个以上的模型见过同一个上下文后,那么所赋予的概率权重将会从全局逐渐转移到局部,以更好地偏向于局部性的补全结果。

深度学习网络是对源代码进行建模的最好方法吗?

总体上来说,作者的算法有如下两点优势:

  1. 基于计数的模型允许作者选择任何模型,这意味着作者可以在平滑方法和许多其他可以处理数据计数的模型之间自如切换。(例如,skip-gram 模型,甚至神经网络)
  2. 该模型可以表示任何变更粒度。 最近的错误检测研究在相隔一个月的快照上建立了模型,并针对随后的更改(直到下一个快照)进行了测试,以使计算变得可解。而这项工作中的模型可以从项目开始时的每次提交进行更新,并在数以千计的提交中以分钟为单位运行,这有助于对代码审查中的代码更改进行建模。

实验结果:

初步结果

作者实现了之前描述的三种平滑方法: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 模型,作者的方法也存在一定的优势。

推荐阅读
5g锚点站是什么意思,锚点示意图、传输方案 2020-08-27 11:05
手机CPU(SOC)性能排行榜,5G芯片最强高通865、联发科的天玑1000+、三星Exynos990 2020-09-01 17:47
氮化镓充电器和普通充电器区别,为什么不早应用的原因? 2021-03-01 09:54
常见三端稳压器工作原理和应用电路图有哪些? 2019-09-09 15:20
4款小米充电器评测比较:输出、价格、快充规格、特点 2019-11-08 10:50