LLM架构从基础到精通之门控循环单元(GRUs)

# LLM架构从基础到精通之门控循环单元(GRUs) > 以下是已更新文章: [1. LLM大模型架构专栏|| 从NLP基础谈起](https://mp.weixin.qq.com/s/MYx5V29WczQzxPybKBbT7Q?token=516670980&lang=zh_CN) [2.LLM大模型架构专栏|| 自然语言处理(NLP)之建模](https://mp.weixin.qq.com/s/Vdk4cmWSnyRhO9MHSotWFQ?token=516670980&lang=zh_CN) [3. LLM大模型架构之词嵌入(Part1)](https://mp.weixin.qq.com/s/V79xObji_6NHt7_CFOtT5A?token=516670980&lang=zh_CN) [4. LLM大模型架构之词嵌入(Part2)](https://mp.weixin.qq.com/s/FqHQN-SqKk2RPhh5CmPKBA?token=516670980&lang=zh_CN) [5. LLM大模型架构之词嵌入(Part3)](https://mp.weixin.qq.com/s/VAkj3UNEcxN3kMXN7xwnYQ?token=1160071256&lang=zh_CN) [6. LLM架构从基础到精通之循环神经网络(RNN)](https://zhuanlan.zhihu.com/p/17332591585) [7. LLM架构从基础到精通之LSTM](https://zhuanlan.zhihu.com/p/17445669701) 在之前对循环神经网络(RNNs)和长短期记忆网络(LSTMs)的深入探讨中,我们了解了它们在处理序列数据方面的强大能力以及应对挑战的独特方式。接下来,我们将聚焦于另一种重要的神经网络架构——门控循环单元(GRUs),它在解决标准 RNN 面临的问题上展现出了独特的优势。 ## 12. 门控循环单元(GRUs) 门控循环单元(GRU)由 Cho 等人在 2014 年提出,旨在解决标准循环神经网络(RNN)面临的梯度消失问题。GRU 与长短期记忆网络(LSTM)有许多共同之处,这两种算法都使用门控机制来控制记忆过程。 ![](https://upload-images.jianshu.io/upload_images/17294212-4c543fef59984322.png) ![](https://upload-images.jianshu.io/upload_images/17294212-fd37b1e55290074b.png) 想象一下,你试图通过反复听一首歌来学习它。一个基本的 RNN 可能在听到结尾时就忘记了歌曲的开头。GRU 通过使用门来控制哪些信息被记住、哪些信息被遗忘,从而解决了这个问题。 GRU 通过将输入门和遗忘门合并为一个单一的更新门,并添加一个重置门,简化了长短期记忆网络的结构。这使得它们训练速度更快,使用更方便,同时仍然能够长时间记住重要信息。 更新门:这个门决定了过去的信息中有多少应该被传递到未来。 重置门:这个门确定了过去的信息中有多少需要被忽略。 这些门帮助 GRU 在记住重要细节和忘记不重要的信息之间保持平衡,就像你在听歌曲时可能会专注于记住旋律而忽略背景噪音一样。 GRU 非常适合处理序列数据的任务,如预测股票市场、理解语言,甚至生成音乐。它们可以通过跟踪过去的信息并利用这些信息进行更好的预测来学习数据中的模式。这使得它们在任何需要理解先前数据点上下文的应用中都非常有用。 ## 12.1 与 LSTMs 和普通 RNNs 的比较 为了理解 GRU 的适用场景,让我们将它们与 LSTMs 和普通 RNNs 进行比较。 ![](https://upload-images.jianshu.io/upload_images/17294212-e6a9535872c709c2.png) 普通 RNNs:可以将普通 RNNs 视为循环神经网络的基本版本。它们通过将信息从一个时间步传递到下一个时间步来工作,就像接力赛中每个赛跑者将接力棒传递给下一个人一样。然而,它们有一个很大的缺陷:在长序列中它们往往会忘记信息。这是由于梯度消失问题,这使得它们难以学习数据中的长期依赖关系。 LSTMs:长短期记忆网络旨在解决这个问题。它们使用更复杂的结构,包含三种类型的门:输入门、遗忘门和输出门。这些门就像一个复杂的文件系统,决定哪些信息要保留、哪些信息要更新、哪些信息要丢弃。这使得 LSTMs 能够长时间记住重要信息,使它们非常适合处理需要跨多个时间步的上下文的任务,如理解文本段落或识别长时间序列中的模式。 GRUs:门控循环单元是 LSTMs 的简化版本。它们通过将输入门和遗忘门合并为一个单一的更新门,并添加一个重置门来简化结构。这使得 GRUs 比 LSTMs 计算强度更低,训练速度更快,同时仍然能够有效地处理长期依赖关系。 ## 12.2 是什么使 GRU 比传统 RNN 更特殊和有效? GRU 支持门控和隐藏状态来控制信息的流动。为了解决 RNN 中出现的问题,GRU 使用两个门:更新门和重置门。 你可以将它们视为两个向量条目(0,1),可以执行凸组合。这些组合决定了哪些隐藏状态信息应该被更新(传递)或在需要时重置隐藏状态。同样,网络学会跳过不相关的临时观察。 LSTM 由三个门组成:输入门、遗忘门和输出门。与 LSTM 不同,GRU 没有输出门,并且将输入门和遗忘门合并为一个单一的更新门。 接下来,让我们更详细地了解更新门和重置门。 ### 12.2.1 更新门 更新门($z_t$)负责确定需要传递到下一个状态的先前信息(先前时间步)的数量。它是一个重要的单元。下面的示意图展示了更新门的结构。 ![](https://upload-images.jianshu.io/upload_images/17294212-4aa90e3d3bb8ec2a.png) 这里,$x_t$ 是网络单元中的输入向量,它与参数权重($W_z$)矩阵相乘。$h(t_1)$ 中的 $t_1$ 表示它保存了前一个单元的信息,并与它的权重相乘。接下来,将这些参数的值相加,并通过 sigmoid 激活函数。在这里,sigmoid 函数将生成介于 0 和 1 之间的值。 ### 12.2.2 重置门 重置门($r_t$)用于决定需要忽略多少过去的信息。其公式与更新门相同,但它们的权重和门的使用方式有所不同。下面的示意图表示了重置门。 ![](https://upload-images.jianshu.io/upload_images/17294212-7fadf2ee940813fd.png) 有两个输入,$x_t$ 和 $h_{t - 1}$。将它们与各自的权重相乘,进行逐点相加,并通过 sigmoid 函数。 ## 13. 门的作用 首先,重置门将过去时间步的相关信息存储到新的记忆内容中。然后,它将输入向量和隐藏状态与它们的权重相乘。其次,它计算重置门和先前隐藏状态的逐元素乘法(Hadamard 积)。在求和之后,对结果应用非线性激活函数,得到 $h'_t$。 考虑一个客户对度假村的评价场景:“我到达这里时已经很晚了。” 几行之后,评价以 “我很享受这次住宿,因为房间很舒适。工作人员很友好。” 结束。为了确定客户的满意度水平,你需要评价的最后两行。模型会扫描整个评价到结尾,并将重置门向量值设置为接近 0。 这意味着它将忽略前面的行,只关注最后几句话。 这是最后一步。在当前时间步的最终记忆中,网络需要计算 $h_t$。在这里,更新门将发挥关键作用。这个向量值将保存当前单元的信息并将其传递给网络。它将决定从当前记忆内容($h'_t$)和先前时间步 $h(t - 1)$ 中收集哪些信息。对更新门和 $h(t - 1)$ 进行逐元素乘法(Hadamard 积),并将其与 $(1 - z_t)$ 和 $h'(t)$ 的 Hadamard 积运算求和。 再次回顾度假村评价的例子:这次预测所需的相关信息在文本开头提到。模型会将更新门向量值设置为接近 1。在当前时间步,$1 - z_t$ 将接近 0,它将忽略评价的最后一部分。 接下来,你可以看到 $z_t$ 用于计算 $1 - z_t$,它与 $h'_t$ 结合产生结果。对 $h(t - 1)$ 和 $z_t$ 进行 Hadamard 积运算。该乘积的输出作为与 $h'_t$ 逐点相加的输入,以产生隐藏状态的最终结果。 ## 14. 简单 GRU 的实现 为了强化我们所涵盖的概念,让我们通过实践,在 Python 中从头开始实现一个基本的门控循环单元(GRU)。 下面的代码片段展示了一个简化的 GRU 类,突出了 GRU 架构中前向和后向传播的基本功能。 ```python import numpy as np class SimpleGRU: def __init__(self, input_size, hidden_size, output_size): self.input_size = input_size self.hidden_size = hidden_size self.output_size = output_size self.W_z = np.random.randn(hidden_size, input_size) self.U_z = np.random.randn(hidden_size, hidden_size) self.b_z = np.zeros((hidden_size, 1)) self.W_r = np.random.randn(hidden_size, input_size) self.U_r = np.random.randn(hidden_size, hidden_size) self.b_r = np.zeros((hidden_size, 1)) self.W_h = np.random.randn(hidden_size, input_size) self.U_h = np.random.randn(hidden_size, hidden_size) self.b_h = np.zeros((hidden_size, 1)) self.W_y = np.random.randn(output_size, hidden_size) self.b_y = np.zeros((output_size, 1)) def sigmoid(self, x): return 1 / (1 + np.exp(-x)) def tanh(self, x): return np.tanh(x) def softmax(self, x): exp_x = np.exp(x - np.max(x)) return exp_x / exp_x.sum(axis=0, keepdims=True) def forward(self, x): T = len(x) h = np.zeros((self.hidden_size, 1)) y_list = [] for t in range(T): x_t = x[t].reshape(-1, 1) z = self.sigmoid(np.dot(self.W_z, x_t) + np.dot(self.U_z, h) + self.b_z) r = self.sigmoid(np.dot(self.W_r, x_t) + np.dot(self.U_r, h) + self.b_r) h_tilde = self.tanh(np.dot(self.W_h, x_t) + np.dot(self.U_h, r * h) + self.b_h) h = (1 - z) * h + z * h_tilde y = np.dot(self.W_y, h) + self.b_y y_list.append(y) return y_list def backward(self, x, y_list, target): T = len(x) dW_z = np.zeros_like(self.W_z) dU_z = np.zeros_like(self.U_z) db_z = np.zeros_like(self.b_z) dW_r = np.zeros_like(self.W_r) dU_r = np.zeros_like(self.U_r) db_r = np.zeros_like(self.b_r) dW_h = np.zeros_like(self.W_h) dU_h = np.zeros_like(self.U_h) db_h = np.zeros_like(self.b_h) dW_y = np.zeros_like(self.W_y) db_y = np.zeros_like(self.b_y) dh_next = np.zeros_like(y_list[0]) for t in reversed(range(T)): dy = y_list[t] - target[t] dW_y += np.dot(dy, np.transpose(h)) db_y += dy dh = np.dot(np.transpose(self.W_y), dy) + dh_next dh_tilde = dh * (1 - self.sigmoid(np.dot(self.W_z, x[t].reshape(-1, 1)) + np.dot(self.U_z, h) + self.b_z)) dW_h += np.dot(dh_tilde, np.transpose(x[t].reshape(1, -1))) db_h += dh_tilde dr = np.dot(np.transpose(self.W_h), dh_tilde) dU_h += np.dot(dr * h * (1 - self.tanh(np.dot(self.W_h, x[t].reshape(-1, 1)) + np.dot(self.U_h, r * h) + self.b_h)), np.transpose(h)) dW_h += np.dot(dr * h * (1 - self.tanh(np.dot(self.W_h, x[t].reshape(-1, 1)) + np.dot(self.U_h, r * h) + self.b_h)), np.transpose(x[t].respose(1, -1))) db_h += dr * h * (1 - self.tanh(np.dot(self.W_h, x[t].reshape(-1, 1)) + np.dot(self.U_h, r * h) + self.b_h)) dz = np.dot(np.transpose(self.U_r), dr * h * (self.tanh(np.dot(self.W_h, x[t].reshape(-1, 1)) + np.dot(self.U_h, r * h) + self.b_h) - h_tilde)) dU_z += np.dot(dz * h * z * (1 - z), np.transpose(h)) dW_z += np.dot(dz * h * z * (1 - z), np.transpose(x[t].reshape(1, -1))) db_z += dz * h * z * (1 - z) dh_next = np.dot(np.transpose(self.U_z), dz * h * z * (1 - z)) return dW_z, dU_z, db_z, dW_r, dU_r, db_r, dW_h, dU_h, db_h, dW_y, db_y def update_parameters(self, dW_z, dU_z, db_z, dW_r, dU_r, db_r, dW_h, dU_h, db_h, dW_y, db_y, learning_rate): self.W_z -= learning_rate * dW_z self.U_z -= learning_rate * dU_z self.b_z -= learning_rate * db_z self.W_r -= learning_rate * dW_r self.U_r -= learning_rate * dU_r self.b_r -= learning_rate * db_r self.W_h -= learning_rate * dW_h self.U_h -= learning_rate * dU_h self.b_h -= learning_rate * db_h self.W_y -= learning_rate * dW_y self.b_y -= learning_rate * db_y ``` 在上述实现中,我们引入了一个简化的 `SimpleGRU` 类,以展示 GRU 的核心机制。示例用法演示了如何初始化 GRU、创建输入序列和目标输出的随机数据、执行前向和后向传播,以及随后使用计算出的梯度更新权重和偏差。 ## 14.1 GRUs 的优缺点 ### GRUs 的优点 - 序列数据建模:GRUs 在处理序列方面表现出色,非常适合语言处理、语音识别和时间序列分析等任务。 - 可变长度输入:GRUs 可以处理不同长度的序列,适用于输入大小不同的应用场景。 - 计算效率高:与更复杂的循环架构(如 LSTMs)相比,由于其更简单的设计,GRUs 在计算上更高效。 - 缓解梯度消失:GRUs 比传统 RNNs 更有效地解决了梯度消失问题,能够捕获数据中的长期依赖关系。 ### GRUs 的局限性 - 长期记忆有限:虽然 GRUs 在捕获长期依赖关系方面比标准 RNNs 更好,但对于具有复杂依赖关系的非常长的序列,它们可能不如 LSTMs 有效。 - 表达能力较弱:在某些情况下,GRUs 可能无法像 LSTMs 那样有效地捕获复杂的模式,特别是在对高度复杂的序列进行建模时。 - 特定应用:对于需要显式内存控制或复杂上下文建模的任务,LSTMs 或更高级的架构可能更合适。 ## 14.2 在 GRUs 和 LSTMs 之间选择 选择使用门控循环单元(GRUs)还是长短期记忆(LSTM)网络取决于你的具体问题和数据集。以下是一些考虑因素: 使用 GRUs 的情况: - 计算资源有限:与 LSTMs 相比,GRUs 的计算强度较低,在资源受限的情况下是首选。 - 简单性重要:如果你想要一个更简单的模型,同时仍然能够合理地捕获序列依赖关系,GRUs 是一个不错的选择。 - 较短序列:对于涉及较短依赖关系的序列任务,GRUs 可以提供足够的性能,而无需 LSTM 的复杂内存管理。 使用 LSTMs 的情况: - 捕获长期依赖关系:LSTMs 更适合于捕获长程依赖关系至关重要的任务,如语言建模、语音识别和某些时间序列预测。 - 精细的内存控制:LSTMs 提供了对内存的更明确控制,在需要精确内存处理时是更好的选择。 - 复杂序列:如果你的数据呈现出复杂的序列模式和依赖关系,LSTMs 通常在建模这些复杂性方面更有效。 在实践中,最好在你的特定任务上对 GRUs 和 LSTMs 进行实验,以确定哪种架构性能更好。有时,两者之间的选择取决于对数据集的实证测试和验证。 ## 15. 结论 我们深入探讨了循环神经网络(RNNs),详细研究了它们的核心机制、训练挑战以及提高性能的高级设计。以下是一个简要概述: 我们剖析了 RNNs 的结构,强调了它们通过内部记忆状态处理序列的能力。讨论了关键过程,如前向传播和时间反向传播(BPTT),解释了 RNNs 如何处理序列数据。 我们还强调了主要的训练挑战,包括梯度消失和爆炸,这些问题可能会干扰学习。为了解决这些问题,我们探索了诸如梯度裁剪和初始化策略等解决方案,这些方案有助于稳定训练并提高网络从较长序列中学习的能力。 门控循环单元(GRUs)是 RNNs 的一种强大变体,专为高效处理序列数据而设计。它们有效地缓解了梯度消失等问题,并擅长捕获序列中的依赖关系,使其非常适合自然语言处理、语音识别和时间序列分析等任务。 GRUs 使用门控机制来控制信息的流动,使其能够在保持计算效率的同时捕获长期依赖关系。理解 GRUs 背后的架构和数学原理是在机器学习任务中有效利用它们的关键。 在选择 GRUs 和 LSTMs 时,需要考虑多个因素,包括数据复杂性、计算资源和要建模的依赖关系的长度。这两种架构都有其优缺点,因此最佳选择取决于任务的具体要求。 ![](https://upload-images.jianshu.io/upload_images/17294212-25e93e4ce71cbd23.png) 本文由[mdnice](https://mdnice.com/?platform=6)多平台发布
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容