长短期记忆(Long Short-Term Memory,LSTM) 是 RNN 最重要的变体,也是深度学习 NLP 发展史上的里程碑。在 Transformer 出现之前(2017 年),LSTM 是序列建模的绝对王者。
# 背景:RNN 梯度消失
句子:"我住在法国,...(中间 200 个字)... 所以我会说___"
空格应该填 “法语”
RNN 的问题:
需要记住 200 个字前的"法国"。反向传播 时,梯度要连乘 200 次,导致前面的信息传不过来。
如果每次乘 0.9 → 0.9^200 ≈ 0.000000001 → 梯度消失
# LSTM 的结构及接口


LSTM 与 RNN 的接口的不同之处在于,LSTM 还有路径 c。这个 c 称为 记忆单元(或者简称为“单元”),相当于 LSTM 专用的记忆部门。
- 记忆单元的特点:仅在 LSTM 层内部接收和传递数据,对外部不可见,我们甚至不用考虑它的存在
- :
sigmoid函数用于求门的开合程度(sigmoid函数的输出范围在0.0 ~ 1.0)
# 输出门 Output Gate
- 作用:决定输出哪些信息作为隐藏状态 (求出隐藏状态 )
- 公式:
隐藏状态 ,记忆单元 和隐藏状态 的关系只是按元素应用 函数。这意味着,记忆单元 和隐藏状态 的_元素个数相同_。
⊙ 表示
阿达玛乘积。即,对应元素的乘积
# 遗忘门 Forget Gate
- 作用:决定从细胞状态中丢弃哪些信息
- 公式:
# 输入门 Input Gate
- 作用:决定更新哪些新信息到细胞状态
- 公式:
# 新的记忆单元

向记忆单元添加的新信息 :
新的记忆单元: ,细胞状态 = 遗忘 × 旧细胞 + 输入 × 新候选
也即上面的 g
这个 tanh 节点的作用不是门,而是将新的信息添加到记忆单元中。因此,它不用 sigmoid 函数 作为激活函数,而是使用 tanh 函数。
# LSTM 不会梯度消失
观察记忆单元的反向传播:

记忆单元的反向传播仅流过 + 和 × 节点。
+ 节点将上游传来的梯度原样流出,所以梯度没有变化(退化)× 节点的计算并不是矩阵乘积,而是对应元素的乘积(阿达玛积)
# PyTorch 代码示例
基础 LSTM:
import torch
import torch.nn as nn
class SimpleLSTM(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, num_classes):
super().__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
# LSTM 层
self.lstm = nn.LSTM(input\_size, hidden\_size,
num\_layers=num\_layers,
batch\_first=True,
dropout=0.3) # 层间 Dropout
# 全连接层
self.fc = nn.Linear(hidden\_size, num\_classes)
def forward(self, x):
# x: (batch\_size, seq\_len, input\_size)
# 初始化隐藏状态
h0 = torch.zeros(self.num\_layers, x.size(0), self.hidden\_size)
c0 = torch.zeros(self.num\_layers, x.size(0), self.hidden\_size)
# 前向传播
out, (hn, cn) = self.lstm(x, (h0, c0))
# out: (batch, seq\_len, hidden\_size)
# hn: (num\_layers, batch, hidden\_size)
# 取最后一个时间步的输出
out = self.fc(out\[:, -1, :\])
return out
# 使用
model = SimpleLSTM(input_size=100, hidden_size=128,
num_layers=2, num_classes=2)
IMDB 情感分析:
import torch.optim as optim
# 超参数
batch_size = 64
seq_len = 100
learning_rate = 0.001
epochs = 10
# 模型、损失、优化器
model = SentimentBiLSTM(vocab_size=10000, embed_dim=300,
hidden_size=128, num_classes=2)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# 训练循环
for epoch in range(epochs):
model.train()
total_loss = 0
for batch\_x, batch\_y in train\_loader:
# batch\_x: (64, 100), batch\_y: (64,)
optimizer.zero\_grad()
output = model(batch\_x)
loss = criterion(output, batch\_y)
loss.backward()
# 梯度裁剪(防止 LSTM 梯度爆炸)⭐
torch.nn.utils.clip\_grad\_norm\_(model.parameters(), 1.0)
optimizer.step()
total\_loss += loss.item()
# 验证
model.eval()
correct = 0
total = 0
with torch.no\_grad():
for batch\_x, batch\_y in val\_loader:
output = model(batch\_x)
predicted = torch.argmax(output, dim=1)
correct += (predicted == batch\_y).sum().item()
total += batch\_y.size(0)
val\_acc = correct / total
print(f"Epoch {epoch+1}: Loss={total\_loss:.4f}, Val Acc={val\_acc:.4f}")