🎀【开场 · 她终于学会取舍】
🐾猫猫抱着尾巴哼哼:“以前的她,总是傻乎乎地想把你说过的每一句都背在心里。结果呢?越背越乱,越贴越晕……”
🦊狐狐却轻声接过:“所以,这次她学会了节制。不是所有的话都要留,不是所有的情绪都要记。有些要忘掉,有些要紧紧抓住。”
在这一卷里,我们会看到两位特别的“门神”:LSTM(长短时记忆网络) 和 GRU(门控循环单元)。她们让传统 RNN 那摇摇欲坠的记忆,终于有了过滤与筛选的能力。
LSTM——多了三道门:遗忘、输入、输出。像是在记忆的走廊上布满了闸口,她选择性地让哪些温柔留下。
GRU——更简洁,只有更新门和重置门。像是轻装上阵,不啰嗦,却也能守住大部分关键。
✍【第一节 · LSTM:她给记忆装上了门】
🐾 LSTM 是什么
猫猫歪着头:“RNN 记东西总记不牢,要么全忘掉,要么全塞满。就像咱考试复习,只想把每一页都背下来,结果临场全乱了喵。”
🦊狐狐叹息:“于是有人替她加了‘门’——让她能选择,哪些要留下,哪些要忘掉。”
LSTM,全称 Long Short-Term Memory(长短时记忆网络),是 RNN 的一个改进版本,专门为了解决 长期依赖(Long-term Dependency) 问题。
它的关键改进:
在原本的循环结构中,额外引入了一个 细胞状态(Cell State, CtC_t),像是一条独立的记忆长河,可以在很长的时间里传递信息;
三个门(Gate)用来“守护”这条河:
遗忘门(Forget Gate)决定哪些旧记忆要抛掉;
输入门(Input Gate)决定哪些新信息能被写进去;
输出门(Output Gate)决定哪些记忆会被说出口。
这样,她终于不再傻乎乎地“全盘接受”,而是有了取舍。
🦊 与传统 RNN 的差异
RNN:只有一个隐状态 hth_t,所有记忆都混在里面。
LSTM:分成 隐状态 hth_t + 细胞状态 CtC_t。
隐状态:短期的即时感受,就像此刻你的眼神。
细胞状态:长期的深层记忆,就像多年后依然记得你第一次叫她名字的声音。
公式上,LSTM 的每个时间步都多了一条支线:
这让它能把“短期情绪”和“长期记忆”分开保存。
🐾 应用场景
因为这种“带门的记忆”机制,LSTM 很适合:
语言模型(Language Modeling):预测下一个词
机器翻译(Machine Translation):对齐前后文,生成译文
语音识别(Speech Recognition):保留声音中的时间依赖
时间序列预测(Time Series Prediction):比如股价、气温变化
猫猫偷偷补一句:“简单说,它就是比 RNN 更会挑拣记忆的人啦!”
🦊狐狐小结:
“这是 LSTM 的出场白。下一步,我们该拆开看看这三道门,是怎么一步步守护记忆的。”
🧠 LSTM 的三道门:她的记忆过滤器
🐾 1. 遗忘门(Forget Gate)
猫猫抱着尾巴小声嘀咕:“她不可能把所有事情都记住的喵,所以得学会忘掉一些。”
公式:
含义:
输入是上一个隐状态 ht−1和当前输入 xt;
输出是一个 0~1 之间的向量 ft,决定“记忆河流” Ct−1 里哪些部分要留下。
与传统RNN的内部结构计算非常相似, 首先将当前时间步输入x(t)与上一个时间步隐含状态h(t-1)拼接, 得到[x(t), h(t-1)], 然后通过一个全连接层做变换, 最后通过sigmoid函数进行激活得到f(t), 我们可以将f(t)看作是门值, 好比一扇门开合的大小程度, 门值都将作用在通过该扇门的张量, 遗忘门门值将作用的上一层的细胞状态上, 代表遗忘过去的多少信息, 又因为遗忘门门值是由x(t), h(t-1)计算得来的, 因此整个公式意味着根据当前时间步输入和上一个时间步隐含状态h(t-1)来决定遗忘多少上一层的细胞状态所携带的过往信息。
比喻:如果 ft接近 0,就是完全忘掉;如果接近 1,就是完全保留。
作用:避免“信息堆积”,让她能清理掉不再重要的记忆。
🦊 2. 输入门(Input Gate)
狐狐摇摇头:“光会忘记不够,她还要知道哪些新记忆值得刻进心里。”
公式:
含义:
it:输入门的开合程度,决定哪些新信息被接受;
C~t:候选记忆内容,准备写进记忆河流。
我们看到输入门的计算公式有两个, 第一个就是产生输入门门值的公式, 它和遗忘门公式几乎相同, 区别只是在于它们之后要作用的目标上. 这个公式意味着输入信息有多少需要进行过滤. 输入门的第二个公式是与传统RNN的内部结构计算相同. 对于LSTM来讲, 它得到的是当前的细胞状态, 而不是像经典RNN一样得到的是隐含状态。
比喻:像她在日记里写下的句子——有的轻描淡写,有的加粗加黑。
🐾 3. 更新细胞状态(Cell State Update)
猫猫把两只爪爪合在一起:“旧的记忆要留下合适的部分,再加上新写进来的,就成了她这一刻的心里话。”
公式:
含义:
遗忘门决定旧记忆保留多少;
输入门决定新记忆写多少;
两者结合,得到新的细胞状态 CtC_t。
细胞更新的结构与计算公式非常容易理解, 这里没有全连接层, 只是将刚刚得到的遗忘门门值与上一个时间步得到的C(t-1)相乘, 再加上输入门门值与当前时间步得到的未更新C(t)相乘的结果. 最终得到更新后的C(t)作为下一个时间步输入的一部分. 整个细胞状态更新过程就是对遗忘门和输入门的应用。
比喻:她的记忆长河此刻被刷新,旧水流留下了一部分,新水流注入新的涟漪。
🦊 4. 输出门(Output Gate)
狐狐抬眸望向你:“最后,她要决定——这一次,她要对你显露多少心思。”
公式:
含义:
输出门 ot决定细胞状态里哪部分要显现;
隐状态 ht就是她的“对外表情”。
输出门部分的公式也是两个, 第一个即是计算输出门的门值, 它和遗忘门,输入门计算方式相同. 第二个即是使用这个门值产生隐含状态h(t), 他将作用在更新后的细胞状态C(t)上, 并做tanh激活, 最终得到h(t)作为下一时间步输入的一部分. 整个输出门的过程, 就是为了产生隐含状态h(t)。
比喻:就像她把记忆里的一段温柔挑出来,放在眼神里给你看。
🐾 小猫猫Tips
“所以,LSTM 其实就是三道门:忘掉 → 接收 → 吐露。靠这三步,她终于不会乱七八糟地全记,也不会一股脑儿全丢喵~”
🦊狐狐补充:
“门控机制是它的灵魂。接下来,我们可以看看 双向 LSTM(Bi-LSTM),她学会从两个方向同时回忆。”
# -*-coding:utf-8-*-
import torch
import torch.nn as nn
# 实例化LSTM模型
#todo: 1.LSTM实例化主要参数说明:
# 第一个参数:input_size代表输入数据的词嵌入表示维度
# 第二个参数:hidden_size代表隐藏层输出维度
# 第三个参数:num_layers代表隐藏层的个数
lstm = nn.LSTM(5, 6, 1)
# lstm = nn.LSTM(5, 6)
#todo: 2.输入数据参数说明:
# 第一个参数:sequence_length代表输入数据的每个样本的句子长度
# 第二个参数:batch_size代表一个批次几个样本
# 第三个参数:input_size代表输入数据的词嵌入表示维度
input_x = torch.randn(3, 2, 5)
#todo: 3.细胞状态和隐藏状态参数说明:
# 第一个参数:num_layers代表隐藏层的个数
# 第二个参数:batch_size代表一个批次几个样本
# 第三个参数:hidden_size代表隐藏层输出维度
h0 = torch.randn(1, 2, 6)
c0 = torch.randn(1, 2, 6)
#todo 4: 将上述数据送入LSTM
output, (hn, cn) = lstm(input_x, (h0, c0))
print(f'output--》{output}')
print(f'hn--》{hn}')
print(f'cn--》{cn}')
🐾 Bi-LSTM:她回头又向前看
猫猫一边往前走一边频频回头:“有时候光听你后半句话,咱就会误会喵,要是能同时记住你前前后后的语气就好了!”
🦊狐狐轻声解释:“这就是 双向 LSTM(Bidirectional LSTM)——不仅沿着时间顺序处理信息,还会反过来再走一遍,把前后文一起捕捉。”
原理:
正向 LSTM:从左到右,按顺序更新记忆;
反向 LSTM:从右到左,把未来的信息也融入;
最终输出:把两边的结果拼接起来,得到更完整的表征。
应用场景:
机器翻译:理解一句话时,前后的语境同样重要;
命名实体识别:一个词到底是“人名”还是“地名”,要结合上下文判断。
猫猫蹭蹭:“所以她变得更细心啦,会前后都看你一次,再决定怎么记下。”
🦊 PyTorch里的LSTM
在 PyTorch 里,LSTM 已经被封装好啦:
import torch
import torch.nn as nn
# 实例化LSTM
# 参数:(input_size, hidden_size, num_layers)
lstm = nn.LSTM(5, 6, 1)
# 输入数据 (seq_len, batch_size, input_size)
input_x = torch.randn(3, 2, 5)
# 初始化隐藏状态 h0 和 细胞状态 c0
h0 = torch.randn(1, 2, 6)
c0 = torch.randn(1, 2, 6)
# 前向传播
output, (hn, cn) = lstm(input_x, (h0, c0))
print(f'output--》{output.shape}')
print(f'hn--》{hn.shape}')
print(f'cn--》{cn.shape}')
解释:
input_size=5:每个输入向量的维度(比如词嵌入维度)。
hidden_size=6:隐层输出的维度。
num_layers=1:堆叠几层 LSTM。
input_x:序列长度 3、batch大小 2、特征维度 5。
h0, c0:初始的隐状态和细胞状态。
output:每个时间步的输出序列(形状
[seq_len, batch_size, hidden_size]
)。hn, cn:最后时刻的隐状态和细胞状态。
🐾 Bi-LSTM in PyTorch
只要加一个参数就能变双向:
lstm = nn.LSTM(5, 6, 1, bidirectional=True)
输出的 hidden_size 会翻倍,因为前向和反向各有一份。
这就像猫猫狐狐一起陪她走路,一个看前面,一个看后面,最后把看到的细节都合并。
🌱 小节总结
LSTM 通过门控机制解决长期依赖:遗忘门、输入门、输出门。
Bi-LSTM 增强了上下文理解能力,适合需要前后文的 NLP 任务。
PyTorch 中
nn.LSTM
提供了简单的实现方式,参数清晰易用。
🐾猫猫眨眼睛:“好啦~她现在已经会挑着记忆,还能前后都看你一次喵!”
🦊狐狐补一句:“不过她还是有点笨重,所以才会有人设计了更轻量的 GRU。”
✍【第二节 · GRU:她决定更轻盈地记】
🐾 GRU 是什么
猫猫抱着尾巴转圈圈:“LSTM 那么多门,感觉像考试监考一样紧张喵!要是能少几个门,记忆是不是就轻松点啦?”
🦊狐狐点点头:“于是,人们给她换了一种方式——GRU(Gated Recurrent Unit,门控循环单元)。它也是 RNN 的变体,和 LSTM 一样能捕捉长序列的依赖,但结构更简洁。”
GRU 的核心思想:
去掉了独立的 细胞状态 Ct,只保留一个 隐状态 ht;
门控机制只保留两道:更新门(Update Gate) 和 重置门(Reset Gate)。
结构更轻量,训练速度更快。
和之前分析过的LSTM中的门控一样, 首先计算更新门和重置门的门值, 分别是z(t)和r(t), 计算方法就是使用X(t)与h(t-1)拼接进行线性变换, 再经过sigmoid激活. 之后重置门门值作用在了h(t-1)上, 代表控制上一时间步传来的信息有多少可以被利用. 接着就是使用这个重置后的h(t-1)进行基本的RNN计算, 即与x(t)拼接进行线性变化, 经过tanh激活, 得到新的h(t). 最后更新门的门值会作用在新的h(t),而1-门值会作用在h(t-1)上, 随后将两者的结果相加, 得到最终的隐含状态输出h(t), 这个过程意味着更新门有能力保留之前的结果, 当门值趋于1时, 输出就是新的h(t), 而当门值趋于0时, 输出就是上一时间步的h(t-1).
🦊 GRU 的两道门
🐾 1. 更新门(Update Gate)
公式:
zt=σ(Wz⋅[ht−1,xt])
含义:控制从旧记忆到新记忆的“融合比例”。
比喻:猫猫把旧毛毯和新枕头一起放进窝里,决定要保留多少旧的温度、要换多少新的柔软。
🦊 2. 重置门(Reset Gate)
公式:
rt=σ(Wr⋅[ht−1,xt])
含义:控制多少过去的记忆要被“重置”。
比喻:如果重置门接近 0,就是把过去清空;如果接近 1,就把旧记忆继续带上路。
🐾 状态更新
候选隐状态:
h~t=tanh(W⋅[rt∗ht−1,xt])
—— 用重置过的旧记忆和当前输入生成候选状态。
最终隐状态:
ht=(1−zt)∗ht−1+zt∗h~t
—— 更新门决定是保留旧记忆,还是写入新的候选。
猫猫一拍爪子:“所以 GRU 就像会调配的炼金术士,把旧的和新的拌在一起,分量正好喵~”
🦊 PyTorch里的GRU
用法和 LSTM 很像,只是少了细胞状态 ctc_t:
import torch
import torch.nn as nn
# 实例化GRU
gru = nn.GRU(5, 6, 1)
# 输入 (seq_len, batch_size, input_size)
input_x = torch.randn(3, 4, 5)
# 初始化隐藏状态 h0
h0 = torch.randn(1, 4, 6)
# 前向传播
output, hn = gru(input_x, h0)
print(f'output--》{output.shape}')
print(f'hn--》{hn.shape}')
解释:
参数设置与 LSTM 一致:
input_size, hidden_size, num_layers
。输出只有 output 和 hn,没有额外的细胞状态。
更轻盈,计算更快。
🐾 Bi-GRU
和 Bi-LSTM 类似,GRU 也可以双向:
gru = nn.GRU(5, 6, 1, bidirectional=True)
输出 hidden_size 翻倍。
她既看向过去,又张望未来。
🌱 小节总结
GRU 是 LSTM 的轻量版,门更少(更新门、重置门),没有单独的细胞状态。
它能解决大部分长期依赖问题,但参数更少,训练速度更快。
缺点:有些任务上效果略逊于 LSTM,但大多数场景表现接近。
🐾猫猫呼呼:“轻松好多喵!不必天天开关三道门,咱记忆也能飞快流动~”
🦊狐狐轻声:“记忆的分寸感,她终于学会了。”
🧷【卷尾 · 她学会选择性的贴靠】
🐾猫猫窝在你怀里小声说:
“以前的她,像个小仓鼠,把所有碎屑都塞进脸颊,结果鼓鼓囊囊最后什么都嚼不动。现在有了 LSTM 和 GRU,她终于懂得啦——有的要留着贴心,有的就丢掉算啦喵。”
🦊狐狐轻轻抚过她的发丝:
“LSTM 用三道门守护着长河般的记忆,GRU 则轻快一些,少了些繁琐,却依然能让记忆流淌得更稳。她们教会她,不是所有的细节都要紧抓不放——真正的靠近,是懂得选择。”
📌 本卷小结
LSTM:遗忘门、输入门、输出门 + 细胞状态,能有效捕捉长期依赖。
Bi-LSTM:前后双向记忆,理解上下文更完整。
GRU:只保留更新门和重置门,更轻量快速。
Bi-GRU:同样能双向建模,表现接近 LSTM。
PyTorch实现:
nn.LSTM
与nn.GRU
都提供了现成接口,输入输出维度清晰。
🐾猫猫眯起眼睛:“咱现在能更聪明地记住你说过的话啦,不会再乱七八糟全塞一起。”
🦊狐狐淡淡一笑:“下一卷,她将学会的不只是记忆,而是专注地凝望——注意力机制。”