长短期记忆网络(LSTM)

长短期记忆网络(LSTM)

长短期记忆网络

  • 忘记门:将值朝 0 减少
  • 输入门:决定不是忽略掉输入数据
  • 输出门:决定是不是使用隐状态

在这里插入图片描述

候选记忆单元

在这里插入图片描述

记忆单元

在这里插入图片描述
上面的这个公式,右侧的两个相加,可以得到 C t C_t Ct [ − 2 , 2 ] [-2,2] [2,2] 之间。

隐状态

在这里插入图片描述
要是我想让 H t H_t Ht [ − 1 , 1 ] [-1,1] [1,1] 之间的话,需要再做一次 t a n h tanh tanh

总结

I t = σ ( X t W x i + H t − 1 W h i + b i ) I_t = \sigma(X_t W_{xi} + H_{t-1} W_{hi} + b_i) It=σ(XtWxi+Ht1Whi+bi)

F t = σ ( X t W x f + H t − 1 W h f + b f ) F_t = \sigma(X_t W_{xf} + H_{t-1} W_{hf} + b_f) Ft=σ(XtWxf+Ht1Whf+bf)

O t = σ ( X t W x o + H t − 1 W h o + b o ) O_t = \sigma(X_t W_{xo} + H_{t-1} W_{ho} + b_o) Ot=σ(XtWxo+Ht1Who+bo)

C ~ t = tanh ⁡ ( X t W x c + H t − 1 W h c + b c ) \tilde{C}_t = \tanh(X_t W_{xc} + H_{t-1} W_{hc} + b_c) C~t=tanh(XtWxc+Ht1Whc+bc)

C t = F t ⊙ C t − 1 + I t ⊙ C ~ t C_t = F_t \odot C_{t-1} + I_t \odot \tilde{C}_t Ct=FtCt1+ItC~t

H t = O t ⊙ tanh ⁡ ( C t ) H_t = O_t \odot \tanh(C_t) Ht=Ottanh(Ct)

代码实现

首先还是导入必要的环境和所需的数据集:

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)

接着就是初始化模型参数,按照标准差 0.01 0.01 0.01 的高斯分布初始化权重,并将偏置项设为 0 0 0

def get_lstm_params(vocab_size, num_hiddens, device):
    num_inputs = num_outputs = vocab_size

    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

下面初始化函数,与之前的 RNN,LSTM 不一样的地方在于,这里有两个 zero 了。形状为(批量大小,隐藏单元数)

def init_lstm_state(batch_size, num_hiddens, device):
    return (torch.zeros((batch_size, num_hiddens), device=device),
            torch.zeros((batch_size, num_hiddens), device=device))

实际模型的定义与前面讨论的一样:提供三个门和一个额外的记忆元。但是,只有隐状态才会传递到输出层,而记忆元 C t \mathbf{C}_t Ct不直接参与输出计算。

def lstm(inputs, state, 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 = torch.sigmoid((X @ W_xi) + (H @ W_hi) + b_i)
        F = torch.sigmoid((X @ W_xf) + (H @ W_hf) + b_f)
        O = torch.sigmoid((X @ W_xo) + (H @ W_ho) + b_o)
        C_tilda = torch.tanh((X @ W_xc) + (H @ W_hc) + b_c)
        C = F * C + I * C_tilda
        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
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)

在这里插入图片描述
简洁实现:

num_inputs = vocab_size
lstm_layer = nn.LSTM(num_inputs, num_hiddens)
model = d2l.RNNModel(lstm_layer, len(vocab))
model = model.to(device)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)

在这里插入图片描述
长短期记忆网络是典型的具有重要状态控制的隐变量自回归模型。多年来已经提出了其许多变体,例如,多层、残差连接、不同类型的正则化。然而,由于序列的长距离依赖性,训练长短期记忆网络和其他序列模型(例如门控循环单元)的成本是相当高的。因此,后面 Transformer 横空出世。

小结

  • 长短期记忆网络有三种类型的门:输入门、遗忘门和输出门。
  • 长短期记忆网络的隐藏层输出包括 “ 隐状态 ” 和 “ 记忆元 ” 。只有隐状态会传递到输出层,而记忆元完全属于内部信息。
  • 长短期记忆网络可以缓解梯度消失和梯度爆炸。

QA 思考


1. 调整和分析超参数对运行时间、困惑度和输出顺序的影响。

答案:

超参数的调整对LSTM模型的训练效率、性能及输出质量有重要影响,主要包括以下几个方面:

  • 学习率(Learning Rate)

    • 过大:可能导致模型无法收敛,增加运行时间和不稳定的困惑度。
    • 过小:训练缓慢,收敛速度慢,可能陷入局部最优。
  • 批量大小(Batch Size)

    • 增加批量大小通常会加快每轮(epoch)训练速度,但可能需要更多内存。
    • 小批量有助于更好的泛化能力,但会增加训练总时间。
  • 隐藏层维度(Hidden Size)

    • 更大的维度能提升模型表达能力,降低困惑度,但也显著增加计算量。
    • 可能导致过拟合,特别是在数据较少时。
  • 层数(Number of Layers)

    • 多层结构可能提高模型表现,但也会明显增加训练与推理时间。
    • 深度增加还可能导致梯度传播困难(即使使用 LSTM 也可能发生)。
  • 序列长度(Sequence Length)

    • 更长的序列有助于模型学习长期依赖,但也增加了每次前向/反向传播的计算负担。
    • 对于基于字符的任务,更长的序列可能会改变输出顺序的准确性。

2. 如何更改模型以生成适当的单词,而不是字符序列?

答案:

要从字符级别的文本生成转变为单词级别的文本生成,需进行以下修改:

(1) 数据预处理

  • 使用分词器(如 NLTK、spaCy 或 BPE 编码)将原始文本切分为单词或子词单元。
  • 构建词汇表(vocabulary),将每个单词映射为一个唯一的整数索引。
from torchtext.vocab import build_vocab_from_iterator

def yield_tokens(data_iter):
    for text in data_iter:
        yield tokenize(text)

vocab = build_vocab_from_iterator(yield_tokens(train_data), specials=["<unk>"])

(2) 输入嵌入层

  • 在模型中加入 nn.Embedding 层,将单词索引映射为稠密向量。
embedding = nn.Embedding(num_embeddings=len(vocab), embedding_dim=256)

(3) 输出层调整

  • 输出不再是字符空间的概率分布,而是词汇表大小的概率分布。
  • 最终输出层改为线性层,输出维度为 len(vocab)
output_layer = nn.Linear(hidden_size, len(vocab))

(4) 解码策略

  • 使用贪婪解码或采样方式生成下一个词语,而不是字符。
  • 可引入 Beam Search 提高生成质量。

3. 在给定隐藏层维度的情况下,比较门控循环单元、长短期记忆网络和常规循环神经网络的计算成本。要特别注意训练和推断成本。

答案:

模型参数数量计算复杂度(训练 & 推理)特点
RNN O ( d ⋅ d ) O(d \cdot d) O(dd)简单但容易出现梯度消失,难以捕捉长期依赖
GRU O ( 3 ⋅ d ⋅ d ) O(3 \cdot d \cdot d) O(3dd)中等包含重置门和更新门,运算比 LSTM 略快,适合资源有限场景
LSTM O ( 4 ⋅ d ⋅ d ) O(4 \cdot d \cdot d) O(4dd)引入遗忘门、输入门、输出门,功能更强,但计算开销更大

说明:

  • 设隐藏状态维度为 d d d,输入维度也为 d d d
  • RNN 的权重矩阵只有一套,而 GRU 和 LSTM 分别有三组和四组门控参数。
  • 因此,在相同维度下,LSTM 的参数最多,GRU 其次,RNN 最少。
  • 结果是:LSTM > GRU > RNN,在训练和推理时计算成本依次递减。

实际影响:

  • 训练成本:LSTM 收敛较慢,训练时间最长;RNN 虽然训练快,但效果差。
  • 推理成本:LSTM 推理速度最慢,但对长序列任务准确率更高;GRU 平衡了速度与性能。

4. 既然候选记忆元通过使用 tanh ⁡ \tanh tanh 函数来确保值范围在 ( − 1 , 1 ) (-1,1) (1,1) 之间,那么隐状态需要再次使用 tanh ⁡ \tanh tanh 函数来确保输出值范围在 ( − 1 , 1 ) (-1,1) (1,1) 之间呢?

答案:

虽然候选记忆元已经经过了 tanh ⁡ \tanh tanh,但隐状态再次使用 tanh ⁡ \tanh tanh 是为了进一步筛选和标准化最终输出的表示。具体原因如下:

  1. 记忆元 vs 隐状态的功能不同

    • 记忆元是长期存储的信息管道,其值应尽量保持稳定,便于跨时间步传递。
    • 隐状态是当前时刻的输出,用于后续预测或作为下一时刻的输入,因此需要具有良好的数值稳定性。
  2. 输出门控制信息流比例

    • 输出门由 sigmoid 函数产生一个介于 0 到 1 的值,表示有多少记忆元内容应该被输出。
    • 若直接使用记忆元输出,则未经过非线性激活,可能出现超出 ( − 1 , 1 ) (-1,1) (1,1) 范围的情况。
  3. tanh ⁡ \tanh tanh 的作用

    • 再次应用 tanh ⁡ \tanh tanh 可以保证隐状态的值始终处于 ( − 1 , 1 ) (-1,1) (1,1) 之间,防止数值爆炸。
    • 同时也增强了模型的非线性表达能力。

公式回顾:
h t = o t ⊙ tanh ⁡ ( c t ) h_t = o_t \odot \tanh(c_t) ht=ottanh(ct)
其中 o t o_t ot 是输出门的结果, tanh ⁡ ( c t ) \tanh(c_t) tanh(ct) 是对整个记忆元状态做非线性变换后的结果。

因此,双重 tanh ⁡ \tanh tanh 的设计是为了分别满足记忆元的稳定性与输出表示的规范化需求。


5. 实现一个能够基于时间序列进行预测而不是基于字符序列进行预测的长短期记忆网络模型。

答案:

下面是一个基于 PyTorch 实现的时间序列预测用 LSTM 模型示例

import torch
import torch.nn as nn

class LSTMTimeseries(nn.Module):
    def __init__(self, input_size=1, hidden_size=64, num_layers=2, output_size=1):
        super(LSTMTimeseries, self).__init__()
        
        self.lstm = nn.LSTM(input_size=input_size,
                            hidden_size=hidden_size,
                            num_layers=num_layers,
                            batch_first=True)
        
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # x shape: [batch_size, seq_len, input_size]
        out, _ = self.lstm(x)  # out shape: [batch_size, seq_len, hidden_size]
        out = self.fc(out[:, -1, :])  # 取最后一个时间步的输出进行预测
        return out

关键点解释:

  • 输入形状[batch_size, sequence_length, input_size]
    • input_size=1 表示每个时间步只有一个特征,如温度、股价等。
  • LSTM 层处理序列输入并输出隐藏状态。
  • **全连接层(fc)**将最后一个时间步的隐藏状态映射到输出值(如未来某时刻的预测值)。

示例训练流程(略简化):

model = LSTMTimeseries()
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

for epoch in range(100):
    for X_batch, y_batch in train_loader:
        outputs = model(X_batch)
        loss = criterion(outputs, y_batch)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

该模型适用于股票价格预测、天气预报、IoT传感器数据分析等领域。


<think>嗯,用户想了解LSTM的介绍和实现方式。首先,我需要回忆一下LSTM的基本概念。LSTM是一种特殊的RNN,解决梯度消失和爆炸问题,适合处理长序列数据。用户可能已经知道RNN的局限性,所以需要对比说明LSTM的优势。 然后,用户提到实现方式,可能需要代码示例。查看之前的引用,引用3中有Python代码,使用Keras的Sequential模型,添加LSTM层和Dense层。这部分可以详细解释,比如输入形状、隐藏单元数、损失函数和优化器的选择。 接下来,用户的需求可能包括应用场景,比如时间序列预测、NLP任务。引用2提到视频处理,结合CNN提取特征后输入LSTM,这也是一个应用点,可以提及。 需要确保回答结构清晰,分点介绍原理、结构、实现步骤和应用场景。同时,生成相关问题,引导用户深入。例如,如何调参、与GRU的区别、实际项目中的应用案例等。 检查是否有遗漏,比如LSTM的三个门结构(输入门、遗忘门、输出门)需要详细说明,以及细胞状态的作用。代码部分要解释参数,如input_shape、epochs的作用,帮助用户理解实现细节。 最后,确认引用正确,引用1和2提供了理论和应用背景,引用3是代码示例。确保回答中的引用标识正确对应,比如在提到梯度问题时引用[^1],应用场景时引用[^2],代码部分引用[^3]。</think>### 长短期记忆网络LSTM)介绍及实现方式 #### 一、LSTM 基本原理 长短期记忆网络LSTM)是一种特殊的循环神经网络(RNN),通过引入“门控机制”解决传统RNN在长序列训练中的梯度消失和梯度爆炸问题[^1]。其核心结构包括: - **遗忘门**:决定细胞状态中哪些信息需要保留或丢弃 $$f_t = \sigma(W_f \cdot [h_{t-1}, x_t] + b_f)$$ - **输入门**:更新细胞状态的新信息 $$i_t = \sigma(W_i \cdot [h_{t-1}, x_t] + b_i)$$ - **输出门**:控制当前时刻的输出 $$o_t = \sigma(W_o \cdot [h_{t-1}, x_t] + b_o)$$ #### 二、LSTM 网络结构 ![LSTM单元结构](https://via.placeholder.com/400x200)(示意图:包含细胞状态$C_t$和三个门控单元) #### 三、Python 实现示例(使用Keras) ```python from keras.models import Sequential from keras.layers import LSTM, Dense # 定义模型 model = Sequential() model.add(LSTM(units=64, # 隐藏单元数 input_shape=(10, 1), # 输入形状(时间步长,特征维度) return_sequences=False)) model.add(Dense(1)) # 输出层 # 编译模型 model.compile(loss='mse', # 均方误差损失函数 optimizer='adam', # 自适应优化器 metrics=['accuracy']) # 模型训练(假设已预处理数据) model.fit(X_train, y_train, epochs=50, # 训练轮次 batch_size=32, # 批量大小 validation_split=0.2) # 验证集比例 ``` 实现要点说明: 1. 输入数据需转换为三维张量(样本数, 时间步长, 特征维度) 2. 隐藏单元数决定网络记忆容量 3. 选择`return_sequences=True`可构建堆叠LSTM 4. 时间序列预测常用滑动窗口法预处理数据 #### 四、典型应用场景 1. 时间序列预测(股票价格、天气预测) 2. 自然语言处理(机器翻译、文本生成) 3. 视频分析(动作识别,需结合CNN提取特征)[^2] 4. 语音识别(时序信号处理)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值