此分类用于记录吴恩达深度学习课程的学习笔记。
课程相关信息链接如下:
- 原课程视频链接:[双语字幕]吴恩达深度学习deeplearning.ai
- github课程资料,含课件与笔记:吴恩达深度学习教学资料
- 课程配套练习(中英)与答案:吴恩达深度学习课后习题与答案
本篇为第五课的第一周内容,1.9的内容以及一些相关基础的补充。
本周为第五课的第一周内容,与 CV 相对应的,这一课所有内容的中心只有一个:自然语言处理(Natural Language Processing,NLP) 。
应用在深度学习里,它是专门用来进行文本与序列信息建模 的模型和技术,本质上是在全连接网络与统计语言模型基础上的一次"结构化特化",也是人工智能中最贴近人类思维表达方式 的重要研究方向之一。
这一整节课同样涉及大量需要反复消化的内容,横跨机器学习、概率统计、线性代数以及语言学直觉。
语言不像图像那样"直观可见",更多是抽象符号与上下文关系的组合,因此理解门槛反而更高 。
因此,我同样会尽量补足必要的背景知识,尽可能用比喻和实例降低理解难度。
本篇的内容关于门控循环单元 GRU,它通过改变 RNN 的隐藏层,来缓解 RNN 的梯度消失问题。
1. 什么是 GRU(Gated Recurrent Unit)?
在前面的内容里中我们已经提到过:RNN 的核心机制,是把历史信息压缩进一个不断传递的隐藏状态中 。
结果就是我们已经分析过的结论:信息一旦隔得太远,就会在反复的线性变换和非线性映射中被不断稀释 。
在反向传播时就体现为梯度越来越小,最终几乎传不回去,导致"开头被遗忘",这就是 RNN 的长距离依赖问题 。
显然,这并不是改善训练技巧可以解决的问题,而是 RNN 本身的结构造成的限制,自然而然地就引出了 NLP 领域的新问题 :如何缓解 RNN 的长距离依赖问题? 即如何让模型"记得更久一些"?
于是,在 2014 年发表的一篇论文: On the Properties of Neural Machine Translation: Encoder--Decoder Approaches 中,提出了一种带有门控机制的循环单元结构 ,即后来被称为 GRU(Gated Recurrent Unit) 的模型。
该工作首次在 Encoder--Decoder 框架下系统性地引入 "更新门"和"重置门" ,通过对隐藏状态的信息流进行选择性控制,从结构层面缓解了传统 RNN 在长序列建模中面临的困难。
同年的另一篇论文:Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling 对其实际效果进行了系统评估。实验结果证明了其优越的性能,这也使其迅速成为实际 NLP 系统中广泛采用的基础循环单元之一。
出现了很多没见过的名词,别担心,下面我们就来详细展开。
1.1 GRU 的解题思路
首先,面对"如何让模型记得更久一些"的问题,GRU给出的答案是:
既然记不住全部,那我能不能只记重要的?
这是什么意思?回忆一下 普通 RNN 是如何进行记忆的:
\[新输入来了\rightarrow旧状态所有信息参与计算\rightarrow得到一个带有新输入信息的新隐藏状态 \]
我们已经很熟悉这个过程了:

但是,实际上,在真实语言中,我们并不是这样处理信息的。
举个例子:
"我昨天去北京出差,
在路上遇到了一个多年未见的朋友,
他现在在......"
当你看到"他"的时候:
- 你不需要记住"北京","昨天"。
- 但你必须记住"朋友"。
- 换句话说,当前时刻的理解,并不依赖于全部历史内容,而只依赖于与当前判断相关的信息。
再概括来讲,人脑在处理序列信息时,隐含着一个非常重要的能力:选择性记忆 。
而 GRU 的设计,正是将这种"取舍能力",通过门控结构的形式,显式地引入到 RNN 的隐藏层更新过程中 。
简单展开一下,GRU 并没有改变 RNN 的整体框架:
- 仍然是时间步 \(t = 1, 2, \dots\)
- 仍然有输入 \(x^{<t>}\)
- 仍然有隐藏状态 \(a^{<t>}\)
它真正改变的,只有一件事:在"旧状态 → 新状态"的过程中,加了两道可学习的"门"。 就是我们下面要说的 "更新门"和"重置门" 。

1.2 GRU 的更新门(Update Gate)
第一道门,叫 更新门(Update Gate) 。
它负责回答的问题是:
"当前这一步,我要保留多少过去的记忆?"
也就是说:
- 更新门开得越大 → 旧状态保留得越多
- 更新门关得越紧 → 当前输入对隐藏状态的影响越大
总结来说,更新门衡量的是:"这条历史信息,值不值得继续往后传?"
这一设计直接解决了 RNN 的一个问题:普通 RNN 每一步都会用新输入强行覆盖旧隐藏状态,
长期信息容易被逐步稀释,而更新门让模型可以选择性保留重要历史信息。
我们回到之前的例子:
"我昨天去北京出差,
在路上遇到了一个多年未见的朋友,
他现在在......"
在生成"他"对应的隐藏状态时:
- 更新门会让模型保留"朋友"这个关键信息,因为它对理解代词至关重要
- 同时,更新门会削弱"昨天""北京"等不相关信息的影响
于是,通过更新门,模型可以自动判断哪些历史值得继续传递,哪些可以淡化。

其实,这种思路和我们之前在优化算法部分介绍的 指数加权平均 很相似:旧状态像过去的平均值,更新门控制"平均时的新权重",让重要信息得到保留。
1.3 GRU 的重置门(Reset Gate)
第二道门,叫 重置门(Reset Gate) 。
它关心的不是"保留多少旧信息",而是:
"在生成新信息时,我要参考多少过去的历史?"
可以把它想象成 选择记忆开关:
- 当历史信息相关 → 重置门值接近 1 → 历史参与计算
- 当历史信息不相关 → 重置门值接近 0 → 模型"暂时忘掉过去"
这个设计是为了应对语言序列中的一类常见情况:句子突然换话题、上下文跳转很大等前面的内容对当前判断几乎没帮助的情况。
还是这个例子:
"我昨天去北京出差,
在路上遇到了一个多年未见的朋友,
他现在在......"
当生成"他"对应的新隐藏状态时:
- 重置门会让模型只关注"朋友"这个历史信息
- 忽略"昨天"、"北京"等与当前代词关系不大的信息

只看概念和例子, 你可能会觉得:更新门和重置门好像是一个作用啊?
实际上,这绝非冗余设计,我们展开看看:
1.4 更新门和重置门
实际上,更新门关注的是 "旧状态在下一步隐藏状态中保留多少" ,它是一种全局记忆控制 ,关注的是"以后 "。
而重置门关注的是 "在生成当前新隐藏状态时,历史信息是否被参考" ,它是一种局部选择性 ,关注的是"现在"。
| 门 | 功能 | 控制对象 | 比喻 |
|---|---|---|---|
| 更新门 | 记忆持续 | 上一隐藏状态整体传递 | 决定哪些历史要带到下一步,就像水龙头调节水流量 |
| 重置门 | 记忆使用 | 当前候选隐藏状态计算 | 决定本步是否参考历史,就像选择性翻阅笔记 |
最终,更新门 负责"信息能延续多远" ,重置门 负责"信息在当前是否参与生成"。
两道门结合,使 GRU 能够在长序列中保留重要历史信息,同时在必要时灵活忘掉无关历史,实现"更好、更有用的记忆"。
现在,了解了 GRU 的基本原理后,我们来看看如何实现 GRU 吧。
2. 如何实现 GRU ?
这里需要提前说明一点,如果你看了原视频,会发现吴恩达老师并没有过多提及重置门,而是用一个新概念:"记忆细胞" 来进行讲解。实际上,记忆细胞是之后的 LSTM 中的概念,因为 GRU 可以看作是 LSTM 的简化形式,因此同样可以使用,但个人感觉不正式引入的前提下并不好理解,最终还是选择使用 GRU 本身的概念来进行这部分内容,在下一篇 LSTM 的介绍中再正式引入记忆细胞。
在理解了 GRU 的核心思想和两道门的作用之后,我们可以进一步探讨如何在实际模型中实现 GRU 。实现上,其实并不复杂,本质上仍是对普通 RNN 的隐藏状态更新增加了门控计算。下面我们按逻辑逐步展开。
2.1 计算重置门
在 GRU 的实现逻辑中,对于当前步新隐藏状态的计算,第一步是计算重置门 。
重置门 \(r^{\langle t \rangle}\) 决定了历史隐藏状态 \(a^{\langle t-1 \rangle}\) 在生成当前候选 隐藏状态 \(\tilde{a}^{\langle t \rangle}\) 时被参考的比例。
来具体演示一下这个过程:

计算公式为:
\[r^{\langle t \rangle} = sigmoid\big( W_{xr} x^{\langle t \rangle} + W_{ar} a^{\langle t-1 \rangle} + b_r \big) \]
你可以比较明显的发现,实际上重置门就是一个使用 \(sigmoid\) 激活函数的全连接层,最终的激活值,也就是重置门值会被 \(sigmoid\) 压缩到 \(0\sim1\) 之间。用来表示一种权重:
- 当 \(r^{\langle t \rangle} \approx 1\) 时,第 \(i\) 个隐藏单元会充分参考历史信息。
- 当 \(r^{\langle t \rangle} \approx 0\) 时,第 \(i\) 个隐藏单元会 "忘掉"历史信息 ,只依赖当前输入生成候选隐藏状态。
什么叫候选隐藏状态?这就是我们的下一步内容。
2.2 计算候选隐藏状态
在计算完重置门 \(r^{\langle t \rangle}\) 后,下一步是生成 候选隐藏状态 \(\tilde{a}^{\langle t \rangle}\),它表示当前步的新信息 ,结合了当前输入 \(x^{\langle t \rangle}\) 和经过重置门处理后的历史隐藏状态 \(a^{\langle t-1 \rangle}\)。
同样,我们在上一步的基础上进行演示:

摆出公式如下:
\[\tilde{a}^{\langle t \rangle} = \tanh\Big( W_{xa} x^{\langle t \rangle} + W_{aa} ( r^{\langle t \rangle} \odot a^{\langle t-1 \rangle} ) + b_a \Big) \]
其中\(r^{\langle t \rangle} \odot a^{\langle t-1 \rangle}\)是指逐元素乘 ,这是将重置门作用于历史隐藏状态。
你会发现,这时我们得到的候选隐藏状态 \(\tilde{a}^{\langle t \rangle}\) 就是是当前步的"新记忆" ,它既包含当前输入信息 \(x^{\langle t \rangle}\),也包含经过选择性过滤的历史信息 \(r^{\langle t \rangle} \odot a^{\langle t-1 \rangle}\)。
到这里,你可能觉得已经实现一些优化了,但 GRU 告诉你:这还不够好。
2.3 计算更新门
在生成候选隐藏状态 \(\tilde{a}^{\langle t \rangle}\) 后,下一步是计算 更新门 \(z^{\langle t \rangle}\)。
而更新门的作用是:决定历史隐藏状态 \(a^{\langle t-1 \rangle}\) 与候选隐藏状态 \(\tilde{a}^{\langle t \rangle}\) 在最终隐藏状态 \(a^{\langle t \rangle}\) 中的融合比例 。
我们继续进行演示:

看完演示,我想一个问题已经呼之欲出了:更新门和重置门看起来就是把一个东西算了两遍啊?这么做的意义是什么?我直接把重置门值拿来用不行吗?
这就涉及到二者在模型中所承担的语义作用:
- 更新门用来记录全局 中的重要记忆,它直接影响最终输出,同样梯度也直接影响它。
- 重置门用来保存局部 记忆,确保使用信息符合当前步需求,它通过候选状态间接 影响最终输出,因此梯度对它的作用就没有那么"长期"。

因此:二者不能参数共享,而是一定要有独立的参数,以此通过反向传播实现二者的语义作用。
同时要说明的是,我们这里演示的是逻辑上的计算顺序,你会发现二者的计算并不存在先后限制,因此在实际应用中可以同步计算二者。
公式表示为:
\[z^{\langle t \rangle} = sigmoid\big( W_{xz} x^{\langle t \rangle} + W_{az} a^{\langle t-1 \rangle} + b_z \big) \]
其中,\(sigmoid\) 函数将输出压缩到 \([0,1]\),表示融合比例:
- 当 \(z^{\langle t \rangle}_i \approx 1\) 时,第 \(i\) 个隐藏单元会更多保留历史信息。
- 当 \(z^{\langle t \rangle}_i \approx 0\) 时,第 \(i\) 个隐藏单元会更多采纳候选隐藏状态 \(\tilde{a}^{\langle t \rangle}\) 的新信息。
最后,我们将用更新门将历史隐藏状态与候选隐藏状态融合 ,得到最终隐藏状态 \(a^{\langle t \rangle}\)。
2.4 计算最终隐藏状态
在前面的步骤中,我们已经得到了三样关键量:
- 上一时刻的隐藏状态 \(a^{\langle t-1 \rangle}\)(历史记忆)
- 当前步生成的候选隐藏状态 \(\tilde{a}^{\langle t \rangle}\)(新记忆)
- 控制二者融合比例的更新门 \(z^{\langle t \rangle}\)
现在,GRU 要做的最后一件事,就是将"历史记忆"和"新记忆"按更新门的指示进行融合 ,从而得到当前时刻的最终隐藏状态 \(a^{\langle t \rangle}\),就像这样:

最终的计算公式为:
\[a^{\langle t \rangle} = z^{\langle t \rangle} \odot a^{\langle t-1 \rangle} + (1 - z^{\langle t \rangle}) \odot \tilde{a}^{\langle t \rangle} \]
同样其中 \(\odot\) 表示逐元素乘 。
这个公式本身就非常直观,它表达的正是更新门的语义含义:
- \(z^{\langle t \rangle}\) 决定了历史信息保留的比例
- \(1 - z^{\langle t \rangle}\) 决定了新信息写入的比例
也就是说:
- 当 \(z^{\langle t \rangle}_i \approx 1\) 时, 第 \(i\) 个隐藏单元几乎直接复制上一时刻的状态,信息得以长期延续。
- 当 \(z^{\langle t \rangle}_i \approx 0\) 时, 第 \(i\) 个隐藏单元主要采用当前步生成的新信息。
2.5 小结
至此,一个 GRU 单元在时间步 \(t\) 的完整计算流程就结束了,完整顺序是这样的:
- 计算重置门 \(r^{\langle t \rangle}\) → 用于调节历史隐藏状态在生成候选隐藏状态 \(\tilde{a}^{\langle t \rangle}\) 时的参考程度:
\[ r^{\langle t \rangle} = sigmoid(W_{xr} x^{\langle t \rangle} + W_{ar} a^{\langle t-1 \rangle} + b_r) \]
- 计算候选隐藏状态 \(\tilde{a}^{\langle t \rangle}\) → 使用当前输入 \(x^{\langle t \rangle}\) 和重置门调节后的历史信息 :
\[ \tilde{a}^{\langle t \rangle} = \tanh(W_{x\tilde{a}} x^{\langle t \rangle} + W_{a\tilde{a}} (r^{\langle t \rangle} \odot a^{\langle t-1 \rangle}) + b_{\tilde{a}}) \]
- 计算更新门 \(z^{\langle t \rangle}\) → 决定历史信息与新候选状态在最终隐藏状态中的融合比例:
\[ z^{\langle t \rangle} = sigmoid(W_{xz} x^{\langle t \rangle} + W_{az} a^{\langle t-1 \rangle} + b_z) \]
- 计算最终隐藏状态 \(a^{\langle t \rangle}\) → 使用更新门进行加权融合:
\[ a^{\langle t \rangle} = z^{\langle t \rangle} \odot a^{\langle t-1 \rangle} + (1 - z^{\langle t \rangle}) \odot \tilde{a}^{\langle t \rangle} \]
综合来看,GRU 缓解长距离依赖问题的方式可以概括为一句话:通过更新门建立"可复制的长期记忆通路",通过重置门实现"按需使用的局部历史选择",从结构上同时保护了信息和梯度的长期传递。
它并不是"记忆更强",而是更聪明地决定什么时候记、什么时候用、什么时候不动 。
而且,你会发现 RGU 中同样有残差的思想:
从结构视角看,GRU 的更新门在时间维度上实现了一种"可学习的残差连接":当更新门接近 1 时,隐藏状态几乎被原样复制,使信息与梯度能够跨越多个时间步稳定传播,这与 ResNet 通过恒等映射缓解深层网络退化问题在思想上是一致的。
3. 总结
| 概念 | 原理 | 比喻 |
|---|---|---|
| GRU 的核心思想 | 不再"全部记住",而是通过门控机制选择性地记忆与使用历史信息 | 记笔记时只标重点,而不是全文背诵 |
| 更新门(Update Gate) | 控制上一时刻隐藏状态在当前隐藏状态中保留的比例,直接决定最终输出 | 水龙头:决定旧记忆"流"到下一步的多少 |
| 重置门(Reset Gate) | 控制历史信息在生成当前候选隐藏状态时是否被参考 | 翻笔记:这一步要不要看以前的内容 |
| 候选隐藏状态 | 在重置门调节下,由当前输入与部分历史信息生成的新记忆 | 当前这一步新写下的草稿想法 |
| 最终隐藏状态 | 使用更新门在旧隐藏状态与候选隐藏状态之间进行加权融合 | 在"照抄旧稿"和"采用新稿"之间做权衡 |
| 更新门 vs 重置门 | 更新门决定信息能"走多远",重置门决定信息"现在用不用" | 一个管未来,一个管当下 |
| 门不共享参数的原因 | 二者承担不同语义角色,通过反向传播分别学习"长期保留"和"局部使用"的策略 | 长期计划和临时决策不能用同一套标准 |
| 梯度传播机制 | 更新门接近 1 时,隐藏状态近似复制,梯度可跨时间步稳定传播 | 给梯度修了一条高速公路 |
| GRU 与残差思想 | 在时间维度上引入"可学习的恒等映射",避免每一步都强制更新 | ResNet 的 skip connection,沿时间轴展开 |