开始的话:
学习了快一年的深度学习中自然语言处理相关的知识包括:各种各样的模型、各类的task,也参加了各种各样的比赛。所以准备好好整理下自己学过的这些知识,从头开始,打好基础(代码基础和理论基础)。为成为一个从容的有经验的攻城狮不断努力。
一位爱生活爱技术来自火星的程序汪
RNN 想必从开始学习深度学习的时候就开始接触这个很简单但是很重要的结构了。
所以今天就先从 RNN 开始我的笔记之旅吧!
话不多说先上图:
-> 表示不同的时间步
-> 表示此时刻的输入
-> 表示此时刻的输出
-> 表示cell中的weights
这里的的输出,不仅仅取决于,同时还取决于的值。
公式为(在tensorflow的源码实现中会有一点差别后面再讲):
表示的就是激活函数activation,在basic_rnn的tensorflow代码中,没有指定的话,默认就是tanh作为激活函数。
下面就从代码角度看下:
def basic_rnn_demo():
cell = tf.nn.rnn_cell.BasicRNNCell(num_units=4)
zero_state = cell.zero_state(batch_size=2, dtype=tf.float32)
a = tf.random_normal([2, 3, 4])
out, state = tf.nn.dynamic_rnn(
cell=cell,
initial_state=zero_state,
inputs=a
)
print(out)
print(state)
tf.nn.rnn_cell.BasicRNNCell 是最基础的一个RNN的实现了,接下来就说明下这个方法具体做了些什么。
下面这段代码是主要的实现源码
def call(self, inputs, state):
"""Most basic RNN: output = new_state = act(W * input + U * state + B)."""
gate_inputs = math_ops.matmul(
array_ops.concat([inputs, state], 1), self._kernel)
gate_inputs = nn_ops.bias_add(gate_inputs, self._bias)
output = self._activation(gate_inputs)
return output, output
结合demo代码说明下具体流程
Most basic rnn
tanh(W * input + U * state + B)
# inputs [2,3,4] state = [2,4]
# unstack(inputs) = 3 size [2,4]
# 每次进行的是每个batch中的第一个字
# a = [2, 8]
# zero_state 也可以不传,但是要指定dtype,这样会自己初始化一个zero_state了
# 这里可以看到此时刻的输入和上一时刻的state也就是上一刻的输出指直接concat
# 然后和weights做matmul
a = concat([inputs,state], 1)
# kernel_ = [8, 4] inputs_dim=4 num_units=4 kernel_在每个时间步都是共享的
kernel_ = [inputs_dim + num_units,num_units]
# b = [2, 4]
b = matmul(a, kernel_)
# c = [2, 4] bias初始化为0 bias 在每个时间步都是共享的
c = b + bias
# [2, 4]
# 会返回一个tuple,内容都是output,一个作为此时刻的state,这样state就可以了从第一个一直往后更新(默认的activation就是tanh)
output,output = tanh(c)
dynamic_rnn和static_rnn,前者支持变长,后者不支持。并且前者是循环展开每一个cell,更加的高效,而后者是创建一个静态的graph。
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.
output,state = tf.nn.dynamic_rnn()
out的shape为: [2, 3, 4] 表示的是每个时间步的输出
tf.Tensor(
[[[ 0.7875833 0.11634824 0.31249827 0.11648687]
[ 0.6418752 -0.9281747 0.6534868 0.3821376 ]
[ 0.9750985 -0.40439364 0.9770327 0.8529797 ]]
[[-0.09945039 -0.49678802 -0.32603818 0.20098403]
[-0.57557577 0.15389016 -0.7197561 -0.36572933]
[ 0.4485007 -0.51780844 -0.6015551 0.16041796]]], shape=(2, 3, 4), dtype=float32)
state的shape为: [2, 4] 表示最后的状态输出
tf.Tensor(
[[ 0.9750985 -0.40439364 0.9770327 0.8529797 ]
[ 0.4485007 -0.51780844 -0.6015551 0.16041796]], shape=(2, 4), dtype=float32)