从零理解循环神经网络(RNN)与长短时记忆(LSTM):AI全景第五章第二节实战指南

1次阅读
没有评论

共计 1875 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

时序数据建模的挑战

想象我们要预测股票价格或翻译一整句话——这些任务都需要模型理解数据在时间上的依赖关系。传统神经网络就像患了 ” 健忘症 ”:每次看到新数据时,之前处理过的信息就完全丢失了。这就是为什么我们需要专门处理序列数据的循环神经网络(RNN)。

RNN 的基本原理与缺陷

循环结构解析

RNN 通过隐藏状态 (hidden state) 来保存历史信息,其核心计算流程为:

h_t = tanh(W_{hh}h_{t-1} + W_{xh}x_t + b_h)  # 隐藏状态更新
y_t = W_{hy}h_t + b_y  # 当前时刻输出

梯度消失的数学解释

通过反向传播通过时间 (BPTT) 算法训练时,梯度需要沿着时间步传播。当计算第 t 步的梯度时:

∂L/∂h_t = ∂L/∂y_t * ∂y_t/∂h_t + ∂L/∂h_{t+1} * ∂h_{t+1}/∂h_t

由于 tanh 导数的值域在 (0,1] 之间,连续相乘会导致梯度指数级衰减。例如经过 k 步传播后:

∂h_{t+k}/∂h_t = ∏_{i=1}^k ∂h_{t+i}/∂h_{t+i-1} ≈ 0.9^k

LSTM 的救赎之道

三门结构图解

从零理解循环神经网络 (RNN) 与长短时记忆(LSTM):AI 全景第五章第二节实战指南
(图示:LSTM 的遗忘门 / 输入门 / 输出门数据流向)

PyTorch 实现核心代码

import torch.nn as nn

class LSTMCell(nn.Module):
    def __init__(self, input_size, hidden_size):
        super().__init__()
        # 输入门参数
        self.W_xi = nn.Parameter(torch.Tensor(input_size, hidden_size))
        self.W_hi = nn.Parameter(torch.Tensor(hidden_size, hidden_size))
        self.b_i = nn.Parameter(torch.Tensor(hidden_size))

        # 类似定义遗忘门、输出门和候选记忆参数...

    def forward(self, x, hc_prev):
        h_prev, c_prev = hc_prev

        # 输入门计算
        i = torch.sigmoid(x @ self.W_xi + h_prev @ self.W_hi + self.b_i)
        # 类似计算遗忘门 f、输出门 o 和候选记忆~c...

        # 记忆细胞更新
        c = f * c_prev + i * torch.tanh(x @ self.W_xc + h_prev @ self.W_hc + self.b_c)
        # 隐藏状态更新
        h = o * torch.tanh(c)

        return h, c

实战优化技巧

梯度裁剪

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=5.0)

序列批处理

使用 pad_sequence 处理不等长序列:

from torch.nn.utils.rnn import pad_sequence
padded = pad_sequence(sequences, batch_first=True)
lengths = [len(seq) for seq in sequences]

双向 LSTM 适用场景

  • 需要上下文信息的任务(如文本分类)
  • 每个时间点的决策依赖前后文(如语音识别)

常见陷阱与解决方案

  1. 学习率调整
  2. 经验法则:学习率 ≈ 1/sqrt(序列长度)
  3. 使用学习率热身 (warmup) 策略

  4. Dropout 配置

    nn.LSTM(..., dropout=0.2)  # 层间 dropout

  5. 输出层选择

  6. 分类任务:Softmax
  7. 回归任务:Linear

进阶思考与实现

处理变长序列的完整流程:

from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence

# 1. 按长度降序排序
sorted_lengths, indices = torch.sort(lengths, descending=True)

# 2. 打包序列
packed = pack_padded_sequence(padded[indices], sorted_lengths, batch_first=True)

# 3. 通过 LSTM
output, (h_n, c_n) = lstm(packed)

# 4. 解包输出
output, _ = pad_packed_sequence(output, batch_first=True)

通过这篇文章,我们从实际问题出发,逐步理解了 RNN 的局限性和 LSTM 的创新设计。建议读者尝试在股票预测或文本生成任务中实践这些技术,体会门控机制如何解决长期依赖问题。记住,理解每个数学公式背后的物理意义,比单纯记忆公式更重要。

正文完
 0
评论(没有评论)