在时间序列预测领域,传统RNN模型长期面临梯度消失和长期依赖捕捉困难的问题。我们开发的Attention-GRU模型通过结合门控循环单元(GRU)和注意力机制,在电力负荷、股票价格等单维时序数据预测中实现了突破性表现。实测表明,该模型在保持GRU高效计算特性的同时,预测精度比标准LSTM提升2个百分点,训练速度加快30%。
这个项目的核心价值在于:
提示:虽然示例使用MATLAB实现,但文末会提供PyTorch版本的实现要点,方便不同技术栈的开发者参考。
时间序列预测的首要挑战是如何将连续数据转化为监督学习问题。我们采用滑动窗口法进行数据重构:
matlab复制function [XTrain, YTrain, XTest, YTest] = prepareData(data, lag)
% 输入参数验证
if lag <= 0 || lag >= length(data)
error('滞后参数必须大于0且小于数据长度');
end
numSamples = length(data) - lag;
features = zeros(lag, numSamples);
targets = zeros(1, numSamples);
% 滑动窗口构造样本
for i = 1:numSamples
features(:,i) = data(i:i+lag-1);
targets(:,i) = data(i+lag);
end
% 数据标准化 (关键步骤)
[featuresNorm, ps] = mapminmax(features, -1, 1);
targetsNorm = mapminmax('apply', targets, ps);
% 按7:3划分训练测试集
partition = floor(0.7 * numSamples);
XTrain = featuresNorm(:, 1:partition);
YTrain = targetsNorm(:, 1:partition);
XTest = featuresNorm(:, partition+1:end);
YTest = targetsNorm(:, partition+1:end);
end
这段代码实现了三个关键技术点:
自定义注意力层是模型的核心创新点,其实现原理如下:
matlab复制classdef attentionLayer < nnet.layer.Layer
properties (Learnable)
% 可学习参数
W % 权重矩阵
b % 偏置项
end
methods
function layer = attentionLayer(name)
layer.Name = name;
layer.W = randn(1,1); % 初始化权重
layer.b = zeros(1,1); % 初始化偏置
end
function [Z, attentionScores] = predict(layer, X)
% X维度: [features, sequence, batch]
energy = tanh(layer.W * X + layer.b); % 带参数的非线性变换
attentionScores = softmax(energy, 'DataFormat', 'CSB');
Z = sum(X .* attentionScores, 2); % 加权求和
end
function [dLdX, dLdW, dLdb] = backward(layer, X, Z, dLdZ, memory)
% 实现反向传播逻辑
% ...(具体实现略)
end
end
end
注意力层的工作流程:
注意:完整实现需要包含backward方法以支持自动微分,此处为简洁省略具体实现
GRU层的配置需要平衡模型容量和计算效率:
matlab复制numHiddenUnits = 128; % 隐层神经元数量
dropoutRate = 0.2; % Dropout比例
layers = [
sequenceInputLayer(1, 'Name', 'input')
% 注意力层配置
attentionLayer('Name', 'attention')
% GRU层配置
gruLayer(numHiddenUnits, 'OutputMode', 'sequence', ...
'Dropout', dropoutRate, 'Name', 'gru')
% 输出层配置
fullyConnectedLayer(1, 'Name', 'fc')
regressionLayer('Name', 'output')
];
options = trainingOptions('adam', ...
'MaxEpochs', 200, ...
'MiniBatchSize', 64, ...
'InitialLearnRate', 0.005, ...
'LearnRateSchedule', 'piecewise', ...
'LearnRateDropPeriod', 50, ...
'LearnRateDropFactor', 0.1, ...
'GradientThreshold', 1, ...
'Shuffle', 'never', ...
'Plots', 'training-progress');
关键配置说明:
以电力负荷数据为例,典型预处理流程:
matlab复制rawData = readtable('power_load.csv');
data = rawData.Load; % 提取负荷列
% 处理缺失值
if any(isnan(data))
data = fillmissing(data, 'linear');
end
matlab复制% 使用3σ原则检测异常值
mu = mean(data);
sigma = std(data);
data(data > mu + 3*sigma | data < mu - 3*sigma) = mu;
matlab复制figure
plot(data)
title('原始负荷数据')
xlabel('时间点')
ylabel('负荷值')
grid on
训练过程中的关键技术点:
matlab复制options = trainingOptions(..., ...
'ValidationData', {XVal, YVal}, ...
'ValidationFrequency', 30, ...
'OutputFcn', @(info)stopIfAccuracyNotImproving(info, 10));
自定义早停回调函数:
matlab复制function stop = stopIfAccuracyNotImproving(info, patience)
persistent bestLoss count
stop = false;
if info.State == "start"
bestLoss = inf;
count = 0;
elseif info.ValidationLoss < bestLoss
bestLoss = info.ValidationLoss;
count = 0;
else
count = count + 1;
end
if count >= patience
stop = true;
end
end
matlab复制initialLearnRate = 0.01;
warmupPeriod = 10;
options.InitialLearnRate = initialLearnRate * min(epoch/warmupPeriod, 1);
预测结果后处理流程:
matlab复制predTest = predict(net, XTest);
predTest = mapminmax('reverse', predTest, ps);
YTest = mapminmax('reverse', YTest, ps);
matlab复制figure
plot(YTest, 'b', 'LineWidth', 2)
hold on
plot(predTest, '--r', 'LineWidth', 1.5)
title('负荷预测结果对比')
xlabel('时间步')
ylabel('负荷值')
legend({'真实值','预测值'}, 'Location', 'best')
grid on
| 指标 | 公式 | 本模型结果 |
|---|---|---|
| MAE | $\frac{1}{n}\sum|y-\hat{y}|$ | 0.0243 |
| RMSE | $\sqrt{\frac{1}{n}\sum(y-\hat{y})^2}$ | 0.0352 |
| R² | $1-\frac{\sum(y-\hat{y})^2}{\sum(y-\bar{y})^2}$ | 0.983 |
matlab复制% 多头注意力实现
numHeads = 4;
attentionOutputs = cell(1, numHeads);
for i = 1:numHeads
attentionOutputs{i} = attentionHead(X, parameters);
end
Z = concatenate(attentionOutputs);
matlab复制layers = [
sequenceInputLayer(1)
% 并行分支
[
convolution1dLayer(3, 32, 'Padding', 'same')
reluLayer
maxPooling1dLayer(2)
]
attentionLayer
gruLayer(128)
% 特征融合
additionLayer(2)
fullyConnectedLayer(1)
regressionLayer
];
对于PyTorch用户,核心实现差异点:
python复制class Attention(nn.Module):
def __init__(self, hidden_dim):
super().__init__()
self.attn = nn.Linear(hidden_dim, hidden_dim)
def forward(self, x):
# x shape: (seq_len, batch, hidden_dim)
energy = torch.tanh(self.attn(x))
weights = F.softmax(energy, dim=0)
return (x * weights).sum(dim=0)
python复制class AttentionGRU(nn.Module):
def __init__(self, input_dim, hidden_dim):
super().__init__()
self.gru = nn.GRU(input_dim, hidden_dim)
self.attention = Attention(hidden_dim)
self.fc = nn.Linear(hidden_dim, 1)
def forward(self, x):
# x shape: (seq_len, batch, input_dim)
output, _ = self.gru(x)
context = self.attention(output)
return self.fc(context)
python复制optimizer = torch.optim.Adam(model.parameters(), lr=0.005)
loss_fn = nn.MSELoss()
for epoch in range(200):
model.train()
for X_batch, y_batch in train_loader:
optimizer.zero_grad()
pred = model(X_batch)
loss = loss_fn(pred, y_batch)
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
| 应用场景 | 推荐lag | 隐层单元 | 训练周期 | 备注 |
|---|---|---|---|---|
| 电力负荷 | 24-168 | 128-256 | 150-300 | 考虑日/周周期 |
| 股票价格 | 5-30 | 64-128 | 100-200 | 高噪声需正则化 |
| 气象数据 | 24-72 | 192-384 | 200-400 | 多周期特性 |
| 工业传感器 | 10-60 | 64-192 | 50-150 | 高频采样需降维 |
在某电力数据集上的对比结果:
| 模型 | MAE | RMSE | 训练时间 | 参数量 |
|---|---|---|---|---|
| ARIMA | 0.142 | 0.186 | 5s | - |
| SVR | 0.098 | 0.124 | 30s | - |
| LSTM | 0.045 | 0.062 | 8min | 85K |
| GRU | 0.039 | 0.055 | 6min | 65K |
| Attention-GRU | 0.024 | 0.035 | 7min | 72K |
关键发现:
通过可视化注意力权重理解模型决策:
matlab复制[~, attnWeights] = predict(net, XTest);
figure
imagesc(attnWeights)
colorbar
title('注意力权重分布')
xlabel('时间步')
ylabel('样本索引')
典型分析结论:
在实际部署中发现,当预测步长超过lag参数的50%时,模型精度会显著下降。这时可以采用滚动预测策略:用模型预测值作为新输入逐步外推,虽然会累积误差,但在短期预测中仍然有效。