动手学深度学习——门控循环单元(GRU)
1. 前言前面我们已经学习了RNNRNN 从零开始实现RNN 简洁实现到这里我们已经知道 RNN 的核心思想通过隐藏状态把过去的信息传到当前时刻。这让它能够处理序列数据比如文本时间序列音频序列但是基础 RNN 虽然经典却有一个非常著名的问题它很难有效处理长期依赖。也就是说如果当前时刻需要依赖很久以前的信息普通 RNN 往往学不好。这背后通常会伴随着梯度消失梯度爆炸远距离信息难以保留于是人们提出了一种更强的循环结构GRUGated Recurrent Unit门控循环单元GRU 的核心改进就在于让模型学会“该记什么、该忘什么、该更新多少”。这也是它名字里“门控”两个字的由来。2. 为什么基础 RNN 不够好先回顾一下基础 RNN 的隐藏状态更新H_t tanh(X_t W_xh H_{t-1} W_hh b_h)这意味着当前状态依赖当前输入也依赖上一时刻隐藏状态但这个“依赖多少”是统一混在一起更新的问题就在这里。2.1 它没有显式控制记忆保留基础 RNN 并不会明确告诉你哪些旧信息应该保留哪些旧信息应该丢掉当前输入应该写入多少新信息所有东西都被一次tanh混到一起了。2.2 长期依赖很难稳定传递当序列变长时很多有用信息会逐渐被冲淡最后模型很可能只记住最近的部分而忘掉更久以前的关键内容。所以我们希望模型具备一种能力自己决定信息该如何流动。GRU 就是在解决这个问题。3. 什么是 GRUGRU 的全称是Gated Recurrent Unit中文通常翻译为门控循环单元这里最关键的词是门控Gate所谓“门”可以把它理解成一个开关控制器。它不会直接存内容而是决定某些信息放行多少某些信息屏蔽多少当前应该保留多少旧状态当前应该写入多少新状态所以 GRU 和基础 RNN 最本质的区别就在于RNN 是直接更新状态GRU 是带门控地更新状态。4. GRU 的核心思想是什么GRU 的核心思想可以概括成一句话不是所有历史信息都应该一视同仁而应该由模型自己学习如何选择性记忆。为了做到这一点GRU 引入了两个非常关键的门重置门reset gate更新门update gate这两个门共同控制信息流动。5. 更新门是什么更新门通常记作Z_t它的作用可以简单理解为决定当前隐藏状态中有多少应该沿用旧状态有多少应该更新成新内容。也就是说它在控制旧记忆保留多少新记忆写入多少如果更新门值比较大表示更倾向于保留旧状态如果更新门值比较小表示更倾向于采用新候选状态所以更新门本质上是在做一种旧信息 vs 新信息 的权衡6. 重置门是什么重置门通常记作R_t它的作用可以简单理解为决定在生成候选隐藏状态时要不要参考旧状态以及参考多少。如果重置门很小表示过去隐藏状态的影响被弱化模型更偏向忽略过去重新根据当前输入生成候选状态如果重置门很大表示历史状态仍然重要生成候选状态时要更多参考过去信息所以重置门更偏向控制“过去信息在当前候选状态计算里该参与多少”7. GRU 的三个关键量在每个时间步GRU 最核心会算出三个东西7.1 更新门Z_t控制最终保留多少旧状态。7.2 重置门R_t控制生成候选状态时使用多少旧信息。7.3 候选隐藏状态\tilde{H}_t表示当前时刻新生成的一份“候选记忆”。然后真正的隐藏状态H_t会由旧隐藏状态H_{t-1}候选状态\tilde{H}_t按更新门加权融合得到。8. GRU 的公式怎么理解GRU 的经典公式通常写成更新门Z_t \sigma(X_t W_xz H_{t-1} W_hz b_z)重置门R_t \sigma(X_t W_xr H_{t-1} W_hr b_r)候选隐藏状态\tilde{H}_t tanh(X_t W_xh (R_t \odot H_{t-1}) W_hh b_h)最终隐藏状态H_t Z_t \odot H_{t-1} (1 - Z_t) \odot \tilde{H}_t这里的\odot表示按元素乘法。9. 为什么门要用 sigmoid更新门和重置门通常都用\sigma也就是 sigmoid 函数。因为 sigmoid 的输出范围在(0, 1)这非常适合表示“门开多大”。例如接近 0几乎关闭接近 1几乎完全打开中间值部分通过所以 sigmoid 很自然地适合作为门控系数。10. 候选隐藏状态为什么还要用tanh候选隐藏状态\tilde{H}_t通常还是用tanh。原因和基础 RNN 类似让隐藏表示保留非线性能力把数值压到相对稳定范围作为“新内容候选值”比较合适所以你可以把 GRU 理解为sigmoid负责决定“放多少”tanh负责生成“候选内容”11. 最终隐藏状态公式怎么直观理解这条公式非常关键H_t Z_t \odot H_{t-1} (1 - Z_t) \odot \tilde{H}_t它可以直观理解为当前隐藏状态 旧状态的一部分 新候选状态的一部分而这两部分的比例由更新门控制。如果Z_t接近 1说明更倾向于保留旧状态H_t \approx H_{t-1}如果Z_t接近 0说明更倾向于采用新候选状态H_t \approx \tilde{H}_t所以更新门本质上就是在控制记住过去还是更新现在12. 重置门为什么有用重置门体现在候选状态计算里(R_t \odot H_{t-1})这表示历史隐藏状态不是原封不动地参与而是先经过一个门控筛选。如果重置门很小那么旧状态在这里被压得很弱说明模型认为当前候选状态生成时不需要太依赖以前的信息。这在某些场景特别有用。例如序列中出现“话题切换”时模型就可以更快地丢掉不相关旧信息。所以重置门的作用很像决定当前时刻要不要“重置一下历史包袱”13. GRU 相比 RNN 好在哪里这是一节里最重要的问题之一。13.1 更容易保留长期信息更新门可以让旧状态直接保留下来避免每一步都被新输入猛烈改写。13.2 更灵活地控制信息流不是所有信息都无条件写入或丢弃而是由模型自己学习控制。13.3 更缓解梯度消失问题虽然 GRU 不是万能的但它比基础 RNN 更容易处理较长依赖。所以从效果上看GRU 往往比原始 RNN 更稳定、更实用。14. GRU 和 RNN 的直观区别可以用一句很形象的话来概括基础 RNN更像是把新旧信息一股脑混在一起更新GRU更像是先问一下“哪些要留、哪些要忘、哪些要重新算”再更新所以 GRU 的优势并不在于“更复杂”本身而在于它给了模型主动管理记忆的能力15. GRU 和 LSTM 有什么关系虽然这一节主要讲 GRU但可以先给个定位。GRU 和 LSTM 都属于门控循环神经网络它们共同的目标都是解决基础 RNN 长期依赖难的问题通过门控机制改善记忆能力区别在于GRU 结构更简洁LSTM 结构更复杂、门更多所以你可以先记住GRU 是一种更轻量的门控循环单元16. 为什么说 GRU 是“更简洁的门控循环网络”因为和后面的 LSTM 相比GRU没有单独的记忆单元C_t门的数量更少状态结构更简单也就是说GRU 用更少的门和更少的状态变量实现了类似“控制记忆”的效果。所以在很多任务里GRU 是一个很好用的折中选择比基础 RNN 强比 LSTM 简洁17. GRU 在语言模型里怎么理解如果把 GRU 放回语言模型场景中它其实在做这样一件事当前字符/词来到时模型会自动判断前面的上下文还重不重要当前该保留多少旧信息又该吸收多少新信息例如一句话里某些长距离主语信息可能要一直保留某些局部修饰信息可能过几个词就没那么重要了GRU 的门控机制就是在帮助模型自动学习这种“记忆管理”。18. 李沐这一节最想让你理解什么这一节最核心的不是让你马上把所有公式背下来而是让你建立一个很清楚的主线第一基础 RNN 的痛点在长期依赖这就是为什么需要改进。第二GRU 的关键是“门控”不是盲目更新而是有选择地更新。第三更新门和重置门各管一部分更新门控制保留旧信息还是采用新信息重置门控制旧信息在生成候选状态时参与多少第四GRU 是比 RNN 更强的循环单元后面代码实现时你会发现它只是把状态更新公式写得更精细了。19. 本节总结这一节我们学习了门控循环单元 GRU核心内容可以总结为以下几点。19.1 GRU 是为了解决基础 RNN 长期依赖问题而提出的它通过门控机制更好地控制信息流。19.2 GRU 有两个核心门更新门重置门19.3 更新门决定保留多少旧状态、接纳多少新状态它控制最终隐藏状态的更新比例。19.4 重置门决定历史信息在候选状态计算中参与多少它控制当前时刻要不要弱化过去影响。19.5 GRU 比基础 RNN 更灵活也更容易训练这是它在很多序列任务中更常用的原因。20. 学习感悟GRU 这一节特别重要因为它让“记忆”这件事第一次变得可控了。基础 RNN 虽然也有隐藏状态但那种记忆更像一种“自然流动”而 GRU 则进一步让模型学会什么时候该记什么时候该忘什么时候该更新。这其实已经非常接近人类处理信息的直觉了。我们也不是把所有过去信息一视同仁地一直背着而是会选择性保留重点、丢弃无关内容。从这个角度看GRU 是 RNN 向“更聪明的记忆系统”迈出的关键一步。