深度学习中的 seq2seq 模型

序列到序列模型,即 seq2seq,是一类深度学习模型:将一个序列数据作为输入,然后输出另一个序列数据。seq2seq 模型在自然语言处理,机器翻译,以及时间序列数据的预测中具有广泛应用。

seq2seq 的结构类似下图:

1. Encoder

编码器,依次处理序列数据中的每一个 token(词元),生成一个固定长度的 contex vector.

  • token 是模型处理的最小文本或时间步单位

2. Convex vector

  • 标准 seq2seq 模型的 encoder 一般是 RNN, LSTM 或 GRU,convex vector 是输入序列产生的隐藏状态
  • 在引入 Attention(注意力) 后,Transformer 模型中的 context vector 的定义变得更加灵活、强大。

3. Decoder

  • 解码器,将 convex vector 作为输入,并生成一个序列数据的输出
  • 根据 contex vector 以及之前的 token,预测并输出下一个 token

4. 一个例子

下面的例子使用 seq2seq 调用了 lstm 作为编码器与解码器,预测 air passengers 数据.

python 复制代码
"""
Python version: 3.12.7
Author: Zhen Chen, chen.zhen5526@gmail.com
Date: 2025/12/2 12:58
Description:


"""

import numpy as np
import pandas as pd
import torch
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt

# 读取 AirPassengers 数据
from statsmodels.datasets import get_rdataset

data = get_rdataset("AirPassengers").data
ts = data["value"].values

# 归一化
scaler = MinMaxScaler()
ts_scaled = scaler.fit_transform(ts.reshape(-1, 1))


# 构建序列
def create_sequences(data, input_len=12, output_len=1):
    X, y = [], []
    for i in range(len(data) - input_len - output_len + 1):
        X.append(data[i : i + input_len])
        y.append(data[i + input_len : i + input_len + output_len])
    return np.array(X), np.array(y)


input_len = 12
output_len = 1

X, y = create_sequences(ts_scaled, input_len, output_len)

# PyTorch 训练时,输入数据类型一般都是 torch.float32 类型
# 多分类数据时才是 torch.long 类型
X = torch.tensor(X, dtype=torch.float32)  # [N, 12, 1]
y = torch.tensor(y, dtype=torch.float32)  # [N, 12, 1]

# 对于这种时间序列数据,不能随机打乱划分训练数据
train_size = int(len(X) * 0.8)
X_train = X[:train_size]
X_test = X[train_size:]
y_train = y[:train_size]
y_test = y[train_size:]

import torch.nn as nn


class Encoder(nn.Module):
    def __init__(self, input_dim=1, hidden_dim=64, num_layers=1):
        super().__init__()
        self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True)

    def forward(self, x):
        outputs, (h, c) = self.lstm(x)  # outputs 为每个所有时间步的隐藏状态
        return h, c


class Decoder(nn.Module):
    def __init__(self, output_len=1, hidden_dim=64, output_dim=1, num_layers=1):
        super().__init__()
        self.output_len = output_len
        self.lstm = nn.LSTM(output_dim, hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Linear(
            hidden_dim, output_dim
        )  # nn.Linear 对输入张量的最后一维做线性变换,hidden_dim, output_dim 分别是输入数据
        # 和输出数据的最后一个维度
        # 改为 self.linear 也可以,则下面 forward 中也得叫 self.linear

    def forward(self, h, c):
        # decoder 输入初始化:用 0 向量作为起点
        # h 的形状: [num_layers * num_directions, batch_size, hidden_dim]
        # PyTorch 的 LSTM 输入必须是 3 维张量,格式是:
        # [batch_size, seq_len, input_size]
        decoder_input = torch.zeros(
            (h.size(1), 1, output_len)  # 中间是 1 表示为 1 个时间步
        )  # [batch, seq=1, feature=1]

        outputs = []
        for _ in range(self.output_len):
            # 第一个输入为 decoder_input
            out, (h, c) = self.lstm(decoder_input, (h, c))
            pred = self.fc(out)  #
            outputs.append(pred)
            decoder_input = pred  # 下一个时间步用前一步预测

        outputs = torch.cat(outputs, dim=1)  # outputs 的维度 [batch, 1, 1]
        return outputs


class Seq2Seq(nn.Module):
    def __init__(self):
        super().__init__()
        self.encoder = Encoder()
        self.decoder = Decoder(output_len=output_len)

    def forward(self, x):
        h, c = self.encoder(x)
        out = self.decoder(h, c)
        return out


model = Seq2Seq()
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)

epochs = 300

for epoch in range(epochs):
    model.train()

    optimizer.zero_grad()
    output = model(X_train)
    loss = criterion(output, y_train)
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 20 == 0:
        print(f"Epoch {epoch+1}/{epochs}  Loss = {loss.item():.6f}")

model.eval()  # 切换到评估模式

with torch.no_grad():
    pred_scaled = model(X_test)

pred_np = pred_scaled.detach().cpu().numpy()
pred_np = pred_np.reshape(-1, 1)  # (-1, 1) 第一个 -1 表示推断
prediction = scaler.inverse_transform(pred_np)

y_test_np = y_test.detach().cpu().numpy()
y_test_np = y_test_np.reshape(-1, 1)  # (-1, 1) 第一个 -1 表示推断
y_test_true = scaler.inverse_transform(y_test_np)

# 平均绝对误差 MAE
mae = np.mean(np.abs(prediction - y_test_true))
# 总绝对误差 SAE(sum of absolute errors)
sae = np.sum(np.abs(prediction - y_test_true))
# (可选)均方根误差 RMSE
rmse = np.sqrt(np.mean((prediction - y_test_true) ** 2))
print(f"平均绝对误差 (MAE): {mae:.6f}")
print(f"总绝对误差 (SAE): {sae:.6f}")
print(f"均方根误差 (RMSE): {rmse:.6f}")

plt.figure(figsize=(10, 5))
plt.plot(ts, label="real")
plt.plot(range(len(ts) - len(prediction), len(ts)), prediction, label="Predicted")
plt.legend()
plt.title("Seq2Seq LSTM Forecast - AirPassengers")
plt.show()

参考文献[1](#1)


  1. https://www.geeksforgeeks.org/machine-learning/seq2seq-model-in-machine-learning/ ↩︎
相关推荐
Coder_Boy_2 小时前
基于SpringAI的在线考试系统-0到1全流程研发:DDD、TDD与CICD协同实践
java·人工智能·spring boot·架构·ddd·tdd
北京耐用通信2 小时前
耐达讯自动化Profibus总线光纤中继器:光伏逆变器通讯的“稳定纽带”
人工智能·物联网·网络协议·自动化·信息与通信
啊阿狸不会拉杆3 小时前
《数字图像处理》第 7 章 - 小波与多分辨率处理
图像处理·人工智能·算法·计算机视觉·数字图像处理
AI即插即用3 小时前
即插即用系列 | CVPR 2025 AmbiSSL:首个注释模糊感知的半监督医学图像分割框架
图像处理·人工智能·深度学习·计算机视觉·视觉检测
数说星榆1813 小时前
脑启发计算与类神经形态芯片的协同
人工智能
m0_650108243 小时前
AD-GS:面向自监督自动驾驶场景的目标感知 B 样条高斯 splatting 技术
论文阅读·人工智能·自动驾驶·基于高斯泼溅的自监督框架·高质量场景渲染
王锋(oxwangfeng)3 小时前
自动驾驶领域OCC标注
人工智能·机器学习·自动驾驶
cxr8283 小时前
从NP-hard到梯度下降:神经-符号架构如何破解因果发现的“计算魔咒”
人工智能·重构·认知框架
老陈聊架构3 小时前
『AI辅助Skill』掌握三大AI设计Skill:前端独立完成产品设计全流程
前端·人工智能·claude·skill