循环神经网络(RNN)的基本原理是什么?
简化版
RNN 是专门处理序列数据(文本、语音、时间序列)的神经网络。核心是循环 + 记忆:它按时间步一个一个处理序列元素,每一步不仅看当前输入,还看上一步传来的「隐藏状态」(是对历史信息的有限维、任务相关摘要),算出新的隐藏状态再传给下一步——公式 h_t = f(W·x_t + U·h_{t-1} + b)。这样序列的历史信息就沿着时间步不断传递、累积,让 RNN 能捕捉上下文和时序依赖。关键是所有时间步共享同一套参数(W、U),所以同一参数可递归应用于不同长度序列,但实际长度受计算、显存与梯度传播限制。缺点:难以捕捉长距离依赖(梯度消失/爆炸),催生了 LSTM、GRU。
详细版
核心公式(每个时间步):
h_t = f(W·x_t + U·h_{t-1} + b) 隐藏状态(记忆)
y_t = g(V·h_t + c) 输出(可选)
x_t = 当前输入, h_{t-1} = 上一步隐藏状态, f 常用 tanh
关键特点:
| 特点 | 说明 |
|---|---|
| 循环/记忆 | 隐藏状态 h 沿时间步传递,携带历史信息 |
| 参数共享 | 所有时间步共用同一套 W、U、V |
| 变长处理 | 同一参数可递归用于不同长度;实际长度受资源与优化限制 |
| 时序建模 | 能捕捉顺序和上下文依赖 |
展开视角: RNN 按时间步「展开」相当于一个很深的网络(每个时间步一层,共享参数)——这也是它梯度消失/爆炸的根源。
完整版教学
一、为什么需要 RNN——序列数据的特殊性
普通神经网络(全连接、CNN)处理的是固定大小、彼此独立的输入。但很多数据是序列:
- 文本(词一个接一个,顺序有意义)、语音(时间上的信号)、时间序列(股价、传感器)。
序列数据有两个特点普通网络处理不好:① 元素有顺序、有上下文依赖(「不 好」和「好 不」意思不同;理解一个词要看前文);② 长度可变(句子有长有短)。
RNN 专为序列设计,核心思路是引入「记忆」——处理序列时记住之前看过的信息,用它来帮助理解当前元素。这让 RNN 能建模顺序和上下文。
二、核心机制:循环 + 隐藏状态(记忆)
RNN 按时间步逐个处理序列元素。关键在于它有一个隐藏状态 h(hidden state),充当「记忆」,在时间步之间循环传递:
每个时间步 t:
h_t = f(W·x_t + U·h_{t-1} + b)
└当前输入┘ └上一步的记忆┘
(f 通常是 tanh 激活)
- x_t:当前时间步的输入(如当前这个词)。
- h_{t-1}:上一个时间步传来的隐藏状态——它是到当前为止历史信息的有限维摘要,可能丢失细节。
- h_t:结合「当前输入」和「历史记忆」算出的新隐藏状态,再传给下一步。
所以隐藏状态像一条贯穿整个序列的记忆线:每一步都把新信息融进记忆、更新记忆、传下去。这就是 RNN 名字里「循环(Recurrent)」的含义——同一套计算在每个时间步循环执行,记忆不断流动。
三、输出
每个时间步可以(按需)产生一个输出:
y_t = g(V·h_t + c)
- 有的任务每步都要输出(如给每个词标注词性);有的任务只在最后输出(如判断整句情感)——取决于任务类型(详见 RNN 类型专题)。
四、参数共享:统一处理不同长度序列的关键
RNN 一个至关重要的特点:所有时间步共享同一套参数(W、U、V)。
- 不管序列多长,每个时间步用的都是同一个 W、U、V——就像用同一个「处理单元」在序列上反复滑动(类比 CNN 权值共享在空间上滑动,RNN 是在时间上滑动)。
好处:
- 同一参数可递归应用于不同长度序列,但实际长度受计算、显存与梯度传播限制:参数量固定,不随序列长度增加。
- 参数少:不用为每个位置单独学参数。
- 时间位置上的参数共享:同样的模式出现在序列不同位置都能被同一套参数处理。
五、展开视角:RNN 是一个「很深」的网络
把 RNN 沿时间步「展开(unfold)」,会看到它相当于一个很深的前馈网络——每个时间步是一「层」,层与层之间共享参数:
x_1 → [单元] → h_1 → [单元] → h_2 → ... → [单元] → h_T
(每个"单元"是同一套参数)
训练时用沿时间的反向传播(BPTT, Backpropagation Through Time)——把展开的网络当成深网络做反向传播。
这个「展开成很深的网络」的视角很重要:正因为序列长时展开层数极多,梯度在时间步之间连乘传播,导致 RNN 严重的梯度消失/爆炸问题——长距离的信息传不过来(详见 RNN 梯度问题专题)。这是 RNN 的核心缺陷,也是 LSTM、GRU 出现的原因。
六、RNN 的局限(引出后续)
- 长距离依赖难捕捉:梯度消失使 RNN 记不住很久之前的信息(如长句子开头的信息)。
- 梯度爆炸:需要梯度裁剪。
- 串行计算、无法并行:必须一步步按时间处理,慢(这也是 Transformer 取代它的原因之一)。
解决方案:LSTM、GRU(门控机制缓解长依赖)、注意力机制、Transformer(详见对应专题)。
七、核对张量形状与参数量
设输入维度 D_x=3、隐藏维度 D_h=4、输出维度 D_y=2。采用 h_t=tanh(W_xh x_t+W_hh h_{t-1}+b_h) 时,W_xh 为 4×3,W_hh 为 4×4;输出矩阵 W_hy 为 2×4。含两个偏置后总参数量为 12+16+4+8+2=42,序列从 10 步变成 100 步不会增加参数,只会增加展开计算和激活存储。
| 张量 | 形状 | 本例参数数 |
|---|---|---|
W_xh | [D_h,D_x] | 12 |
W_hh | [D_h,D_h] | 16 |
b_h | [D_h] | 4 |
W_hy,b_y | [D_y,D_h], [D_y] | 10 |
批量输入通常写成 [B,T,D_x],循环会沿 T 依次更新状态,B 维可以并行。不同框架可能把时间维放在第一维,面试手算前应先说明形状约定。
记忆钩子:参数共享让参数量不随 T 增长,不代表运行时间、激活内存或可学习依赖长度也不随 T 增长。
八、常见误区与追问
- 误区:隐藏状态完整保存了此前所有输入。 它只是固定维度的任务相关摘要,容量与训练都会造成信息丢失。
- 误区:RNN 能递归处理变长序列就能记住任意长依赖。 可执行长度和有效记忆长度是两回事,梯度与状态容量仍有限。
- 追问:参数量为什么与序列长度无关? 各时间步复用同一组输入、循环和输出权重。
- 追问:BPTT 需要保存什么? 通常要保存各步激活和门值供反向使用,也可截断或重计算节省内存。
- 追问:训练时哪些维度可以并行? 同一时间步的 batch 维可并行,但普通 RNN 的时间步依赖限制 T 维并行。
九、加强记忆
RNN 专处理序列数据,核心是循环 + 记忆:按时间步逐个处理,用隐藏状态 h_t = f(W·x_t + U·h_{t-1} + b) 把历史信息沿时间步传递、累积(h 是「记忆线」,每步融合当前输入和上一步记忆)。所有时间步共享同一套参数,所以同一模型能处理不同长度序列,实际长度仍受计算、内存和优化限制(类比 CNN 空间权值共享,RNN 是时间上共享)。按时间展开相当于很深的网络,用 BPTT 训练——但梯度在时间步间连乘导致严重的梯度消失/爆炸,使 RNN 难捕捉长距离依赖,且串行不能并行。这些缺陷催生了 LSTM、GRU(门控缓解长依赖) 和 Transformer。