上个星期,我花了一些时间参加了 Numerai 的机器学习金融比赛。这篇文章就是我对于比赛的一些笔记:我尝试过得一些方法,我做了什么工作以及什么工作我直接放弃不做。首先,让我们来介绍一下这个比赛和平台:
Numerai 是一家对冲基金,它利用比赛的形式来收集用户的策略,然后在内部对这些策略进行组合集成来交易。这个平台还有一个独一无二的地方是它对所有数据都会进行加密。每个星期,Numerai 都会重置比赛,然后公布一个新的数据集。然后经过一周短短的比赛,来评出第一名和第二名。这些名词不是单单利用比赛的分数来评定,还会有策略的原创性来考核。截止到这周末,我的策略的 log loss 在 0.68714。这个分数大约可以获得价值 $ 8.17 的比特币价值。
下面是训练数据的一个示例:
- feature1 feature2 feature3 feature4 feature5 feature6 feature7 feature8 feature9 feature10 feature11 feature12 feature13 feature14 feature15 feature16 feature17 feature18 feature19 feature20 feature21 target
- 0.801620682063483 0.954026655642816 0.5705311667363 0.144232717229754 0.0500492688121434 0.0786789084265974 0.929508009336688 0.171077685785306 0.883034072289284 0.179369932130713 0.943269363053669 0.0288614434941082 0.1991065940751 0.968784322087461 0.064581284883459 0.916191480500131 0.238189399122635 0.0936556660484072 0.206537285119826 0.160288788368154 0.659444719296122 1
- 0.54007050183969 0.459006729530628 0.933131512414551 0.499801268888154 0.437992709228964 0.267235986043502 0.49719463349721 0.456020915709475 0.609753221442359 0.187454050581248 0.469354733168323 0.118559329564251 0.129507550533594 0.479953630810717 0.416888497364001 0.634561462124642 0.710520556134985 0.324297337296292 0.339857563207418 0.349509997045832 0.432920679407234 1
- 0.518214112985959 0.383788083593395 0.424871302717305 0.792762973770667 0.7742857173559 0.451385276234671 0.791400908677086 0.845301217143346 0.11862555750929 0.419257487945476 0.376741188540958 0.625610979113513 0.266524021808175 0.648017340815601 0.420797663214614 0.00431873269449817 0.992130143506543 0.718080611565518 0.992093533667718 0.134067138702938 0.0743077276110433 0
- 0.806453569351584 0.161343358248138 0.636975566410459 0.20171554254616 0.420537856699983 0.151818600989567 0.305146784581374 0.117884737420495 0.6108915265627 0.249714474623046 0.819308099139668 0.250770579717342 0.205248270449002 0.0862103305974124 0.532109927231077 0.790242340397417 0.198781520011354 0.274051362170583 0.192056729456156 0.0975874452607068 0.669890711788048 0
- 0.415003336362225 0.170186235034517 0.418823297694966 0.471830387551152 0.570765150255677 0.213788507104613 0.158155235117551 0.0117300575807448 0.418275738366933 0.558753991377777 0.179990534571358 0.436470111914978 0.506977165129181 0.300005858796116 0.742479443088257 0.206816619126046 0.814364270840413 0.331907239807323 0.0654237310678893 0.406143623934384 0.746499721419085 0
- 0.704449319534233 0.850338911254686 0.455998303770994 0.224563394324338 0.6995439084974 0.834721324063086 0.74728784556282 0.901345710496236 0.497415817686694 0.903353074258274 0.571234586425579 0.700451571955351 0.834000656317827 0.729095524732872 0.81078297616965 0.609452318787617 0.192694538456569 0.621665212601283 0.737860261408827 0.194185986803504 0.364996083433718 1
- 0.788055769180724 0.0545958246530098 0.200827379203853 0.0673784077815309 0.941077448055137 0.778947613783854 0.116750357635139 0.22800316108405 0.136388118235054 0.964284570466578 0.504061018335779 0.990569143158103 0.784283053374759 0.0393941086088943 0.933946479526913 0.669042714854619 0.138144590100865 0.918439009896414 0.880195626011122 0.0703418399004744 0.479384331357069 1
- 0.188297550227483 0.604074503040718 0.0183412656148252 0.81576618771192 0.518477669691875 0.803097540388692 0.711386433623059 0.652814191221587 0.0526464320116979 0.817774122488179 0.237459905042212 0.602959633090017 0.730338364463652 0.586008172371978 0.416888497364001 0.223150580662151 0.626276488264701 0.963008567409791 0.689730272299441 0.689253363243162 0.132085515045565 0
- 0.820315384108692 0.637408836538877 0.907437156607062 0.243122252397027 0.378765978591899 0.25798160900915 0.983337691578471 0.355088818872554 0.88329260758454 0.12352921389316 0.71346744394059 0.369877122677724 0.221232749108118 0.812421645851051 0.112857777466973 0.709823231860924 0.598130707018941 0.0413007685381965 0.50006278551784 0.266615825161751 0.735885538367622 1
我在比赛中的第一步就是设计我们的验证集,以方便我们在本地测试我们的模型性能,从而可以对模型在排行榜上面的排名可以有一个预估。如果我们只是简单的对数据进行分割,那么验证结果并不能正确的反应在排行榜上面,所以在这里我采用的是 “对抗验证”。这个聪明的想法是 @fastml 中的一个想法,具体博客在这里。具体思想如下:
按照这种方法设计的验证集,我们把相同的模型在公开排行榜上面进行测试的时候,得到的 log loss 误差大约在 0.001 左右。有趣的是,相对于训练数据,测试数据是不满足 IID 的条件。
现在我们已经有了一个非常好的验证集,之后我们需要去设计一个模型,并且对这个模型进行验证和上传结果,这个模型就作为我们的基准模型了。作为起点,我就是用最简单的逻辑回归并且没有用任何的特征工程。这个模型在验证集上面得到了 0.69290 的结果,在公开的排行榜上面得到了 0.69162 的结果。这个结果并不好,但是我们的目的达到了,现在我们拥有了一个初始目标。为了进行比较,第一名目前的得分为 0.64669,所以我们最简单的基准分数与它之间的大约只有 6.5% 的差距。这也就意味着我们对于基准模型的任何改动,对最终的排名结果的影响都是非常小的。我们在对基准模型加上 1e-2 的 L2 正则化之后,这在排行榜上面达到了 0.69286 的正确率,比基准测试提高了 0.006 。
在开始使用特征工程之前,我快速的浏览了一下神经网络算法。在理想的情况下,神经网络可以从足够多的原始数据中学习到特征,但不幸的是,我尝试过目前所有的神经网络架构,但是他们取得的效果都不能比简单的逻辑回归有很大的改进。此外,深度神经网络需要比逻辑回归更多的参数,所有我们需要去使用 L2 正则和批量归一化来规范参数,给模型加上 Dropout 也可以给架构带来性能的改进。
一个有效但是非常有意思的架构是使用一个非常宽的隐藏层(2048个神经元),拥有很高的 Dropout 值(达到 0.9),并且在开始训练的时候我们需要对初始化参数做一个固定设置。因为拥有很高的 Dropout ,所以这就造成了很多的集成模型。虽然这个模型能很好地工作,大约可以达到 0.689 的正确率,但是该模型太有个性,最终还是不打算采用这个模型。最后,神经网络没有在这个问题中得到比较好的结果,所以我们没有在神经网络的改进上花费更多的精力,而且本文我们主要分析我们的特征工程。
现在我们需要去挖掘我们的数据价值,先从一个简单的特征分布图开始吧。具体如下:
每个特征的分布都是非常相似的,那么特征之间的相关性如何呢?我们也画了一个图,如下:
从图中,我们可以看到很多的特征之间是强相关的。我们可以通过多项式关系在我们的模型中使用这一个特性。根据这个性质,我们在验证数据集上面取得了 0.69256 的成绩。
接下来,我们来做降维处理。我们采用的是 PCA 来把维度下降到二维,然后进行可视化操作,如下:
从图中我们得不到很多有用的信息,那么尝试一下多项式特征如何?如下图:
多项式 PCA 通过将很多目标是 “1” 的值拉向边缘,并且把目标是 “0” 的值拉向中间,从图中看效果比前面一个好了很多。但是看起来还不是很好,所以我打算放弃使用 PCA,而使用别的方法。
我们将使用一种称为 t-SNE 的方法,t-SNE 通常用于高维数据的可视化,但是它拥有一个 PCA 没有的特性:t-SNE 是非线性的,并且根据两个点的概率来选择是否作为中心点的邻居。
这里,t-SNE 获得了很好的的可视化结果。我将这些 2D 特征添加到我们的模型中,获得了目前最好的 log loss 成绩:0.68947,。我怀疑这个起作用的原因是局部特征,逻辑回归不能从数据中得到这些局部特征,但是这些局部特征对我们的分类是非常有效的。
由于 t-SNE 是随机的,我们每一次的运行结果都会产生不一样的结果。为了利用这一个特性,我们需要在不同的困惑度和维度(2D或者3D)上面,运行 t-SNE 五到六次,然后去分析这些额外的特征。最后,根据这个结果,我们的验证成绩达到了 0.68839 。
由于 t-SNE 可以很好地工作,所以我又采用了集中其他的嵌入方法,包括自动编码器,去噪编码器和生成对抗网络。自动编码器可以对原始数据进行重新建模,而且建模的正确率可以高达 95% 的正确率,即使我们的数据中有噪音,但是这种嵌入表示并没有造成很大的影响。GAN,包括各种半监督的变体,他们的性能并没有超过简单的逻辑回归模型。我还是重点关注非线性降维犯法,比如内核PCA和等值线。但是这些方法很花费计算时间,减少了我迭代的次数,所以最终我还是把他们抛弃了。我没有尝试过 LargeVis 或者参数 t-SNE,但是他们都是非常值得研究的,因为它们可以保持一定的适应性,而不是将所有的学习样本一次输入。
其中一个集成模型是数据互动。基本上,从两个样本的特征可以直接看出,两个样本中哪个样本更大的可能性会被归类为 “1”。由于我们是对样本之间的交互进行建模,而不是对单个样本,因此根据这一个特性我们可以得到更多的数据。我们还可以根据我们的分类结果,从而从数据中学习到更有用的特征。
现在我们有一些有用的特征和一些性能良好的模型,所以我想运行一个超参数模型,看看它是否可以胜过现有的模型。由于 scikit 包只能用 GridSearchCV 和 RandomSearchCV 进行探索超参数,而不是整个架构,所以我选择使用在两者之间的 tpot 包。我发现,使用随机 PCA 的性能由于 PCA,并且 L1 正则化(稀疏性)略由于 L2 正则化(平滑),特别是跟随机 PCA 进行配合使用。不幸的是,我们所发现的数据之间的关联性并没有用在最终的集成模型中:手工工程取得了胜利。
我们已经完成了几个模型,是时候对这几个模型进行集成预测了。集成开发有很多的方式,我们这里采用的是最简单的基于几何平均值的简单平均值。
最后的集成模型我们采用了 4 个集成模型: logistic regression, gradient boosted trees, factorization machines 和 the pairwise model。我对每个模型都是使用相同的特征,特征由原始的 21 个特征组成,并且在 5,10,15,30 和 50 的困惑度下,在 2D 中运行 t-SNE 五次,以及在 3D 中运行 t-SNE 一次(困惑度 30)。这些特征与多项式特征相互结合,并且应用到模型中,这个集成模型最终得到的分数是 0.68714 。
总体而言,这是一个非常有趣的比赛,它和 kaggle 最不同的地方是它的数据是加密的,而且它独特的奖金分配制度也是吸引我的一个地方,但是我更倾向于将奖励看做是积分,而不是货币,因为这使得竞争更加乐观。另一方面,现在我有了我的第一个比特币。
来源: http://www.36dsj.com/archives/100993