机器之心专栏
机器之心编辑部
北京大学李戈教授团队与阿里巴巴大淘宝团队的研究者,共同完成了为淘系前端生成业务代码的模型,这是首个在工业开发环境中被采用的代码生成系统。
代码生成,即希望机器能像人一样将一些自然逻辑,用形式化的方式,或者说代码表达出来,这样的能力非常令人振奋,同样也充满了困难。目前尽管深度学习非常强大,但即使是百亿级的 Transformer,仍然在这个任务上做的不尽人意。而本文提出的模型,第一次真正在工业开发场景中,帮助用户快速生成高效的代码。
本工作已被计算机软件工程顶会 ESEC/FSE 2022 Industry Track 接收,并且已经在阿里巴巴的 BizCook 平台应用。据我们所知,阿里前端智能化小组的核心团队 F(x)Team,以及他们开发的 imgcook.com 和 BizCook 设计生产一体化业务交付系统,是第一个在工业开发环境中被采用的领域代码生成系统。已经在阿里巴巴大淘宝技术营销和策略前台技术团队落地,并应用在阿里巴巴大促业务平台方舟的大促产品化项目中,极大提升了业务交付效率。
论文地址:https://arxiv.org/abs/2208.10091
一、本论文做了什么
自动代码生成是指根据自然语言的功能描述来生成代码。代码生成技术可以提高软件开发的自动化水平,减少软件开发人员的工作量,从而有效地提高软件开发和维护的效率和质量。因此, 在近些年来自动代码生成得到了越来越多的关注。
本工作的应用场景是阿里巴巴的 BizCook 平台,它是根据淘宝的业务特点定制的前端开发平台。这个系统涵盖了前端开发全过程的主要阶段,包括需求、设计、编码和测试,旨在开发过程中引入智能方法,提高开发效率。其中,一个重要的探索方向是根据需求文档和设计草稿生成代码,本文提出的模型主要尝试解决这个问题。
在探索这个代码生成任务之前,首先我们要明确,在前端开发中,大部分的前端代码都属于以下三类。
(一) 用户界面代码,它们用于控制用户界面元素的布局、风格等,如文本区的位置和按钮的颜色。
(二) 业务逻辑代码,它们用于控制 UI 元素的显示内容,如文本区或按钮上的文本内容。
(三) 控制流代码,它们用于控制元素的点击或其他行为,如按钮的点击事件。
在这三类中,用户界面代码之前已经有比较不错的工作,这部分代码与设计稿密切相关,我们已经开发了一个名为 imgcook 的工具,可以直接从 Sketch、Photoshop 和 Figma 设计稿中生成用户界面代码。
因为业务逻辑代码在前端代码中占很大的比例,而且没有控制流代码那么复杂,所以在 imgcook 的基础上,本文想把 BizCook 系统的代码生成能力扩展业务逻辑代码方面。
由于业务逻辑代码和控制流代码的描述是写在需求文档中的,所以这个功能需要能够读取需求文档中的自然语言描述,然后将其转换成 JavaScript 表达式,并将表达式绑定到 UI 元素上。正确、高效地将自然语言描述,转化生成业务逻辑 JavaScript 代码,即本论文的核心目标。
二、实践遇到的主要问题
一般来说,主流的代码生成方法都依赖于大量的配对训练数据,包括自然语言描述和代码。然而,在一些特定领域的场景中,由于不存在直接可用的配对数据,建立相应的大型配对语料库进行代码生成是非常困难的。这需要程序员花费大量的精力手工编写代码描述来构建高质量的训练数据集。另一方面,如果训练数据量受限,生成模型就无法得到充分的训练,很可能出现过拟合的情况,使得模型的性能在实际使用中不尽人意。
在本文的 JavaScript 表达式生成场景中,主要的限制正是训练数据不足。为了进行代码生成,我们需要从头开始建立训练数据集,即:挖掘一组代码,并为每一组代码手动编写相应的自然语言语义描述。这种方式能构建高质量数据集,但成本是相当昂贵的,因此只能获得相对小规模的配对训练数据。训练数据的缺乏使得神经网络很难学习到有效的表征来生成代码,从而严重影响了模型的性能。
三、构建数据集是第一步
为了生成 JS 表达式,第一步就是从头构建和预处理数据集。简单而言,本文从阿里巴巴的代码库中随机收集一些表达式,进行过滤和去重,得到一个业务逻辑代码表达式的集合,再手工制作每个代码的自然语言描述。最后,一个包含 2489 个例子的配对数据集被制作完成。在进行实验时,本文从训练集中随机抽取一个子集作为验证集。
在这个数据集中,本文进一步将 JavaScript 逻辑表达分为以下四个类别:
(一) 字符串模板表达(STE)。这一类的代码是通过用变量填充字符串模板产生的字符串表达式。
(二)OR 逻辑表达(OLE)。这类代码是由短路的 OR 运算符连接的几个值。
(三) 条件表达式(CE)。这类代码通常是一个三元组条件表达式。
(四) 数据处理表达式(DPE)。这类代码通常包含对数据的处理,如取一个字符串的子串。
在实际使用中,输入的描述是中文的,本文提供了相应的英文翻译。本文使用类别标签来标记测试集中的每一条数据,并按类别统计测试集的细节。
考虑到本文的数据集规模较小,而且数据集的变异性和噪声会影响代码生成的性能,因此本文对原始数据进行了一系列的预处理,接下来将介绍三种主要使用的预处理方法:
(一) 代码规范化:在本文的数据集中,代码存在一些风格上的差异。本文使用 Esprima 解析器将 JavaScript 代码解析为抽象语法树(AST),然后使用 Escodege 代码生成器将 AST 转换为规范化的代码。通过这种方式,可以在一定程度上消除代码之间的风格差异。
(二) 字符串字面替换:在本文的数据集中,有一部分代码包含字符串字面。按照规定,代码中的所有字符串字面必须出现在自然语言描述中。因此,可以使用占位符来替换这些字符串字面,以简化代码的生成。
(三) 成员访问的简化:很多代码都包括对成员变量的访问。本文在代码中简化了成员变量的访问,删除了被访问的对象,只保留被访问的字段。开发平台中的其他模块会使用代码静态分析等技术搜索编程上下文,寻找合适的访问对象,并完成成员访问表达。
四、搭建模型,精巧而高效
对于整个 JS 表达式生成任务,只依赖较小的高质量训练集显然不能得到较好的效果,因此我们需要从任务、模型架构等方面做文章,让其尽可能在小的数据集上获得比较好的效果,并能泛化到更加广泛的业务场景中。
为此,本文从两方面优化模型的构建与训练过程。首先是采用任务增强的方式,利用额外的任务,要求模型能学会利用常见的变量名进行代码生成。其次是采用更加精巧的模型架构,利用代码天然分层的语法树结构,以及 JavaScript 特定的抽象语法描述,进行更高效的建模与生成。相比单纯简单的序列建模,这种方式显然在该场景中能拥有更好的效果。
4.1 任务增强
由于面临着训练数据不足的问题,本文希望利用外部领域的知识来协助代码的生成。在前端 JavaScript 代码中,变量名占据了很大一部分。因此,如果让模型从领域知识中学习更多关于正确使用变量名的知识,会有很大的好处。因此本文提取了一个变量语义表,其中包含前端开发中常用的变量的名称和语义描述。
本文使用了任务增强的方法来利用变量语义表进行代码生成。为了在基于 AST 的代码生成方法中应用任务增强,我们提出了一种辅助任务。
本文发现,对于一个没有其他字符串字面意义连接的字符串模板表达式,代码只是一个变量名加上语法符号,如大括号和分号。因此可以将变量语义表转化为这些字符串模板表达式。以 ``picUrl - 图片链接 (图片的链接)'为例,可以将输入改写为 `` 展示图片链接 (展示图片的链接)',输出改写为 `` { picUrl; }'。通过这种方式,输出就变成能够解析成抽象语法树的合法代码,并将该辅助任务应用于基于 AST 的代码生成方法。
4.2 Subtoken-TranX 模型
为了利用代码天然的层次结构,本文采用了代码生成领域效果比较好的 TranX 模型作为基础,并对其做了一些很有意义的改进。
具体而言,原始的 TranX 模型在 Token 层面生成代码,这种设置对小规模训练数据下的代码生成并不友好。Token 级别的词汇通常很大,所以模型需要维持一个大的嵌入矩阵,这使得模型容易过拟合。而且,代码中的 Token 可能只在训练集中出现几次,所以模型很难学会一个好的词嵌入表示。此外,模型可能无法从有限的训练数据中捕捉到包含相同 Subtoken 的 Token 的关系。所以本文在 Subtoken 层面上生成代码。
本文规定,在 AST 的每个需要生成终端符号的位置,模型可以生成多个 Subtoken 动作。本文使用一个特殊的标记 <EOT > 来表示当前字段中标记的所有 Subtoken 已经完全生成。
4.3 支持 JavaScript 语言
为了让 Subtoken-TranX 模型支持 JavaScript 代码的生成,本文为 JavaScript 语言的语法规则构建相应的 ASDL。
本文以 Esprima 分析器文档中提供的 Mozilla 分析器 API 的形式来编写关于 JavaScript 抽象语法的 ASDL。本文编写的 JavaScript 的 ASDL 可以覆盖我们研究场景中绝大多数的前端 JavaScript 代码。除了 JavaScript 的 ASDL 之外,本文还构造了两个函数来完成 ASDL AST 和 JavaScript AST 之间的转换,并使用 Esprima 和 Escodegen 来完成 JavaScript AST 和 JavaScript 代码之间的转换。
五、实验效果
实验表明,无论是否应用任务增强,Subtoken-TranX 都优于原始 TranX 和 Transformer 模型。当应用任务增强时,Transformer 和 Subtoken-TranX 模型在 Subtoken 级别生成代码的性能都有明显的提高。
可以看到,在应用任务增强后,变量使用的指标对所有模型都有很大的改善。这些结果验证了我们的猜想:增加辅助任务可以提高代码生成中变量使用的准确性。在所有的模型中,Subtoken-TranX 模型在变量使用方面取得了最好的精度、召回率和 F1。
本文分别在不同类别的测试数据上计算了方法的度量。从结果中可以看出,应用任务增强方法后,模型的性能在不同类别的数据上有不同幅度的提高。对于 STE,Subtoken TA 的前 5 名准确率为 44.90%,编辑相似度达到 86.21%。对于 OLE,Transformer TA 的前 5 名准确率为 52.11%,编辑相似度达到 88.40%。该模型在这两类代码上的表现已经达到了在实际工业系统上应用的要求。本文的研究成果已被阿里巴巴的 BizCook 系统采用。
本文比较了以不同方式将变量语义表纳入模型的代码生成结果。“ PT” 是用变量语义表预训练模型的结果,并用代码生成数据集进行微调。“ VP”'是用变量语义预测变量名称的辅助任务来训练模型的结果。“ CG” 是将变量语义表改写成代码生成配对数据后,用辅助任务训练模型的结果。
结果表明,所有使用变量语义表的方法都能有效提高代码生成性能。与我们的预期一致,带有代码生成辅助任务( CG)的任务增强方法实现了最佳性能。
本文进行了一个样本分析,以直观地比较不同方法的生成结果。
总之,这些实验结果都表明,面对训练数据不足的问题,通过任务增强的方式将外部领域知识纳入到代码生成中,通过 Subtoken-TranX 模型更好地建模代码生成过程,能得到非常不错的结果。因此本文提出的代码生成方法在两个代码类别上满足了实际工业系统的应用要求,并被阿里巴巴的 BizCook 系统采用进行生产。
© THE END
转载请联系本公众号获得授权
投稿或寻求报道:content@jiqizhixin.com