【重点】LSTM – 长短时记忆神经网络

摘要:LSTM旨在解决传统RNN在处理长序列时出现的梯度消失和爆炸问题。通过输入门、遗忘门和输出门来控制信息的流动。

【面试复习】LSTM - 长短时记忆神经网络-20240907-1

1 模型原理

1.1 超参数

  • input_size :input_size 是指每个时间步长输入的特征维度。在处理序列数据时,每个时间步可能包含多个特征。例如,在一个天气预测问题中,每天的输入可能包含温度、湿度、风速等多个特征。这些特征的数量就是 input_size
  • sequence_length :sequence_length 决定了模型会处理多少个时间步的数据。假设我们有一个每日温度数据集,并且我们希望使用过去7天的温度来预测第8天的温度。每个输入序列的长度就是7,即 sequence_length = 7

1.2 输入

  • 序列数据:(batch_size, sequence_length, input_size)
  • 隐藏状态:默认全0,(num_layers * num_directions, batch_size, hidden_size)
  • 单元状态:默认全0,(num_layers * num_directions, batch_size, hidden_size)

1.3 计算

对于每一个时间步:

  • 输入门、遗忘门和输出门:将当前输入 xt 和上一个隐藏状态 h(t-1) 拼接后乘上权重,相加后再套一个 sigmoid ,最后得到一个 0-1 的值。
  • 候选单元状态:和输入门差不多,只不过最后套的是 tanh 。
  • 更新单元状态:遗忘门输出上一个单元状态+输入门输出候选单元状态。
  • 隐状态:用 tanh 套单元状态,再和输出门的结果点乘。
    【面试复习】LSTM - 长短时记忆神经网络-20240907

1.4 输出

序列数据:(batch_size, sequence_length, hidden_size)

1.5 后处理

使用 out = self.fc(out[:, -1, :]) 获得最终输出结果。

  • out[:, -1, :] :提取最后一个时间步的隐藏状态,形状为 (batch_size, hidden_size)
  • self.fc :将隐藏状态的最后一个时间步映射为输出,形状为 (batch_size, output_size)

2 Q&A

2.1 hidden_size的意义

hidden_size 的意义是什么,为什么不能直接用 input_size ?

  • input_size 是输入特征的数量,比如每个时间步的特征数。它由输入数据的特征决定。
  • hidden_size 是隐藏状态的特征数量,用于控制网络的表示能力和容量。
  • 如果使用 input_size 代替 hidden_size,模型的容量和复杂度将受到限制,尤其当 input_size 很小时,网络的表达能力会严重受限,无法有效学习到数据中的复杂模式。

2.2 sigmoid的意义

为什么输入门、遗忘门和输出门的计算使用了 sigmoid ?

  • sigmoid 将输出映射为 [0, 1]
  • 当输出接近0时,表示几乎没有信息通过。
  • 当输出接近1时,表示信息完全通过。
  • 这种特性使其非常适合用作门控机制。

2.3 tanh的意义

为什么候选单元状态、隐藏状态的计算使用了 tanh ?

  • tanh 将输出映射为 [-1, 1] ,候选单元状态可以具有正负值,提高模型表示能力。

2.4 权重的意义

为什么在计算各单元时需要用到权重?

  • 提高模型表示能力。
  • 改变矩阵形状。拼接后矩阵形状为 (batch_size, input_size + hidden_size) ,乘上权重后的形状为 (batch_size, hidden_size) 。

2.5 代码中的偏置

为什么在“手动实现”的代码中只有 w 没有 b 。

  • 因为 nn.Linear 已经内置了 b 。

2.6 LSTM和GRU的区别

  • LSTM:具有三个门(输入门、遗忘门、输出门)和一个单元状态。参数数量比GRU更多,有更好的表现能力。
  • GRU:只有两个门(重置门、更新门),并且没有单独的单元状态,隐藏状态同时扮演了单元状态的角色。简化了LSTM的结构并提高了计算效率。

3 代码实现

3.1 手动实现

import torch  
import torch.nn as nn  

class LSTMCell(nn.Module):  
    def __init__(self, input_size, hidden_size):  
        super(LSTMCell, self).__init__()  
        self.input_size = input_size  
        self.hidden_size = hidden_size  

        # 用于计算输入门、遗忘门、候选单元状态和输出门的全连接层  
        self.W_i = nn.Linear(input_size + hidden_size, hidden_size)  
        self.W_f = nn.Linear(input_size + hidden_size, hidden_size)  
        self.W_C = nn.Linear(input_size + hidden_size, hidden_size)  
        self.W_o = nn.Linear(input_size + hidden_size, hidden_size)  

    def forward(self, x, h_prev, C_prev):  
        # 将当前输入 x 和前一时间步的隐藏状态 h_prev 拼接在一起  
        combined = torch.cat((x, h_prev), dim=1)  
        # 输入门、遗忘门、输出门  
        i_t = torch.sigmoid(self.W_i(combined))  
        f_t = torch.sigmoid(self.W_f(combined))  
        o_t = torch.sigmoid(self.W_o(combined))  
        # 候选单元状态  
        C_tilde = torch.tanh(self.W_C(combined))  
        # 更新单元状态  
        C_t = f_t * C_prev + i_t * C_tilde  
        # 计算当前时间步的隐藏状态  
        h_t = o_t * torch.tanh(C_t)  
        # 返回当前时间步的隐藏状态和单元状态  
        return h_t, C_t  

# 超参数  
input_size = 10  # 输入特征的维度  
hidden_size = 20  # 隐藏状态的维度  

# 实例化LSTM单元  
lstm_cell = LSTMCell(input_size, hidden_size)  

# 输入数据  
batch_size = 5  
x = torch.randn(batch_size, input_size)  # 当前时间步的输入  
h_prev = torch.zeros(batch_size, hidden_size)  # 前一时间步的隐藏状态  
C_prev = torch.zeros(batch_size, hidden_size)  # 前一时间步的单元状态  

# 前向传播  
h_t, C_t = lstm_cell(x, h_prev, C_prev)  
print("Hidden state:", h_t)  
print("Cell state:", C_t)

Copy

3.2 调用库函数

import torch  
import torch.nn as nn  

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

    def forward(self, x):  
        # 手动初始化隐藏状态和单元状态  
        # h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)  
        # c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)        # out, _ = self.lstm(x, (h0, c0))  
        # 使用默认隐藏状态和单元状态(全0)  
        out, _ = self.lstm(x)  

        # 解码最后一个时间步的隐状态  
        # -> (batch_size, hidden_size) -> (batch_size, output_size)  
        out = self.fc(out[:, -1, :])  
        return out  

# 超参数  
input_size = 10  
hidden_size = 20  
output_size = 1  
num_layers = 2  

# 实例化模型、损失函数和优化器  
model = LSTMModel(input_size, hidden_size, output_size, num_layers)  
criterion = nn.MSELoss()  
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  

# 示例输入 (batch_size, sequence_length, input_size)x = torch.randn(5, 3, input_size)  
y = torch.randn(5, output_size)  

# 前向传播  
output = model(x)  
loss = criterion(output, y)  

print(f'Output: {output}')  
print(f'Loss: {loss.item()}')  

# 反向传播和优化  
optimizer.zero_grad()  
loss.backward()  
optimizer.step()

版权声明:
作者:Zhang, Hongxing
链接:http://zhx.info/archives/125
来源:张鸿兴的学习历程
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>