当前位置: 首页 > news >正文

LSTM实战:回归 - 实现交通流预测

回归 - 实现交通流预测

  • LSTM时间序列预测代码详解
    • 1. 导入必要的库
    • 2. 设置随机种子
    • 3. 数据生成函数
    • 4. 创建数据集函数
    • 5. LSTM模型定义
  • LSTM神经网络设计解析
    • 1. 网络结构设计
      • 输入输出维度
      • 层数设计
      • 线性层
    • 2. 前向传播设计
      • 隐藏状态初始化
      • 输出处理
    • 3. 设计哲学
      • 简单性与有效性
      • 针对时间序列的特性
      • 可扩展性
    • 4. 适用场景
    • 5. 可能的改进
    • 6. 主函数
      • 6.1 数据准备
      • 6.2 创建数据集
      • 6.3 数据转换
      • 6.4 模型初始化
      • 6.5 训练循环
      • 6.6 模型评估
      • 6.7 结果可视化
    • 7. 程序入口
    • 总结

LSTM时间序列预测代码详解

我将逐部分解释这个代码,让初学者能够理解每个部分的作用。

1. 导入必要的库

import random
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
  • torch: PyTorch深度学习框架
  • nn: PyTorch的神经网络模块
  • numpy: 数值计算库
  • matplotlib.pyplot: 绘图库
  • MinMaxScaler: 数据归一化工具

2. 设置随机种子

torch.manual_seed(42)
np.random.seed(42)

这确保了每次运行代码时,随机数生成的结果都是一样的,使得实验可重复。

3. 数据生成函数

def generate_data():x = [424, 405, 441, ...]  # 原始数据data = np.array(x, dtype=np.float32)scaler = MinMaxScaler(feature_range=(0, 1))data = scaler.fit_transform(data.reshape(-1, 1)).flatten()return data

这个函数做了三件事:

  1. 定义了一个包含交通流量数据的列表
  2. 将列表转换为NumPy数组
  3. 使用MinMaxScaler将数据归一化到0-1范围(这是为了让神经网络更容易学习)

4. 创建数据集函数

def create_dataset(data, lookback=10):X, y = [], []for i in range(len(data) - lookback):X.append(data[i:i+lookback])y.append(data[i+lookback])return np.array(X), np.array(y)

这个函数将时间序列数据转换为监督学习格式:

  • X: 包含过去lookback个时间点的数据
  • y: 包含下一个时间点的数据(我们要预测的值)

例如,如果lookback=3,那么:

  • X[0] = [data[0], data[1], data[2]]
  • y[0] = data[3]

5. LSTM模型定义

class SimpleLSTM(nn.Module):def __init__(self, input_size=1, hidden_size=50, output_size=1, num_layers=1):super(SimpleLSTM, self).__init__()self.hidden_size = hidden_sizeself.num_layers = num_layersself.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)self.linear = nn.Linear(hidden_size, output_size)def forward(self, x):h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).requires_grad_()c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).requires_grad_()out, (hn, cn) = self.lstm(x, (h0.detach(), c0.detach()))out = self.linear(out[:, -1, :])return out

这个类定义了一个简单的LSTM模型:

  • __init__方法初始化模型结构
    • nn.LSTM: LSTM层,处理序列数据
    • nn.Linear: 线性层,将LSTM输出转换为预测值
  • forward方法定义了数据如何通过模型
    • 初始化隐藏状态h0和细胞状态c0
    • 将输入数据通过LSTM层
    • 取LSTM输出的最后一个时间步,通过线性层得到预测结果

LSTM神经网络设计解析

这个SimpleLSTM类的设计是一个典型的用于时间序列预测的LSTM神经网络架构。下面我将详细解释为什么这样设计:

1. 网络结构设计

def __init__(self, input_size=1, hidden_size=50, output_size=1, num_layers=1):super(SimpleLSTM, self).__init__()self.hidden_size = hidden_sizeself.num_layers = num_layersself.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)self.linear = nn.Linear(hidden_size, output_size)

输入输出维度

  • input_size=1: 每个时间步的输入特征维度为1,因为我们处理的是单变量时间序列(只有一个数值特征)
  • output_size=1: 输出维度为1,因为我们预测的是单个数值(下一个时间步的值)
  • hidden_size=50: 隐藏状态的维度,这是一个超参数,50是一个中等大小的选择,平衡了模型复杂度和计算效率

层数设计

  • num_layers=1: 使用单层LSTM,对于相对简单的时间序列问题,单层通常足够
  • batch_first=True: 使输入张量的形状为(batch_size, sequence_length, input_size),更符合直觉

线性层

  • nn.Linear(hidden_size, output_size): 将LSTM的隐藏状态映射到输出空间
  • 这种设计是因为LSTM输出的是高维隐藏状态,而我们需要的是单个预测值

2. 前向传播设计

def forward(self, x):h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).requires_grad_()c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).requires_grad_()out, (hn, cn) = self.lstm(x, (h0.detach(), c0.detach()))out = self.linear(out[:, -1, :])return out

隐藏状态初始化

  • h0c0初始化为全零张量,这是LSTM的标准初始化方式
  • .requires_grad_()允许这些初始状态在训练过程中被优化(虽然通常效果有限)
  • .detach()确保不会在反向传播时计算这些初始状态的梯度,避免不必要的计算

输出处理

  • out[:, -1, :]: 只取LSTM输出的最后一个时间步
  • 这种设计基于一个假设:对于时间序列预测,最近的信息最重要
  • 对于多步预测,代码中使用递归方式(在predict_multiple_steps函数中)逐步生成预测

3. 设计哲学

简单性与有效性

  • 这个设计遵循"简单但有效"的原则
  • 对于许多时间序列预测问题,简单的LSTM架构已经能提供不错的结果
  • 更复杂的架构(如添加更多层、注意力机制等)可能会提高性能,但也会增加过拟合风险和计算成本

针对时间序列的特性

  • LSTM特别适合处理时间序列数据,因为它能捕捉长期依赖关系
  • 单变量设计简化了问题,专注于时间模式而非特征间的关系

可扩展性

  • 虽然当前设计简单,但架构允许轻松扩展:
    • 可以增加hidden_sizenum_layers来提高模型容量
    • 可以修改为多变量输入(修改input_size
    • 可以添加Dropout层防止过拟合
    • 可以添加额外的全连接层增加非线性

4. 适用场景

这种设计特别适合:

  • 单变量时间序列预测
  • 中等复杂度的模式识别
  • 需要平衡准确性和计算效率的场景
  • 作为更复杂模型的基线

5. 可能的改进

虽然当前设计已经足够用于许多场景,但可以考虑以下改进:

  1. 添加Dropout层防止过拟合
  2. 使用双向LSTM捕捉前后文信息
  3. 添加注意力机制关注重要时间点
  4. 使用更复杂的输出层处理多步预测

总之,这个LSTM设计是一个经典且实用的时间序列预测模型,平衡了复杂性、效果和计算效率,非常适合作为时间序列预测任务的起点。

6. 主函数

6.1 数据准备

data = generate_data()
train_size = int(len(data)*0.8)
train_data = data[:train_size]
test_data = data[train_size:]

将数据分为训练集(80%)和测试集(20%)

6.2 创建数据集

lookback = 24
X_train, y_train = create_dataset(train_data, lookback)
X_test, y_test = create_dataset(test_data, lookback)

使用过去24个时间点的数据预测下一个时间点

6.3 数据转换

X_train = torch.from_numpy(X_train).float().unsqueeze(-1)
y_train = torch.from_numpy(y_train).float().unsqueeze(-1)
X_test = torch.from_numpy(X_test).float().unsqueeze(-1)
y_test = torch.from_numpy(y_test).float().unsqueeze(-1)

将NumPy数组转换为PyTorch张量,并调整形状以适应LSTM输入要求

6.4 模型初始化

model = SimpleLSTM(1, 50, 1, 1)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
  • 创建LSTM模型
  • 使用均方误差损失函数
  • 使用Adam优化器

6.5 训练循环

epochs = 500
train_losses = []for epoch in range(epochs):model.train()optimizer.zero_grad()outputs = model.forward(X_train)loss = criterion(outputs, y_train)loss.backward()optimizer.step()train_losses.append(loss.item())if (epoch + 1) % 10 == 0:print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss.item():.6f}')

训练过程:

  1. 设置模型为训练模式
  2. 清零梯度
  3. 前向传播计算预测值
  4. 计算损失
  5. 反向传播计算梯度
  6. 更新模型参数
  7. 记录损失值

6.6 模型评估

model.eval()
with torch.no_grad():test_outputs = model.forward(X_test)test_loss = criterion(test_outputs, y_test)print(f'Test Loss: {test_loss.item():.6f}')

在测试集上评估模型性能

6.7 结果可视化

plt.figure(figsize=(12, 6))# 绘制训练损失
plt.subplot(4,1,1)
plt.plot(train_losses)
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')# 绘制测试集上的真实值和预测值
plt.subplot(4, 1, 2)
plt.plot(y_test.numpy(), label='True Values')
plt.plot(test_outputs.numpy(), label='Predictions')
plt.title('Test Data: True vs Predicted')
plt.legend()# 绘制所有真实值
true_values1 = np.concatenate([y_train.numpy(), y_test.numpy()])
plt.subplot(4, 1, 3)
plt.plot(true_values1, label='True Values')
plt.title('All True Values')
plt.legend()# 绘制所有真实值和预测值
true_values2 = np.concatenate([y_train.numpy(), test_outputs.numpy()])
plt.subplot(4, 1, 4)
plt.plot(true_values1, label='True Values')
plt.plot(true_values2, label='Predict Values')
plt.title('True vs Predicted (Whole Dataset)')
plt.legend()plt.tight_layout()
plt.show()

创建四个子图:

  1. 训练损失随epoch的变化
  2. 测试集上的真实值和预测值对比
  3. 所有真实值(训练集+测试集)
  4. 所有真实值和预测值对比

7. 程序入口

if __name__ == "__main__":main()

当直接运行此脚本时,执行main函数。

总结

这个代码实现了一个使用LSTM进行时间序列预测的完整流程:

  1. 数据准备和预处理(归一化)
  2. 将时间序列转换为监督学习格式
  3. 定义LSTM模型
  4. 训练模型
  5. 评估模型性能
  6. 可视化结果

LSTM特别适合处理时间序列数据,因为它能够捕捉数据中的长期依赖关系。在这个例子中,模型学习了过去24个时间点的交通流量模式,用来预测下一个时间点的流量。

http://www.xdnf.cn/news/1387531.html

相关文章:

  • 在Windows系统上将Java的.jar文件部署为服务
  • stylelint在项目中使用
  • 构筑沉浸式3D世界:渲染、资源与体验的协同之道
  • 牛客网 DP35 二维前缀和
  • 【算法】链表专题
  • C#连接SQL-Server数据库超详细讲解以及防SQL注入
  • 零基础json入门教程(基于vscode的json配置文件)
  • 序列化和反序列的学习
  • 医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(五)
  • Word - Word 查找文本中的特定内容
  • Redis vs Elasticsearch:核心区别深度解析
  • c++二叉搜索树
  • 在Linux的环境下安装GitLab(保姆级别)
  • Ubuntu下的压缩及解压缩
  • Llama-index学习文档
  • AI驱动万物智联:IOTE 2025深圳展呈现无线通信×智能传感×AI主控技术融合
  • 【Python办公】CSV按列去重工具
  • LangChain实战(三):深入理解Model I/O - Prompts模板
  • 聊聊Prompt Engineering (提示词工程)
  • Rust Web框架Axum学习指南之响应和异常封装
  • websocket建立连接过程
  • AI供应链优化+AI门店排班:蜜雪冰城降本20%、瑞幸提效的AI商业落地实战
  • 港科大开放世界长时域具身导航!LOVON:足式机器人开放词汇目标导航
  • LeetCode Hot 100 Python (1~10)
  • 1 分钟 Maya 动画渲染要多久?5 天还是 5 小时
  • linux系统学习(15.启动管理)
  • 第一百零二章:AI的“未来电影制片厂CEO”:多模态系统落地项目实战(完整 AI 视频创作平台)
  • 极飞科技AI智慧农业实践:3000亩棉田2人管理+产量提15%,精准灌溉与老农操作门槛引讨论
  • 组件的生命周期:`useEffect` 的威力与副作用处理
  • 随机森林的 “Bootstrap 采样” 与 “特征随机选择”:如何避免过拟合?(附分类 / 回归任务实战)