使用LSTM完成时间序列预测

news2024/11/15 21:09:31

c

在本教程中,我们将介绍一个简单的示例,旨在帮助初学者入门时间序列预测和 PyTorch 的使用。通过这个示例,你可以学习如何使用 LSTMCell 单元来处理时间序列数据。

我们将使用两个 LSTMCell 单元来学习从不同相位开始的正弦波信号。模型在学习了这些正弦波之后,将尝试预测未来的信号值。

使用方法

  1. 生成正弦波信号:

    python generate_sine_wave.py
    
  2. 训练模型:

    python train.py
    

生成正弦波训练数据

在这一步中,我们将生成用于训练的正弦波信号数据。以下是代码及其详细解释:

import numpy as np
import torch

# 设置随机种子,以确保结果的可重复性
np.random.seed(2)

# 定义常数 T、L 和 N
T = 20
L = 1000
N = 100

# 创建一个空的 numpy 数组 x,用于存储生成的序列
x = np.empty((N, L), 'int64')

# 为数组 x 赋值,每行都是一个按顺序排列的整数序列,
# 并加入了一个随机偏移量
x[:] = np.array(range(L)) + np.random.randint(-4 * T, 4 * T, N).reshape(N, 1)

# 对 x 进行正弦变换,以此生成正弦波数据
data = np.sin(x / 1.0 / T).astype('float64')

# 将生成的正弦波数据保存为一种 PyTorch 可读的格式
torch.save(data, open('traindata.pt', 'wb'))
代码解析
  1. 导入库
  • numpy:用于数值计算
  • torch:用于深度学习中的数据处理和模型训练
  1. 设置随机种子
  • 通过 np.random.seed(2) 设置随机种子,以保证每次运行代码时生成相同的随机数,从而使结果可重复。
  1. 定义常量
  • T:周期长度
  • L:每行的序列长度
  • N:生成的样本数量
  1. 生成随机序列
  • 创建一个空的 numpy 数组 x,用于存储生成的整数序列。
  • 对数组 x 进行赋值,每一行是一个按顺序排列的整数序列,加上一个随机的偏移量。偏移量的范围由 np.random.randint(-4 * T, 4 * T, N).reshape(N, 1) 确定。
  1. 生成正弦波数据
  • x 进行正弦变换,生成标准的正弦波数据。np.sin(x / 1.0 / T).astype('float64') 将整数序列转换为浮点数序列,并进行正弦变换。
  1. 保存数据
  • 使用 torch.save 将生成的正弦波数据保存为 traindata.pt,方便后续训练时加载使用。

搭建与训练时间序列预测模型

在本教程中,我们将详细讲解如何使用 PyTorch 搭建一个LSTM模型,进行时间序列预测。以下是代码及其逐行解释,我们将整个过程分为三个部分:模型定义、数据加载与预处理,以及模型训练与预测。

模型定义

首先,我们定义一个 LSTM 模型,该模型包含两个 LSTMCell 层和一个全连接层用于输出。

from __future__ import print_function
import argparse
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

# 定义一个序列模型
class Sequence(nn.Module):
    def __init__(self):
        super(Sequence, self).__init__()
        # 定义两个LSTMCell层和一个全连接层
        self.lstm1 = nn.LSTMCell(1, 51)
        self.lstm2 = nn.LSTMCell(51, 51)
        self.linear = nn.Linear(51, 1)

    # 定义前向传播
    def forward(self, input, future = 0):
        outputs = []
        # 初始化LSTMCell的隐藏状态和细胞状态
        h_t = torch.zeros(input.size(0), 51, dtype=torch.double)
        c_t = torch.zeros(input.size(0), 51, dtype=torch.double)
        h_t2 = torch.zeros(input.size(0), 51, dtype=torch.double)
        c_t2 = torch.zeros(input.size(0), 51, dtype=torch.double)

        # 遍历输入序列
        for input_t in input.split(1, dim=1):
            # 更新LSTMCell的隐藏状态和细胞状态
            h_t, c_t = self.lstm1(input_t, (h_t, c_t))
            h_t2, c_t2 = self.lstm2(h_t, (h_t2, c_t2))
            # 通过全连接层得到输出
            output = self.linear(h_t2)
            outputs += [output]
        # 如果需要预测未来值
        for i in range(future):
            h_t, c_t = self.lstm1(output, (h_t, c_t))
            h_t2, c_t2 = self.lstm2(h_t, (h_t2, c_t2))
            output = self.linear(h_t2)
            outputs += [output]
        # 将输出序列拼接起来
        outputs = torch.cat(outputs, dim=1)
        return outputs

这段代码是一个简单的序列模型,包括两个LSTMCell层和一个全连接层。它接受一个输入序列,通过LSTMCell层和全连接层对输入进行处理,最终输出一个序列。如果需要预测未来值,则可以在forward函数中传入future参数来进行预测。

具体解释如下:

  1. 首先导入必要的库,包括torch、torch.nn等。
  2. 定义了一个名为Sequence的序列模型,继承自nn.Module。
  3. 在初始化函数中,定义了两个LSTMCell层和一个全连接层,分别是lstm1、lstm2和linear。
  4. forward函数用来定义模型的前向传播过程,接受输入input和可选的future参数,返回处理后的输出序列。
  5. 在forward函数中,首先初始化了LSTMCell的隐藏状态和细胞状态h_t、c_t、h_t2、c_t2。
  6. 遍历输入序列input,对每个输入进行处理,更新LSTMCell的隐藏状态和细胞状态,通过全连接层得到输出,并将输出保存在outputs列表中。
  7. 如果future参数大于0,表示需要预测未来值,进入一个for循环,通过当前输出不断更新LSTMCell的状态,并将预测得到的输出保存在outputs中。
  8. 最后将所有输出序列拼接起来,返回最终的输出。

这段代码主要实现了对输入序列的处理和未来值的预测,是一个简单的序列预测模型。


数据加载与预处理

接下来,我们加载生成的训练数据,并构建训练集和测试集。

if __name__ == '__main__':
    # 定义命令行参数
    parser = argparse.ArgumentParser()
    parser.add_argument('--steps', type=int, default=15, help='steps to run')
    opt = parser.parse_args()

    # 设置随机种子
    np.random.seed(0)
    torch.manual_seed(0)

    # 加载数据并构建训练集
    data = torch.load('traindata.pt')
    input = torch.from_numpy(data[3:, :-1])
    target = torch.from_numpy(data[3:, 1:])
    test_input = torch.from_numpy(data[:3, :-1])
    test_target = torch.from_numpy(data[:3, 1:])

这段代码是一个Python脚本的入口点,通常用于定义和设置命令行参数,加载数据,并准备训练数据。让我逐行解释:

  1. if __name__ == '__main__': 这是Python中用来判断是否当前脚本被当做程序入口执行的一种常见方式。如果当前脚本被当做主程序执行,而不是被其他模块导入,这个条件会成立。

  2. parser = argparse.ArgumentParser() 创建了一个命令行参数解析器。

  3. parser.add_argument('--steps', type=int, default=15, help='steps to run') 定义了一个名为steps的命令行参数,指定了参数的类型为整数,默认值为15,以及参数的帮助信息。

  4. opt = parser.parse_args() 解析命令行参数,并将结果存储在opt变量中。

  5. np.random.seed(0)torch.manual_seed(0) 设置了随机数生成器的种子,用于确保实验结果的可复现性。

  6. data = torch.load('traindata.pt') 从名为traindata.pt的文件中加载数据。

  7. input = torch.from_numpy(data[3:, :-1]) 创建了一个PyTorch张量input,用于存储数据中第4列到倒数第2列之间的数据。

  8. target = torch.from_numpy(data[3:, 1:]) 创建了一个PyTorch张量target,用于存储数据中第4列到最后一列之间的数据。

  9. test_input = torch.from_numpy(data[:3, :-1]) 创建了一个PyTorch张量test_input,用于存储数据中第1列到倒数第2列之间的数据,这是用于测试的输入数据。

  10. test_target = torch.from_numpy(data[:3, 1:]) 创建了一个PyTorch张量test_target,用于存储数据中第2列到最后一列之间的数据,这是用于测试的目标数据。

这段代码的主要作用是准备数据,设置随机种子和命令行参数,为后续的数据处理和模型训练做准备。


模型训练与预测

最后,我们进行模型训练并进行预测。

    # 构建模型
    seq = Sequence()
    seq.double()
    criterion = nn.MSELoss()
    # 使用LBFGS作为优化器,因为我们可以将所有数据加载到训练中
    optimizer = optim.LBFGS(seq.parameters(), lr=0.8)
    
    # 开始训练
    for i in range(opt.steps):
        print('STEP: ', i)
        def closure():
            optimizer.zero_grad()
            out = seq(input)
            loss = criterion(out, target)
            print('loss:', loss.item())
            loss.backward()
            return loss
        optimizer.step(closure)
        
        # 开始预测,不需要跟踪梯度
        with torch.no_grad():
            future = 1000
            pred = seq(test_input, future=future)
            loss = criterion(pred[:, :-future], test_target)
            print('test loss:', loss.item())
            y = pred.detach().numpy()
        
        # 绘制结果
        plt.figure(figsize=(30,10))
        plt.title('Predict future values for time sequences\n(Dashlines are predicted values)', fontsize=30)
        plt.xlabel('x', fontsize=20)
        plt.ylabel('y', fontsize=20)
        plt.xticks(fontsize=20)
        plt.yticks(fontsize=20)
        def draw(yi, color):
            plt.plot(np.arange(input.size(1)), yi[:input.size(1)], color, linewidth = 2.0)
            plt.plot(np.arange(input.size(1), input.size(1) + future), yi[input.size(1):], color + ':', linewidth = 2.0)
        draw(y[0], 'r')
        draw(y[1], 'g')
        draw(y[2], 'b')
        plt.savefig('predict%d.pdf'%i)
        plt.close()

这段代码是一个简单的 PyTorch 深度学习模型训练和预测的示例。让我为您解释一下代码的主要部分:

  1. 首先,代码创建了一个名为 “seq” 的序列模型。然后转换这个模型为双精度数据类型。接着定义了均方误差损失函数 “criterion”。LBFGS 作为优化器,学习速率为 0.8。

  2. 在训练过程中,通过一个循环来进行多次优化迭代。在每次迭代中,通过闭包函数 “closure()” 来计算损失并执行反向传播,然后优化器根据损失进行参数更新。

  3. 接着,使用 torch.no_grad() 上下文管理器来禁止跟踪梯度,开始进行预测。在这里,预测未来的 1000 个时间步。然后计算预测结果和测试目标之间的损失,并打印损失值。

  4. 接下来是绘制结果的部分。代码会使用 matplotlib 库绘制预测的结果图。其中,将实线用于已知的数据部分,虚线用于预测的数据部分。最后,结果图被保存为名为 ‘predict%d.pdf’ 的文件,其中 %d 是迭代的次数。


在GPU上运行

这段代码的默认配置是使用CPU进行训练和预测。如果你想利用GPU加速训练过程,可以通过以下步骤修改代码将模型和数据放到GPU上进行计算。

以下是针对GPU的修改:

  1. 检查是否有可用的GPU:一般使用 torch.cuda.is_available() 来检查是否有可用的GPU。
  2. 将模型和数据移动到GPU:将数据和模型移动到GPU设备上进行计算。

所有涉及到数据和模型的地方都需要做相应的改动,使其可以在GPU上执行。

以下是修改后的代码:

from __future__ import print_function
import argparse
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

# 定义一个序列模型
class Sequence(nn.Module):
    def __init__(self):
        super(Sequence, self).__init__()
        # 定义两个LSTMCell层和一个全连接层
        self.lstm1 = nn.LSTMCell(1, 51)
        self.lstm2 = nn.LSTMCell(51, 51)
        self.linear = nn.Linear(51, 1)

    # 定义前向传播
    def forward(self, input, future = 0):
        outputs = []
        # 初始化LSTMCell的隐藏状态和细胞状态
        h_t = torch.zeros(input.size(0), 51, dtype=input.dtype, device=input.device)
        c_t = torch.zeros(input.size(0), 51, dtype=input.dtype, device=input.device)
        h_t2 = torch.zeros(input.size(0), 51, dtype=input.dtype, device=input.device)
        c_t2 = torch.zeros(input.size(0), 51, dtype=input.dtype, device=input.device)

        # 遍历输入序列
        for input_t in input.split(1, dim=1):
            # 更新LSTMCell的隐藏状态和细胞状态
            h_t, c_t = self.lstm1(input_t, (h_t, c_t))
            h_t2, c_t2 = self.lstm2(h_t, (h_t2, c_t2))
            # 通过全连接层得到输出
            output = self.linear(h_t2)
            outputs += [output]
        # 如果需要预测未来值
        for i in range(future):
            h_t, c_t = self.lstm1(output, (h_t, c_t))
            h_t2, c_t2 = self.lstm2(h_t, (h_t2, c_t2))
            output = self.linear(h_t2)
            outputs += [output]
        # 将输出序列拼接起来
        outputs = torch.cat(outputs, dim=1)
        return outputs


if __name__ == '__main__':
    # 定义命令行参数
    parser = argparse.ArgumentParser()
    parser.add_argument('--steps', type=int, default=15, help='steps to run')
    opt = parser.parse_args()

    # 设置随机种子
    np.random.seed(0)
    torch.manual_seed(0)

    # 检查是否有可用的GPU
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    # 加载数据并构建训练集
    data = torch.load('traindata.pt')
    input = torch.from_numpy(data[3:, :-1]).double().to(device)
    target = torch.from_numpy(data[3:, 1:]).double().to(device)
    test_input = torch.from_numpy(data[:3, :-1]).double().to(device)
    test_target = torch.from_numpy(data[:3, 1:]).double().to(device)

    # 构建模型
    seq = Sequence().double().to(device)
    criterion = nn.MSELoss()
    # 使用LBFGS作为优化器,因为我们可以将所有数据加载到训练中
    optimizer = optim.LBFGS(seq.parameters(), lr=0.8)
    
    # 开始训练
    for i in range(opt.steps):
        print('STEP: ', i)
        def closure():
            optimizer.zero_grad()
            out = seq(input)
            loss = criterion(out, target)
            print('loss:', loss.item())
            loss.backward()
            return loss
        optimizer.step(closure)
        
        # 开始预测,不需要跟踪梯度
        with torch.no_grad():
            future = 1000
            pred = seq(test_input, future=future)
            loss = criterion(pred[:, :-future], test_target)
            print('test loss:', loss.item())
            y = pred.detach().cpu().numpy()  # 将结果移回CPU以便绘图
        
        # 绘制结果
        plt.figure(figsize=(30,10))
        plt.title('Predict future values for time sequences\n(Dashlines are predicted values)', fontsize=30)
        plt.xlabel('x', fontsize=20)
        plt.ylabel('y', fontsize=20)
        plt.xticks(fontsize=20)
        plt.yticks(fontsize=20)
        def draw(yi, color):
            plt.plot(np.arange(input.size(1)), yi[:input.size(1)], color, linewidth = 2.0)
            plt.plot(np.arange(input.size(1), input.size(1) + future), yi[input.size(1):], color + ':', linewidth = 2.0)
        draw(y[0], 'r')
        draw(y[1], 'g')
        draw(y[2], 'b')
        plt.savefig('predict%d.pdf'%i)
        plt.close()

修改解释

  1. 检查GPU:

    • 使用 torch.cuda.is_available() 检查是否有可用的GPU。如果有,将 device 设置为 cuda,否则为 cpu
  2. 数据和模型移到GPU:

    • 使用 .to(device) 方法将数据和模型移到指定设备(CPU或GPU)。
    • 初始化隐藏状态和细胞状态时,指定相应的设备 device 和数据类型 dtype
  3. 绘图前将数据移回CPU:

    • 由于 matplotlib 需要在 CPU 上的 numpy 数组,因此在绘图前将预测数据移回 CPU,并调用 .detach().cpu().numpy()

通过这些修改,你可以利用GPU来加速模型训练过程。当然,前提是你的计算机上配备了兼容的GPU。如果没有,代码将自动退回到使用CPU进行训练。

可以看到GPU明显上升

在这里插入图片描述

结果

STEP:  0
loss: 0.5023738122475573
loss: 0.4985663937943564
loss: 0.479011960611529
loss: 0.44633490214842303
loss: 0.35406310257493023
loss: 0.2050701661768143
loss: 1.3960531561166554
loss: 0.03249441148471743
...
test loss: 6.382565835674331e-06
STEP:  13
loss: 3.76246839739177e-06
test loss: 6.382565835674331e-06
STEP:  14
loss: 3.76246839739177e-06
test loss: 6.382565835674331e-06

得到图像:

第1次训练后:

在这里插入图片描述

第5次训练后:

在这里插入图片描述

第10次:

在这里插入图片描述

第15次:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1943073.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑马程序员2024最新SpringCloud微服务开发与实战 个人学习心得、踩坑、与bug记录Day4 重置版 全网最全最快

你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客 这是我的 github https://github.com/Qiuner ⭐️ gitee https://gitee.com/Qiuner 🌹 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 😄 (^ ~ ^) 想看更多 那就点个关注吧 我会…

python3.10.4——windows环境安装

python下载官网:https://www.python.org/downloads/ 如果安装在C盘,需要右键→选择“以管理员身份运行” 勾选2个按钮,选择自定义安装 全部选择,点击Next 更改安装路径 命令行检查python是否安装成功: 出现版本号说明…

通俗地理解主动元数据管理

元数据管理,是企业开展数据管理的核心基础,内容涉及元数据的创建,确定需要捕获哪些元数据,通过哪些工具和流程进行创建,继而将元数据妥善存储,保障安全性和可访问性,并不断更新维护,…

vue3前端开发-小兔鲜项目-使用逻辑函数拆分业务模块

vue3前端开发-小兔鲜项目-使用逻辑函数拆分业务模块!其实就是把一些单独的业务代码组成一个js文件。抽离出去后,方便后面的维护。 如图,在一级分类下面新建一个文件夹。composables里面新建2个js文件。 分别封装之前的分类,和ban…

Electron 企业级开发通信与本地存储实用解决方案

背景 之前写了一篇Electron通信的方式,讲述了一下三者之间的通信机制,比较恶心,后来发现有个electron/remote, Electron 渲染进程直接调用主进程的API库electron/remote引用讲解-CSDN博客文章浏览阅读58次。remote是个老库&…

将mars3D导入自己的项目中

文章目录 Mars3D官方文档 一、打开自己的vite项目二、创建场景配置文件1.json文件路径 public\config\config.json2.创建组件定义文件路径 src\components\mars-work\mars-map.vue三、demo中引入四、必要样式 依赖文件 总结 Mars3D官方文档 一、打开自己的vite项目 我创建了一…

【Linux】安装Nacos-单机版

一、摘要 单机模式又称单例模式, 拥有所有Nacos的功能及特性,具有极易部署、快速启动等优点。但无法与其他节点组成集群,无法在节点或网络故障时提供高可用能力。单机模式同样可以使用内置Derby数据库(默认)和外置数据库进行存储…

爆赞!终于有大佬把网络安全零基础入门教程给讲明白了!

网络安全的一个通用定义指网络信息系统的硬件、软件及其系统中的数据受到保护,不因偶然的或者恶意的破坏、更改、泄露,系统能连续、可靠、正常地运行,服务不中断。网络安全简单的说是在网络环境下能够识别和消除不安全因素的能力。 网络安全…

探索智能本质:技术智能的演进趋势

在人工智能的浪潮中,我们常常被各种技术术语和概念所包围,但智能的本质究竟是什么?香港大学计算与数据科学学院院长马毅教授,在第三届「知乎 AI 先行者沙龙」上的演讲,为我们提供了全新视角。香港大学马毅:…

vue3 vite 引入包报错 无法找到模块“lib-flexible/flexible.js”的声明文件

文章目录 vue3 vite 引入包报错解决方法 1解决方法 2 vue3 vite 引入包报错 无法找到模块“lib-flexible/flexible.js”的声明文件。“d:/mine/web面试/video-demo/node_modules/lib-flexible/flexible.js”隐式拥有 "any" 类型。尝试使用 npm i --save-dev types…

《昇思25天学习打卡营第23天 | 基于MobileNetv2的垃圾分类》

《昇思25天学习打卡营第23天 | 基于MobileNetv2的垃圾分类》 目录 《昇思25天学习打卡营第23天 | 基于MobileNetv2的垃圾分类》1、实验目的2、MobileNetv2模型原理介绍3、实验环境4、数据处理4.1数据准备下载data_en数据集 4.2数据加载将模块导入,具体如下&#xff…

k8s 公共服务

修改named.conf。修改第13行和第21行 下面是 named.rfc1912 修改位置,在最后 所以用cp -p 复制文件,保留权限 nslookup 回车,server是看哪个dns 在起作用 dns服务器要配置给所有公共服务节点和 k8s 节点 就在网络文件加个DNS2就行了&…

c++ - set、map、multiset、multimap容器介绍和常用接口使用

文章目录 前言一、set容器二、multiset三、map四、multimap 前言 1、set、map、 multiset、 multimap都是基于红黑树实现的容器。 2、set、multiset都使用头文件#include<set>,map、multimap都是使用头文件#include<map> 一、set容器 1、set容器的介绍 C标准库中的…

S02. 中断与异常处理

中断处理过程及保护 1、 中断处理过程 处理器根据中断向量号定位中断门描述符。中断向量号是中断描述符的索引&#xff0c;当处理器收到一个外部中断向量号后&#xff0c;它用此向量号在中断描述符表中查询对应的中断描述符&#xff0c;然后再去执行该中断描述符中的中断处理程…

谷歌浏览器跨域

首先创建谷歌浏览器快捷键 右击 属性 在C盘创建文件夹MyChromeDevUserData “C:\Program Files\Google\Chrome\Application\chrome.exe” --disable-web-security --user-data-dirC:\MyChromeDevUserData

数据结构初阶(C语言)-二叉树-顺序表建堆

一&#xff0c;堆的概念与结构 如果有⼀个关键码的集合&#xff0c;把它的所有元素按完全⼆叉树的顺序存储方式存储&#xff0c;在⼀个⼀维数组中&#xff0c;并满足&#xff1a;&#xff0c;i0,1,2...则称为小堆(或⼤堆)。将根结点最大的堆叫做最大堆或大根堆&#xff0c;根结…

调度子系统在特定时间执行

时序逻辑调度器设计模式允许您安排Simulink子系统在指定时间执行。以下模型说明了这种设计模式。 时序逻辑调度器图表包含以下逻辑&#xff1a; 时序逻辑调度器的关键行为 时序逻辑调度器图表包含两个状态&#xff0c;它们以不同的速率调度函数调用子系统A1、A2和A3的执行&…

frp反向代理的安装与配置、ftp服务的搭建及应用

1、frp简介 frp 是⼀个开源、简洁易⽤、⾼性能的内⽹穿透和反向代理软件&#xff0c;⽀持 tcp, udp, http, https等 协议。frp 项⽬官⽹是 https://github.com/fatedier/frp 2、frp⼯作原理 服务端运⾏&#xff0c;监听⼀个主端⼝&#xff0c;等待客户端的连接&#xff1b; …

基于内容的音乐推荐网站/基于ssm的音乐推荐系统/基于协同过滤推荐的音乐网站/基于vue的音乐平台

获取源码联系方式请查看文末&#x1f345; 摘 要 随着信息化时代的到来&#xff0c;系统管理都趋向于智能化、系统化&#xff0c;音乐推荐网站也不例外&#xff0c;但目前国内的有些公司仍然都使用人工管理&#xff0c;公司规模越来越大&#xff0c;同时信息量也越来越庞大&…

C++中const关键字的多方面应用

const 的基本作用 const有且只有一种作用&#xff0c;那就是限定被修饰的对象无法被修改&#xff0c;在c中&#xff0c;被const修饰的对象被看作常量&#xff0c;存储在只读存储区(.rodata)。 测试代码 const int a 5;char arr[a];对测试代码进行汇编编译 – gcc -S test.c …