论文笔记 StructCoder: Structure-Aware Transformer for Code Generation

目录

简单介绍

摘要

相关模型结构的比较

 本文的贡献

StructCoder模型与设计

符号表示

Code

AST  

DFG

编码器Encoder

输入编码Input emdedding

具有结构感知力的自注意力机制structure-aware self-attention

解码器Decoder

语言模型Token预测

AST路径预测APP

DFG关系预测DFP

decoder总损失

预训练

实验结果

PL-PL

 NL-PL

案例对比

消融实验

 模型的限制与更深思考​​​​​​​


简单介绍

这篇文章介绍提出了一个具有代码结构感知能力的Transformer模型用来处理代码翻译(基于某种程序语言生成另一种程序语言)和代码生成任务(基于自然语言描述生成程序语言)。

原文链接

摘要

Transformer在处理自然语言任务上取得了不错的效果,由于代码语言(PL)和自然语言(NL)

之间存在一定的共同性,因此近年来有很多的组织开始使用Transformer来处理PL任务。

但是相比于自然语言,程序语言具有更强的语义和句法逻辑,基于此,开发者改进Transformer提出StructCoder模型,使模型的编码器可以很好的学到source code的语法和数据流向(data flow),模型的解码器可以很好的学到target code的语法和数据流。此模型在CodeXGLUE 集上是目前的SOTA模型。

目前已有的两种代码句法表征方法是将以下两种结构编码进模型:

  •         AST(Abstract Syntax Tree ,抽象语法树):生成AST的中序遍历,AST的生成规则,使用RNN                          编码AST的路径,在序列模型中使用基于AST的attention机制;​​​​​​​
    •         DFG(Data Flow Graph 数据流向图):含有的信息比AST更多,却没有AST复杂。
int add1 ( int a ) { s= a + 1 ; return s ; }

基于以上代码生成的AST与DFG分别如下图所示:

相关模型结构的比较

MLM :Masked Language Modeling

RTD:Replaced Token Detection

DAE:Denoising Autoencoding

BT:Back-Translation

 本文的贡献

  1. 改进Transformer模型使编码器解码器都具有结构感知能力,提出StructCoder:
    1. 编码器使用结构感知自注意力机制(structure-aware self-attention);
    2. 解码器加入两项附加任务:APP(AST路径预测),DFP(数据流向预测);
  2. 预训练,使输入的代码、AST和DFG中部分失去意义,以此训练生成原始代码并且进行APP和DFP两项训练任务;

    3.   StructCoder是目前基于CodeXGLUE 数据集的SOTA模型。

StructCoder模型与设计

符号表示

Code

Source Code tokens S=(s_1,...,s_{|S|})

Target Code tokens T=(t_1,...,t_{|T|})

AST  

\tau =(N,N_{leaf},r,p(.),L^{ast})

        N代表AST中所有节点的集合;

        N_{leaf}代表所有AST中的叶子节点;

        r代表AST的根结点;

        p(n)表示的节点n的父节点;

        L^{ast}\in \{0,1\}^{|S|\times |N_{leaf}|},当且仅当token S_i是叶节点l_j一部分时 L_{ij}^{ast}=1;

        n.type代表是一个节点的类型。

DFG

G=(V,D,L^{dfg})

        V=\{\upsilon_1,...,\upsilon _{|V|} \}代表了Code S中的所有变量;

        D \in \{0,1\}^{|V|\times|V|}是两个变量之间的邻接矩阵,当且仅当\upsilon _i来源于\upsilon _jD_{ij}=1;

        L^{dfg} \in \{0,1\}^{|S|\times|V|},当且仅当变量\upsilon _i是token s_j的一部分时, L_{ij}^{dfg}=1

编码器Encoder

输入编码Input emdedding

输入序列由源代码tokens,对应的AST叶节点,对应的DFG变量构成:


<CLS>,s_1,...,s_{|S|},<SEP>,l_1,....,l_{|leaf|},\upsilon _1,...,\upsilon _{V}

token:通过单词表来编码;

DFG变量:use a default emdedding(原文是这么写的,还不是很理解);

AST叶节点:需要按照以下公式编码叶节点的路径信息:

E(l)=\sum _{I=1}^{|l|}E_{type}(r_i.type)\bigodot E_{height}(|l|-i) \in \mathbb{R}^d

其中(r_1,...,r_{|l|})为根结点r_1到该叶节点r_{|l|}的路径上的节点;

E_{type}(\cdot ) \in \mathbb{R}^d为节点类型的编码函数;

E_{height}(\cdot ) \in \mathbb{R}^d为节点顺序编码函数。

具有结构感知力的自注意力机制structure-aware self-attention

代码之间Code-code:和传统注意力计算方式一致

A(s_i,s_j)=E_{s_i}^TW_q^TW_kE_{s_j}+\phi(|i-j|))

其中\phi代表的lookup emdedding函数,用来存储两个token之间的相对位置信息。

叶节点之间Leaf-leaf:除了计算两个叶节点之间的自注意力之外还需要计算两节点之间的相似度

sim(l_i,l_j)=log(\frac{(\sum_{k=1}^{min(|l_i|,|l_j|)}\mathbb{I}(r_k^i=r_k^j))^2}{|l_i||l_j|})

=2log(\sum_{k=1}^{min(|l_i|,|l_j|)}\mathbb{I}(r_k^i=r_k^j))-log|l_i|-log|l_j|

A(l_i,l_j)=E_{l_i}^TW_q^TW_kE_{l_j}+(w_asim(l_i,l_j)+w_b)

变量之间Variable-variable:两个变量之间有联系才会计算注意力

A(\upsilon _i,\upsilon _j)=\begin{cases} & E_{\upsilon _i}^TW_q^TW_kE_{\upsilon _j},D_{ij}=1 \\ & -\infty ,else \end{cases}

代码与叶节点或者变量之间Code-leaf/variable:两者相互之间有联系才会计算注意力

A(s _i,l_j)=\begin{cases} & E_{s_i}^TW_q^TW_kE_{l_j},L_{ij}^{ast}=1 \\ & -\infty ,else \end{cases}

特殊符号被视作代码并且和其他所有的变量与叶节点有联系。

上图就代表了输入编码和注意力机制的可视化表示。

解码器Decoder

在解码器的输出需要完成三个任务:基于以输出序列的下一个token的预测,根叶节点路径预测和DFG变量之间联系预测。

语言模型Token预测

和传统的语言模型一样。

概率:

p_i=softmax(Wh_i)

h_i为解码器第i个时刻隐藏层的输出向量;

p_i为第i个时刻各个token的预测概率。

损失:

L_{lm}=-\sum_{i=1}^{|T|}log\ p_i(t_i)

p_i(t_i)为第i个时刻输出的在正确token上面的预测概率。

AST路径预测APP

时刻i,根叶路径r_1 \to r_{|l_i|}中第k个节点的概率分布:

p_{ik}^{ast}=softmax(W_{ast(|i_i-k|)}h_i))

损失:

L_{app}=-\sum_{I=1}^{|T|}\sum_{k=1}^{|l_i|}log\ p_{ik}^{ast}(r_k^i.type)

DFG关系预测DFP

第i个时刻的token来自于第j个时刻的token的概率:

p_{ij}^{dfg}=\sigma (h_i^TU_{dfg}^TV_{dfg}h_j+u_{dfg}^Th_i+v_{dfg}^Th_j+w_{dfg})

cond判断在ground truth中两个token有联系(一个来源于另一个):

存在两个有联系的变量\upsilon _{i'},\upsilon _{j'}D_{i'j'}=1,使得\upsilon _{i'}与token t_i有联系即L_{ii'}^{dfg}=1,并且v_{j'}和token t_j有联系即L_{jj'}^{dfg}=1

损失:

L_{dfp}=-\sum_{I=1}^{|T|}\sum_{j=1}^{|T|}(\mathbb{I}(cond)log\ p_{ij}+\mathbb{I}(!cond)log\ (1-p_{ij}))

decoder总损失

L=(3-\alpha -\beta )L_{lm}+\alpha L_{app}+\beta L_{dfp}

预训练

增加噪声:掩盖或者去除原始输入的35%的token,AST叶节点与DFG变量,和35%的叶节点路径;

模型参数初始化:使用CodeT5模型中的参数来初始化本次预训练的参数,与AST,DFG有关的参数随机初始化;

数据集:CodeSearchNet

实验结果

PL-PL

NL-PL

 从上述数据可以看出CodeT5是一个有力的竞争对手,而StructCoder是在CodeXGLUE上实现代码翻译和代码生成的SOTA模型。

案例对比

 说明StructCoder可以很好学习到代码中的语义与数据流向。

消融实验

去除StructCoder中的某一个组件,观察StructCoder中的表现,发现去除任意一个组件都会降低StructCoder的评价表现,尤其是去除AST输入之后。

 模型的限制与更深思考

  1. 由于在输入序列中加入了AST与DFG信息,处理起来复杂度增加尤其是在算注意力时复杂度二次增长
  2. 存在少数bad case,需要二次加工
  3. 代码的生成有多种正确答案,而在训练时数据集只有一个ground truth,因此需要制定合适的评价标准
  4. 生成的代码缺少安全性,有效性,模块化方面的思考
  5. 只有大企业才适合训练此巨大规模的模型

以上就是我认为论文中值得学习的内容,更多的训练细节与超参设置推荐去阅读原文!

你可能感兴趣的:(论文阅读,人工智能,自然语言处理,transformer)