手算循环神经网络(RNN)和LSTM

简介: 本文介绍了循环神经网络(RNN)和长短期记忆网络(LSTM)的原理与PyTorch实现。主要内容包括: RNN基础:解释RNN处理序列数据的机制,展示输入/隐藏层维度关系,并通过"不好看"情感分析示例演示数据预处理和前向传播过程。 PyTorch实现:详细构建SimpleRNN类,包含RNN层和全连接层,实现正向传播、损失计算和参数更新完整流程。 LSTM原理:对比RNN的不足,说明LSTM通过输入门、遗忘门和输出门解决长序列梯度消失问题。 LSTM实践:修改RNN代码为SimpleLS

 RNN

介绍

RNN(Recurrent Neural Network) 是一种用于处理序列数据的神经网络模型,特别适用于

处理时间序列、语音、文本等具有顺序关系的数据。简单的神经网络都是水平方向的延申,RNN

可以关联不同的时刻,RNN的输出不仅取决于当前时刻的输出还取决于上一时刻隐藏层的输出。

(神经网络具有某种记忆的能力),公式表示如下:

image.gif

image.gif

结构

     假设,这里我们的输入神经元(input_size ,输入特征数为3),隐藏层神经元个数是2

(hidden_size)。

    假设现在我们想做一个情感识别任务,我们的词汇表有3个字,标签集是正面和反面,训练数据

是“不好看”,标签是负面。首先要做的是把词用词向量表示。

它的计算结构如下图:这里假设,这里我们的输入神经元(input_size ,输入特征数为3),隐藏

层神经元个数是2(hidden_size),  RNN 层数是1(num_layers = 1),时间步是3(seq_length

= 3),样本的个数是1(batch_size = 1  )。

image.gif

简单的输入和输出

import torch
import torch.nn as nn
import numpy as np
# 设置随机种子以确保结果可复现
torch.manual_seed(42)
# --- 1. 数据和维度 ---
V = 3  # 输入特征数 (词汇表大小)
H = 2  # 隐藏层大小 (H=2)
# 输入数据: “不 好 看” (One-Hot)
# 形状: (Seq_len=3, Input_size=3)
X_data_np = np.array([
    [0, 1, 0],  # 不
    [1, 0, 0],  # 好
    [0, 0, 1]   # 看
], dtype=np.float32)
# 转换为 RNN 要求的形状: (T, B, V) => (3, 1, 3)
X_tensor = torch.from_numpy(X_data_np).unsqueeze(1) 
SEQ_LEN = X_tensor.shape[0]
BATCH_SIZE = X_tensor.shape[1]
print("--- 步骤 1: 输入数据 X_tensor ---")
print(f"数据形状 (T, B, V): {X_tensor.shape}")
print(X_tensor)
print("-" * 50)
# --- 2. 简单的 RNN 模块实例化 ---
# 定义 RNN 层 (包含了 U, W, b_h)
rnn_module = nn.RNN(
    input_size=V,   #词汇表的大小
    hidden_size=H,  #隐藏层的个数
    num_layers=1,   #RNN的层数
    batch_first=False, # T 优先
    nonlinearity='tanh'
)
# 初始化初始隐藏状态 h0 (对应 h_{-1})
# 形状: (Num_layers, Batch_size, Hidden_size) => (1, 1, 2)
h0 = torch.zeros(1, BATCH_SIZE, H)
# --- 3. 前向传播并显示输出 ---
# 执行 RNN 前向传播
# out_all_steps: 所有时间步的隐藏状态 (h1, h2, h3)
# h_n_final: 最后一个时间步的隐藏状态 (h3)
out_all_steps, h_n_final = rnn_module(X_tensor, h0)
print("--- 步骤 2: RNN 模块输出解析 ---")
# [A] out_all_steps: 所有时间步的隐藏状态 (h1, h2, h3)
print("\n[A] out_all_steps (所有隐藏状态 h1...h3):")
print(f"形状 (T, B, H): {out_all_steps.shape} -> ({SEQ_LEN}, {BATCH_SIZE}, {H})")
print("---------------------------------------")
print(f"h1 状态 ('不' 后的隐藏状态):\n{out_all_steps[0, 0].detach().numpy()}")
print(f"h2 状态 ('好' 后的隐藏状态):\n{out_all_steps[1, 0].detach().numpy()}")
print(f"h3 状态 ('看' 后的隐藏状态):\n{out_all_steps[2, 0].detach().numpy()}")
# [B] h_n_final: 最终隐藏状态 (h3)
print("\n[B] h_n_final (最终隐藏状态 h_T=h3):")
print(f"形状 (L, B, H): {h_n_final.shape}")
print("---------------------------------------")
print(f"数据 (这是用于最终分类的关键向量):\n{h_n_final.squeeze().detach().numpy()}")

image.gif

图解

  unsqueeze(1) 在索引 1 的位置增加了一个维度,将形状变为 (3, 1, 3),符合 RNN 输入的标

准格式 (seq_len, batch, input_size),如果需要处理更大的批次数据,只需调整 B 维度的大小

即可。

模型的输入是隐藏状态,它的大小是(1*1*2),第1个数字代表RNN的层数,第2个1代表批次

数,第3个数字代表隐藏层神经元的个数。还有我们的数据输入,它的大小是(3*1*3)第1个数字

代表时间步(或者说是词汇表的大小),第2个数字代表批次数,第3个数字代表特征数(或者说是

输入神经元的个数)。然后进入到模型中(需要定义rnn的层数,词汇表的大小特征数,还有隐藏

层的个数),模型的最后的输出是所有隐藏层的状态和最后一个隐藏层的状态。

image.gif

tensor.squeeze().detach()

先简化张量形状,再脱离梯度追踪,常用于提取模型输出的最终结果(如分类概率)。

image.gif 编辑

代码实现

导入需要的包

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
# 设置随机种子以确保结果可复现
torch.manual_seed(42)

image.gif

定义模型

      把最后一层隐藏层的状态当成神经元的输入,然后再经过激活函数计算概率。

# --- 1. 模型定义: SimpleRNN (Many-to-One) ---
class SimpleRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleRNN, self).__init__()
        self.hidden_size = hidden_size
        # 定义 RNN 层 (包含了 U, W, b_h)
        # 激活函数默认为 'tanh'
        self.rnn = nn.RNN(
            input_size=input_size, 
            hidden_size=hidden_size, 
            num_layers=1, 
            batch_first=False, # 输入形状要求 (序列长度, Batch大小, 特征维度)
            nonlinearity='tanh'
        )
        # 定义全连接层 (包含了 V_out, b_o)
        # 将最后一个隐藏状态 (H=2) 映射到最终输出 (C=2)
        self.fc = nn.Linear(hidden_size, output_size)
        # PyTorch 会自动初始化 U, W, V_out 等参数,但我们也可以自定义初始化
    # 实现前向传播
    def forward(self, x):
        # x 形状: (Seq_len=3, Batch_size=1, Input_size=3)
        # 初始化初始隐藏状态 h_0 (对应 h_{-1})
        # 形状: (Num_layers, Batch_size, Hidden_size) => (1, 1, 2)
        h0 = torch.zeros(1, x.size(1), self.hidden_size)
        # 1. RNN 计算
        # out: 每一时间步的隐藏状态 (不使用)
        # h_n: 最后一个时间步的隐藏状态 (我们需要的)
        out, h_n = self.rnn(x, h0)
        # 2. 提取并准备最终隐藏状态 (h_T)
        # h_n 形状是 (1, 1, 2),我们需要移除第一个维度 (Num_layers) 
        final_hidden_state = h_n.squeeze(0) # 形状变为 (Batch_size, Hidden_size) => (1, 2)
        # 3. 全连接层到输出 Logits (O)
        # output 形状: (Batch_size, Output_size) => (1, 2)
        logits = self.fc(final_hidden_state)
        return logits

image.gif

定义数据

# --- 2. 数据准备与模型实例化 ---
# 模型维度
V = 3  # 词汇表大小
H = 2  # 隐藏层大小 (H=2)
C = 2  # 类别数
# 标签映射: 0 -> 正面, 1 -> 负面
LABEL_MAP = {0: "正面", 1: "负面"}
# 学习率
LEARNING_RATE = 0.01
# 输入数据: “不 好 看” (One-Hot)
# (Seq_len, Batch_size, Input_size) => (3, 1, 3)
X_data_np = np.array([
    [0, 1, 0],  # 不
    [1, 0, 0],  # 好
    [0, 0, 1]   # 看
], dtype=np.float32)
X_tensor = torch.from_numpy(X_data_np).unsqueeze(1) 
print(X_tensor)
# 目标标签: 负面 [0, 1],索引是 1
Y_true_index = torch.tensor([1], dtype=torch.long) # 形状: (1,)
print(Y_true_index)

image.gif

前向传播

# 模型、损失函数、优化器实例化
model = SimpleRNN(V, H, C)
criterion = nn.CrossEntropyLoss() # 交叉熵损失 (自动包含 Softmax 激活)
optimizer = optim.SGD(model.parameters(), lr=LEARNING_RATE)
# -------------------------- 3. 训练循环:实现一次参数更新 --------------------------
print("--- 步骤 3: 准备进行参数更新 ---")
print(f"初始模型参数总数: {sum(p.numel() for p in model.parameters())}")
# --- 清除旧梯度 (重要步骤!) ---
optimizer.zero_grad() 
# --------------------- A. 前向传播 (Forward Pass) ---------------------
logits = model(X_tensor) 
print("\n--- 步骤 A: 前向传播结果 ---")
print(f"输出 Logits (未激活) 形状: {logits.shape} -> {logits.detach().numpy()}")

image.gif

反向传播更新参数

# --------------------- B. 损失计算 ---------------------
loss = criterion(logits, Y_true_index)
print(f"初始交叉熵损失 J: {loss.item():.6f}")
# --------------------- C. 反向传播 (Backward Pass / Autograd) ---------------------
loss.backward()
print("\n--- 步骤 C: 反向传播结果 ---")
rnn_weights = model.rnn.weight_hh_l0
print(f"RNN 循环权重 W 的梯度 (部分):\n{rnn_weights.grad.detach().numpy()[:2, :2]}")

image.gif

全部代码

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
# 设置随机种子以确保结果可复现
torch.manual_seed(42)
# --- 1. 模型定义: SimpleRNN (Many-to-One) ---
class SimpleRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleRNN, self).__init__()
        
        self.hidden_size = hidden_size
        
        # 定义 RNN 层 (包含了 U, W, b_h)
        # 激活函数默认为 'tanh'
        self.rnn = nn.RNN(
            input_size=input_size, 
            hidden_size=hidden_size, 
            num_layers=1, 
            batch_first=False, # 输入形状要求 (序列长度, Batch大小, 特征维度)
            nonlinearity='tanh'
        )
        
        # 定义全连接层 (包含了 V_out, b_o)
        # 将最后一个隐藏状态 (H=2) 映射到最终输出 (C=2)
        self.fc = nn.Linear(hidden_size, output_size)
        
        # PyTorch 会自动初始化 U, W, V_out 等参数,但我们也可以自定义初始化
    # 实现前向传播
    def forward(self, x):
        # x 形状: (Seq_len=3, Batch_size=1, Input_size=3)
        
        # 初始化初始隐藏状态 h_0 (对应 h_{-1})
        # 形状: (Num_layers, Batch_size, Hidden_size) => (1, 1, 2)
        h0 = torch.zeros(1, x.size(1), self.hidden_size)
        
        # 1. RNN 计算
        # out: 每一时间步的隐藏状态 (不使用)
        # h_n: 最后一个时间步的隐藏状态 (我们需要的)
        out, h_n = self.rnn(x, h0)
        
        # 2. 提取并准备最终隐藏状态 (h_T)
        # h_n 形状是 (1, 1, 2),我们需要移除第一个维度 (Num_layers) 
        final_hidden_state = h_n.squeeze(0) # 形状变为 (Batch_size, Hidden_size) => (1, 2)
        
        # 3. 全连接层到输出 Logits (O)
        # output 形状: (Batch_size, Output_size) => (1, 2)
        logits = self.fc(final_hidden_state)
        
        return logits
# --- 2. 数据准备与模型实例化 ---
# 模型维度
V = 3  # 词汇表大小
H = 2  # 隐藏层大小 (H=2)
C = 2  # 类别数
# 标签映射: 0 -> 正面, 1 -> 负面
LABEL_MAP = {0: "正面", 1: "负面"}
# 学习率
LEARNING_RATE = 0.01
# 输入数据: “不 好 看” (One-Hot)
# (Seq_len, Batch_size, Input_size) => (3, 1, 3)
X_data_np = np.array([
    [0, 1, 0],  # 不
    [1, 0, 0],  # 好
    [0, 0, 1]   # 看
], dtype=np.float32)
X_tensor = torch.from_numpy(X_data_np).unsqueeze(1) 
# 目标标签: 负面 [0, 1],索引是 1
Y_true_index = torch.tensor([1], dtype=torch.long) # 形状: (1,)
# 模型、损失函数、优化器实例化
model = SimpleRNN(V, H, C)
criterion = nn.CrossEntropyLoss() # 交叉熵损失 (自动包含 Softmax 激活)
optimizer = optim.SGD(model.parameters(), lr=LEARNING_RATE)
# -------------------------- 3. 训练循环:实现一次参数更新 --------------------------
print("--- 步骤 3: 准备进行参数更新 ---")
print(f"初始模型参数总数: {sum(p.numel() for p in model.parameters())}")
# --- 清除旧梯度 (重要步骤!) ---
optimizer.zero_grad() 
# --------------------- A. 前向传播 (Forward Pass) ---------------------
logits = model(X_tensor) 
print("\n--- 步骤 A: 前向传播结果 ---")
print(f"输出 Logits (未激活) 形状: {logits.shape} -> {logits.detach().numpy()}")
# --------------------- B. 损失计算 ---------------------
loss = criterion(logits, Y_true_index)
print(f"初始交叉熵损失 J: {loss.item():.6f}")
# --------------------- C. 反向传播 (Backward Pass / Autograd) ---------------------
loss.backward()
print("\n--- 步骤 C: 反向传播结果 ---")
rnn_weights = model.rnn.weight_hh_l0
print(f"RNN 循环权重 W 的梯度 (部分):\n{rnn_weights.grad.detach().numpy()[:2, :2]}")
# --------------------- D. 参数更新 ---------------------
optimizer.step()
print("\n--- 步骤 D: 参数更新完成 ---")
print(f"更新后的 W (部分):\n{model.rnn.weight_hh_l0.detach().numpy()[:2, :2]}")
# --------------------- 4. 验证:再次前向传播并输出分类结果 ---------------------
logits_new = model(X_tensor)
loss_new = criterion(logits_new, Y_true_index)
probabilities_new = torch.softmax(logits_new, dim=1) # (1, 2)
# 找到预测概率最高的索引
predicted_index = torch.argmax(probabilities_new, dim=1).item() 
predicted_label = LABEL_MAP[predicted_index]
print("\n--- 步骤 4: 更新后验证与最终预测 ---")
print(f"新的交叉熵损失 J_new: {loss_new.item():.6f}")
print(f"新的预测概率 (正面/负面): {probabilities_new.detach().numpy()}")
# 最终输出
print("---------------------------------------")
print(f"✅ 模型最终预测情感: **{predicted_label}** (索引: {predicted_index})")
print(f"✅ 损失变化: {loss.item():.6f} -> {loss_new.item():.6f}")

image.gif


LSTM

介绍

   LSTM(Long Short-Term Memory) 是一种特殊类型的循环神经网络(RNN),旨在解决传

统 RNN 在处理长序列时面临的梯度消失问题。LSTM 引入了特殊的结构(称为“门”)来控制信息

的流动,能够更好地捕捉长期依赖关系。(简单的RNN在信息记忆方面效果不好)

    与RNN相比,除了输入和前一时刻还要包括当前时刻(日记的信息Ct),包含了,两个更细致

的操作,删除旧日记,增添新日记,f1函数就像是一个橡皮擦,根据昨天的记忆ht-1和今天的输入

Xt,决定要修改日记中的那些记录。 f2函数就像是一个铅笔,根据昨天的记忆ht-1和今天的输入

Xt,增加日记中的那些记录。f1和 f2进行运算得到新的日记啦。LSTM 就像延缓记忆衰退的良药,

可以带来更好的结果。

image.gif

     在一些教科书上,认为经过sigmoid之后的才是门。Ct和Ht只是状态更新。

输入门(Input Gate):决定当前输入的哪部分信息应该被存储到“记忆单元”(Cell State)中。

        image.gif 编辑  

遗忘门(Forget Gate):决定前一时刻的“记忆单元”中哪些信息应该被遗忘。

                                image.gif 编辑

输出门(Output Gate):决定记忆单元中的信息在当前时间步输出时的贡献。

                          image.gif 编辑

image.gif

简单的输入和输出

和上面不同的是这里的输出部分多了一个细胞的状态,然后输出也是多了一个细胞的状态,最后做

预测的时候也是让隐藏状态作为神经元的输入。(一般是用隐藏层的状态)。

import torch
import torch.nn as nn
import numpy as np
# 设置随机种子以确保结果可复现
torch.manual_seed(42)
# --- 1. 数据和维度 ---
V = 3  # 输入特征数 (词汇表大小)
H = 2  # 隐藏层大小 (H=2)
# 输入数据: “不 好 看” (One-Hot)
# 形状: (Seq_len=3, Input_size=3)
X_data_np = np.array([
    [0, 1, 0],  # 不
    [1, 0, 0],  # 好
    [0, 0, 1]   # 看
], dtype=np.float32)
# 转换为 LSTM 要求的形状: (T, B, V) => (3, 1, 3)
X_tensor = torch.from_numpy(X_data_np).unsqueeze(1)
SEQ_LEN = X_tensor.shape[0]
BATCH_SIZE = X_tensor.shape[1]
print("--- 步骤 1: 输入数据 X_tensor ---")
print(f"输入张量形状 (T, B, V): {X_tensor.shape}")
print(X_tensor)
print("-" * 50)
# --- 2. 简单的 LSTM 模块实例化 ---
# 定义 LSTM 层 (包含了 W_f, W_i, W_o, W_c 等所有权重和偏置)
lstm_module = nn.LSTM(
    input_size=V,
    hidden_size=H,
    num_layers=1,
    batch_first=False # T 优先
)
# 初始化初始状态 (h0, c0)
# 形状: (Num_layers, Batch_size, Hidden_size) => (1, 1, 2)
# h0: 初始隐藏状态
h0 = torch.zeros(1, BATCH_SIZE, H) 
# c0: 初始细胞状态 (这是 LSTM 独有的!)
c0 = torch.zeros(1, BATCH_SIZE, H) 
# --- 3. 前向传播并显示输出 ---
# 执行 LSTM 前向传播
# out_all_steps: 所有时间步的隐藏状态 (h1, h2, h3)
# (h_n_final, c_n_final): 最终的隐藏状态 h3 和细胞状态 c3
out_all_steps, (h_n_final, c_n_final) = lstm_module(X_tensor, (h0, c0))
print("--- 步骤 2: LSTM 模块输出解析 ---")
# [A] out_all_steps: 所有时间步的隐藏状态 (h1, h2, h3)
print("\n[A] out_all_steps (所有隐藏状态 h1...h3):")
print(f"形状 (T, B, H): {out_all_steps.shape} -> ({SEQ_LEN}, {BATCH_SIZE}, {H})")
print("---------------------------------------")
print(f"h1 状态 ('不' 后的隐藏状态):\n{out_all_steps[0, 0].detach().numpy()}")
print(f"h2 状态 ('好' 后的隐藏状态):\n{out_all_steps[1, 0].detach().numpy()}")
print(f"h3 状态 ('看' 后的隐藏状态):\n{out_all_steps[2, 0].detach().numpy()}")
# [B] h_n_final: 最终隐藏状态 (h3)
print("\n[B] h_n_final (最终隐藏状态 h_T=h3):")
print(f"形状 (L, B, H): {h_n_final.shape}")
print("---------------------------------------")
print(f"数据 (这是用于最终分类的关键向量):\n{h_n_final.squeeze().detach().numpy()}")
# [C] c_n_final: 最终细胞状态 (c3) - LSTM 独有
print("\n[C] c_n_final (最终细胞状态 c_T=c3):")
print(f"形状 (L, B, H): {c_n_final.shape}")
print("---------------------------------------")
print(f"数据 (这是 LSTM 内部的长时记忆):\n{c_n_final.squeeze().detach().numpy()}")

image.gif

image.gif

代码实现全过程

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
# 设置随机种子以确保结果可复现
torch.manual_seed(42)
# --- 1. 模型定义: SimpleLSTM (Many-to-One) ---
class SimpleLSTM(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleLSTM, self).__init__()
        
        self.hidden_size = hidden_size
        
        # 定义 LSTM 层 (替代 nn.RNN)
        self.lstm = nn.LSTM(
            input_size=input_size, 
            hidden_size=hidden_size, 
            num_layers=1, 
            batch_first=False # 输入形状要求 (序列长度, Batch大小, 特征维度)
        )
        
        # 定义全连接层 (与 RNN 结构相同)
        # 将最后一个隐藏状态 (H=2) 映射到最终输出 (C=2)
        self.fc = nn.Linear(hidden_size, output_size)
    # 实现前向传播
    def forward(self, x):
        # x 形状: (Seq_len=3, Batch_size=1, Input_size=3)
        
        # 1. 初始化初始状态 (h_0, c_0)
        # 形状: (Num_layers, Batch_size, Hidden_size) => (1, 1, 2)
        h0 = torch.zeros(1, x.size(1), self.hidden_size)
        c0 = torch.zeros(1, x.size(1), self.hidden_size) # ❗ 必须初始化细胞状态
        
        # 2. LSTM 计算
        # out: 每一时间步的隐藏状态
        # (h_n, c_n): 最后一个时间步的 (隐藏状态, 细胞状态)
        out, (h_n, c_n) = self.lstm(x, (h0, c0))
        
        # 3. 提取并准备最终隐藏状态 (h_T)
        # h_n 形状是 (1, 1, 2),移除第一个维度 (Num_layers) 
        final_hidden_state = h_n.squeeze(0) # 形状变为 (Batch_size, Hidden_size) => (1, 2)
        
        # 4. 全连接层到输出 Logits (O)
        logits = self.fc(final_hidden_state)
        
        return logits
# --- 2. 数据准备与模型实例化 (与原代码相同) ---
# 模型维度
V = 3  # 词汇表大小
H = 2  # 隐藏层大小 (H=2)
C = 2  # 类别数
# 标签映射
LABEL_MAP = {0: "正面", 1: "负面"}
LEARNING_RATE = 0.01
# 输入数据: “不 好 看” (One-Hot)
X_data_np = np.array([
    [0, 1, 0],  # 不
    [1, 0, 0],  # 好
    [0, 0, 1]   # 看
], dtype=np.float32)
X_tensor = torch.from_numpy(X_data_np).unsqueeze(1) 
# 目标标签: 负面 [0, 1],索引是 1
Y_true_index = torch.tensor([1], dtype=torch.long) # 形状: (1,)
# ❗ 模型实例化:使用 SimpleLSTM
model = SimpleLSTM(V, H, C)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=LEARNING_RATE)
# -------------------------- 3. 训练循环:实现一次参数更新 --------------------------
print("--- 步骤 3: 准备进行参数更新 (LSTM) ---")
print(f"初始模型参数总数: {sum(p.numel() for p in model.parameters())}")
# 注意:LSTM 参数数量远多于 RNN (约是 4 倍)
# --- 清除旧梯度 ---
optimizer.zero_grad() 
# --------------------- A. 前向传播 (Forward Pass) ---------------------
logits = model(X_tensor) 
print("\n--- 步骤 A: LSTM 前向传播结果 ---")
print(f"输出 Logits (未激活) 形状: {logits.shape} -> {logits.detach().numpy()}")
# --------------------- B. 损失计算 ---------------------
loss = criterion(logits, Y_true_index)
print(f"初始交叉熵损失 J: {loss.item():.6f}")
# --------------------- C. 反向传播 (Backward Pass) ---------------------
loss.backward()
print("\n--- 步骤 C: LSTM 反向传播结果 ---")
# 检查 LSTM 循环权重 (W_hh_l0) 的梯度是否已计算
# LSTM 的权重名包含了 gate 的信息,因此形状是 4*H x H
lstm_weights = model.lstm.weight_hh_l0 
print(f"LSTM 循环权重 W 的梯度 (部分, 对应 HxH 矩阵):\n{lstm_weights.grad.detach().numpy()[:2, :2]}")
# --------------------- D. 参数更新 ---------------------
optimizer.step()
print("\n--- 步骤 D: 参数更新完成 ---")
print(f"更新后的 LSTM 循环权重 W (部分):\n{model.lstm.weight_hh_l0.detach().numpy()[:2, :2]}")
# --------------------- 4. 验证:再次前向传播并输出分类结果 ---------------------
logits_new = model(X_tensor)
loss_new = criterion(logits_new, Y_true_index)
probabilities_new = torch.softmax(logits_new, dim=1)
predicted_index = torch.argmax(probabilities_new, dim=1).item() 
predicted_label = LABEL_MAP[predicted_index]
print("\n--- 步骤 4: 更新后验证与最终预测 ---")
print(f"新的交叉熵损失 J_new: {loss_new.item():.6f}")
print(f"新的预测概率 (正面/负面): {probabilities_new.detach().numpy()}")
# 最终输出
print("---------------------------------------")
print(f"✅ 模型最终预测情感: **{predicted_label}** (索引: {predicted_index})")
print(f"✅ 损失变化: {loss.item():.6f} -> {loss_new.item():.6f}")

image.gif

image.gif


GRU

                                                          GRU的参数量更少

image.gif

重置门和更新门

重置门 (Reset Gate, r_t)

直观含义:要不要把「过去的旧记忆」清空 / 弱化

在公式中的体现:它通过哈达玛乘积直接乘在历史记忆上:r_t *h_t-1。

更新门 (Update Gate, z_t)

直观含义:旧记忆 与 新记忆 各占多少比例

z_t = 0:多用新记忆,少用旧记忆

候选隐藏态

当前时刻,结合「被重置门筛选后的旧记忆」+「当前新输入」,生成的临时新记忆 。

image.gif

image.gif

用更新门做权重,加权融合「候选新记忆」和「历史旧记忆」,得到当前时刻最终记忆,保留多少

新候选记忆,保留多少旧历史记忆。

image.gif

image.gif

重置门:先决定「要不要忘掉过去」,过滤旧记忆

更新门:用过滤后的旧记忆 + 当前输入,生成新临时记忆

候选隐态:决定「旧记忆留多少、新记忆用多少」,生成当前新的临时记忆

最终隐态:按比例新旧融合,输出当前最终记忆,传给下一时刻

目录
相关文章
|
5月前
|
机器学习/深度学习 数据可视化 PyTorch
PyTorch深度学习实战 | 基于LSTM的时间序列预测任务
本文介绍了使用LSTM模型预测印度德里市平均温度的两个项目。项目1对温度数据进行归一化处理,采用滑动窗口法构建监督学习样本,设计5层LSTM网络结构,并详细说明了模型训练过程及评估方法。项目2在数据处理上增加了标准化和周期性特征,改进了网络架构,引入了学习率调整和早停机制优化训练过程。两个项目均通过可视化对比预测值和真实值,验证了LSTM模型在时间序列预测中的有效性。文章从数据处理、模型构建到训练优化,完整呈现了温度预测的实现流程,为时序预测任务提供了实用参考。
340 0
|
5月前
|
机器学习/深度学习 数据挖掘 PyTorch
PyTorch深度学习实战 |手算​​FCN全卷积神经网络
本文介绍了FCN-8s语义分割网络的实现细节。首先解释了语义分割的概念及其与图像分类的区别,重点分析了FCN网络结构中的全卷积化、上采样和跳跃连接三个关键技术。全卷积化将传统CNN的全连接层改为卷积层,实现像素级分类;上采样通过双线性插值恢复特征图尺寸;跳跃连接则融合高低层特征以提升细节表现。文章详细推导了损失函数的计算过程,并提供了完整的PyTorch实现代码,包括双线性插值权重初始化、VGG16骨干网络和FCN-8s主体结构。最后通过测试验证了模型能正确输出与输入尺寸匹配的预测结果。
463 3
|
5月前
|
机器学习/深度学习 PyTorch 测试技术
PyTorch深度学习实战 |多头注意力机制
摘要:本文详细介绍了Transformer中的多头自注意力机制,从整体结构到实现细节,包括四部分内容:(1)多头自注意力的基本架构;(2)内部计算流程解析;(3)注意力计算公式详解;(4)代码实现。重点阐述了多头并行的计算方式、缩放点积注意力的计算步骤(QK转置、缩放、softmax和加权求和),以及残差连接和层归一化的作用。通过"Are you OK?"示例展示了输入张量如何经过8个64维注意力头处理后拼接成512维输出。文章最后提供了完整的PyTorch实现代码,并附测试用例验证模型
357 0
|
5月前
|
机器学习/深度学习 算法 vr&ar
图解强化学习 |手算Actor-Critic
Actor-Critic是一种融合策略优化(Actor)与价值评估(Critic)的强化学习算法:Actor负责选动作,Critic实时打分(如TD误差),实现单步更新、低方差、高效率,兼顾离散/连续动作空间。(239字)
732 0
|
5月前
|
机器学习/深度学习 存储 人工智能
图解人工智能的数学基础(线性代数)
本文系统讲解线性代数核心概念,涵盖向量(定义、几何/坐标表示、内积)、矩阵(含义、运算、秩、逆、相似、分解)、行列式(几何意义与变换关系)、线性方程组、特征值与特征向量、二次型、向量空间及范数等,强调其在AI与神经网络中的实际应用。
648 7
|
5月前
|
机器学习/深度学习 人工智能 PyTorch
PyTorch深度学习实战 | 人工智能项目从训练到部署
本项目基于LSTM模型对污水处理厂总曝气量(旧区+新区)进行时序预测。通过数据清洗、Min-Max归一化、滑动窗口构造(12小时输入→预测未来1小时),构建并训练轻量级LSTM模型,支持API部署与实时调用,已实现端到端预测流程及模型保存。
389 6
|
5月前
|
机器学习/深度学习 编解码 算法
PyTorch深度学习实战 |手算​​U-net
本文详细解析了U-Net网络架构及其在医学图像分割中的应用。重点对比了U-Net与FCN的核心区别:U-Net采用特征拼接(Concat)保留所有层级信息,而FCN使用特征相加(Add)进行融合。文章深入剖析了U-Net的编码器-瓶颈-解码器结构,解释了其独特的裁剪拼接机制和Overlap-tile策略,并提供了完整的PyTorch实现代码。现代U-Net通过SamePadding实现了输入输出尺寸一致,显著提升了分割精度。文章还探讨了弹性形变数据增强和带空间权重的损失函数设计,为医学图像分析提供了实用解决
434 2
|
5月前
|
机器学习/深度学习 算法 机器人
图解强化学习 |手算SAC算法
SAC(Soft Actor-Critic)是最稳定、强大的连续动作强化学习算法,广泛应用于机器人控制与决策任务。其核心是最大熵强化学习:通过双Q网络抑制过估计,柔性策略网络增强探索,自适应温度系数α动态平衡利用与探索,兼顾最优性与鲁棒性。(239字)
805 1
|
5月前
|
机器学习/深度学习 自然语言处理 PyTorch
PyTorch深度学习实战 |手动计算 Transformer和完整的代码实现
本文介绍了基于PyTorch实现Transformer模型的完整过程。主要内容包括:1)Transformer架构的核心组件实现,如多头注意力机制、位置前馈网络、位置编码等;2)模型构建步骤,包括词嵌入层、编码器/解码器块和输出层的实现;3)完整的训练流程,包含数据处理、损失计算和参数优化;4)评估方法验证模型性能。文章通过代码示例详细展示了如何从零开始构建Transformer,并应用于机器翻译任务,同时对模型各层的输入输出维度进行了说明。该实现可作为深度学习实践者学习Transformer架构的实用指南
414 0
|
5月前
|
人工智能 知识图谱
图解人工智能的数学基础(概率论)
本内容系统讲解概率论与数理统计核心知识:从随机事件、古典/几何概型、条件概率、贝叶斯公式,到一维随机变量及其分布(离散型/连续型)、数字特征(期望、方差、协方差、相关系数),再到大数定律、中心极限定理及卡方/t/F分布,最后涵盖最大似然估计方法。理论结合水果店、掷骰子等生活实例,图文并茂,深入浅出。
477 2