序列建模能力的三次跃迁:RNN、LSTM与BiLSTM的机制演化与本质对比
NLP-AHU-037
本文从序列建模的角度出发,对RNN、LSTM与BiLSTM三种典型模型进行了梳理与分析。首先介绍RNN在处理序列数据中的基本思路及其存在的问题,在此基础上进一步说明LSTM如何通过细胞状态和门控机制改善长期依赖的建模能力。随后引出BiLSTM,通过双向结构使模型能够同时利用前后文信息。通过结合结构设计、计算过程与数学表达,对三类模型进行对比总结,并分析各自的适用场景与局限性,更清晰地理解序列模型从简单到复杂的演进逻辑。
引入
当我们阅读一句话时,通常不会只看某一个词就下结论,而是会结合前面已经出现的信息,甚至在看到后面的内容时,还会重新理解前面的意思。
这说明,人类在理解信息时,其实一直在利用“上下文”和“记忆”。
但对于传统神经网络来说,每一个输入往往是独立处理的,很难像人一样记住之前的信息,更无法结合前后内容进行理解。这就带来了一个问题:
如何让模型在处理序列数据时具备“记忆能力”,能够利用上下文信息?
为了解决这一问题,循环神经网络及其改进模型逐渐被提出。从最基础的RNN,到能够记住更长信息的LSTM,再到能够同时利用前后文的BiLSTM,序列建模能力也在不断提升。
目录
循环神经网络RNN——从静态处理到时序建模
1 设计启发
RNN 的提出源于神经网络对序列信息建模的需求。传统前馈神经网络只能处理相互独立的固定长度输入,难以捕捉时间依赖关系。
例如:
- 理解一句话时,需要结合前文词语才能读懂后文语义
-
分析股价走势时,需要参考历史数据才能预测未来趋势
-
语音识别时,需要结合前面连续的语音片段,才能准确判断当前时刻对应的语义避免歧义
因此,这种模仿人类的时序记忆逻辑、依赖历史信息的逻辑,正是循环神经网络RNN的设计灵感来源。
2 发展历程
RNN的发展经历了从早期理论探索到结构逐步完善的过程。最初,研究者尝试在神经网络中引入反馈连接,使模型能够保留历史信息,从而具备处理序列数据的能力。随后,随着以时间反向传播BPTT为代表的训练方法的提出,RNN逐渐具备了可训练性,并在语音识别、自然语言处理等任务中得到应用。
然而,研究也逐渐发现,传统RNN在学习长期依赖关系时存在明显困难,这一问题进一步推动了后续以LSTM为代表的改进模型的发展。
为了更清晰地展示RNN的发展脉络,整理了如下关键研究成果与代表性工作:

从早期Werbos对反向传播思想的探索,到Jordan和Elman提出具有代表性的循环网络结构,再到Bengio等人对长期依赖问题的分析,这些工作共同构成了RNN发展的重要基础。
其中,Jordan Network与Elman Network是早期最具代表性的两类循环神经网络结构,对后续RNN模型的设计产生了重要影响。
2.1 Jordan Network
Jordan Network由Michael I. Jordan提出,其核心思想是将上一时刻的输出作为当前时刻的输入之一,从而引入历史信息。该结构通过反馈连接使网络能够利用过去的预测结果,对当前输出产生影响。
这种设计体现了序列建模中“历史信息参与当前决策”的思想,是循环神经网络早期的重要探索之一。
2.2 Elman Network
Elman Network由Jeffrey L. Elman提出,是现代RNN最经典的基础形式之一。与Jordan Network不同,Elman Network将上一时刻的隐藏状态作为当前时刻的输入之一,作为模型内部的记忆。
该结构通过隐藏状态对历史信息进行编码,从而更有效地建模时间依赖关系,这一思想直接影响了后续RNN及其以LSTM为代表的变体的设计。
3 结构设计
为了使神经网络能够处理具有时间依赖关系的序列数据,RNN在传统前馈神经网络的基础上引入了循环结构,从而使模型具备记忆历史信息的能力。
核心由三大模块组成,关键在于隐藏层的循环连接,表示第
个时间步:
-
输入层Input:接收序列中每个时间步的输入,如NLP中的词向量、时间序列中的单个数据点,记为
。
-
隐藏层Hidden Layer:负责存储记忆的核心模块,隐含层的输出
会反馈到自身,作为下一个时间步的输入,与新的输入
共同计算新的隐藏状态
。
-
输出层Output:根据隐藏状态
,输出当前时间步的结果
。

RNN隐藏层在每个时间步会产生一个隐藏状态,并在时间维度上传递。
4 算法细节
为了更直观地理解RNN的计算过程,可以如上图将其在时间维度上进行展开,进行从
到
的逐时间步计算:

- 在每一个时间步
接收当前输入
- 通过线性变换结合
与
的隐藏状态
,传入激活函数,计算当前隐藏状态
- 将隐藏状态
通过线性变换,传入对应激活函数,得到输出
-
将传递
给下一个时间步
,重复上述步骤,直到处理完所有序列
具体推导细节将在后面部分阐述。展开后的结构呈现为一个沿时间连接的链式网络,其中每一个时间步的计算结构是相同的,且共享同一组参数。这种设计使得RNN能够处理不同长度的序列数据,同时保证模型参数规模不会随序列长度增加而增长。
因此从本质上看,RNN可以理解为在时间维度上重复应用同一个网络结构,通过隐藏状态实现信息在序列中的逐步传递。
5 数学表达
5.1 前向传播
在明确RNN的结构设计后,可以从计算流程的角度进一步理解其工作机制。RNN的计算过程可以看作是在时间维度上的递归计算,在每一个时间步重复执行相同的操作。
- 输入序列:
,其中
表示第
个时间步的输入,
表示序列长度
- 隐藏状态:
,其中
表示第
个时间步的隐藏状态
- 输出序列:
,其中
表示第
个时间步的输出
- 初始隐藏状态:
,表示序列开始前的初始状态,通常可初始化为零向量
- 输入权重矩阵:
,用于将当前输入
映射到隐藏状态空间
- 循环权重矩阵:
,用于将上一时刻隐藏状态
映射到当前隐藏状态
- 输出权重矩阵:
,用于将隐藏状态
映射为输出
- 偏置项:
和
,分别表示隐藏层和输出层的偏置
- 隐藏层激活函数:
,通常取
或
,用于引入非线性表达能力。
- 输出层激活函数:
,具体形式取决于任务类型
5.1.1 输入接收
在时间步,模型接收当前输入
。
可以是不同形式的数据,例如自然语言处理中的词向量等。
5.1.2 隐藏状态更新
RNN的核心在于隐藏状态的递归更新。当前隐藏状态由当前输入
和上一时刻的隐藏状态
共同决定:
这一过程体现了RNN的记忆机制,历史信息通过隐藏状态在时间维度上传递,并影响当前时刻的表示。
5.1.3 输出计算
在得到当前隐藏状态后,可以进一步计算当前时间步的输出:
对于多分类任务,通常使用函数,将输出映射为各类别上的概率分布;对于二分类任务,通常使用
函数,将输出压缩到
区间;而对于回归任务,输出层通常不使用激活函数,直接输出线性结果。
如果是多分类任务,可以进一步写为:
其中函数定义为:
这里表示类别数,
表示第
个类别对应的得分。
5.1.4 状态传递与递归
在实际计算过程中,上述步骤会在时间维度上不断重复执行,即对于,模型依次完成输入接收、隐藏状态更新和输出计算,从而得到完整的输出序列
。
所有时间步共享同一组参数、
、
、
、
,因此RNN能够处理不同长度的序列,而模型参数规模不会随序列长度增加而增长。
5.2 反向传播
在完成前向传播并得到输出结果后,需要通过损失函数衡量模型预测与真实目标之间的差异,并依据误差对模型参数进行更新。
由于 RNN隐藏状态在时间维度上存在递归依赖关系,误差信息不仅要在网络层之间传播,还需要沿时间维度向前传递。因此,RNN的训练通常采用时间反向传播算法BPTT。
如图所示,RNN可以沿时间维度展开为一个链式结构,每一个时间步对应一个输出和损失:

在该展开结构中,模型预测输出与真实标签
共同决定当前时间步的损失
,所有时间步的损失共同构成整体优化目标,从而为后续梯度沿时间传播提供基础。
5.2.1 损失函数建模
对于长度为的输入序列,其整体损失函数可表示为:
其中,表示整个序列的总损失,
表示第
个时间步的损失。
例如,在分类任务中,每个时间步的损失可以写为:
其中,为类别数。
5.2.2 梯度传播机制
由于前向传播过程中隐藏状态满足递推关系:
将该结构沿时间维度展开后,可以得到一个链式结构网络
这种展开方式使RNN可被视为一个深度为的前馈网络,从而可以应用标准反向传播算法。
在反向传播过程中,误差信息不仅在网络层之间传播,还需要沿时间维度逐步向前传递。对于较早时刻的隐藏状态,其对当前时刻损失
的影响可表示为:
进一步展开单步梯度项:
因此整体梯度本质上是多个时间步的矩阵连乘:
由此可见,RNN的梯度传播本质上是沿时间维度的多步链式乘积,可以看到,梯度的变化主要取决于该连乘结构的性质。当序列较长时,会出现以下两种典型情况:
- 梯度消失
当权重矩阵,则在多次连乘过程中,梯度会不断缩小:
此时:
- 梯度爆炸
当权重矩阵,则在多次连乘过程中,梯度会不断放大:
此时:
这一问题是传统RNN结构的主要局限之一,也是后续LSTM等改进模型提出的重要动机。
6 数学本质
从数学角度看,RNN并不是若干相互独立的计算单元,而是一个在时间维度上重复作用的递归函数。对于输入序列,模型在每个时间步利用当前输入和历史状态共同构造新的隐藏表示,因此其本质可以抽象为:
其中,表示模型参数集合。输出则可表示为:
由此可见,RNN本质上建立的是从输入序列到输出序列的映射关系。若记输入序列为,输出序列为
,则模型可以看作:
其中表示由递归结构定义的序列映射函数。
RNN的一个重要数学特征是参数共享, 共享参数表明循环神经网络中的每一步都在做相同的事,只是输入不同, 因此大大地降低了网络中需要学习的参数,从而有利于改善训练效率。
因此RNN可以理解为一个带有隐藏状态的动态系统。隐藏状态随着时间递推更新,输出则由当前状态决定,正是这种递归建模使RNN能够描述序列中的时间依赖关系。
7 应用与优缺点
7.1 典型应用
由于RNN能够建模序列中的时间依赖关系,因此被广泛应用于各类序列数据处理任务中,主要包括:
-
自然语言处理
如文本分类、语言模型、机器翻译、情感分析等。 -
语音识别与处理
用于语音信号建模,实现语音识别、语音合成等任务。 -
时间序列预测
如股票价格预测、气象数据分析、传感器数据预测等。 -
序列标注任务
如命名实体识别、词性标注等。
7.2 输入输出结构分类
根据输入序列与输出序列之间的关系,RNN可以分为如下多种结构形式:

异步不等长many-to-many时序建模常对应Seq2Seq任务,采用编码器–解码器Encoder–Decoder结构
7.3 优点与局限性
| 优点 | 建模时间依赖关系 | 通过隐藏状态传递历史信息 |
| 参数共享 | 所有时间步使用同一组参数 | |
| 结构简单 | 易于理解和实现 | |
| 缺点 | 难以捕捉长期依赖 | 早期信息在长序列中逐渐衰减 |
| 梯度消失 / 梯度爆炸 | 反向传播过程中梯度不稳定 | |
| 难以并行计算 | 时间步间存在依赖导致计算效率较低 |
-
难以捕捉长期依赖
- 实际应用中,RNN对远距离信息的记忆能力较弱。当序列较长时,早期输入对后续时刻的影响会随着状态的不断更新而逐渐减弱,从而使模型更倾向于关注最近几个时间步的信息,而难以有效利用较早出现的重要上下文。
- 提出LSTM,捕捉远距离的依赖关系
- 梯度消失 / 梯度爆炸
- RNN通过时间反向传播BPTT进行训练。在反向传播过程中,梯度需要沿着时间维度从后向前不断传递。当序列较长时,梯度会经历多次链式相乘,易导致梯度消失或梯度爆炸。
- 提出LSTM,有效缓解梯度消失
8 核心代码
使用PyTorch架构简单展示RNN模型代码如下:
import torch
import torch.nn as nn
class SimpleRNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
# RNN层:实现 h_t = f(x_t, h_{t-1})
self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
# 全连接层:将隐藏状态映射为输出
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
"""
x: 输入序列
形状 (batch_size, seq_len, input_size)
"""
# RNN前向传播
out, h_n = self.rnn(x)
"""
out: 每个时间步的隐藏状态
形状 (batch_size, seq_len, hidden_size)
h_n: 最后一个时间步的隐藏状态
形状 (1, batch_size, hidden_size)
"""
# 取最后一个时间步的隐藏状态(代表整个序列)
out = out[:, -1, :]
# 映射为输出(分类/回归)
out = self.fc(out)
return out
可以看到,RNN通过递归方式在时间维度上传递隐藏状态,对应前文的单向序列建模。
长短期记忆神经网络LSTM——从短程记忆到长期依赖
1 设计启发
LSTM的提出源于对传统RNN在序列建模中局限性的改进需求。尽管RNN能够通过隐藏状态在一定程度上保留历史信息,但在实际应用中,随着序列长度的增加,模型难以有效捕捉远距离的依赖关系。
例如:
-
在理解一段较长文本时,句首的重要信息可能在后续处理中逐渐被遗忘
-
在时间序列预测中,较早阶段的关键变化往往难以持续影响当前预测结果
-
在语音识别中,长时间跨度的上下文信息对准确识别当前语音具有重要作用
产生这一问题的根本原因在于RNN在时间维度上的信息传递依赖连续的非线性变换,导致历史信息在传播过程中不断衰减,难以长期保留。
因此,如何在序列建模过程中有选择地保留重要信息、过滤无关信息,并实现长期稳定的记忆能力,成为改进RNN结构的关键方向。
基于这一需求,LSTM引入细胞状态和门控机制,对信息的保留、遗忘与更新进行动态控制,从而使模型能够更有效地建模长期依赖关系。
因此,这种在时间维度上对信息进行选择性控制、强化长期记忆能力的机制设计,正是长短期记忆网络LSTM的核心设计启发。
2 发展历程
LSTM的发展是在传统RNN研究基础上,针对其在长期依赖建模中的局限性逐步演进而来的。虽然RNN能够通过递归结构引入历史信息,但在训练过程中仍难以稳定地学习长距离依赖关系。
1997年,Hochreiter与Schmidhuber提出了长短期记忆网络LSTM,通过引入细胞状态和门控机制,实现对信息的选择性保留与遗忘,从而有效缓解了传统RNN在长期依赖建模中的困难。
随后,研究者对LSTM结构进行了进一步优化。Gers等人提出了遗忘门,使模型能够主动丢弃无关信息,从而提升了模型的灵活性与稳定性。

LSTM的提出标志着从简单递归结构向可控记忆机制的转变,为后续GRU及注意力机制等模型的发展奠定了基础。
3 结构设计
针对传统RNN在长序列建模中存在的问题,LSTM显著提升模型对长时依赖关系的建模能力。
LSTM的核心仍基于时间递归结构,但在隐藏层内部进行了结构性扩展。
- 输入层 Input:接收当前时间步的输入
,如NLP中的词向量或时间序列数据。
- 状态变量 State:
- 细胞状态 Cell State:记为
,用于在时间维度上传递长期信息。该状态在多个时间步之间相对稳定地传递,仅通过门控机制进行少量更新,从而有效缓解信息衰减问题。
- 隐藏状态 Hidden State:记为
,表示当前时间步的输出状态,并传递给下一时间步和输出层。
- 细胞状态 Cell State:记为
- 门控机制 Gates:
- 遗忘门 Forget Gate:用于控制上一时刻细胞状态
中的信息保留程度,决定哪些历史信息需要被遗忘,其输出记为
。
- 输入门 Input Gate:用于控制当前输入
所携带的新信息中,有多少应写入细胞状态,实现对新信息的选择性更新,其输出记为
。
- 候选状态 Candidate State:记为
,由当前输入
与上一时刻隐藏状态
共同生成,作为待写入细胞状态的新信息。
-
输出门 Output Gate:用于控制当前细胞状态
中的信息输出程度,从而生成当前隐藏状态
,其输出记为
。
- 遗忘门 Forget Gate:用于控制上一时刻细胞状态
-
输出层 Output:根据隐藏状态
,生成当前时间步的输出结果
。

综上,LSTM通过引入细胞状态作为长期信息的传递通道,并利用遗忘门、输入门和输出门对信息流进行精细控制,实现了对历史信息的选择性保留与更新。
4 算法细节
LSTM的算法流程核心是细胞状态的更新和门控的开关控制,每个时间步逻辑如下:

-
在每一个时间步
接收当前输入
,通过
激活函数,得到遗忘门向量
,决定遗忘细胞状态中哪些信息
-
通过
激活筛选重要新信息,得输入门向量
;同时通过
激活生成候选细胞状态
,作为待存入细胞状态的新信息
-
结合遗忘门和输入门的结果更新细胞状态。先通过
遗忘旧信息,再通过
存入新信息,得到新的细胞状态
-
通过
激活筛选需要输出的信息,得到输出门向量
;同时将细胞状态
通过
激活,与
逐元素相乘,得到当前隐藏状态
,传递给下一个时间步
-
可根据需求,由
计算输出
。
综上,LSTM在每个时间步通过门控机制对信息流进行逐步调节。首先通过遗忘门筛选历史信息,再通过输入门引入新信息,并更新细胞状态,最后通过输出门生成当前隐藏状态。
5 数学表达
在明确LSTM的结构设计后,可以从计算流程的角度进一步理解其工作机制。与RNN类似,LSTM的计算过程同样是在时间维度上的递归计算,但在每一个时间步中引入了门控机制与细胞状态,从而实现对信息的精细控制。
- 输入序列:
,其中
表示第
个时间步的输入,
表示序列长度
- 隐藏状态:
,其中
表示第
个时间步的隐藏状态
- 细胞状态:
,其中
表示第
个时间步的细胞状态
- 输出序列:
,其中
表示第
个时间步的输出
-
遗忘门输出:
-
输入门输出:
-
输出门输出:
-
候选状态:
-
遗忘门、输入门、候选状态输出门、输出层对应权重矩阵:
、
、
、
、
-
遗忘门、输入门、候选状态输出门、输出层对应偏置项:
、
、
、
、
-
激活函数:
表示
函数,
表示双曲正切函数
-
符号
表示逐元素乘法
5.1 前向传播
在每一个时间步,LSTM的前向传播过程如下:
5.1.1 遗忘门计算
遗忘门根据当前输入和上一时刻隐藏状态,决定细胞状态中历史信息的保留程度:
5.1.2 输入门计算 候选状态生成
输入门用于控制当前输入信息写入细胞状态的比例:
同时根据当前输入与上一时刻隐藏状态生成新的候选信息:

5.1.3 细胞状态更新
结合遗忘门与输入门,对细胞状态进行更新:
该过程体现了LSTM的核心机制:一方面通过对历史信息进行筛选,另一方面通过
控制新信息的写入,从而实现对长期记忆的动态更新。
5.1.4 输出门计算 隐藏状态更新
输出门用于控制细胞状态中信息的输出比例:
同时根据当前细胞状态生成隐藏状态:

5.1.5 输出计算
在得到隐藏状态后,可以进一步计算当前时间步的输出:
其中,为输出层激活函数,其形式取决于具体任务,如分类任务中常用
。在某些简化模型或表示学习任务中,隐藏状态
也可以直接作为输出使用,此时可视为
。
5.2 反向传播
在完成前向传播并得到输出结果后,需要通过损失函数衡量模型预测与真实目标之间的差异,并依据误差对模型参数进行更新。
与RNN类似,由于模型在时间维度上存在递归结构,误差信息不仅需要在网络层之间传播,还需要沿时间维度逐步向前传递,因此,LSTM的训练同样采用时间反向传播算法BPTT。
5.2.1 时间展开与损失函数
与RNN相同,LSTM可以沿时间维度展开为一个链式结构。对于长度为的序列,其整体损失函数可以表示为:
其中,表示第
个时间步的损失,由预测输出
与真实标签
共同决定。
5.2.2 梯度传播路径
在反向传播过程中,梯度会从输出层开始,依次传递到隐藏状态、细胞状态
,以及各个门控变量,最终用于更新对应的权重矩阵和偏置项。与RNN不同的是,LSTM中存在两条主要的梯度传播路径:
-
一条通过隐藏状态
传播
-
一条通过细胞状态
沿时间维度传递
5.2.3 细胞状态的梯度传递
LSTM的关键在于细胞状态的更新方式:
由此可以得出细胞状态的梯度在时间维度上的传播形式为:
这表明,梯度在传播过程中通过遗忘门进行逐元素调节,从而沿时间维度传递至前一时刻的细胞状态。
与此同时,梯度还会通过隐藏状态以及各门控变量继续向前传播,并最终用于更新对应的权重矩阵和偏置项。
6 数学本质
前述RNN模型本质可以表示为一个在时间维度上递归展开的非线性函数:
该结构通过不断叠加非线性变换来建模序列依赖关系,但其梯度传播依赖于多步矩阵连乘,容易导致梯度消失或爆炸问题,从而限制了对长期依赖关系的建模能力。
LSTM在RNN的基础上引入了新的状态变量——细胞状态,并将原有的单一状态递归扩展为双状态递归结构:
其中,细胞状态作为主要的信息传递通道,在时间维度上承担长期记忆的作用,而隐藏状态
则用于当前时刻的输出表示。这种结构性扩展使得信息在时间上的传播路径由原来的嵌套非线性变换转变为显式状态传递。
LSTM的核心更新形式为:
该表达式以加法结构为主,使历史信息能够通过直接传递至当前状态
。同时,门控变量
,
,
由
函数生成,其取值范围在
,从而对信息的保留、写入与输出进行连续调节,使模型具备对信息流的动态控制能力。
从梯度传播角度来看,RNN中的梯度在时间维度上表现为权重矩阵的连乘,而LSTM中细胞状态的梯度在时间维度上的传播形式为:
该形式避免了梯度对权重矩阵的反复依赖,使梯度能够在时间维度上更加稳定地传递;当接近1时,梯度可以近似无衰减传播,从而有效缓解梯度消失问题。同时由于门控机制的存在,也在一定程度上抑制了梯度爆炸,但通常仍需配合梯度裁剪等方法以保证训练稳定性。
综上,LSTM通过引入细胞状态与门控机制,将传统RNN中的乘法链式递归转化为加法主导的动态结构,实现了对信息流与梯度流的有效调控,从而增强了对长期依赖关系的建模能力。
7 应用与优缺点
在输入输出结构形式上,LSTM与前述RNN类似,同样适用于one-to-many、many-to-one以及many-to-many等多种序列建模任务形。
不同之处在于,LSTM能够在上述结构中更有效地建模长距离依赖关系,尤其适用于序列较长或上下文关联较强的任务。例如,在文本分类或序列标注任务中,LSTM能够更好地保留远距离上下文信息,从而提升模型表现。
| 优点 |
长期依赖建模能力 | 能够保留较长范围的历史信息 |
| 缓解梯度消失 | 加法结构与门控机制 | |
| 信息具有选择性 | 通过门控机制对信息动态筛选与更新 | |
| 缺点 |
计算开销较大 | 多组门控参数增加模型复杂度 |
| 难以并行计算 | 时间步之间存在依赖关系 | |
| 单向依赖限制上下文建模 | 每个时间步无法直接获取未来信息 |
尽管LSTM在一定程度上解决了传统RNN中的梯度消失问题,并显著提升了对长期依赖关系的建模能力,但其在实际应用中仍存在一些局限性,并推动了LSTM的后续改进与发展:
-
单向依赖限制上下文建模
-
标准LSTM在每个时间步仅利用历史信息,无法直接获取未来上下文。NLP中词语语义通常依赖前后文,仅依赖单向信息可能导致表示不充分。
-
提出双向循环神经网络BiLSTM,通过正向与反向两个LSTM同时建模前后文信息。
-
-
模型结构复杂、计算开销较大
-
LSTM引入多组门控参数,导致参数量增加、计算成本较高。
-
提出了如GRU等轻量化变体,在减少参数的同时保持较好性能。
-
-
对极长序列建模能力有限
-
尽管LSTM缓解了长期依赖问题,但在极长序列中信息仍可能逐渐衰减。
-
引入了注意力机制以及Transformer模型,实现对序列中任意位置关系的直接建模。
-
8 核心代码
使用PyTorch架构简单展示LSTM模型代码如下:
class SimpleLSTM(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
# LSTM层:包含门控机制 细胞状态 c_t
self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
# 输出层
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
"""
x: (batch_size, seq_len, input_size)
"""
# LSTM前向传播
out, (h_n, c_n) = self.lstm(x)
"""
out: 每个时间步的隐藏状态 h_t
(batch_size, seq_len, hidden_size)
h_n: 最后一个时间步的隐藏状态
(1, batch_size, hidden_size)
c_n: 最后一个时间步的细胞状态
(1, batch_size, hidden_size)
"""
# 取最后时间步的隐藏状态作为序列表示
out = out[:, -1, :]
# 输出层
out = self.fc(out)
return out
可以发现,LSTM在RNN基础上引入细胞状态与门控机制,使模型具备长期记忆能力。
双向长短期记忆网络BiLSTM——从单向理解到双向上下文
1 设计启发
BiLSTM的提出源于对传统LSTM在序列建模中局限性的进一步改进需求。尽管LSTM能够通过门控机制有效建模长期依赖关系,但在标准结构中,信息仅沿时间正向传播,即每个时间步只能利用过去的历史信息,难以充分利用未来时刻的上下文信息。
在许多实际任务中,当前时刻的语义往往不仅依赖于历史信息,还依赖于未来信息。例如:
-
在句子理解任务中,一个词的含义通常需要结合其前后文共同确定
-
在序列标注任务中,当前标签的判断往往依赖后续词语提供的语义线索
-
在语音识别中,后续语音片段的信息有助于消除当前语音的歧义
产生这一问题的根本原因在于:LSTM的状态更新过程是沿时间单向递归展开的,其隐藏状态 仅依赖于
和
,而不包含来自未来时刻的信息。
基于这一需求,双向循环神经网络BiLSTM通过在原有LSTM结构的基础上引入正向与反向两个独立的LSTM网络,分别对序列进行前向和反向建模,提升模型的表达能力。
这种在时间维度上同时引入双向信息流、强化上下文建模能力的结构设计,正是BiLSTM的核心设计启发。
2 发展历程
BiLSTM源于对双向循环结构的扩展思想。早在1997年,Schuster和Paliwal提出了双向循环神经网络Bidirectional RNN,通过同时建模序列的正向与反向信息,使模型能够利用完整的上下文信息。随着LSTM模型的提出与发展,研究者将双向结构与LSTM相结合,形成了BiLSTM模型,从而进一步提升了序列建模能力。
3 结构设计
BiLSTM的核心仍基于时间递归结构,但与单向LSTM不同的是,其隐藏层由两个相互独立的LSTM组成,一个按照时间正向处理输入序列,另一个按照时间反向处理输入序列。二者在每个时间步分别生成对应的隐藏状态,随后进行融合,共同形成当前时刻的输出表示。
-
输入层 Input:接收当前时间步的输入
,如NLP中的词向量或时间序列中的观测值。
-
双向隐藏层 Bidirectional Hidden Layer:BiLSTM核心模块,由两个方向相反的LSTM组成:
-
正向LSTM Forward LSTM:按时间顺序对序列进行建模,在第
个时间步生成正向隐藏状态,记为
。
-
反向LSTM Backward LSTM:按时间逆序对序列进行建模,在第
个时间步生成反向隐藏状态,记为
。
-
-
状态融合 State Fusion:在每个时间步,将正向隐藏状态
与反向隐藏状态
进行融合,得到当前时刻的整体表示。常见方式为向量拼接:
其中,表示向量拼接操作。
-
输出层 Output:根据融合后的隐藏状态
,生成当前时间步的输出结果
。

4 算法细节
BiLSTM在每个时间步的计算过程可以看作两个方向的LSTM同时进行,并在当前时刻对结果进行融合,其计算流程如下:
-
在每一个时间步接收当前输入
,该输入同时送入正向与反向两个LSTM网络进行处理
-
正向LSTM按照时间顺序
递归计算,将当前输入
与上一时刻隐藏状态
及细胞状态
结合,通过门控机制得到当前正向隐藏状态
-
反向LSTM按照时间逆序
递归计算,将当前输入
与下一时刻的隐藏状态
及细胞状态
结合,得到当前反向隐藏状态
-
在当前时间步,将正向隐藏状态
与反向隐藏状态
进行融合,通常采用向量拼接,得到最终隐藏表示
-
将融合后的
传入输出层,通过线性变换及激活函数计算当前时间步的输出
-
正向与反向两个方向的状态分别沿时间维度继续传递,重复上述过程,直至完成整个序列的计算

具体而言,BiLSTM可以理解为在时间维度上同时展开两条相互独立但结构相同的计算路径,并在每个时间步对两条路径的结果进行融合。这种结构使得模型在生成当前表示时能够同时利用过去与未来的信息,从而获得更加完整的上下文表达。
5 数学表达
在BiLSTM中,模型在每个时间步同时进行正向与反向两个方向的序列建模。其核心计算仍基于LSTM单元,但在结构上引入双向信息流与状态融合机制。
- 输入序列:
,其中
表示第
个时间步的输入,
表示序列长度
-
正向隐藏状态:
-
反向隐藏状态:
-
融合后的隐藏状态:
-
输出序列:
,其中
表示第
个时间步的输出
-
正向与反向LSTM分别具有独立参数:
和
-
向量拼接操作:
5.1 前向传播
在每个时间步,BiLSTM分别通过正向与反向两个LSTM对输入序列进行建模:
5.1.1 正向LSTM计算
按照时间顺序递归计算:
其中,表示正向LSTM单元,其内部计算过程与前文LSTM完全一致。
5.1.2 反向LSTM计算
按照时间逆序递归计算:
其中,表示反向LSTM单元,其计算过程同样与前文LSTM一致。
5.1.3 状态融合
在每个时间步,将两个方向的隐藏状态进行融合:
5.1.4 输出计算
融合后的隐藏状态用于生成当前输出:
5.2 反向传播
BiLSTM的训练同样基于BPTT算法。与单向LSTM相比,其梯度传播路径在结构上分为两条独立方向。
5.2.1 损失函数
与LSTM相同,整体损失函数可表示为:
其中由预测输出
与真实标签共同决定。
5.2.2 梯度传播路径
在反向传播过程中,梯度从输出层开始,首先作用于融合后的隐藏状态:
由于融合操作为拼接,可将梯度拆分为两部分:
,
- 正向LSTM梯度传播,梯度沿时间反向传播
:
- 反向LSTM梯度传播,梯度沿时间正向传播
:
两者内部梯度传播形式均与单向LSTM一致。
5.2.3 参数更新
正向与反向两个LSTM分别更新各自参数:
,
二者相互独立,不共享权重。
BiLSTM在数学表达上可以看作两个独立LSTM的并行组合,其前向传播通过双向建模获得上下文信息,而反向传播则沿两个时间方向分别进行梯度传递。在不改变LSTM基本计算形式的前提下,实现了对序列双向依赖关系的建模。
6 数学本质
在前述LSTM模型中,其本质可以表示为在时间维度上的单向递归结构,通过细胞状态实现信息的稳定传递。然而,该结构在每个时间步仅依赖历史信息,其隐藏状态可表示为:
这一单向递归形式决定了模型只能利用过去的上下文信息,无法直接建模未来信息。
在此基础上,BiLSTM通过引入双向递归结构,将原有的单向状态扩展为两个方向的状态表示:
其中,依赖历史信息;
依赖未来信息。二者参数相互独立,分别进行建模。在每个时间步,BiLSTM通过对两个方向的隐藏状态进行融合,得到最终表示:
该结构的本质在于将原有的单一信息流扩展为双向信息流,使模型在当前时刻能够同时利用前后文信息,从而获得更加完整的上下文表示。
从信息建模角度来看,LSTM实现了在时间维度上的稳定信息传递,而BiLSTM在此基础上进一步扩展了信息的来源范围。
从梯度传播角度来看,BiLSTM仍采用与LSTM相同的BPTT,但由于存在两个独立的递归路径,梯度会分别沿正向与反向两个时间方向传播,并最终汇聚到融合层进行参数更新。这种结构并未改变单个LSTM单元的梯度传播形式,但通过双路径建模增强了模型对序列依赖关系的表达能力。
综上,BiLSTM的数学本质可以理解为在LSTM稳定信息传递机制的基础上,通过引入双向递归结构与状态融合操作,实现对序列上下文信息的全面建模,从而进一步提升模型的表示能力。
7 应用与优缺点
在输入输出结构形式上,BiLSTM与前述LSTM、RNN类似,同样适用于one-to-many、many-to-one以及many-to-many等多种序列建模任务形式。
不同之处在于,BiLSTM通过同时建模正向与反向序列信息,使其在需要利用上下文信息的任务中表现更加优越。尤其是在序列标注或句子理解任务中,当前词的语义往往依赖其前后文信息,BiLSTM能够同时融合历史与未来信息,从而获得更加完整的上下文表示,进一步提升模型性能。
| 优点 | 双向上下文建模能力强 | 同时利用前向与后向信息 |
| 语义表达更充分 | 擅长序列标注与句子理解任务 | |
| 适用于上下文依赖强的任务 | 词性标注等任务表现优于单向LSTM | |
| 缺点 |
计算开销进一步增加 | 相较LSTM需同时计算两个方向 |
| 实时性较差 | 反向计算依赖完整序列 | |
| 无法建模全局依赖 | 依赖递归结构,难以直接捕捉任意位置关系 |
尽管BiLSTM在LSTM的基础上进一步提升了序列建模能力,通过引入双向结构有效融合前后文信息,使模型在上下文理解任务中表现更加优越,但其在实际应用中仍存在一定局限性,这些问题也推动了后续模型的不断发展。
-
计算复杂度较高
- BiLSTM需要同时构建正向与反向两个LSTM网络,其参数规模与计算量相比单向LSTM进一步增加。在长序列或大规模数据场景下,训练与推理的时间成本较高
- 提出门控循环单元GRU等轻量化结构,在减少参数数量的同时保持较好的性能
-
实时性与在线处理能力受限
- 由于反向LSTM需要依赖完整序列,BiLSTM难以应用于需要实时响应的场景
- 采用单向模型或引入局部上下文建模策略,以兼顾实时性与性能
- 仍难建模全局依赖关系
- BiLSTM能够信息传递仍依赖递归结构,难以直接捕捉序列中任意位置之间的全局关系。在极长序列中,信息传播仍可能受到路径长度的限制
- 引入注意力机制,进一步发展出Transformer模型,通过全局建模方式实现任意位置之间的信息交互
8 核心代码
使用PyTorch架构简单展示BiLSTM的模型代码如下:
class SimpleBiLSTM(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
# 双向LSTM:forward + backward 两个方向
self.bilstm = nn.LSTM(
input_size,
hidden_size,
batch_first=True,
bidirectional=True #启用双向
)
# 双向 hidden_size 变为 2倍
self.fc = nn.Linear(hidden_size * 2, output_size)
def forward(self, x):
"""
x: (batch_size, seq_len, input_size)
"""
# BiLSTM前向传播
out, (h_n, c_n) = self.bilstm(x)
"""
out: 每个时间步的融合隐藏状态
(batch_size, seq_len, hidden_size * 2)
h_n: 最终隐藏状态
(num_directions, batch_size, hidden_size)
num_directions = 2
"""
# 取最后时间步的输出 双向融合结果
out = out[:, -1, :]
"""
对应数学表达:h_t = [h_t_forward ; h_t_backward]
"""
# 输出层
out = self.fc(out)
return out
可以看出,BiLSTM通过双向结构实现前后文信息融合,对应前文所述的双向上下文建模。
三种模型的演进关系与核心差异总结
我们分别从设计启发、结构设计、算法细节、数学表达与应用特点等方面,对RNN、LSTM和BiLSTM进行了系统梳理。三者并不是彼此孤立的模型,而是沿着“序列建模能力不断增强”的方向逐步演进:
- RNN首先引入循环结构,使神经网络具备处理序列数据的能力;
- LSTM进一步通过门控机制与细胞状态缓解长期依赖与梯度消失问题;
- BiLSTM则在LSTM基础上引入双向信息流,提升了对上下文信息的建模能力。

三者之间的联系与差异总结如下表:
| 比较维度 | RNN | LSTM | BiLSTM |
|---|---|---|---|
| 核心思想 | 通过循环结构在时间维度上传递隐藏状态 | RNN基础上引入细胞状态与门控机制 | LSTM基础上引入正向与反向两个LSTM |
| 主要解决的问题 | 序列数据建模 | 长期依赖与梯度消失 | 单向LSTM上下文信息不完整 |
| 状态结构 | 单一隐藏状态 |
隐藏状态 |
正向状态与反向状态进行融合 |
| 信息流方向 | 单向 | 单向 | 双向 |
| 长期依赖建模能力 | 较弱 | 较强 | 更强 |
| 梯度传播稳定性 | 容易出现梯度消失/爆炸 | 有效缓解梯度消失 | 与LSTM相近 |
| 上下文建模能力 | 只能利用历史信息 | 只能利用历史信息,但保留能力更强 | 同时利用前文与后文信息 |
| 结构复杂度 | 较低 | 中等 | 较高 |
| 计算开销 | 较小 | 较大 | 更大 |
| 并行能力 | 较弱 | 较弱 | 较弱 |
| 实时性 | 较好 | 较好 | 较差,需依赖完整序列 |
| 典型优势 | 易于理解和实现 | 适合长序列 | 适合上下文依赖强的任务 |
| 典型局限 | 难以捕捉长期依赖 | 无法直接利用未来信息 | 难以建模全局依赖 |
| 典型任务 | 基础序列建模、时间序列预测 | 长文本分类、序列预测 | 命名实体识别、词性标注 |
RNN、LSTM与BiLSTM体现了循环神经网络从“能够处理序列”到“能够稳定记忆长期信息”,再到“能够同时利用双向上下文”的逐步演进过程:
- RNN奠定了序列建模的基本框架;
- LSTM增强了模型对长期依赖的建模能力;
- BiLSTM则进一步提升了上下文表达的完整性
三者共同构成了序列建模方法发展的重要脉络,也为后续注意力机制与Transformer等模型的发展提供了基础。
最小代码实现示例
为了更直观地展示RNN、LSTM与BiLSTM在同一任务中的使用方式,下面构造一个极简的情感分类案例。代码重点在于说明三种模型在实现与调用方式上的差异,而非进行严格的性能评测。
import torch
import torch.nn as nn
import torch.optim as optim
torch.manual_seed(42)
# =========================
# 1. 构造一个极简数据集
# =========================
samples = [
("i love this movie", 1),
("this film is great", 1),
("i really like it", 1),
("what a wonderful movie", 1),
("this movie is terrible", 0),
("i hate this film", 0),
("it is very bad", 0),
("what a boring movie", 0),
("this movie is not good", 0),
("the movie looked good but was terrible", 0),
("i thought it was nice but it was bad", 0),
("the ending was wonderful", 1),
]
# =========================
# 2. 文本预处理
# =========================
def tokenize(text):
return text.lower().split()
# 构造词表
vocab = {"<pad>": 0, "<unk>": 1}
for text, _ in samples:
for word in tokenize(text):
if word not in vocab:
vocab[word] = len(vocab)
# 文本转索引
def encode(text):
return [vocab.get(word, vocab["<unk>"]) for word in tokenize(text)]
# 补齐到相同长度
def pad_sequence(seq, max_len):
return seq + [vocab["<pad>"]] * (max_len - len(seq))
encoded_samples = [(encode(text), label) for text, label in samples]
max_len = max(len(seq) for seq, _ in encoded_samples)
X = torch.tensor([pad_sequence(seq, max_len) for seq, _ in encoded_samples], dtype=torch.long)
y = torch.tensor([label for _, label in encoded_samples], dtype=torch.long)
# =========================
# 3. 定义三个模型
# =========================
class RNNClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
self.rnn = nn.RNN(embed_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, num_classes)
def forward(self, x):
# x: (batch_size, seq_len)
x = self.embedding(x) # -> (batch_size, seq_len, embed_dim)
out, _ = self.rnn(x) # -> (batch_size, seq_len, hidden_dim)
out = out[:, -1, :] # 取最后一个时间步的隐藏状态
out = self.fc(out) # -> (batch_size, num_classes)
return out
class LSTMClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, num_classes)
def forward(self, x):
x = self.embedding(x) # -> (batch_size, seq_len, embed_dim)
out, (h, c) = self.lstm(x) # h为隐藏状态, c为细胞状态
out = out[:, -1, :] # 取最后一个时间步输出
out = self.fc(out)
return out
class BiLSTMClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
self.bilstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True, bidirectional=True)
self.fc = nn.Linear(hidden_dim * 2, num_classes)
def forward(self, x):
x = self.embedding(x) # -> (batch_size, seq_len, embed_dim)
out, _ = self.bilstm(x) # -> (batch_size, seq_len, hidden_dim * 2)
out = out[:, -1, :] # 取最后一个时间步的双向融合输出
out = self.fc(out)
return out
# =========================
# 4. 定义训练函数
# =========================
def train_model(model, X, y, epochs=100):
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
for epoch in range(epochs):
model.train()
optimizer.zero_grad()
outputs = model(X)
loss = criterion(outputs, y)
loss.backward()
optimizer.step()
return model
# =========================
# 5. 定义预测函数
# =========================
def predict(model, text):
model.eval()
seq = encode(text)
seq = pad_sequence(seq, max_len)
x_input = torch.tensor([seq], dtype=torch.long)
with torch.no_grad():
logits = model(x_input)
pred = torch.argmax(logits, dim=1).item()
label = "positive" if pred == 1 else "negative"
return logits, label
# =========================
# 6. 实例化并训练三个模型
# =========================
vocab_size = len(vocab)
embed_dim = 16
hidden_dim = 16
num_classes = 2
rnn_model = RNNClassifier(vocab_size, embed_dim, hidden_dim, num_classes)
lstm_model = LSTMClassifier(vocab_size, embed_dim, hidden_dim, num_classes)
bilstm_model = BiLSTMClassifier(vocab_size, embed_dim, hidden_dim, num_classes)
rnn_model = train_model(rnn_model, X, y, epochs=100)
lstm_model = train_model(lstm_model, X, y, epochs=100)
bilstm_model = train_model(bilstm_model, X, y, epochs=100)
# =========================
# 7. 输入案例并输出结果
# =========================
test_text = "this movie is terrible"
rnn_logits, rnn_result = predict(rnn_model, test_text)
lstm_logits, lstm_result = predict(lstm_model, test_text)
bilstm_logits, bilstm_result = predict(bilstm_model, test_text)
print("输入句子:", test_text)
print("RNN 输出 logits:", rnn_logits)
print("RNN 预测结果:", rnn_result)
print("LSTM 输出 logits:", lstm_logits)
print("LSTM 预测结果:", lstm_result)
print("BiLSTM 输出 logits:", bilstm_logits)
print("BiLSTM 预测结果:", bilstm_result)
从实现过程可以看到,三种模型在整体调用流程上较为相似,核心递归单元逐步从RNN扩展为LSTM,再到BiLSTM,对应了前文所分析的序列建模能力从单向递归到长期记忆再到双向上下文融合的演进过程。

理解与思考
在学习RNN、LSTM和BiLSTM的过程中,一开始更多是在跟着公式走,但在实际整理这些内容的时候,逐渐发现,仅仅记住这些公式其实并不能真正理解模型在做什么。
在理解RNN时,一个比较明显的感受是,它的结构看起来很简单,但真正理解的关键是信息的记忆机制。
到了LSTM部分,最开始会觉得结构图突然变复杂了。但在反复对照公式和结构图之后,会发现它其实是在做一件很直观的事情,就是有选择地记住信息。尤其是细胞状态这一部分,更像是在专门为解决“记不住”的问题而设计的。
再到BiLSTM的时候,理解上反而相对顺畅一些,因为它的改进不是在结构内部,而是在信息流方向上做了扩展。可以理解为,不只是看前面的信息,还把后面的信息也考虑进来,这一点在处理句子时确实很直观。
在整理这几个模型的过程中,我还有两点比较直观的体会:
第一点,就像前面分析的演进过程一样,虽然现在很多任务中Transformer模型已经成为主流,但RNN及其变体仍然是理解序列建模的一个很好切入点。一方面,这些模型结构相对清晰;另一方面,在一些简单任务或者资源受限的场景中,这类模型依然具有实际价值。
进一步想,这种现象其实不只存在于序列模型中。在很多深度学习模型的发展过程中,后续模型往往是在前一代模型基础上的改进,而不是完全替代。例如在自然语言处理中,从n-gram模型到神经网络语言模型,简单模型仍然有其解释性和参考价值。因此,这些较早的模型虽然在性能上可能不占优势,但在理解问题本质和构建模型思路方面,仍然具有不可替代的作用。
第二点是关于模型创新方式的理解。在学习BiLSTM时,我一开始会觉得它是一个更复杂的模型,但后来发现,它的核心改进其实并不在于结构本身有多复杂,而只是对信息流方向进行了扩展。从这个角度来看,很多看起来高级的模型,其实都是围绕某一个具体问题做出比较直观的改动。
这让我逐渐意识到,科研中的模型创新并不一定都是完全从零开始的复杂设计,很多时候是在已有模型基础上,针对某个不足做出合理而直观的调整。像BiLSTM这样,通过引入反向信息来弥补单向建模的不足,就是一个很典型的例子。这种思路相比单纯记忆模型结构,更值得在学习过程中去理解和体会。
参考资料
一、学术论文
[1] Werbos, P. (1990). Backpropagation Through Time: What It Does and How to Do It
https://ieeexplore.ieee.org/document/58337
[2] Hochreiter, S., & Schmidhuber, J. (1997). Long Short-Term Memory
https://www.bioinf.jku.at/publications/older/2604.pdf
[3] Schuster, M., & Paliwal, K. (1997). Bidirectional Recurrent Neural Networks
https://ieeexplore.ieee.org/document/650093
二、其他资料
[4] 刘建平Pinard. 循环神经网络RNN模型与前向反向传播算法循环神经网络(RNN)模型与前向反向传播算法 - 刘建平Pinard - 博客园 (cnblogs.com)
[5] 刘建平Pinard. LSTM模型与前向反向传播算法LSTM模型与前向反向传播算法 - 刘建平Pinard - 博客园 (cnblogs.com)
[6] 循环神经网络RNN完全解析:从基础理论到PyTorch实战(25 封私信 / 2 条消息) 循环神经网络RNN完全解析:从基础理论到PyTorch实战 - 知乎 (zhihu.com)
三、推荐教程
李宏毅《Recurrent Neural Network(Part I)(Part II)》
ML Lecture 21-1: Recurrent Neural Network (Part I) (youtube.com)
ML Lecture 21-2: Recurrent Neural Network (Part II) (youtube.com)
边学边整理的,可能会有笔误,欢迎指正
李宏毅老师的扩散模型讲的也很好,推荐
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)