作者:chen_h
微信号 & QQ:862251340
微信公众号:coderpai
简书地址:http://www.jianshu.com/p/f9ca56d6407d
本文的代码请点击这里。Talk is cheap, show me the code.
在过去的几个月中,我对深度学习非常着迷,特别是它在自然语言处理中的应用。我的大部分职业生涯都是在金融领域度过的,特别是在算法交易和交易数据替换方面。
我写这篇文章的想法直接来自我的工作经验。我作为一名“深度学习”的爱好者时,对于深度学习我没有很深入的了解。但是我希望我的理解可以表达到位,并且可以帮助到你。
在很多的 NLP 问题中,我们最终会把序列编码成一个固定大小的表示,然后将这个编码再解码成另一个序列。例如,我们可能会在文本中进行实体标记,从英文翻译成法语,或将音频转换成文本。在这些领域都出现了大量的工作,并且很多成果都取得了最先进的表现。
在我看来,NLP 与金融数据分析之间的最大不同在于,语言有一定的结构性,虽然这个结构性比较模糊难懂。另一方面,金融市场数据可能是没有任何结构可以学习的。
在这个文章中,我们假设金融市场数据是存在结构性的,当然这个假设可能是不成立的,我们这个文章可能也会直接推翻这个假设。我们按照 NLP 的语义编码模型来类比这个市场结构,如果你觉得这个想法没有意义,那么请阅读下去,我相信你会觉得会有意义的。
词向量的模型有很多的,Richard Socher 的讲座是一个非常好的教程。简而言之,我们可以用语言模型来描述所有的单词,并且在坐标图中进行显示,并且利用几何图形来捕捉单词之间的关联性。有一个经典的例子是 “King - man + woman = Queen” ,或者类似别的例子。
词嵌入是非常酷的一个应用,因为我们可以利用一个很浓缩的方式来表示一个词信息。比较传统的词嵌入方法是构建一个包含所有单词的词表,如果当前词是查询词,那么该位置设置为 1 ,其余设置为 0 。但是这不是一个有效的方法,而且也没有任何词关联意义。但是通过词嵌入,我们用一个固定维度的向量来表示一个单词,然后利用更高维度的几何特性来寻找词之间的关联性。
下图展示了一个例子。词向量的模型是需要一个大型的语料库进行训练。经过几天的高密度训练,每个词都会得到一个高维度的词向量。这个“空间”向量会有类似于“距离”的概念,所以我们可以计算哪些词是互相接近的。该方法的作者举了一个例子(就是下图),用来计算跟 Frog 距离近的词。
但是除了词,我们对别的事物也可以进行高维度的编码,比如我们接下来要介绍的金融市场数据。
第一个我听说的词嵌入算法是 word2vec 。我想在金融市场数据上面得到相同的效果,虽然我会使用一些比较不同于 word2vec 的算法。我的输入数据是 csv 格式的。第一类表示日期,另外的 4*1000 列对应于 1000 只股票的高低开仓价。那么,我们的输入向量就是 4000 维度的,这个维度太大了。所以,我们第一件事就是把这个高维度的向量压缩到一个低维度的向量,比如说 300 维度(因为我们喜欢这个电影 ^_^)。
如果你觉得把一个 4000 维度的向量压缩到 300 维度是一件很难的事,那么你就大错特错了。其实这很简单,我们只需要乘以一个矩阵,矩阵就相当于一个巨大的 excel 表格,每个单元格中都会有一个数字,并且不存在任何的格式。想象一下,我们有一个 4000 列和 300 行的 excel 表格,我们把这个表格与上面的 4000 维度向量进行相乘,那么我们就可以得到一个 300 维度的向量。这个就是最基础的线性代数知识,你肯定能明白。
那么,现在的难点就是如何去设置这个巨大的表格矩阵,“深度学习” 就是用在这个矩阵的初始化和更新上面。最终,这个电子表格矩阵将会是一个不错的转移矩阵,可以将我们的 4000 维度向量转换成一个简洁的 300 维度向量。
接下来,我们要做一些比较神奇的事,那就是激活函数。我们要给每一个值加上一个激活函数。将这个激活函数添加到每一个值上面,将每个值的大小归到 0 到 1 之间。那么,为什么这样做可以使得我们的向量更加特别,可以学习和理解更加复杂的事。你可以点击这里进行学习。
那么接下来我们需要处理的重要的事就是如何去找到这个转移矩阵,使得我们可以损失更少的信息,将高维的股票向量转换成低维的股票向量。我们希望能利用这个向量来挖掘各个股票之间的相关性,也许我们会发现当某个行业下滑时,另一个行业的股票也会下滑。当然,我们可能也会发现一些别的特征,我认为这对我们将来的研究会非常有用。
让我们先把股票向量放在一边,来谈谈语言模型。Andrej Karpathy 写了一篇很流行的文章 The Unreasonable effectiveness of Recurrent Neural Networks 。对这篇文章可以简单总结如下:
然后这个模型会产生一大堆看起来像莎士比亚风格的文字。然后,我们也可以将这个思想用在 Linux 源码上面,然后程序来产生代码。或者,我们可以用在几何代数课本上面,让程序来产生内容。
这个魔术盒的实现机制我们会在后续进行介绍,但是让我们先来假想一下,Karpathy 是利用前面的一些字符来预测下一个字符,那么我们是否可以根据之前的市场向量,来预测下一个市场表现、市场向量或者别的东西,而这个预测模型就是这个魔术盒。我们还没有决定我们想要预测的是什么,但是没有关系,因为我们不会将我们的输出反馈到我们的输入中。
所以,Karpathy 向我们介绍了一些 Linux 例子的输出,下面的代码就是魔术盒的内容:
static void action_new_function(struct s_stat_info *wb)
{
unsigned long flags;
int lel_idx_bit = e->edd, *sys & ~((unsigned long) *FIRST_COMPAT);
buf[0] = 0xFFFFFFFF & (bit << 4);
min(inc, slist->bytes);
printk(KERN_WARNING "Memory allocated %02x/%02x, "
"original MLL instead\n"),
min(min(multi_run - s->len, max) * num_data_in),
frame_pos, sz + first_seg);
div_u64_w(val, inb_p);
spin_unlock(&disk->queue_lock);
mutex_unlock(&s->sock->mutex);
mutex_unlock(&func->mutex);
return disassemble(info->pending_bh);
}
请注意,该魔术盒可以知道如何去打开和关闭括号,并且能遵守程序的缩进约定,也就是函数代码的正确缩进,多行 printk 语句也具有正确的内部缩进。这也就意味着这个魔术盒可以理解代码的远程依赖关系,即魔术盒可以联系到前面的代码进行分析推敲。当它预测在 print 语句中时,它会知道它在一个 print 语句中,并且会记住它是在一个函数中(或者至少记住是在另一个缩进范围)中。太好了,这就很容易理解,具有捕捉细节和记住长期依赖关系的算法是非常有用的,因为我们也是希望能在市场上找到长期的依赖关系。
那么这个神奇的魔术盒里面有什么呢?它是一种被称为 LSTM 的循环神经网络(RNN)。RNN 是一种深度学习算法,它擅长的是对序列进行操作(如字符序列)。在每一步,它对会对下一个字符进行预测,并且使用一个矩阵记性表示,就像我们之前看到的那样。因为 RNN 有一些内部的记忆,所以它可以记住一些以前看到的东西。它会使用这个内部记忆来决定下一次输入中应该如何操作。使用该内存记忆,RNN 可以记住它在一定范围内的内容,这就是我们为什么可以对输入文本进行预测处理。
RNN 有一个很好的变体称为 LSTM,LSTM 巧妙的设计了 RNN 内部的记忆单元,这个单元可以做以下事:
所以,当一个 LSTM 看到一个 “{” ,并且对自己说“这个非常重要,我应该把它记住” 。而当它这样做时,它基本上就记住了这是一个嵌套的范围。一旦看到相应的 “}” ,它就会决定忘记原来的大括号,因此忘记它在一个嵌套的范围内。
我们可以将几个 LSTM 堆叠在一起,这样 LSTM 可以学习到更多的抽象概念。现在,前一个 LSTM 的输出变成了下一个 LSTM 的输入,每个 LSTM 会学习到更加抽象的数据。在上面的例子中(这个只是说明性的猜测解释),第一层的 LSTM 可能会学习由空格分割的字符,下一层可能会学习像(static void action_new_function)这样的字符类型,下一层可能会学习函数的概念以及参数等等。虽然 Karpathy 在博客中已经有非常好的说明例子了,但是要清楚的说明每一层的具体任务还是非常苦难的。
你可能会注意到,Karpathy 使用字符作为他模型的输入,而不是词嵌入(技术上是使用 0-1 编码)。但是,Lars Eidnes 在 Auto-Generating Clickbait With Recurrent Neural Network 文章中使用词嵌入来处理。
上图显示了他使用的网络。我们先不考虑 softmax 部分(这部分我们后续介绍),先来看看 LSTM 堆栈和模型的输入部分。我们发现模型的底部输入是一系列的单词向量(记住,一个词向量表示的是一个单词的语义)。Lars 输入一个字向量序列,其中每一个作用如下:
虽然我们要做同样的事情,但是可能有一点小小的区别,我们不是把字向量作为输入,而是我们的 Market2Vec ,这个市场向量就是我们之前描述出来的。我们来回顾一下,对于给定的时间点,我们的 MarketVectors 应该包含在这个时间点发生的市场情况的总和。通过 LSTM 的序列化,我们希望能够捕捉到市场上发生的一个长期依赖关系。通过几个 LSTM 的堆栈,我们希望可以得到更高层次的市场行为抽象数据。
到目前为止,我们还没有谈到算法是如何在实际环境中工作的,我们上面只是谈论了如何对数据做巧妙的转换,把一个高维度的数据转换成低维度的数据。在接下来的文章中,我们会来具体说如何应用这个算法,但是请记住,正是因为前面的铺垫才使得后面的应用有了价值。
在 Karpathy 的例子中,LSTM 的输出是抽象的表示下一个字符的预测向量。在 Eidnes 的例子中,LSTM 是输出下一个字符向量。这两种情况的下一步都是将一个抽象的事物表示成一个概率向量,这是一个概率列表,说明每个字符或者单词在下一个位置出现的可能性。这里则是 softmax 函数在工作了,一旦我们拥有了这个概率列表,我们则需要选择出最有可能出现的单词或者字符。
当我们处理“市场预测”的问题时,我们需要问自己,我们真正想要预测的是市场的什么情况?我这里有一些自己的想法,大家可以参考一下:
第一和第二个就是回归问题,我们必须预测的是一个实际的数字而不是一个特定事件的概率(比如字符 n 出现或者市场上涨)。这个非常准确,但是不是我想做的事。
第三和第四是两个非常相似的问题,他们都要求预测一个事件(技术术语,就是一个类标签)。这个事件可能是接下来出现字母是 n ,或者接下来10分钟内某只股票是否会上升 5%,而不会下降超过 3%。第三和第四之间的权衡关系是,第三个问题更加常见,因此更容易学习到,而第四个问题更加有价值,它不但对利润有预测,而且对风险也有一定的约束。
第五个问题是我们在这篇文章需要解决的,这个问题跟第三个和第四个问题非常类似,但是它更加容易一些,因为我们有一些机制可以遵循。VIX 指数被称为恐怖指数,它代表了 S&P 500 指数的股票波动。它是通过观察指数中每个股票的隐含波动率得出来的。
那么我们为什么会去预测 VIX 这个指数呢?原因主要如下:
在未来几分钟内,我们如何使用之前的数据来预测 VIX 的变化呢?对于我们数据集中的每一个点,我会在 5 分钟后再次看到该数据点的 VIX 变化。如果在那段时间内上升了超过 1%,但是没有超过 0.5%。那么,我们的模型将输出 1,否则就是输出 0。然后我们会得到如下一系列的标签:
0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0 ….
我们想利用之前压缩的市场向量,然后输入到 LSTM 模型中得出最终的概率结果,从技术上来分析,因为我们最终只要输出一个标签,我们也可以使用 sigmoid 函数,但是如果我们做二分类结果,还是建议使用 softmax 函数。
所以,在我们深入分析这个算法应用之前,让我们先来回顾一下我们已经学习了哪些内容:
现在是最有趣的部分。我们现在所做的一切都可以被称为是一个前向的过程。当我们训练算法时,我们也会去做以上的那些步骤,然后部署到生产环境中使用它。现在我们需要讨论的是一个反馈的过程,这个反馈是我们需要在算法中进行学习的过程。
在训练的过程中,我们不仅准备了数年的历史训练数据,还手动准备了一系列的预测目标,0 和 1 都是我们对历史数据进行打的标签。
为了学习,我们需要将市场数据输入我们的神经网络,并且将网络的输出结果和我们的标签数据进行对比,最后得到一个损失值,这个对比函数就是一个损失函数。在我们的例子中这个对比比较简单,我们可以将我们的损失函数设计如下:
ERROR = (1/2)*(((precomputed)— (predicted probability))² )
这就是一个损失函数。也就是说,我们可以去计算出深度学习模型的输出值与准确值之间的误差有多大。根据这些误差,我们可以利用一些更新函数来更新一整个网络的权重,从而得到一个比较好的模型。
该更新过程会一直更新到模型的开始,所以模型会不断的调整我们的 MarketVectors 向量,以便我们的 MarketVectors 可以表示更多的信息。
LSTM 模型可以选择去记住哪些内容和忘记哪些内容,以便模型可以分析那些与我们任务最相关的量。
LSTM 模型还可以学习数据的更加抽象表示,以便模型学习数据更多的特征。
这一点是非常让我吃惊的,因为深度学习可以学习那么多负责和抽象的东西。我们从前都没有接触过这种模型。
对于这个项目我还有一些更加深入的想法,一些我可能还会尝试的想法,并且我认为这些想法对实际项目时有用的。
一般来说,特定市场的流动性越高资金的利用效率越高。我认为这是一个鸡与蛋之前的循环,而市场变得更加的流动,它能够吸收更多的资本交易,并且资本收到的损害最小。随着市场变得更加流动,更多的资本可以在市场中使用,你会发现更多复杂的交易玩家会进入市场。
一个快速的推论是,在一个比较不流动的市场中,竞争并不是那么复杂,所以这样的机制可能会带来机会,我会在这样的市场尝试交易。
这些算法的知识和架构对计算机领域是比较陈旧的,但是在股票金融领域的应用是一个全新的应用。我们假设一些顶级玩家已经在使用这些技术进行套利,并且可能已经很长一段时间了。但是正如我前面提到的,他们可能是在一个流动性很大的市场进行套利工作,他们需要很大的市场资金规模,这样才能去平摊他们的运营成本。我认为,下一次的市场应用会在那些流动性比较小的市场,这些市场还没有被开发完全,存在很大的套利空间。
虽然我在上面提到的是输入一个单通道的数据流,但我认为一个更加有效的训练方法是在多个时间通道上面对市场数据进行训练。也就是说,我目前采用的是 30 秒采样一次,我希望网络可以解决几个小时的数据依赖关系。
我不知道这个想法是否有意义,但是我认为如果我们可以将计算成本降低到一定程度,那么我们可以把多个时间点进行融合训练。这个融合模型的训练我还在思考中,目前有点困难。
当我们在自然语言处理中,使用的词向量模型都是在一个预训练模型之上进行微调的。但是在我们的情况下,没有一个预训练好的市场向量可以使用,也没有一个明确的算法去进行训练。
我原来的想法是使用一个自动编码器来进行训练,就好像这篇论文,但是端到端的训练好像看起来更加酷。
更重要的考虑是,端到端的序列模型在机器翻译领域已经比较成熟了,也就是说我们可以把一些文本编码成一个向量,并且进行解码。在这种观点下,我所描述的整个架构本质上就是一个编码器,但是我还没有真正的设计出一个解码器。
但是,对于网络的第一层,我想设计一个特殊层,即我们需要把输入的 4000 维度向量降维到一个 300 维度的向量。通过这些低维度的向量,我们需要去找到各个股票之间的一些关联度。
另一个替代的方向是对每个 LSTM 输入一个数据,然后编码成一个输出向量。但我认为这个可能是无效的,因为各个数据模型之间的相关度会下降很多,并且这种方法会需要 10 倍的计算资源,比较浪费。但是从另一方面来说,这种架构可以多个 GPU 进行训练,可以提高一点速度。
最近,字符级的机器翻译方面出现了很多的论文。这篇论文带给了我很多的启发,因为这篇文章试图采用 CNN 来解决长距离的依赖关系,而不是 RNN 。我还没有深入的去阅读这篇论文,但是我们可以假想一下,我们把每个股票当做一个通道(类似于 RGB图像的三个通道),然后把每个通道输入卷积模型中进行卷积操作,从而来捕捉市场的长期依赖信息。这种方法跟他们从字符级别上进行语义编码的方式是相同的。
算法社区直播课:请点击这里
作者:chen_h
微信号 & QQ:862251340
简书地址:http://www.jianshu.com/p/f9ca56d6407d
CoderPai 是一个专注于算法实战的平台,从基础的算法到人工智能算法都有设计。如果你对算法实战感兴趣,请快快关注我们吧。加入AI实战微信群,AI实战QQ群,ACM算法微信群,ACM算法QQ群。长按或者扫描如下二维码,关注 “CoderPai” 微信号(coderpai)