学习长短期记忆网络lstm

目录

介绍

[1. 基本变量与集合](#1. 基本变量与集合)

[2. 门控机制 (Gates)](#2. 门控机制 (Gates))

[3. 激活函数](#3. 激活函数)

[4. 运算符](#4. 运算符)

[5. 权重与偏置参数](#5. 权重与偏置参数)

实现:


介绍

Ct可能是-2到2之间,所以后面再来一个tanh缩小范围-1到1之间

1. 基本变量与集合

  • t:当前的时间步(Time step)。

  • Xt​:在 t时刻的输入数据(Input)。

  • Ht​ :在 t时刻的隐藏状态(Hidden state),也称为输出值。它包含了当前时间步的上下文信息。

  • Ct​:在 t时刻的细胞状态(Cell state),即记忆(Memory)。这是 LSTM 的核心,负责在不同的时间步之间传递信息。

  • Ct−1​:上一个时间步的细胞状态。

  • C~t​ :在 t时刻的候选记忆(Candidate memory),表示当前输入可能带来的新信息。

2. 门控机制 (Gates)

LSTM 通过三个"门"来控制信息的保留与遗忘,它们的值都在 0 到 1 之间:

  • It​(Input gate / 输入门) :决定当前哪些新信息(C~t)会被加入到细胞状态 Ct​中。

  • Ft​(Forget gate / 遗忘门)决定从上一个细胞状态 Ct−1​中丢弃(遗忘)多少旧信息。

  • Ot​(Output gate / 输出门):决定当前细胞状态 Ct​中的哪些信息会被输出到隐藏状态 Ht​。

3. 激活函数

  • σ(Sigmoid):Sigmoid 激活函数。它将数值映射到 (0,1)的区间内,非常适合用来做门控开关。

  • tanh :双曲正切激活函数。它将数值映射到 (−1,1)的区间内,主要用于调节数据的范围并生成候选记忆。

4. 运算符

  • ⊙(Hadamard product 哈达玛乘积 / 逐元素乘法):表示两个相同维度的矩阵或向量对应位置的元素相乘。在图中表现为带有圆圈的乘号(⨀)。

  • +:矩阵或向量的加法运算,用于信息的累加。

5. 权重与偏置参数

  • W:权重矩阵(Weight matrix)。

    • 下标规则:例如 Wxi​表示输入 X到输入门 I的权重; Whf​表示隐藏状态 H到遗忘门 F的权重。
  • b:偏置向量(Bias vector)。

    • 下标对应相应的门,例如 bi​是输入门的偏置,bf​是遗忘门的偏置。

实现:

python 复制代码
# 导入必要的库
import torch
from torch import nn
from d2l import torch as d2l

# 设置超参数
batch_size, num_steps = 32, 35
# 加载时间机器数据集,返回迭代器和词汇表
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)

# 初始化 LSTM 的参数
def get_lstm_params(vocab_size, num_hiddens, device):
    """
    vocab_size: 词汇表大小(也是输入和输出的维度)
    num_hiddens: 隐藏单元数量
    device: 运行设备(CPU/GPU)
    """
    num_inputs = num_outputs = vocab_size

    # 辅助函数:生成均值为0、标准差0.01的正态分布随机张量
    def normal(shape):
        return torch.randn(size=shape, device=device) * 0.01

    # 辅助函数:一次性生成输入权重、隐藏权重和偏置(均为三维)
    def three():
        return (
            normal((num_inputs, num_hiddens)),   # 输入到门的权重
            normal((num_hiddens, num_hiddens)),  # 上一隐藏状态到门的权重
            torch.zeros(num_hiddens, device=device)  # 偏置
        )

    # 四个门的参数:输入门、遗忘门、输出门、候选记忆元
    W_xi, W_hi, b_i = three()  # 输入门参数
    W_xf, W_hf, b_f = three()  # 遗忘门参数
    W_xo, W_ho, b_o = three()  # 输出门参数
    W_xc, W_hc, b_c = three()  # 候选记忆元参数

    # 输出层参数(将隐藏状态映射到词汇表大小的输出)
    W_hq = normal((num_hiddens, num_outputs))
    b_q = torch.zeros(num_outputs, device=device)

    # 将所有参数放入列表,并开启梯度追踪
    params = [
        W_xi, W_hi, b_i, 
        W_xf, W_hf, b_f, 
        W_xo, W_ho, b_o, 
        W_xc, W_hc, b_c, 
        W_hq, b_q
    ]
    for param in params:
        param.requires_grad_(True)
    return params

# 初始化 LSTM 状态(隐藏状态 H 和细胞状态 C)
def init_lstm_state(batch_size, num_hiddens, device):
    """
    返回两个全零张量:(H, C),形状均为 (batch_size, num_hiddens)
    """
    return (
        torch.zeros((batch_size, num_hiddens), device=device),
        torch.zeros((batch_size, num_hiddens), device=device)
    )

# LSTM 前向传播
def lstm(inputs, state, params):
    """
    inputs: 输入序列,形状 (num_steps, batch_size, vocab_size)
    state: 初始状态 (H, C)
    params: 所有可学习参数
    """
    # 解包参数
    [W_xi, W_hi, b_i, 
     W_xf, W_hf, b_f, 
     W_xo, W_ho, b_o, 
     W_xc, W_hc, b_c, 
     W_hq, b_q] = params

    # 获取初始状态
    (H, C) = state
    outputs = []

    # 按时间步遍历输入
    for X in inputs:
        # 输入门 I
        I = torch.sigmoid((X @ W_xi) + (H @ W_hi) + b_i)
        # 遗忘门 F
        F = torch.sigmoid((X @ W_xf) + (H @ W_hf) + b_f)
        # 输出门 O
        O = torch.sigmoid((X @ W_xo) + (H @ W_ho) + b_o)
        # 候选记忆元 C_tilda
        C_tilda = torch.tanh((X @ W_xc) + (H @ W_hc) + b_c)

        # 更新细胞状态:遗忘旧信息 + 输入新信息
        C = F * C + I * C_tilda
        # 更新隐藏状态:输出门控制 tanh(C) 的输出
        H = O * torch.tanh(C)

        # 计算当前时间步的输出(通过输出层)
        Y = (H @ W_hq) + b_q
        outputs.append(Y)

    # 将所有时间步的输出拼接在一起,并返回最终状态
    return torch.cat(outputs, dim=0), (H, C)

# 训练和预测
vocab_size, num_hiddens, device = len(vocab), 256, d2l.try_gpu()
num_epochs, lr = 500, 1

# 创建自定义 RNN 模型(基于手动实现的 LSTM)
model = d2l.RNNModelScratch(
    len(vocab), num_hiddens, device, 
    get_lstm_params,       # 参数初始化函数
    init_lstm_state,       # 状态初始化函数
    lstm                   # 前向传播函数
)

# 开始训练
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
部分 说明
**get_lstm_params**​ 初始化四个门的权重(输入、遗忘、输出、候选)和输出层的权重,每个门包含 W_x(输入→门)、W_h(隐藏→门)、b(偏置)。
**init_lstm_state**​ 返回全零的隐藏状态 H和细胞状态 C,形状均为 (batch_size, num_hiddens)
lstm前向函数 按时间步依次计算三个门和候选记忆,然后更新 CH,最后通过输出层得到每个时间步的预测。
简洁实现 直接用 nn.LSTM替代手动实现,更高效且稳定。
python 复制代码
# 输入维度等于词汇表大小
num_inputs = vocab_size

# 使用 PyTorch 内置的 LSTM 层
lstm_layer = nn.LSTM(num_inputs, num_hiddens)
# 将其包装成适用于语言模型的 RNN 模型
model = d2l.RNNModel(lstm_layer, len(vocab))
model = model.to(device)

# 训练
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)