import torch import torch.nn as nn import torch.nn.functional as F import numpy as np class moving_avg(nn.Module): """ Moving average block to highlight the trend of time series """ 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): # padding on the both ends of time series 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)) x = x.permute(0, 2, 1) return x class series_decomp(nn.Module): """ Series decomposition block """ 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 class DLinearModel(nn.Module): """ DLinear """ def __init__(self, seq_len, pred_len, individual=True, channels=12,channels_outs=1,kernel_size=25): """ seq_len: 历史输入长度 pred_len: 预测长度 individual: 是一个布尔值(True / False),控制 是否为每个通道单独建一个线性层。 channels: 表示输入序列的 特征数(通道数) """ super(DLinearModel, self).__init__() self.seq_len = seq_len self.pred_len = pred_len # Decompsition Kernel Size self.decompsition = series_decomp(kernel_size) self.individual = individual self.channels = channels if self.individual: self.Linear_Seasonal = nn.ModuleList() self.Linear_Trend = nn.ModuleList() self.Linear_Decoder = 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]) ) self.Linear_Decoder.append(nn.Linear(self.seq_len, self.pred_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_Decoder = 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]) ) self.line = nn.Linear(self.channels,channels_outs) 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 x = x.permute(0, 2, 1) x = self.line(x) # to [Batch, Output length, Channel] B , L , C = x.shape x = x.reshape(B,-1) return x if __name__ == "__main__": B = 32 ecmwf = torch.randn(B, 144, 1) model = DLinearModel(144, 1440) y_hat = model(ecmwf) print("Output shape:", y_hat.shape)