1、MLP,很好理解,就是一张网络清楚地显示了张量流向。general MLP是这样的拓扑: Xi 为输入特征向量,蓝色中间层为多个隐藏层,Y对应的是输出向量。
CNN也好理解,跟MLP无差若干 。CNN是这样的拓扑:
RecurrentNNs 结构理解
的拓扑发生了一个很大的改动,即一个MLP会在time_step这个维度上进行延伸,每个时序都会有input。
将1中的MLP图旋转并引入时间概念,如下视频
视频中的t我们称为时间步(time_step),每个t称为1步,t1-t5为1个周期。然后引入记忆概念。把上一个时间步产生的结果( Yt-1 )同X一起输入进去。RNN之所以有记忆力,是因为在每个t完成后,其产生的结果会在下一个t开始时,与X一起输送给RNN运算,相当于输入中包含了之前所有t的「精华」
或者可以参考这个图
每个时序t的输入 是一次time_step一张input tensor,隐状态 也就代表了一张MLP的hidden layer的一个cell。输出 理解无异。注意,红色的箭头指向时间维度。
4、简单例子讲解
例子1,文本分类
如果有一条长文本,给句子事先分割好句子,并且进行tokenize, dictionarize,接着再由look up table 查找到embedding,将token由embedding表示,再对应到上图的输入。流程如下
step1, raw text:
接触LSTM模型不久,简单看了一些相关的论文,还没有动手实现过。然而至今仍然想不通LSTM神经网络究竟是怎么工作的。……
step2, tokenize (中文得分词):
sentence1: 接触 LSTM 模型 不久 ,简单 看了 一些 相关的 论文 , 还 没有 动手 实现过 。
sentence2: 然而 至今 仍然 想不通 LSTM 神经网络 究竟是 怎么 工作的。
……
step3, dictionarize:
sentence1: 1 34 21 98 10 23 9 23
sentence2: 17 12 21 12 8 10 13 79 31 44 9 23
……
step4, padding every sentence to fixed length:
sentence1: 1 34 21 98 10 23 9 23 0 0 0 0 0
sentence2: 17 12 21 12 8 10 13 79 31 44 9 23 0
……
step5, mapping token to an embeddings:
sentence1:
,每一列代表一个词向量,词向量维度自行确定;矩阵列数固定为time_step length。
sentence2:
……
step6, feed into RNNs as input:
假设 一个RNN的time_step 确定为l, 则padded sentence length(step5中矩阵列数)固定为l。一次RNNs的run只处理一条sentence。每个sentence的每个token的embedding对应了每个时序t的输入 。一次RNNs的run,连续地将整个sentence处理完。
step7, get output:
看图,每个time_step都是可以输出当前时序t的隐状态 ;但整体RNN的输出 是在最后一个time_step t=l 时获取,才是完整的最终结果。
step8, further processing with the output:
我们可以将output根据分类任务或回归拟合任务的不同,分别进一步处理。比如,传给cross_entropy&softmax进行分类……或者获取每个time_step对应的隐状态 ,做seq2seq 网络……或者搞创新……
例子2,mnist分类
在每一个时间步,维数是[batch_size,28]的数据进入LSTM。28表示每行数据的像素数目,一共有28行,也就是28个时间步,因此输入数据的维数是[batch_size,input_size,time_steps],输出数据的维数是[batch_size,num_units(LSTM隐藏层的个数),time_steps],但是我们只需要输出最后一个时间步的结果,维数是[batch_size,num_units],然后经过一个[num_units,num_class]的矩阵变换得到[batch_size,num_class]的输出结果,再和标准的结果比较计算Loss。
5、tensorflow 的一个简单例子的实现
import tensorflow as tf
import numpy as np
def dynamic_rnn(rnn_type='lstm'):
X=np.random.rand(3,6,4)
X[1,4:]=0
X_length=[6,4,6]
rnn_hidden_size=5
if(rnn_type=='lstm'):
cell=tf.contrib.rnn.BasicLSTMCell(num_units=rnn_hidden_size,state_is_tuple=True)
else:
cell=tf.contrib.rnn.GRUCell(num_units=rnn_hidden_size)
num=cell.output_size
outputs,last_states = tf.nn.dynamic_rnn(
cell=cell,
dtype=tf.float64,
sequence_length=X_length,
inputs=X
)
with tf.Session() as session:
session.run(tf.global_variables_initializer())
o1,s1 = session.run([outputs,last_states])
print(X)
print(np.shape(o1))
print(o1)
print(np.shape(s1))
print(s1)
print(num)
if __name__ == '__main__':
dynamic_rnn(rnn_type='lstm')
代码分析
1)输入
LSTM单元中喂进的数据是一个3维数据,维度分别是input_size,batch_size,time_size。这里把X作为input的数据:
X=np.random.rand(3,6,4) #batch_size=3,time_size=6,input_size=4
input_size和Cell中的hidden_size有关;
time_size则是处理一组数据的步长;
batch_size则是用户自己选定的(通常开源文献中选为128、256等,从Memory中取出,再投喂给网络)
为了更好理解,将X的第二个batch的后两个步长数据置零,并通过X_length 设置掩码
2)构建网络
根据我们提供的hidden_size来创建LSTM单元
hidden_size=5
#创建LSTMcell
cell=tf.contrib.rnn.BasicLSTMCell(num_units=hidden_size,state_is_tuple=True)
创建LSTM网络, 给上面的LSTM单元input等参数
outputs,last_states=tf.nn.dynamic_rnn(
cell=cell,
dtype=tf.float64,
sequence_length=X_length,
input=X
)
tf.nn.dynamic_run()有两个返回值,分别是outputs和last_states,这和LSTM的的结构是有关系的。
with tf.Session() as session:
session.run(tf.global_variables_initializer())
o1,s1 = session.run([outputs,lats_states])
3)输出
可以这样理解:o1指明了每个batch上的每个time_step的每个input的输出,而s1(=C_t + H_t)则表明了经过LSTM抽象处理后的最终结果。
[batch_size,time_size,hidden_size] 就是每一个batch 和step对应的输出特征
S1的维度为: [2,batch_size,hidden_size] 2是因为包含cell支路和ht支路,每个支路输出batch size个最后一个step结束后的特征(对应维度一般为hidden size)