查看全课大纲(第 41 / 69 节)
- 1.NLP 的研究领域、应用与学习路径
- 2.自然语言的基本术语
- 3.信息论基础:信息熵与交叉熵
- 4.语言的进化与语义变迁
- 5.主观概率、客观概率与条件概率
- 6.贝叶斯原理与推理
- 7.随机变量:二项式、期望方差与联合概率
- 8.常见概率分布
- 9.辛普森悖论与贝叶斯解题实例
- 10.语言模型:语言概率与词袋模型
- 11.n-gram 语言模型(二元 / 三元 / N-Gram)
- 12.语言模型评价:交叉熵、困惑度与插值
- 13.朴素贝叶斯文本分类:垃圾邮件
- 14.拼写检查
- 15.中文分词:构词法、最大匹配、最优路径与 Jieba
- 16.词表示问题与语义相似度
- 17.TF-IDF 权重处理
- 18.One-Hot 表达与词表示的局限
- 19.Word2Vec 核心思想
- 20.Word2Vec 训练过程
- 21.Skip-gram 模型详解
- 22.词性标注任务与标注方法(规则 + N-Gram)
- 23.EM 算法与混合模型
- 24.高斯混合模型 GMM
- 25.从混合模型到隐马尔可夫模型
- 26.隐马尔可夫模型(HMM)推导与维特比解码
- 27.线性链条件随机场 CRF
- 28.贝叶斯网络与图模型
- 29.隐马尔可夫链与 HMM 推导
- 30.PLSA 主题模型
- 31.LSI 与 SVD 向量表达
- 32.LDA 与狄利克雷分布
- 33.主题模型实验复现(困惑度 / 分类 / 相似度 / 延展)
- 34.上下文无关文法(CFG)与句法分析
- 35.概率上下文无关文法 PCFG
- 36.统计机器翻译:词对齐与翻译模型
- 37.统计机器翻译中的对齐模型与参数估计
- 38.从统计机器翻译到神经机器翻译
- 39.神经元与反向传播速览(指向深度学习课)
- 40.CNN 原理及其在 NLP 中的应用
- 41.循环神经网络的基本原理与 Elman / Jordan 网络
- 42.LSTM:长短期记忆网络的核心思想与分步实现
- 43.Encoder-Decoder 框架
- 44.Seq2Seq 与早期注意力机制
- 45.注意力机制的背景与动机
- 46.注意力模型实现原理与匹配函数
- 47.记忆网络:组成与计算
- 48.注意力机制的应用与历史延展
- 49.Transformer 结构:Encoder-Decoder 栈与 Self-Attention
- 50.Multi-Head Attention、FFNN 与位置编码
- 51.层归一化、损失函数与 Universal Transformer 延展
- 52.预训练范式:从 ELMo 到 BERT
- 53.BERT 与掩码语言模型
- 54.BERT 下游任务微调与 GLUE 口径
- 55.GPT 与自回归语言建模
- 56.GPT-1 到 GPT-4 的演进
- 57.指令微调与 RLHF
- 58.分词器:BPE、WordPiece 与中文词表
- 59.上下文窗口与长文本处理
- 60.能力边界、成本与适用场景
- 61.文本分类实战:从朴素贝叶斯到逻辑回归
- 62.命名实体识别实战
- 63.文本生成与摘要实战
- 64.语义检索与向量库实战
- 65.RAG 知识库问答
- 66.NLP 评估指标:BLEU / ROUGE / BERTScore 与人工评估
- 67.幻觉与事实性问题
- 68.文本与知识结合(知识图谱压缩)
- 69.跨媒体信息检索与未来展望
循环神经网络的基本原理与 Elman / Jordan 网络
小象实战讲义 · 自然语言处理
在自然语言处理中,我们处理的文本数据本质上是序列数据——一个词接着一个词,一句话接着一句话。如何让模型“记住”并利用序列中之前的信息,是建模的关键挑战。本节我们将从经典的时序模型(隐马尔可夫模型)出发,引出其局限性,并系统性地介绍循环神经网络(RNN)的核心思想、数学原理及其两种经典变体(Elman Network 与 Jordan Network)。学完本节,你将能够理解 RNN 如何通过“循环”结构捕捉序列依赖,并为其后续学习 LSTM、GRU 等更复杂的序列模型打下坚实基础。
💡 核心导读
- 从 HMM 到 RNN:理解隐马尔可夫模型(HMM)中状态转移矩阵的局限性,以及如何将其泛化为一个可学习的函数,从而引出 RNN 的核心动机。
- RNN 的循环结构:掌握 RNN 的基本架构,理解其如何通过共享的权重矩阵(U, V, W)和隐藏状态()在时间维度上展开,实现信息传递。
- RNN 的前向传播公式:学会推导并写出 RNN 在任意时刻 t 的隐藏状态 和输出 的计算公式。
- Elman vs. Jordan 网络:了解 RNN 的两种经典变体,区分它们将何种信息(隐藏状态 vs. 输出)反馈给下一时刻,并理解其设计思想与潜在影响。
从时序模型到循环神经网络
在之前的课程中,我们学习过用于词性标注(POS tagging)等任务的隐马尔可夫模型(HMM)。HMM 的核心思想是假设存在一个不能被直接观察到的隐含状态序列(例如,词性标签),而我们可以观察到的是隐含状态“释放”出来的观测序列(例如,单词)。
HMM 通过两个关键概率来建模序列:
- 状态转移概率:定义了从上一时刻隐含状态 转移到当前时刻隐含状态 的概率,通常表示为一个矩阵 ,其中 。
- 发射概率:定义了从当前隐含状态 生成当前观测 的概率,通常表示为一个矩阵 ,其中 。
HMM 的局限性在于,其状态转移关系被固定为一个查表式的概率矩阵。这意味着:
- 状态 只严格依赖于上一时刻的状态 (一阶马尔可夫性)。
- 转移概率是一张预先统计、固定的查表,状态只能取离散标签,依赖形式被限制在一阶马尔可夫框架内;这张概率表本身并不预设“线性关系”,但也无法表达由连续历史经复杂非线性函数得到的依赖。
然而,在真实的序列中,依赖关系可能更复杂:
- 当前状态可能依赖于之前多个时刻的状态(例如,前两个词共同决定当前词的词性)。
- 依赖的“历史长度”本身可能是可变的(例如,在句子开头依赖短,在句子中间依赖长)。
- 状态之间的关系可能是一个复杂的非线性函数。
为了突破这些限制,一个自然的想法是:将状态转移矩阵 泛化为一个可学习的函数 。 我们可以将下一时刻的状态 表示为之前所有状态 的一个函数:
一个最简单的假设是线性函数: 其中权重 的大小决定了 与历史状态 的关联程度。如果 为 0,则表示 与 无关。这样,模型可以通过学习权重 来自动决定与哪些历史状态相关,以及相关的程度。
显然,线性函数的表达能力依然有限。如果我们希望 是任意复杂的非线性函数,那么一个强大的工具就是神经网络。用神经网络来替代固定的转移概率矩阵,建模隐藏状态之间的复杂、非线性、可变长度的依赖关系,这就是循环神经网络(RNN) 的核心思想。
循环神经网络(RNN)的基本原理
结构展开与共享权重
我们先从一个普通的前馈神经网络(FNN)说起。假设我们有一个用于处理序列数据的 FNN,在时刻 ,它接收输入 ,并产生输出 。对于序列中的每个时间步,我们都使用同一个神经网络进行计算。这个网络本身是静态的,不同时刻的输入 之间没有信息流动。
为了让网络具备“记忆”,RNN 的关键创新在于:将上一时刻隐藏层的输出,作为额外输入,连接到当前时刻的隐藏层。
下面我们通过结构图来理解。首先,我们看一个在单个时间步上展开的 RNN 单元:
(输出)
^
| U (权重矩阵)
|
[隐藏层 h_t] ---W---> [隐藏层 h_{t+1}] (循环连接,信息传递给下一时刻)
^
| V (权重矩阵)
|
[输入 x_t]更常见的是将 RNN 沿着时间轴展开,这清晰地展示了信息的流动:
时间步 t=1: 时间步 t=2: 时间步 t=3:
x_1 x_2 x_3
| V | V | V
v v v
h_1 --W--> h_2 --W--> h_3 --W--> ...
| U | U | U
v v v
y_1 y_2 y_3关键点:
- 共享权重:在所有时间步上,权重矩阵 (输入到隐藏层)、(隐藏层到隐藏层)、(隐藏层到输出层)是共享的。这意味着我们只需要学习一套参数,就可以处理任意长度的序列。
- 隐藏状态: 是 RNN 在时刻 的隐藏状态(或称记忆状态)。它封装了到当前时刻为止,网络所“看到”的序列历史信息。
- 循环连接:箭头 代表了循环连接,它将 的信息传递给 ,从而实现了跨时间步的信息传递。
前向传播公式
基于上面的结构,我们可以写出 RNN 在任意时刻 的前向传播公式。
首先,计算当前时刻的隐藏状态 。它由两部分决定:
- 当前的外部输入 经过权重 的变换。
- 上一时刻的隐藏状态 经过权重 的变换。
这两部分相加后,通常经过一个非线性激活函数(如 或 ): 其中:
- 是时刻 的输入向量。
- 是上一时刻的隐藏状态向量,初始状态 通常初始化为零向量。
- 是输入层到隐藏层的权重矩阵。
- 是隐藏层到隐藏层(循环)的权重矩阵。
- 是隐藏层的偏置向量。
- 是隐藏层的激活函数(例如 )。
然后,基于当前隐藏状态 计算输出 : 其中:
- 是隐藏层到输出层的权重矩阵。
- 是输出层的偏置向量。
- 是输出层的激活函数。对于分类任务,通常是 函数,将输出转换为概率分布。
公式递归性:注意 的计算依赖于 ,而 又依赖于 ,如此递归。这意味着 实际上间接地包含了从序列开始 的所有历史信息,尽管其容量有限。
下面我们用一个小规模的数值示例来演示单步计算。为了确保结果确定且可复现,我们使用固定的随机种子和小矩阵。
import numpy as np
# 固定随机种子,确保结果可复现
np.random.seed(42)
# 定义模型参数维度 (小规模示例)
input_dim = 3
hidden_dim = 4
output_dim = 2
# 初始化权重矩阵和偏置 (使用小数值以便观察)
V = np.random.randn(hidden_dim, input_dim) * 0.1 # 输入到隐藏层
W = np.random.randn(hidden_dim, hidden_dim) * 0.1 # 循环权重
U = np.random.randn(output_dim, hidden_dim) * 0.1 # 隐藏层到输出层
b_h = np.zeros((hidden_dim, 1)) # 隐藏层偏置
b_o = np.zeros((output_dim, 1)) # 输出层偏置
# 定义激活函数
def tanh(x):
return np.tanh(x)
def softmax(x):
exp_x = np.exp(x - np.max(x)) # 数值稳定
return exp_x / exp_x.sum(axis=0, keepdims=True)
# 模拟两个时间步的数据
# 时间步 t=1
x1 = np.array([[0.5], [-0.2], [0.1]]) # 形状 (input_dim, 1)
h0 = np.zeros((hidden_dim, 1)) # 初始隐藏状态
# 计算 h1
z_h1 = V @ x1 + W @ h0 + b_h
h1 = tanh(z_h1)
# 计算 y1
z_o1 = U @ h1 + b_o
y1_probs = softmax(z_o1)
# 时间步 t=2
x2 = np.array([[-0.1], [0.3], [0.2]])
# 计算 h2 (依赖于 h1)
z_h2 = V @ x2 + W @ h1 + b_h
h2 = tanh(z_h2)
# 计算 y2
z_o2 = U @ h2 + b_o
y2_probs = softmax(z_o2)
print("时间步 t=1:")
print(f"隐藏状态 h1:\n{h1.round(4)}")
print(f"输出概率 y1:\n{y1_probs.round(4)}")
print("\n时间步 t=2:")
print(f"隐藏状态 h2:\n{h2.round(4)}")
print(f"输出概率 y2:\n{y2_probs.round(4)}")时间步 t=1:
隐藏状态 h1:
[[0.0341]
[0.0783]
[0.0588]
[0.0317]]
输出概率 y1:
[[0.5013]
[0.4987]]
时间步 t=2:
隐藏状态 h2:
[[-0.0223]
[-0.0377]
[-0.0031]
[-0.0352]]
输出概率 y2:
[[0.497]
[0.503]]从输出可以看到, 是基于 和 计算得到的,而 包含了 的信息。因此, 的输出概率间接受到了 的影响,体现了 RNN 的记忆能力。
Elman Network 与 Jordan Network
基本的 RNN 结构(如上所述)通常被称为 Simple RNN 或 Elman Network,以其提出者 Jeffrey Elman 命名。在这种结构中,反馈到下一时间步的是隐藏状态 。
还存在另一种变体,称为 Jordan Network,以其提出者 Michael I. Jordan 命名。在 Jordan Network 中,反馈到下一时间步的是网络输出 (或经过某种变换后的输出)。
我们可以用以下对比来理解两者的区别:
Elman Network (反馈隐藏状态): 信息流:
Jordan Network (反馈输出): 信息流:
Elman Network (反馈隐藏状态): Jordan Network (反馈输出):
h_{t-1} ---W---> h_t ---U---> y_t y_{t-1} ---W---> h_t ---U---> y_t
^ ^
| V | V
x_t x_t
反馈源:h_{t-1}(隐藏状态) 反馈源:y_{t-1}(上一时刻输出)图中权重分工: 是输入 到隐藏层 的权重, 是反馈项到隐藏层的权重, 是隐藏层 到输出 的权重;输入 不直接连到输出 ,必须先经过隐藏层。
两者的比较与思考:
- Elman Network:隐藏状态 是网络的“内部记忆”,它可能编码了更丰富、更抽象的序列历史信息。反馈 意味着记忆在隐藏层内部直接传递。
- Jordan Network:输出 是网络对外的“预测”。反馈 意味着下一时刻的隐藏状态会直接受到上一时刻预测结果的影响。
- 理论差异:Jordan Network 将输出层的信息(经过 矩阵变换后的信息)也纳入了循环路径。如果任务中上一时刻的预测结果对当前时刻的推理至关重要(例如,在文本生成中,已生成的词直接影响下一个词的选择),那么 Jordan Network 可能更有优势。
- 实践差异:在实际应用中,两者的性能差异通常并不显著。Elman Network 更为常见。有时,Jordan Network 可能因为反馈的是“更高级别”(输出层)且通常维度更低的信息,而在训练稳定性上略有不同。但没有免费的午餐定理(No Free Lunch Theorem)在此同样适用,没有哪一种结构在所有任务上都绝对优于另一种。
RNN 的意义与局限性
RNN 的核心意义在于,它首次为神经网络提供了处理可变长度序列数据和建模长距离依赖(尽管能力有限)的统一框架。它成功地将参数共享的思想从空间(CNN)扩展到了时间维度,使得模型规模不随序列长度增长而线性增长,极大地提升了效率。
然而,基本的 Simple RNN(包括 Elman 和 Jordan 网络)在实践中面临一个严峻挑战:长程依赖建模困难。需要区分两个紧密相关但不等同的概念:长程依赖是“模型需要利用很早之前的信息”这一建模目标;梯度消失/爆炸(Vanishing/Exploding Gradient)则是 BPTT 训练时梯度在时间步上连乘导致的数值问题。在通过时间反向传播(BPTT)训练时,梯度需要在时间步上连续相乘。当序列很长时,梯度可能会指数级地消失(变得极小)或爆炸(变得极大),导致网络难以学习到长距离的依赖关系。例如,在句子“I grew up in France… I speak fluent French.”中,预测最后一个词“French”需要依赖很远之前的“France”这个词,基本的 RNN 很难做到这一点。
正是为了克服这一根本缺陷,更复杂的循环单元如长短期记忆网络(LSTM) 和门控循环单元(GRU) 被提出,它们通过引入“门”机制来有选择地保留和遗忘信息,从而显著改善了长程依赖的学习能力。这将是下一节的重点内容。
今天怎么做:如今,在需要显式序列建模且计算资源受限的场合,LSTM 和 GRU 仍然是可靠的选择。然而,对于大多数先进的 NLP 任务,基于 Transformer 的模型(如 BERT、GPT)凭借其全局注意力机制,已经成为了主流的架构,因为它们能更有效地并行化并捕获长距离依赖。RNN 及其变体的思想,特别是其“状态”和“循环”的概念,在理解序列模型发展史和某些特定动态系统建模中,依然具有重要价值。
📝 动手练一练
- 概念辨析:Elman Network 和 Jordan Network 在结构上的根本区别是什么?请分别画出它们在时间步 的简化计算图(包含 及循环连接)。
👉 点击查看参考答案
根本区别:反馈到当前时间步隐藏层 的信息来源不同。
- Elman Network:反馈的是上一时间步的隐藏状态 。
- Jordan Network:反馈的是上一时间步的输出 。
简化计算图:
Elman Network (时刻 t):
[h_{t-1}] -----------W----------> (+)
^
| (与 V*x_t 相加)
|
[x_t] --------V----------> (+)
|
v
[σ_h] ---> [h_t] ---U---> [σ_o] ---> [y_t]
Jordan Network (时刻 t):
[y_{t-1}] -----------W----------> (+)
^
| (与 V*x_t 相加)
|
[x_t] --------V----------> (+)
|
v
[σ_h] ---> [h_t] ---U---> [σ_o] ---> [y_t]注意:图中省略了偏置项 和 。
手算推导:给定一个超简单的 RNN 单元,其中:
- 激活函数 和 均为恒等函数 (即无非线性)。
- (2x2单位矩阵), , 。
- 所有偏置 均为 0。
- 初始隐藏状态 。
- 输入序列:, 。
请手动计算 , , , 。
👉 点击查看参考答案
根据公式 , 。
时间步 t=1:
时间步 t=2:
因此: , ,
本章小结
本节我们深入探讨了循环神经网络(RNN)的起源、基本原理和两种早期变体。
- 我们从隐马尔可夫模型(HMM) 的局限性出发,认识到将固定的状态转移矩阵泛化为一个可学习的函数(尤其是神经网络)的必要性,这直接引出了 RNN 的核心思想。
- RNN 通过引入循环连接和共享权重,使得一个神经网络能够处理任意长度的序列。其关键组件是隐藏状态 ,它作为网络的记忆,递归地融合了当前输入 和过去的历史信息 。
- 我们学习了 RNN 的前向传播公式: 和 。
- 我们区分了 Elman Network(反馈隐藏状态)和 Jordan Network(反馈网络输出)这两种结构,理解了它们设计上的差异以及在实际中性能相近的特点。
- 最后,我们指出了基本 RNN 存在的长程依赖问题,这为后续学习更强大的 LSTM 和 GRU 模型,乃至 Transformer 架构埋下了伏笔。
📋 行动清单
- 在白板或纸上推导并画出 RNN 在三个时间步上展开的完整结构图,标出所有权重矩阵(U, V, W)、输入(x)、隐藏状态(h)和输出(y)。
- 编写一个简单的 Python 函数
rnn_forward_step,实现单个 RNN 时间步的前向计算(给定 ,返回 ),并用小规模数据验证。 - 思考一个具体的 NLP 任务(例如情感分析),用文字描述输入序列(词向量)如何流经 RNN,以及最终的预测(如正面/负面)是如何从最后一个隐藏状态得到的。
—— 小象教研组