PyTorch D-Linear模型输出形状与目标不匹配的解决方案

DDD
发布: 2025-11-01 13:31:13
原创
678人浏览过

PyTorch D-Linear模型输出形状与目标不匹配的解决方案

本文深入探讨了pytorch d-linear模型在时间序列预测中常见的输出形状与目标数据不匹配问题。当模型设计为输出多通道预测(例如,每个输入特征对预测的贡献)而目标仅为单通道时,会导致损失计算错误。教程详细分析了d-linear模型架构、数据准备过程,并提供了通过对模型输出的通道维度进行求和来对齐形状的解决方案,确保模型能够正确训练。

D-Linear模型架构概述

D-Linear模型是一种为时间序列预测设计的深度学习架构,其核心思想是将时间序列分解为趋势(Trend)和季节性(Seasonal)两部分,然后分别对这两部分进行线性预测。模型通过series_decomp模块实现序列分解,该模块内部使用moving_avg来提取趋势成分。

moving_avg模块: 该模块通过一维平均池化(nn.AvgPool1d)计算滑动平均,以捕捉时间序列的趋势。为了处理边界效应,它会在序列两端进行填充。

class moving_avg(nn.Module):
    def __init__(self, kernel_size, stride):
        super(moving_avg, self).__init__()
        self.kernel_size = kernel_size
        self.avg = nn.AvgPool1d(kernel_size=kernel_size, stride=stride, padding=0)

    def forward(self, x):
        # 填充以处理边界
        front = x[:, 0:1, :].repeat(1, (self.kernel_size - 1) // 2, 1)
        end = x[:, -1:, :].repeat(1, (self.kernel_size - 1) // 2, 1)
        x = torch.cat([front, x, end], dim=1)
        x = self.avg(x.permute(0, 2, 1)) # Permute for AvgPool1d
        x = x.permute(0, 2, 1) # Permute back
        return x
登录后复制

series_decomp模块: 此模块利用moving_avg来分解时间序列。它将原始序列减去滑动平均得到季节性残差(res),并将滑动平均作为趋势成分(moving_mean)。

class series_decomp(nn.Module):
    def __init__(self, kernel_size):
        super(series_decomp, self).__init__()
        self.moving_avg = moving_avg(kernel_size, stride=1)

    def forward(self, x):
        moving_mean = self.moving_avg(x)
        res = x - moving_mean
        return res, moving_mean
登录后复制

Model模块(D-Linear核心): D-Linear模型接收输入序列长度seq_len、预测长度pred_len、输入通道数enc_in等参数。它首先通过series_decomp将输入x分解为季节性seasonal_init和趋势trend_init。然后,这两部分会被permute调整维度以适应后续的线性层处理。

模型的关键在于其处理多通道输入的方式。当individual参数为True时,模型会为每个输入通道(self.channels,即enc_in)创建独立的线性层Linear_Seasonal和Linear_Trend。这意味着模型会为每个输入特征(通道)分别学习其季节性和趋势的预测。

最终,模型将每个通道的季节性预测和趋势预测相加,并通过permute操作将输出维度调整为[Batch, Output length, Channel],即[批次大小, 预测步长, 输入通道数]。

class Model(nn.Module):
    def __init__(self, seq_len, pred_len, individual, enc_in, kernel_size = 25):
        super(Model, self).__init__()
        self.seq_len = seq_len
        self.pred_len = pred_len
        self.kernel_size = kernel_size
        self.decompsition = series_decomp(self.kernel_size)
        self.individual = individual
        self.channels = enc_in # enc_in 定义了输入通道数,也决定了输出通道数

        if self.individual:
            self.Linear_Seasonal = nn.ModuleList()
            self.Linear_Trend = nn.ModuleList()
            for i in range(self.channels):
                self.Linear_Seasonal.append(nn.Linear(self.seq_len,self.pred_len))
                self.Linear_Seasonal[i].weight = nn.Parameter((1/self.seq_len)*torch.ones([self.pred_len,self.seq_len]))
                self.Linear_Trend.append(nn.Linear(self.seq_len,self.pred_len))
                self.Linear_Trend[i].weight = nn.Parameter((1/self.seq_len)*torch.ones([self.pred_len,self.seq_len]))
        else:
            self.Linear_Seasonal = nn.Linear(self.seq_len,self.pred_len)
            self.Linear_Trend = nn.Linear(self.seq_len,self.pred_len)
            self.Linear_Seasonal.weight = nn.Parameter((1/self.seq_len)*torch.ones([self.pred_len,self.seq_len]))
            self.Linear_Trend.weight = nn.Parameter((1/self.seq_len)*torch.ones([self.pred_len,self.seq_len]))

    def forward(self, x):
        # x: [Batch, Input length, Channel]
        seasonal_init, trend_init = self.decompsition(x)
        seasonal_init, trend_init = seasonal_init.permute(0,2,1), trend_init.permute(0,2,1)
        if self.individual:
            seasonal_output = torch.zeros([seasonal_init.size(0),seasonal_init.size(1),self.pred_len],dtype=seasonal_init.dtype).to(seasonal_init.device)
            trend_output = torch.zeros([trend_init.size(0),trend_init.size(1),self.pred_len],dtype=trend_init.dtype).to(trend_init.device)
            for i in range(self.channels):
                seasonal_output[:,i,:] = self.Linear_Seasonal[i](seasonal_init[:,i,:])
                trend_output[:,i,:] = self.Linear_Trend[i](trend_init[:,i,:])
        else:
            seasonal_output = self.Linear_Seasonal(seasonal_init)
            trend_output = self.Linear_Trend(trend_init)

        x = seasonal_output + trend_output
        return x.permute(0,2,1) # 最终输出形状为 [Batch, Output length, Channel]
登录后复制

数据准备与模型初始化

在时间序列预测任务中,数据需要被转换为序列-目标对。假设原始数据包含多个特征,我们通常会使用过去多个时间步的特征和目标值来预测未来多个时间步的目标值。

以下代码展示了如何从Pandas DataFrame创建序列数据,并进行数据标准化、拆分以及转换为PyTorch TensorDataset和DataLoader。

Rose.ai
Rose.ai

一个云数据平台,帮助用户发现、可视化数据

Rose.ai 74
查看详情 Rose.ai
import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import torch.optim as optim

# 创建示例数据
df = pd.DataFrame(np.random.randint(0,100,size=(1000, 5)), columns=list('ABCDE'))

# 参数设置
seq_len = 12  # 输入序列长度
pred_len = 3  # 预测序列长度
kernel_size = 5 # 移动平均核大小
batch_size = 4
individual = True # D-Linear模型参数

# 目标列
target_column = 'A'

# 创建序列数据函数
def create_sequence(data, seq_len, pred_len):
    sequences = []
    targets = []
    for i in range(len(data) - seq_len - pred_len + 1):
        sequence = data.iloc[i:i + seq_len].values # 提取所有特征作为输入序列
        target = data.iloc[i + seq_len:i + seq_len + pred_len][target_column].values # 仅提取目标列作为预测目标
        sequences.append(sequence)
        targets.append(target)
    return np.array(sequences), np.array(targets)

sequences, targets = create_sequence(df, seq_len, pred_len)

# 数据拆分与标准化
train_data, test_data, train_target, test_target = train_test_split(sequences, targets, test_size = 0.25, random_state = 42)
train_data, val_data, train_target, val_target = train_test_split(train_data, train_target, test_size = 0.33, random_state = 42)

scaler = StandardScaler()
# 对多维数据进行标准化时,需要 reshape 成 (样本数 * 序列长度, 特征数)
train_data = scaler.fit_transform(train_data.reshape(-1, train_data.shape[-1])).reshape(train_data.shape)
val_data = scaler.transform(val_data.reshape(-1, val_data.shape[-1])).reshape(val_data.shape)
test_data = scaler.transform(test_data.reshape(-1, test_data.shape[-1])).reshape(test_data.shape)

# 转换为 PyTorch Tensor
train_data_tensor = torch.Tensor(train_data)
train_target_tensor = torch.Tensor(train_target)
val_data_tensor = torch.Tensor(val_data)
val_target_tensor = torch.Tensor(val_target)
test_data_tensor = torch.Tensor(test_data)
test_target_tensor = torch.Tensor(test_target)

# 创建 DataLoader
train_dataset = TensorDataset(train_data_tensor, train_target_tensor)
train_loader = DataLoader(train_dataset, batch_size = batch_size, shuffle = True)

# 模型初始化
# enc_in 设置为原始 DataFrame 的列数,即输入特征的数量
model = Model(seq_len = seq_len, pred_len = pred_len, individual = individual, enc_in = df.shape[1], kernel_size = kernel_size)
optimizer = optim.Adam(model.parameters(), lr = 0.001)
criterion = nn.MSELoss()
num_epoch = 30
登录后复制

PyTorch模型输出形状不匹配问题分析

在上述设置中,D-Linear模型的enc_in参数被设置为df.shape[1],即5。这意味着模型被设计为处理5个输入通道,并且在forward方法中,最终输出的形状将是[Batch, pred_len, Channel],例如[4, 3, 5]。这表示对于批次中的每个样本,模型会为未来3个时间步的每个“通道”(原始的5个特征)提供一个预测值。

然而,create_sequence函数在提取目标值时,仅针对target_column(即'A')进行,因此targets的形状是[Batch, pred_len],例如[4, 3]。

当尝试使用criterion(outputs, targets)计算损失时,PyTorch的MSELoss期望输入和目标具有兼容的形状。在这种情况下,outputs是`[4, 3

以上就是PyTorch D-Linear模型输出形状与目标不匹配的解决方案的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号