AAAI最佳论文Informer:效果远超Transformer的神器
- 1 简介
-
- 1.1 Informer的整体架构
- 2 预处理 Preliminary 与样本生成
-
- 2.1 Encoder输入
- 2.2 Decoder输入
- 3 Embedding
- 4 EncoderStack
-
- 4.1 单个Encoder
-
- 4.1.1 传统 Transformer 的 Multi-head Self-attention
- 4.1.2 ProbSparse Self-attention
- 4.1.3 Lazy queries 的处理和多头拼接
- 4.2 EncoderStack : 多个Encoder和蒸馏层的组合
-
- 4.2.1 EncoderStack结构介绍
- 4.2.2 Encoder Stack 源码
- 5 Decoder
-
- 5.1 Decoder 结构
- 5.2 Generative Style Decoder
- 6 计算损失与迭代优化
- 7 代码
- End 2021/06/03
之前在网上搜索了很多informer的解读文章,但大部分看到的都是文章翻译甚至机翻,看完之后依然对算法原理一头雾水。
Github论文源码
自己看过代码之后想要写一篇详细一点的解读,码在这里也方便自己以后回来翻看。
另外感谢@Error_hxy大佬的帮助!
由于Informer主要是在Transformer上的改进,这里不再赘述Transformer的细节,可以参见另外的博文:
深入理解Transformer及其源码解读
最火的几个全网络预训练模型梳理整合(从ELMO到BERT到XLNET)
1 简介
那么Informer是做什么的呢?
主要针对长序列预测(Long Sequence Time-series Forecasting, LSTF)
目前Transformre具有较强的捕获长距离依赖的能力,但传统的Transformer依然存在以下不足,因此Informer做出了一些改进。
上面的三个改进猛地一看可能让人摸不着头脑
没关系
我们接着往下看
1.1 Informer的整体架构
下面我们对Informer的结构图进行一下简单的解释。
- 红色圈:用 ProbSparse Self-attention 代替了 self-attention ,采用一种新的attention机制减少了计算量
- 蓝色圈:Self-attention Distilling,减少维度和网络参数量
- 黄色圈:Layer stacking replicas 提高了鲁棒性
- 绿色圈:Decoder 获得长序列输入,目标部分用0进行padding
- 紫色圈:Generative style预测目标部分,一步生成预测,而不是动态解码
如果看到这里还不太知道Informer是在干啥,那么我们从预处理开始一点一点看起。
2 预处理 Preliminary 与样本生成
上面的Xt,YtX^t,Y^tXt,Yt就是模型的输入和输出。
我们以代码中给出的某一个数据为例:
数据介绍:ETDataset(Electricity Transformer Dataset)电力变压器的负荷、油温数据。
ETDataset (github)
(小时维度的数据) 数据规格:17420 * 7
Batch_size:32
下面这张图是ETDataset的一个示例
这里并不是小时维度,而是15min的时间序列数据
那么输入模型的样本大概长什么样子?
2.1 Encoder输入
Xenc:32×96×7X_{enc}:32\times96\times7Xenc:32×96×7
这里的32是批次大小,一个批次有32个样本,一个样本代表96个时间点的数据,如上图
date=2016-07-01 00:00 是一个时间点0的数据
date=2016-07-01 01:00 是时间点1的数据。
那么批次中的样本1:时间点0到时间点95的96个维度为7的数据
批次中的样本2:时间点1到时间点96的96个维度为7的数据
批次中的样本3:时间点2到时间点97的96个维度为7的数据
……
直到取够32个样本,形成一个批次内的所有样本。
Xmark:32×96×4X_{mark}:32\times96\times4Xmark:32×96×4
这里的4代表时间戳,例如我们用小时维度的数据,那么4分别代表
年、月、日、小时,
第一个时间点对应的时间戳就是[2016, 07, 01, 00],
第二个时间点对应的时间戳就是[2016, 07, 01, 01]
与上面的XencX_{enc}Xenc对应得到所有的样本对应的时间戳。
2.2 Decoder输入
Xenc:32×72×7X_{enc}:32\times72\times7Xenc:32×72×7
Xmark:32×72×4X_{mark}:32\times72\times4Xmark:32×72×4
decoder的输入与encoder唯一不同的就是,每个样本对应时间序列的时间点数量并不是96,而是72。具体在进行截取样本时,从encoder输入的后半段开始取。
即:
encoder的第一个样本:时间点0到时间点95的96条维度为7的数据
那与之对应decoder的:时间点47到时间点95的48条维度为7的数据 + 时间点 95到时间点119的24个时间点的7维数据。
则最终48+24是72维度的数据。画成图大概这个亚子:
上面是encoder的输入
下面是decoder的输入
均只画出时间点数量的那个维度
3 Embedding
输入:
x_enc/y_dec
:32 * 96/72 * 7
x_mark /y_mark:
32 * 96/72 *4
输出:
32 * 96/72 * 512
embedding的目的: 嵌入投影,在这里相当于将维度为7的一个时间点的数据投影成维度为512的数据。
整体公式:
等号右边显然可以分成三个部分:
1.输入:对应公式中的 αuit\alpha u_i^tαuit
具体操作为通过conv1D(width=3,stride=1) 映射为D维度(512)
(conv1D是一维卷积)
2.Position Stamp: 对应公式中的 PE(Lx×(t−1)+i)PE_{(L_x\times(t-1)+i)}PE(Lx×(t−1)+i)
与Transformer的postition embedding 一模一样
3.Global Time Stamp:对应公式中的 SE
具体操作为全连接层。
长序列预测问题中,需要获取全局信息,比如分层次的时间戳(week, month year),不可知的时间戳(holidays, events).
这里最细的粒度可以自行选择。
𝛼:是平衡标量投影和局部/全局嵌入之间大小的因子,如果输入序列已经标准化过了,则该值为1。
那么把上面的1,2,3加起来,就是最终模型要输入encoder或者decoder的数据。
那么,embedding部分到这里就结束了~~~
4 EncoderStack
输入:
32 * 96 * 512(来自上面embedding 长度为96的部分)
输出:
3251512(这里的51应该是conv1d卷积取整导致的,因为源码要自行调整,所以是这样的)
4.1 单个Encoder
整个EncoderStack是由多个Encoder组合而成的。首先来讲一个Encoder的编码过程。
encoder的目的: 提取长序列输入的鲁棒性远程依赖。
既然讲到encoder,encoder部分的核心必然是计算attention。
4.1.1 传统 Transformer 的 Multi-head Self-attention
回顾一下传统的attention:
这些图相信大家都反复看过啦(没看过的看最上面的推荐~)
QKV分别是 查询向量(queries)、键向量(keys)和值向量(values)组成的矩阵。
上面的图是仿照 Jay Alammar Blog 大佬的图画的。
其中如果用qiq_iqi,kik_iki,viv_ivi代表QKV矩阵中的第i行,那么可以把最终输出𝑍的第i行表示成:
A(qi,K,V)=∑jk(qi,kj)∑lk(qi,kl)vj=Ep(kj∣qi)[vj]A(q_i,K,V) = \sum_j{\frac{k(q_i,k_j)}{\sum_l{k(q_i,k_l)}}v_j = E_{p(k_j|q_i)}[v_j] }A(qi,K,V)=∑j∑lk(qi,kl)k(qi,kj)vj=Ep(kj∣qi)[vj]
k(qi,kj)k(q_i,k_j)k(qi,kj)其实是非对称的指数核函数exp(qikjTd)exp(\frac{q_i k_j^T}{\sqrt{d}})exp(dqikjT)
那么也就是用p(kj∣qi)p(k_j|q_i)p(kj∣qi)对值向量也就是V矩阵进行加权求和。
这部分结果其实是一个2*2的矩阵,像下面这样。经过softmax之后变成了概率矩阵。也就是其中的0.88,0.12代表的是,“在”这个字分配给“在”它本身和“电”这个字的注意力概率分别为0.88和0.12.用这两个概率分别和他们对应的值向量加权,再求和,从而得到最终的输出向量z。
4.1.2 ProbSparse Self-attention
输入: 32×8×96×6432\times8\times96\times6432×8×96×64 (8×64=5128\times64 = 5128×64=512 ,这也是多头的原理,即8个头)
输出:32×8×25×6432\times8\times25\times6432×8×25×64
我们回顾了传统的注意力机制,引入了注意力概率矩阵,下面直接进入到最精彩的部分——ProbSparse的注意力机制。
传统Self-attention需要O(LQLK)O(L_QL_K)O(LQLK)的内存以及二次点积计算代价,是其预测能力的主要缺点。
通过研究发现,稀疏性self-attention得分形成长尾分布,即少数点积对主要注意有贡献,其他点积对可以忽略。
也就是说上面的概率矩阵不一定都是0.88,0.12这样有侧重的分布(也就是Active Query),有可能是0.5,0.5这样接近均匀分布(Lazy Query)。类似于作者画的下面的图。
那么如何对这里的active和lazy进行量化区分呢?
作者认为突出的点积对导致对应的query的注意力概率分布远离均匀分布
p(kj∣qi)=k(qi,kj∑lk(qi,kl)p(k_j|q_i) = \frac{k(q_i,k_j}{\sum_lk(q_i,k_l)}p(kj∣qi)=∑lk(qi,kl)k(qi,kj 是注意力概率分布
q(kj∣qi)=1LKq(k_j|q_i)=\frac{1}{L_K}q(kj∣qi)=LK1 是均匀分布
(这里的LkL_kLk就是query查询向量的长度)
那么提出一个比较自然的想法:度量两种分布的距离:运用KL散度公式。
KL散度(Kullback-Leibler divergence)又称相对熵(relative entropy)是描述两个概率分布P和Q差异的一种方法,是非对称的。
离散的KL散度定义如下
A(qi,K,V)=∑jk(qi,kj)∑lk(qi,kl)vj=Ep(kj∣qi)[vj]A(q_i,K,V) = \sum_j\frac{k(q_i,k_j)}{\sum_lk(q_i,k_l)}v_j = E_{p(k_j|q_i)}[v_j]A(qi,K,V)=∑j∑lk(qi,kl)k(qi,kj)vj=Ep(kj∣qi)[vj]
p(kj∣qi)=k(qi,kj∑lk(qi,kl)p(k_j|q_i) = \frac{k(q_i,k_j}{\sum_lk(q_i,k_l)}p(kj∣qi)=∑lk(qi,kl)k(qi,kj 传统Transformer中的概率分布公式
q(kj∣qi)=1LKq(k_j|q_i) = \frac{1}{L_K}q(kj∣qi)=LK1 均匀分布
k(qi,kj)k(q_i,k_j)k(qi,kj)是非对称的指数核函数exp(qikjTd)exp(\frac{q_i k_j^T}{\sqrt{d}})exp(dqikjT)
代入KL散度公式(如下进行简单的加减乘除运算)
舍弃常数项,最终定义第i个query sparsity的评估如下式
第一项是qiq_iqi和所有keys内积的log-sum-exp,第二项是他们的算术平均。
然而这样的计算依然有O(LQLK)O(L_QL_K)O(LQLK)的内存使用,并且LSE存在潜在的数值稳定性问题。据此提出query sparsity评估的近似:
近似过程如图所示:
因此随机选择U=LQlnLKU=L_QlnL_KU=LQlnLK个点积对计算 Mˉ(qi,K)\bar M(q_i,K)Mˉ(qi,K)这样使复杂度降低到O(LlnL)O(LlnL)O(LlnL),选出其中的top u个 queries 也就是查询向量。在代码的默认参数中U使25。
那么这里的25个queries就是作者认为相对远离均匀分布,也就是不那么lazy的分布。
最终:
这里的Qˉ\bar{Q}Qˉ就是topu个queries选拔后的。计算流程如下图所示:
这里首先还是看一下传统transformer计算attention的过程
而probsparse使用Qˉ\bar{Q}Qˉ计算
因此最终得到的 ProbSparse Self-attention计算的输出维度为:32×8×25×6432\times8\times25\times6432×8×25×64
4.1.3 Lazy queries 的处理和多头拼接
从上图可以看出来,其实用Qˉ\bar{Q}Qˉ计算 attention,得到的 z 是 25*96 维的,也就是说只表示了25个 active queires 对应的时间点自注意力编码后的向量。那么剩下的时间点怎么办呢?
作者采取的办法是,用V向量的平均来代替剩余的 Lazy queries 对应的时间点的向量,如下图所示。
32×8×25×6432\times8\times25\times6432×8×25×64 -> 32×8×96×6432\times8\times96\times6432×8×96×64 的过程
这样做的原因是,通过判定得到的 Lazy queries 的概率分布本来就接近均匀向量,也就是说这个时间点对96个时间点的注意力是均衡的,每个都差不多,所以他们atteniton计算后的向量也应当接近于全部V向量的平均。
这样也就得到了全部96个时间点的向量表示。
在将8个头的注意力计算结果合并
32×8×96×6432\times8\times96\times6432×8×96×64 -> 32×96×51232\times96\times51232×96×512
4.2 EncoderStack : 多个Encoder和蒸馏层的组合
输入: 32×96×51232\times96\times51232×96×512
输出: 32×51×51232\times51\times51232×51×512
4.2.1 EncoderStack结构介绍
论文中提出的EncoderStack 其实是由多个Encoder 和蒸馏层组合而成的
那么我们来详细解释一下上面的这张图。
1.每个水平stack代表一个单独的Encoder Module;
啥意思呢?
也就是说,下面用红色笔圈出来的这一坨是第一个stack
而剩下那部分是第二个stack
下面的stack还写了similar operation
2.上面的stack是主stack,它接收整个输入序列,而第二个stack取输入的一半,并且第二个stack扔掉一层自我注意蒸馏层的数量,使这两个stack的输出维度使对齐的;
这句话的意思是,红色圈圈的部分中输入减半,作为第二个stack的输入。(这里减半的处理方法就是直接用96个时间点的后48个得到一半)
画出来大概就是这样的吧。
3.红色层是自我注意机制的点积矩阵,通过对各层进行自我注意蒸馏得到级联降低;
这一句说的就是蒸馏的操作了,也就是上面手画图例的convLayer,还是通过一维卷积来进行蒸馏的。
4.连接2个stack的特征映射作为编码器的输出。
最后很简单,把手画图里的Encoder1 和 Encoder2 25和26的输出连接起来,就能得到最终的51维输出了。(这里的51或许还是因为卷积取整,导致这个数看起来不太整)
到这里Encoder Stack的结构介绍就告一段落了。
4.2.2 Encoder Stack 源码
图片里的1234分别对应上面结构解释中的1234
Github论文源码
5 Decoder
输入: 32×51×51232\times51\times51232×51×512 & 32×72×51232\times72\times51232×72×512
输出: 32×72×51232\times72\times51232×72×512
5.1 Decoder 结构
decoder 的输入有两部分,即下图中红色框圈出的两部分。
第一个 32×51×51232\times51\times51232×51×512 是 encoder 的输出
第二个 32×72×51232\times72\times51232×72×512 是 embedding 后的 decoder 的输入,即用0遮住了后半部分的输入。
下图就是decoder 的第二个输入,维度为32×72×51232\times72\times51232×72×512 。从图中可以看出,上面的是encoder的输入,下面是decoder,decoder 的后半部分被0遮盖住。整个模型的目的即为预测被遮盖的decoder的输出的部分。
5.2 Generative Style Decoder
同样我们先来回顾 transformer step-by-step 动态解码
Decoder的最后一个部分是过一个linear layer将decoder的输出扩展到与vocabulary size一样的维度上。经过softmax 后,选择概率最高的一个word作为预测结果。假设我们有一个已经训练好的网络,在预测时,步骤如下:
(1)给 decoder 输入 encoder 对整个句子 embedding 的结果 和一个特殊的开始符号 。decoder 将产生预测,在我们的例子中应该是 “I”。
(2)给 decoder 输入 encoder 的 embedding 结果和 “I”,在这一步 decoder 应该产生预测 “am”。
(3)给 decoder 输入 encoder 的 embedding 结果和 “I am”,在这一步 decoder 应该产生预测 “a”。
(4)给 decoder 输入 encoder 的 embedding 结果和 “I am a”,在这一步 decoder 应该产生预测 “student”。
(5)给 decoder 输入 encoder 的 embedding 结果和 “I am a student”, decoder应该生成句子结尾的标记,decoder 应该输出 ””。
(6)然后 decoder 生成了 ,翻译完成
上面的过程就是传统Transformer 解码器 decoder 的 step-by-step 解码。
而 Informer 提出了一种一步到位的预测,整个流程如下图所示
其中
1.最上面的 Encoder1 对应的是Encoder Stack 里的主 Stack, 而 Encoder2 则是将输入减半后的第二个 Stack。
将他们的输出连接,得到 32∗51∗51232*51*51232∗51∗512 的输出。也就是decoder的一部分输入。用这部分输入得到 keys 和 values。
2.decoder 的第二部分输入,即将预测部分用0遮盖后的输入向量计算 ProbAttention,也就是和Encoder的ProbSparse Self-Atteniton类似的操作,这里与encoder中不同的地方是 为了防止提前看到未来的部分进行了mask,原理如下图所示。
同时 Lazy queries 不再用 mean(V) 填充,而是用 Cumsum,也就是每一个queries之前所有时间点V向量的累加,来防止模型关注未来的信息。
即4.1.3 这张图的mean 改成累加
利用 ProbAttention 的结果计算得到 queries。
最终对这样的 queries, keys, values 计算 Full Attention, 得到想预测的部分(之前被0遮盖的部分)。这样使得模型通过一个前向过程直接预测所有输出,而不需要step – by-step 的动态解码。
以上就是Decoder的全部内容。
6 计算损失与迭代优化
损失函数为 预测值和真值的 MSE(均方误差)
优化器为 Adam
现在再来看开头的Informer 三大改进,是不是清楚了很多~
7 代码
有评论说我放上来的注释过的代码不是逐行注释,所以这里就不放了,要想真学到东西还是自己去看。
论文源码
菜鸡学生一枚,指导不了别人,需要帮助的朋友还是尽快和自己导师同学交流!