本文部分理论引用了:4 Strategies for Multi-Step Time Series Forecasting
首先在本文中不考虑已知序列的预测,即不使用观测值去进行下一步的预测,这在实际中是没有应用意义的,除非是已知实验数据再去进行一个模型的检验。
第一步:加载数据,并进行数据预处理
load Forcedata_1.mat %加载数据(double型,只有一列数据,时序预测没有实际时间,只有事情发生的顺序)
data=force'; %将数据集转置
%% 序列的前 90% 用于训练,后 10% 用于测试
dataTrain = data(1:1000); %定义训练集
dataTest = data(1001:1100); %该数据是用来在最后与预测值进行对比的
%% 数据预处理,将训练数据标准化为具有零均值和单位方差。
mu = mean(dataTrain); %求均值
sig = std(dataTrain); %求均差
dataTrainStandardized = (dataTrain - mu) / sig; %这里是防止数据发散,获得较好的拟合
LSTM对于数据标准化要求很高。且这里只对训练集进行标准化的原因是,对于测试集我们应该是不知道的。
第二步: 输入的每个时间步,LSTM网络学习预测下一个时间步(这里一个时间步我是0.01秒)的值,这和时间的概念相符合,将前面的为x、后面的为y作为神经网络的映射关系来源。
XTrain = dataTrainStandardized(1:end-1);
YTrain = dataTrainStandardized(2:end);
大多数LSTM相关的资料还是使用默认的lag=1来将时间序列拆分为具有输入和输出分量的样本。我在自己的其他试验中使用不同时间步的滞后来观察(lag=1,2,5,10)。其数目的意义是能规定用于预测下一时间步长y的输入变量的数目。设置不同时间步的目的是观察是否能够改善预测模型性能。但据某些验证表明,增加滞后并不能改善预测模型的性能。
现在即输入[[1],[2],[3],...,[10]]对应着输出[[2],[3],[4],...[11]],lag为2的时候即[[1,2],[2,3],[3,4],...,[9,10]]对应着[[3],[4],[5],...,[11]]。但不论怎样输出对应的都应该是下一个时间步,要与实际结合。
第三步:定义LSTM网络架构
numFeatures = 1; %是时间序列,因此输入一维
numResponses = 1; %输出一维
numHiddenUnits = 93; %创建LSTM回归网络,指定LSTM层的隐含单元个数93
layers = [ ...
sequenceInputLayer(numFeatures) %输入层
lstmLayer(numHiddenUnits) % lstm层,如果是构建多层的LSTM模型,可以修改。
fullyConnectedLayer(numResponses) %为全连接层,是输出的维数。
regressionLayer]; %其计算回归问题的半均方误差模块 。即说明这不是在进行分类问题。
%指定训练选项,求解器设置为adam, 1000轮训练。
%梯度阈值设置为 1。指定初始学习率 0.01,在 125 轮训练后通过乘以因子 0.2 来降低学习率。
options = trainingOptions('adam', ...
'MaxEpochs',1000, ...
'GradientThreshold',1, ...
'InitialLearnRate',0.01, ...
'LearnRateSchedule','piecewise', ...%每当经过一定数量的时期时,学习率就会乘以一个系数。
'LearnRateDropPeriod',125, ... %乘法之间的纪元数由“ LearnRateDropPeriod”控制。
'LearnRateDropFactor',0.2, ... %乘法因子由参“ LearnRateDropFactor”控制,
'Verbose',0, ... %如果将其设置为true,则有关训练进度的信息将被打印到命令窗口中。默认值为true。
'Plots','training-progress'); %构建曲线图 将'training-progress'替换为none
需要提醒的是:在MATLAB中,其已经将LSTM网络进行封装,类似工具箱的形式,因此不再涉及底层的“门”概念。
第四步:训练LSTM网络
net = trainNetwork(XTrain,YTrain,layers,options);
第五步:初始化网络状态
net = predictAndUpdateState(net,XTrain); %将新的XTrain数据用在网络上进行初始化网络状态
[net,YPred] = predictAndUpdateState(net,YTrain(end)); %用训练的最后一步来进行预测第一个预测值
训练的最后一个,即预测的第一个。
第六步:利用LSTM网络进行循环预测(100步)递归多步预测
for i = 2:100 %从第二步开始
[net,YPred(:,i)] = predictAndUpdateState(net,YPred(:,i-1),'ExecutionEnvironment','cpu'); %predictAndUpdateState函数是一次预测一个值并更新网络状态
end
递归多步预测会一直累积误差,以至于使性能迅速下降,但LSTM对比NAR网络独有的是其有这个特殊的函数predictAndUpdateState能够在每次预测时更新网络状态。因此其能够预测更长的序列。需要说明的是,使用预测值或者观测值要根据实际情况去确定。如波浪力是实时变化的,在 实验中能够通过仪器在两次预测之间能够观测到波浪力的值,即0.01s之内并将其作为输入来更新网络状态然后输出下一个预测值的。毕竟根据我的工作来说,只是已知100个数据,要往下一直推8000步数据,只能用观测值来进行更新。
第七步:进行预测结果的对比
YPred = sig*YPred + mu; %使用先前计算的参数对预测去标准化。
rmse = sqrt(mean((YPred-YTest).^2)) ; %计算均方根误差 (RMSE)。
% 利用预测绘制整个时序
figure
subplot(2,1,1)
plot(dataTrain(1:end))
hold on
idx = 1000:(1000+100);
plot(idx,[data(1000) YPred],'.-')
hold off
xlabel("Time")
ylabel("Force")
title("Forecast")
legend(["Observed" "Forecast"])
subplot(2,1,2)
plot(data)
xlabel("Time")
ylabel("Force")
title("Wave Force")
% 将预测值与测试数据进行比较。
figure
subplot(2,1,1)
%指的是在一个2行1列共2个子图的图中,定位第1个图来进行操作。
% plot(dataTest)
hold on %用于添加新绘图的时候保留当前绘图
plot(YPred,'.-') %点变成点画线
hold off %取消hold on功能,即不保留
legend(["Observed" "Predicted"])
ylabel("Force")
title("Wave Force")
subplot(2,1,2)
% stem(YPred - dataTest) %stem函数用于绘制火柴棍图
xlabel("Time")
ylabel("Error")
title("RMSE = " + rmse) %需要说的是RMSE与MSE相比,RMSE对预测误差的惩罚性更大
第八步:重置网络状态
net = resetState(net);
如果需要进行其他的预测,如进行下一轮300个的预测。需要重置一下网络状态。
应用到实际可以发现:
但在预测的时候,不能单从训练集和预测集的数量以及比例去评价这个数据集的好坏,从理论上来说,训练集应包含一个周期的变化,不然肯定无法通过此去进行预测(不管是不是用到之前所述的插值策略,插值策略是根据我的控制理论来决定的)。而且100个数据去预测30个数据这并不意味着用1000个数据去预测后面300个是一样的表现,两者是不相同的。
使预测模型更加精确的方法有:增加预测输入的维度,即加入不同的特征。