论文地址
GitHub代码地址

论文题目为《基于双阶段注意力机制的循环神经网络》,文章本质上还是基于Seq2Seq的模型,结合了注意力机制实现的时间序列的预测方法,文章的一大亮点是:不仅在解码器的输入阶段引入注意力机制,还在编码器阶段引入注意力机制,编码器的阶段的注意力机制实现了特征选取和把握时序依赖关系的作用。

分为两个阶段:

  1. 第一阶段:使用注意力机制从而能够自适应提取每个时刻的特征,这是本文的最大亮点
  2. 第二阶段:使用注意力机制选取与之相关的encoder hidden states

1:模型架构图

算法实现流程:

  1. 编码器阶段,也就是输入阶段,利用Attention机制,即:原始xt=(xt1,xt2,…,xtn){\boldsymbol{x}}_{t}=\left( x_{t}^{1}, x_{t}^{2}, \ldots, x_{t}^{n}\right)xt​=(xt1​,xt2​,…,xtn​) 利用Attention机制,结合隐层信息,会对每一维特征赋予一定的权重,转变为:x~t=(αt1xt1,αt2xt2,…,αtnxtn)\tilde{\boldsymbol{x}}_{t}=\left(\alpha_{t}^{1} x_{t}^{1}, \alpha_{t}^{2} x_{t}^{2}, \ldots, \alpha_{t}^{n} x_{t}^{n}\right)x~t​=(αt1​xt1​,αt2​xt2​,…,αtn​xtn​),从而实现自适应提取每个时刻的各个维度特征,使用更新后的 x~t\tilde{\boldsymbol{x}}_{t}x~t​ 作为编码器的输入。这也是本篇文章最大的亮点!
  2. 解码器阶段,也就是输出阶段,与传统Attention实现功能相同,使用另一个注意力机制选取与之相关的encoder hidden states

2:输入阶段的Attention

第一阶段输入阶段的编码器Attention机制实现过程如下:

文章中定义 ht∈Rm\mathbf{h}_{t} \in \mathbb{R}^{m}ht​∈Rm 为encoder在时刻 ttt 的hidden state, 其中 mmm 是hidden state的大小。

第一阶段,使用当前时刻的输人 xt∈Rn,\boldsymbol{x}_{t} \in \mathbb{R}^{n},xt​∈Rn, 以及上一个时刻编码器的hidden state ht−1\boldsymbol{h}_{\boldsymbol{t}-1}ht−1​, 来计算当前时刻编码器的hidden state ht∈Rm,\boldsymbol{h}_{t} \in \mathbb{R}^{m},ht​∈Rm, 其中m是编码器的size。更新公式可写为:
ht=f1(ht−1,xt)h_{t}=f_{1}\left(h_{t-1}, \boldsymbol{x}_{t}\right)ht​=f1​(ht−1​,xt​)

其中f1f_{1}f1​是一个非线性激活函数,我们可以使用通常的循环神经网络vanilla RNN或LSTM以及GRU作为 f1f_{1}f1​ ,在该文章中,使用的是LSTM来捕获长依赖关系。

这里,为了自适应地选取相关feature(即给每一个特征赋予一定的权重), 作者在此处引入了注意力机制。简单来说,即对每个时刻的输入 xt\boldsymbol{x}_{t}xt​ 为其中的每个影响因子赋予一定的注意力权重(attention weight) αtk\alpha_{t}^{k}αtk​ 。 αtk\alpha_{t}^{k}αtk​ 衡量了时刻 ttt 的第 kkk 个 feature的重要性。更新后的 x~t\tilde{\boldsymbol{x}}_{t}x~t​ 为
x~t=(αt1xt1,αt2xt2,…,αtnxtn)\tilde{\boldsymbol{x}}_{t}=\left(\alpha_{t}^{1} x_{t}^{1}, \alpha_{t}^{2} x_{t}^{2}, \ldots, \alpha_{t}^{n} x_{t}^{n}\right) x~t​=(αt1​xt1​,αt2​xt2​,…,αtn​xtn​)

那么 αtk\alpha_{t}^{k}αtk​ 如何计算得到?

文章中给出的方法:根据上一个时刻编码器的hidden state ht−1\boldsymbol{h}_{\boldsymbol{t}-\mathbf{1}}ht−1​ 和cell state st−1\boldsymbol{s}_{\boldsymbol{t}-\mathbf{1}}st−1​ 计算得到:
etk=veTtanh⁡(We[ht−1;st−1]+Uexk)e_{t}^{k}=\boldsymbol{v}_{e}^{T} \tanh \left(\boldsymbol{W}_{e}\left[\boldsymbol{h}_{t-1} ; \boldsymbol{s}_{t-1}\right]+\boldsymbol{U}_{e} \boldsymbol{x}^{k}\right) etk​=veT​tanh(We​[ht−1​;st−1​]+Ue​xk)
其中 [ht−1;st−1]\left[\boldsymbol{h}_{t-1} ; \boldsymbol{s}_{t-1}\right][ht−1​;st−1​] 是hidden state ht−1\boldsymbol{h}_{t-1}ht−1​ 与cell state st−1\boldsymbol{s}_{t-1}st−1​ 的连接(concatenation)。

该式即把第 kkk 个driving series(文章中的driving series就是特征的含义)与前一个时刻的hidden state ht−1\boldsymbol{h}_{\boldsymbol{t}-1}ht−1​ 和cell state st−1\boldsymbol{s}_{\boldsymbol{t}-\mathbf{1}}st−1​ 线性组合, 再用 tanh激活得到。

计算得到 etke_{t}^{k}etk​ 后,再用softmax函数进行归一化:

αtk=exp⁡(etk)∑i−1nexp⁡(eti)\alpha_{t}^{k}=\frac{\exp \left(e_{t}^{k}\right)}{\sum_{i-1}^{n} \exp \left(e_{t}^{i}\right)}αtk​=∑i−1n​exp(eti​)exp(etk​)​

更新后的 x~t\tilde{\boldsymbol{x}}_{t}x~t​
x~t=(αt1xt1,αt2xt2,…,αtnxtn)\tilde{\boldsymbol{x}}_{t}=\left(\alpha_{t}^{1} x_{t}^{1}, \alpha_{t}^{2} x_{t}^{2}, \ldots, \alpha_{t}^{n} x_{t}^{n}\right) x~t​=(αt1​xt1​,αt2​xt2​,…,αtn​xtn​)作为下一个阶段temporal Attention的输入

input attention机制,使得编码器能够关注其中输入特征中重要的特征,而不是对所有特征一视同仁,这也是所有attention的本质作用。

3:temporal attention的解码器

第二阶段temporal Attention机制实现过程如下:


为了区别起见,参考罗未萌的建议,与论文中公式略有不同的是,将解码器中的时间序列下标标注为 t′,t^{\prime},t′, 以与编码 器种的下标 ttt 区分。

第二阶段的解码器注意力机制设计类似于传统的seq2seq中的Attention机制,也就是第二阶段temporal attention的机制其实就是传统Attention的机制。

传统Attentionde 解决的问题是: 传统的seq2seq模型中, 编码器输出的context vector基于最后时刻的hidden state或对所有 hidden state取平均。这样输出的context vector对所有时刻 ttt 均相同,没有体现出差异化,就像人一样没有将注意力集中到关键部分,无法起到只选取相关时刻编码器hidden state的功能。

解决问题的思路是在不同时刻采用不同的context vector。类似于 seq2seq, 最简单的办法是对所有时刻的 ht′\boldsymbol{h}_{t^{\prime}}ht′​ 取加权平均, 即:
ct′=∑t=1Tβt′tht\boldsymbol{c}_{t^{\prime}}=\sum_{t=1}^{T} \beta_{t^{\prime}}^{t} h_{t} ct′​=t=1∑T​βt′t​ht​

βt′t\beta_{t^{\prime}}^{t}βt′t​ 是基于前一个时刻解码器的hidden state dt′−1\boldsymbol{d}_{\boldsymbol{t}^{\prime}-\mathbf{1}}dt′−1​ 和cell state st′−1′s_{t^{\prime}-1}^{\prime}st′−1′​ 计算得到:

lt′t=vdTtanh⁡(Wd[dt′−1;st′−1′]+Udht)l_{t^{\prime}}^{t}=\boldsymbol{v}_{d}^{T} \tanh \left(\boldsymbol{W}_{d}\left[\boldsymbol{d}_{t^{\prime}-1} ; \boldsymbol{s}_{t^{\prime}-1}^{\prime}\right]+\boldsymbol{U}_{d} \boldsymbol{h}_{t}\right)lt′t​=vdT​tanh(Wd​[dt′−1​;st′−1′​]+Ud​ht​)
βt′t=exp⁡(lt′t)∑j=1Texp⁡(lt′j)\beta_{t^{\prime}}^{t}=\frac{\exp \left(l_{t^{\prime}}^{t}\right)}{\sum_{j=1}^{T} \exp \left(l_{t^{\prime}}^{j}\right)}βt′t​=∑j=1T​exp(lt′j​)exp(lt′t​)​

根据文章中的模型流程,可以看到解码器的输入是上一个时刻的目标序列 yt′−1y_{t^{\prime}-1}yt′−1​ 和hidden state dt′−1\boldsymbol{d}_{t^{\prime}-1}dt′−1​ 以及context vector ct′−1\boldsymbol{c}_{t^{\prime}-1}ct′−1​共同组成

即dt′=f2(yt′−1,ct′−1,dt′−1)\boldsymbol{d}_{t^{\prime}}=f_{2}\left(y_{t^{\prime}-1}, \boldsymbol{c}_{t^{\prime}-1}, \boldsymbol{d}_{t^{\prime}-1}\right)dt′​=f2​(yt′−1​,ct′−1​,dt′−1​)

然后
dt′=f2(dt′−1,y~t′−1)\boldsymbol{d}_{t^{\prime}}=f_{2}\left(\boldsymbol{d}_{t^{\prime}-1}, \tilde{y}_{t^{\prime}-1}\right) dt′​=f2​(dt′−1​,y~​t′−1​)
类似于编码器的最后一个公式, 这里的激活函数 f2f_{2}f2​ 还是选择LSTM。

4:预测部分

文章回顾了非线性自回归(Nonlinear autoregressive exogenous, NARX)模型的最终目标,需要建立当前输入与所有时刻的输人以及之前时刻的输出之间的关系,即:
y^T=F(y1,…,yT−1,x1,…,xT)\hat{y}_{T}=F\left(y_{1}, \ldots, y_{T-1}, \boldsymbol{x}_{1}, \ldots, \boldsymbol{x}_{T}\right) y^​T​=F(y1​,…,yT−1​,x1​,…,xT​)

通过之前编码器解码器模型的训练,已经得到了解码器的hidden state 和context vector, hT\boldsymbol{h}_{T}hT​ 与 cT\boldsymbol{c}_{T}cT​ 。最后再使用一个全连接层对 y^T\hat{y}_{T}y^​T​ 做回归, 即
y^T=vyT(Wy[dT;cT]+bw)+bv\hat{y}_{T}=\boldsymbol{v}_{y}^{T}\left(\boldsymbol{W}_{y}\left[\boldsymbol{d}_{T} ; \boldsymbol{c}_{T}\right]+\boldsymbol{b}_{w}\right)+b_{v}y^​T​=vyT​(Wy​[dT​;cT​]+bw​)+bv​

这样可以得到最终的预测 y^\hat{y}y^​

5:总结


文章是将input Attention 和temporal Attention 分开讲述的,模型架构图是放在一起的,刚开始读完论文些不太理解的地方:

如input attention 中的f1是使用LSTM,接着x~t\tilde{\mathbf{x}}_{t}x~t​又作为temporal Attention 中LSTM的输入,接着解码层,又使用LSTM来进行预测,这样的话,不就是共有3个LSTM进行训练吗?

在深入阅读以及查看源代码后,发现之前理解出现偏差,其实总共只有2个阶段LSTM,分别对应input attention阶段用来提取自适应特征的attention模块中的LSTM,解码阶段的LSTM。我重新模型架构图整理了下,并进行箭头标注,表示对应的位置。

左边 input attention一大块计算得到的x~t\tilde{\mathbf{x}}_{t}x~t​, x~t\tilde{\boldsymbol{x}}_{t}x~t​x~t=(αt1xt1,αt2xt2,…,αtnxtn)\tilde{\boldsymbol{x}}_{t}=\left(\alpha_{t}^{1} x_{t}^{1}, \alpha_{t}^{2} x_{t}^{2}, \ldots, \alpha_{t}^{n} x_{t}^{n}\right) x~t​=(αt1​xt1​,αt2​xt2​,…,αtn​xtn​)
实际上只是temporal attention中的一个时刻的输入, 我们从input attentionht\mathbf{h}_{t}ht​的与temporal attention中的ht\mathbf{h}_{t}ht​对应的位置可以观察到。

也就是说,坐标的input attention 实际上只是temporal attention将某一个时刻剥离出来的计算过程细节而已。单看右边的temporal attention,实际上就是一个Seq2Seq的attention的实现,并没有不同,作者将temporal attention的输入x~t\tilde{\mathbf{x}}_{t}x~t​单独剥离出来,强调其计算过程,也就是input attention的实现机制,目的就是说明文章的亮点:在输入阶段也实现基于attention的自适应特征的提取

6: 数据输入部分

原始数据大小:[110,81]
训练数据比例:70%
默认批数据量大小:128
train_timesteps:110*0.7=77
时间窗口长度:T=10
因此第一批训练数据长度为:67 = ref_idx = np.array(range(self.train_timesteps - self.T))下面以第一批训练数据为例:原始数据X维度:[67,81]
Encoder层:X_tilde 的初始化为0:[67,9,81]LSTM的隐层h_n和输出层s_n的初始化:[1,67,64]h_n、s_n经过repeat得到[67, 81, 9]然后经过torch.cat将h_n、s_n和X_tilde进行拼接,得到x:[67, 81, 137]经过view函数进行维度重塑,得到# [5427, 137]再经过attention层,得到x:[5427, 1]再经过view函数进行维度重塑并且维度reshape得到alpha,即每个特征的权重大小alpha:[67,81]通过unsqueeze函数进行维度变化得到[1, 67, 81],并将其作为LSTM的输入,输入格式为:self.encoder_lstm(x_tilde.unsqueeze(0), (h_n, s_n))并将隐层和输入层进行赋值,作为下一个T时刻的输入h_n = final_state[0]  # [1, 67, 64]s_n = final_state[1]  # [1, 67, 64X_tilde[:, t, :] = x_tilde   # X_tilde[:, t, :]    [67, 81]  Encoder层中的LSTM中的输入 (带有权重的输入)X_encoded[:, t, :] = h_n    # X_encoded[:, t, :]   [67, 64]  Encoder层中的LSTM中的隐层输出 (经过LSTM后的输出)Encoder层返回的是[67, 9, 64]Decoder层:输入数据X_encoded:[67, 9, 64]输入数据y: y_prev:[67, 9] 作为前一时刻的输出结果与当前进行拼接LSTM的隐层d_n和输出层c_n的初始化: [1, 67, 128]d_n、c_n经过repeat得到[67, 9, 64]然后经过torch.cat将d_n、c_n和X_encoded进行拼接,得到x:[67, 9, 320]经过view函数进行维度重塑,得到 [603, 320]再经过attention层,得到x:[603, 1]再经过view函数进行维度重塑并且softmax得到beta,得到 # [67,1,9]  即向量的权重通过unsqueeze函数进行维度变化得到[67,1,9]通过torch.bmm(beta.unsqueeze(1), X_encoded)进行矩阵相乘得到[67,1,64],经过变换得到context [67, 64]最后将context与上一个时刻y_prev[:, t]进行全连接得到y_tilde [67, 1]将其作为LSTM的输入, self.lstm_layer(y_tilde.unsqueeze(0), (d_n, c_n))d_n = final_states[0]  # 1 * batch_size * decoder_num_hiddenc_n = final_states[1]  # 1 * batch_size * decoder_num_hidden最后LSTM的输出

7:关键部分代码

import matplotlib.pyplot as pltimport torch
import numpy as npfrom torch import nn
from torch import optimfrom torch.autograd import Variable
import torch.nn.functional as Fclass Encoder(nn.Module):"""encoder in DA_RNN."""def __init__(self, T,input_size,encoder_num_hidden,parallel=False):"""Initialize an encoder in DA_RNN."""super(Encoder, self).__init__()self.encoder_num_hidden = encoder_num_hiddenself.input_size = input_sizeself.parallel = parallelself.T = T# Fig 1. Temporal Attention Mechanism: Encoder is LSTMself.encoder_lstm = nn.LSTM(input_size=self.input_size,hidden_size=self.encoder_num_hidden,num_layers=1)# Construct Input Attention Mechanism via deterministic attention model# Eq. 8: W_e[h_{t-1}; s_{t-1}] + U_e * x^kself.encoder_attn = nn.Linear(in_features=2 * self.encoder_num_hidden + self.T - 1,out_features=1)def forward(self, X):"""forward.Args:X: input data"""X_tilde = Variable(torch.zeros(X.size(0), self.T - 1,self.input_size))print('X_tilde',X_tilde.shape) #[67, 9, 81]X_encoded = Variable(torch.zeros(X.size(0), self.T - 1,self.encoder_num_hidden))  #[67, 9, 64]# X_tilde = Variable(X.data.new(# X.size(0), self.T - 1, self.input_size).zero_())# X_encoded = Variable(X.data.new(#     X.size(0), self.T - 1, self.encoder_num_hidden).zero_())# Eq. 8, parameters not in nn.Linear but to be learnt# v_e = torch.nn.Parameter(data=torch.empty(#     self.input_size, self.T).uniform_(0, 1), requires_grad=True)# U_e = torch.nn.Parameter(data=torch.empty(#     self.T, self.T).uniform_(0, 1), requires_grad=True)# h_n, s_n: initial states with dimention hidden_sizeh_n = self._init_states(X)  #  初始化LSTM hidden信息s_n = self._init_states(X)  #  初始化LSTM cell信息print('initial-h_n',h_n.shape)  # [1, 67, 64]  # 输入信息self.num_layers,x.size(0), self.hidden_size)print('initial-h_n',s_n.shape)  # [1, 67, 64]  # 输入信息self.num_layers,x.size(0), self.hidden_size)'''nhidden_encoder = 64batchsize = 200'''# 输入的X :[67, 9, 81]# Encoder层的for循环的作用:计算attention的权重后,将数据输入至Encoder层的LSTM进行训练,共T-1次for t in range(self.T - 1):print('-----AAA--------')print('X_tilde', X_tilde.shape)  # [67, 9, 81]# batch_size * input_size * (2 * hidden_size + T - 1)print('h_n.repeat',h_n.repeat(self.input_size, 1, 1).permute(1, 0, 2).shape) #[67, 81, 64]print('s_n.repeat',s_n.repeat(self.input_size, 1, 1).permute(1, 0, 2).shape) #[67, 81, 64]print('X.permute',X.permute(0, 2, 1).shape) #[67, 81, 9]x = torch.cat((h_n.repeat(self.input_size, 1, 1).permute(1, 0, 2),s_n.repeat(self.input_size, 1, 1).permute(1, 0, 2),X.permute(0, 2, 1)), dim=2)print('x',x.shape) #[67, 81, 137]print('x.view',(x.view(-1, self.encoder_num_hidden * 2 + self.T - 1)).shape)  # [5427, 137]x = self.encoder_attn(x.view(-1, self.encoder_num_hidden * 2 + self.T - 1))print('x-shape',x.shape) # [5427, 1]# get weights by softmaxalpha = F.softmax(x.view(-1, self.input_size))print('alpha',alpha.shape)  # [67, 81]# get new input for LSTMprint(' X[:, t, :]', X[:, t, :].shape)  #[67, 81]x_tilde = torch.mul(alpha, X[:, t, :])print('x_tilde',x_tilde.shape)  # [67, 81]# Fix the warning about non-contiguous memory# https://discuss.pytorch.org/t/dataparallel-issue-with-flatten-parameter/8282self.encoder_lstm.flatten_parameters()   ## 这段代码的含义是什么? 有什么作用?# 输入信息self.num_layers,x.size(0), self.hidden_size)print('x_tilde.unsqueeze(0)',x_tilde.unsqueeze(0).shape)  #[1, 67, 81] 作为LSTM的输入数据 # encoder LSTM_, final_state = self.encoder_lstm(x_tilde.unsqueeze(0), (h_n, s_n))print('final_state[0]',final_state[0].shape)  # [1, 67, 64]h_n = final_state[0]  # [1, 67, 64]s_n = final_state[1]  # [1, 67, 64]X_tilde[:, t, :] = x_tilde   # X_tilde[:, t, :]    [67, 81]  Encoder层中的LSTM中的输入X_encoded[:, t, :] = h_n    # X_encoded[:, t, :]   [67, 64]  Encoder层中的LSTM中的隐层输出return X_tilde, X_encodeddef _init_states(self, X):"""Initialize all 0 hidden states and cell states for encoder."""# https://pytorch.org/docs/master/nn.html?#lstmreturn Variable(X.data.new(1, X.size(0), self.encoder_num_hidden).zero_())class Decoder(nn.Module):"""decoder in DA_RNN."""def __init__(self, T, decoder_num_hidden, encoder_num_hidden):"""Initialize a decoder in DA_RNN."""super(Decoder, self).__init__()self.decoder_num_hidden = decoder_num_hiddenself.encoder_num_hidden = encoder_num_hiddenself.T = Tself.attn_layer = nn.Sequential(nn.Linear(2 * decoder_num_hidden +encoder_num_hidden, encoder_num_hidden),nn.Tanh(),nn.Linear(encoder_num_hidden, 1))self.lstm_layer = nn.LSTM(input_size=1,hidden_size=decoder_num_hidden)self.fc = nn.Linear(encoder_num_hidden + 1, 1)self.fc_final = nn.Linear(decoder_num_hidden + encoder_num_hidden, 1)self.fc.weight.data.normal_()def forward(self, X_encoded, y_prev):print('-----BBB--------')print('X_encoded',X_encoded.shape)   # [67, 9, 64]print('y_prev',y_prev.shape)         # [67, 9]"""forward."""d_n = self._init_states(X_encoded)  # 初始化LSTM hidden信息c_n = self._init_states(X_encoded)  # 初始化LSTM cell信息print('initial-d_n',d_n.shape)  # [1, 67, 128]    # 输入信息self.num_layers,x.size(0), self.hidden_size)print('initial-c_n',c_n.shape)  # [1, 67, 128]# Decoder 层的for循环作用:将Encoder层的计算结果输入至Decoder层的Attention层进行计算# 得到权重,权重Beta再和Encoder中输入h1(h1即X_encoded)计算矩阵乘积,得到context,再将context输入至Decoder层的LSTM # 并与上一次LSTM中的输出结果进行cat,计算T-1次,即LSTM的时间戳长度for t in range(self.T - 1):print('d_n.repeat', d_n.repeat(self.T - 1, 1, 1).permute(1, 0, 2).shape)  # [67, 9, 128]print('c_n.repeat',c_n.repeat(self.T - 1, 1, 1).permute(1, 0, 2).shape)   # [67, 9, 128]print('X_encoded',X_encoded.shape)  # [67, 9, 64]x = torch.cat((d_n.repeat(self.T - 1, 1, 1).permute(1, 0, 2),c_n.repeat(self.T - 1, 1, 1).permute(1, 0, 2),X_encoded), dim=2)print('x-shape',x.shape)   # [67, 9, 320]print('x.view',x.view(-1, 2 * self.decoder_num_hidden + self.encoder_num_hidden).shape)  # [603, 320]x = self.attn_layer(x.view(-1, 2 * self.decoder_num_hidden + self.encoder_num_hidden))print('x-atten',x.shape) # [603, 1])beta = F.softmax(x.view(-1, self.T - 1))print('beta',beta.shape) # [67, 9] 得到权重# Eqn. 14: compute context vector# batch_size * encoder_hidden_sizeprint('beta.unsqueeze(1)',beta.unsqueeze(1).shape)  # [67,1,9]    # X_encoded [67, 9, 64]  矩阵相乘得到 [67,1,64]print('torch.bmm(beta.unsqueeze(1), X_encoded)',torch.bmm(beta.unsqueeze(1), X_encoded).shape)  # [67,1,64]context = torch.bmm(beta.unsqueeze(1), X_encoded)[:, 0, :]print('context',context.shape) # [67, 64]  if t < self.T - 1:# Eqn. 15# batch_size * 1print('y_prev[:, t].unsqueeze(1)',y_prev[:, t].unsqueeze(1).shape)  # [67, 1]y_tilde = self.fc(torch.cat((context, y_prev[:, t].unsqueeze(1)), dim=1))  # 上一个时刻的预测值与当前隐层进行拼接print('y_tilde',y_tilde.shape) # [67, 1])# Eqn. 16: LSTMself.lstm_layer.flatten_parameters()_, final_states = self.lstm_layer(y_tilde.unsqueeze(0), (d_n, c_n))d_n = final_states[0]  # 1 * batch_size * decoder_num_hiddenc_n = final_states[1]  # 1 * batch_size * decoder_num_hidden# final_states[0] torch.Size([1, 67, 128])#  final_states[1] torch.Size([1, 67, 128])# Eqn. 22: final outputprint('d_n[0]',d_n[0].shape)  #[67, 128]y_pred = self.fc_final(torch.cat((d_n[0], context), dim=1))  # 将LSTM最后一个cell的输出与attention的输入进行拼接,然后输出结果# y_pred [67, 1]return y_preddef _init_states(self, X):"""Initialize all 0 hidden states and cell states for encoder."""# hidden state and cell state [num_layers*num_directions, batch_size, hidden_size]# https://pytorch.org/docs/master/nn.html?#lstmreturn Variable(X.data.new(1, X.size(0), self.decoder_num_hidden).zero_())
class DA_RNN(nn.Module):"""Dual-Stage Attention-Based Recurrent Neural Network."""def __init__(self, X, y, T,encoder_num_hidden,decoder_num_hidden,batch_size,learning_rate,epochs,parallel=False):"""initialization."""super(DA_RNN, self).__init__()self.encoder_num_hidden = encoder_num_hiddenself.decoder_num_hidden = decoder_num_hiddenself.learning_rate = learning_rateself.batch_size = batch_sizeself.parallel = parallelself.shuffle = Falseself.epochs = epochsself.T = Tself.X = Xself.y = yself.device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')print("==> Use accelerator: ", self.device)self.Encoder = Encoder(input_size=X.shape[1],encoder_num_hidden=encoder_num_hidden,T=T).to(self.device)self.Decoder = Decoder(encoder_num_hidden=encoder_num_hidden,decoder_num_hidden=decoder_num_hidden,T=T).to(self.device)# Loss functionself.criterion = nn.MSELoss()if self.parallel:self.encoder = nn.DataParallel(self.encoder)self.decoder = nn.DataParallel(self.decoder)self.encoder_optimizer = optim.Adam(params=filter(lambda p: p.requires_grad,self.Encoder.parameters()),lr=self.learning_rate)self.decoder_optimizer = optim.Adam(params=filter(lambda p: p.requires_grad,self.Decoder.parameters()),lr=self.learning_rate)# Training setself.train_timesteps = int(self.X.shape[0] * 0.7)  # 原始数据csv共110条数据,因此train_timesteps=77self.y = self.y - np.mean(self.y[:self.train_timesteps])self.input_size = self.X.shape[1]def train(self):"""Training process."""iter_per_epoch = int(np.ceil(self.train_timesteps * 1. / self.batch_size))self.iter_losses = np.zeros(self.epochs * iter_per_epoch)self.epoch_losses = np.zeros(self.epochs)n_iter = 0for epoch in range(self.epochs):# ref_idx长度大小=77-10=67# ref_idx:67if self.shuffle:ref_idx = np.random.permutation(self.train_timesteps - self.T)  # 打乱顺序排序 else:ref_idx = np.array(range(self.train_timesteps - self.T))  # 按顺序输出 数据长度- 时间戳长度idx = 0# 按batch_size(64条)进行处理,直到达到train_timesteps数据量,首先是0<77,然后是64<77while (idx < self.train_timesteps):# get the indices of X_train# batch_size=128,大于idx长度,所以indices即为整个ref_idx的索引,长度为67indices = ref_idx[idx:(idx + self.batch_size)] # x = np.zeros((self.T - 1, len(indices), self.input_size))x = np.zeros((len(indices), self.T - 1, self.input_size)) # x:[64,9,81]y_prev = np.zeros((len(indices), self.T - 1))  # y_prev:[64,9] 训练的y_prevy_gt = self.y[indices + self.T]  # y_gt:indices 后的第T个真实y值与预测进行比较 因此长度也为67 # format x into 3D tensor  数据切分,X和yfor bs in range(len(indices)): # range: 0-67x[bs, :, :] = self.X[indices[bs]:(indices[bs] + self.T - 1), :]  # 赋值数据,原始数据每次[9,81]赋予 xy_prev[bs, :] = self.y[indices[bs]: (indices[bs] + self.T - 1)]  # 原始y值每次赋予9个数值给y_prevloss = self.train_forward(x, y_prev, y_gt)  # 输入训练数据,真实值和预测值进行比较self.iter_losses[int(epoch * iter_per_epoch + idx / self.batch_size)] = lossidx += self.batch_sizen_iter += 1if n_iter % 10000 == 0 and n_iter != 0:for param_group in self.encoder_optimizer.param_groups:param_group['lr'] = param_group['lr'] * 0.9for param_group in self.decoder_optimizer.param_groups:param_group['lr'] = param_group['lr'] * 0.9self.epoch_losses[epoch] = np.mean(self.iter_losses[range(epoch * iter_per_epoch, (epoch + 1) * iter_per_epoch)])if epoch % 10 == 0:print("Epochs: ", epoch, " Iterations: ", n_iter," Loss: ", self.epoch_losses[epoch])if epoch % 10 == 0:y_train_pred = self.test(on_train=True)y_test_pred = self.test(on_train=False)y_pred = np.concatenate((y_train_pred, y_test_pred))plt.ioff()plt.figure()plt.plot(range(1, 1 + len(self.y)), self.y, label="True")plt.plot(range(self.T, len(y_train_pred) + self.T),y_train_pred, label='Predicted - Train')plt.plot(range(self.T + len(y_train_pred), len(self.y) + 1),y_test_pred, label='Predicted - Test')plt.legend(loc='upper left')plt.show()def train_forward(self, X, y_prev, y_gt):   #训练数据"""Forward pass."""# zero gradientsself.encoder_optimizer.zero_grad()  # 初始化self.decoder_optimizer.zero_grad()# 编码器input_weighted, input_encoded = self.Encoder(Variable(torch.from_numpy(X).type(torch.FloatTensor).to(self.device)))# 解码器y_pred = self.Decoder(input_encoded, Variable(torch.from_numpy(y_prev).type(torch.FloatTensor).to(self.device)))y_true = Variable(torch.from_numpy(y_gt).type(torch.FloatTensor).to(self.device))y_true = y_true.view(-1, 1)loss = self.criterion(y_pred, y_true)loss.backward()self.encoder_optimizer.step()self.decoder_optimizer.step()return loss.item()

论文笔记及Pytorch复现:A Dual-Stage Attention-Based Recurrent Neural Network for Time Series Prediction相关推荐

  1. Attention-Based Recurrent Neural Network Models for Joint Intent Detection and Slot Filling论文笔记

    文章目录 摘要 方法 Encoder-Decoder Model with Aligned Inputs Attention-Based RNN Model 实验 论文连接:Attention-Bas ...

  2. 论文笔记与解读《DRAW: A Recurrent Neural Network for Image Generation》

    前言 笔者临近硕士毕业,我的硕士毕业项目十分类似一个非常著名的研究工作: DRAW: A Recurrent Neural Network for Image Generation,该项工作由2015 ...

  3. 【李宏毅机器学习笔记】 23、循环神经网络(Recurrent Neural Network,RNN)

    [李宏毅机器学习笔记]1.回归问题(Regression) [李宏毅机器学习笔记]2.error产生自哪里? [李宏毅机器学习笔记]3.gradient descent [李宏毅机器学习笔记]4.Cl ...

  4. 【李宏毅机器学习】Recurrent Neural Network Part1 循环神经网络(p20) 学习笔记

    李宏毅机器学习学习笔记汇总 课程链接 文章目录 Example Application Slot Filling 把词用向量来表示的方法 1-of-N encoding / one-hot Beyon ...

  5. 2021李宏毅机器学习课程笔记——Recurrent Neural Network

    注:这个是笔者用于期末复习的一个简单笔记,因此难以做到全面详细,有疑问欢迎大家在评论区讨论 I. Basic Idea 首先引入一个例子,槽填充(Slot Filling)问题: Input: I w ...

  6. 读论文《Recurrent neural network based language model 》

    读论文<Recurrent neural network based language model > 标签(空格分隔): 论文 introduce 本文将循环神经网络RNN引入了神经网络 ...

  7. Long Short-Term Memory Recurrent Neural Network Architectures for Large Scale Acoustic Modeling论文阅读

    <Long Short-Term Memory Recurrent Neural Network Architectures for Large Scale Acoustic Modeling& ...

  8. 【读论文】Loop Closure Detection for Visual SLAM Systems Using Convolutional Neural Network

    [读论文]Loop Closure Detection for Visual SLAM Systems Using Convolutional Neural Network 发表于2017年,作者是南 ...

  9. 【论文阅读】Recurrent Neural Network Regularization

    本来是打算搜搜RNN或者LSTM的原文,结果arxiv上没搜到(求求,帮帮),然后就搜到知乎上一个回答: 心想完蛋,年份跨度比较远的论文读起来会不会很费劲啊.结果搜到LSTM的原论文了(论文链接:ht ...

最新文章

  1. mysql 数据库安装命令_教你MySQL数据库的编译安装以及命令详解(5.7版本)
  2. Python获取磁盘使用信息,python获取GPU信息,python根据进程号获取进程信息,pynvml 获取GPU信息,psutil 获取进程信息,系统信息等
  3. STM32之中断嵌套控制器
  4. 阿里云终端连接与实例管理
  5. MYSQL主从不同步延迟原理分析及解决方案
  6. Linux 邮件服务器 之跟我一步一步来实现一个邮件系统
  7. LBP(Local Binary Patterns)局部二进制模式
  8. python 怎么取对数_重新开始学习Python 第二十八天 Python 数学模块
  9. java下载文件或文件夹
  10. 制作双系统U盘启动盘【Ventoy】
  11. 很有道理的程序员的小故事
  12. R语言循环读取excel并保存为RData
  13. 支付订单中未付款倒计时
  14. 在MySQL中第一章选择题_北大青鸟第二学期 123章数据库选择题
  15. C++描述 LeetCode 5676. 生成交替二进制字符串的最少操作数
  16. 远程控制桌面计算机怎么填写,远程控制电脑桌面如何操作【图解】
  17. Arduino案例实操 -- 智能巡防小车(四)火焰检测功能扩展
  18. 深入 Go 中各个高性能 JSON 解析库
  19. 把Excel批注的“红三角”放在单元格左上角_Excel的批注功能,全部知道的不足10%,你会用的仅仅是冰山一角...
  20. Deepdive 教程--数据准备

热门文章

  1. 创新实训1 小组分工以及项目部署
  2. element-icon 图标大全
  3. Google Docs API 介绍
  4. 葫芦侠三楼API整理
  5. A.O.史密斯净水新品闪耀德国IFA展 斩获年度产品创新成果奖
  6. 运用hibernate接口实现增删改查
  7. ios怎么引入masonry_IOS Masonry的基本使用
  8. 容器云时代,Commvault的变与不变
  9. 【坤坤讲师--图】Dinic
  10. MATLAB | 如何从热图中提取数据