Kaggle冠军方案:用Autoencoder MLP+XGBoost做金融市场预测

竞赛主页:Jane Street Market Prediction


竞赛背景

金融市场的低买高卖,听起来是很容易的。但是在现实中,通过低买高卖进行交易获利一直是一个非常困难的问题,尤其是在当今复杂的金融市场中。电子交易系统允许在0.1秒内发生数千笔的交易,在其中产生很多可以被发现并用以获利的机会。在一个完全有效的市场中,买家和卖家将拥有做出理性交易决定所需的信息。因此,资产将始终保持其实际真实价值,不会被高估或低估。然而这在现实世界的金融市场中并非完全有效的。制定交易策略来识别利用市场的v无效性是非常有挑战性的。即使一个策略现在是盈利的,它在未来也不一定是持续盈利的,而且市场的波动性使得交易者无法确定地预测每笔交易的盈利能力。造成的结果就是,我们很难区分出策略的盈利是因为运气好还是策略本身好。

竞赛描述

竞赛数据

训练数据 竞赛数据集包含一组脱敏特征,代表真实的股票市场数据。数据集中的每一行代表一个交易机会,将为此预测一个action值:1 表示进行交易,0 表示跳过。每笔交易都有一个关联的weight和resp,它们共同代表交易的回报。date列是一个整数,代表交易日期,而ts_id列代表时间排序。除了匿名特征值之外,还可以获得有features.csv 中特征的元数据。 在训练集train.csv中,会提供了一个resp值,以及resp_{1,2,3,4}代表不同时间范围内回报的其他几个值。这些变量不包含在测试集中。为完整性起见,交易weight = 0故意包含在数据集中,尽管此类交易不会对评分评估做出贡献。 测试数据 在比赛的模型训练阶段,这个看不见的测试集由大约 100 万行历史数据组成。在实时预测阶段,测试集将使用定期更新的实时市场数据。

评估方法

本次比赛根据效用得分进行评估。测试集中的每一行代表一个交易机会,需要预测一个action值,action=1 表示进行交易,action=0 表示跳过。每笔交易j都有一个关联的weight和resp,代表回报。效用得分计算方法如下:

方案分享

方案地址:Jane Street: AE MLP+xgb

方案得分:6476.594分, 排名第一,该方案是AE MLP方案模型和XGBoost方案模型的的简单混合。下面分别对两个方案进行解析。

AE MLP方案

方案描述:Training Supervised Autoencoder with MLP

AE MLP引入了自动编码器进行分类特征提取。自动编码器是一种神经网络,可用于学习原始数据的压缩表示。自编码器由编码器和解码器子模型组成。编码器压缩输入,解码器尝试从编码器提供的压缩版本重新创建输入。训练后,编码器模型被保存,解码器被丢弃。然后,编码器可用作特征提取,对可用于训练不同机器学习模型的原始数据执行特征提取。

如上图的网络框架图所示,整个网络的设计可以分为两部分,左侧的Autoencoder部分和右侧的MLP预测部分。 Autoencoder部分在常见的架构上进行了调整,在将数据送入Encoder之前增加高斯噪声,可以达到缓解过拟合,提高模型泛化能力的作用。Decoder之后不仅使用了之前的MSE Loss,也增加了一个分类器模块,用来保证编码器学习到的特征可以被用来分类。 MLP部分的输入是将每个样本的原始输入和编码器的编码进行拼接一起输入,使用多层MLP网络进行处理,输出进行分类。 代码实现如下:


def create_ae_mlp(num_columns, 
                  num_labels, 
                  hidden_units, 
                  dropout_rates, 
                  ls = 1e-2, 
                  lr = 1e-3):
    # 定义输入
    inp = tf.keras.layers.Input(shape = (num_columns, ))
    x0 = tf.keras.layers.BatchNormalization()(inp)
    # 高斯加噪
    noise_x0 = tf.keras.layers.GaussianNoise(dropout_rates[0])(x0)
    # 编码器
    encoder = tf.keras.layers.Dense(hidden_units[0])(encoder)
    encoder = tf.keras.layers.BatchNormalization()(encoder)
    encoder = tf.keras.layers.Activation('swish')(encoder)
    # 解码器
    decoder = tf.keras.layers.Dropout(dropout_rates[1])(encoder)
    decoder = tf.keras.layers.Dense(num_columns, name = 'decoder')(decoder)
    # Classification Layer
    x_ae = tf.keras.layers.Dense(hidden_units[1])(decoder)
    x_ae = tf.keras.layers.BatchNormalization()(x_ae)
    x_ae = tf.keras.layers.Activation('swish')(x_ae)
    x_ae = tf.keras.layers.Dropout(dropout_rates[2])(x_ae)
    # Autoencoder的分类器
    out_ae = tf.keras.layers.Dense(num_labels,
                                   activation='sigmoid', 
                                   name = 'ae_action')(x_ae)
    # 拼接原始输入和编码器输入
    x = tf.keras.layers.Concatenate()([x0, encoder])
    # MLP的网络层
    x = tf.keras.layers.BatchNormalization()(x)
    x = tf.keras.layers.Dropout(dropout_rates[3])(x)
    for i in range(2, len(hidden_units)):
        x = tf.keras.layers.Dense(hidden_units[i])(x)
        x = tf.keras.layers.BatchNormalization()(x)
        x = tf.keras.layers.Activation('swish')(x)
        x = tf.keras.layers.Dropout(dropout_rates[i + 2])(x)
    # MLP的分类器
    out = tf.keras.layers.Dense(num_labels, activation = 'sigmoid', name = 'action')(x)
    # 定义输入输出
    model = tf.keras.models.Model(inputs = inp, outputs = [decoder, out_ae, out])
    # 定义优化器,损失函数和Metrics
    model.compile(optimizer = tf.keras.optimizers.Adam(learning_rate = lr),
                  loss = {'decoder': tf.keras.losses.MeanSquaredError(), 
                          'ae_action': tf.keras.losses.BinaryCrossentropy(label_smoothing = ls),
                          'action': tf.keras.losses.BinaryCrossentropy(label_smoothing = ls), 
                         },
                  metrics = {'decoder': tf.keras.metrics.MeanAbsoluteError(name = 'MAE'), 
                             'ae_action': tf.keras.metrics.AUC(name = 'AUC'), 
                             'action': tf.keras.metrics.AUC(name = 'AUC'), 
                            }, 
                 )
    
    return model


有用的技巧

  1. 交叉验证,由于金融任务存在任务数据是时间序列,模型的训练和使用存在时间延迟 。作者使用了PurgedGroupTimeSeriesSplit进行交叉验证划分。如下图所示,PurgedGroupTimeSeriesSplit支持对时序数据进行划分,避免了验证集使用在训练集之前的数据,同时支持定义训练集和验证集之间的时间间隔。本文使用了5-折 31-间隔的交叉验证划分方式。


2. 使用 swish 激活函数而不是 ReLU 来防止“死神经元”并平滑梯度; 3. 用 3 个不同的随机种子训练模型并取平均值以减少预测方差; 4. 仅使用在最后两次 交叉验证拆分中训练的模型(具有不同种子),因为它们使用了较多的数据; 5. 通过MLP 的 BCE 损失进行早停;6. 使用 Hyperopt 找到最优的超参数集。

Xgboost方案

Xgboost比较简单,指定超参,使用不同的随机数种子训练了三个模型进行使用。代码如下:


X = train.loc[:, train.columns.str.contains('feature')].values
y = train.loc[:, 'action'].astype('int').values

X_ = X
y_ = train.loc[:, 'action3'].astype('int').values

clf1 = xgb.XGBClassifier(
    n_estimators=100,
    max_depth=11,
    learning_rate=0.05,
    subsample=0.90,
    colsample_bytree=0.7,
    missing=-999,
    random_state=21,
    tree_method='gpu_hist',  # THE MAGICAL PARAMETER
    reg_alpha=10,
    reg_lambda=10,
)


clf1.fit(X_, y_)

clf2 = xgb.XGBClassifier(
    n_estimators=100,
    max_depth=11,
    learning_rate=0.05,
    subsample=0.90,
    colsample_bytree=0.7,
    missing=-999,
    random_state=210,
    tree_method='gpu_hist',  # THE MAGICAL PARAMETER
    reg_alpha=10,
    reg_lambda=10,
)


clf2.fit(X_, y_)

clf3 = xgb.XGBClassifier(
    n_estimators=100,
    max_depth=11,
    learning_rate=0.05,
    subsample=0.90,
    colsample_bytree=0.7,
    missing=-999,
    random_state=2010,
    tree_method='gpu_hist',  # THE MAGICAL PARAMETER
    reg_alpha=10,
    reg_lambda=10,
)


clf3.fit(X_, y_)


参考:

  1. Autoencoder Feature Extraction for Classification
  2. JiaqiWu-hub/kaggle-Jane-Street-Market-Prediction



风险提示及免责条款

市场有风险,投资需谨慎。本文不构成个人投资建议,也未考虑到个别用户特殊的投资目标、财务状况或需要。用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部