人工智能之语言领域 自然语言处理 第十章 循环神经网络(RNN)

人工智能之语言领域

第十章 循环神经网络(RNN)


文章目录

  • 人工智能之语言领域
  • 前言循环神经网络(RNN)
    • [10.1 RNN基础原理](#10.1 RNN基础原理)
    • [10.2 RNN的核心问题](#10.2 RNN的核心问题)
      • [10.2.1 梯度消失与梯度爆炸](#10.2.1 梯度消失与梯度爆炸)
      • [10.2.2 长序列依赖问题](#10.2.2 长序列依赖问题)
    • [10.3 RNN的改进模型](#10.3 RNN的改进模型)
      • [10.3.1 LSTM(长短期记忆网络):门控机制详解](#10.3.1 LSTM(长短期记忆网络):门控机制详解)
      • [10.3.2 GRU(门控循环单元):简化门控机制](#10.3.2 GRU(门控循环单元):简化门控机制)
    • [10.4 RNN在NLP中的应用](#10.4 RNN在NLP中的应用)
      • [10.4.1 文本生成](#10.4.1 文本生成)
      • [10.4.2 序列标注](#10.4.2 序列标注)
    • [10.5 实战:基于LSTM的文本生成任务](#10.5 实战:基于LSTM的文本生成任务)
    • [RNN vs Transformer 对比](#RNN vs Transformer 对比)
    • 小结
  • 资料

前言循环神经网络(RNN)

循环神经网络(Recurrent Neural Network, RNN)是专为处理序列数据而设计的神经网络架构,在自然语言处理(NLP)发展史上具有里程碑意义。尽管近年来 Transformer 架构逐渐成为主流,但 RNN 及其变体(LSTM、GRU)因其结构简洁、内存效率高,仍在许多实际场景中广泛应用。本章将深入浅出地讲解 RNN 的基本原理、核心问题、改进模型,并通过文本生成实战完整演示其应用。


10.1 RNN基础原理

10.1.1 序列数据的建模逻辑

序列数据 :元素之间存在顺序依赖关系的数据,如:

  • 文本:["我", "爱", "自然", "语言", "处理"]
  • 语音:时间步音频帧
  • 时间序列:股票价格、传感器读数

🎯 核心思想

"过去的信息会影响当前的输出 "。

例如,在句子"我爱吃___"中,前文"吃"强烈暗示空格应填"饭"而非"书"。

传统前馈神经网络(如 MLP)无法建模这种时序依赖,因为每次输入都是独立的。而 RNN 通过引入隐藏状态(Hidden State)作为记忆单元,将历史信息传递到当前时刻。


10.1.2 RNN的网络结构与前向传播

网络结构

RNN 在每个时间步 t 接收两个输入:

  • 当前输入 x_t
  • 上一时刻的隐藏状态 h_{t-1}

并输出:

  • 当前隐藏状态 h_t
  • (可选)当前输出 y_t

时间步 t+1
时间步 t
时间步 t-1
xₜ₋₁
RNN Cell
hₜ₋₂
hₜ₋₁
xₜ
RNN Cell
hₜ
yₜ
xₜ₊₁
RNN Cell
hₜ₊₁

💡 参数共享:所有时间步共享同一组参数 W_{xh}, W_{hh}, b_h ,使模型能处理任意长度序列。

数学表达(前向传播)

h t = tanh ⁡ ( W x h x t + W h h h t − 1 + b h ) y t = W h y h t + b y \begin{aligned} h_t &= \tanh(W_{xh} x_t + W_{hh} h_{t-1} + b_h) \\ y_t &= W_{hy} h_t + b_y \end{aligned} htyt=tanh(Wxhxt+Whhht−1+bh)=Whyht+by

其中:

  • h_t \\in \\mathbb{R}\^d :隐藏状态(记忆)
  • \\tanh :激活函数(也可用 ReLU,但 tanh 更常见)
  • W_{xh}, W_{hh}, W_{hy} :可学习权重矩阵

优势 :结构简单,天然适合序列建模

缺陷:难以捕捉长距离依赖(见 10.2 节)


10.2 RNN的核心问题

10.2.1 梯度消失与梯度爆炸

RNN 通过时间反向传播(BPTT) 训练,即展开整个序列后进行反向传播。

  • 梯度爆炸 :梯度值过大 → 参数更新剧烈 → 训练不稳定
    解决:梯度裁剪(Gradient Clipping)
  • 梯度消失 :梯度值趋近于 0 → 早期时间步参数几乎不更新 → 无法学习长距离依赖

🔍 原因

隐藏状态递归计算涉及矩阵连乘:

∂ h t ∂ h 0 = ∏ k = 1 t ∂ h k ∂ h k − 1 \frac{\partial h_t}{\partial h_0} = \prod_{k=1}^{t} \frac{\partial h_k}{\partial h_{k-1}} ∂h0∂ht=k=1∏t∂hk−1∂hk

若 Jacobian 矩阵特征值 < 1,连乘后趋近于 0。


10.2.2 长序列依赖问题

由于梯度消失,标准 RNN 只能有效利用最近几步的信息

🌰 示例:

句子:"法国...(50个词)...首都是巴黎。"

标准 RNN 难以将"法国"与"巴黎"关联,因中间间隔太长。


10.3 RNN的改进模型

为解决上述问题,研究者提出了门控机制(Gating Mechanism),代表模型:LSTM 和 GRU。

10.3.1 LSTM(长短期记忆网络):门控机制详解

LSTM 引入三个门控制信息流动:

作用 公式
遗忘门(Forget Gate) 决定丢弃多少历史信息 f_t = \\sigma(W_f \[h_{t-1}, x_t\] + b_f)
输入门(Input Gate) 决定更新多少新信息 i_t = \\sigma(W_i \[h_{t-1}, x_t\] + b_i)
输出门(Output Gate) 决定输出多少当前状态 o_t = \\sigma(W_o \[h_{t-1}, x_t\] + b_o)

并维护一个细胞状态(Cell State) C_t 作为长期记忆:

C ~ t = tanh ⁡ ( W C [ h t − 1 , x t ] + b C ) C t = f t ⊙ C t − 1 + i t ⊙ C ~ t h t = o t ⊙ tanh ⁡ ( C t ) \begin{aligned} \tilde{C}t &= \tanh(W_C [h{t-1}, x_t] + b_C) \\ C_t &= f_t \odot C_{t-1} + i_t \odot \tilde{C}_t \\ h_t &= o_t \odot \tanh(C_t) \end{aligned} C~tCtht=tanh(WC[ht−1,xt]+bC)=ft⊙Ct−1+it⊙C~t=ot⊙tanh(Ct)
xₜ

hₜ₋₁, xₜ

hₜ₋₁
σ (遗忘门)
σ (输入门)
σ (输出门)
tanh (候选细胞)
Cₜ₋₁

  • Cₜ
    tanh

    hₜ

优势 :细胞状态提供"高速公路",梯度可无损回传,有效缓解梯度消失

缺点:参数多(4倍于 RNN),训练慢


10.3.2 GRU(门控循环单元):简化门控机制

GRU 将遗忘门和输入门合并为更新门(Update Gate),并取消细胞状态,结构更简洁:

z t = σ ( W z [ h t − 1 , x t ] ) (更新门) r t = σ ( W r [ h t − 1 , x t ] ) (重置门) h ~ t = tanh ⁡ ( W [ r t ⊙ h t − 1 , x t ] ) h t = ( 1 − z t ) ⊙ h t − 1 + z t ⊙ h ~ t \begin{aligned} z_t &= \sigma(W_z [h_{t-1}, x_t]) \quad \text{(更新门)} \\ r_t &= \sigma(W_r [h_{t-1}, x_t]) \quad \text{(重置门)} \\ \tilde{h}t &= \tanh(W [r_t \odot h{t-1}, x_t]) \\ h_t &= (1 - z_t) \odot h_{t-1} + z_t \odot \tilde{h}_t \end{aligned} ztrth~tht=σ(Wz[ht−1,xt])(更新门)=σ(Wr[ht−1,xt])(重置门)=tanh(W[rt⊙ht−1,xt])=(1−zt)⊙ht−1+zt⊙h~t

优势 :参数少(2倍于 RNN),训练快,性能接近 LSTM

📊 实践建议:优先尝试 GRU,若效果不足再用 LSTM


10.4 RNN在NLP中的应用

10.4.1 文本生成

任务:给定起始 token,逐字生成连贯文本。

  • 训练 :输入句子 x = [x₀, x₁, ..., xₙ],目标为 [x₁, x₂, ..., xₙ₊₁]
  • 推理:自回归生成(上一输出作为下一输入)

🌰 应用:诗歌生成、代码补全、聊天机器人(早期)


10.4.2 序列标注

任务:为序列中每个元素打标签,如:

  • 命名实体识别(NER):[B-PER, I-PER, O, B-LOC]
  • 词性标注(POS):[NOUN, VERB, ADJ]

架构Embedding → BiLSTM → CRF
💡 双向 RNN(BiRNN):同时考虑前后文,显著提升性能
词序列
Embedding
BiLSTM
CRF
标签序列


10.5 实战:基于LSTM的文本生成任务

任务目标

使用莎士比亚英文作品训练一个字符级 LSTM,生成风格相似的新文本。

完整代码实现(PyTorch)

python 复制代码
# Step 1: 安装依赖
# pip install torch numpy matplotlib

import torch
import torch.nn as nn
import numpy as np
import random
import matplotlib.pyplot as plt

# Step 2: 加载并预处理数据
def load_data(url="https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt"):
    import requests
    response = requests.get(url)
    text = response.text
    print(f"数据集长度: {len(text)} 字符")
    print("前200字符:\n", text[:200])
    return text

text = load_data()

# 构建字符映射
chars = sorted(list(set(text)))
vocab_size = len(chars)
char_to_idx = {ch: i for i, ch in enumerate(chars)}
idx_to_char = {i: ch for i, ch in enumerate(chars)}

# 转换文本为索引
data = [char_to_idx[c] for c in text]

# Step 3: 定义LSTM模型
class CharLSTM(nn.Module):
    def __init__(self, vocab_size, embed_dim=256, hidden_dim=512, num_layers=2):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.lstm = nn.LSTM(embed_dim, hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, vocab_size)

    def forward(self, x, hidden=None):
        # x: [batch, seq_len]
        embed = self.embedding(x)  # [B, L, D]
        lstm_out, hidden = self.lstm(embed, hidden)  # [B, L, H]
        logits = self.fc(lstm_out)  # [B, L, vocab_size]
        return logits, hidden

# Step 4: 数据加载器
def get_batch(data, batch_size=32, seq_len=100):
    start_indices = torch.randint(len(data) - seq_len, (batch_size,))
    x = torch.stack([torch.tensor(data[i:i+seq_len]) for i in start_indices])
    y = torch.stack([torch.tensor(data[i+1:i+1+seq_len]) for i in start_indices])
    return x, y

# Step 5: 训练设置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = CharLSTM(vocab_size).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.002)

# Step 6: 训练循环
losses = []
for epoch in range(50):
    model.train()
    x, y = get_batch(data)
    x, y = x.to(device), y.to(device)
    
    logits, _ = model(x)
    loss = criterion(logits.view(-1, vocab_size), y.view(-1))
    
    optimizer.zero_grad()
    loss.backward()
    torch.nn.utils.clip_grad_norm_(model.parameters(), 5)  # 梯度裁剪
    optimizer.step()
    
    losses.append(loss.item())
    if epoch % 10 == 0:
        print(f"Epoch {epoch}, Loss: {loss.item():.4f}")

# 可视化损失
plt.plot(losses)
plt.title("Training Loss")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.show()

# Step 7: 文本生成函数
def generate(model, start_str="ROMEO:", length=500, temperature=1.0):
    model.eval()
    with torch.no_grad():
        input_indices = [char_to_idx[c] for c in start_str]
        input_tensor = torch.tensor([input_indices]).to(device)
        hidden = None
        
        generated = start_str
        for _ in range(length):
            logits, hidden = model(input_tensor[:, [-1]], hidden)  # 只取最后一个字符
            # 应用温度采样
            logits = logits.squeeze() / temperature
            probs = torch.softmax(logits, dim=-1)
            next_idx = torch.multinomial(probs, num_samples=1).item()
            
            generated += idx_to_char[next_idx]
            input_tensor = torch.cat([input_tensor, torch.tensor([[next_idx]]).to(device)], dim=1)
            
        return generated

# 生成新文本
generated_text = generate(model, start_str="KING LEAR:", length=300, temperature=0.8)
print("\n生成的文本:\n", generated_text)

关键技巧说明

技巧 作用
梯度裁剪 防止梯度爆炸
温度采样(Temperature Sampling) 控制生成多样性: - T<1:更确定、重复 - T>1:更多样、可能乱码
字符级建模 避免未登录词,但需更长序列

预期输出示例

复制代码
KING LEAR:
What art thou that usurp'st this time of night,
Together with that fair and warlike form
In which the majesty of buried Denmark
Did sometimes march? By heaven I charge thee, speak!
...

💡 中文适配 :将字符改为,使用中文语料(如《红楼梦》)


RNN vs Transformer 对比

特性 RNN/LSTM Transformer
并行化 ❌(时序依赖) ✅(自注意力)
长距离依赖 △(LSTM 可缓解) ✅(直接建模)
训练速度 快(大规模)
内存占用 高(O(n²) 注意力)
适用场景 小模型、边缘设备、实时流 大模型、高精度任务

📌 现状 :Transformer 主导大模型,但 RNN 仍在资源受限场景(如手机端)有优势。


小结

循环神经网络(RNN)通过引入隐藏状态 实现了对序列数据的建模,其改进版本 LSTM 和 GRU 通过门控机制 有效缓解了梯度消失问题,推动了 NLP 在 2010s 的快速发展。尽管 Transformer 架构已成为当前主流,但 RNN 因其结构简单、内存高效,仍在文本生成、序列标注等任务中占有一席之地。理解 RNN 是掌握现代 NLP 模型演进的关键一步。


资料

咚咚王

《Python 编程:从入门到实践》

《利用 Python 进行数据分析》

《算法导论中文第三版》

《概率论与数理统计(第四版) (盛骤) 》

《程序员的数学》

《线性代数应该这样学第 3 版》

《微积分和数学分析引论》

《(西瓜书)周志华-机器学习》

《TensorFlow 机器学习实战指南》

《Sklearn 与 TensorFlow 机器学习实用指南》

《模式识别(第四版)》

《深度学习 deep learning》伊恩·古德费洛著 花书

《Python 深度学习第二版(中文版)【纯文本】 (登封大数据 (Francois Choliet)) (Z-Library)》

《深入浅出神经网络与深度学习 +(迈克尔·尼尔森(Michael+Nielsen)》

《自然语言处理综论 第 2 版》

《Natural-Language-Processing-with-PyTorch》

《计算机视觉-算法与应用(中文版)》

《Learning OpenCV 4》

《AIGC:智能创作时代》杜雨 +&+ 张孜铭

《AIGC 原理与实践:零基础学大语言模型、扩散模型和多模态模型》

《从零构建大语言模型(中文版)》

《实战 AI 大模型》

《AI 3.0》

相关推荐
光锥智能2 小时前
无界动力与生数科技达成战略合作,将在算法、数据与系统方面深度融合
大数据·人工智能·科技
Lethehong2 小时前
WorkBuddy 使用指南:腾讯 AI Agent 如何接入微信、飞书、钉钉、企微、QQ,AI 将不再只是助手,而是你的“数字同事”
人工智能·玩转openclaw·workbuddy
龙亘川2 小时前
AI 时代数据治理的破局与重构:2025 白皮书核心洞察解析
大数据·人工智能·ai 时代数据治理白皮书
杜子不疼.2 小时前
Spring Cloud 熔断降级详解:用 “保险丝“ 类比,Sentinel 实战教程
人工智能·spring·spring cloud·sentinel
北京地铁1号线2 小时前
快手NLP面试题:Agent开发框架
人工智能·自然语言处理
Coovally AI模型快速验证2 小时前
国产小龙虾方案实战:nanobot + 通义千问,钉钉上随时派活
人工智能·深度学习·学习·计算机视觉·3d
AI人工智能+2 小时前
往来港澳通行证识别:深度融合计算机视觉(CV)与自然语言处理(NLP)技术
计算机视觉·自然语言处理·ocr·往来港澳通行证识别
pingao1413782 小时前
智慧城市扬尘监测站:科技助力气象环保新篇章
人工智能·科技·智慧城市
ruiang2 小时前
开源模型应用落地-工具使用篇-Spring AI-高阶用法(九)
人工智能·spring·开源