引用
Anh Nguyen and Michael Hilton and Mihai Codoban and Hoan Nguyen and Lily Mast and Eli Rademacher and Tien Nguyen and Danny Dig. API Code Recommendation using Statistical Learning from Fine-Grained Changes. In Proceedings of the 24th International Symposium on Foundations of Software Engineering, 2016, 511-522.
摘要
学习和记忆如何使用 API 是一件困难的事情。尽管代码补全工具能够推荐 API 方法,但是浏览冗长的 API 方法名列表及文献是一件苦闷的工作,并且用户经常对数量过多的信息望而却步。
我们提出了一种新的 API 推荐方法,它利用重复代码更改的预测能力为开发人员提供相关 API 的推荐。我们使用的方法及工具 APIREC 是通过对细粒度代码更改及这些更改所在的上下文进行统计学习从而实现的。我们的评估结果显示,在 59%的情况下,APIERC 能够将正确的推荐 API 置于首位,在 77%的情况下,能将其置于前 5 位。这相对于之前最先进的方法有着极大的改善:其在首位的准确度上有 30-160% 的提升,在前 5 位上有 10-30% 的提升。我们的结果还显示,即使只用从 50 个开源项目获得的一次性、最小训练数据集,APIERC 也能展现出色的性能。
定义
1. 原子更改:一个(细粒度)的原子更改可以表示为(<操作类型>,,<标签>)。实验中用 AST 表示源代码,以防止格式更改对推荐结果的影响。对于更改的文件,通过比对更改前后的 AST 获取细粒度更改。主要采用的工具是 GumTree。 操作类型可归纳为:change、add、delete 和 move。AST 节点类型表示 Java 抽象语法树的节点,而标签表示 AST 节点所含的文本信息。图 1 显示了一次代码更改,而表 1 是对其 AST 中原子更改的归纳。
2. 交易:在一次提交中,更改文件中的所有原子更改被收集在交易中。这些更改是以袋而不是列表的方式存储,因为同一更改模式下,不同的编程者所写的原子更改也是不一样的,如果建立严格的顺序,将会难以统计学习用于推荐的模式。此外,由于这些代码更改都是从代码仓库中已提交的更改中收集的,所以无法得知它们编辑的顺序。
3. 更改上下文:它表示在同一编辑会话中,发生在当前更改请求之前的细粒度更改组成的更改袋。它们对于当前方法的推荐十分有用,除此之外,实验中还按这些更改对编程元素的影响赋予它们权值,其与当前代码元素的数据依赖性越高,权值也就越大,目前只涉及变量的定义和使用,以及相同变量的不同方法调用间的依赖。
4. 代码上下文:它表示代码记号的集合,这些代码记号处于当前编辑处之前的一段距离内,该距离长短取决于代码记号自身。实验中代码记号从 AST 上获取。其原理在于:推荐请求点周围的一些代码即使不发生更改,也经常与期望的 API 调用同时出现,因而这些代码上下文对推荐正确的 API 有益。实验中忽略分隔符与标点符号。
实验中对于更改上下文和代码上下文都要考虑到它们的距离和范围,对于更加接近推荐请求点的上下文,会赋予其更高的权重。实验中距离以记号数为单位进行测量;而对于范围,实验中赋予方法作用域内的上下文更高的权重。
模型介绍
该模型的目的在于:由先于当前更改 c 的更改上下文 C 中给定的代码更改,以及 API 请求处周围的代码上下文 T 中给定的代码记号,能够推断出一个更改 c 出现在请求处的可能性分数。其中 c 的形式为,而 APIREC 的任务是预测 methodName。
一个更改 c 的出现可能性分数 Score(c, (C, T))是关于 c、C 和 T 的函数。为了进行计算,作者假设更改上下文与代码上下文对于下一记号出现的影响是相互独立的,分别记为 Score(c, C)和 Score(c, T)。Score(c, (C, T))是 Score(c, C)与 Score(c, T)的赋权线性组合:
训练和推荐
训练:
公式(3)和(4)APPREC 需要学习三种参数:
推荐:
当上述所有的参数都训练好了,便可以使用公式(3)与(4)以及训练过程中获得的代码及更改出现数量来估算更改 c 发生的可能性。实验中计算了所有在词汇表中并满足以下条件的候选更改的可能性:1、该更改是对方法调用的增添;2、它至少在出现在一个交易中的车 c1,c2,c3,c4....cn 现在一个交易中的的 t1,t2,t3,t4.....,tn 至少一个记号中。最终将候选项按分数排序。表 2 显示的是图 1 所示代码更改中记号 result 后的代码补全实验结果:
其中 c1,c2,c3....,c13 表示更改上下文,t1,t2,....,t6 表示代码上下文。所有满足条件 1 和 2 的候选方法调用 c 被列在右列,包括:add、remove、contains、addAll 和 clear。表 2 中的分数表示可能性分数 Score(c, C)(表示 ci 出现在更改上下文时更改 c 出现的可能性)与 Score(c, T)(表示记号 ti 出现在代码上下文时 c 出现的可能性),它们分别通过公式(3)和(4)计算得出。考虑到所有因素如距离、范围以及数据依赖性,在所有候选项的联合得分中,add 得到了最高分并且获得了最高排名。从结果看来也是有理可循的,因为编程者经常在循环中使用 add 将元素加入容器中。
语料库介绍:
大语料库:使用 50 个从 Github 随机选取的具有很长开发历史的 Java 项目构成,将其所有提交中的原子更改使用 GumTree 提取了出来
社区语料库:较小的语料库,仅包含 8 个 Github 上的项目,处理方式与大语料库相同。
实证评估:
1、准确性:
社区版:在这项实验中,APIREC 使用大语料库进行训练,并在社区语料库上进行测试。实验中作者复现了 n-gram API 、Bruch、GraLan 等模型并使用相同的语料库进行训练和测试,并通过两种不同的设置进行比较:
1)、在语料库的所有库中的 API 上进行比较:
考虑到 APIREC 无法推荐训练集上未出现过的 API 调用 ,而这些 API 可能会在测试集上出现,为了公平比较,实验中测量的是词汇表内的准确率。如图 3 ,实验
结果表明 APIREC 的首位准确率达到了 44.2-59.5%,相较于其他工具有 30-160%的提高,在前五位的准确率达到了 83.6%,相较于其他工具有 20-30%的提高。
2)、在 JDK 库中的所有 API 上进行比较:
在这一环节中,只针对 JDK 中的 API 库进行推荐准确率评估。实验结果表明 APRREC 在 JDK 中 API 的推荐准确率十分高,其首位准确率达到了 56.4-74.3%,在前五位的准确率达到了 76.1-89.8%,并且在任何项目上都远远高于其他工具(其首位准确率比 n-gram 高 200%,比 Bruch 高 170%,比 GraLan 高 120%)。
项目版:这项实验的目的在于评估企业文化对 APIREC 准确率的影响。为了便于比较,实验中选取了企业版中的测试项目,并在相同的项目上进行训练和测试。对于每一个项目,将其所有的提交按时间排序,并将最近的 10%的提交用于推荐,其余的用于训练。实验结果表明,虽然 APIREC 的表现仍然好于其他工具,但是不管是在通用 API 还是特定(JDK)API 上, 它的准确率都明显低于社区版。
用户版:在这项实验中,我们评估了仅在一个用户的所有提交上训练 APIREC 时的准确率。实验中选取了一位提交数最多的用户,其提交在各项目中占比 10-28%,为便于比较,实验项目的选用与数据集的划分都与项目版相同。实验结果表明,虽然其准确率仍低于社区版(可能是训练数据较少),但是有趣的是,用户版的准确率总体上高于项目版。这提醒我们:相比与将整个项目作为训练集,如果将代码作者纳入考虑,可能会通过更少的训练数据获得更精确的结果。
2.灵敏度分析:
更改上下文与代码上下文:实验测量了预测点前的更改的数量以及代码记号的数量对推荐结果的影响。实验中随机选取了社区语料库中的 antlr 项目,并改变其上下文的规模并测量了词汇表内准确性。结果表明:增加更多的更改上下文可以与更改的高层意图建立关联,从而带来更高的准确性,而代码上下文的增加对结果增幅较小。而当增加数量超过 15 时,准确率的提高就变得十分微小了。
预测点:由于 APIREC 的推荐结果是基于预测点前的代码及更改数的,因此从交易的 n 个更改中选择不同的预测点将会对结果造成影响。模型在大语料库上进行训练,并随机选取了社区语料库中的 JGit 项目进行测试。实验选取了三个不同的预测点,其结果表明预测点的后移将略微增加推荐的准确率,因为后移使得更改上下文拥有了更多的前置更改。
训练数据的规模:实验中选取了 6 组不同的数据集,并分别增加了 50-300 个项目。结果显示当项目数增加时,准确率只从 57.3%提高到 58.6%。可见更大的训练数据集确实能带来更好的性能,但是提升非常小。这也表示 APIREC 只需要使用来自 50 个项目的最小训练集就能获得很好的结果。
3、运行时:
所有实验都是使用一台配置为 Xeon E5-2620 2.1GHz(1 线程和 32GB RAM)的计算机运行的。推荐响应时间为每次推荐小于 1 秒,因此 APIREC 十分适合在 IDE 中交互式使用。
本文贡献
1. 方法:本文提出了一种全新的方法:通过对周围代码上下文的细粒度变化进行统计学习来实现代码推荐。我们还提供了新的方向,即结合利用源代码和细粒度变化的重复性。
2. 实现:我们通过工具 APIREC 实现了提出的方法,它能够计算出在代码请求处最可能被插入的 API 方法调用。
3. 实证评估:我们通过实证评估表明:APIREC 在 API 代码补全上有着很高的准确性(59%的首位准确率,这相比于之前最先进的方法都有着 30-160%的提升)。我们的评估还表明:只用从 50 个开源项目获得的最小训练数据集,APIERC 就能展现出色的性能。有趣地是,我们发现比起使用一整个项目进行训练,如果考虑到代码的作者,甚至可以用更少的训练数据得到更高的准确率。使用社区语料库训练模型比使用来自项目或单个开发人员的训练结果更加准确。
致谢
感谢国家重点研发计划课题:基于协同编程现场的智能实时质量提升方法与技术(2018YFB1003901)和国家自然科学基金项目:基于可理解信息融合的人机协同移动应用测试研究(61802171)支持!
本文由南京大学软件学院 2020 级硕士生孙昱翻译转述。