3 时间序列预测入门:TCN

0 引言

      TCN(全称Temporal Convolutional Network),时序卷积网络,是在2018年提出的一个卷积模型,但是可以用来处理时间序列。

论文:https://arxiv.org/pdf/1803.01271.pdf

3 时间序列预测入门:TCN_第1张图片

0.1 卷积对比

 一维卷积:在时间步长方向(句子方向)进行滑动,并且输入通道的大小与词向量的大小相同。

二维卷积: 先平移然后可下移(宽的方向、高的方向);

torch.nn.Conv1d( in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)

  • in_channels : 在文本应用中,句子的长度。
  • out_channels: 卷积产生的通道数,有多少个out_channels,就需要多少个一维卷积(也就是卷积核的数量)
  • kernel_size :卷积核的尺寸;卷积核的第二个维度由 : kernel_size * in_channels
  • padding: 对输入的每一条边,补充0的层数, padding是左右两头都增加0

output = conv1(input)  :(in_channels,out_channelse,kernel_size )

input: (batch_size, in_channels, input_width)

output:(batch_size, out_channelse,output_size)

input_width = output_size

  • 3 时间序列预测入门:TCN_第2张图片

1 TCN 架构

3 时间序列预测入门:TCN_第3张图片

2 模型的定义与训练

训练过程中出现抖动情况,

建议把五百个充电桩子的数据按照 0.8 0.2 进行划分;后续会尝试;

import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from torch.nn.utils import weight_norm
#import tushare as ts
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from torch.utils.data import TensorDataset
from tqdm import tqdm
from sklearn.model_selection import train_test_split

import matplotlib.pyplot as plt
import tqdm
import sys
import os
import gc
import argparse
import warnings
 
warnings.filterwarnings('ignore')

# 读取数据
train_power_forecast_history = pd.read_csv('../data/data1/train/power_forecast_history.csv')
train_power = pd.read_csv('../data/data1/train/power.csv')
train_stub_info = pd.read_csv('../data/data1/train/stub_info.csv')
 
test_power_forecast_history = pd.read_csv('../data/data1/test/power_forecast_history.csv')
test_stub_info = pd.read_csv('../data/data1/test/stub_info.csv')
 
# 聚合数据
train_df = train_power_forecast_history.groupby(['id_encode','ds']).head(1)
del train_df['hour']
 
test_df = test_power_forecast_history.groupby(['id_encode','ds']).head(1)
del test_df['hour']
 
tmp_df = train_power.groupby(['id_encode','ds'])['power'].sum()
tmp_df.columns = ['id_encode','ds','power']
 
# 合并充电量数据
train_df = train_df.merge(tmp_df, on=['id_encode','ds'], how='left')
 
### 合并数据
train_df = train_df.merge(train_stub_info, on='id_encode', how='left')
test_df = test_df.merge(test_stub_info, on='id_encode', how='left')

h3_code = pd.read_csv("../data/h3_lon_lat.csv")
train_df = train_df.merge(h3_code,on='h3')
test_df = test_df.merge(h3_code,on='h3')

def kalman_filter(data, q=0.0001, r=0.01):
    # 后验初始值
    x0 = data[0]                              # 令第一个估计值,为当前值
    p0 = 1.0
    # 存结果的列表
    x = [x0]
    for z in data[1:]:                        # kalman 滤波实时计算,只要知道当前值z就能计算出估计值(后验值)x0
        # 先验值
        x1_minus = x0                         # X(k|k-1) = AX(k-1|k-1) + BU(k) + W(k), A=1,BU(k) = 0
        p1_minus = p0 + q                     # P(k|k-1) = AP(k-1|k-1)A' + Q(k), A=1
        # 更新K和后验值
        k1 = p1_minus / (p1_minus + r)        # Kg(k)=P(k|k-1)H'/[HP(k|k-1)H' + R], H=1
        x0 = x1_minus + k1 * (z - x1_minus)   # X(k|k) = X(k|k-1) + Kg(k)[Z(k) - HX(k|k-1)], H=1
        p0 = (1 - k1) * p1_minus              # P(k|k) = (1 - Kg(k)H)P(k|k-1), H=1
        x.append(x0)                          # 由输入的当前值z 得到估计值x0存入列表中,并开始循环到下一个值
    return x


#kalman_filter()
train_df['new_label'] = 0
for i in range(500):
    #print(i)
    label = i
    #train_df[train_df['id_encode']==labe]['power'].values
    train_df.loc[train_df['id_encode']==label, 'new_label'] = kalman_filter(data=train_df[train_df['id_encode']==label]['power'].values)

### 数据预处理
train_df['flag'] = train_df['flag'].map({'A':0,'B':1})
test_df['flag'] = test_df['flag'].map({'A':0,'B':1})
 
def get_time_feature(df, col):
 
    df_copy = df.copy()
    prefix = col + "_"
    df_copy['new_'+col] = df_copy[col].astype(str)
 
    col = 'new_'+col
    df_copy[col] = pd.to_datetime(df_copy[col], format='%Y%m%d')
    #df_copy[prefix + 'year'] = df_copy[col].dt.year
    df_copy[prefix + 'month'] = df_copy[col].dt.month
    df_copy[prefix + 'day'] = df_copy[col].dt.day
    # df_copy[prefix + 'weekofyear'] = df_copy[col].dt.weekofyear
    df_copy[prefix + 'dayofweek'] = df_copy[col].dt.dayofweek
    # df_copy[prefix + 'is_wknd'] = df_copy[col].dt.dayofweek // 6
    df_copy[prefix + 'quarter'] = df_copy[col].dt.quarter
    # df_copy[prefix + 'is_month_start'] = df_copy[col].dt.is_month_start.astype(int)
    # df_copy[prefix + 'is_month_end'] = df_copy[col].dt.is_month_end.astype(int)
    del df_copy[col]
 
    return df_copy
 
train_df = get_time_feature(train_df, 'ds')
test_df = get_time_feature(test_df, 'ds')

train_df = train_df.fillna(999)
test_df = test_df.fillna(999)

cols = [f for f in train_df.columns if f not in ['ds','power','h3','new_label']]


scaler = MinMaxScaler(feature_range=(0,1))
scalar_falg = False
if scalar_falg == True:
    df_for_training_scaled = scaler.fit_transform(train_df[cols])
    df_for_testing_scaled= scaler.transform(test_df[cols])
else:
    df_for_training_scaled = train_df[cols]
    df_for_testing_scaled = test_df[cols]
#df_for_training_scaled
# scaler_label = MinMaxScaler(feature_range=(0,1))
# label_for_training_scaled = scaler_label.fit_transform(train_df['new_label']..values)
# label_for_testing_scaled= scaler_label.transform(train_df['new_label'].values)
# #df_for_training_scaled




x_train, x_test, y_train, y_test = train_test_split(df_for_training_scaled.values, train_df['new_label'].values,shuffle=False, test_size=0.2)


# 将数据转为tensor
x_train_tensor = torch.from_numpy(x_train.reshape(-1,config.timestep,1)).to(torch.float32)
y_train_tensor = torch.from_numpy(y_train.reshape(-1,1)).to(torch.float32)
x_test_tensor = torch.from_numpy(x_test.reshape(-1,config.timestep,1)).to(torch.float32)
y_test_tensor = torch.from_numpy(y_test.reshape(-1,1)).to(torch.float32)

# 5.形成训练数据集
train_data = TensorDataset(x_train_tensor, y_train_tensor)
test_data = TensorDataset(x_test_tensor, y_test_tensor)

# 6.将数据加载成迭代器
train_loader = torch.utils.data.DataLoader(train_data,
                                           config.batch_size,
                                           True)

test_loader = torch.utils.data.DataLoader(test_data,
                                          config.batch_size,
                                          True)

class Config():
    data_path = '../data/data1/train/power.csv'
    timestep = 18  # 时间步长,就是利用多少时间窗口
    batch_size = 32  # 批次大小
    feature_size = 1  # 每个步长对应的特征数量,这里只使用1维,每天的风速
    num_channels = [32,64,128]  # 卷积核的尺寸序列
    output_size = 1  # 由于是单卷机和输出任务,最终输出层大小为1,预测未来1天风速
    num_layers = 2  # lstm的层数
    epochs = 10 # 迭代轮数
    best_loss = 0 # 记录损失
    learning_rate = 0.00003 # 学习率
    model_name = 'tcn' # 模型名称
    save_path = './{}.pth'.format(model_name) # 最优模型保存路径
    
config = Config()

#train_df[cols]
# 这个函数是用来修剪卷积之后的数据的尺寸,让其与输入数据尺寸相同。
class Chomp1d(nn.Module):
    def __init__(self, chomp_size):
        super(Chomp1d, self).__init__()
        self.chomp_size = chomp_size

    def forward(self, x):
        #Tensor底层一维数组元素的存储顺序与Tensor按行优先一维展开的元素顺序是一致的。
        # self.chomp_size 与pandding的个数又关系,因为padding size 是在序列两端添加0, 会多出padding size个数据,因此需要进行截断
        return x[:, :, :-self.chomp_size].contiguous()


# 7.定义TCN网络
class TemporalBlock(nn.Module):
    def __init__(self, n_inputs, n_outputs, kernel_size, stride, dilation, padding, dropout=0.2):
        """
        n_inputs: 词向量的维度
        n_outputs: 输出尺寸的维度
        kernel_size: 卷积核大小
        stride: 移动步长
        """
        super(TemporalBlock, self).__init__()
        
        self.conv1 = weight_norm(nn.Conv1d(n_inputs, n_outputs, kernel_size,
                                           stride=stride, padding=padding, dilation=dilation))
        self.chomp1 = Chomp1d(padding)
        self.relu1 = nn.ReLU()
        self.dropout1 = nn.Dropout(dropout)
        # weight_norm 权重归一化(Weight Normalization,简称WN)是一种优化技术,它可以加速训练过程并提高模型的泛化能力。
        self.conv2 = weight_norm(nn.Conv1d(n_outputs, n_outputs, kernel_size,
                                           stride=stride, padding=padding, dilation=dilation))
        self.chomp2 = Chomp1d(padding)
        
        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):
        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):
        out = self.net(x)
        res = x if self.downsample is None else self.downsample(x)
        return self.relu(out + res)

class ConvNetModel(nn.Module):
    def __init__(self, num_inputs, num_channels, output_size = 1,kernel_size=2, dropout=0.2):
        super(ConvNetModel, 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)
        self.linear = nn.Linear(num_channels[-1], output_size)


    def forward(self, x):
        x  = self.network(x)
        #print(x.shape)
        return self.linear(x[:, :, -1])

model = ConvNetModel(config.timestep, config.num_channels,output_size = config.output_size)  # 定义LSTM网络
#model

loss_function = nn.L1Loss()  # 定义损失函数
optimizer = torch.optim.AdamW(model.parameters(), lr=config.learning_rate)  # 定义优化器
from tqdm import tqdm

# 8.模型训练
for epoch in range(config.epochs):
    model.train()
    running_loss = 0
    train_bar = tqdm(train_loader)  # 形成进度条
    for data in train_bar:
        x_train, y_train = data  # 解包迭代器中的X和Y
        optimizer.zero_grad()
        y_train_pred = model(x_train)
        loss = loss_function(y_train_pred, y_train.reshape(-1, 1))
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                 config.epochs,
                                                                 loss)

    # 模型验证
    model.eval()
    test_loss = 0
    with torch.no_grad():
        test_bar = tqdm(test_loader)
        for data in test_bar:
            x_test, y_test = data
            y_test_pred = model(x_test)
            test_loss = loss_function(y_test_pred, y_test.reshape(-1, 1))

    if test_loss < config.best_loss:
        config.best_loss = test_loss
        torch.save(model.state_dict(), save_path)

print('Finished Training')
train epoch[1/10] loss:274.725: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [02:18<00:00, 26.97it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 51.09it/s]
train epoch[2/10] loss:278.706: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [01:52<00:00, 33.06it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 51.29it/s]
train epoch[3/10] loss:1418.840: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [02:07<00:00, 29.29it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:22<00:00, 41.65it/s]
train epoch[4/10] loss:98.374: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [01:59<00:00, 31.16it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 51.39it/s]
train epoch[5/10] loss:563.562: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [01:54<00:00, 32.56it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 50.35it/s]
train epoch[6/10] loss:290.543: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [01:57<00:00, 31.77it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 49.62it/s]
train epoch[7/10] loss:210.841: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [01:54<00:00, 32.52it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 51.27it/s]
train epoch[8/10] loss:626.850: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [01:52<00:00, 33.05it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 50.73it/s]
train epoch[9/10] loss:447.136: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [01:52<00:00, 33.03it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 50.07it/s]
train epoch[10/10] loss:411.241: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3727/3727 [01:53<00:00, 32.72it/s]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 932/932 [00:18<00:00, 49.62it/s]
Finished Training

 

from darts.models import TCNModelmodel = TCNModel(
    input_size=train.width,
    n_epochs=20, 
    input_length=365,
    output_length=7, 
    dropout=0, 
    dilation_base=2, 
    weight_norm=True,
    kernel_size=7,
    num_filters=4,
    random_state=0
)model.fit(
    training_series=train_transformed,
    target_series=train_transformed['0'],
    val_training_series=val_transformed,
    val_target_series=val_transformed['0'], verbose=True
)

REF: 时域卷积网络TCN详解:使用卷积进行序列建模和预测-CSDN博客

你可能感兴趣的:(#,5时间序列,深度学习,机器学习,人工智能)