社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  机器学习算法

【深度学习】突破LSTM,CNN和LSTM时间序列预测 !!

机器学习初学者 • 1 周前 • 44 次点击  

在很多的时间序列预测任务中,利用卷积神经网络(CNN)和长短期记忆网络(LSTM)的混合模型是目前常见的深度学习解决方案之一。

CNN和LSTM各自有不同的特长,CNN擅长局部模式的捕捉,LSTM擅长捕捉序列的长依赖关系。通过混合这两种网络,可以非常好地学习时间序列数据中的复杂模式。

核心原理

  • CNN 部分:CNN 的优势在于能够从输入数据中提取局部特征。对于时间序列预测问题,时间序列可以看作一维数据序列。CNN 可以通过一维卷积操作提取时间序列的局部时间依赖模式,如趋势、周期性波动等局部特征。

  • LSTM 部分:LSTM 是一种递归神经网络(RNN)的变体,专门设计用来解决长期依赖问题。通过记忆门控机制(如输入门、遗忘门、输出门),LSTM 能够很好地捕捉序列中长期的时间依赖关系,并对未来的值进行预测。

混合模型首先使用 CNN 提取局部特征,然后将这些特征输入到 LSTM 中,进一步捕捉时间序列的长时间依赖模式,从而提高预测精度。

模型架构

  1. 输入层:输入的时间序列数据可以是一个  的矩阵, 其中  表示时间序列的长度, 表示特征的维度。

  2. 卷积层(CNN):通过一维卷积层(1D Convolutional Layer)来提取局部特征。假设卷积核的大小为 ,则卷积操作可以表示为:

其中, 表示卷积操作, 是卷积核, 是偏置, 是激活函数,通常选择 ReLU 或者 Sigmoid。卷积层会提取局部时间窗口内的模式,如趋势或短期波动。

  1. 池化层(可选):通过池化层(Pooling Layer)减少特征的空间尺寸。常用的池化方式包括最大池化(Max Pooling)和平均池化(Average Pooling),以便于减少模型的计算量和防止过拟合。

  2. LSTM 层:将经过 CNN 处理后的特征序列输入到 LSTM 层,捕捉长期依赖。LSTM 的输入是卷积层的输出特征序列。LSTM 的计算公式如下:

其中, 是遗忘门, 是输入门, 是输出门, 是记忆单元的状态, 是当前的隐藏状态, 和  分别是权重矩阵和偏置项。LSTM 层能够有效捕捉输入序列中长期的依赖关系。

  1. 全连接层:LSTM 输出的隐藏状态  被传递给全连接层,生成最终的预测值。全连接层的输出公式为:

其中, 和  分别是全连接层的权重和偏置项, 是预测的结果。

  1. 损失函数:常见的时间序列预测任务的损失函数为均方误差(MSE):

其中, 是样本数, 是实际值, 是预测值。

公式解释

  • 卷积操作:在时间序列中,1D 卷积可以理解为将卷积核应用到连续的时间点上,提取时间窗口内的特征。公式中的  表示当前时间点  及其前后  个时间点的数据。通过这种方式,CNN 能够在不丧失时间顺序的情况下提取局部模式。

  • LSTM 结构:LSTM 是通过门控机制来控制信息的流动。遗忘门  控制哪些历史信息需要保留,输入门 控制哪些新信息需要加入,输出门  决定了当前时刻的隐藏状态输出。记忆单元  记录着每个时刻的重要信息,从而保留了时间序列的长期依赖性。

优势

  • 局部和全局模式捕捉:CNN 擅长在短期窗口内捕捉局部时间模式(例如季节性波动),而 LSTM 擅长捕捉长期依赖。结合二者可以更好地应对复杂的时间序列预测任务。

  • 降维与特征提取:CNN 提取特征的同时减少数据的维度,减少了输入 LSTM 的信息量,避免了 LSTM 因输入序列过长导致的效率问题。

总的来说,CNN 和 LSTM 的混合模型结合了卷积网络和递归网络的优势,可以在时间序列预测任务中更好地捕捉短期与长期的特征,从而提升预测精度。

一个完整案例

CNN-LSTM 混合模型在时间序列预测中的应用。

时间序列预测是机器学习中的一大热门课题,特别是在金融、气象、能源消耗预测等领域。CNN(卷积神经网络)和 LSTM(长短期记忆网络)分别在特征提取和捕获时间依赖性方面各有独特优势,因此将这两者结合起来构建混合模型,能够有效提升预测性能。

本案例将介绍如何基于 PyTorch 实现一个 CNN-LSTM 混合模型用于时间序列预测,并展示相关的可视化图形,帮助大家直观理解模型表现。

CNN-LSTM 的混合模型结合了 CNN 提取局部特征的能力和 LSTM 学习时序依赖的能力。该模型首先通过 CNN 层对输入的时间序列进行卷积,提取高层次特征,然后将这些特征输入到 LSTM 中,用于捕获时间上的长期依赖,最后通过全连接层输出预测结果。

模型的结构如下图所示:

输入数据 -> CNN层(卷积+池化) -> LSTM层 -> 全连接层 -> 输出预测

这种结构能够有效提取数据的局部特征(CNN)和时间依赖关系(LSTM),在时间序列预测任务中具有优势。

数据准备

我们使用虚拟数据集来模拟时间序列预测任务。假设数据是某种周期性波动和随机噪声叠加的时间序列。

import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.preprocessing import MinMaxScaler
from torch.utils.data import DataLoader, TensorDataset

# 生成虚拟时间序列数据
np.random.seed(42)
time = np.arange(010000.1)
data = np.sin(0.05 * time) + np.sin(0.01 * time) + 0.5 * np.random.randn(len(time))

# 标准化数据
scaler = MinMaxScaler(feature_range=(-11))
data = scaler.fit_transform(data.reshape(-11)).reshape(-1)

# 准备时间序列数据集 (输入序列, 预测值)
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data[i:(i+seq_length)]
        y = data[i+seq_length]
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

seq_length = 50  # 时间序列长度
X, y = create_sequences(data, seq_length)

# 转换为 PyTorch 张量
X = torch.from_numpy(X).float()
y = torch.from_numpy(y).float()

# 划分训练集和测试集
train_size = int(len(X) * 0.8)
train_X, test_X = X[:train_size], X[train_size:]
train_y, test_y = y[:train_size], y[train_size:]

train_dataset = TensorDataset(train_X, train_y)
test_dataset = TensorDataset(test_X, test_y)

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)

构建 CNN-LSTM 模型

接下来,定义 CNN-LSTM 模型。我们将使用 1D 卷积层提取时间序列的局部特征,接着将这些特征输入到 LSTM 层进行时序预测。

class CNN_LSTM_Model(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers):
        super(CNN_LSTM_Model, self).__init__()
        
        # 1D卷积层
        self.cnn = nn.Sequential(
            nn.Conv1d(in_channels=1, out_channels=32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2)
        )
        
        # LSTM层
        self.lstm = nn.LSTM(input_size=32, hidden_size=hidden_size, num_layers=num_layers, batch_first=True)
        
        # 全连接层
        self.fc = nn.Linear(hidden_size, 1)
        
    def forward(self, x):
        x = x.unsqueeze(1)  # 增加一个通道维度, 因为1D卷积需要 (batch, channel, seq_len)
        x = self.cnn(x)
        x = x.permute(021)  # 调整维度 (batch, seq_len, features)
        lstm_out, _ = self.lstm(x)
        out = self.fc(lstm_out[:, -1, :])  # 取最后时间步的输出
        return out

模型训练

我们使用均方误差(MSE)作为损失函数,Adam 作为优化器。训练过程将会迭代多个 epoch,并在训练和测试集上记录损失,以便后续可视化分析。

# 超参数
input_size = 32
hidden_size = 64
num_layers = 2
num_epochs = 100
learning_rate = 0.001

# 模型实例化
model = CNN_LSTM_Model(input_size, hidden_size, num_layers)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

# 训练模型
train_losses = []
test_losses = []

for epoch in range(num_epochs):
    model.train()
    train_loss = 0
    for batch_X, batch_y in train_loader:
        optimizer.zero_grad()
        outputs = model(batch_X)
        loss = criterion(outputs.squeeze(), batch_y)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()
    
    train_losses.append(train_loss / len(train_loader))
    
    # 测试模型
    model.eval()
    test_loss = 0
    with torch.no_grad():
        for batch_X, batch_y in test_loader:
            outputs = model(batch_X)
            loss = criterion(outputs.squeeze(), batch_y)
            test_loss += loss.item()
    
    test_losses.append(test_loss / len(test_loader))
    
    if (epoch+1) % 10 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {train_loss/len(train_loader):.4f}, Test Loss: {test_loss/len(test_loader):.4f}')

预测和可视化

在模型训练完成后,我们在测试集上进行预测,并绘制预测结果和训练过程中的损失变化图。

# 绘制损失变化曲线
plt.figure(figsize=(106))
plt.plot(train_losses, label='Train Loss')
plt.plot(test_losses, label='Test Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Curve')
plt.legend()
plt.grid(True)
plt.show()

# 测试集上的预测曲线
model.eval()
predictions = []
with torch.no_grad():
    for batch_X, _ in test_loader:
        outputs = model(batch_X)
        predictions.append(outputs.detach().numpy())

predictions = np.concatenate(predictions).squeeze()
predictions = scaler.inverse_transform(predictions.reshape(-11)).reshape(-1)
true_values = scaler.inverse_transform(test_y.numpy().reshape(-11)).reshape(-1)

# 绘制预测值与真实值对比
plt.figure(figsize=(106))
plt.plot(true_values, label='True Values', color='b')
plt.plot(predictions, label='Predictions', color='r')
plt.xlabel('Time Steps')
plt.ylabel('Value')
plt.title('Predictions vs True Values')
plt.legend()
plt.grid(True)
plt.show()

损失曲线

第一张图展示了训练过程中模型的损失变化情况,可以观察到训练损失和测试损失逐渐下降的趋势。通过这张图,可以直观地看到模型的收敛情况以及是否出现过拟合。

预测曲线

第二张图展示了模型在测试集上的预测结果与真实值的对比。通过这张图,我们能够直观评估模型在未见过的数据上的表现,观察预测是否准确拟合了数据的趋势。

残差分布

绘制残差(预测值与真实值的差异)的分布图,可以评估模型在预测时的误差特征,是否存在系统性偏差。

# 计算残差
residuals = true_values - predictions

# 绘制残差分布图
plt.figure(figsize=(106))
plt.hist(residuals, bins=50, color='purple', alpha=0.7)
plt.title('Residuals Distribution')
plt.xlabel('Residual')
plt.ylabel('Frequency')
plt.grid(True)
plt.show()

残差序列图

残差序列图可以帮助我们查看误差随时间的变化是否有规律。

# 绘制残差随时间变化图
plt.figure(figsize=(106))
plt.plot(residuals, label='Residuals', color='orange')
plt.xlabel('Time Steps')
plt.ylabel('Residual')
plt.title('Residuals over Time')
plt.grid(True)
plt.show()

模型优化与调参

网络架构优化

  • 卷积核尺寸与数量: 可以尝试不同大小的卷积核(例如 5 或 7),或者增加卷积层数量,提取更深层的局部特征。
  • LSTM层数与隐藏单元数: 增加或减少 LSTM 的层数和每层的隐藏单元数,寻找最佳的时序依赖模式捕获能力。
  • 激活函数: 除了 ReLU,还可以尝试 LeakyReLU 或者 ELU,观察是否能加速收敛或提高模型效果。

正则化与防止过拟合

  • Dropout: 可以在 LSTM 或 CNN 层之间添加 dropout 层,以防止模型过拟合。
  • 早停机制: 在训练时引入 early stopping,根据测试集的损失提前终止训练,避免过度训练导致的过拟合。

超参数调优

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/178363
 
44 次点击