Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenDocCN
hands-on-ml-zh
提交
0c2bcdda
H
hands-on-ml-zh
项目概览
OpenDocCN
/
hands-on-ml-zh
通知
13
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
H
hands-on-ml-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
0c2bcdda
编写于
6月 20, 2018
作者:
W
wizardforcel
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
ch14 pic
上级
9f5592b1
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
16 addition
and
16 deletion
+16
-16
docs/14.循环神经网络.md
docs/14.循环神经网络.md
+16
-16
未找到文件。
docs/14.循环神经网络.md
浏览文件 @
0c2bcdda
...
...
@@ -10,19 +10,19 @@
到目前为止,我们主要关注的是前馈神经网络,其中激活仅从输入层到输出层的一个方向流动(附录 E 中的几个网络除外)。 循环神经网络看起来非常像一个前馈神经网络,除了它也有连接指向后方。 让我们看一下最简单的 RNN,它由一个神经元接收输入,产生一个输出,并将输出发送回自己,如图 14-1(左)所示。 在每个时间步
`t`
(也称为一个帧),这个循环神经元接收输入 !
[
x^{(t)}
](
../images/tex-df9ed87e836e463cd086106035aef441.gif
)
以及它自己的前一时间步长 !
[
y^{(t-1)}
](
../images/tex-9d51d9238bb54c1c9448b87d61b7503e.gif
)
的输出。 我们可以用时间轴来表示这个微小的网络,如图 14-1(右)所示。 这被称为随着时间的推移展开网络。
![](
img
/14-1.png
)
![](
../images/chapter_14
/14-1.png
)
你可以轻松创建一个循环神经元层。 在每个时间步t,每个神经元都接收输入向量 !
[
x^{(t)}
](
../images/tex-df9ed87e836e463cd086106035aef441.gif
)
和前一个时间步 !
[
y^{(t-1)}
](
../images/tex-9d51d9238bb54c1c9448b87d61b7503e.gif
)
的输出向量,如图 14-2 所示。 请注意,输入和输出都是向量(当只有一个神经元时,输出是一个标量)。
![](
img
/14-2.png
)
![](
../images/chapter_14
/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),可以计算单个循环神经元的输出。
![](
img
/e-14-1.png
)
![](
../images/chapter_14
/e-14-1.png
)
就像前馈神经网络一样,我们可以使用上一个公式的向量化形式,对整个小批量计算整个层的输出(见公式 14-2)。
![](
img
/e-14-2.png
)
![](
../images/chapter_14
/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 所示。
![](
img
/14-3.png
)
![](
../images/chapter_14
/14-3.png
)
## 输入和输出序列
![](
img
/14-4.png
)
![](
../images/chapter_14
/14-4.png
)
RNN 可以同时进行一系列输入并产生一系列输出(见图 14-4,左上角的网络)。 例如,这种类型的网络对于预测时间序列(如股票价格)非常有用:你在过去的
`N`
天内给出价格,并且它必须输出向未来一天移动的价格(即从
`N - 1`
天前到明天)。
...
...
@@ -175,7 +175,7 @@ with tf.Session() as sess:
我们得到所有实例,所有时间步长和所有神经元的单一
`outputs_val`
张量:
![](
img
/o-14-1.png
)
![](
../images/chapter_14
/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 输出序列长度的每个时间步都会输出零向量(查看第二个时间步的第二个输出):
![](
img
/o-14-2.png
)
![](
../images/chapter_14
/o-14-2.png
)
此外,状态张量包含每个单元的最终状态(不包括零向量):
![](
img
/o-14-3.png
)
![](
../images/chapter_14
/o-14-3.png
)
## 处理变长输出序列
...
...
@@ -281,15 +281,15 @@ with tf.Session() as sess:
为了训练一个 RNN,诀窍是在时间上展开(就像我们刚刚做的那样),然后简单地使用常规反向传播(见图 14-5)。 这个策略被称为时间上的反向传播(BPTT)。
![](
img
/14-5.png
)
![](
../images/chapter_14
/14-5.png
)
就像在正常的反向传播中一样,展开的网络(用虚线箭头表示)有第一个正向传递。然后使用损失函数评估输出序列 !
[](
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`
,所以反向传播将做正确的事情并且总结所有时间步骤。
就像在正常的反向传播中一样,展开的网络(用虚线箭头表示)有第一个正向传递。然后使用损失函数评估输出序列 !
[](
../images/chapter_14
/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)。
![](
img
/14-6.png
)
![](
../images/chapter_14
/14-6.png
)
建模阶段非常简单, 它和我们在第 10 章中建立的 MNIST 分类器几乎是一样的,只是展开的 RNN 替换了隐层。 注意,全连接层连接到状态张量,其仅包含 RNN 的最终状态(即,第 28 个输出)。 另请注意,
`y`
是目标类的占位符。
...
...
@@ -347,7 +347,7 @@ with tf.Session() as sess:
输出应该是这样的:
![](
img
/o-14-5.png
)
![](
../images/chapter_14
/o-14-5.png
)
我们获得了超过 98% 的准确性 - 不错! 另外,通过调整超参数,使用 He 初始化初始化 RNN 权重,更长时间训练或添加一些正则化(例如,droupout),你肯定会获得更好的结果。
...
...
@@ -357,17 +357,17 @@ with tf.Session() as sess:
现在让我们来看看如何处理时间序列,如股价,气温,脑电波模式等等。 在本节中,我们将训练一个 RNN 来预测生成的时间序列中的下一个值。 每个训练实例是从时间序列中随机选取的 20 个连续值的序列,目标序列与输入序列相同,除了向后移动一个时间步(参见图14-7)。
![](
img
/14-7.png
)
![](
../images/chapter_14
/14-7.png
)
首先,我们来创建一个 RNN。 它将包含 100 个循环神经元,并且我们将在 20 个时间步骤上展开它,因为每个训练实例将是 20 个输入那么长。 每个输入将仅包含一个特征(在该时间的值)。 目标也是 20 个输入的序列,每个输入包含一个值。 代码与之前几乎相同:
![](
img
/o-14-6.png
)
![](
../images/chapter_14
/o-14-6.png
)
一般来说,你将不只有一个输入功能。 例如,如果你试图预测股票价格,则你可能在每个时间步骤都会有许多其他输入功能,例如竞争股票的价格,分析师的评级或可能帮助系统进行预测的任何其他功能。
在每个时间步,我们现在有一个大小为 100 的输出向量。但是我们实际需要的是每个时间步的单个输出值。 最简单的解决方法是将单元包装在
`OutputProjectionWrapper`
中。 单元包装器就像一个普通的单元,代理每个方法调用一个底层单元,但是它也增加了一些功能。
`Out putProjectionWrapper`
在每个输出之上添加一个完全连接的线性神经元层(即没有任何激活函数)(但不影响单元状态)。 所有这些完全连接的层共享相同(可训练)的权重和偏差项。 结果 RNN 如图 14-8 所示。
![](
img
/14-8.png
)
![](
../images/chapter_14
/14-8.png
)
包装单元是相当容易的。 让我们通过将
`BasicRNNCell`
包装到
`OutputProjectionWrapper`
中来调整前面的代码:
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录