机器之心原创
参与:思源
开发者写代码,和数学家写公式一样是非常自然的一件事。开发者将完成某个任务的步骤和逻辑,一行行写成代码,并期待达到预定的效果。数学家从某个事实出发,将思考过程一行行写成表达式,并期待找到复杂逻辑背后的简单关系。这两者经常会有交叉,也会有融合。数学推导结果可以大量简化代码,并提供新的解决路径;而代码可以快速验证推导过程,并用于实际的生活中。代码和表达式都是一种形式化语言,而另一种必不可少的是用来描述它的自然语言,也就是注释或文档。通过注释,我们能知道这段代码干了什么,甚至很自然地想到「如果是我,这段代码该怎么写」。通过阅读代码,我们能沿着开发者的思路走一遍,总结出它到底干了什么。这两者似乎是一种对偶关系,从代码到注释、从注释到代码,这就是代码生成与代码总结两大任务。在这篇文章中,我们将介绍代码生成与总结的最新进展,北大 Bolin Wei、李戈等研究者提出的对偶学习在 Python 和 Java 代码生成上获得了新的 SOTA,并且被接收为 NeurIPS 2019 论文。如下是北大新研究根据注释生成的两段代码,其中 dcsp 表示 tab 键、dcnl 表示换行符,它们控制 Python 代码的缩进结构。大家都说深度神经网络能力很强,那么从函数注释生成函数代码,以及从函数代码总结函数注释这种最基础的代码任务到底能不能行?像 Python、Java 这样的通用高级语言,到底在代码生成上能达到什么水平?本文介绍的就是这样一篇北大前沿研究。



也就是说,logP(x) + logP(y|x) 需要等于 logP(y) + logP(x|y),这是代码生成与总结的内在联系。如果两项差别很大,那么至少可以判定代码生成与总结都没有达到最优。所以,常规的做法就是把这个约束构建为损失函数:
其中 P(x) 和 P(y) 分别是针对代码和注释的语言模型,它们都是边缘分布。这个损失有点类似于回归模型常用的均方误差,如上所示,只要两个子模型不满足理论上的概率条件,那么肯定会产生损失,在训练中就会建立起代码生成与总结的关系。注意力权重也来约束上面是其中一个正则项,另一个正则项主要是考虑两个子模型之间的对称性。在北大的这一项研究中,他们考虑了注意力权重的对称性。研究者表明,因为注意力权重能度量源代码 Token 与注释 Token 之间的匹配关系,而这种匹配关系又是对称的,所以注意力权重也需要是对称的。研究者举了一个例子,例如代码注释为「find the position of a character inside a string」,那么对应源代码可能为「string . find ( character )」。现在,不论是从代码到注释还是从注释到代码,源代码中的「find」一定需要匹配到注释中的「find」,它们之间的关系是不变的。所以,现在最直观的思想是,我们希望两个注意力权重矩阵 A_xy 和 A_yx,它们之间对应的元素尽可能相等。因为 A_xy 表示代码部分注意到注释部分的程度,所以,A_xy 矩阵的每一行表示代码的某个 Token,与注释的所有 Tokens 之间的关系。同理 A_yx 表示注释部分注意到代码部分的程度,A_yx 的每一列表示代码的某个 Token,和注释的所有 Tokens 之间的关系。具体而言,如果
,其中 i 表示 A_xy 的第 i 行;
,其中 i 表示 A_yx 的第 i 列。那么很明显,我们需要令 b_i 尽可能等于 b_i'。如果它们非常相近,那么可以表明注意力权重矩阵是对称的,源代码和代码注释之间的匹配是成功的。因为经过 softmax 的 b_i 和 b_i'都是一种概率分布,所以北大研究者通过 JS 散度度量这两类分布之间的距离。最常见的 KL 散度是不对称的,也就是说 KL(b_i || b_i') 不等于 KL(b_i' || b_i),而 JS 散度是 KL 散度的「对称版」,所以采用 JS 散度非常合理。此外,因为 JS 散度是对称的,所以代码生成模型与代码总结模型都能采用这样的距离度量作为约束条件。最后,以注意力权重的对称性作为正则项,JS 散度可以表述为:
伪代码带你走近联合训练现在两种正则项都已经完成了,只需要联合训练两个子模型就行了。如下算法 1 所示,输入两种数据源的语言模型预计对应的数据,模型就能开始学。
如上所示,对于每一个批量数据,模型会计算两个子模型各自的预测损失,并同时计算两个公共的对偶正则项。这样的损失能算出对应的梯度,并分别更新两个子模块的权重。目前该研究的开源实现已经放到了 GitHub,研究者使用 PyTorch 实现了整个模型的训练过程。如上伪代码所示,模型架构方面,Seq2Seq 大家已经比较熟了,我们需要重点理解的是目标函数。


如上所示为代码生成与总结的总体效果,我们可以发现对偶训练效果要超过其它方法,且相比独立训练的 Basic Model,效果也要更好一些。
值得注意的是,在代码生成中,Java 和 Python 的 PoV 分别只有 27.4 与 51.9%。也就是说,生成的代码首先不管是不是完成了自然语言描述的功能,它能通过词法分析、语法分析,最终成功地构建成抽象语法树,占比并不高。这样的效果,也许代表着正统代码生成,最前沿的水平。它离生成合理的代码,辅助开发者完成实战开发还太远了。正如该论文作者李戈教授所说,程序的数据空间非常稀疏,而自然语言数据空间也比较稀疏,这两个稀疏空间的变换肯定会比较困难。它并不能像图像生成这种连续空间的变换,程序的生成还有很长的路要走。本文为机器之心原创,转载请联系本公众号获得授权。✄------------------------------------------------加入机器之心(全职记者 / 实习生):hr@jiqizhixin.com投稿或寻求报道:content@jiqizhixin.com广告 & 商务合作:bd@jiqizhixin.com