(PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测

news/2025/2/9 8:23:00 标签: pytorch, tcn, lstm, rnn, gru

目录

  • I. 前言
  • II. TCN
  • III. TCN-RNN/LSTM/GRU
    • 3.1 TCN-RNN
    • 3.2 TCN-LSTM
    • 3.3 TCN-GRU
  • IV. 实验结果

I. 前言

前面已经写了一系列有关LSTM时间序列预测的文章:

  1. 深入理解PyTorch中LSTM的输入和输出(从input输入到Linear输出)
  2. PyTorch搭建LSTM实现时间序列预测(负荷预测)
  3. PyTorch中利用LSTMCell搭建多层LSTM实现时间序列预测
  4. PyTorch搭建LSTM实现多变量时间序列预测(负荷预测)
  5. PyTorch搭建双向LSTM实现时间序列预测(负荷预测)
  6. PyTorch搭建LSTM实现多变量多步长时间序列预测(一):直接多输出
  7. PyTorch搭建LSTM实现多变量多步长时间序列预测(二):单步滚动预测
  8. PyTorch搭建LSTM实现多变量多步长时间序列预测(三):多模型单步预测
  9. PyTorch搭建LSTM实现多变量多步长时间序列预测(四):多模型滚动预测
  10. PyTorch搭建LSTM实现多变量多步长时间序列预测(五):seq2seq
  11. PyTorch中实现LSTM多步长时间序列预测的几种方法总结(负荷预测)
  12. PyTorch-LSTM时间序列预测中如何预测真正的未来值
  13. PyTorch搭建LSTM实现多变量输入多变量输出时间序列预测(多任务学习)
  14. PyTorch搭建ANN实现时间序列预测(风速预测)
  15. PyTorch搭建CNN实现时间序列预测(风速预测)
  16. PyTorch搭建CNN-LSTM混合模型实现多变量多步长时间序列预测(负荷预测)
  17. PyTorch搭建Transformer实现多变量多步长时间序列预测(负荷预测)
  18. PyTorch时间序列预测系列文章总结(代码使用方法)
  19. TensorFlow搭建LSTM实现时间序列预测(负荷预测)
  20. TensorFlow搭建LSTM实现多变量时间序列预测(负荷预测)
  21. TensorFlow搭建双向LSTM实现时间序列预测(负荷预测)
  22. TensorFlow搭建LSTM实现多变量多步长时间序列预测(一):直接多输出
  23. TensorFlow搭建LSTM实现多变量多步长时间序列预测(二):单步滚动预测
  24. TensorFlow搭建LSTM实现多变量多步长时间序列预测(三):多模型单步预测
  25. TensorFlow搭建LSTM实现多变量多步长时间序列预测(四):多模型滚动预测
  26. TensorFlow搭建LSTM实现多变量多步长时间序列预测(五):seq2seq
  27. TensorFlow搭建LSTM实现多变量输入多变量输出时间序列预测(多任务学习)
  28. TensorFlow搭建ANN实现时间序列预测(风速预测)
  29. TensorFlow搭建CNN实现时间序列预测(风速预测)
  30. TensorFlow搭建CNN-LSTM混合模型实现多变量多步长时间序列预测(负荷预测)
  31. PyG搭建图神经网络实现多变量输入多变量输出时间序列预测
  32. PyTorch搭建GNN-LSTM和LSTM-GNN模型实现多变量输入多变量输出时间序列预测
  33. PyG Temporal搭建STGCN实现多变量输入多变量输出时间序列预测
  34. 时序预测中Attention机制是否真的有效?盘点LSTM/RNN中24种Attention机制+效果对比
  35. 详解Transformer在时序预测中的Encoder和Decoder过程:以负荷预测为例
  36. (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测

时间卷积网络TCN和CNN都是一种利用卷积操作提取特征的模型,CNN是通过卷积层来提取图像中的特征,而TCN则通过时序卷积层来处理时间序列数据。TCN强调如何使用非常深的网络(residual)和膨胀卷积的组合来扩大感受野进而捕捉更广泛的上下文信息。

有关TCN的原理部分不做过多讲解,原理比较简单,下面直接讲解代码。

II. TCN

class Chomp1d(nn.Module):
    def __init__(self, chomp_size):
        super(Chomp1d, self).__init__()
        self.chomp_size = chomp_size

    def forward(self, x):
        """
        裁剪的模块,裁剪多出来的padding
        """
        return x[:, :, :-self.chomp_size].contiguous()


class TemporalBlock(nn.Module):
    def __init__(self, n_inputs, n_outputs, kernel_size, stride, dilation, padding, dropout=0.2):
        """
        相当于一个Residual block

        :param n_inputs: int, 输入通道数
        :param n_outputs: int, 输出通道数
        :param kernel_size: int, 卷积核尺寸
        :param stride: int, 步长,一般为1
        :param dilation: int, 膨胀系数
        :param padding: int, 填充系数
        :param dropout: float, dropout比率
        """
        super(TemporalBlock, self).__init__()
        self.conv1 = weight_norm(nn.Conv1d(n_inputs, n_outputs, kernel_size,
                                           stride=stride, padding=padding, dilation=dilation))
        # 经过conv1,输出的size其实是(Batch, input_channel, seq_len + padding)
        self.chomp1 = Chomp1d(padding)  # 裁剪掉多出来的padding部分,维持输出时间步为seq_len
        self.relu1 = nn.ReLU()
        self.dropout1 = nn.Dropout(dropout)

        self.conv2 = weight_norm(nn.Conv1d(n_outputs, n_outputs, kernel_size,
                                           stride=stride, padding=padding, dilation=dilation))
        self.chomp2 = Chomp1d(padding)  # 裁剪掉多出来的padding部分,维持输出时间步为seq_len
        self.relu2 = nn.ReLU()
        self.dropout2 = nn.Dropout(dropout)

        self.net = nn.Sequential(self.conv1, self.chomp1, self.relu1, self.dropout1,
                                 self.conv2, self.chomp2, self.relu2, self.dropout2)
        self.downsample = nn.Conv1d(n_inputs, n_outputs, 1) if n_inputs != n_outputs else None
        self.relu = nn.ReLU()
        self.init_weights()

    def init_weights(self):
        """
        参数初始化

        :return:
        """
        self.conv1.weight.data.normal_(0, 0.01)
        self.conv2.weight.data.normal_(0, 0.01)
        if self.downsample is not None:
            self.downsample.weight.data.normal_(0, 0.01)

    def forward(self, x):
        """
        :param x: size of (Batch, input_channel, seq_len)
        :return:
        """
        out = self.net(x)
        res = x if self.downsample is None else self.downsample(x)
        return self.relu(out + res)


class TCN(nn.Module):
    def __init__(self, num_inputs, channels, kernel_size=2, dropout=0.2):
        """
        :param num_inputs: int, 输入通道数
        :param channels: list,每层的hidden_channel数,例如[25,25,25,25]表示有4个隐层,每层hidden_channel数为25
        :param kernel_size: int, 卷积核尺寸
        :param dropout: float, drop_out比率
        """
        super(TCN, self).__init__()
        super().__init__()
        layers = []
        num_levels = len(channels)
        for i in range(num_levels):
            dilation_size = 2 ** i  # 膨胀系数:1,2,4,8……
            in_channels = num_inputs if i == 0 else channels[i - 1]  # 确定每一层的输入通道数
            out_channels = channels[i]  # 确定每一层的输出通道数
            layers += [TemporalBlock(in_channels, out_channels, kernel_size, stride=1, dilation=dilation_size,
                                     padding=(kernel_size - 1) * dilation_size, dropout=dropout)]

        self.network = nn.Sequential(*layers)

    def forward(self, x):
        """
        :param x: size of (Batch, input_channel, seq_len)
        :return: size of (Batch, output_channel, seq_len)
        """
        x = self.network(x)
        return x

可以看到这里TCN输入的尺寸是(batch_size, input_channel, seq_len),输出尺寸是(batch_size, output_channel, seq_len)。这与前面讲的文章大致类似,如果需要直接利用TCN得到输出,可以取输出的最后一个时间步,然后经过一个nn.Linear即可得到预测结果,即:

self.fc = nn.Linear(channels[-1], output_size)
x = x[:, :, -1]
x = self.fc(x)

III. TCN-RNN/LSTM/GRU

TCN的输出尺寸为(batch_size, output_channel, seq_len),这天然满足了RNN类模型的输入要求,因此将时序数据先经过TCN再经过RNN等模型是很自然的想法。

3.1 TCN-RNN

TCN-RNN模型搭建如下:

class TCN_RNN(nn.Module):
    def __init__(self):
        super(TCN_RNN, self).__init__()
        self.tcn = TCN(num_inputs=7, channels=[32, 32, 32])
        self.rnn = nn.RNN(input_size=32, hidden_size=64,
                          num_layers=2, batch_first=True)
        self.fc = nn.Linear(64, 1)

    def forward(self, x):
        x = x.permute(0, 2, 1)  # b i s
        x = self.tcn(x)  # b h s
        x = x.permute(0, 2, 1)  # b s h
        x, _ = self.rnn(x)  # b, s, h
        x = x[:, -1, :]
        x = self.fc(x)  # b output_size
        return x

由于我们构建的输入为(batch_size, seq_len, input_size),而TCN要求的输入为(batch_size, input_channel, seq_len),因此首先需要进行一个permute操作。经过TCN后,输出为(batch_size, output_channel, seq_len),其中output_channelchannels=[32, 32, 32]中最后一个数,即32。

接着RNN的输入应该为(batch_size, seq_len, output_channel),因此还需要经过一个permute。最后利用一个nn.Linear得到这个batch的预测结果。

3.2 TCN-LSTM

相比TCN-RNN,TCN-LSTM只是进行了简单替换:

class TCN_LSTM(nn.Module):
    def __init__(self):
        super(TCN_LSTM, self).__init__()
        self.tcn = TCN(num_inputs=7, channels=[32, 32, 32])
        self.lstm = nn.LSTM(input_size=32, hidden_size=64,
                            num_layers=2, batch_first=True)
        self.fc = nn.Linear(64, 1)

    def forward(self, x):
        x = x.permute(0, 2, 1)  # b i s
        x = self.tcn(x)  # b h s
        x = x.permute(0, 2, 1)  # b s h
        x, _ = self.lstm(x)  # b, s, h
        x = x[:, -1, :]
        x = self.fc(x)  # b output_size
        return x

3.3 TCN-GRU

TCN-GRU类似:

class TCN_GRU(nn.Module):
    def __init__(self):
        super(TCN_GRU, self).__init__()
        self.tcn = TCN(num_inputs=7, channels=[32, 32, 32])
        self.gru = nn.GRU(input_size=32, hidden_size=64,
                          num_layers=2, batch_first=True)
        self.fc = nn.Linear(64, 1)

    def forward(self, x):
        x = x.permute(0, 2, 1)  # b i s
        x = self.tcn(x)  # b h s
        x = x.permute(0, 2, 1)  # b s h
        x, _ = self.gru(x)  # b, s, h
        x = x[:, -1, :]
        x = self.fc(x)  # b output_size
        return x

IV. 实验结果

数据集依然选择前边的负荷预测数据集,前24小时的负荷+其余6个变量,预测未来1小时的负荷。由于TCN耗时较长,这里只使用了前5000条数据。

模型效果比较:

模型TCNTCN-RNNTCN-LSTMTCN-GRU
MAPE / %6.915.607.796.75

可以发现TCN-RNN的效果稍好一点,不过以上结果只针对本实验的数据集,并且没有经过调参,因此不具备太多参考性。


http://www.niftyadmin.cn/n/5308406.html

相关文章

线性代数 --- 为什么LU分解中L矩阵的行列式一定等于正负1?

以下是关于下三角矩阵L的行列式一定等于-1的一些说明 笔者的一些话(写在最前面): 这是一篇小文,是我写的关于求解矩阵行列式的一篇文章中的一部分。之所以把这一段专门提溜出来,是因为这一段相对于原文是可以完全独立的,也是因为我…

计算机毕业设计 SpringBoot的中小型制造企业质量管理系统 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

如何批量自定义视频画面尺寸

在视频制作和编辑过程中,对于视频画面尺寸的调整是一项常见的需求。有时候,为了适应不同的播放平台或满足特定的展示需求,我们需要对视频尺寸进行批量调整。那么,如何实现批量自定义视频画面尺寸呢?本文将为您揭示这一…

【AIGC工具】我找到了使用大模型问答的最短路径!

大家好,我是豆小匠~ 好久没介绍提高效率的工具啦,这次来介绍一个UTools的骚操作,可以极速打开LLM进行提问! 完成后的效果是: 快捷键调出输入框;2. 输入问题;3. 选择模型;4. 回车提…

我用 midjourney 创作的那些好看的图片

下面这些是个人的midjourney v5的关键词,各种类型都有 抽象画 One piece of original artwork from 1998 , in the style of confucian ideology, pop art-inspired collages, recycled material murals, meticulous military scenes, close-up intensity, grocer…

Linux--vim操作

目录 前言 一、vim模式 二、底行模式的操作 三、命令模式的操作 四、替换模式 五、视图模式 六、vim的多文件编译 前言 本文主要讲解了vim的一些使用技巧。帮助我们轻松上手vim。 vim是一种多模式编辑器,通过vim打开文件并对文件进行编辑。 使用vim很简单…

2024苹果Mac电脑免费文件数据恢复软件EasyRecovery

EasyRecovery是一个操作安全、价格便宜、用户自主操作的非破坏性的只读应用程序,它不会往源驱上写任何东西,也不会对源驱做任何改变!EasyRecovery是一个操作安全、价格便宜、用户自主操作的非破坏性的只读应用程序,它不会往源驱上…

Python 面向对象设计

Python是一种面向对象的编程语言,它支持面向对象的设计模式。面向对象设计是一种将问题分解为对象的方法,每个对象有自己的属性和方法。 在Python中,可以使用class关键字定义一个类,类是对象的模板,定义了对象的属性和…