LSTNet详解

LSTNet详解

论文题目:Modeling Long- and Short-Term Temporal Patterns with Deep Neural Networks

Long- and Short-term Time-series network (LSTNet)专门设计用于时间序列预测的深度学习网络,该网络特点有如下5点:

  • CNN。一维普通卷积(没有使用因果扩张卷积),用于捕捉短期局部信息
  • RNN。使用LSTM或GRU,捕捉长期宏观信息
  • Skip-RNN。对输入数据维度整理,使用LSTM或GRU,捕捉更长期的信息并充分利用序列的周期特性,但此时的周期P是引入的另一个超参数。
  • Attention。论文中有提到使用attention机制,灵活调整周期P,但在代码中没见到
  • HighWay。使用Dense模拟AR自回归过程,为预测添加线性成份,同时使输出可以响应输入的尺度变化。

代码部分有3类:

这里主要介绍keras/tensorflow版本,数据集以论文内的exchange_rate为例。

其它时间序列方法可参考回答,时间序列预测方法总结


1. 网络结构

论文中给出的网络结构如下,大致对应上了论文中提到的结构特点。

整个预测包括两部分,最终结果=非线性预测+线性预测:

  • 非线性层:CNN层、RNN层、Skip-RNN层
  • 线性层:AR层

2. 网络参数

网络参数定义代码为在LSTNet.py的初始化函数中,具体如下:

def __init__(self, args, dims):
    super(LSTNet, self).__init__()
    self.P = args.window # 时间序列窗口,输入网络的时间戳长度
    self.m = dims # 时间序列输入维度
    self.hidR = args.hidRNN # RNN层输出维度
    self.hidC = args.hidCNN # CNN层输出维度
    self.hidS = args.hidSkip # Skip-RNN层输出维度
    self.Ck = args.CNN_kernel # CNN层kernel大小
    self.skip = args.skip # 周期长度
    # window在kernel作用下,以skip为周期的数据数量。周期数目。
    self.pt = int((self.P-self.Ck)/self.skip) 
    self.hw = args.highway_window # highway通道的输出节点数目
    self.dropout = args.dropout 
    self.output = args.output_fun
    self.lr = args.lr
    self.loss = args.loss
    self.clip = args.clip

主要难理解的是pt,用于Skip-RNN结构,pt就表示在时间窗口window下会存在多少个以skip为周期的数据点。因为在Skip-RNN时,需要设置时间步长为周期数目,时序关系以周期间隔递进。

3. 网络定义代码

网络定义代码为在LSTNet.py的make_model中,具体如下:

def make_model(self):
    x = Input(shape=(self.P, self.m))

    # CNN,普通卷积,无casual-dilation
    c = Conv1D(self.hidC, self.Ck, activation='relu')(x)
    c = Dropout(self.dropout)(c)

    # RNN, 普通RNN
    r = GRU(self.hidR)(c)
    r = Lambda(lambda k: K.reshape(k, (-1, self.hidR)))(r)
    r = Dropout(self.dropout)(r)

    # skip-RNN,以skip为周期的RNN,需要对数据进行变换
    if self.skip > 0:
        # c: batch_size*steps*filters, steps=P-Ck
        s = Lambda(lambda k: k[:, int(-self.pt*self.skip):, :])(c)
        s = Lambda(lambda k: K.reshape(k, (-1, self.pt, self.skip, self.hidC)))(s)
        s = Lambda(lambda k: K.permute_dimensions(k, (0,2,1,3)))(s)
        # 这里设置时间步长为周期数目self.pt,时序关系以周期间隔递进,输入维度为self.hidC
        s = Lambda(lambda k: K.reshape(k, (-1, self.pt, self.hidC)))(s)

        s = GRU(self.hidS)(s)
        s = Lambda(lambda k: K.reshape(k, (-1, self.skip*self.hidS)))(s)
        s = Dropout(self.dropout)(s)
        # 合并RNN及Skip-RNN
        r = concatenate([r,s])
    
    res = Dense(self.m)(r)

    # highway,模型线性AR
    if self.hw > 0:
        z = Lambda(lambda k: k[:, -self.hw:, :])(x)
        z = Lambda(lambda k: K.permute_dimensions(k, (0,2,1)))(z)
        # hw设置以7天(self.hw=7)的值做为特征,利用Dense求预测量
        z = Lambda(lambda k: K.reshape(k, (-1, self.hw)))(z)
        z = Dense(1)(z)
        z = Lambda(lambda k: K.reshape(k, (-1, self.m)))(z)
        res = add([res, z])
   
    if self.output != 'no':
        res = Activation(self.output)(res)

    model = Model(inputs=x, outputs=res)
    model.compile(optimizer=Adam(lr=self.lr, clipnorm=self.clip), loss=self.loss)
    # print(model.summary())
    # plot_model(model, to_file="LSTNet_model.png", show_shapes=True)
    return model

以pt=2,skip=7为例,介绍维度变换过程。

一开始长度为2×7=14的序列向量,这里7是周期,2是周期数据,即序列向量每间隔7个点代表同一时刻状态;经过reshape和permute,转化为维度(7,2)的矩阵,代表同一时刻状态的数据位于同一行,每一行代表一条长周期样本,输入到RNN网络的时间步长为2,即以同周期数据作为输入,周期数目作为时间步长。

4. 运行示例

以论文内的exchange_rate数据集为例,演示结果,包括数据维度、网络参数、网络图结构、运行时间、评估精度等。

4.1 数据维度:

train (4552, 14, 8) (4552, 8) 
val (1518, 14, 8) (1518, 8) 
test (1502, 14, 8) (1502, 8) 

4.2 网络参数

self.P = 14
self.m = 8
self.hidR = 100
self.hidC = 100
self.hidS = 50
self.Ck = 6
self.skip = 7 
self.pt = 1 # int((self.P-self.Ck)/self.skip) 
self.hw = 7

4.3 网络图结构

图中,结构特点依次为:

  • 顶部conv1d_1,卷积结构
  • 左路lambda_2,Skip-RNN起点,开始变换维度
  • 中路gru_1,RNN起点
  • 右路lambda_7,AR起点

4.4 运行时间及评估

数据集:exchange_rate

环境:CPU i7-7700k, GPU 1080Ti,占用显存1.5G,计算资源40%

运行时间:5m34.404s

Average result: | rrse: 0.0408 | corr: 0.9516


欢迎讨论,转载请联系我~

编辑于 03-25