【NLP】智能软件工程课程笔记
2023-09-16 17:51:33

引子

这门课本来是研究生课程,不知道为什么,本学期也给本科生开了。

说是上课,不过是老师带着学生读论文而已。

这门课所讲述的内容,是将NLP相关的模型,用于软件工程上,简化相应工作。

我将老师讲述的部分模型记录于此,权当备忘。

代码表示学习和几类代表性任务

代码表示学习,就是将代码用一系列张量表示,通过机器学习的相关模型,学习出一些特征来。在软件开发中,常见的几类代码表示学习任务有:

  1. Bug定位。将缺陷报告和源文件作为输入,输出为缺陷代码具体的位置,免去人工调试定位。
  2. 代码分类。
  3. 代码生成。将用户需求作为输入,输出实现对应功能的代码。
  4. 代码翻译。将Java程序翻译成C程序。

根据编译原理的相关知识,源代码变为目标代码需要经历以下几个步骤:

  1. 词法分析,该步生成token序列。
  2. 语法分析,该步生成抽象语法树(AST)。
  3. 语义分析,该步可以生成图,比如数据依赖图(DDG)、控制依赖图(CDG)等。
  4. 中间代码生成。
  5. 优化。
  6. 目标代码生成。

那么代码表示学习大致可以分为三类:

  1. 基于序列的代码表示学习。
  2. 基于树的代码表示学习。
  3. 基于图的代码表示学习。

基于序列的代码表示学习

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结构如下:

其步骤为:

  1. 获取AST,并向量化。
  2. 定深窗口卷积。用一个定深的窗口检测器在树上滑动卷积,缺位补0。
  3. 动态池化。
  4. 输出。

分阶段的代码表示学习

也可以直接将CNN应用到语句上,比如将语句利用one-hot表示,然后变换成一个矩阵,用CNN卷积、池化,接着将每一行都导入LSTM当中去,池化输出。

ASTNN

采用分治算法,将大AST按照语句粒度分解为小AST,接着通过RvNN编码,输入双向RNN中,池化,输出。

基于图的代码表示学习

待更新…