RNN循环神经网络的直观理解:基于TensorFlow的简单RNN例子
2017-12-26 15:08
956 查看
RNN循环神经网络的直观理解:基于TensorFlow的简单RNN例子
RNN 直观理解
一个非常棒的RNN入门Anyone Can learn To Code LSTM-RNN in Python(Part 1: RNN)基于此文章,本文给出我自己的一些愚见
基于此文章,给出其中代码的TensorFlow的实现版本。完整代码请看这里
RNN的结构
如果从网上搜索关于RNN的结构图,大概可以下面的结构图第一次看到这样的图,我是懵逼的,这货怎么有两种形态? 先说结论:
左侧是RNN的原始结构, 右侧是RNN在时间上展开的结果
RNN的结构,本质上和全连接网络相同
为什么可以根据时间维度展开,这主要是因为RNN的的输入是具有时间序列的。这一点是和全连接网络最大的不同,输入决定了RNN的结构
假设RNN的输入是一句话,这句话中有多个单词,那么RNN需要forward多次,如下图
橙色部分是上一个时刻的隐层的值,可以直观的理解为“记忆”
当前时刻的输出与当前时刻的输入还有记忆有关。
RNN对一个样本需要做多次forward,这一点与全连接网络不一样,全连接网络对一个样本只做一次forward。
就将RNN看成是全连接网络吧
将RNN看成是全连接网络就能很好的理解它。将上面gif图中的隐层中的每一个神经元看成是LSTM单元,就得到了基于LSTM的RNN
RNN的输入、输出都和全连接网络一模一样
RNN只是一个需要做好多次forward的全连接网络
一个RNN的简单例子
基于TensorFlow,搭建一个RNN,教会神经网络进行二进制加法。参考Anyone Can learn To Code LSTM-RNN in Python(Part 1: RNN)import tensorflow as tf import numpy as np # 一个字典,隐射一个数字到其二进制的表示 # 例如 int2binary[3] = [0,0,0,0,0,0,1,1] int2binary = {} # 最多8位二进制 binary_dim = 8 # 在8位情况下,最大数为2^8 = 256 largest_number = pow(2,binary_dim) # 将[0,256)所有数表示成二进制 binary = np.unpackbits( np.array([range(largest_number)],dtype=np.uint8).T,axis=1) # 建立字典 for i in range(largest_number): int2binary[i] = binary[i] def binary_generation(numbers, reverse = False): ''' 返回numbers中所有数的二进制表达, 例如 numbers = [3, 2, 1] 返回 :[[0,0,0,0,0,0,1,1], [0,0,0,0,0,0,1,0], [0,0,0,0,0,0,0,1]' 如果 reverse = True, 二进制表达式前后颠倒, 这么做是为训练方便,因为训练的输入顺序是从低位开始的 numbers : 一组数字 reverse : 是否将其二进制表示进行前后翻转 ''' binary_x = np.array([ int2binary[num] for num in numbers], dtype=np.uint8) if reverse: binary_x = np.fliplr(binary_x) return binary_x def batch_generation(batch_size, largest_number): ''' 生成batch_size大小的数据,用于训练或者验证 batch_x 大小为[batch_size, biniary_dim, 2] batch_y 大小为[batch_size, biniray_dim] ''' # 随机生成batch_size个数 n1 = np.random.randint(0, largest_number//2, batch_size) n2 = np.random.randint(0, largest_number//2, batch_size) # 计算加法结果 add = n1 + n2 # int to binary binary_n1 = binary_generation(n1, True) binary_n2 = binary_generation(n2, True) batch_y = binary_generation(add, True) # 堆叠,因为网络的输入是2个二进制 batch_x = np.dstack((binary_n1, binary_n2)) return batch_x, batch_y, n1, n2, add def binary2int(binary_array): ''' 将一个二进制数组转为整数 ''' out = 0 for index, x in enumerate(reversed(binary_array)): out += x*pow(2, index) return out
设置参数
batch_size = 64 # LSTM的个数,就是隐层中神经元的数量 lstm_size = 20 # 隐层的层数 lstm_layers =2
定义输入输出
# 输入,[None, binary_dim, 2], # None表示batch_size, binary_dim表示输入序列的长度,2表示每个时刻有两个输入 x = tf.placeholder(tf.float32, [None, binary_dim, 2], name='input_x') # 输出 y_ = tf.placeholder(tf.float32, [None, binary_dim], name='input_y') # dropout 参数 keep_prob = tf.placeholder(tf.float32, name='keep_prob')
建立模型
# 搭建LSTM层(看成隐层) # 有lstm_size个单元 lstm = tf.contrib.rnn.BasicLSTMCell(lstm_size) # dropout drop = tf.contrib.rnn.DropoutWrapper(lstm, output_keep_prob=keep_prob) # 一层不够,就多来几层 def lstm_cell(): return tf.contrib.rnn.BasicLSTMCell(lstm_size) cell = tf.contrib.rnn.MultiRNNCell([ lstm_cell() for _ in range(lstm_layers)]) # 初始状态,可以理解为初始记忆 initial_state = cell.zero_state(batch_size, tf.float32) # 进行forward,得到隐层的输出 # outputs 大小为[batch_size, lstm_size*binary_dim] outputs, final_state = tf.nn.dynamic_rnn(cell, x, initial_state=initial_state) # 建立输出层 weights = tf.Variable(tf.truncated_normal([lstm_size, 1], stddev=0.01)) bias = tf.zeros([1]) # [batch_size, lstm_size*binary_dim] ==> [batch_size*binary_dim, lstm_size] outputs = tf.reshape(outputs, [-1, lstm_size]) # 得到输出, logits大小为[batch_size*binary_dim, 1] logits = tf.sigmoid(tf.matmul(outputs, weights)) # [batch_size*binary_dim, 1] ==> [batch_size, binary_dim] predictions = tf.reshape(logits, [-1, binary_dim])
损失函数和优化方法
cost = tf.losses.mean_squared_error(y_, predictions) optimizer = tf.train.AdamOptimizer().minimize(cost)
训练
steps = 2000 with tf.Session() as sess: tf.global_variables_initializer().run() iteration = 1 for i in range(steps): # 获取训练数据 input_x, input_y,_,_,_ = batch_generation(batch_size, largest_number) _, loss = sess.run([optimizer, cost], feed_dict={x:input_x, y_:input_y, keep_prob:0.5}) if iteration % 1000 == 0: print('Iter:{}, Loss:{}'.format(iteration, loss)) iteration += 1 # 训练结束,进行测试 val_x, val_y, n1, n2, add = batch_generation(batch_size, largest_number) result = sess.run(predictions, feed_dict={x:val_x, y_:val_y, keep_prob:1.0}) # 左右翻转二进制数组。因为输出的结果是低位在前,而正常的表达是高位在前,因此进行翻转 result = np.fliplr(np.round(result)) result = result.astype(np.int32) for b_x, b_p, a, b, add in zip(np.fliplr(val_x), result, n1, n2, add): print('{}:{}'.format(b_x[:,0], a)) print('{}:{}'.format(b_x[:,1], b)) print('{}:{}\n'.format(b_p, binary2int(b_p)))
Iter:1000, Loss:0.012912601232528687 Iter:2000, Loss:0.000789149955380708 [0 1 0 1 0 0 1 0]:82 [0 0 1 1 1 1 0 0]:60 [1 0 0 0 1 1 1 0]:142 [0 1 1 1 1 1 0 0]:124 [0 1 1 0 0 0 1 0]:98 [1 1 0 1 1 1 1 0]:222 [0 0 0 1 1 0 1 0]:26 [0 0 0 0 0 1 0 1]:5 [0 0 0 1 1 1 1 1]:31 [0 0 0 1 0 0 1 1]:19 [0 0 1 0 1 0 0 1]:41 [0 0 1 1 1 1 0 0]:60 [0 1 0 1 1 0 1 1]:91 [0 1 0 0 0 0 0 0]:64 [1 0 0 1 1 0 1 1]:155 [0 1 0 1 0 1 0 0]:84 [0 0 0 0 1 1 0 1]:13 [0 1 1 0 0 0 0 1]:97 [0 1 0 0 1 0 1 1]:75 [0 0 1 0 0 0 0 1]:33 [0 1 1 0 1 1 0 0]:108 [0 0 1 1 0 1 0 0]:52 [0 1 1 1 0 0 1 0]:114 [1 0 1 0 0 1 1 0]:166 [0 1 1 1 0 0 0 0]:112 [0 0 1 0 1 1 0 1]:45 [1 0 0 1 1 1 0 1]:157 [0 0 1 1 0 1 0 1]:53 [0 1 0 0 0 0 0 1]:65 [0 1 1 1 0 1 1 0]:118 [0 0 0 0 1 0 1 1]:11 [0 1 1 0 0 1 0 1]:101 [0 1 1 1 0 0 0 0]:112 [0 0 0 0 1 1 0 0]:12 [0 0 1 0 0 1 0 0]:36 [0 0 1 1 0 0 0 0]:48 [0 0 0 0 1 0 0 1]:9 [0 1 1 0 0 0 0 1]:97 [0 1 1 0 1 0 1 0]:106 [0 0 0 1 1 1 1 0]:30 [0 1 0 0 0 0 0 1]:65 [0 1 0 1 1 1 1 1]:95 [0 0 0 1 0 0 1 1]:19 [0 1 0 0 0 0 0 1]:65 [0 1 0 1 0 1 0 0]:84 [0 1 1 0 1 1 0 0]:108 [0 0 1 0 0 0 0 1]:33 [1 0 0 0 1 1 0 1]:141 [0 0 1 1 1 1 1 1]:63 [0 0 1 0 0 0 0 1]:33 [0 1 1 0 0 0 0 0]:96 [0 0 0 1 0 1 1 1]:23 [0 0 0 1 1 1 0 1]:29 [0 0 1 1 0 1 0 0]:52 [0 0 1 1 1 1 0 0]:60 [0 1 0 0 1 0 0 1]:73 [1 0 0 0 0 1 0 1]:133 [0 1 0 0 1 1 0 0]:76 [0 1 1 1 0 1 1 1]:119 [1 1 0 0 0 0 1 1]:195 [0 1 1 0 1 1 1 1]:111 [0 0 0 1 1 0 0 1]:25 [1 0 0 0 1 0 0 0]:136 [0 0 1 1 0 0 0 1]:49 [0 1 1 0 1 0 0 0]:104 [1 0 0 1 1 0 0 1]:153 [0 1 1 1 1 1 0 0]:124 [0 1 1 0 1 0 1 1]:107 [1 1 1 0 0 1 1 1]:231 [0 0 1 1 1 0 1 1]:59 [0 0 0 1 1 0 1 1]:27 [0 1 0 1 0 1 1 0]:86 [0 1 1 0 0 0 0 0]:96 [0 0 0 1 1 1 0 1]:29 [0 1 1 1 1 1 0 1]:125 [0 0 1 1 1 0 1 0]:58 [0 1 0 0 0 1 0 0]:68 [0 1 1 1 1 1 1 0]:126 [0 0 1 1 1 1 1 0]:62 [0 1 1 0 0 1 1 1]:103 [1 0 1 0 0 1 0 1]:165 [0 1 1 1 1 0 0 0]:120 [0 0 1 0 1 1 1 0]:46 [1 0 1 0 0 1 1 0]:166 [0 0 0 0 1 1 0 1]:13 [0 0 1 1 0 0 0 0]:48 [0 0 1 1 1 1 0 1]:61 [0 0 1 0 0 1 0 0]:36 [0 0 1 1 0 0 0 1]:49 [0 1 0 1 0 1 0 1]:85 ....... ....... [0 0 1 0 0 1 1 0]:38 [0 1 0 0 1 1 0 1]:77 [0 1 1 1 0 0 1 1]:115 [0 1 0 1 1 1 1 0]:94 [0 0 1 1 1 1 1 1]:63 [1 0 0 1 1 1 0 1]:157 [0 0 0 0 0 0 1 0]:2 [0 1 0 1 0 0 0 0]:80 [0 1 0 1 0 0 1 0]:82
总结
本文向大家介绍了RNN的结构,以及从全连接网络的角度出去,去理解RNN,得到结论有:RNN的结构与全连接网络基本一致
RNN具有时间展开的特点,这是由其输入决定的
全连接网络对一个样本做一次forward,RNN对一个样本做多次forward
同时,基于TensorFlow给出一个简单的RNN网络,从实验结果看,该网络经过训练,以及能够正确进行二进制加法
相关文章推荐
- 基于循环神经网络实现基于字符的语言模型(char-level RNN Language Model)-tensorflow实现
- TensorFlow练手项目二:基于循环神经网络(RNN)的古诗生成器
- 【直观理解】一文搞懂RNN(循环神经网络)基础篇
- tensorflow 学习笔记11 最简单的循环神经网络(RNN)
- TensorFlow练手项目一:使用循环神经网络(RNN)实现影评情感分类
- tensorflow 学习笔记12 循环神经网络RNN LSTM结构实现MNIST手写识别
- 深度学习(07)_RNN-循环神经网络-02-Tensorflow中的实现
- tensorflow 循环神经网络RNN
- CNN(卷积神经网络)、RNN(循环神经网络)、DNN(深度神经网络)概念区分理解
- 使用tensorlayer来实现:通过keras例子来理解lstm循环神经网络
- 深度学习框架TensorFlow学习与应用(七)——循环神经网络(RNN)应用于MNIST数据集分类
- [置顶] 【深度学习】RNN循环神经网络Python简单实现
- Tensorflow实战-8循环神经网络RNN
- TensorFlow-RNN循环神经网络 Example 1:预测Sin函数
- 理解神经网络,从简单的例子开始(1)7行python代码构建神经网络
- RNN-LSTM循环神经网络-03Tensorflow进阶实现
- TensorFlow-RNN循环神经网络 Example 2:文本情感分析
- CNN(卷积神经网络)、RNN(循环神经网络)、DNN(深度神经网络)概念区分理解
- 基于循环神经网络(RNN)的古诗生成器
- CNN(卷积神经网络)、RNN(循环神经网络)、DNN(深度神经网络)概念区分理解