基于Transformer模型的智能选股策略
由lifei521创建,最终由small_q 被浏览 285 用户
旧版声明
本文为旧版实现,仅供学习参考。
https://bigquant.com/wiki/doc/demos-ecdRvuM1TU(新版开发环境下的模版目录)
\
导语
RNN、LSTM和GRU网络已在序列模型、语言模型、机器翻译等应用中取得不错的效果。循环结构(recurrent)的语言模型和编码器-解码器体系结构取得了不错的进展。
但是,RNN固有的顺序属性阻碍了训练样本间的并行化,对于长序列,内存限制将阻碍对训练样本的批量处理。这样,一是使得RNN的训练时间会相对比较长,对其的优化方法也比较少,二是对于长时间记忆来说,其的效果也大打折扣。
而Transformer的核心,注意力机制(Attention)允许对输入输出序列的依赖项进行建模,而无需考虑它们在序列中的距离,这样对上面两个RNN中比较突出的问题就有了一个比较好的解决办法。本文将对Attention、Transformer以及其在选股策略中的应用进行讲解。
\
Attention介绍
Attention本质
Attention机制如果浅层地理解,跟它的名字非常匹配。它的核心逻辑就是“从关注全部到关注重点”。
Attention机制很像人类看图片的逻辑,当我们在看一张图片时,我们并没有看清图片的全部内容,而是将注意力集中在了图片的焦点上:
比如上面的图片,我们一眼望去,最先接收到的是“锦江饭店”的信息,所以,我们的视觉系统就是一种 Attention 机制,将有限的注意力集中在重点信息上,从而节省资源,快速获得最有效的信息。
Attention机制最早是在CV中应用的,随后在NLP领域也开始应用了,真正发扬光大的是在NLP领域,因为2018年BERT和GPT效果出奇地好,进而走红,Transformer、Attention这些核心开始被大家重点关注。
\
Attention原理
在说到Attention的原理之前,我们要先介绍一下什么是 Encoder-Decoder结构。
Encoder-Decoder模型
encoder-decoder模型也可以称为 seq2seq,它是为了应对 RNN 中无法满足不等长序列的输入输出而产生的一个 RNN 变种。encoder-decoder是深度学习中非常常见的一个模型框架,比如无监督算法的 auto-encoder 就是用编码的结构设计并训练的,再比如神经网络机器翻译NMT模型,往往就是LSTM-LSTM的编码-解码框架。
准确的说,encoder-decoder并不是一个具体的模型,而是一类框架。Encoder和Decoder部分可以是任意的文字,语音,图像,视频数据,模型可以采用CNN,RNN,BiRNN,LSTM,GRU等等。其中,encoder对数据进行编码,decoder对encoder生成的编码进行解码。
Encoder
encoder先将输入数据编码成一个上下文向量c:
得到 c 的方式有多种,最简单的方法就是把encoder的最后一个隐状态赋值给 c ,还可以对最后的隐状态做一个变换得到 c,也可以对所有的隐状态做变换。
Decoder
拿到 c 后,就用另一个 RNN 网络对其进行解码,这部分 RNN 网络被称为 Decoder,具体的做法就是将 c 当作之前的初始化状态 h0 输入到 Decoder 中:
Attention图解
在 encoder-decoder 结构中,encoder把所有的输入序列都编码成一个统一的语义特征 c 再解码,因此,c 中必须包含原始序列中的所有信息,它的长度就变成了限制模型性能的瓶颈,如机器翻译问题,当要翻译的句子较长时,一个 c 可能存不下那么多信息,就会造成翻译精度的下降。
Attention机制通过在每个时间输入不同的 c 来解决这个问题,下面是带有 Attention 机制的 Decoder:
每一个 c 会自动去选取与当前所要输出的 y 最合适的上下文信息。具体来说,我们用 aij 衡量 encoder 中第 j 阶段的 hj 和解码时第 i 阶段的相关性,最终 decoder 中第 i 阶段的输入的上下文信息 ci,就来自于所有 hj 对 aij 的加权和。这里的 aij 就是我们要学习的参数。
比如上图中,输入的序列是“我爱中国”,因此,Encoder中的 h1、h2、h3、h4 就可以分别看是“我”、“爱”、“中国”所代表的信息。在翻译成英语时,第一个上下文 c1 应该和“我”这个字最相关,因此对应的 a11 就比较大,而相应的 a12、a13、a14 就比较小。c2 应该和“爱”最相关,同理,a22 的值就比较大。
那么 aij 这个权重是怎么算出来的呢?
事实上,aij 是从模型中学出来的,它实际和 decoder 的第 i - 1 阶段的隐状态、encoder第 j 个阶段的隐状态有关。比如 a1j 的计算: Attention中的数学计算
上面都是对 Attention 机制一个直观上的理解,现在咱们用数学公式来对其进行解读:
这里关键的操作是计算 encoder 和 decoder state 之间的关联性的权重,得到 Attention 分布,从而对于当前输出位置得到比较重要的输入位置的权重,在预测输出时相应的会占较大的权重。通过 Attention 机制,我们打破了只能利用 encoder 最终单一向量(即只能利用一个 c )结果的限制,从而使模型可以集中在所有对于下一个目标单词重要的输入信息上,使模型效果得到极大的改善。并且,通过观察 attention 权重矩阵的变化,可以更好地知道哪些部分翻译对应哪部分源文字,有助于更好地理解模型工作机制。
Attention中的QKV
熟悉Attention的都知道,我们使用 Attention时,一般都使用 Attention(Q, K, V) 来进行表示。
其中,Q表示 Decoder 的(上一次)隐状态,K 表示 Encoder 中的隐状态,V表示 Encoder 的输入。 以上面的计算为例:St-1 就是 Q,K就是 (h1, h2, h3 .., hT),V就是(x1, x2, x3, ..., xT)。
也有一个很形象的比喻可以进行理解:
Attention优点
参数少
模型复杂度跟CNN、RNN相比,复杂度更小,参数也更少,所以对算力的要求也就更小。
速度快
RNN不能并行计算的问题,Attention机制每一步计算不依赖于上一步的计算结果,因此可以和CNN一样并行处理。
效果好
在Attention机制引入之前,有一个问题一直给大家带来困扰:长距离的信息会被弱化,就好像记忆能力弱的人,记不住过去的事情是一样的。Attention是挑重点,就算文本比较长,也能从中间抓住重点,不丢失重要的信息。
\
Transformer介绍
Transformer抛弃了传统的 CNN 和 DNN,整个网络结构完全是由 Attention 机制组成。更准确地讲,Transformer由且仅由 self-attention 和 feed forward neural network 组成。一个基于 Transformer 的可训练的神经网络可以通过堆叠 Transformer 的形式进行搭建,《Attention is all you need》文中,作者通过搭建编码器和解码器各6层,总共12层的 encoder-decoder,并在机器翻译中取得了 BLEU 的新高。
高层 Transformer
Transformer 本质上是一个 encoder-decoder 结构:
Encoder 由 6 个编码 block 组成,同样解码器是由 6 个解码 block 组成的。与所有的生成模型相同的是,编码器的输出会作为解码器的输入(Encoder-Decoder Attention 结构中):
\n Encoder结构
继续分析每个encoder的详细结构:在Transformer的encoder中,数据首先会经过一个叫做‘self-attention’的模块得到一个加权之后的特征向量Z,这个Z便是论文中的 Attention(Q,K,V):
得到 Z 后,它会被送到 encoder 的下一个模块,即 Feed Forward Neural Network(FNN)。这个全连接有两层,第一层的激活函数是 ReLU,第二层是一个线性激活函数:
所有的编码器在结构上都是相同的,但它们没有共享参数,每个解码器都可以分解成两个子层。
\
Decoder结构
它和 Encoder 的不同之处在于 Decoder 多了一个 Encoder-Decoder Attention,并且,Decoder 的 Self-Attention 为 multi-self-decoder Attention(带有 mask),两个 Attention 分别用于计算输入和输出的权值:
- Self-Attention:当前翻译和已经翻译的前文之间的关系
- Encoder-Decoder Attention:当前编码和翻译的特征向量之间的关系
encoder 和 decoder 都是有多层的(论文中取的 6 层),并且,除了第一层,其他的每一层 encoder 的输入来自上一层的 encoder 的输出,同样,decoder 的输入也来自上一层的 decoder 的输出。encoder 和 decoder 的交互是在 encoder-decoder attention 中。
\
输入编码
首先通过 Word2Vec 等词嵌入方法将输入预料转化为特征向量,论文中使用的词嵌入的维度为 d(model) = 512:
位置编码
位置信息其实算是 Transformer 中的缺点的权宜之计,其丢弃了 RNN 和 CNN ,使得模型其实丧失了捕捉局部特征的能力。具体的,位置编码会在词向量中加入单词的位置信息,这样 Transformer 就能区分不同位置的单词了。
如何编码这个位置信息?常见的有:a:根据数据学习;b:自己设计编码规则。通常,位置编码是一个长度为 dmodel 的特征向量,这样便于和词向量进行单位加的操作(融合两者的信息): Self Attention
Attention 的核心内容就是为输入向量的每个单词学习一个权重,例如在下面的例子中,我们判断 it 代指的内容:
The animal didn't cross the street because it was too tired
通过加权之后得到类似下图的情况:
在 self-attention 中,每个单词有3个不同的向量,它们分别是Query(Q),Key向量(K)和Value(V),长度均是64,它们是通过3个不同的权值矩阵由嵌入向量 X 乘以三个不同的权值矩阵 WQ,WK,WV 得到,其中三个矩阵的尺寸也是相同的,均为 512 * 64.
![](/wiki/api/attachments.redirect?id=818e8c43-096e-4908-a9a4-1ef3d62324c5) ![](/wiki/api/attachments.redirect?id=5a8c32df-4536-4c08-8534-a52f6a813f16) ![](/wiki/api/attachments.redirect?id=76da5557-6fac-4c28-a094-e3288092290d)
总的来说,就是:
上面是 Attention 的部分,要注意的是,在 self-attention 中,除了 attention,还有残差网络的 short-cut 结构,目的自然是为了解决深度学习中的退化问题,得到的最终结果如下图:
Multi-Head Attention
Multi-Head Attention 相当于 n 个不同的 self-attention 的集成(ensemble),在这里以 h=8 举例。Multi-Head Attention 的输出分为 5 步:
当然,Multi-Head Attention 中也加入了 short-cut 机制。
Transformer用于选股的优化
经过上面的介绍,大家对Transformer能有个系统一点的认识,由于上述应用于NLP领域的Transformer,结构过于庞大,并且不适用于股票因子数据这样的时序数据,因此,参考相关论文,本文提出了一种简化的适用于股票数据的Transformer结构,其根据时间嵌入的思想构建,能很好的应用于量化选股。下面先来讲解下如何进行时间的嵌入。
时间嵌入
首先,明确下我们模型的输入数据,一个(32,128,5)的三维张量,其由 32 条(batchsize=32)时间序列数据构成,每个序列的长度为 128 天(seq_len=128),每天使用 5 个因子数据。
在处理时间序列数据时,顺序性是必须要考虑的因素。但是,当使用 Transformer 处理时间序列 / 顺序数据时,序列数据会一次同时通过 Transformer,这使得提取时间 / 顺序依赖关系变得困难。因此,将 Transformer 应用于 NLP 时,我们倾向于利用位置编码来向模型提供单词顺序的概念。简单来讲,位置编码是单词在句子中的位置的表示,使Transformer可以获取有关句子结构和单词相互依赖性的知识。其具体的生成方式在上文已经介绍,这里便不再赘述。实践证明使用了位置编码的Transformer模型会拥有更好的效果。
同样,在处理我们的股票因子数据时,Transformer需要时间的概念。没有时间嵌入,我们的Transformer将不会获取到股票因子数据的时间顺序信息。比如,1956年的行情信息对预测2021年的股票涨跌并没有多大帮助。并且,股票因子时序数据不仅包含顺序信息,还包含其特有的时间特性,因此不能简单的把位置编码用于处理时序数据,我们这里使用时间嵌入的方法来获取股票因子数据的时间特征信息。
Time2Vec
为了实现时间嵌入,我们将使用论文:《Time2Vec:Learning a Vector Representation of Time》 中描述的方法。文中提出一种“对于时间的模型无关向量表示,叫做Time2Vector”。你可以认为一个该向量表示,就像一层普通的 embedding layer 一样,可以被添加到神经网络结构来提高一个模型的性能。
这篇论文主要有两个思想:
-
作者发现时间有意义的表示必须包括周期性和非周期性两个模式。周期性模式的一个例子就是每年不同季节的天气变化。相反,病毒传播就是一个非周期性的事件。
-
时间表示应该对时间缩放具有不变性,这意味着时间表示不受不同时间增量的影响。结合周期性和非周期性模式的思想以及时间缩放的不变性,我们通过以下数学定义来表示:
时间向量 t2v 由两部分取得,其中 ωᵢτ + φᵢ 表示时间向量的非周期/线性特征, F(ωᵢτ + φᵢ) 表示时间向量的周期性特征。 ωᵢτ + φᵢ 是时间向量的线性函数。 第二部分 F(ωᵢτ + φᵢ) 代表时间向量的周期性特征。其中 ωᵢτ + φᵢ 和第一部分一样代表时间向量的线性函数,然后将其输入给添加的函数 F(), 作者尝试了不同的 F() 以最好地描述周期性关系(sigmoid,tanh,ReLU,mod,triangle等)。最后, 正弦函数 获得了最佳和最稳定的性能(余弦得到了类似的结果)。
\
股票数据的Transformer结构
给输入数据加上时间特征
在实现时间嵌入之后,我们给输入数据加上时间向量作为我们的Transformer的输入。所述 Time2Vector 层接收输入数据,计算非周期和周期时间向量并拼接。然后,将计算出的时间向量与因子向量连接起来,形成 (32, 128, 7) 的矩阵,具体过程如下:
Self-Attention层
本层有三个输入 (Query, Key, Value), 三个输入均为上一步得到的输入矩阵,然后分别输入三个分隔的Dense Layer,其有96个神经元,对因子时间向量进行embedding,得到的输出矩阵分别用 q,k,v 表示,大小均为(32, 128, 96),如下图所示:
初始线性转换后,我们将计算注意力得分。注意力得分决定了在预测未来股价时将注意力集中在各个时间序列上的程度。注意力得分是通过 k 和 q 的点积来计算得到的,即 q 矩阵乘 k 的转置的。然后,将点积除以Dense Layer的大小的开方,以避免梯度爆炸。相除的点积将通过 softmax 函数生成一组权重,这些权重之和为1,该权重即为各个时间点在的注意力得分。最后,将等到的注意力得分矩阵与 v 矩阵相乘,得出自注意力层的输出。过程如下图所示:
Multi-Head Attention 层
多头注意力层的功能是拼接 n 个单头注意力层 ,然后输出到Dense Layer进行非线性变换。下图显示了3个单头自注意力层的串联。具有多头自注意力机制允许将多个独立的单头层输出作为输入。因此,该模型能够一次关注多个时间序列步骤。注意力头数量的增加会加强模型捕获远程依赖关系的能力。
编码器
每个编码器层都包含一个自注意力子层和一个前馈子层。前馈子层由两个Dense Layer组成,中间有ReLU激活。每个子层后面都有一个子层,每两个子层之间通过将初始Query输入添加到上一个子层的输出中来形成残差连接,然后接入归一化层,以加速训练过程。如下图所示:
以上便是Transformer层,我们可以轻松的对其进行堆叠以提高模型的性能。
总体结构
总之,我们首先初始化时间嵌入层以及3个Transformer层。初始化之后,我们将回归头(输出大小为1的Dense Layer)堆叠到最后一个子层,然后开始训练过程。
\
可视化实现Transformer
Transformer的底层逻辑确实比较复杂,但是在实际使用中,只要能够理解其大致原理和关键参数,通过调用平台上相关的各种可视化模块,便能省时省力地构建深度学习网络,从而将精力更多地放在策略逻辑优化上。下面,我们将以一个可视化实例展现如何通过Transformer模型进行选股,这里的Transformer模型采用的是上述所介绍的架构。
如图所示,Transformer选股策略构建包含下列步骤:
- 获取数据:A股所有股票;
- 特征和标签提取:计算7个因子作为样本特征,计算5日个股收益率,极值处理;
- 特征预处理:进行缺失值处理;去掉特征异常的股票,比如某个特征值高于99.5%或低于0.5%的;标准化处理,去除特征量纲/数量级差异的影响;
- 序列窗口滚动:窗口大小设置为5,滚动切割。这里的意思是使用 过去5天的因子数据 作为输入。窗口大小可调整,在“序列窗口滚动”模块中进行;
- 搭建Transformer模型 :构建单头注意力层的Transformer模型预测股票收益率(keras代码方式构建);
- 模型训练与预测:使用Transformer模型进行训练和预测;
- 策略回测:利用2010到2016年数据进行训练,预测2016到2019年的股票表现。每日买入预测排名最靠的30只股票,至少持有5日,同时淘汰排名靠后的股票。具体而言,预测排名越靠前,分配到的资金越多且最大资金占用比例不超过20%;初始5日平均分配资金,之后,尽量使用剩余资金(这里设置最多用等量的1.5倍);
- 模型评价:查看模型回测结果。
Transformer模型的参数如下:
-
输入数据 :2个因子,使用了过去5天的因子数据,因此输入是一个三维张量(batch_size, 5, 2);
-
Transformer层 :Time2Vec层权重和偏置向量均采用glorot_uniform初始化方式初始化,自注意力层3个输入Dense Layer大小均为100,自注意力头数为1,编码器Dense Layer隐藏层的大小为256、 激活函数为:relu,Layer归一化层的epsilon为1e-6;
-
全连接层 :激活函数tanh。权重使用glorot_uniform初始化方法,偏置向量使用Zeros初始化方法;
-
输出层 :最后一个全连接层。需要选择“输出空间维度”为1,因为要得到个股的收益率预测结果,这是一个值;
-
随机断开输入神经元比例dropout :0.1。在不同隐藏层之间使用dropout可以让网络更耐用并且 避免过拟合 ;
-
训练次数率 :epochs值为5,共训练5轮,以mae、mape作为评估指标;
-
优化器 :Adam;
-
损失函数 :均方误差MSE。
\
\
策略模板
https://bigquant.com/experimentshare/a79e1bec856141d4a7c810ca0d091eb8
参考链接
https://zhuanlan.zhihu.com/p/48508221
https://zhuanlan.zhihu.com/p/85864250
\