找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

819

积分

0

好友

107

主题
发表于 昨天 23:21 | 查看: 0| 回复: 0

时间序列预测是一个经典且充满挑战的任务,尤其是在业务场景中,数据往往混杂着季节性、节假日效应和促销活动等多种模式。传统的ARIMA模型可能难以捕捉复杂非线性关系,而基于LSTM或Transformer的深度模型则常常面临训练慢、调参难的困扰。

近年来,一种名为TiDE(Time-series Dense Encoder)的模型在长序列时间序列预测领域表现突出。这是Google团队在2023年提出的工作,其核心在于完全由MLP(多层感知机)构成,摒弃了注意力机制和循环结构。它在多个公开数据集上的性能能够媲美甚至超越许多Transformer模型,同时推理速度要快上数倍。

本文将从原理到实践,详细拆解TiDE模型的设计思路,并提供一个可运行的简化版Python实现。

TiDE的核心思想:带记忆的高阶线性回归

你可以将TiDE理解为一个带有“历史记忆”能力的高阶线性回归模型

  • 输入:一段过去的历史序列(例如过去96小时的用电量),以及相关的特征:
    • 历史协变量:如小时、星期几、是否周末、天气等。
    • 未来协变量:未来预测时段(如未来24小时)的已知特征,例如节假日日历。
  • 输出:未来一段时间的目标序列预测值。

TiDE模型结构围绕三个关键组件展开:

  1. 编码器 (Encoder):通过一系列带有残差连接的MLP层,将“历史目标值 + 历史特征”压缩成一个高维向量,这个向量可以看作是整个历史信息的“总结”。
  2. 解码器 (Decoder):使用几层MLP,将编码器输出的历史总结向量,映射为一个代表“未来整体趋势”的隐式表示向量。
  3. 时序解码器 (Temporal Decoder):这是TiDE的精妙之处。对于未来每一个时间点,它将上一步得到的未来隐向量与该时间点对应的未来特征拼接起来,再通过一个MLP输出该单点的预测值。这使得模型在每个未来时间点上都能感知到特定的外部信息(例如“今天是否是节假日”),从而进行精细调整。

此外,TiDE还设计了一条线性残差通路:使用一个简单的线性层直接将历史目标值映射到未来预测值,然后将这个线性预测结果叠加到模型的最终输出上。这保证了模型至少包含了一个基础线性模型的预测能力,增强了模型的鲁棒性。

优势:为何选择TiDE?

与传统时序模型相比,TiDE解决了以下几个痛点:

  • 长序列友好:完全基于MLP,计算复杂度大致与序列长度呈线性关系,避免了Transformer中自注意力机制的O(L²)复杂度爆炸问题。
  • 天然支持协变量:能够灵活地将历史与未来的外部特征融合到每个时间点的预测中,特别适用于受节假日、促销活动、天气预报等因素强烈驱动的业务场景。
  • 实现简单:没有RNN或注意力模块,从代码层面看就是堆叠Linear + ReLU + Dropout + LayerNorm构成的残差块,使用PyTorch可以快速实现。
  • 生态成熟:已有多个开源库提供了TiDE的高质量实现,如Darts的 TiDEModel、PyTorch Forecasting的 TiDE 以及Nixtla的 neuralforecast.models.TiDE,方便直接投入生产使用。

Python实现:构建一个简化版TiDE

下面我们实现一个极简版的TiDE模型。它并非与论文完全一致,但完整复现了其核心思想:

  • Encoder:使用残差块将展平的历史序列编码为一个向量。
  • Decoder:将该向量解码为未来序列的隐表示。
  • Temporal Decoder:融合未来协变量,逐步输出未来预测值。

首先,定义一个基础的残差块:

import torch
import torch.nn as nn
import torch.nn.functional as F

class ResidualBlock(nn.Module):
    def __init__(self, in_dim, hidden_dim, dropout=0.1):
        super().__init__()
        self.fc1 = nn.Linear(in_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, in_dim)
        self.dropout = nn.Dropout(dropout)
        self.norm = nn.LayerNorm(in_dim)

    def forward(self, x):
        # x: (batch, dim)
        residual = x
        out = F.relu(self.fc1(x))
        out = self.dropout(out)
        out = self.fc2(out)
        out = self.dropout(out)
        out = self.norm(out + residual)
        return out

接下来是简化版的TiDE模型。我们假设:

  • 历史窗口长度:input_len
  • 预测长度:output_len
  • 目标序列为单变量,但可附带:
    • 历史协变量维度:hist_cov_dim
    • 未来协变量维度:fut_cov_dim
class MiniTiDE(nn.Module):
    def __init__(
        self,
        input_len: int,
        output_len: int,
        hist_cov_dim: int = 0,
        fut_cov_dim: int = 0,
        hidden_size: int = 128,
        enc_layers: int = 2,
        dec_layers: int = 2,
        temporal_hidden: int = 64,
    ):
        super().__init__()
        self.input_len = input_len
        self.output_len = output_len
        self.hist_feat_dim = 1 + hist_cov_dim  # 1 for target y

        # 编码输入总维度 = input_len * 每个时间点特征数
        enc_input_dim = self.input_len * self.hist_feat_dim

        # Encoder: 把长序列压成一个向量
        self.encoder_in = nn.Linear(enc_input_dim, hidden_size)
        self.encoder_blocks = nn.ModuleList(
            [ResidualBlock(hidden_size, hidden_size * 2) for _ in range(enc_layers)]
        )

        # Decoder: 把 latent 映射到 “output_len * decoder_dim”
        decoder_dim = hidden_size
        self.decoder_in = nn.Linear(hidden_size, decoder_dim)
        self.decoder_blocks = nn.ModuleList(
            [ResidualBlock(decoder_dim, decoder_dim * 2) for _ in range(dec_layers)]
        )

        # Temporal decoder:逐时间步融合未来协变量
        self.temporal_in = nn.Linear(decoder_dim + fut_cov_dim, temporal_hidden)
        self.temporal_out = nn.Linear(temporal_hidden, 1)

        # 线性残差:历史 y -> 未来 y
        self.linear_residual = nn.Linear(self.input_len, self.output_len)

    def forward(self, hist_y, hist_cov=None, fut_cov=None):
        """
        hist_y:   (batch, input_len, 1)
        hist_cov: (batch, input_len, hist_cov_dim) or None
        fut_cov:  (batch, output_len, fut_cov_dim) or None
        """
        B = hist_y.size(0)

        if hist_cov is not None:
            x_hist = torch.cat([hist_y, hist_cov], dim=-1)  # (B, L_in, feat)
        else:
            x_hist = hist_y

        # flatten 成一个大向量
        x_enc = x_hist.reshape(B, -1)          # (B, L_in * feat_dim)
        x_enc = F.relu(self.encoder_in(x_enc)) # (B, hidden)
        for block in self.encoder_blocks:
            x_enc = block(x_enc)

        # decoder 得到每个未来时间点的“共享表示”
        x_dec = F.relu(self.decoder_in(x_enc))  # (B, dec_dim)
        for block in self.decoder_blocks:
            x_dec = block(x_dec)

        # 拓展到每个时间步
        x_dec = x_dec.unsqueeze(1).repeat(1, self.output_len, 1)  # (B, L_out, dec_dim)

        # 融合未来协变量
        if fut_cov is not None:
            td_in = torch.cat([x_dec, fut_cov], dim=-1)
        else:
            td_in = x_dec

        h = F.relu(self.temporal_in(td_in))
        y_hat = self.temporal_out(h).squeeze(-1)  # (B, L_out)

        # 线性残差(只用历史 y)
        linear_part = self.linear_residual(hist_y.squeeze(-1))  # (B, L_out)

        return y_hat + linear_part

训练过程的伪代码非常直观:

model = MiniTiDE(
    input_len=96,
    output_len=24,
    hist_cov_dim=4,   # 比如: 小时、星期几、是否周末、是否节假日
    fut_cov_dim=4,
)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
loss_fn = nn.MSELoss()

for batch in dataloader:  # 你自己提前把窗口切好
    hist_y, hist_cov, fut_cov, target = batch
    pred = model(hist_y, hist_cov, fut_cov)
    loss = loss_fn(pred, target)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

这里的 dataloader 可以基于一个标准的滑动窗口数据集构建:每个样本以过去 input_len 个时间点作为输入,后续 output_len 个时间点作为预测目标,同时准备好对应的历史和未来特征。在实际应用中,你可以直接使用前述的 Darts、neuralforecast 或 PyTorch Forecasting 等库,它们已经封装好了数据预处理、协变量处理等功能。

何时应考虑使用TiDE?

这里有几个TiDE表现可能尤其出色的场景:

  • 长历史窗口 + 长预测范围:例如,用过去30天的数据预测未来14天。这类任务常常让基于Transformer的模型面临显存爆炸的挑战。
  • 受节假日/活动强烈驱动的业务:如果你拥有大量“已知的未来信息”,如大促日程、法定假期、气温预报等,这些都可以作为未来协变量喂给TiDE的Temporal Decoder进行精细调节。
  • 希望避免复杂模型调优:当团队具备一定的Python和PyTorch基础,但对注意力机制、RNN等复杂结构经验有限时,TiDE这种“全MLP”的架构更容易理解、实现和调试。

如果你手头有一条相对干净的业务时间序列数据,不妨先用上面的简化版MiniTiDE跑一个基线模型,再与简单的线性模型、LSTM或Transformer进行效果对比。你很可能会发现,TiDE这种结构清晰明了的MLP堆叠,在时间序列预测任务上具备令人惊喜的竞争力。更多关于人工智能和时序模型的深入讨论,欢迎在云栈社区交流。




上一篇:Node.js 作者 Ryan Dahl 最新观点:AI时代下,软件工程师的职责变迁
下一篇:台积电财报释放明确信号:AI芯片需求强劲,资本开支指引大超预期
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-25 20:35 , Processed in 0.251400 second(s), 43 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表