从 RNN 到 LSTM 再到 BiLSTM:一文搞懂 NLP 核心序列模型原理

0 阅读10分钟

从 RNN 到 LSTM 再到 BiLSTM:一文搞懂 NLP 核心序列模型原理

NLP-AHU-166

引言

在自然语言处理(NLP)的世界里,我们处理的几乎都是序列数据—— 一句话是由词按顺序组成的,一段文本是由句子串联而成的,就连语音信号也是按时间顺序排列的数值序列。普通的神经网络(比如全连接网络)有个致命问题:它完全无视数据的顺序关系,把输入当成独立的个体去处理,根本没法理解 “词 A 在词 B 前面” 这种序列语义。

RNN、LSTM、BiLSTM就是为解决序列数据处理而生的三大核心模型,也是 NLP 入门的必学基础。接下来我会从 “为什么要设计它们”“具体怎么设计”“数学公式怎么推导” 三个维度,一步步拆解这三个模型的原理,尽量用初学者能看懂的通俗语言讲清楚。

一、RNN:循环神经网络,序列处理的 “初代尝试”

1. 设计动机

普通神经网络的输入输出都是固定长度的,比如输入一张 28×28 的图片,输出就是 10 个分类概率。但序列数据的长度是不固定的 —— 一句话可能有 5 个词,也可能有 20 个词,这就需要模型能 “记住” 之前的信息,同时适配不同长度的输入。

RNN 的核心设计思路就是:让隐藏层保留 “记忆” ,把上一个时刻的隐藏状态传递到下一个时刻,这样模型就能结合 “当前输入” 和 “历史记忆” 处理序列数据。简单说,RNN 就像一个 “边走边记笔记” 的学习者,遇到新内容会结合之前的笔记理解,而不是从零开始。

2. 核心结构与数学表达

RNN 的核心是循环连接,它的网络结构可以拆解为三个部分:输入层、隐藏层、输出层。

  • 输入层:处理序列中的每个元素(比如一句话里的每个词,会先转换成词向量xt​)。
  • 隐藏层:这是 RNN 的核心,负责存储 “历史记忆”,并更新记忆状态。
  • 输出层:根据当前时刻的隐藏状态,输出当前的预测结果(比如词性标注、下一个词的概率)。
关键公式(隐藏层更新)

假设序列中第t个时刻的输入为xt​,第t−1个时刻的隐藏状态为ht−1​(也就是历史记忆),第t个时刻的隐藏状态ht​(更新后的记忆)计算公式为:

image.png

其中各参数的含义:

  • Wxh​:输入层到隐藏层的权重矩阵,负责将当前输入xt​映射到隐藏层维度
  • Whh​:上一时刻隐藏层到当前时刻隐藏层的权重矩阵,负责传递历史记忆;
  • bh​:隐藏层的偏置项,增加模型的拟合能力;
  • tanh:激活函数,将隐藏层的输出映射到[−1,1]区间,同时引入非线性,让模型能学习复杂的序列规律。

输出层的公式也很简单,以分类任务为例(比如情感分析):

image.png

  • Who​:隐藏层到输出层的权重矩阵;
  • bo​:输出层的偏置项;
  • softmax:将输出值转换为概率分布,让每个类别的概率之和为 1。

3. 致命缺点:梯度消失 / 爆炸(长期依赖问题)

RNN 看似解决了序列处理的问题,但实际用起来却有个大漏洞 ——长期依赖问题

简单说,当序列长度很长时(比如一句话有 100 个词),RNN 在反向传播计算梯度时,梯度会随着时间步的增加呈指数级衰减(梯度消失)或爆炸(梯度爆炸)。这会导致模型 “记不住” 很久之前的信息,比如一句话 “我童年住在北京,后来去了上海,现在还记得____的胡同”,模型很难准确填出 “北京”,因为它把前面的记忆 “丢了”。

这就是为什么需要 LSTM 来优化 RNN—— 专门解决长期依赖问题。

二、LSTM:长短期记忆网络,解决长期依赖的 “升级版 RNN”

1. 设计动机

LSTM 的全称是 Long Short-Term Memory,它在 RNN 的基础上做了核心改进:引入 “门控机制” 和 “细胞状态” ,精准控制信息的 “遗忘”“输入” 和 “输出”,让模型能选择性地保留重要信息,丢弃无用信息,从而解决长期依赖问题。

如果说 RNN 是一个 “记满所有笔记的笔记本”,那 LSTM 就是一个 “智能笔记本”—— 会主动扔掉没用的笔记,只保留关键内容,还能随时补充新笔记,最后只输出需要的部分。

2. 核心结构:细胞状态 + 三大门控

LSTM 的核心是细胞状态(Cell State) ,可以理解为模型的 “长期记忆通道”,信息在这个通道里流动时,只会经过少量的线性变换,避免梯度消失。

而控制信息流动的关键是三大门控(门控本质上是一个带 sigmoid 激活的全连接层,输出 0~1 之间的数值,0 表示 “完全丢弃信息”,1 表示 “完全保留信息”),分别是:

  1. 遗忘门(Forget Gate) :决定丢弃细胞状态中哪些旧信息;
  2. 输入门(Input Gate) :决定将哪些新信息存入细胞状态;
  3. 输出门(Output Gate) :决定从细胞状态中输出哪些信息作为当前时刻的隐藏状态。

3. 详细数学公式

接下来逐个拆解 LSTM 的公式,结合通俗解释会更好理解。

第一步:计算遗忘门

遗忘门的作用是 “筛选历史记忆”,它会结合当前输入xt​和上一时刻隐藏状态ht−1​,输出一个遗忘概率向量ft​(每个元素都在 0~1 之间)。

image.png

  • σ:sigmoid 激活函数,输出 0~1 的概率;
  • Wxf​、Whf​:分别是输入、上一时刻隐藏状态到遗忘门的权重;
  • bf​:遗忘门的偏置。
第二步:计算输入门

输入门分两步:先决定 “哪些新信息要存入”,再生成 “新的候选信息”。

① 生成输入门概率it​: image.png

② 生成候选新信息Ct​(用 tanh 激活输出 - 11 之间的数值):image.png

第三步:更新细胞状态(重点)

用 “遗忘的旧信息”+“新增的新信息”,更新细胞状态Ct​:

image.png

  • 这里是逐元素相乘:ft​⋅Ct−1​ 表示按遗忘概率丢弃旧记忆,it​⋅C~t​ 表示按输入概率添加新记忆,两者相加就是更新后的长期记忆。
第四步:计算输出门

先决定 “输出哪些记忆”,再结合细胞状态生成当前时刻的隐藏状态ht​:

① 生成输出门概率

image.png

② 生成当前隐藏状态ht​:

image.png

  • ot​⋅tanh(Ct​) 表示按输出概率筛选细胞状态中的信息,只输出需要的部分作为当前时刻的隐藏状态,传递给下一个时刻。

4. 为什么 LSTM 能解决长期依赖?

LSTM 的细胞状态Ct​是线性流动的,只有少量的逐元素相乘和相加操作,反向传播时梯度不会轻易消失 / 爆炸。而三大门控让模型能精准控制信息的流动,比如对重要的历史信息(如句子中的主语),门控会输出接近 1 的概率,保留该信息;对无关的信息(如语气词),输出接近 0 的概率,丢弃该信息。

这样一来,即使序列长度很长,LSTM 也能牢牢记住关键信息,完美解决了 RNN 的长期依赖问题。

三、BiLSTM:双向长短期记忆网络,兼顾上下文的 “升级版 LSTM”

1. 设计动机

LSTM 虽然解决了长期依赖,但它是单向的—— 只能从左到右处理序列,只能利用 “前文信息”,看不到 “后文信息”。

但在很多 NLP 任务中,上下文信息对理解语义至关重要。比如一句话 “苹果公司发布了新款手机,它的屏幕很大”,要理解 “它” 指的是 “新款手机”,不仅需要看前面的 “苹果公司发布了新款手机”,还需要看后面的 “屏幕很大”。

BiLSTM 的设计思路就是:拼接两个单向 LSTM,一个从左到右处理序列(前向 LSTM),一个从右到左处理序列(后向 LSTM),这样每个时刻的输出都能结合 “前文 + 后文” 的信息,让语义理解更全面。

2. 核心结构:前向 + 后向 LSTM 拼接

BiLSTM 的结构可以拆解为两部分:

  1. 前向 LSTM:按t=1,2,...,T的顺序处理序列,得到前向隐藏状态ht→​;
  2. 后向 LSTM:按t=T,T−1,...,1的逆序处理序列,得到后向隐藏状态ht←​;
  3. 拼接层:将每个时刻的ht→​和ht←​拼接起来,得到 BiLSTM 的最终输出ht​。

3. 数学表达

前向 LSTM 的隐藏状态计算

和普通 LSTM 的计算逻辑一致,只是顺序是从左到右:

image.png

后向 LSTM 的隐藏状态计算

后向 LSTM 的计算逻辑和前向一致,只是顺序是从右到左,上一时刻的隐藏状态是ht+1←​:

image.png

BiLSTM 的最终输出

将前向和后向的隐藏状态拼接,得到每个时刻的最终输出:ht​=[ht→​;ht←​]这里的;表示向量拼接,如果前向隐藏状态的维度是d,后向也是d,那拼接后的维度就是2d。

4. 适用场景

BiLSTM 特别适合需要 “上下文联动” 的 NLP 任务,比如:

  • 词性标注:每个词的词性不仅和前面的词有关,也和后面的词有关;
  • 命名实体识别:识别一句话中的 “人名”“地名”“机构名”,需要结合上下文判断;
  • 文本分类:尤其是短文本,上下文信息对语义判断影响很大。

四、RNN、LSTM、BiLSTM 核心对比总结

RNN 是最基础的循环模型,结构简单,依靠循环连接处理序列,但存在严重的长期依赖问题,只能用于短序列和基础演示,不适合实际复杂任务。

LSTM 在 RNN 基础上加入细胞状态和三大门控,专门解决梯度消失和长期依赖问题,能够处理长序列,是后续很多序列模型的基础。

BiLSTM 则是在 LSTM 基础上实现双向结构,同时利用前文和后文信息,语义理解更全面,在命名实体识别、词性标注、文本分类等 NLP 任务中表现更好,是实际工程中更常用的结构。

最后:代码实现(PyTorch 版)

为了让大家更直观地理解 BiLSTM 的实际应用,我写了一个简易的基于 PyTorch 的 BiLSTM 文本分类示例(以情感分析为例),代码里加了详细注释,大家能直接看懂。

python

运行

import torch
import torch.nn as nn
import torch.optim as optim
from torchtext.vocab import build_vocab_from_iterator
from torchtext.data.utils import get_tokenizer

# 1. 定义超参数
VOCAB_SIZE = 10000  # 词汇表大小
EMBED_DIM = 128     # 词嵌入维度
HIDDEN_DIM = 256    # LSTM隐藏层维度
NUM_CLASSES = 2     # 情感分析二分类(正面/负面)
BATCH_SIZE = 32
EPOCHS = 5

# 2. 定义分词器和词汇表
tokenizer = get_tokenizer("basic_english")  # 英文基础分词器
# 这里可以替换成你的数据集,示例用简单的文本
texts = [
    "I love this movie, it's amazing!",
    "This film is terrible, I want to leave.",
    "The acting is great, highly recommend it.",
    "The plot is boring, waste of time."
]
labels = [1, 0, 1, 0]  # 1=正面,0=负面

# 构建词汇表
def yield_tokens():
    for text in texts:
        yield tokenizer(text)
vocab = build_vocab_from_iterator(yield_tokens(), max_tokens=VOCAB_SIZE, specials=["<unk>"])
vocab.set_default_index(vocab["<unk>"])  # 未知词映射为<unk>

# 3. 定义BiLSTM模型
class BiLSTMClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
        super(BiLSTMClassifier, self).__init__()
        # 词嵌入层
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        # BiLSTM层:bidirectional=True表示双向,batch_first=True表示输入格式为[batch, seq_len]
        self.bilstm = nn.LSTM(
            input_size=embed_dim,
            hidden_size=hidden_dim,
            num_layers=1,
            bidirectional=True,
            batch_first=True
        )
        # 分类层:因为双向,所以输入维度是hidden_dim*2
        self.fc = nn.Linear(hidden_dim * 2, num_classes)

    def forward(self, x):
        # x: [batch_size, seq_len]
        embed = self.embedding(x)  # [batch_size, seq_len, embed_dim]
        # bilstm输出:output[batch_size, seq_len, hidden_dim*2](每个时刻的输出),_是隐藏状态和细胞状态
        output, _ = self.bilstm(embed)