RNN是什么样子呢?网上一大堆我就不重复了,我这里大致画了一个展开形式的
那输入是什么样子呢?比如一句话有趣的灵魂重两百斤,计算机不认识字的,只认识数字。我们将每个字按照字典的位置编码,假如字典有1000个字,有这个字再字典的第100个位置,趣在字典的第107个位置,依次类推,我们可以将这句话编码成(我瞎写的)
[100,107,109,111,245,345,567,789,999]
这样就相当于一个样本。假如有很多这样的话,长短不一,我们就可以得到很多个这样的编码
[1,101,9]
[7,77,777,177,377]
[100,107,109,111,245,345,567,789,999]
如果我们是以单个字输入,每一个输入就是一个数字 n _ i n p u t = 1 n\_input=1 n_input=1,相当于输入神经只有一个,我们也可以对单个字进行one-hot编码,每个字都对应了一个1000维的向量,这个向量上相应的位置为1,例如,“我”在字典的第1个位置,“你”在字典的第2个位置,那么
我 -> [1,0,0,0,0,other is zero]
你 -> [0,1,0,0,0,other is zero]
以此类推,这样输入就是一个向量
n
_
i
n
p
u
t
=
1000
n\_input=1000
n_input=1000,输入就有1000个神经元。
如果我们以一句话为一个样本,**“有趣的灵魂重两百斤”这句话有8个字,依次输入这8个字,即8个1000维的向量依次输入RNN中,此时
n
_
s
t
e
p
s
=
8
n\_steps=8
n_steps=8。“我爱你”**这句话只有3个字,这个样本输入只需要循环3次,即
n
_
s
t
e
p
s
=
3
n\_steps=3
n_steps=3,如果用的是同一个字典
n
_
i
n
p
u
t
=
1000
n\_input=1000
n_input=1000。
这些一个个样本和在一起我们可以作为一个batch,当然样本的输入神经元肯定需要一致的,即使单个字都要编码成相同的长度,不能一个样本的字是1000维,另一个样本的字编码成500维,这样输入就对不上了。但是样本的长度肯定是会有长短不一的,这个时候就需要在短的样本后面补一些PAD,使其和长的一样长。下面给出了batch的大致样子。
那么训练的时候数据是怎么输进去的呢?
每次输入数据的大小为
s
h
a
p
e
=
(
b
a
t
c
h
,
n
_
i
n
p
u
t
)
shape=(batch,n\_input)
shape=(batch,n_input),总共需要输入
n
_
s
t
e
p
s
n\_steps
n_steps次。写个伪代码
x_input = [batch,n_steps,n_input] #先将数据解压成输入格式
tmp = []
for i in range(x_input.shape[1]): #x_input.shape[1]=n_steps
tmp.append(x_input[:,i,:])
#tmp -> [[batch,n_input],[batch,n_input]...] -> len(tmp)=n_steps
#tmp.shape -> [n_steps, batch, n_input]
outputs = [] #保存每次的输出结果
state = cell.zero_state()
for x in tmp: # x -> [batch,n_input],循环n_steps次
output,state = cell(x,state)
outputs.append(output) #outputs -> [y_1,y_2,...y_steps]
return outputs,state
上面这段伪代码,其实就是两个步骤,解压数据,输入rnn,tensorflow已经帮我们写好了
x_input = [batch,n_steps,n_input]
x_input = tf.unstack(x_input,n_steps,axis=1) #解压数据
outputs,state = tf.nn.static_rnn(cell,x_input) #输入rnn
下面这个是数据解压函数
tf.unstack(
value,
num=None,
axis=0,
name='unstack'
)
Unpacks num tensors from value by chipping it along the axis dimension. If num is not specified (the default), it is inferred from value’s shape. If value.shape[axis] is not known, ValueError is raised.
For example, given a tensor of shape (A, B, C, D);
- **If axis == 0 then the i’th tensor in output is the slice value[i, :, :, :] and each tensor in output will have shape (B, C, D). **
- **If axis == 1 then the i’th tensor in output is the slice value[:, i, :, :] and each tensor in output will have shape (A, C, D). **
rnn的输入函数
tf.nn.static_rnn(
cell,
inputs,
initial_state=None,
dtype=None,
sequence_length=None,
scope=None
)
- cell: An instance of RNNCell.
- inputs: A length T list of inputs, each a Tensor of shape [batch_size, input_size], or a nested tuple of such elements.
- initial_state: (optional) An initial state for the RNN. If cell.state_size is an integer, this must be a Tensor of appropriate type and shape [batch_size, cell.state_size]. If cell.state_size is a tuple, this should be a tuple of tensors having shapes [batch_size, s] for s in cell.state_size.
- dtype: (optional) The data type for the initial state and expected output. Required if initial_state is not provided or RNN state has a heterogeneous dtype.
- sequence_length: Specifies the length of each sequence in inputs. An int32 or int64 vector (tensor) size [batch_size], values in [0, T).
- scope: VariableScope for the created subgraph; defaults to “rnn”.
对我们来说主要就是输入,可以看到要求输入是一个list,list中的每个元素的shape是
[
b
a
t
c
h
_
s
i
z
e
,
i
n
p
u
t
_
s
i
z
e
]
[batch\_size,input\_size]
[batch_size,input_size],这个list的长度其实就是
n
_
s
t
e
p
s
n\_steps
n_steps,即
x
_
i
n
p
u
t
.
s
h
a
p
e
=
(
n
_
s
t
e
p
s
,
b
a
t
c
h
,
n
_
i
n
p
u
t
)
y
_
o
u
t
p
u
t
.
s
h
a
p
e
=
(
n
_
s
t
e
p
s
,
b
a
t
c
h
,
n
_
h
i
d
d
e
n
)
x\_input.shape=(n\_steps,batch,n\_input)\\ y\_output.shape=(n\_steps,batch,n\_hidden)
x_input.shape=(n_steps,batch,n_input)y_output.shape=(n_steps,batch,n_hidden)
实际中,尤其是自然语言,我们输入的句子很多时候是长短不一的,也就是
n
_
s
t
e
p
s
n\_steps
n_steps是不一样的,这可怎么办呢?
标准的rnn会创建一个指定长度的静态的计算图,如果我们的
n
_
s
t
e
p
s
n\_steps
n_steps是固定长度200,那么就会创建一个长度为200的静态RNN,你想输入大于200的序列就不行,tensorflow提供了dynamic_rnn。下面是一个引用解释为何会不同
在每一个train step,传入model的是一个batch的数据(这一个batch的数据forward得到predictions,计算loss,backpropagation更新参数),这一个batch内的数据一定是padding成相同长度的。那么,如果可以只在一个batch内部进行padding,例如一个batch中数据长度均在6-10这个范围内,就可以让这个batch中所有数据pad到固定长度10,而整个dataset上的数据最大长度很可能是100,这样就不需要让这些数据也pad到100那么长,白白浪费空间。所以dynamic_rnn实现的功能就是可以让不同迭代传入的batch可以是长度不同数据,但同一次迭代一个batch内部的所有数据长度仍然是固定的。例如,第一时刻传入的数据shape=[batch_size, 10],第二时刻传入的数据shape=[batch_size, 12],第三时刻传入的数据shape=[batch_size, 8]等等。但是rnn不能这样,它要求每一时刻传入的batch数据的[batch_size, max_seq],在每次迭代过程中都保持不变。这样不就必须要求全部数据都要pad到统一的max_seq长度了吗?是的,但也有个折中办法。——将数据集的sequence length做个初步统计,看会落在哪几个区间段内。然后根据区间段将数据进行归类,也就是所谓的放在不同buckets中。最后用rnn为每一个buckets都创建一个sub graph。训练的时候,根据当前batch data所归属的bucket id,找到它对应的sub graph,进行参数更新(虽然是不同的sub graph,但参数是共享的。至少tensorflow中是这么实现的(≧▽≦)/)王买买提
上面是对dynamic_rnn的一个解释吧,我们主要还是看数据如何输入输出。
x
_
s
h
a
p
e
=
(
b
a
t
c
h
,
n
_
s
t
e
p
s
,
n
_
i
n
p
u
t
)
y
_
s
h
a
p
e
=
(
n
_
s
t
e
p
s
,
b
a
t
c
h
,
n
_
h
i
d
d
e
n
)
x\_shape=(batch,n\_steps,n\_input)\\ y\_shape=(n\_steps,batch,n\_hidden)
x_shape=(batch,n_steps,n_input)y_shape=(n_steps,batch,n_hidden)
所以我们需要
t
f
.
t
r
a
n
s
p
o
s
e
(
o
u
t
p
u
t
s
,
[
1
,
0
,
2
]
)
tf.transpose(outputs, [1, 0, 2])
tf.transpose(outputs,[1,0,2]),这样就可以取到最后一步的output