核心使命:解决「序列数据的时序依赖」问题
在 Word2Vec 部分我们聊过,Word2Vec 是"词袋思想",完全忽略词的顺序------"我 爱 吃 苹果"和"苹果 吃 爱 我"的 Word2Vec 向量组合没有区别,这显然不符合语言逻辑。
而现实中,序列数据(文本、语音、时间序列)的核心就是"顺序":
- 文本:"他打了我"和"我打了他",顺序变了,意思完全相反;
- 语音:先发音"我"再发"爱",和先"爱"再"我",是两个不同的表达。
RNN 就是为了处理这类带时序依赖的序列数据而生的------它能让模型"记住"前面的信息,用前面的内容影响后面的预测。
一、RNN 的基础网络结构
RNN 和普通前馈神经网络(如 Word2Vec 的三层结构)最大的区别是:隐藏层有"循环连接",即隐藏层的输出会"留一份",作为下一个时间步的输入。
我们从「核心组件」和「多时间步展开」两个视角讲清结构:
1. 核心组件(3个)
- 输入层:每个时间步的输入 ( x_t )(比如句子中第 ( t ) 个词的词向量,由 Word2Vec 或 One-Hot 转换而来);
- 隐藏层 :核心是隐藏状态 ( h_t ),是 RNN 实现"记忆"的关键;
- 输出层:每个时间步的输出 ( y_t )(比如预测的下一个词的概率分布)。
2. 多时间步展开图(直观理解)
若输入是长度为 ( T ) 的序列 ([x_1, x_2, ..., x_T]),RNN 结构可展开为:
x₁ → [输入层] → [隐藏层] → h₁ → [输出层] → y₁
↑ ↑
│ ↓
└────────────┘
x₂ → [输入层] → [隐藏层] → h₂ → [输出层] → y₂
↑ ↑
│ ↓
└────────────┘
...
x_T → [输入层] → [隐藏层] → h_T → [输出层] → y_T
关键细节:
- 所有时间步的隐藏层共享同一个权重矩阵 ( W_{hh} )(这是 RNN 参数少、泛化能力强的核心原因);
- 每个时间步的隐藏状态 ( h_t ),由「当前输入 ( x_t )」和「上一个时间步的隐藏状态 ( h_{t-1} )」共同决定。
二、时序依赖建模原理(RNN 怎么"记住"前面的信息?)
核心是隐藏状态 ( h_t ) 的计算公式(Markdown 公式格式),用通俗语言拆解:
1. 隐藏状态的计算逻辑
ht=tanh(Wxh⋅xt+Whh⋅ht−1+bh) h_t = \tanh\left(W_{xh} \cdot x_t + W_{hh} \cdot h_{t-1} + b_h\right) ht=tanh(Wxh⋅xt+Whh⋅ht−1+bh)
- ( W_{xh} ):输入层到隐藏层的权重矩阵;
- ( W_{hh} ):上一个隐藏状态到当前隐藏状态的权重矩阵(循环连接的核心);
- ( b_h ):隐藏层的偏置项;
- ( \tanh ):激活函数,作用是把 ( h_t ) 的值限制在 ([-1,1]) 之间,防止数值过大。
人话翻译:
RNN 处理第 ( t ) 个词时,会做两件事:
- 分析当前词 ( x_t ) 的信息(通过 ( W_{xh} \cdot x_t ) 转换);
- 调取上一个时间步"记住"的信息 ( h_{t-1} )(通过 ( W_{hh} \cdot h_{t-1} ) 转换);
- 把两者加起来,再通过 ( \tanh ) 函数整合,得到新的隐藏状态 ( h_t )------这个 ( h_t ) 包含了「从 ( x_1 ) 到 ( x_t ) 的所有时序信息」。
2. 实例:输入句子"我 爱 吃 苹果"
- 时间步 ( t=1 ):输入 ( x_1=)"我"的词向量,无初始隐藏状态 ( h_0 )(通常设为全 0 向量),故 ( h_1 ) 仅包含"我"的信息,输出 ( y_1 ) 预测下一个词;
- 时间步 ( t=2 ):输入 ( x_2=)"爱"的词向量,结合 ( h_1 )("我"的信息),得到 ( h_2 )(包含"我+爱"),输出 ( y_2 ) 预测下一个词;
- 时间步 ( t=3 ):输入 ( x_3=)"吃"的词向量,结合 ( h_2 )("我+爱"),得到 ( h_3 )(包含"我+爱+吃");
- 时间步 ( t=4 ):输入 ( x_4=)"苹果"的词向量,结合 ( h_3 )("我+爱+吃"),得到 ( h_4 )(包含整句话的时序信息)。
可见,RNN 通过「隐藏状态的循环传递」,实现了对时序依赖的建模------后面的状态能"记住"前面的内容。
三、梯度消失/梯度爆炸问题的根源(RNN 的致命缺陷)
RNN 虽能建模时序依赖,但无法处理长序列(如 100 字以上的文本),核心原因是 反向传播时的梯度消失/梯度爆炸。从"反向传播的链式法则"拆解根源:
1. 先明确:梯度的作用
梯度是「损失函数对模型权重的偏导数」(( \frac{\partial Loss}{\partial W} )),决定参数更新的方向和幅度:
- 梯度消失:梯度值趋近于 0 → 参数几乎不更新 → 模型学不到长距离依赖;
- 梯度爆炸:梯度值极大 → 参数更新幅度过大 → 模型训练失控(出现 NaN 值)。
2. 核心根源:权重矩阵的"连乘效应"
RNN 反向传播时,损失函数对早期时间步权重 ( W_{hh} ) 的梯度,需要多次乘以 ( W_{hh} ) 本身。简化推导逻辑(Markdown 公式格式):
若计算损失函数对 ( t=1 ) 时 ( W_{hh} ) 的梯度,需从最后一个时间步 ( t=T ) 往回传递:
∂Loss∂Whh∝∂Loss∂hT⋅∂hT∂hT−1⋅∂hT−1∂hT−2⋅...⋅∂h2∂h1⋅∂h1∂Whh \frac{\partial Loss}{\partial W_{hh}} \propto \frac{\partial Loss}{\partial h_T} \cdot \frac{\partial h_T}{\partial h_{T-1}} \cdot \frac{\partial h_{T-1}}{\partial h_{T-2}} \cdot ... \cdot \frac{\partial h_2}{\partial h_1} \cdot \frac{\partial h_1}{\partial W_{hh}} ∂Whh∂Loss∝∂hT∂Loss⋅∂hT−1∂hT⋅∂hT−2∂hT−1⋅...⋅∂h1∂h2⋅∂Whh∂h1
其中,( \frac{\partial h_t}{\partial h_{t-1}} ) 与 ( W_{hh} ) 直接相关(由 ( h_t ) 的计算公式推导),可近似看作 ( W_{hh} ) 的函数。这意味着:梯度的计算,等价于 ( W_{hh} ) 矩阵自乘 ( T-1 ) 次。
3. 两种极端情况
-
情况1:梯度消失
若 ( W_{hh} ) 的「特征值小于 1」(如 0.9),自乘多次后结果指数级衰减:
( 0.9^{100} \approx 2.65 \times 10^{-5} ) → 梯度趋近于 0。
表现:早期时间步(如 ( t=1 ))的梯度传不到最后,模型记不住长序列前面的信息。
-
情况2:梯度爆炸
若 ( W_{hh} ) 的「特征值大于 1」(如 1.1),自乘多次后结果指数级增大:
( 1.1^{100} \approx 13780.61 ) → 梯度极大。
表现:参数更新幅度过大,训练时出现 NaN 值,模型直接崩溃。
4. 补充:激活函数的"雪上加霜"
RNN 常用的 ( \tanh ) 激活函数,其导数最大值为 1,且大部分情况下小于 1:
tanh′(x)=1−tanh2(x)∈(0,1] \tanh'(x) = 1 - \tanh^2(x) \in (0,1] tanh′(x)=1−tanh2(x)∈(0,1]
反向传播时,梯度需额外乘以 ( \tanh'(x) ),进一步加剧梯度消失的速度。
四、核心总结
- RNN 的价值:首次实现序列数据的时序依赖建模,解决了 Word2Vec 忽略语序的问题;
- RNN 的关键:隐藏层的循环连接 + 权重共享,让模型能"记住"前面的时序信息;
- RNN 的致命缺陷:梯度消失/爆炸 → 无法处理长序列,这也直接催生了后续的 LSTM/GRU(用门控机制解决该问题)。