RNN及RNN的几个变体
序列形的数据不太好用原始的神经网络处理,为了建模序列问题,RNN引入了隐状态h(hidden)的概念,h可以对序列形的数据提取特征,接着转换为输出。
圆圈或方块表示的是向量
一个箭头就表示对该向量做一次变换。如上图中h0和x1分别有一个箭头连接,就表示对h0和x1各做了一次变换
Tips:U、W、b都是一样的,每个步骤的参数都是共享的,这是RNN的重要特点。
依次计算剩下来的(使用相同的参数U、W、b):
输出值的方法就是直接通过h进行计算:
一个箭头就表示对对应的向量做一次类似于f(Wx+b)的变换,这里的这个箭头就表示对h1进行一次变换,得到输出y1
输入是x1, x2, …..xn,输出为y1, y2, …yn,也就是说,输入和输出序列必须要是等长的。
一些问题适合用经典的RNN结构建模,如:
- 计算视频中每一帧的分类标签。因为要对每一帧进行计算,因此输入和输出序列等长。
- 输入为字符,输出为下一个字符的概率。这就是著名的Char RNN(详细介绍请参考:The Unreasonable Effectiveness of Recurrent Neural Networks)。
N vs 1 多输入单输出
这种结构通常用来处理序列分类问题。如输入一段文字判别它所属的类别,输入一个句子判断其情感倾向,输入一段视频并判断它的类别等等。
1 VS N 单输入多输出
输入不是序列而输出为序列的情况怎么处理?我们可以只在序列开始进行输入计算:
还有一种结构是把输入x作为每个阶段的输入:
这种结构处理的问题:
- 从图像生成文字(image caption),此时输入的X就是图像的特征,而输出的y序列就是一段句子
- 从类别生成语音或音乐等
N vs M
RNN最重要的一个变种:N vs M。这种结构又叫Encoder-Decoder模型,也可以称之为Seq2Seq模型。
为此,Encoder-Decoder结构先将输入数据编码成一个上下文向量c:
c有多种方式,最简单的方法就是把Encoder的最后一个隐状态赋值给c,还可以对最后的隐状态做一个变换得到c,也可以对所有的隐状态做变换。
拿到c之后,就用另一个RNN网络对其进行解码,这部分RNN网络被称为Decoder。具体做法就是将c当做之前的初始状态h0输入到Decoder中:
还有一种做法是将c当做每一步的输入:
Encoder-Decoder结构不限制输入和输出的序列长度,因此应用的范围非常广泛,比如:
- 机器翻译。Encoder-Decoder的最经典应用,事实上这一结构就是在机器翻译领域最先提出的
- 文本摘要。输入是一段文本序列,输出是这段文本序列的摘要序列
- 阅读理解。将输入的文章和问题分别编码,再对其进行解码得到问题的答案。
- 语音识别。输入是语音信号序列,输出是文字序列。
- ………
tensorflow 实现RNN
RNNCell
RNNCell 是TensorFlow中实现RNN的基本单元,每个RNNCell都有一个call方法,使用方式是:(output, next_state) = call(input, state)
借助图片来说可能更容易理解。假设我们有一个初始状态h0,还有输入x1,调用call(x1, h0)后就可以得到(output1, h1):
再调用一次call(x2, h1)就可以得到(output2, h2):
也就是说,每调用一次RNNCell的call方法,就相当于在时间上“推进了一步”,这就是RNNCell的基本功能。
RNNCell只是一个抽象类,其他的RNNcell都会继承该方法,然后具体实现其中的call()函数。主要有state_size和output_size两个属性,分别代表了隐藏层和输出层的维度。然后就是zero_state()和call()两个函数,分别用于初始化初始状态h0为全零向量和定义实际的RNNCell的操作(比如RNN就是一个激活,GRU的两个门,LSTM的三个门控等,不同的RNN的区别主要体现在这个函数)。
BasicRNNCell
把state_size和output_size定义成相同,而且ht和output也是相同的
最普通的RNN定义方式。也就是说output = new_state = f(W input + U state + B)
GRU
相比BasicRNNCell只改变了call函数部分,增加了重置门和更新门两部分,分别由r和u表示。然后c表示要更新的状态值。其对应的公式如如下所示:
r = f(W1 input + U1 state + B1)r=f(W1∗input+U1∗state+B1)
u = f(W2 input + U2 state + B2)u=f(W2∗input+U2∗state+B2)
c = f(W3 input + U3 r state + B3)c=f(W3∗input+U3∗r∗state+B3)
h_new = u h + (1 - u) * chnew=u∗h+(1−u)∗c
BasicLSTMCell
相比GRU,LSTM又多了一个输出门,而且又新增添了一个C表示其内部状态,然后将h和c以tuple的形式返回作为LSTM内部的状态变量。
我们用的时候都是用的它的两个子类BasicRNNCell和BasicLSTMCell。顾名思义,前者是RNN的基础类,后者是LSTM的基础类。看下RNNCell、BasicRNNCell、BasicLSTMCell这三个类的注释部分,应该就可以理解它们的功能了。
除了call方法外,对于RNNCell,还有两个类属性比较重要:
- state_size
- output_size
前者是隐层的大小,后者是输出的大小。比如我们通常是将一个batch送入模型计算,设输入数据的形状为(batch_size, input_size),那么计算时得到的隐层状态就是(batch_size, state_size),输出就是(batch_size, output_size)。
|
|
BasicLSTMCell
|
|
参考链接:
https://zhuanlan.zhihu.com/p/28054589
https://zhuanlan.zhihu.com/p/28196873