解读tensorflow之rnn:
该开始接触RNN我们都会看到这样的张图:
如上图可以看到每t-1时的forward的结果和t时的输入共同作为这一次forward的输入
所以RNN存在一定的弊端,就是如果输入足够的长,因为每一次forward都会带有之前数据的信息,就会使效果变差:
“张三走了!天气也不错,我要去打篮球。”这句话的重点肯定在于天气不错所以我去打球,而不是因为张三走了,所以如果句子太长就会造成RNN在之后的训练中含有太多前面的冗余信息,使得效果变差。
为解决这类问题便有了LSTM,这里只做简单的RNN实现,便先不介绍。
RNN为什么有俩种形态? 先说结论:
- 左侧是RNN的原始结构, 右侧是RNN在时间上展开的图
- 我认为RNN本质上和全连接网络相同(全连接网络对一个样本做一次forward,RNN对一个样本做多次forward)
为什么可以根据时间维度展开,这主要是因为RNN的的输入是具有时间序列的。这一点是和全连接网络最大的不同,输入决定了RNN的结构
假设RNN的输入是一句话,这句话中有多个单词,那么RNN需要forward多次,如下图
- 橙色部分是上一个时刻的隐层的值,可以直观的理解为“记忆”
- 当前时刻的输出与当前时刻的输入还有记忆有关。
- RNN对一个样本需要做多次forward,这一点与全连接网络不一样,全连接网络对一个样本只做一次forward。
下面进行一个简单句子的RNN实现:(输入前一个字符预测它的后一个字符)
基于tensorflow的RNN实现中我们的难点只在与它参数的设置规则,深层次的处理不做研究。
1.首先做一个最基本的文本处理:
获取原字符串中含有字符的列表,循环遍历出列表内的值以及它的索引,构建新的字典(列表的值作为键,对应值的索引作为值),在字典里取出原句中包含所有字符的索引构成新的列表(由数字组成)。
看一张图: 这是一句 “hihello” 它进行rnn训练时某一时刻的流程图
总体意思就是通过输入h预测i,i预测h,h预测e,以此类推,最后再次从头来过,通过这种不断循环更新这个字母后下面是哪一个字母出现的概率,来获得模型。
所以对于一句话来说:输入要去掉最后一个。输出要去掉第一个
sample = " 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao) "
idx2char = list(set(sample)) # 去重放列表里
char2idx = {c: i for i, c in enumerate(idx2char)} # 转为字典 把字母作为键 它的索引作为值
sample_idx = [char2idx[c] for c in sample] # char to index 在字典里取出对应值
x_data = [sample_idx[:-1]] # 输入 去掉最后一个
y_data = [sample_idx[1:]] #输出 去掉第一个
print(x_data)
print(y_data)
2.设置构建RNN所需要的参数
rnn_hidden_size是可以自己设定的,在下一篇
实验:构建cell时num_units的个数怎么影响输出
中展示。
dic_size = len(char2idx) #字典长度
rnn_hidden_size = len(char2idx) # 单元个数 cell中神经元的个数
num_classes = len(char2idx)
batch_size = 1 # 一个样本数据,一个批次
sequence_length = len(sample) - 1 # 序列的个数
3.定义占位符并且进行独热编码的转化
X = tf.placeholder(tf.int32, [None, sequence_length]) # X data
Y = tf.placeholder(tf.int32, [None, sequence_length]) # Y label
X_one_hot = tf.one_hot(X, num_classes) # one hot: 1 -> 0 1 0 0 0 0 0 0 0 0
4.构建RNN
state_is_tuple=True 必须写 它规定了状态信息的格式
initial_state = cell.zero_state(batch_size, tf.float32)为RNN的初始化状态,一般都这样定义
cell = tf.contrib.rnn.BasicLSTMCell(num_units=rnn_hidden_size, state_is_tuple=True)
initial_state = cell.zero_state(batch_size, tf.float32)
outputs, _states = tf.nn.dynamic_rnn(cell, X_one_hot , initial_state=initial_state, dtype=tf.float32)
5.加一层全连接,相当于加一层深度,使预测更准确
outputs = contrib.layers.fully_connected(inputs=outputs, num_outputs=num_classes, activation_fn=None)
6.定义损失、训练过程和结果的获取
weight为t时与t+1时之间的权重
最后的outputs是三维的 所以axis=2
weights = tf.ones([batch_size, sequence_length])
sequence_loss = tf.contrib.seq2seq.sequence_loss(logits=outputs, targets=Y,weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)
prediction = tf.argmax(outputs, axis=2)
7.进行训练的输入和打印结果
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# print(sess.run(X_one_hot,feed_dict={X: x_data}))
for i in range(3000):
l, _ = sess.run([loss, train], feed_dict={X: x_data, Y: y_data})
result = sess.run(prediction, feed_dict={X: x_data})
# print char using dic
result_str = [idx2char[c] for c in np.squeeze(result)]
print(i, "loss:", l, "Prediction:", ''.join(result_str))
print(len(result_str))
整体代码为:
import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf
from tensorflow.contrib import rnn
from tensorflow import contrib
import numpy as np
sample = " 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao) "
idx2char = list(set(sample)) # 去重放列表里
char2idx = {c: i for i, c in enumerate(idx2char)} # 转为字典 把字母作为键 它的索引作为值
sample_idx = [char2idx[c] for c in sample] # 在字典里取出对应值
x_data = [sample_idx[:-1]] # 输入 去掉最后一个
y_data = [sample_idx[1:]] #输出 去掉第一个
print(x_data)
print(y_data)
# 一些参数
dic_size = len(char2idx)
rnn_hidden_size = len(char2idx)
num_classes = len(char2idx)
batch_size = 1
sequence_length = len(sample) - 1
X = tf.placeholder(tf.int32, [None, sequence_length]) # X data
Y = tf.placeholder(tf.int32, [None, sequence_length]) # Y label
X_one_hot = tf.one_hot(X, num_classes)
cell = tf.contrib.rnn.BasicLSTMCell(num_units=rnn_hidden_size, state_is_tuple=True)
initial_state = cell.zero_state(batch_size, tf.float32)
outputs, _states = tf.nn.dynamic_rnn(cell, X_one_hot , initial_state=initial_state, dtype=tf.float32)
outputs = contrib.layers.fully_connected(inputs=outputs, num_outputs=num_classes, activation_fn=None)
weights = tf.ones([batch_size, sequence_length])
sequence_loss = tf.contrib.seq2seq.sequence_loss(logits=outputs, targets=Y,weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)
prediction = tf.argmax(outputs, axis=2)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# print(sess.run(X_one_hot,feed_dict={X: x_data}))
for i in range(3000):
l, _ = sess.run([loss, train], feed_dict={X: x_data, Y: y_data})
result = sess.run(prediction, feed_dict={X: x_data})
# print char using dic
result_str = [idx2char[c] for c in np.squeeze(result)]
print(i, "loss:", l, "Prediction:", ''.join(result_str))
print(len(result_str))
输出结果为:
可以看到循环3000次训练的结果与原句子完全一致!!!
2991 loss: 0.101450786 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
2992 loss: 0.10137266 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
2993 loss: 0.1012946 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
2994 loss: 0.10121668 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
2995 loss: 0.10113874 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
2996 loss: 0.101061 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
2997 loss: 0.10098329 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
2998 loss: 0.10090562 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
2999 loss: 0.10082806 Prediction: 这是一个基于tensorflow的RNN短句子练习 (CSDN_qihao)
遗留问题:
构建cell时num_units的个数怎么影响输出???
解决方式点这里!!RNN:构建cell时num_units的个数怎么影响输出