引子
这门课本来是研究生课程,不知道为什么,本学期也给本科生开了。
说是上课,不过是老师带着学生读论文而已。
这门课所讲述的内容,是将NLP相关的模型,用于软件工程上,简化相应工作。
我将老师讲述的部分模型记录于此,权当备忘。
代码表示学习和几类代表性任务
代码表示学习,就是将代码用一系列张量表示,通过机器学习的相关模型,学习出一些特征来。在软件开发中,常见的几类代码表示学习任务有:
- Bug定位。将缺陷报告和源文件作为输入,输出为缺陷代码具体的位置,免去人工调试定位。
- 代码分类。
- 代码生成。将用户需求作为输入,输出实现对应功能的代码。
- 代码翻译。将Java程序翻译成C程序。
根据编译原理的相关知识,源代码变为目标代码需要经历以下几个步骤:
- 词法分析,该步生成token序列。
- 语法分析,该步生成抽象语法树(AST)。
- 语义分析,该步可以生成图,比如数据依赖图(DDG)、控制依赖图(CDG)等。
- 中间代码生成。
- 优化。
- 目标代码生成。
那么代码表示学习大致可以分为三类:
- 基于序列的代码表示学习。
- 基于树的代码表示学习。
- 基于图的代码表示学习。
基于序列的代码表示学习
RNN(循环神经网络)
RNN的结构如下:
整个RNN其实只有一套参数,只不过$t-1$时刻的输出会作为$t$时刻的输入而已。$o_{t-1},o_{t},o_{t+1}$可有可无,根据实际需要修改即可。
每个时刻的输出如下:
$$
s_t=f(Ux_t+Ws_{t-1}+b_0)
$$
$$
o_t=g(Vs_t+b_1)
$$
$f,g$分别为隐藏层、输出层的激活函数。
因为每个时刻的输出和前一个时刻有关,所以RNN能够一定程度记忆过去发生的信息,有一定的时序记忆性。
优点:结构简单,易于训练。
缺点:简单根据链式法则推算反向传播,容易发现RNN有梯度消失和梯度爆炸的问题。详见RNN 的梯度消失问题。
LSTM(长短期记忆模型)
为了解决RNN的问题,有研究者提出了LSTM,其结构如下:
其内部有三个门(上图中三个$\sigma$),从左到右分别为遗忘门、输入门和输出门。这三个门就是三个全连接网络。遗忘门用来选择性遗忘上一个节点传来的信息,输入门用来选择性记忆输入的信息,输出门用来筛选输出的信息。
将输入token $x_t$和前一时刻的状态$h_{t-1}$应用concat操作,作为三个门的输入:
$$
f_t = \sigma(W_f[h_{t-1},x_t]+b_f)
$$
$$
i_t = \sigma(W_i[h_{t-1},x_t]+b_i)
$$
$$
o_t = \sigma(W_o[h_{t-1},x_t]+b_o)
$$
此外,LSTM也有另外一个输入:记忆节点状态${c}_t$。在图中,其从节点左上角输入。根据图中结构:
$$
h_t=o_t\circ tanh(c_t)
$$
$$
c_t=f_t\circ c_{t-1}+i_t\circ tanh(W_c[h_{t-1},x_t]+b_c)
$$
优点:易于长期记忆重要信息。
缺点:参数量太多,不易训练。
GRU(门控循环单元)
LSTM参数太多,那么就简化一下:
从左到右的两个门分别为重置门、更新门,公式如下:
优点:平衡了易训练性和记忆性。
缺点:这个算是循环网络的通病,在训练长序列时,根据链式法则,总会出现梯度下降、梯度爆炸的问题。
基于语法树的代码表示学习
Tree-LSTM
和LSTM差不多,只不过有多个记忆节点状态和前时刻状态而已。假设一个节点有$k$个子节点,那么该cell中,就有$k$个遗忘门,将每个记忆节点状态输入对应的遗忘门中,将结果相加,就等价于LSTM的遗忘门输出。
对于前时刻状态,则将他们加起来即可。
结构如下:
基于序列的LSTM可以看作树LSTM的特例。
TBCNN
NLP也从CV那里获得了很多有趣的灵感,比如将CNN应用到NLP相关人物的求解上去。
TBCNN结构如下:
其步骤为:
- 获取AST,并向量化。
- 定深窗口卷积。用一个定深的窗口检测器在树上滑动卷积,缺位补0。
- 动态池化。
- 输出。
分阶段的代码表示学习
也可以直接将CNN应用到语句上,比如将语句利用one-hot表示,然后变换成一个矩阵,用CNN卷积、池化,接着将每一行都导入LSTM当中去,池化输出。
ASTNN
采用分治算法,将大AST按照语句粒度分解为小AST,接着通过RvNN编码,输入双向RNN中,池化,输出。
基于图的代码表示学习
待更新…