【翻译】Sklearn与TensorFlow机器学习实用指南 —— 第13章 循环神经网络(中)
Python爱好者社区专栏作者
GitHub:https://github.com/apachecn/hands_on_Ml_with_Sklearn_and_TF
前文传送门:
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— Chapter 0.前言
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第1章 机器学习概览(上)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第1章 机器学习概览(下)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第2章 一个完整的机器学习项目(上)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第2章 一个完整的机器学习项目(中)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第2章 一个完整的机器学习项目(中二)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第2章 一个完整的机器学习项目(下)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第3章 分类(上)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第3章 分类(中)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第3章 分类(下)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第4章( 上) 训练模型
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第4章( 中) 训练模型
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第4章( 下) 训练模型
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第5章( 上)支持向量机
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第5章( 中)支持向量机
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第5章( 中)支持向量机
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第5章( 下)支持向量机
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第6章 决策树
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第7章 集成学习和随机森林(上)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第7章 集成学习和随机森林 (中)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第7章 集成学习和随机森林 (下)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第8章 降维(上)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第8章 降维(下)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第9章 (上)启动并运行TensorFlow
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第9章 (中)启动并运行TensorFlow
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第9章 (下)启动并运行TensorFlow
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第10章 人工神经网络介绍(上)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第10章 人工神经网络介绍(中)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第10章 人工神经网络介绍(下)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第11章 训练深层神经网络(上)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第11章 训练深层神经网络(中)
【翻译】Sklearn 与 TensorFlow 机器学习实用指南 —— 第11章 训练深层神经网络(下)
【翻译】Sklearn与TensorFlow机器学习实用指南 —— 第12章 卷积神经网络(上)
【翻译】Sklearn与TensorFlow机器学习实用指南 —— 第12章 卷积神经网络(下)
【翻译】Sklearn与TensorFlow机器学习实用指南 —— 第13章 循环神经网络(上)
处理变长输入序列
到目前为止,我们只使用固定大小的输入序列(全部正好两个步长)。 如果输入序列具有可变长度(例如,像句子)呢? 在这种情况下,你应该在调用dynamic_rnn()
(或static_rnn()
)函数时设置sequence_length
参数;它必须是一维张量,表示每个实例的输入序列的长度。 例如:
n_steps = 2
n_inputs = 3
n_neurons = 5
reset_graph()
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
seq_length = tf.placeholder(tf.int32, [None])
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32,
sequence_length=seq_length)
例如,假设第二个输入序列只包含一个输入而不是两个输入。 为了适应输入张量X
,必须填充零向量(因为输入张量的第二维是最长序列的大小,即 2)
X_batch = np.array([
# step 0 step 1
[[0, 1, 2], [9, 8, 7]], # instance 1
[[3, 4, 5], [0, 0, 0]], # instance 2 (padded with zero vectors)
[[6, 7, 8], [6, 5, 4]], # instance 3
[[9, 0, 1], [3, 2, 1]], # instance 4
])
seq_length_batch = np.array([2, 1, 2, 2])
当然,你现在需要为两个占位符X
和seq_length
提供值:
with tf.Session() as sess:
init.run()
outputs_val, states_val = sess.run(
[outputs, states], feed_dict={X: X_batch, seq_length: seq_length_batch})
现在,RNN 输出序列长度的每个时间步都会输出零向量(查看第二个时间步的第二个输出):
此外,状态张量包含每个单元的最终状态(不包括零向量):
处理变长输出序列
如果输出序列长度不一样呢? 如果事先知道每个序列的长度(例如,如果知道长度与输入序列的长度相同),那么可以按照上面所述设置sequence_length
参数。 不幸的是,通常这是不可能的:例如,翻译后的句子的长度通常与输入句子的长度不同。 在这种情况下,最常见的解决方案是定义一个称为序列结束标记(EOS 标记)的特殊输出。 任何在 EOS 后面的输出应该被忽略(我们将在本章稍后讨论)。
好,现在你知道如何建立一个 RNN 网络(或者更准确地说是一个随着时间的推移而展开的 RNN 网络)。 但是你怎么训练呢?
训练 RNN
为了训练一个 RNN,诀窍是在时间上展开(就像我们刚刚做的那样),然后简单地使用常规反向传播(见图 14-5)。 这个策略被称为时间上的反向传播(BPTT)。
训练序列分类器
我们训练一个 RNN 来分类 MNIST 图像。 卷积神经网络将更适合于图像分类(见第 13 章),但这是一个你已经熟悉的简单例子。 我们将把每个图像视为 28 行 28 像素的序列(因为每个MNIST图像是28×28
像素)。 我们将使用 150 个循环神经元的单元,再加上一个全连接层,其中包含连接到上一个时间步的输出的 10 个神经元(每个类一个),然后是一个 softmax 层(见图 14-6)。
建模阶段非常简单, 它和我们在第 10 章中建立的 MNIST 分类器几乎是一样的,只是展开的 RNN 替换了隐层。 注意,全连接层连接到状态张量,其仅包含 RNN 的最终状态(即,第 28 个输出)。 另请注意,y
是目标类的占位符。
n_steps = 28
n_inputs = 28
n_neurons = 150
n_outputs = 10
learning_rate = 0.001
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.int32, [None])
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
logits = tf.layers.dense(states, n_outputs)
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y,
logits=logits)
loss = tf.reduce_mean(xentropy)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
init = tf.global_variables_initializer()
现在让我们加载 MNIST 数据,并按照网络的预期方式将测试数据重塑为[batch_size, n_steps, n_inputs]
。 我们之后会关注训练数据的重塑。
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/tmp/data/")
X_test = mnist.test.images.reshape((-1, n_steps, n_inputs))
y_test = mnist.test.labels
现在我们准备训练 RNN 了。 执行阶段与第 10 章中 MNIST 分类器的执行阶段完全相同,不同之处在于我们在将每个训练的批量提供给网络之前要重新调整。
batch_size = 150
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
for iteration in range(mnist.train.num_examples // batch_size):
X_batch, y_batch = mnist.train.next_batch(batch_size)
X_batch = X_batch.reshape((-1, n_steps, n_inputs))
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
acc_train = accuracy.eval(feed_dict={X: X_batch, y: y_batch})
acc_test = accuracy.eval(feed_dict={X: X_test, y: y_test})
print(epoch, "Train accuracy:", acc_train, "Test accuracy:", acc_test)
输出应该是这样的:
我们获得了超过 98% 的准确性 - 不错! 另外,通过调整超参数,使用 He 初始化初始化 RNN 权重,更长时间训练或添加一些正则化(例如,droupout),你肯定会获得更好的结果。
你可以通过将其构造代码包装在一个变量作用域内(例如,使用variable_scope("rnn", initializer = variance_scaling_initializer())
来使用 He 初始化)来为 RNN 指定初始化器。
为预测时间序列而训练
现在让我们来看看如何处理时间序列,如股价,气温,脑电波模式等等。 在本节中,我们将训练一个 RNN 来预测生成的时间序列中的下一个值。 每个训练实例是从时间序列中随机选取的 20 个连续值的序列,目标序列与输入序列相同,除了向后移动一个时间步(参见图14-7)。
首先,我们来创建一个 RNN。 它将包含 100 个循环神经元,并且我们将在 20 个时间步骤上展开它,因为每个训练实例将是 20 个输入那么长。 每个输入将仅包含一个特征(在该时间的值)。 目标也是 20 个输入的序列,每个输入包含一个值。 代码与之前几乎相同:
一般来说,你将不只有一个输入功能。 例如,如果你试图预测股票价格,则你可能在每个时间步骤都会有许多其他输入功能,例如竞争股票的价格,分析师的评级或可能帮助系统进行预测的任何其他功能。
在每个时间步,我们现在有一个大小为 100 的输出向量。但是我们实际需要的是每个时间步的单个输出值。 最简单的解决方法是将单元包装在OutputProjectionWrapper
中。 单元包装器就像一个普通的单元,代理每个方法调用一个底层单元,但是它也增加了一些功能。Out putProjectionWrapper
在每个输出之上添加一个完全连接的线性神经元层(即没有任何激活函数)(但不影响单元状态)。 所有这些完全连接的层共享相同(可训练)的权重和偏差项。 结果 RNN 如图 14-8 所示。
包装单元是相当容易的。 让我们通过将BasicRNNCell
包装到OutputProjectionWrapper
中来调整前面的代码:
cell =tf.contrib.rnn.OutputProjectionWrapper(
tf.contrib.rnn.BasicRNNCell(num_units=n_neurons,activation=tf.nn.relu),
output_size=n_outputs)
到现在为止还挺好。 现在我们需要定义损失函数。 我们将使用均方误差(MSE),就像我们在之前的回归任务中所做的那样。 接下来,我们将像往常一样创建一个 Adam 优化器,训练操作和变量初始化操作:
生成 RNN
到现在为止,我们已经训练了一个能够预测未来时刻样本值的模型,正如前文所述,可以用模型来生成新的序列。
为模型提供 长度为n_steps
的种子序列, 比如全零序列,然后通过模型预测下一时刻的值;把该预测值添加到种子序列的末尾,用最后面 长度为n_steps
的序列做为新的种子序列,做下一次预测,以此类推生成预测序列。
如图 14-11 所示,这个过程产生的序列会跟原始时间序列相似。
如果你试图把约翰·列侬的唱片塞给一个 RNN 模型,看它能不能生成下一张《想象》专辑。
注
约翰·列侬 有一张专辑《Imagine》(1971),这里取其双关的意思
也许你需要一个更强大的 RNN 网络,它有更多的神经元,层数也更多。下面来探究一下深度 RNN。
深度 RNN
一个朴素的想法就是把一层层神经元堆叠起来,正如图 14-12 所示的那样,它呈现了一种深度 RNN。
为了用 TensorFlow 实现深度 RNN,可先创建一些神经单元,然后堆叠进MultiRNNCell
。
以下代码中创建了 3 个相同的神经单元(当然也可以用不同类别的、包含不同不同数量神经元的单元)
n_neurons = 100
n_layers = 3
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
multi_layer_cell = tf.contrib.rnn.MultiRNNCell([basic_cell] * n_layers)
outputs, states = tf.nn.dynamic_rnn(multi_layer_cell, X, dtype=tf.float32)
这些代码就完成了这部分堆叠工作。status
变量包含了每层的一个张量,这个张量就代表了该层神经单元的最终状态(维度为[batch_size, n_neurons]
)。
如果在创建MultiRNNCell
时设置了state_is_tuple=False
,那么status
变量就变成了单个张量,它包含了每一层的状态,其在列的方向上进行了聚合,维度为[batch_size, n_layers*n_neurons]
。
注意在 TensorFlow 版本 0.11.0 之前,status
是单个张量是默认设置。
Python爱好者社区历史文章大合集:
Python爱好者社区历史文章列表(每周append更新一次)
关注后在公众号内回复“课程”即可获取:
小编的Python入门免费视频课程!!!
【最新免费微课】小编的Python快速上手matplotlib可视化库!!!
崔老师爬虫实战案例免费学习视频。
陈老师数据分析报告制作免费学习视频。
玩转大数据分析!Spark2.X+Python 精华实战课程免费学习视频。