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

本文涉及的产品
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 本文介绍了循环神经网络(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

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

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

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

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

目录
相关文章
|
16天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
5871 30
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
1天前
|
数据采集 人工智能 前端开发
让 Coding Agent 从黑盒到透明:阿里云 Agent 观测审计数据采集实践
AI Agent 规模化落地带来执行黑盒、行为难追溯、成本难度量三大难题。阿里云基于 OTel 标准,面向 Coding Agent、个人通用助理和框架型 Agent,推出 LoongSuite Pilot、插件及探针等无侵入采集方案,让 Agent 实现可看见、可分析、可审计、可治理。
561 134
|
10天前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1177 2
|
8天前
|
人工智能 安全 定位技术
CodeGraph深度解析 让Claude Code工具调用直降七成的核心原理与实操教程
如今以Claude Code为代表的AI编程智能体已经成为开发者日常编码、项目重构、漏洞修复的必备工具。但在长期使用过程中,几乎所有开发者都会遇到同一个明显痛点:AI虽然具备强大的代码生成与分析能力,却常常陷入盲目探索的循环中。
959 1
|
17天前
|
人工智能 自然语言处理 供应链
|
8天前
|
人工智能 弹性计算 安全
阿里云618活动时间、活动入口、优惠活动详细解读
2026年阿里云618创新加速季已全面开启,作为年度力度最大的云产品促销活动,本次大促覆盖轻量应用服务器、ECS云服务器、GPU云服务器、数据库、AI算力、安全服务、CDN等全品类产品,推出5亿元算力补贴、新用户限时秒杀、普惠满减、企业专享、免费试用、云大使返佣等多重福利,个人开发者、中小企业、AI团队均可享受专属低价。本文将系统梳理2026年阿里云618活动的完整时间节点、官方参与入口、各类优惠细则、使用规则、热门产品推荐及实操代码,帮助用户精准参与、高效省钱,以最低成本完成上云部署。
764 4
|
8天前
|
运维
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
1432 0