时间序列预测——时序卷积网络(TCN)

本文详述了使用时序卷积网络(TCN)进行时间序列预测的步骤,包括数据处理、模型构建、训练、预测及误差评估。通过动态调整学习率和early stopping提升模型性能,并使用tcn库完成TCN模型的构建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  本文展示了使用时序卷积网络(TCN)进行时间序列预测的全过程,包含详细的注释。整个过程主要包括:数据导入、数据清洗、结构转化、建立TCN模型、训练模型(包括动态调整学习率和earlystopping的设置)、预测、结果展示、误差评估等完整的时间序列预测流程。
  本文使用的tcn库在本人上传的资源中,链接为tcn.py
  本文使用的数据集在本人上传的资源中,链接为mock_kaggle.csv

import pandas as pd
import numpy as np
import math
from matplotlib import pyplot as plt
from matplotlib.pylab import mpl
import tensorflow as tf
from sklearn.preprocessing import MinMaxScaler
from keras import backend as K
from keras.layers import LeakyReLU
from tcn import TCN,tcn_full_summary
from sklearn.metrics import mean_squared_error # 均方误差
from keras.callbacks import LearningRateScheduler
from keras.callbacks import EarlyStopping
from tensorflow.keras import Input, Model,Sequential

mpl.rcParams['font.sans-serif'] = ['SimHei']   #显示中文
mpl.rcParams['axes.unicode_minus']=False       #显示负号

取数据

data=pd.read_csv('mock_kaggle.csv',encoding ='gbk',parse_dates=['datetime'])
Date=pd.to_datetime(data.datetime)
data['date'] = Date.map(lambda x: x.strftime('%Y-%m-%d'))
datanew=data.set_index(Date)
series = pd.Series(datanew['股票'].values, index=datanew['date'])
series
date
2014-01-01    4972
2014-01-02    4902
2014-01-03    4843
2014-01-04    4750
2014-01-05    4654
              ... 
2016-07-27    3179
2016-07-28    3071
2016-07-29    4095
2016-07-30    3825
2016-07-31    3642
Length: 937, dtype: int64

滞后扩充数据

dataframe1 = pd.DataFrame()
num_hour = 16
for i in range(num_hour,0,-1):
    dataframe1['t-'+str(i)] = series.shift(i)
dataframe1['t'] = series.values
dataframe3=dataframe1.dropna()
dataframe3.index=range(len(dataframe3))
dataframe3
t-16t-15t-14t-13t-12t-11t-10t-9t-8t-7t-6t-5t-4t-3t-2t-1t
04972.04902.04843.04750.04654.04509.04329.04104.04459.05043.05239.05118.04984.04904.04822.04728.04464
14902.04843.04750.04654.04509.04329.04104.04459.05043.05239.05118.04984.04904.04822.04728.04464.04265
24843.04750.04654.04509.04329.04104.04459.05043.05239.05118.04984.04904.04822.04728.04464.04265.04161
34750.04654.04509.04329.04104.04459.05043.05239.05118.04984.04904.04822.04728.04464.04265.04161.04091
44654.04509.04329.04104.04459.05043.05239.05118.04984.04904.04822.04728.04464.04265.04161.04091.03964
......................................................
9161939.01967.01670.01532.01343.01022.0813.01420.01359.01075.01015.0917.01550.01420.01358.02893.03179
9171967.01670.01532.01343.01022.0813.01420.01359.01075.01015.0917.01550.01420.01358.02893.03179.03071
9181670.01532.01343.01022.0813.01420.01359.01075.01015.0917.01550.01420.01358.02893.03179.03071.04095
9191532.01343.01022.0813.01420.01359.01075.01015.0917.01550.01420.01358.02893.03179.03071.04095.03825
9201343.01022.0813.01420.01359.01075.01015.0917.01550.01420.01358.02893.03179.03071.04095.03825.03642

921 rows × 17 columns

二折划分数据并标准化

# pot=int(len(dataframe3)*0.8)
pd.DataFrame(np.random.shuffle(dataframe3.values))  #shuffle
pot=len(dataframe3)-12
train=dataframe3[:pot]
test=dataframe3[pot:]
scaler = MinMaxScaler(feature_range=(0, 1)).fit(train)
#scaler = preprocessing.StandardScaler().fit(train)
train_norm=pd.DataFrame(scaler.fit_transform(train))
test_norm=pd.DataFrame(scaler.transform(test))
test_norm.shape,train_norm.shape
((12, 17), (909, 17))
X_train=train_norm.iloc[:,:-1]
X_test=test_norm.iloc[:,:-1]
Y_train=train_norm.iloc[:,-1:]
Y_test=test_norm.iloc[:,-1:]

转换为3维数据 [samples, timesteps, features]

source_x_train=X_train
source_x_test=X_test
X_train=X_train.values.reshape([X_train.shape[0],1,X_train.shape[1]]) #从(909,16)-->(909,1,16)
X_test=X_test.values.reshape([X_test.shape[0],1,X_test.shape[1]])  #从(12,16)-->(12,1,16)
Y_train=Y_train.values
Y_test=Y_test.values
X_train.shape,Y_train.shape
((909, 1, 16), (909, 1))
X_test.shape,Y_test.shape
((12, 1, 16), (12, 1))
type(X_train),type(Y_test)
(numpy.ndarray, numpy.ndarray)

动态调整学习率与提前终止函数

def scheduler(epoch):
    # 每隔50个epoch,学习率减小为原来的1/10
    if epoch % 50 == 0 and epoch != 0:
        lr = K.get_value(tcn.optimizer.lr)
        if lr>1e-5:
            K.set_value(tcn.optimizer.lr, lr * 0.1)
            print("lr changed to {}".format(lr * 0.1))
    return K.get_value(tcn.optimizer.lr)

reduce_lr = LearningRateScheduler(scheduler)
early_stopping = EarlyStopping(monitor='loss', 
                               patience=20, 
                               min_delta=1e-5,
                               mode='auto',
                               restore_best_weights=False,#是否从具有监测数量的最佳值的时期恢复模型权重
                               verbose=2)

构造TCN模型

batch_size=None
timesteps=X_train.shape[1]
input_dim=X_train.shape[2] #输入维数
tcn = Sequential()
input_layer =Input(batch_shape=(batch_size,timesteps,input_dim))
tcn.add(input_layer)
tcn.add(TCN(nb_filters=64, #在卷积层中使用的过滤器数。可以是列表。
        kernel_size=3, #在每个卷积层中使用的内核大小。
        nb_stacks=1,   #要使用的残差块的堆栈数。
        dilations=[2 ** i for i in range(6)], #扩张列表。示例为:[1、2、4、8、16、32、64]。
        #用于卷积层中的填充,值为'causal' 或'same'。
        #“causal”将产生因果(膨胀的)卷积,即output[t]不依赖于input[t+1:]。当对不能违反时间顺序的时序信号建模时有用。
        #“same”代表保留边界处的卷积结果,通常会导致输出shape与输入shape相同。
        padding='causal',
        use_skip_connections=True, #是否要添加从输入到每个残差块的跳过连接。
        dropout_rate=0.1, #在0到1之间浮动。要下降的输入单位的分数。
        return_sequences=False,#是返回输出序列中的最后一个输出还是完整序列。
        activation='relu', #残差块中使用的激活函数 o = Activation(x + F(x)).
        kernel_initializer='he_normal', #内核权重矩阵(Conv1D)的初始化程序。
        use_batch_norm=True, #是否在残差层中使用批处理规范化。
        use_layer_norm=True, #是否在残差层中使用层归一化。
        name='tcn' #使用多个TCN时,要使用唯一的名称
        ))
tcn.add(tf.keras.layers.Dense(64))
tcn.add(tf.keras.layers.LeakyReLU(alpha=0.3))
tcn.add(tf.keras.layers.Dense(32))
tcn.add(tf.keras.layers.LeakyReLU(alpha=0.3))
tcn.add(tf.keras.layers.Dense(16))
tcn.add(tf.keras.layers.LeakyReLU(alpha=0.3))
tcn.add(tf.keras.layers.Dense(1))
tcn.add(tf.keras.layers.LeakyReLU(alpha=0.3))
tcn.compile('adam', loss='mse', metrics=['accuracy'])
tcn.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
tcn (TCN)                    (None, 64)                143168    
_________________________________________________________________
dense (Dense)                (None, 64)                4160      
_________________________________________________________________
leaky_re_lu (LeakyReLU)      (None, 64)                0         
_________________________________________________________________
dense_1 (Dense)              (None, 32)                2080      
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None, 32)                0         
_________________________________________________________________
dense_2 (Dense)              (None, 16)                528       
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU)    (None, 16)                0         
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 17        
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU)    (None, 1)                 0         
=================================================================
Total params: 149,953
Trainable params: 148,417
Non-trainable params: 1,536
_________________________________________________________________

训练

history=tcn.fit(X_train,Y_train, epochs=80,batch_size=32,callbacks=[reduce_lr])
Train on 909 samples
Epoch 1/80
909/909 [==============================] - 14s 16ms/sample - loss: 0.1332 - accuracy: 0.0187
 ......
Epoch 10/80
909/909 [==============================] - 1s 1ms/sample - loss: 0.0135 - accuracy: 0.0187
 ......
Epoch 20/80
909/909 [==============================] - 1s 1ms/sample - loss: 0.0091 - accuracy: 0.0187
 ......
Epoch 30/80
909/909 [==============================] - 1s 1ms/sample - loss: 0.0090 - accuracy: 0.0176
 ......
Epoch 40/80
909/909 [==============================] - 1s 1ms/sample - loss: 0.0059 - accuracy: 0.0187
 ......
Epoch 50/80
909/909 [==============================] - 1s 1ms/sample - loss: 0.0067 - accuracy: 0.0187
lr changed to 0.00010000000474974513
 ......
Epoch 60/80
909/909 [==============================] - 1s 1ms/sample - loss: 0.0047 - accuracy: 0.0187
 ......
Epoch 70/80
909/909 [==============================] - 1s 1ms/sample - loss: 
......
Epoch 80/80
909/909 [==============================] - 1s 1ms/sample - loss: 0.0050 - accuracy: 0.0187
history.history.keys() #查看history中存储了哪些参数
plt.plot(history.epoch,history.history.get('loss')) #画出随着epoch增大loss的变化图
#plt.plot(history.epoch,history.history.get('acc'))#画出随着epoch增大准确率的变化图

在这里插入图片描述

预测

predict = tcn.predict(X_test)
real_predict=scaler.inverse_transform(np.concatenate((source_x_test,predict),axis=1))
real_y=scaler.inverse_transform(np.concatenate((source_x_test,Y_test),axis=1))
real_predict=real_predict[:,-1]
real_y=real_y[:,-1]

误差评估

plt.figure(figsize=(15,6))
bwith = 0.75 #边框宽度设置为2
ax = plt.gca()#获取边框
ax.spines['bottom'].set_linewidth(bwith)
ax.spines['left'].set_linewidth(bwith)
ax.spines['top'].set_linewidth(bwith)
ax.spines['right'].set_linewidth(bwith)
plt.plot(real_predict,label='real_predict')
plt.plot(real_y,label='real_y')
plt.plot(real_y*(1+0.15),label='15%上限',linestyle='--',color='green')
# plt.plot(real_y*(1+0.1),label='10%上限',linestyle='--')
# plt.plot(real_y*(1-0.1),label='10%下限',linestyle='--')
plt.plot(real_y*(1-0.15),label='15%下限',linestyle='--',color='green')
plt.fill_between(range(0,12),real_y*(1+0.15),real_y*(1-0.15),color='gray',alpha=0.2)
plt.legend()
plt.show()

在这里插入图片描述

round(mean_squared_error(Y_test,predict),4)
0.0012
from sklearn.metrics import r2_score
round(r2_score(real_y,real_predict),4)
0.5192
per_real_loss=(real_y-real_predict)/real_y
avg_per_real_loss=sum(abs(per_real_loss))/len(per_real_loss)
print(avg_per_real_loss)
0.13984217335814073
#计算指定置信水平下的预测准确率
#level为小数
def comput_acc(real,predict,level):
    num_error=0
    for i in range(len(real)):
        if abs(real[i]-predict[i])/real[i]>level:
            num_error+=1
    return 1-num_error/len(real)
comput_acc(real_y,real_predict,0.2),comput_acc(real_y,real_predict,0.15),comput_acc(real_y,real_predict,0.1)
(0.75, 0.6666666666666667, 0.33333333333333337)
### 什么是时序卷积网络 (TCN) 时序卷积网络(Temporal Convolutional Network, TCN)是一种基于卷积神经网络(CNN)架构设计的深度学习模型,专门用于处理序列数据。其核心思想是通过一维卷积操作捕捉时间维度上的特征模式[^2]。 #### 核心概念 1. **因果卷积** 因果卷积确保当前时刻的输出仅依赖于过去和当下的输入,而不受未来输入的影响。这种特性对于许多实际应用场景非常重要,比如实时预测任务[^1]。 2. **膨胀卷积** 膨胀卷积(Dilated Convolution)允许模型在不增加参数数量的情况下扩大感受野。通过指数级增长的扩张率,TCN能够捕获更长时间跨度内的上下文信息[^3]。 --- ### TCN 的实现原理 TCN 结合了因果卷积和膨胀卷积的特点,在结构上具有以下特点: 1. **层叠的一维卷积** 每一层都是一维卷积核作用于输入的时间轴方向。每一层的感受野随着层数加深而逐渐增大,从而覆盖更大的历史窗口范围。 2. **残差连接** 类似于 ResNet 中的设计理念,TCN 使用跳跃连接将前几层的结果传递到后续层,缓解梯度消失问题并加速训练过程。 以下是 Python 和 PyTorch 实现的一个简单例子: ```python import torch.nn as nn class TemporalBlock(nn.Module): def __init__(self, n_inputs, n_outputs, kernel_size, stride, dilation, padding, dropout=0.2): super(TemporalBlock, self).__init__() self.conv1 = nn.Conv1d(n_inputs, n_outputs, kernel_size, stride=stride, padding=padding, dilation=dilation) self.relu1 = nn.ReLU() self.dropout1 = nn.Dropout(dropout) self.conv2 = nn.Conv1d(n_outputs, n_outputs, kernel_size, stride=stride, padding=padding, dilation=dilation) self.relu2 = nn.ReLU() self.dropout2 = nn.Dropout(dropout) def forward(self, x): out = self.conv1(x) out = self.relu1(out) out = self.dropout1(out) out = self.conv2(out) out = self.relu2(out) out = self.dropout2(out) return out + x # 残差连接 class TemporalConvNet(nn.Module): def __init__(self, num_inputs, num_channels, kernel_size=2, dropout=0.2): super(TemporalConvNet, self).__init__() layers = [] num_levels = len(num_channels) for i in range(num_levels): dilation_size = 2 ** i in_channels = num_inputs if i == 0 else num_channels[i-1] out_channels = num_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): return self.network(x) ``` 上述代码定义了一个基本的 TCN 架构,其中 `TemporalBlock` 是单个时间步的核心模块,包含了两层带膨胀因子的卷积以及残差连接。 --- ### 应用场景 由于 TCN 在保持高效计算的同时具备强大的表达能力,因此广泛应用于多个领域: 1. **时间序列预测** 如股票价格预测、天气预报等需要对未来趋势做出估计的任务中表现优异。 2. **语音信号处理** 利用 TCN 对音频波形进行分析可以完成诸如说话人验证、情感检测等功能。 3. **自然语言生成与理解** 尽管 RNN/LSTM 更常见于此类任务,但在某些特定条件下(如固定长度文本分类),TCN 提供了一种替代方案。 4. **视频动作识别** 当帧间关系较为稳定时,采用 TCN 可有效提取动态变化规律。 --- ### 总结 相比传统方法,TCN 不仅继承了 CNN 高效并行化运算的优点,还解决了因循环机制引入复杂状态管理的问题;同时借助因果性和可变尺度感知区域两项关键技术实现了灵活可控的历史依赖建模效果[^2].
评论 103
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值