<think>好的,用户现在需要的是一个使用CNN+Transformer架构的完整PyTorch代码,用于预测时间序列流量数据,并评估MAE、MAPE、RMSE指标。之前用户的问题是关于LSTM的实现,现在转向CNN+Transformer,可能是因为他们想尝试不同的模型结构,或者认为结合CNN和Transformer能更好地捕捉时间序列的局部和全局特征。
首先,我需要回顾之前提供的LSTM代码的结构,确保数据预处理部分保持一致,比如滑动窗口的构造、归一化方法。用户的数据是单变量流量数据,所以输入应该是历史窗口的序列,输出是未来3天的预测。接下来,设计CNN+Transformer模型。CNN部分通常用于提取局部特征,比如一维卷积层处理时间序列的局部模式。然后,Transformer的编码器可以捕捉长期依赖关系。需要注意CNN的输出如何与Transformer的输入对接,比如调整维度,添加位置编码等。在模型结构方面,可能需要先通过几个一维卷积层提取特征,然后将其输入到Transformer编码器中。考虑到时间序列的顺序性,位置编码是必要的。此外,Transformer的多头注意力机制能够捕获不同时间步之间的关系,这对预测有帮助。
数据预处理部分需要保持和之前一致,滑动窗口的构造,T=7可能还是合适的,但用户可能需要调整这个参数。另外,数据划分、归一化方法也需要沿用,确保代码的连贯性。训练过程中,损失函数还是用MSE,优化器用Adam。需要调整学习率,可能需要更多的训练轮次,或者添加学习率调度器。评估指标部分已经写好了MAE、MAPE、RMSE的计算,可以直接复用。在代码实现中,需要注意张量的形状转换,比如卷积层的输出可能需要调整维度以适应Transformer的输入要求。此外,Transformer的输入需要加上位置编码,以保留时间顺序信息。可能还需要考虑模型的超参数调整,比如卷积层的数量、滤波器大小,Transformer的层数、头数等。但用户现在需要的是一个完整的示例,所以先提供一个基础版本,再在相关问题中提示超参数调优的方法。
验证部分,用户可能需要划分验证集来监控过拟合,或者在代码中添加交叉验证的步骤。但根据用户需求,目前只需预测最后三天,所以示例代码可能直接使用最后窗口进行预测。最后,确保代码的可运行性,包括数据加载、模型定义、训练循环、预测和指标计算。同时,引用之前提到的参考文献,比如多尺度处理[^2]和注意力机制的应用[^3],来支持模型设计的合理性。需要检查是否有遗漏的步骤,比如数据加载时的批次划分,或者模型训练中的梯度清零。此外,示例数据是随机生成的,需要提醒用户替换为真实数据。可能的问题包括:如何调整CNN和Transformer的结构,如何处理更长的序列,如何集成外部特征等。在相关问题中,可以引导用户进一步探索这些方面,引用之前的文献,如使用注意力机制改进模型[^3]。</think>### 基于PyTorch的CNN+Transformer时间序列预测实现
---
#### 一、模型架构设计
**设计思路**:
1. **CNN模块**:提取局部时间模式特征(如短期波动)
2. **Transformer编码器**:捕捉全局时间依赖关系[^2]
3. **混合架构优势**:CNN的平移不变性 + Transformer的长程建模能力[^3]
**数学表达**:
对于输入序列$X \in \mathbb{R}^{T \times 1}$,模型计算过程为:
$$ \begin{aligned}
H_{\text{CNN}} &= \text{Conv1D}(X) \in \mathbb{R}^{T \times d_{model}} \\
H_{\text{pos}} &= H_{\text{CNN}} + P_{\text{position}} \\
H_{\text{trans}} &= \text{TransformerEncoder}(H_{\text{pos}}) \\
\hat{Y} &= \text{Linear}(H_{\text{trans}}[-S:]) \in \mathbb{R}^{S}
\end{aligned} $$
其中$T$为历史窗口长度,$S$为预测步长
---
#### 二、完整代码实现
##### 1. 数据预处理(与LSTM方案兼容)
```python
import torch
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error, mean_squared_error
# 构造数据(替换为真实数据)
dates = pd.date_range("2022-01-01", "2022-01-28", freq="D")
flow = np.random.randint(100, 500, size=28)
df = pd.DataFrame({"timestamp": dates, "flow": flow})
# 归一化处理
scaler = MinMaxScaler()
df['flow_scaled'] = scaler.fit_transform(df[['flow']])
# 滑动窗口构造
T = 7 # 历史窗口长度
S = 3 # 预测步长
def create_dataset(data, window_size, pred_steps):
X, y = [], []
for i in range(len(data)-window_size-pred_steps+1):
X.append(data[i:i+window_size])
y.append(data[i+window_size:i+window_size+pred_steps])
return np.array(X), np.array(y)
X, y = create_dataset(df['flow_scaled'].values, T, S)
```
##### 2. CNN+Transformer模型定义
```python
class PositionalEncoding(torch.nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-np.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
self.register_buffer('pe', pe)
def forward(self, x):
return x + self.pe[:x.size(1)]
class CNNTransformer(torch.nn.Module):
def __init__(self, input_dim=1, d_model=64, nhead=4, num_layers=3, pred_steps=3):
super().__init__()
# CNN特征提取
self.cnn = torch.nn.Sequential(
torch.nn.Conv1d(input_dim, 32, kernel_size=3, padding=1),
torch.nn.ReLU(),
torch.nn.Conv1d(32, d_model, kernel_size=3, padding=1)
)
# Transformer编码器
self.pos_encoder = PositionalEncoding(d_model)
encoder_layer = torch.nn.TransformerEncoderLayer(d_model, nhead, dim_feedforward=256)
self.transformer_encoder = torch.nn.TransformerEncoder(encoder_layer, num_layers)
# 预测层
self.decoder = torch.nn.Linear(d_model, pred_steps)
def forward(self, x):
# x形状: [batch_size, T, 1]
x = x.permute(0, 2, 1) # 转换为Conv1d需要的格式
cnn_out = self.cnn(x) # [batch_size, d_model, T]
cnn_out = cnn_out.permute(0, 2, 1) # [batch_size, T, d_model]
# Transformer处理
src = self.pos_encoder(cnn_out)
memory = self.transformer_encoder(src)
# 取最后S个时间步预测
return self.decoder(memory[:, -S:, :]).squeeze(-1)
```
##### 3. 训练与评估流程
```python
# 数据集划分
train_size = int(len(X)*0.8)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]
# 转换为PyTorch张量
train_data = torch.utils.data.TensorDataset(
torch.FloatTensor(X_train).unsqueeze(-1),
torch.FloatTensor(y_train)
)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=16, shuffle=True)
# 模型初始化
model = CNNTransformer(pred_steps=S)
optimizer = torch.optim.Adam(model.parameters(), lr=0.0005)
loss_fn = torch.nn.MSELoss()
# 训练循环
for epoch in range(200):
model.train()
for inputs, targets in train_loader:
outputs = model(inputs)
loss = loss_fn(outputs, targets)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 最终预测
model.eval()
with torch.no_grad():
test_input = torch.FloatTensor(X[-1]).unsqueeze(0).unsqueeze(-1)
pred_scaled = model(test_input).numpy().flatten()
pred = scaler.inverse_transform(pred_scaled.reshape(-1, 1)).flatten()
# 指标计算(需替换真实值)
y_true = np.array([320, 310, 300]) # 示例真实值
mae = mean_absolute_error(y_true, pred)
mape = mean_absolute_percentage_error(y_true, pred) * 100
rmse = np.sqrt(mean_squared_error(y_true, pred))
print(f"MAE: {mae:.2f}")
print(f"MAPE: {mape:.2f}%")
print(f"RMSE: {rmse:.2f}")
```
---
#### 三、关键改进说明
1. **多尺度特征融合**
CNN层通过不同卷积核尺寸捕捉局部模式,Transformer编码器建立全局依赖关系,实现多尺度时间建模[^2]
2. **位置编码增强**
使用正弦/余弦位置编码保留时序信息,弥补CNN的平移不变性带来的时序位置信息丢失[^3]
3. **动态注意力机制**
Transformer的自注意力机制可动态调整不同时间步的重要性权重,优于传统RNN的固定模式传播[^2]
---
#### 四、结果可视化示例
```python
import matplotlib.pyplot as plt
plt.figure(figsize=(10,5))
plt.plot(df['timestamp'], df['flow'], 'b-', label='历史流量')
future_dates = pd.date_range("2022-01-29", periods=3, freq="D")
plt.plot(future_dates, y_true, 'go--', label='真实值')
plt.plot(future_dates, pred, 'rs--', label='预测值')
plt.title('CNN-Transformer流量预测结果')
plt.legend()
plt.grid(True)
plt.show()
```
---
###