循环神经网络(RNN)入门:从「有记忆」的神经元到 PyTorch 实战

—深度学习、RNN、循环神经网络、PyTorch、NLP、序列模型

一、开头例子:为什么普通神经网络「记不住」上一句话?

你问模型:「小赵在北京大学读书,喜欢什么?」
人一眼就知道「他」指小赵;但普通全连接网络把每个输入独立看待,上一时刻的信息不会自动传到下一时刻。

序列数据——句子、语音、股票走势、传感器读数——关键不在单个点,而在顺序和前后关系
循环神经网络(RNN)的核心想法很朴素:加一个「隐藏状态」当短期记忆,每读一个新输入,就结合「刚才记住了什么」再更新。

下面这张脑图里的三块——神经元内部结构 → 代码实现 → 优缺点——就是入门 RNN 最该先搞懂的一条线。


二、神经元内部结构:把时间「展开」来看

2.1 它在干什么?

可以把 RNN 想成同一个神经元在时间轴上反复工作

  • 时刻 (t) 收到输入 (x_t)
  • 同时带着上一时刻的隐藏状态 (h_{t-1})(短期记忆)
  • 经过激活函数,算出新的隐藏状态 (h_t),再传给下一时刻

脑图里把这条链按时间步展开(unroll),就得到:

x₁ → [A] → h₁ → [A] → h₂ → [A] → h₃
      ↑         ↑         ↑
     h₀        h₁        h₂

绿色方框 A 里通常是 tanh(把值压到合理范围,避免数值乱飞)。

2.2 公式:两种写法,本质一样

深度学习教材版(输入和隐藏状态各有一套权重):

[
h_t = \tanh(W_{ih} x_t + b_{ih} + W_{hh} h_{t-1} + b_{hh})
]

NLP 简化版(把 (x_t) 和 (h_{t-1}) 拼起来再乘一个矩阵):

[
h_t = \tanh(W_t [x_t, h_{t-1}] + b_t)
]

符号 含义
(x_t) 当前时刻输入(如一个词向量)
(h_{t-1}) 上一时刻隐藏状态(记忆)
(h_t) 当前隐藏状态(更新后的记忆)
(W, b) 可学习参数

人话版:新记忆 = 「当前看到什么」+ 「刚才还记得什么」,再过一个非线性变换。

2.3 图例小抄(读架构图时不迷路)

  • 黄色方框:神经网络层 / 激活
  • 粉色圆点:逐元素运算(加、乘)
  • 箭头:向量传递
  • 多条线汇成一条:拼接(concat)
  • 一条线分叉:复制同一份张量

三、代码实现:PyTorch 里几行就能跑

3.1 定义 RNN 层

import torch
import torch.nn as nn

# input_size: 每个时间步输入特征维度
# hidden_size: 隐藏状态维度(记忆容量)
# num_layers: 堆叠几层 RNN
# batch_first: True 时输入形状为 (batch, seq, feature)
rnn = nn.RNN(input_size=10, hidden_size=20, num_layers=1, batch_first=True)

3.2 前向传播

batch_size, seq_len, input_size = 3, 5, 10
hidden_size = 20

# 输入:3 个样本,每个 5 个时间步,每步 10 维特征
x = torch.randn(batch_size, seq_len, input_size)

# 初始隐藏状态 h0:形状 (num_layers, batch, hidden_size)
h0 = torch.zeros(1, batch_size, hidden_size)

output, hidden = rnn(x, h0)
返回值 形状(batch_first=True) 含义
output (batch, seq, hidden_size) 每个时间步最后一层的输出
hidden (num_layers, batch, hidden_size) 最后一个时间步的隐藏状态

小贴士:很多任务只取 output[:, -1, :](序列末尾)或 hidden[-1] 做分类;做序列标注则用每一步的 output

3.3 和「文本预处理」怎么接?

上一篇把句子变成 词 → 索引 → Embedding 向量。接 RNN 时常见管线:

分词 → 词表索引 → nn.Embedding → nn.RNN → 全连接 → 分类/生成

Embedding 输出 (batch, seq, embed_dim),正好喂给 batch_first=True 的 RNN。


四、优缺点:简单高效,但「记性」有限

4.1 优点

说明
结构简单 比 LSTM、GRU 参数少,原理好懂
运算效率高 训练通常更快,小数据、短序列上很香
不易过拟合 模型相对「瘦」,有时反而更稳

4.2 缺点:梯度在时间里「连乘」

反向传播要沿时间步往回传梯度,相当于反复乘同一个权重矩阵

  • 乘积 > 1梯度爆炸(参数更新过猛,训练发散)
  • 乘积 < 1梯度消失(早期时间步几乎学不到东西)

人话版:标准 RNN 像只有短期工作记忆的人——离得远的信息,很难真正「记住」。

所以实际工程里:

  • 短序列、轻量任务:标准 RNN 仍可用
  • 长依赖、复杂 NLP:更常用 LSTM / GRU(下一篇可展开)
  • 当下主流:Transformer 用自注意力替代循环,但理解 RNN 仍是读 LSTM、读序列建模的地基

五、小结:RNN 在学什么?

  • 核心:用隐藏状态 (h_t) 把过去的信息带到现在,适合序列数据。
  • 结构:按时间展开;(h_t = \tanh(Wx_t + Wh_{t-1} + b)) 两种写法等价理解即可。
  • 代码nn.RNN(input_size, hidden_size, ...) + output, hidden = rnn(x, h0)
  • 取舍:简单快,但长序列上梯度消失/爆炸是硬伤;需要「长记性」时升级 LSTM/GRU 或 Transformer。

六、下一篇预告

可以从本篇接到 「LSTM / GRU:给 RNN 装上遗忘门和更新门」,或做一个 「Embedding + RNN 文本分类」最小 demo,把「能跑」和「能说清楚」对齐。


版权声明与互动

本文为学习整理,脑图思路来自个人课程/读书笔记,公式与代码为通用写法。若有不严谨之处,欢迎在评论区指正,一起改到更对新手友好。

你目前卡在哪一步? 展开图、公式还是 output / hidden 的形状?留言里说说,后面我可以按提问多的方向续写。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐