LSTM代码打包

作品简介

代码介绍:https://zhuanlan.zhihu.com/p/700048609

压缩后,运行MyLSTM即可

介绍回顾:


上一篇我们介绍了LSTM的理论框架

卷心菜:LSTM模型(1):理论简介到混沌系统预测0 赞同 · 0 评论文章

不过原理是原理,不会用还是不行。这篇文章教你用Matlab实现一个LSTM来预测混沌系统的时间序列。

数据说明

我们这里用LSTM来拟合一个经典的混沌系统 - Lorenz系统。它由气象学家Edward Lorenz于1963年提出,这个系统由三个简单的非线性微分方程组成,但它们的行为却极其复杂,能够产生不可预测的混沌动态。它也是蝴蝶效应的经典例子。

首先,加载我们的数据是这个系统的 维坐标变化的时间序列,如下图所示

数据导入

% 导入数据
clear; clc; close all
data = load('Lorenz.mat').y(1,:)';
data = data';
figure(); 
plot(data, 'LineWidth',1)
grid on; box on;
xlim([1, length(data)])
xlabel('step'); ylabel('x')

接着,对我们的数据进行切分。将90%的数据用来训练,10%的数据用来测试。

train_num = floor(0.9*numel(data));
data_train = data(1:train_num+1);
data_test = data(train_num+1:end);

数据预处理

对训练数据进行归一化

mu = mean(data_train);
sig = std(data_train);
dataTrainStandardized = (data_train - mu) / sig; 
data_norm = (data - mu) / sig;

从数据中定义特征与标签

XTrain = dataTrainStandardized(1:end-1);
YTrain = dataTrainStandardized(2:end);

训练设置

接下来,是定义你所需要的网络结构了

layers = [
    sequenceInputLayer(1,"Name","input")    % 序列层,表示我们输入的是一维时间序列
    lstmLayer(64,"Name","lstm")             % 将上面的序列传递给LSTM层,这里我们用64个LSTM进行训练
    fullyConnectedLayer(1,"Name","fc")      % 输出层我们用一个全连接,而且输出的维数为1
    regressionLayer];                       % 这里可选标签是用来分类还是回归,因为是连续信号,所以选回归
注:上面的网络结构可以在Matlab自带的网络设计器中打开和设计。设计路径如下

设计完成后,进行导出即可

定义训练参数并开始训练

options = trainingOptions('adam', ...       % 选择 Adam 优化器,可以避免过拟合
    'MaxEpochs',250, ...                    % 最大训练次数为 250'GradientThreshold',1, ...              % 梯度阈值,用于控制梯度的最大范数,超过该值会被裁剪
    'InitialLearnRate',0.005, ...           % 初始学习率为 0.005
    'LearnRateSchedule','piecewise', ...    % 使用分段学习率调度策略
    'LearnRateDropPeriod',125, ...          % 学习率下降周期为 125
    'LearnRateDropFactor',0.2, ...          % 学习率下降因子为 0.2
    'Verbose',0, ...                        % 关闭训练过程中的详细输出信息
    'Plots','training-progress');           % 显示训练进度图

net = trainNetwork(XTrain,YTrain,layers,options);   % 开始训练

此时会弹出一个预测进度框,可以实时看到训练的进度。

预测阶段

训练完成之后,我们训练好的模型存储在net变量中。我们用的预测方法是,先从 t 预测 t+1 的数据,然后将 t+1 的结果带回模型预测 t+2 的数据。

% 测试集数据处理
dataTestStandardized = (data_test - mu) / sig;
XTest = dataTestStandardized(1:end-1);
% 预测
net = predictAndUpdateState(net,XTrain);
[net,YPred] = predictAndUpdateState(net,YTrain(end));   % 先预测下一刻的数据

numTimeStepsTest = numel(XTest);
for i = 2:numTimeStepsTest
    % 不断迭代进行预测
    [net,YPred(:,i)] = predictAndUpdateState(net,YPred(:,i-1),'ExecutionEnvironment','cpu');
end

展示结果与画图

% 训练数据归一化
mu = mean(YTrain);
sig = std(YTrain);
dataTrainStandardized = (YTrain - mu) / sig; 

% 绘图
figure;
plot(dataTrainStandardized(1:end-1))
hold on
idx = train_num:(train_num+numTimeStepsTest);
plot(idx,[data_norm(train_num) YPred],'k.-'),hold on
plot(idx,data_norm(train_num:end-1),'r'),hold on
hold off
xlabel("Step")
ylabel("Value")
legend(["Observed" "Forecast"])

可以看到,在未来的100步时,预测的效果还算不错,但过了这个范围效果就变差了。毕竟每一步的预测都存在误差,而这种迭代的预测会造成误差的快速累加。以至于后期变得完全不一样了


创作时间: