RNN在时间维度上做循环,将当前step的输入和之前的输出状态混合计算作为当前step的输出。
LSTM 解决the long-term dependency problem
贯穿网络始终的核心是cell state ,起了conveyor belt传送带的作用,LSTM使用门gate来对cell state中的信息进行添加和删减。
一个gate的大致结构为,黄色部分为sigmoid控制的layer,以0到1的概率允许信息的通过。
- 忘记门
基于上一step的输出$h_{t-1}$和当前的输入$x_{t}$操作,决定上一step的信息有多少应该被保留并送入当前step的cell中,当我们看到新的主语,我们希望忘记旧的主语。 例如,他今天有事,所以我。。。当处理到‘’我‘’的时候选择性的忘记前面的’他’,或者说减小这个词对后面词的作用。
- 输入门
两步操作,sigmoid生成一个向量,tanh生成一个向量,在下一步中做组合。我们希望增加新的主语的类别到细胞状态中,来替代旧的需要忘记的主语。
例如:他今天有事,所以我。。。。当处理到‘’我‘’这个词的时候,就会把主语我更新到细胞中去。
- 输出门
这个门将旧状态$C_{t-1}$最终更新到新的当前输出状态$C_t$中,用到的变量也是前面的门计算出来的数值
状态的计算,通过这三个门计算就能得出,但实际的输出$h_t$还需一步:
如果要得到最终的输出, 一般来说还需要添加一个全连接层。
一个简单的伪代码示例:
关于LSTM的参数个数:
双向LSTM
单向的 RNN,是根据前面的信息推出后面的信息,双向网络照顾了前后两个方向的信息,对候选信息的判定更加精准。
Tensorflow中双向LSTM接口为tensorflow.nn.bidirectional_dynamic_rnn()
函数的输出为一个tuple(outputs,output_state)。对于outputs来说存储了fw和bw两个方向的输出tensor,shape为[batch_size, step,cell.state_size],最终结果需要将两者做一个concat,tf.concat(outputs,2),shape变为[batch_size,step,2*cell.state_size]。从上面可以看出,LSTM输出的最后一个dim维度其实就是由隐层单元(状态数量)决定的。特别注意的是bidirectional_dynamic_rnn不支持多层cell,只能传递单层cell,如果要使用多层cell,只能在外面循环调用多次接口来实现,比较坑。
tensorflow中的rnn接口和dynamic_rnn接口的区别在于动态创建和静态创建step:
tf.nn.rnn creates an unrolled graph for a fixed RNN length. That means,
if you call tf.nn.rnn with inputs having 200 time steps you are creating
a static graph with 200 RNN steps. First, graph creation is slow.
Second, you’re unable to pass in longer sequences (> 200) than you’ve
originally specified.tf.nn.dynamic_rnn solves this. It uses a tf.While
loop to dynamically construct the graph when it is executed. That means
graph creation is faster and you can feed batches of variable size.