提交 9f5592b1 编写于 作者: W wizardforcel

ch14 pic

上级 f37e4f78
......@@ -10,19 +10,19 @@
到目前为止,我们主要关注的是前馈神经网络,其中激活仅从输入层到输出层的一个方向流动(附录 E 中的几个网络除外)。 循环神经网络看起来非常像一个前馈神经网络,除了它也有连接指向后方。 让我们看一下最简单的 RNN,它由一个神经元接收输入,产生一个输出,并将输出发送回自己,如图 14-1(左)所示。 在每个时间步`t`(也称为一个帧),这个循环神经元接收输入 ![x^{(t)}](../images/tex-df9ed87e836e463cd086106035aef441.gif) 以及它自己的前一时间步长 ![y^{(t-1)}](../images/tex-9d51d9238bb54c1c9448b87d61b7503e.gif) 的输出。 我们可以用时间轴来表示这个微小的网络,如图 14-1(右)所示。 这被称为随着时间的推移展开网络。
![](https://img-blog.csdn.net/20171115211435614?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/14-1.png)
你可以轻松创建一个循环神经元层。 在每个时间步t,每个神经元都接收输入向量 ![x^{(t)}](../images/tex-df9ed87e836e463cd086106035aef441.gif) 和前一个时间步 ![y^{(t-1)}](../images/tex-9d51d9238bb54c1c9448b87d61b7503e.gif) 的输出向量,如图 14-2 所示。 请注意,输入和输出都是向量(当只有一个神经元时,输出是一个标量)。
![](https://img-blog.csdn.net/20171115212115098?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/14-2.png)
每个循环神经元有两组权重:一组用于输入 ![x^{(t)}](../images/tex-df9ed87e836e463cd086106035aef441.gif),另一组用于前一时间步长 ![y^{(t-1)}](../images/tex-9d51d9238bb54c1c9448b87d61b7503e.gif) 的输出。 我们称这些权重向量为 ![w_x](../images/tex-4a5d1969fe1ef947082a32d547c247d7.gif) 和 ![w_y](../images/tex-e939f0323d8d49a8482b30a5284c8374.gif)。如公式 14-1 所示(`b`是偏差项,`φ(·)`是激活函数,例如 ReLU),可以计算单个循环神经元的输出。
![](https://img-blog.csdn.net/20171115212708396?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](img/e-14-1.png)
就像前馈神经网络一样,我们可以使用上一个公式的向量化形式,对整个小批量计算整个层的输出(见公式 14-2)。
![](https://img-blog.csdn.net/20171115213134195?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](img/e-14-2.png)
* ![Y^{(t)}](../images/tex-626343cb96bdf9ee8429b7d5d8f4607a.gif) 是 ![m \times n_{neurons}](../images/tex-5034ed18fa4c81ee7bc176f0a4cbab80.gif) 矩阵,包含在最小批次中每个实例在时间步`t`处的层输出(`m`是小批次中的实例数,![n_{neurons}](../images/tex-e57673e94f4eeddd53be04f2167db8d2.gif) 是神经元数)。
* ![X^{(t)}](../images/tex-56662b2579440b8c9f02e9c09b8b021d.gif) 是 ![m \times n_{inputs}](../images/tex-3550fe5e2cae185a9af7235e7f7fb05d.gif) 矩阵,包含所有实例的输入的 (![n_{inputs}](../images/tex-53e90522ca559971bb2d9d6009b82a44.gif) 是输入特征的数量)。
......@@ -39,11 +39,11 @@
一般情况下,时间步`t`处的单元状态,记为 ![h^{(t)}](../images/tex-7ddb4d2d45df22e2e98e6cc504f84787.gif)`h`代表“隐藏”),是该时间步的某些输入和前一时间步的状态的函数:![h^{(t)} = f(h^{(t-1)}, x^{(t)})](../images/tex-07be34d7c17f39052675948cb5b75838.gif)。 其在时间步`t`处的输出,表示为 ![y^{(t)}](../images/tex-cc685401bcf131ce4e9f980be319daac.gif),也和前一状态和当前输入的函数有关。 在我们已经讨论过的基本单元的情况下,输出等于单元状态,但是在更复杂的单元中并不总是如此,如图 14-3 所示。
![](https://img-blog.csdn.net/20171121165432399?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/14-3.png)
## 输入和输出序列
![](https://img-blog.csdn.net/20171121165842411?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/14-4.png)
RNN 可以同时进行一系列输入并产生一系列输出(见图 14-4,左上角的网络)。 例如,这种类型的网络对于预测时间序列(如股票价格)非常有用:你在过去的`N`天内给出价格,并且它必须输出向未来一天移动的价格(即从`N - 1`天前到明天)。
......@@ -175,7 +175,7 @@ with tf.Session() as sess:
我们得到所有实例,所有时间步长和所有神经元的单一`outputs_val`张量:
![](https://img-blog.csdn.net/20171122172049232?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/o-14-1.png)
但是,这种方法仍然会建立一个每个时间步包含一个单元的图。 如果有 50 个时间步,这个图看起来会非常难看。 这有点像写一个程序而没有使用循环(例如,`Y0 = f(0,X0)``Y1 = f(Y0,X1)``Y2 = f(Y1,X2)`;...;`Y50 = f(Y49,X50)`)。 如果使用大图,在反向传播期间(特别是在 GPU 内存有限的情况下),你甚至可能会发生内存不足(OOM)错误,因为它必须在正向传递期间存储所有张量值,以便可以使用它们在反向传播期间计算梯度。
......@@ -265,11 +265,11 @@ with tf.Session() as sess:
现在,RNN 输出序列长度的每个时间步都会输出零向量(查看第二个时间步的第二个输出):
![](https://img-blog.csdn.net/20171122182439986?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/o-14-2.png)
此外,状态张量包含每个单元的最终状态(不包括零向量):
![](https://img-blog.csdn.net/20171205095036952?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/o-14-3.png)
## 处理变长输出序列
......@@ -281,15 +281,15 @@ with tf.Session() as sess:
为了训练一个 RNN,诀窍是在时间上展开(就像我们刚刚做的那样),然后简单地使用常规反向传播(见图 14-5)。 这个策略被称为时间上的反向传播(BPTT)。
![](https://img-blog.csdn.net/20171205101227725?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/14-5.png)
就像在正常的反向传播中一样,展开的网络(用虚线箭头表示)有第一个正向传递。然后使用损失函数评估输出序列 ![](https://img-blog.csdn.net/20171205101635352?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)(其中 ![t_{min}](../images/tex-e703e1ba18d3838a4834b8529afbddff.gif) 和 ![t_{max}](../images/tex-afdc190074a7e451cec25ee2fde23fa2.gif) 是第一个和最后一个输出时间步长,不计算忽略的输出),并且该损失函数的梯度通过展开的网络向后传播(实线箭头);最后使用在 BPTT 期间计算的梯度来更新模型参数。 请注意,梯度在损失函数所使用的所有输出中反向流动,而不仅仅通过最终输出(例如,在图 14-5 中,损失函数使用网络的最后三个输出 ![Y^{(2)}](../images/tex-a0b673959034f5d7721eda22ec8f8a59.gif),![Y^{(3)}](../images/tex-a9fd2d0f4eafa552b514c6a68092a08c.gif) 和 ![Y^{(4)}](../images/tex-966fcc27b1359fe6160829661f0657cc.gif),所以梯度流经这三个输出,但不通过 ![Y^{(0)}](../images/tex-347b979a3eaaf423a7c376ba475f1313.gif) 和 ![Y^{(1)}](../images/tex-44e3acde9f1b11682289b6069b6f2a1f.gif))。 而且,由于在每个时间步骤使用相同的参数`W``b`,所以反向传播将做正确的事情并且总结所有时间步骤。
就像在正常的反向传播中一样,展开的网络(用虚线箭头表示)有第一个正向传递。然后使用损失函数评估输出序列 ![](img/o-14-4.png)(其中 ![t_{min}](../images/tex-e703e1ba18d3838a4834b8529afbddff.gif) 和 ![t_{max}](../images/tex-afdc190074a7e451cec25ee2fde23fa2.gif) 是第一个和最后一个输出时间步长,不计算忽略的输出),并且该损失函数的梯度通过展开的网络向后传播(实线箭头);最后使用在 BPTT 期间计算的梯度来更新模型参数。 请注意,梯度在损失函数所使用的所有输出中反向流动,而不仅仅通过最终输出(例如,在图 14-5 中,损失函数使用网络的最后三个输出 ![Y^{(2)}](../images/tex-a0b673959034f5d7721eda22ec8f8a59.gif),![Y^{(3)}](../images/tex-a9fd2d0f4eafa552b514c6a68092a08c.gif) 和 ![Y^{(4)}](../images/tex-966fcc27b1359fe6160829661f0657cc.gif),所以梯度流经这三个输出,但不通过 ![Y^{(0)}](../images/tex-347b979a3eaaf423a7c376ba475f1313.gif) 和 ![Y^{(1)}](../images/tex-44e3acde9f1b11682289b6069b6f2a1f.gif))。 而且,由于在每个时间步骤使用相同的参数`W``b`,所以反向传播将做正确的事情并且总结所有时间步骤。
## 训练序列分类器
我们训练一个 RNN 来分类 MNIST 图像。 卷积神经网络将更适合于图像分类(见第 13 章),但这是一个你已经熟悉的简单例子。 我们将把每个图像视为 28 行 28 像素的序列(因为每个MNIST图像是`28×28`像素)。 我们将使用 150 个循环神经元的单元,再加上一个全连接层,其中包含连接到上一个时间步的输出的 10 个神经元(每个类一个),然后是一个 softmax 层(见图 14-6)。
![](https://img-blog.csdn.net/20171205102304032?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/14-6.png)
建模阶段非常简单, 它和我们在第 10 章中建立的 MNIST 分类器几乎是一样的,只是展开的 RNN 替换了隐层。 注意,全连接层连接到状态张量,其仅包含 RNN 的最终状态(即,第 28 个输出)。 另请注意,`y`是目标类的占位符。
......@@ -347,7 +347,7 @@ with tf.Session() as sess:
输出应该是这样的:
![](https://img-blog.csdn.net/20171205110116957?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/o-14-5.png)
我们获得了超过 98% 的准确性 - 不错! 另外,通过调整超参数,使用 He 初始化初始化 RNN 权重,更长时间训练或添加一些正则化(例如,droupout),你肯定会获得更好的结果。
......@@ -357,17 +357,17 @@ with tf.Session() as sess:
现在让我们来看看如何处理时间序列,如股价,气温,脑电波模式等等。 在本节中,我们将训练一个 RNN 来预测生成的时间序列中的下一个值。 每个训练实例是从时间序列中随机选取的 20 个连续值的序列,目标序列与输入序列相同,除了向后移动一个时间步(参见图14-7)。
![](https://img-blog.csdn.net/20171219120815106?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/14-7.png)
首先,我们来创建一个 RNN。 它将包含 100 个循环神经元,并且我们将在 20 个时间步骤上展开它,因为每个训练实例将是 20 个输入那么长。 每个输入将仅包含一个特征(在该时间的值)。 目标也是 20 个输入的序列,每个输入包含一个值。 代码与之前几乎相同:
![](https://img-blog.csdn.net/20171219121147273?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/o-14-6.png)
一般来说,你将不只有一个输入功能。 例如,如果你试图预测股票价格,则你可能在每个时间步骤都会有许多其他输入功能,例如竞争股票的价格,分析师的评级或可能帮助系统进行预测的任何其他功能。
在每个时间步,我们现在有一个大小为 100 的输出向量。但是我们实际需要的是每个时间步的单个输出值。 最简单的解决方法是将单元包装在`OutputProjectionWrapper`中。 单元包装器就像一个普通的单元,代理每个方法调用一个底层单元,但是它也增加了一些功能。`Out putProjectionWrapper`在每个输出之上添加一个完全连接的线性神经元层(即没有任何激活函数)(但不影响单元状态)。 所有这些完全连接的层共享相同(可训练)的权重和偏差项。 结果 RNN 如图 14-8 所示。
![](https://img-blog.csdn.net/20171219121927939?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWtvbl93YW5nX2hrYnU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](img/14-8.png)
包装单元是相当容易的。 让我们通过将`BasicRNNCell`包装到`OutputProjectionWrapper`中来调整前面的代码:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册