针对Math老师提到的《MATLAB时间序列神经网络训练好以后怎么预测》
MATLAB时间序列神经网络训练好以后怎么预测 – MATLAB中文论坛 (ilovematlab.cn)https://www.ilovematlab.cn/thread-132940-1-1.html通过仔细的探索终于把所有东西都理清楚了,在这里自我记录和分享给大家。另外我需要把我之前的博客全部整理了一下,该删的删。因为一些理论之前是错的,而且也很乱。首先引用一下math老师对于时间序列神经网络预测的方法,分为两种:循环预测和一次性预测。其实这两种方法本质上是一样的。
循环预测的原理即是利用开环网络(open-loop)在循环中进行多步预测。开环网络的特点就是一次只能预测出一个,训练的时候就是用的开环网络。可以发现在训练的时候,其输出的performance都特别的好,误差也很小,因为观测值和预测值是一个个来进行对比的,一步预测结果当然是不错的。至于训练神经网络的代码,利用工具箱或者网上各种教程,特别多了。开环网络如图:
下面是循环预测的代码:每一步只能预测出一个,然后手动编写代码将其输出y(t)添加到输入中,不断更新输入,然后就能实现多步预测(multi-step prediction)。其中force_raw是延迟(delay),即输入延迟,即假设force_raw是10个(在训练中是feedbackDelays = 1:10),那么就是用十个数去预测下一个。其神经网络只能够输出其下一个时间步的值。
for j=1:31
force_prediction(j)=net(force_raw);
force_raw=[force_raw(2:end);force_prediction(j)];
end
需要注意的是,这里的j=1:31,31在这里即指代的是预测范围(prediction horizon),其在MPC(Model Prediciton Control)中会有应用,这个应用在后面一篇文章中会讲到。这个预测范围的大小是有着限定的,即由于该神经网络本身的结构原因(通过上述代码可以更加清晰地发现),其预测误差是不断累积的,然而我们是无法规避这种误差的,除非更换更加适合长序列预测的神经网络,在后面会提到,我会用到一种长短时记忆时序预测网络(LSTM, Long-short term Memory),它能够解决该神经网络在预测上的一些问题。如果想从100个数据预测后面10个,用NAR或者NARx都可以(NARx有额外的一个变量,我的理解是能够用两个变量之间的关系去辅助预测)
一次性预测的原理即是利用了闭环网络(close-loop)。它能够通过nar_net_closed = closeloop(net)这段代码将开环网络变成闭环的。闭环网络的特点是它代替了我们上述编写代码的步骤 ,即它自己在内部就会不断迭代反馈循环,然后能够实现一次性预测。
nar_net_closed = closeloop(net)
[p1,Pi1,Ai1,t1] = preparets(nar_net_closed,{},{},y1);
pred = nar_net_closed(p1,Pi1,Ai1);
我发现其p1、Pi1均为空矩阵(因为nar没有输入矩阵,那么p1和Pi1当然也就不存在),然后p1这个空矩阵的维度直接决定了预测的长度(至于空矩阵为什么还有维度,应该跟计算机里面的线性存储和数据划分方式有关),只有Ai1是有实际意义的,其矩阵的形式为——列长度方面:层与层之间最长延迟向量为长度,因此为30(可以看到图中为1:30)。行长度方面:分为两个部分,蓝色所示部分跟隐藏层层数有关,该部分数值全部为零,该网络只有一个隐藏层,因此只有一个维度,同时每个维度中还内含维度,维度数为每层的节点数因此为20。黑色所示部分为回馈连接层,只有它有数值。Ai1数据结构如下图所示:
回馈层维度中的数值就是延迟向量,经过整理处理后的数值,这些数值就是用于多步预测的初始数据。 因此,如果想利用矩阵最后两个序列做多步预测,那就只要单独在预测时候把最后两个序列提取出来当成nar的目标矩阵再用preparets函数准备就可以了。但是这样的话也需要更改一下p1,因为它决定了预测步数。方式是取前10个数据作为delay,然后将preparets预测结构更改为需要预测的步数,使其直接预测变为5步,如下图代码。当然,训练的时候需要用到整个的数据集,如用30个数据预测后面5个,那么这30个数据需要全部放进训练集里面,在30个里面划分比例。
p1=cell(0,5);
其中5为往后预测的步数。
下面说一下preparets函数的实质:将整组数输入进去之后,它能够将输入矩阵(在nar里面就只有输出矩阵)前delay个数提取出来,训练只包括iter个,前面(假设delay=30)1-30个数会辅助第31个数的拟合,第2-30个数会辅助第32个数的拟合,以此类推,来保证训练效果。
我们会发现闭环虽然可以实现多步预测,但是除了最初的几步,以后的预测其实相当不准。如下图所示;
%% my NAR
clear all;
load('force_PM_LT_interp.mat'); %这里数据是一个8001*2的矩阵,第一列是时间,第二列是波浪力。只有一个量,故采用NAR时间序列神经网络。
force=interp_LT(:,2);
T=tonndata(force,false,false); %输入和输出矩阵须为cell类型的矩阵,且不能用num2cell来转换,如果使用二维cell矩阵,将会被认为是两个输入从而不能训练。
trainFcn = 'trainbr'; %默认的lmh函数训练时间序列效果很差,采用贝叶斯正则化算法
对于大多数问题,推荐使用 Levenberg-Marquardt (trainlm),不过,对于一些含噪小型问题,贝叶斯正则化 (trainbr) 虽然可能需要更长的时间,但会获得更好的解。但是,对于大型问题,推荐使用量化共轭梯度 (trainscg),因为它使用的梯度计算比其他两种算法使用的 Jacobian 矩阵计算更节省内存。由于样本数据特性,此处选择贝叶斯正则化(Bayesian Regularization)。
feedbackDelays = 1:10; %延迟向量1:10,即输出参考了前10个值
以我的经验来看,delay的选取,如果超过10,训练将会变得比较缓慢,所以一般取到10以下。然后这个延迟向量,调用的时候需要和训练的时候保持一致。
hiddenLayerSize = 13;
%这一项可以来调整神经元的个数,这个需要结合performance的误差来结合调整。一般地,靠增加隐层节点数来获得较低的误差,其训练效果要比增加隐层数更容易实现。需要注意的是,与在每一层中添加更多的神经元相比,添加层层数将获得更大的性能提升。因此,不要在一个隐藏层中加入过多的神经元。
net = narnet(feedbackDelays,hiddenLayerSize,'open',trainFcn);
[Xs,Xi,Ai,Ts] = preparets(net,{},{},T);
net.divideParam.trainRatio = 70/100;
net.divideParam.valRatio = 15/100;
net.divideParam.testRatio = 15/100;
%NARX和NAR与普通时间序列的区别就是最后的输出是否反馈到输入.
net = train(net,Xs,Ts,Xi,Ai); 。
%Xs当前矩阵,即被提取之后的输入矩阵,是T去掉前十个数。
%Ts为被提取后的输出矩阵,为T去掉前十个数。 Xs=Ts=90个
%Xi是已知的矩阵(跟延迟向量有关),为T的前十个,即被提取出的矩阵。
%Ai是层间的延迟。
view(net);
save('training_net.mat','net');
这篇文章具体阐释了如何确定神经网络的层数和隐藏层神经元数量。
如何确定神经网络的层数和隐藏层神经元数量 - 知乎 (zhihu.com)
作为训练集本身的数据本身也是十分重要的,因为取的delay(即由几个数据来进行预测下一个)需要包含一个合理的波动趋势(波动幅度)来预测出下一个值,这是符合我们逻辑的,如果100个数都是线性上升,那么不可能去预测到第101个数是下降。但是作为delay,有时候并不能取太大,第一这样预测不准确,第二这样计算量很大、训练的时候很慢。但是我们又想要delay有一个合理的波动趋势,因此就采用插值法,即在训练和预测的两段代码中加入取值间距,即隔10个数来取一个数,再嵌套一个循环。
clear all;
load('force_PM_LT_interp.mat')
load ('training_net.mat');
force=interp_LT(:,2); %相当于前面的force=force(1: N,1);
T=tonndata(force,false,false); %输入和输出矩阵须为cell类型的矩阵,且不能用num2cell来转换,如果使用二维cell矩阵,将会被认为是两个输入从而不能训练。
force_raw=T(1:10); %将force_raw作为延迟向量,取前10个,与训练的时候对应起来。
for j=1:8000 %y1的50个是对照着force里面第11个到8011个,由于force只有8001个,于是后面10个数据是预测得来的。但是有个问题就是误差非常大。
y1(j)=net(T(10),force_raw);