深度学习中的 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/ ↩︎
相关推荐
Johny_Zhao2 小时前
OpenClaw安装部署教程
linux·人工智能·ai·云计算·系统运维·openclaw
飞哥数智坊2 小时前
我帮你读《一人公司(OPC)发展研究》
人工智能
冬奇Lab5 小时前
OpenClaw 源码精读(3):Agent 执行引擎——AI 如何「思考」并与真实世界交互?
人工智能·aigc
没事勤琢磨7 小时前
如何让 OpenClaw 控制使用浏览器:让 AI 像真人一样操控你的浏览器
人工智能
用户5191495848457 小时前
CrushFTP 认证绕过漏洞利用工具 (CVE-2024-4040)
人工智能·aigc
牛马摆渡人5288 小时前
OpenClaw实战--Day1: 本地化
人工智能
前端小豆8 小时前
玩转 OpenClaw:打造你的私有 AI 助手网关
人工智能
BugShare8 小时前
写一个你自己的Agent Skills
人工智能·程序员
机器之心8 小时前
英伟达护城河被AI攻破,字节清华CUDA Agent,让人人能搓CUDA内核
人工智能·openai
后端小肥肠9 小时前
公众号躺更神器!OpenClaw+Claude Skill 实现自动读对标 + 写文 + 配图 + 存入草稿箱
人工智能·aigc·agent