作者:google Research 高级软件工程师 Maxim Tabachnyk 和高级工程经理 Stoyan Nikolov
代码复杂性的增加对软件工程的生产力提出了关键挑战。代码完成一直是一个必不可少的工具,它有助于减轻集成开发环境(IDE) 中的这种复杂性。通常,代码完成建议是使用基于规则的语义引擎(SE) 实现的,这些引擎通常可以访问完整的存储库并了解其语义结构。最近的研究表明,大型语言模型(例如,Codex和PaLM)可以实现更长、更复杂的代码建议,因此出现了有用的产品(例如,Copilot)。然而,机器学习 (ML) 支持的代码完成如何影响开发人员的生产力,除了感知的生产力和接受的建议,这个问题仍然悬而未决。
今天我们描述了我们如何结合 ML 和 SE 来开发一种新颖的Transformer- 基于混合语义 ML 代码完成,现在可供 Google 内部开发人员使用。我们讨论如何通过以下方式组合 ML 和 SE:(1)使用 ML 重新排列 SE 单标记建议,(2)使用 ML 应用单行和多行完成并使用 SE 检查正确性,或(3)使用单和通过 ML 对单标记语义建议进行多行延续。我们将 10k+ 名 Google 员工(超过 3 个月的八种编程语言)的混合语义 ML 代码完成与对照组进行比较,发现编码迭代时间(构建和测试之间的时间)减少了 6%,上下文切换减少了 7%(即,离开 IDE)当暴露于单行 ML 完成时。这些结果表明,ML 和 SE 的结合可以提高开发人员的生产力。目前,
Transformers for Completion
代码完成的一种常见方法是训练 Transformer 模型,该模型使用自注意力机制进行语言理解,以实现代码理解和完成预测。我们处理类似于语言的代码,用子词标记和SentencePiece词汇表表示,并使用在TPU上运行的编码器-解码器转换器模型进行完成预测。输入是光标周围的代码(~1000-2000 个标记),输出是一组完成当前行或多行的建议。序列是通过解码器上的波束搜索(或树探索)生成的。
在 Google 的monorepo培训期间,我们屏蔽了一行的其余部分和一些后续行,以模仿正在积极开发的代码。我们在八种语言(C++、JAVA、Python/ target=_blank class=infotextkey>Python、Go、Typescript、Proto、Kotlin 和 Dart)上训练了一个模型,并观察到所有语言的性能改进或相同,无需专用模型。此外,我们发现约 0.5B 参数的模型大小可以很好地权衡高预测精度、低延迟和资源成本。该模型极大地受益于 monorepo 的质量,该质量由指南和审查强制执行。对于多行建议,我们迭代地应用具有学习阈值的单行模型来决定是否开始预测下一行的完成情况。
使用 ML 重新排列单个令牌建议
当用户在 IDE 中键入时,ML 模型和 SE 同时在后端以交互方式请求代码完成。SE 通常只预测单个令牌。我们使用的 ML 模型预测多个标记直到行尾,但我们只考虑第一个标记来匹配来自 SE 的预测。我们确定了 SE 建议中也包含的前三个 ML 建议,并将它们的排名提升到了首位。然后,重新排序的结果会在 IDE 中显示为用户的建议。
在实践中,我们的 SE 在云中运行,提供开发人员熟悉的语言服务(例如,语义完成、诊断等),因此我们将 SE 配置在与执行 ML 推理的 TPU 相同的位置上运行。SE 基于一个内部库,该库提供类似编译器的低延迟特性。由于设计设置,请求是并行完成的,而 ML 通常可以更快地提供服务(中位数约为 40 毫秒),我们不会为完成添加任何延迟。我们观察到实际使用中的质量显着提高。对于 28% 的接受完成,完成的排名由于提升而更高,在 0.4% 的情况下更差。此外,我们发现用户在接受完成建议之前输入的字符数减少了 10% 以上。
检查单行/多行 ML 完成的语义正确性
在推理时,ML 模型通常不知道其输入窗口之外的代码,并且在训练期间看到的代码可能会错过最近在积极变化的存储库中完成所需的添加。这导致了 ML 驱动的代码完成的一个常见缺点,即模型可能会建议看起来正确但无法编译的代码。根据内部用户体验研究,随着时间的推移,此问题可能会导致用户信任度下降,同时降低生产力收益。
我们使用 SE 在给定的延迟预算内执行快速语义正确性检查(端到端完成<100ms),并使用缓存的抽象语法树来实现“完整”的结构理解。典型的语义检查包括引用解析(即,该对象是否存在)、方法调用检查(例如,确认使用正确数量的参数调用方法)和可分配性检查(确认类型是否符合预期)。
例如,对于编码语言Go,约 8% 的建议在语义检查之前包含编译错误。然而,语义检查的应用过滤掉了 80% 的不可编译的建议。在加入该功能的前六周,单行完成的接受率提高了 1.9 倍,这可能是由于用户信任度的提高。作为比较,对于我们没有添加语义检查的语言,我们只看到接受度增加了 1.3 倍。
结果
超过 10k 的 Google 内部开发人员在他们的 IDE 中使用完成设置,我们测量了 25-34% 的用户接受率。我们确定基于转换器的混合语义 ML 代码完成完成了超过 3% 的代码,同时将 Google 员工的编码迭代时间减少了 6%(在 90% 的置信水平下)。转变的大小对应于观察到的转换特征(例如,关键框架)的典型影响,这些特征通常只影响一个子群体,而 ML 有可能推广到大多数主要语言和工程师。
在探索 API 的同时提供长补
全我们还将语义补全与全行补全紧密集成。当出现带有语义单标记补全的下拉菜单时,我们会显示从 ML 模型返回的单行补全。后者表示作为下拉焦点的项目的延续。例如,如果用户查看 API 的可能方法,则内联完整行完成显示完整的方法调用还包含调用的所有参数。
结论和未来的工作
我们展示了如何结合使用基于规则的语义引擎和大型语言模型来显着提高开发人员的工作效率并实现更好的代码完成。作为下一步,我们希望通过在推理时向 ML 模型提供额外信息来进一步利用 SE。一个例子是在 ML 和 SE 之间来回进行长预测,其中 SE 迭代地检查正确性并为 ML 模型提供所有可能的延续。在添加由 ML 提供支持的新功能时,我们要注意超越“智能”结果,但要确保对生产力产生积极影响。
致谢
本研究是 Google Core 和 Google Research, BrAIn Team 两年合作的成果。特别感谢 Marc Rasi、Yurun Shen、Vlad Pchelin、Charles Sutton、Varun Godbole、Jacob Austin、Danny Tarlow、Benjamin Lee、Satish Chandra、Ksenia Korovina、Stanislav Pyatykh、Cristopher Claeys、Petros Maniatis、Evgeny Gryaznov、Pavel Sychev、Chris Gorgolewski , Kristof Molnar, Alberto Elizondo, Ambar Murillo, Dominik Schulz, David Tattersall, Rishabh Singh, Manzil Zaheer, Ted Ying, Juanjo Carin, Alexander Froemmgen 和 Marcus Revaj 的贡献。