本文代码来自于其他人的博客:
import tensorflow as tf
import numpy as np
tf.reset_default_graph()
x = np.random.randn(2,3, 4)
x[1, 2:] = 0
x_length = [3, 2]
cell = tf.nn.rnn_cell.LSTMCell(num_units=2, state_is_tuple=True)
outpus, last_states = tf.nn.dynamic_rnn(
cell=cell,
dtype=tf.float64,
sequence_length=x_length,
inputs=x)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
o = sess.run(outpus)
s = sess.run(last_states)
print("output.shape: ", o.shape)
print("output: \n", o)
print("last_o: \n", o[:, -1, :]) #it is the s.h
print("--" * 30)
#print("last_states.shape: ", s.shape)
print("last_states: \n", s)
print("last_state.c: \n", s.c)
print("last_states.h: \n", s.h)
输出结果:
output.shape: (2, 3, 2)
output:
[[[ 0.08568362 -0.01016954]
[ 0.00164345 0.01237355]
[-0.11443694 -0.16274641]]
[[-0.33810379 -0.2537948 ]
[-0.27564873 -0.18751839]
[ 0. 0. ]]]
last_o:
[[-0.11443694 -0.16274641]
[ 0. 0. ]]
------------------------------------------------------------
last_states:
LSTMStateTuple(c=array([[-0.63167065, -0.27030757],
[-0.9166081 , -0.43904415]]), h=array([[-0.11443694, -0.16274641],
[-0.27564873, -0.18751839]]))
last_state.c:
[[-0.63167065 -0.27030757]
[-0.9166081 -0.43904415]]
last_states.h:
[[-0.11443694 -0.16274641]
[-0.27564873 -0.18751839]]
本文主要包含三个部分:分别是(1)如何根据已有库函数写RNN;(2)整个程序设计框架;(3)对数据填充的理解。
(1)如何根据已有库函数写RNN
如果我们需要使用循环神经网络来建模型,做预测,那么有两种方案;1.调用库函数,将各种库函数拼装起来;2.字节写循环神经网络库函数模型。本文仅介绍第一种情况。
下文引用代码为tensorflow 自带代码https://tensorflow.google.cn/api_docs/python/tf/nn/dynamic_rnn
创建单层RNN 模型,输出为 outputs 和 state,其中,outputs是所有时间步max_time上输出门产生的向量,向量形状为
[batch_size, max_time, cell_state_size]
state 为最后一个时间步上产生的向量,包括记忆单元和输出单元,即(cell,hidden_state),向量形状为
[batch_size, cell_state_size]
创建单层RNN模型
# create a BasicRNNCell
rnn_cell = tf.nn.rnn_cell.BasicRNNCell(hidden_size)
# 'outputs' is a tensor of shape [batch_size, max_time, cell_state_size]
# defining initial state
initial_state = rnn_cell.zero_state(batch_size, dtype=tf.float32)
# 'state' is a tensor of shape [batch_size, cell_state_size]
outputs, state = tf.nn.dynamic_rnn(rnn_cell, input_data,
initial_state=initial_state,
dtype=tf.float32)
创建多层RNN模型时,如下,outputs 向量形状为
[batch_size, max_time, 256]
# create 2 LSTMCells
rnn_layers = [tf.nn.rnn_cell.LSTMCell(size) for size in [128, 256]]
# create a RNN cell composed sequentially of a number of RNNCells
multi_rnn_cell = tf.nn.rnn_cell.MultiRNNCell(rnn_layers)
# 'outputs' is a tensor of shape [batch_size, max_time, 256]
# 'state' is a N-tuple where N is the number of LSTMCells containing a
# tf.contrib.rnn.LSTMStateTuple for each cell
outputs, state = tf.nn.dynamic_rnn(cell=multi_rnn_cell,
inputs=data,
dtype=tf.float32)
如果需要些RNN模型,只需要套上面的代码即可。
(2)整个程序设计框架
模型的主体已经写好了,或者说模型的影藏层已经完成,现在还差输入层和输出层。
对于输入层来说,需要规范送入模型的数据格式,因为送入模型的数据的向量形状为
[batch_size, time_step, embedding_size]
而通常我们得到的数据格式可能并不是这样的,因此,在昨晚数据预处理之后,需要将数据形状调整为上述向量形状,不然会直接报错。
对于输出层来说,需要获取影藏层的数据,这里的数据有两种,一种是在所有时间步上产生的output,还有一种是最后时刻产生的state。注意:
outputs[:, -1, :] == state.h
因此,如果需要将产生的数据送入下一层,可以直接从 output 和 state 中提取特征。这两类数据格式需要参考 隐藏层神经单元数目,最内层数据维度为神经单元数目。因此,在进一步做全连接时,要注意权重矩阵的设计。
(3)对数据填充的理解
在进行自然语言处理时,可能每句话长度不一致,但是在我们将向量送入模型时,要求向量长度是一致的,因此需要进行填充,但是如果仅完成填充,但是在训练时不把这些填充的数据去掉的话,那么这些位置的向量也会学到特征,并进一步影响梯度计算。因此需要跳过填充部分的计算。
dynamic_rnn 采取主动告知模型向量有效长度的方式来避免计算这些填充,即 sequence_length 。上述代码中,
x_length = [3, 2] ,表示在第一个 batch 中时间步有效长度为 3,第二个 batch 中,时间步有效长度为 2,从 output 中可以看到[1, 2, :]被填充为 0,在输出的 state 中可以看到,
0:[-0.11443694 -0.16274641]
1:[-0.27564873 -0.18751839]
第 0 行向量就是第一个 batch 的最后 output, 因为这里没有填充,使用的是最后一个时间步的向量。
第 1 行向量就是第二个 batch 的最后 output, 因为有效时间步为 2,因此最后输出为第二个时间步的向量。这个向量用于迭代计算,但是该位置上的向量没有意义。
last_o:
[[-0.11443694 -0.16274641]
[ 0. 0. ]]
从[0, 0]就可以看到。
最后送上一个完整使用案例,该案例也是从书上抄到的,根据历史时间汇率 完成汇率预测任务。
import tensorflow as tf
import numpy as np
import pandas as pd
import sys
tf.reset_default_graph()
round_iter = 3
learnrate = 0.001
cell_count = 3
unit_count = 5
path = r"E:\tf_project\练习\exchangeData.txt"
file_data = pd.read_csv(path,
dtype=np.float32,
engine="python",
header=None)
#shape (32, 1) ==> (32,)
whole_data = np.reshape(file_data.as_matrix(), (-1))
test_data = whole_data[-cell_count: ]
print("test_data: %s\n"%test_data)
row_count = whole_data.shape[0] - cell_count
print("row_count: %d\n"%row_count)
x_data = [whole_data[i: i + cell_count] for i in range(row_count)]
y_traindata = [whole_data[i + cell_count] for i in range(row_count)]
print("x_data: %s\n" %x_data)
print("y_train_data: %s\n"%y_traindata)
x = tf.placeholder(shape=[cell_count], dtype=tf.float32)
y_train = tf.placeholder(dtype=tf.float32)
cell = tf.nn.rnn_cell.LSTMCell(unit_count)
init_state = cell.zero_state(1, dtype=tf.float32)
#outputs shape ==> [1, cell_count, unit_count]
#findal_state shape ==> [1, unit_count]
outputs, findal_state = tf.nn.dynamic_rnn(cell,
tf.reshape(x, [1, cell_count, 1]),
initial_state=init_state,
dtype=tf.float32)
outputs_reshape = tf.reshape(outputs, [cell_count, unit_count])
w2 = tf.Variable(tf.random_normal([unit_count, 1]), dtype=tf.float32)
b2 = tf.Variable(0.0, dtype=tf.float32)
y = tf.reduce_sum(tf.matmul(outputs_reshape, w2) + b2)
loss = tf.abs(y - y_train)
optimizer = tf.train.RMSPropOptimizer(learnrate)
train = optimizer.minimize(loss)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(round_iter):
loss_sum = 0.0
for j in range(row_count):
result = sess.run([train, x, y_train, y, outputs, findal_state, loss],
feed_dict={x: x_data[j], y_train: y_traindata[j]})
loss_sum = loss_sum + float(result[-1])
if j == (row_count - 1):
print("i: %d,x: %s, y_train: %s, y: %s, output: %s, final_state: %s, loss: %s, avgloss:%10.10f\n"
%(i, result[1], result[2], result[3], result[4], result[5], result[6], (loss_sum/row_count)))
exchangeData.txt内容为
6.5379
6.5428
6.5559
6.5321
6.5062
6.5062
6.5062
6.5062
6.4909
6.5029
6.4933
6.4874
6.4874
6.4874
6.4973
6.5262
6.5054
6.5045
6.4606
6.4606
6.4349
6.4415
6.4329
6.4174
6.3989
6.3989
6.4034
6.4017
6.3550
6.3188
6.3198
6.3198