数模国赛冲刺 | 预测类创新算法 TCN-GRU/BiGRU

​预测类创新算法 TCN-GRU/BiGRU

TCN-GRUTCN-BiGRU在时序预测中至关重要,因为它们结合了TCN的长依赖建模能力与GRU/BiGRU的动态序列处理优势。TCN能够通过膨胀卷积捕捉长时间跨度的信息,而GRU和BiGRU则能更有效地处理时间序列中的短期和长期依赖关系。特别是BiGRU还能同时考虑前向和后向的信息流。这种组合不仅提高了模型的预测精度,还增强了处理复杂时序数据的能力,使其在实际应用中表现优异。接下来我们将详细学习这两种算法案例,文末附注代码案例数据

目录

一、TCN算法

(1)算法原理

(2)算法特点

二、TCN-GRU算法

(1)GRU算法

(2)TCN-GRU

(3)python案例

三、TCN-BiGRU算法

(1)BiGRU算法

(2)TCN-BiGRU

(3)Python案例


一、TCN算法

(1)算法原理

TCN(Temporal Convolutional Network,时序卷积神经网络) 模型在一维因果卷积的基础上,结合了膨胀卷积和残差连接,通过并行卷积处理数据,从而在时间步上提取特征。其完整结构如图 1 所示。

图1:时序卷积神经网络基本结构

因果膨胀卷积由因果卷积和膨胀卷积组成,结构如图 2 所示。

图2:因果膨胀卷积

因果卷积由一维卷积组成,其输出基于上一层当前时刻和更早时刻的输入联合生成,是一种严格遵循时间约束的模型,适用于挖掘时序数据的潜在特征。膨胀卷积最初为解决图像领域在采样过程中信息丢失的问题而提出。与传统只关注局部邻域的卷积层不同,膨胀卷积能够在更大的感受野内获取信息,从而更好地捕捉序列中的长期依赖关系。

(2)算法特点

  •  并行计算能力:相比传统的循环神经网络(RNN),TCN通过卷积操作实现高效的并行计算。这使得TCN在大规模数据的训练和推理中更具优势,能够更好地利用现代硬件的并行计算能力。

  •  长依赖关系建模:传统卷积操作在局部感受野内工作,原始TCN因此难以捕捉长序列数据中的长期依赖关系。然而,通过采用深层TCN和膨胀卷积(dilated convolutions)等技术,TCN能够扩大感受野并增加信息传递的距离,从而更有效地捕捉和建模长期依赖关系。

  •  多尺度信息提取:TCN能够通过堆叠多个卷积层提取不同尺度的特征。每个卷积层使用不同大小的卷积核处理输入,从而捕捉序列数据中不同时间范围的特征。这种多尺度信息提取能力使得TCN对序列数据中的局部依赖关系更加敏感。

  •  输入长度灵活性:与传统的RNN相比,TCN在处理变长输入序列时更加灵活。TCN可以处理固定长度和可变长度的序列数据,这种灵活性使得TCN在处理不同长度的序列数据时更加便捷。

  •  适用于各种序列建模任务:得益于TCN的并行计算能力和长依赖关系建模能力,它适用于语音识别、自然语言处理、动作识别、时间序列预测等多种序列建模任务。在某些任务中,TCN的性能可能与传统RNN相当,甚至更优。

二、TCN-GRU算法

(1)GRU算法

GRU(Gated Recurrent Unit)网络是一种循环神经网络(Recurrent Neural Network, RNN)的变体,采用门控机制(gated mechanism)来控制信息流动和记忆。与传统的RNN相比,GRU能够更有效地处理长期依赖(long-term dependencies)问题;同时,相较于LSTM(Long Short-Term Memory),GRU结构更为简单,参数更少,计算也更高效。

(2)TCN-GRU

  •  TCN层:输入时间序列数据,首先通过一系列的因果卷积和扩张卷积操作,提取多尺度的时间特征。由于TCN可以并行计算且具有较大的感受野,因此在处理长时间依赖时,能够比传统的RNN结构更高效。

  •  GRU层:TCN层的输出被传递到GRU层,GRU通过其门机制进一步捕捉数据中的序列依赖性,尤其是处理长程依赖的情况下非常有效。GRU层会根据前面的时间步的信息动态更新其隐藏状态,从而生成最终的时间序列预测结果。

  •  输出层:GRU层的输出经过一个或多个全连接层(可能包含激活函数)后,产生最终的预测结果。

(3)python案例

1)导入库和读取数据
import os  # 导入os模块,用于操作系统功能,比如环境变量
import math  # 导入math模块,提供基本的数学功能
import pandas as pd  # 导入pandas模块,用于数据处理和分析
import openpyxl
import numpy as np  # 导入numpy模块,用于数值计算
import matplotlib.pyplot as plt  # 导入matplotlib.pyplot模块,用于绘图
from math import sqrt  # 从math模块导入sqrt函数,用于计算平方根
from sklearn.preprocessing import MinMaxScaler  # 导入sklearn中的MinMaxScaler,用于特征缩放
from sklearn.preprocessing import StandardScaler  # 导入sklearn中的StandardScaler,用于特征标准化
from sklearn.preprocessing import LabelEncoder  # 导入sklearn中的LabelEncoder,用于标签编码
from sklearn.metrics import mean_squared_error  # 导入sklearn中的mean_squared_error,用于计算均方误差
from tensorflow.keras.layers import *  # 从tensorflow.keras.layers导入所有层,用于构建神经网络
from tensorflow.keras.models import *  # 从tensorflow.keras.models导入所有模型,用于构建和管理模型
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score  # 导入额外的评估指标
from pandas import DataFrame  # 从pandas导入DataFrame,用于创建和操作数据表
from pandas import concat  # 从pandas导入concat函数,用于DataFrame的拼接
import keras.backend as K  # 导入keras的后端接口
from scipy.io import savemat, loadmat  # 从scipy.io导入savemat和loadmat,用于MATLAB文件的读写
from sklearn.neural_network import MLPRegressor  # 从sklearn.neural_network导入MLPRegressor,用于创建多层感知器回归模型
from keras.callbacks import LearningRateScheduler  # 从keras.callbacks导入LearningRateScheduler,用于调整学习率
from tensorflow.keras import Input, Model, Sequential  # 从tensorflow.keras导入Input, Model和Sequential,用于模型构建
import mplcyberpunk
from qbstyles import mpl_style
from tensorflow import keras  # 从tensorflow.keras导入所有层,用于构建神经网络
from tcn.tcn import TCN
from prettytable import PrettyTable  # 可以优美的打印表格结果

# 读取数据集
dataset=pd.read_csv("股票预测.csv",encoding='gb2312')
print(dataset)  # 显示dataset数据

 运行结果

                 发布日期     开盘指数     最高指数     最低指数  成交量(亿股)  成交额(亿元)  涨跌幅(%)  \
0      2022/5/20 0:00  3223.16  3267.41  3220.70    25.14   243.93    0.77   
1      2022/5/19 0:00  3211.60  3265.00  3208.24    25.61   254.88   -0.86   
2      2022/5/18 0:00  3255.59  3280.32  3245.07    21.21   204.77   -0.17   
3      2022/5/17 0:00  3301.25  3301.25  3255.96    26.54   268.17   -1.14   
4      2022/5/16 0:00  3241.51  3311.60  3221.92    30.75   301.98    3.09   
...               ...      ...      ...      ...      ...      ...     ...   
5401    2000/1/7 0:00  1076.06  1117.71  1058.83     0.65     8.75    3.80   
5402    2000/1/6 0:00  1025.38  1070.79  1011.00     0.30     4.21    3.58   
5403    2000/1/5 0:00  1035.47  1057.85  1013.07     0.25     2.90    0.12   
5404    2000/1/4 0:00  1001.98  1035.36   985.47     0.14     1.83    2.77   
5405  1999/12/30 0:00  1000.00  1000.00  1000.00     0.10     1.24    0.00   

         收盘指数  
0     3259.34  
1     3234.45  
2     3262.52  
3     3267.97  
4     3305.64  
...       ...  
5401  1106.19  
5402  1065.67  
5403  1028.87  
5404  1027.66  
5405  1000.00  

[5406 rows x 8 columns]
2)数据预处理与特征工程
# 单输入单步预测,就让values等于某一列数据,n_out = 1,n_in, num_samples, scroll_window 根据自己情况来
# 单输入多步预测,就让values等于某一列数据,n_out > 1,n_in, num_samples, scroll_window 根据自己情况来
# 多输入单步预测,就让values等于多列数据,n_out = 1,n_in, num_samples, scroll_window 根据自己情况来
# 多输入多步预测,就让values等于多列数据,n_out > 1,n_in, num_samples, scroll_window 根据自己情况来
values = dataset.values[:, 1:]  # 只取第2列数据,要写成1:2;只取第3列数据,要写成2:3,取第2列之后(包含第二列)的所有数据,写成 1:
# 从dataset DataFrame中提取数据。
# dataset.values将DataFrame转换为numpy数组。
# [:,1:]表示选择所有行(:)和从第二列到最后一列(1:)的数据。
# 这样做通常是为了去除第一列,这在第一列是索引或不需要的数据时很常见。

# 确保所有数据是浮动的
values = values.astype('float32')
# 将values数组中的数据类型转换为float32。
# 这通常用于确保数据类型的一致性,特别是在准备输入到神经网络模型中时。

# 构造数据,这个函数可以实现单输入单输出,单输入多输出,多输入单输出,和多输入多输出
def data_collation(data, n_in, n_out, or_dim, scroll_window, num_samples):
    res = np.zeros((num_samples, n_in * or_dim + n_out))
    for i in range(0, num_samples):
        h1 = values[scroll_window * i: n_in + scroll_window * i, 0: or_dim]
        h2 = h1.reshape(1, n_in * or_dim)
        h3 = values[n_in + scroll_window * i: n_in + scroll_window * i + n_out, -1].T
        h4 = h3[np.newaxis, :]
        h5 = np.hstack((h2, h4))
        res[i, :] = h5
    return res

# 举例设置参数
n_in = 5  # 输入前5行的数据
n_out = 1  # 预测未来1个时刻的数据
or_dim = values.shape[1]  # 记录特征数据维度
num_samples = 2000  # 可以设定从数据中取出多少个点用于本次网络的训练与测试。
scroll_window = 1  # 如果等于1,下一个数据从第二行开始取。如果等于2,下一个数据从第三行开始取
res = data_collation(values, n_in, n_out, or_dim, scroll_window, num_samples)
3)数据集划分与归一化
# 把数据集分为训练集和测试集
values = np.array(res)
# 将前面处理好的DataFrame(data)转换成numpy数组,方便后续的数据操作。

n_train_number = int(num_samples * 0.85)
# 计算训练集的大小。
# 设置85%作为训练集
# int(...) 确保得到的训练集大小是一个整数。
# 先划分数据集,在进行归一化,这才是正确的做法!
Xtrain = values[:n_train_number, :n_in * or_dim]
Ytrain = values[:n_train_number, n_in * or_dim:]

Xtest = values[n_train_number:, :n_in * or_dim]
Ytest = values[n_train_number:, n_in * or_dim:]

# 对训练集和测试集进行归一化
m_in = MinMaxScaler()
vp_train = m_in.fit_transform(Xtrain)  # 注意fit_transform() 和 transform()的区别
vp_test = m_in.transform(Xtest)  # 注意fit_transform() 和 transform()的区别

m_out = MinMaxScaler()
vt_train = m_out.fit_transform(Ytrain)  # 注意fit_transform() 和 transform()的区别
vt_test = m_out.transform(Ytest)  # 注意fit_transform() 和 transform()的区别

# 重塑训练集和测试集
vp_train = vp_train.reshape((vp_train.shape[0], n_in, or_dim))
# 将训练集的输入数据vp_train重塑成三维格式。
# 结果是一个三维数组,其形状为[样本数量, 时间步长, 特征数量]。

vp_test = vp_test.reshape((vp_test.shape[0], n_in, or_dim))
# 将训练集的输入数据vp_test重塑成三维格式。
# 结果是一个三维数组,其形状为[样本数量, 时间步长, 特征数量]。
4)构建TCN-GRU模型
def tcn_gru_model():
    # 定义一个函数来创建TCN-GRU模型

    inputs = Input(shape=(vp_train.shape[1], vp_train.shape[2]))
    # 创建模型的输入层

    model = Sequential()
    model.add(inputs)
    model.add(TCN(nb_filters=10, kernel_size=2, dilations=[1, 2, 4], return_sequences=True))
    model.add(GRU(units=128, return_sequences=False))
    model.add(Dense(10))
    model.add(LeakyReLU(alpha=0.3))
    model.add(Dense(vt_train.shape[1], activation='linear'))
    
    # 配置和训练
    model.compile(optimizer='Adam', loss='mse', metrics='mae')
    model.summary()

    return model
    # 返回构建的模型。

model = tcn_gru_model()
# 调用tcn_gru_model函数来建立TCN-GRU模型。

history = model.fit(vp_train, vt_train, batch_size=32, epochs=60, validation_split=0.25, verbose=2)
# 训练模型。指定批处理大小为32,训练轮数为60,将25%的数据用作验证集。
# verbose=2表示在训练过程中会输出详细信息。

# 绘制历史数据
plt.plot(history.history['loss'], label='train')
# 绘制训练过程中的损失曲线。
# history.history['loss']获取训练集上每个epoch的损失值。
# 'label='train''设置该曲线的标签为'train'。

plt.plot(history.history['val_loss'], label='test')
# 绘制验证过程中的损失曲线。
# history.history['val_loss']获取验证集上每个epoch的损失值。
# 'label='test''设置该曲线的标签为'test'。
plt.legend()
# 显示图例,方便识别每条曲线代表的数据集。
plt.show()
# 展示绘制的图像。

图:TCN-GRU损失曲线图

5)结果预测与评估
# 作出预测
yhat = model.predict(vp_test)
# 使用模型对测试集的输入特征(vp_test)进行预测。
# yhat是模型预测的输出值。

predicted_data = m_out.inverse_transform(yhat)  # 反归一化

def mape(y_true, y_pred):
    # 定义一个计算平均绝对百分比误差(MAPE)的函数。
    record = []
    for index in range(len(y_true)):
        # 遍历实际值和预测值。
        temp_mape = np.abs((y_pred[index] - y_true[index]) / y_true[index])
        # 计算单个预测的MAPE。
        record.append(temp_mape)
        # 将MAPE添加到记录列表中。
    return np.mean(record) * 100
    # 返回所有记录的平均值,乘以100得到百分比。

def evaluate_forecasts(Ytest, predicted_data, n_out):
    # 定义一个函数来评估预测的性能。
    mse_dic = []
    rmse_dic = []
    mae_dic = []
    mape_dic = []
    r2_dic = []
    # 初始化存储各个评估指标的字典。
    table = PrettyTable(['测试集指标','MSE', 'RMSE', 'MAE', 'MAPE','R2'])
    for i in range(n_out):
        # 遍历每一个预测步长。每一列代表一步预测,现在是在求每步预测的指标
        actual = [float(row[i]) for row in Ytest]  #一列列提取
        # 从测试集中提取实际值。
        predicted = [float(row[i]) for row in predicted_data]
        # 从预测结果中提取预测值。
        mse = mean_squared_error(actual, predicted)
        # 计算均方误差(MSE)。
        mse_dic.append(mse)
        rmse = sqrt(mean_squared_error(actual, predicted))
        # 计算均方根误差(RMSE)。
        rmse_dic.append(rmse)
        mae = mean_absolute_error(actual, predicted)
        # 计算平均绝对误差(MAE)。
        mae_dic.append(mae)
        MApe = mape(actual, predicted)
        # 计算平均绝对百分比误差(MAPE)。
        mape_dic.append(MApe)
        r2 = r2_score(actual, predicted)
        # 计算R平方值(R2)。
        r2_dic.append(r2)
        if n_out == 1:
            strr = '预测结果指标:'
        else:
            strr = '第'+ str(i + 1)+'步预测结果指标:'
        table.add_row([strr, mse, rmse, mae, str(MApe)+'%', str(r2*100)+'%'])

    return mse_dic,rmse_dic, mae_dic, mape_dic, r2_dic, table
    # 返回包含所有评估指标的字典。

mse_dic,rmse_dic, mae_dic, mape_dic, r2_dic, table = evaluate_forecasts(Ytest, predicted_data, n_out)
# 调用evaluate_forecasts函数。
# 传递实际值(Ytest)、预测值(predicted_data)以及预测的步数(n_out)作为参数。
# 此函数将计算每个预测步长的MSE、RMSE、MAE、MAPE和R2值。

print(table)  # 显示预测指标数值
测试集指标MSERMSEMAEMAPER2
预测结果指标:26948.54164.160117.7986.607%85.07%
6)结果可视化
# %%
## 画结果图
from matplotlib import rcParams

config = {
    "font.family": 'serif',
    "font.size": 10,  # 相当于小四大小
    "mathtext.fontset": 'stix',  # matplotlib渲染数学字体时使用的字体,和Times New Roman差别不大
    "font.serif": ['Times New Roman'],  # Times New Roman
    'axes.unicode_minus': False  # 处理负号,即-号
}
rcParams.update(config)

plt.ion()
for ii in range(n_out):

    plt.rcParams['axes.unicode_minus'] = False
    # 设置matplotlib的配置,用来正常显示负号。
    # 使用赛博朋克风样式
    plt.style.use('cyberpunk')
    # 创建一个图形对象,并设置大小为10x2英寸,分辨率为300dpi。
    plt.figure(figsize=(10, 2), dpi=300)
    x = range(1, len(predicted_data) + 1)
    # 创建x轴的值,从1到实际值列表的长度。
    plt.xticks(x[::int((len(predicted_data)+1))])
    # 设置x轴的刻度,每几个点显示一个刻度。
    plt.tick_params(labelsize=5)  # 改变刻度字体大小
    # 设置刻度标签的字体大小。
    plt.plot(x, predicted_data[:, ii], linestyle="--", linewidth=0.5, label='predict')
    # 绘制预测值的折线图,线型为虚线,线宽为0.5,标签为'predict'。

    plt.plot(x, Ytest[:, ii], linestyle="-", linewidth=0.5, label='Real')
    # 绘制实际值的折线图,线型为直线,线宽为0.5,标签为'Real'。

    plt.rcParams.update({'font.size': 5})  # 改变图例里面的字体大小
    # 更新图例的字体大小。

    plt.legend(loc='upper right', frameon=False)
    # 显示图例,位置在图形的右上角,没有边框。

    plt.xlabel("Sample points", fontsize=5)
    # 设置x轴标签为"样本点",字体大小为5。

    plt.ylabel("value", fontsize=5)
    # 设置y轴标签为"值",字体大小为5。
    if n_out == 1:  # 如果是单步预测
        plt.title(f"The prediction result of TCN-GRU :\nMAPE: {mape(Ytest[:, ii], predicted_data[:, ii])} %")
    else:
        plt.title(f"{ii+1} step of TCN-GRU prediction\nMAPE: {mape(Ytest[:,ii], predicted_data[:,ii])} %")
    # plt.xlim(xmin=600, xmax=700)  # 显示600-1000的值   局部放大有利于观察
    # 如果需要,可以取消注释这行代码,以局部放大显示600到700之间的值。

    # plt.savefig('figure/预测结果图.png')
    # 如果需要,可以取消注释这行代码,以将图形保存为PNG文件。

plt.ioff()  # 关闭交互模式
plt.show()
# 显示图形。

图:TCN-GRU预测结果图

三、TCN-BiGRU算法

(1)BiGRU算法

GRU 是沿单一方向传输信息的,它仅获取当前时间之前的历史信息,从而忽略了未来信息,可能导致丢失重要信息。为了解决这一问题,提出了 BiGRU 网络,其结构如图 3 所示。BiGRU 由一个前向 GRU 隐藏层和一个后向 GRU 隐藏层组成,能够同时捕获先前和未来的信息特征,充分利用序列信息以便更好地进行特征提取。

这两个隐藏层都与输出层相连接,分别在时间维度上对输入序列进行前向和后向计算,得到两个不同的隐藏层状态。随后,这两个状态向量相加,生成最终的输出。同时,每个时间步的输出也连接到最终的输出层。

(2)TCN-BiGRU

该模型充分利用了 TCN 在局部特征提取和捕捉时序数据中远距离依赖关系的能力,以及 BiGRU 在全局上下文理解和双向信息流处理上的优势,从而更全面地捕捉时序数据中的相关信息,同时更好地建模时序数据中的长期依赖关系,最终实现提高预测精度的目标。

(3)Python案例

1)导入库和读取数据
# 调用相关库
import os  # 导入os模块,用于操作系统功能,比如环境变量
import math  # 导入math模块,提供基本的数学功能
import pandas as pd  # 导入pandas模块,用于数据处理和分析
import openpyxl
from math import sqrt  # 从math模块导入sqrt函数,用于计算平方根
from numpy import concatenate  # 从numpy模块导入concatenate函数,用于数组拼接
import matplotlib.pyplot as plt  # 导入matplotlib.pyplot模块,用于绘图
import numpy as np  # 导入numpy模块,用于数值计算
import tensorflow as tf  # 导入tensorflow模块,用于深度学习
from sklearn.preprocessing import MinMaxScaler  # 导入sklearn中的MinMaxScaler,用于特征缩放
from sklearn.preprocessing import StandardScaler  # 导入sklearn中的StandardScaler,用于特征标准化
from sklearn.preprocessing import LabelEncoder  # 导入sklearn中的LabelEncoder,用于标签编码
from sklearn.metrics import mean_squared_error  # 导入sklearn中的mean_squared_error,用于计算均方误差
from tensorflow.keras.layers import *  # 从tensorflow.keras.layers导入所有层,用于构建神经网络
from tensorflow.keras.models import *  # 从tensorflow.keras.layers导入所有层,用于构建神经网络
from tensorflow.keras.models import *  # 从tensorflow.keras.models导入所有模型,用于构建和管理模型
from sklearn.metrics import mean_squared_error, mean_absolute_error,r2_score  # 导入额外的评估指标
from pandas import DataFrame  # 从pandas导入DataFrame,用于创建和操作数据表
from pandas import concat  # 从pandas导入concat函数,用于DataFrame的拼接
import keras.backend as K  # 导入keras的后端接口
from scipy.io import savemat, loadmat  # 从scipy.io导入savemat和loadmat,用于MATLAB文件的读写
from sklearn.neural_network import MLPRegressor  # 从sklearn.neural_network导入MLPRegressor,用于创建多层感知器回归模型
from keras.callbacks import LearningRateScheduler  # 从keras.callbacks导入LearningRateScheduler,用于调整学习率
from tensorflow.keras import Input, Model, Sequential  # 从tensorflow.keras导入Input, Model和Sequential,用于模型构建
import mplcyberpunk
from qbstyles import mpl_style
from tensorflow import keras  # 从tensorflow.keras导入所有层,用于构建神经网络
from tcn.tcn import TCN
from prettytable import PrettyTable #可以优美的打印表格结果

# dataset=pd.read_excel("风电场功率预测.xlsx")
dataset=pd.read_csv("股票预测.csv",encoding='gb2312')
# 参数'encoding'设置为'gbk',这通常用于读取中文字符,确保文件中的中文字符能够正确读取。
# 读取的数据被存储在名为'dataset'的DataFrame变量中。
print(dataset)#显示dataset数据
数据示例:
                 发布日期     开盘指数     最高指数     最低指数  成交量(亿股)  成交额(亿元)  涨跌幅(%)  \
0      2022/5/20 0:00  3223.16  3267.41  3220.70    25.14   243.93    0.77   
1      2022/5/19 0:00  3211.60  3265.00  3208.24    25.61   254.88   -0.86   
2      2022/5/18 0:00  3255.59  3280.32  3245.07    21.21   204.77   -0.17   
3      2022/5/17 0:00  3301.25  3301.25  3255.96    26.54   268.17   -1.14   
4      2022/5/16 0:00  3241.51  3311.60  3221.92    30.75   301.98    3.09   
...               ...      ...      ...      ...      ...      ...     ...   
5401    2000/1/7 0:00  1076.06  1117.71  1058.83     0.65     8.75    3.80   
5402    2000/1/6 0:00  1025.38  1070.79  1011.00     0.30     4.21    3.58   
5403    2000/1/5 0:00  1035.47  1057.85  1013.07     0.25     2.90    0.12   
5404    2000/1/4 0:00  1001.98  1035.36   985.47     0.14     1.83    2.77   
5405  1999/12/30 0:00  1000.00  1000.00  1000.00     0.10     1.24    0.00   

         收盘指数  
0     3259.34  
1     3234.45  
2     3262.52  
3     3267.97  
4     3305.64  
...       ...  
5401  1106.19  
5402  1065.67  
5403  1028.87  
5404  1027.66  
5405  1000.00  

[5406 rows x 8 columns]
2) 数据预处理与特征工程
# 单输入单步预测,就让values等于某一列数据,n_out = 1,n_in, num_samples, scroll_window 根据自己情况来
# 单输入多步预测,就让values等于某一列数据,n_out > 1,n_in, num_samples, scroll_window 根据自己情况来
# 多输入单步预测,就让values等于多列数据,n_out = 1,n_in, num_samples, scroll_window 根据自己情况来
# 多输入多步预测,就让values等于多列数据,n_out > 1,n_in, num_samples, scroll_window 根据自己情况来
values = dataset.values[:,1:]  # 只取第2列数据,要写成1:2;只取第3列数据,要写成2:3,取第2列之后(包含第二列)的所有数据,写成 1:
# 从dataset DataFrame中提取数据。
# dataset.values将DataFrame转换为numpy数组。
# [:,1:]表示选择所有行(:)和从第二列到最后一列(1:)的数据。
# 这样做通常是为了去除第一列,这在第一列是索引或不需要的数据时很常见。

# 确保所有数据是浮动的
values = values.astype('float32')
# 将values数组中的数据类型转换为float32。
# 这通常用于确保数据类型的一致性,特别是在准备输入到神经网络模型中时。

# 构造数据,这个函数可以实现单输入单输出,单输入多输出,多输入单输出,和多输入多输出。
def data_collation(data, n_in, n_out, or_dim, scroll_window, num_samples):
    res = np.zeros((num_samples,n_in*or_dim+n_out))
    for i in range(0, num_samples):
        h1 = values[scroll_window*i: n_in+scroll_window*i,0:or_dim]
        h2 = h1.reshape( 1, n_in*or_dim)
        h3 = values[n_in+scroll_window*(i) : n_in+scroll_window*(i)+n_out,-1].T
        h4 = h3[np.newaxis, :]
        h5 = np.hstack((h2,h4))
        res[i,:] = h5
    return res

# 举例设置参数
n_in = 5  # 输入前5行的数据
n_out = 1  # 预测未来1个时刻的数据
or_dim = values.shape[1]  # 记录特征数据维度
num_samples = 2000  # 可以设定从数据中取出多少个点用于本次网络的训练与测试。
scroll_window = 1  # 如果等于1,下一个数据从第二行开始取。如果等于2,下一个数据从第三行开始取
res = data_collation(values, n_in, n_out, or_dim, scroll_window, num_samples)
3) 数据集划分与归一化
# 把数据集分为训练集和测试集
values = np.array(res)
# 将前面处理好的DataFrame(data)转换成numpy数组,方便后续的数据操作。

n_train_number = int(num_samples * 0.85)
# 计算训练集的大小。
# 设置80%作为训练集
# int(...) 确保得到的训练集大小是一个整数。
# 先划分数据集,在进行归一化,这才是正确的做法!
Xtrain = values[:n_train_number, :n_in*or_dim]
Ytrain = values[:n_train_number, n_in*or_dim:]

Xtest = values[n_train_number:, :n_in*or_dim]
Ytest = values[n_train_number:,  n_in*or_dim:]

# 对训练集和测试集进行归一化
m_in = MinMaxScaler()
vp_train = m_in.fit_transform(Xtrain)  # 注意fit_transform() 和 transform()的区别
vp_test = m_in.transform(Xtest)  # 注意fit_transform() 和 transform()的区别

m_out = MinMaxScaler()
vt_train = m_out.fit_transform(Ytrain)  # 注意fit_transform() 和 transform()的区别
vt_test = m_out.transform(Ytest)  # 注意fit_transform() 和 transform()的区别
4)调整训练数据格式
vp_train = vp_train.reshape((vp_train.shape[0], n_in, or_dim))
# 将训练集的输入数据vp_train重塑成三维格式。
# 结果是一个三维数组,其形状为[样本数量, 时间步长, 特征数量]。

vp_test = vp_test.reshape((vp_test.shape[0], n_in, or_dim))
# 将训练集的输入数据vp_test重塑成三维格式。
# 结果是一个三维数组,其形状为[样本数量, 时间步长, 特征数量]。
5)构建TCN-BiGRU模型
from keras.layers import Dense, Activation, Dropout, LSTM, Bidirectional, LayerNormalization, Input
# 从keras.layers模块导入多种层类型。
# Dense是用于创建全连接层的类。
# Activation是用于添加激活函数的层。
# Dropout是用于减少过拟合的丢弃层。
# LSTM是长短时记忆网络层,用于处理序列数据。
# Bidirectional是用于创建双向LSTM层的包装器。
# LayerNormalization是用于层级归一化的类。
# Input是用于模型输入层的函数。

from tensorflow.keras.models import Model
# 从tensorflow.keras.models模块导入Model类。
# Model是用于创建Keras函数式API模型的类。

from sklearn.model_selection import KFold
# 从sklearn.model_selection模块导入KFold类。
# KFold是一种交叉验证方法,用于评估模型的泛化能力。

def tcn_bigru_model():
    # 定义一个函数来创建TCN-BiGRU模型

    inputs = Input(shape=(vp_train.shape[1], vp_train.shape[2]))
    # 创建模型的输入层

    model = Sequential()
    model.add(inputs)
    model.add(TCN(nb_filters=32, kernel_size=2, dilations=[1, 2, 4], return_sequences=True))
    model.add(Bidirectional((GRU(35, activation='selu', return_sequences=False))))
    model.add(Dense(10))
    model.add(LeakyReLU(alpha=0.3))
    model.add(Dense(vt_train.shape[1], activation='linear'))

    # 配置和训练
    model.compile(optimizer='Adam', loss='mse', metrics='mae')
    model.summary()

    return model
    # 返回构建的模型。

model = tcn_bigru_model()
# 调用tcn_bigru_model函数来建立TCN-BiGRU模型。

history = model.fit(vp_train, vt_train, batch_size=32, epochs=60, validation_split=0.25, verbose=2)
# 训练模型。指定批处理大小为32,训练轮数为60,将25%的数据用作验证集。
# verbose=2表示在训练过程中会输出详细信息。
6)绘制训练与验证损失曲线
# 绘制历史数据
plt.plot(history.history['loss'], label='train')
# 绘制训练过程中的损失曲线。
# history.history['loss']获取训练集上每个epoch的损失值。
# 'label='train''设置该曲线的标签为'train'。

plt.plot(history.history['val_loss'], label='test')
# 绘制验证过程中的损失曲线。
# history.history['val_loss']获取验证集上每个epoch的损失值。
# 'label='test''设置该曲线的标签为'test'。
plt.legend()
# 显示图例,方便识别每条曲线代表的数据集。
plt.show()
# 展示绘制的图像。

图:TCN-BiGRU损失曲线

7)结果预测与评估
# 作出预测
yhat = model.predict(vp_test)
# 使用模型对测试集的输入特征(vp_test)进行预测。
# yhat是模型预测的输出值。

predicted_data = m_out.inverse_transform(yhat)  # 反归一化

def mape(y_true, y_pred):
    # 定义一个计算平均绝对百分比误差(MAPE)的函数。
    record = []
    for index in range(len(y_true)):
        # 遍历实际值和预测值。
        temp_mape = np.abs((y_pred[index] - y_true[index]) / y_true[index])
        # 计算单个预测的MAPE。
        record.append(temp_mape)
        # 将MAPE添加到记录列表中。
    return np.mean(record) * 100
    # 返回所有记录的平均值,乘以100得到百分比。

def evaluate_forecasts(Ytest, predicted_data, n_out):
    # 定义一个函数来评估预测的性能。
    mse_dic = []
    rmse_dic = []
    mae_dic = []
    mape_dic = []
    r2_dic = []
    # 初始化存储各个评估指标的字典。
    table = PrettyTable(['测试集指标','MSE', 'RMSE', 'MAE', 'MAPE','R2'])
    for i in range(n_out):
        # 遍历每一个预测步长。每一列代表一步预测,现在是在求每步预测的指标
        actual = [float(row[i]) for row in Ytest]  #一列列提取
        # 从测试集中提取实际值。
        predicted = [float(row[i]) for row in predicted_data]
        # 从预测结果中提取预测值。
        mse = mean_squared_error(actual, predicted)
        # 计算均方误差(MSE)。
        mse_dic.append(mse)
        rmse = sqrt(mean_squared_error(actual, predicted))
        # 计算均方根误差(RMSE)。
        rmse_dic.append(rmse)
        mae = mean_absolute_error(actual, predicted)
        # 计算平均绝对误差(MAE)。
        mae_dic.append(mae)
        MApe = mape(actual, predicted)
        # 计算平均绝对百分比误差(MAPE)。
        mape_dic.append(MApe)
        r2 = r2_score(actual, predicted)
        # 计算R平方值(R2)。
        r2_dic.append(r2)
        if n_out == 1:
            strr = '预测结果指标:'
        else:
            strr = '第'+ str(i + 1)+'步预测结果指标:'
        table.add_row([strr, mse, rmse, mae, str(MApe)+'%', str(r2*100)+'%'])

    return mse_dic, rmse_dic, mae_dic, mape_dic, r2_dic, table
    # 返回包含所有评估指标的字典。

mse_dic, rmse_dic, mae_dic, mape_dic, r2_dic, table = evaluate_forecasts(Ytest, predicted_data, n_out)
# 调用evaluate_forecasts函数。
# 传递实际值(Ytest)、预测值(predicted_data)以及预测的步数(n_out)作为参数。
# 此函数将计算每个预测步长的MSE、RMSE、MAE、MAPE和R2值。

print(table)  # 显示预测指标数值
测试集指标MSERMSEMAEMAPER2
预测结果指标:10542.140102.6773.424.10%94.16%
8)结果可视化
# 画结果图
from matplotlib import rcParams

config = {
            "font.family": 'serif',
            "font.size": 10,# 相当于小四大小
            "mathtext.fontset": 'stix',#matplotlib渲染数学字体时使用的字体,和Times New Roman差别不大
            "font.serif": ['Times New Roman'],#Times New Roman
            'axes.unicode_minus': False # 处理负号,即-号
         }
rcParams.update(config)

plt.ion()
for ii in range(n_out):

    plt.rcParams['axes.unicode_minus'] = False
    # 设置matplotlib的配置,用来正常显示负号。
    # 使用赛博朋克风样式
    plt.style.use('cyberpunk')
    # 创建一个图形对象,并设置大小为10x2英寸,分辨率为300dpi。
    plt.figure(figsize=(10, 2), dpi=300)
    x = range(1, len(predicted_data) + 1)
    # 创建x轴的值,从1到实际值列表的长度。
    plt.xticks(x[::int((len(predicted_data)+1))])
    # 设置x轴的刻度,每几个点显示一个刻度。
    plt.tick_params(labelsize=5)  # 改变刻度字体大小
    # 设置刻度标签的字体大小。
    plt.plot(x, predicted_data[:,ii], linestyle="--",linewidth=0.5, label='predict')
    # 绘制预测值的折线图,线型为虚线,线宽为0.5,标签为'predict'。

    plt.plot(x, Ytest[:,ii], linestyle="-", linewidth=0.5,label='Real')
    # 绘制实际值的折线图,线型为直线,线宽为0.5,标签为'Real'。

    plt.rcParams.update({'font.size': 5})  # 改变图例里面的字体大小
    # 更新图例的字体大小。

    plt.legend(loc='upper right', frameon=False)
    # 显示图例,位置在图形的右上角,没有边框。

    plt.xlabel("Sample points", fontsize=5)
    # 设置x轴标签为"样本点",字体大小为5。

    plt.ylabel("value", fontsize=5)
    # 设置y轴标签为"值",字体大小为5。
    if n_out == 1:  # 如果是单步预测
        plt.title(f"The prediction result of TCN-BiGRU :\nMAPE: {mape(Ytest[:, ii], predicted_data[:, ii])} %")
    else:
        plt.title(f"{ii+1} step of TCN-BiGRU prediction\nMAPE: {mape(Ytest[:,ii], predicted_data[:,ii])} %")
    # plt.xlim(xmin=600, xmax=700)  # 显示600-1000的值   局部放大有利于观察
    # 如果需要,可以取消注释这行代码,以局部放大显示600到700之间的值。

    # plt.savefig('figure/预测结果图.png')
    # 如果需要,可以取消注释这行代码,以将图形保存为PNG文件。

plt.ioff()  # 关闭交互模式
plt.show()
# 显示图形。

图:TCN-BiGRU预测结果图

有关预测问题的并基于TCN的创新算法还有TCN-LSTM、TCN-RNN以及结合注意力机制的组合优化算法,关注Easy数模,我们将持续输出紧跟学术前沿的数模方法论!

关注公众号,后台回复“TCN”即可获得本文TCN-GRU和TCN-BiGRU算法的python代码和案例数据获取方法!

参考文献

Li L, Li Y, Mao R, et al. Remaining useful life prediction for lithium-ion batteries with a hybrid model based on TCN-GRU-DNN and dual attention mechanism[J]. IEEE Transactions on Transportation Electrification, 2023, 9(3): 4726-4740.

Song Y, Luktarhan N, Shi Z, et al. TGA: a novel network intrusion detection method based on TCN, BiGRU and attention mechanism[J]. Electronics, 2023, 12(13): 2849.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值