技术 | 深度学习预测股票系列-3

继“数据特征”介绍完毕后(技术 | 深度学习预测股票系列-1技术 | 深度学习预测股票系列-2)这篇我们来介绍为什么用、如何用GANs来预测股价,以及怎样优化GANs的超参数组。

03│GAN生成式对抗神经网络

图1. GAN神经网络架构图

GAN神经网络包括两个模型:一个生成模型(Generator)- 通过机器生成数据(大部分情况下是图形),目的是“骗过”判别器;一个判别模型(Discriminator):判断一个特征,如一张图片是真实的还是生成的,目的是为了找出生成器做的“假数据”。

训练一个生成式对抗神经网络包括以下几个步骤:

  • 阶段一:固定“判别器D”,训练“生成器G”。
    一开始“生成器G”技能很弱,很容易被“判别器D”识别出来,随着不断地训练,“生成器G”技能不断提升,最终骗过“判别器D”,最终“判别器D”基本属于瞎猜的状态,判断是否为假数据的概率为50%;
     
  • 阶段二:固定“生成器G”,训练“判别器D”;
     
  • 循环阶段一和阶段二;
     

GAN在合成图片、画像、录像等领域的应用比较多,在时间序列数据方面的应用并不常见,我们希望用GAN预测股价走向,使得未来GS股票的模式和行为与预测基本一致。在本系列中,LSTM作为时间序列生成器,CNN作为判别器。

1. 一层生成器 - LSTM

在前面两个系列中,我们已经详细介绍了LSTM。现在我们来看看LSTM cell单元背后的数学公式:

ʘ是“数组元素依次相乘”(Element Wise Multiplication)算子。对于x = [x1, x2,…,xk]TϵR^k,激活函数为下面两个:

LSTM的架构也相对简单:1个LSTM层,包含112个输入单元和500个隐藏单元;1个全连接层,包含一个输出,即每日的股票价格。Xavier为初始程序,L1 loss为损失函数,Adam算法为优化器(学习速率为0.1)。下面让我们来看段代码,如何实现上述架构的LSTM:

gan_num_features = dataset_total_df.shape[1]

sequence_length = 17class RNNModel(gluon.Block):

def __init__(self, num_embed, num_hidden, num_layers, bidirectional=False, sequence_length=sequence_length, **kwargs):

super(RNNModel, self).__init__(**kwargs)

self.num_hidden = num_hidden

with self.name_scope():

self.rnn = rnn.LSTM(num_hidden, num_layers, input_size=num_embed, bidirectional=bidirectional, layout='TNC')

self.decoder = nn.Dense(1, in_units=num_hidden)

def forward(self, inputs, hidden):

output, hidden = self.rnn(inputs, hidden)

decoded = self.decoder(output.reshape((-1,self.num_hidden)))

return decoded, hidden

def begin_state(self, *args, **kwargs):

return self.rnn.begin_state(*args, **kwargs)

lstm_model=RNNModel(num_embed=gan_num_features, num_hidden=500, num_layers=1)

lstm_model.collect_params().initialize(mx.init.Xavier(), ctx=mx.cpu())

trainer = gluon.Trainer(lstm_model.collect_params(), 'adam', {'learning_rate': .01})

loss = gluon.loss.L1Loss()

看下将LSTM用MXNet打印出来,结果如何:

print(lstm_model)output >>>RNNModel(

(rnn): LSTM(112 -> 500, TNC)

(decoder): Dense(500 -> 1, linear)

LSTM用来预测股票的逻辑是:我们用17天的股票数据特征(17天为数据长度),以一天作为滑动窗口,来预测第18天的股票。

2. 判别器 - 一维CNN

CNN经常被用在图像处理领域,包括图像分类,文字截取等。它从特征中提取特征的功能十分强大。 譬如我们给到一只狗的图片,第一层卷积层能检测到边缘,第二层卷积层即能提取到圆圈的特征,第三层卷积层可提取到鼻子的特征。以此类推,数据点形成小趋势,再变成大趋势,最后成为图像/模式。我们希望利用CNN的这个功能从GS的股价信息中提取相应的图像规律,以预测股票趋势。

CNN对于空间数据的处理能力也异常强大。空间数据的特征是,越相邻的数据点之间的关联关系越强。时间数列数据则不具备该项特征。在预测GS股票领域,相邻的数据点代表相邻两个交易日的股价,我们有理由认为交易日的日期挨得越近,则股价间的相互影响越大,因此可以认为GS的日交易股价是“空间数据”,这也是选择CNN作为股票预测领域的判别器的第二个理由。

我们来看看图2显示的CNN架构图。

图2. CNN架构图

我们跳过CNN的完整代码,仅显示用MXNet打印出的CNN代码:

Sequential(

(0): Conv1D(None -> 32, kernel_size=(5,), stride=(2,))

(1): LeakyReLU(0.01)

(2): Conv1D(None -> 64, kernel_size=(5,), stride=(2,))

(3): LeakyReLU(0.01)

(4): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)

(5): Conv1D(None -> 128, kernel_size=(5,), stride=(2,))

(6): LeakyReLU(0.01)

(7): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)

(8): Dense(None -> 220, linear)

(9): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)

(10): LeakyReLU(0.01)

(11): Dense(None -> 220, linear)

(12): Activation(relu)

(13): Dense(None -> 1, linear)

)

3. 防止“过拟合(overfitting)”与“权衡偏差与方差(bias-variance tradeoff)”

为了防止“过拟合”现象,我们可以采取以下几个技术手段,用以降低参数空间的维度或者降低每个维度上的有效规模:

  • 保证数据质量: 参照系列2,数据章节中的“6.统计学检验”;
     
  • 正则化(Regularization): 将保留所有的特征变量,但是会减小特征变量的数量级;最常用的有LASSO (L1)正则化及Ridge (L2)正则化:L1正则化是指权值向量ω中各个元素的绝对值之和,通常表示为║ω║1,用以产生一个稀疏模型,可以用于特征提取,及一定程度上得过拟合;L2正则化是指权值向量ω各个元素的平方和让后再求平方根,通常表示为║ω║2,用以防止过拟合;
     
  • Dropout layer: 为了防止CNN过拟合;
     
  • DSD(Dense-Sparse-Dense Training)算法: 通过改进训练过程提高传统模型的准确率;
     
  • Early Stopping(早停法): 在训练中计算模型在验证集上的表现,当模型在验证集上的表现开始下降的时候,停止训练,这样就能避免继续训练导致过拟合的问题;
     

“权衡偏差与方差(bias-variance tradeoff)”

  • 偏差(bias) 度量了学习算法的期望预测与真实结果的偏离程度,即刻画了学习算法本身的拟合能力;
     
  • 方差(variance) 度量了同样大小的训练集的变动所导致的学习性能的变化,即刻画了数据扰动所造成的影响;
     

通常,简单的模型variance小(不同数据上的结果差异较小),bias大,容易表现为欠拟合,需要增加模型复杂度,加入新的特征;复杂的模型variance大(表达能力强,对不同数据较敏感,结果差异较大),bias小(平均来说与真实结果较为接近),容易表现为过拟合,需要增加更多数据(非常有效,但不太现实)或者用正则化来控制模型的复杂程度。为了达到一个合理的 bias-variance 的平衡,此时需要对模型进行认真地评估。

4. 超参数

  • 学习率(Learning Rate): 作为监督学习以及深度学习中重要的超参,其决定着目标函数 (Optimizer, 如SGD,Adam,RMSProp…) 能否收敛到局部最小值以及何时收敛到最小值。合适的学习率能够使目标函数在合适的时间内收敛到局部最小值。

在我们的神经网路模型中,有“LSTM”和“CNN”两个模型,因此我们有两个学习率超参数:

LSTM_lr;

CNN_lr;

下面我们给出一段每一个epoch中的LSTM学习率和epoch之间函数关系的代码(注:1个epoch表示经过1遍训练集中的所有样本):

schedule=CyclicalSchedule(TriangularSchedule, min_lr=0.5, max_lr=2, cycle_length=500)

iterations=1500plt.plot([i+1 for i in range(iterations)],[schedule(i) for i in range(iterations)])

plt.title('Learning rate for each epoch')

plt.xlabel("Epoch")

plt.ylabel("Learning Rate")

plt.show()

图6给出了用代码生成的“学习率”和“每阶段”的关系图:

图6. 每时期的学习率和时期之间的关系

  • 批量大小(batch size): LSTM和CNN的批量大小;
     
  • 步幅(strides): CNN步幅的数量;
     
  • lrelu_alpha: GAN中LeakyReLU的alpha参数;
     
  • batchnorm_momentum: CNN神经网络中batch normalization中的momentum参数。Batch normalization,即归一化,基本原理即“保证网络每次接受的输入都是均值(mean)0,标准差(standard deviation)1”,来加快训练速度,改进网络正则化(regularization),提高准确率,具体算法如下:
     

Momentum算法可以让结果更收敛。

  • 填充(Padding): CNN中的padding;
     
  • kernel_size’:1: CNN中的卷积核大小(kernel size);
     
  • dropout: LSTM中的dropout;
     
  • filters: filters的初始数量;
     

我们对200个epochs进行训练。

04│超参优化

再用GAN模型训练了200个epochs后,LSTM中的MAE会被记录下来并作为一个激励值传给“增强学习”,用来决定是否要改变或者优化超参数。为什么还要来决定是否优化超参数呢?因为股市更新换代的速度日新月异,就算用GAN或者LSTM能得到准确率很高的结果,这些结果也只是在某段时间内有效,所以为了保证模型准确的时效性,必须不停地优化整个过程,我们介绍如何用“强化学习”和“贝叶斯优化”优化超参:

  • 无模型的“强化学习”优化超参:
    Q Learning算法是一种off-policy的强化学习算法,一种典型的与模型无关的算法,其Q表的更新不同于选取动作时所遵循的策略,在更新的时候计算了下一个状态的最大价值,但是取那个最大值的时候所对应的行动不依赖于当前策略。我们将采用本系列1所介绍的Rainbow算法,即7种Q Learning算法的组合来优化超参;
     
  • PPO算法进行超参优化;
     

2. “贝叶斯优化”改进超参:

图7给出了一个使用高斯过程进行贝叶斯超参优化的示例:

图7. 使用高斯过程进行贝叶斯超参优化

05│实验结果

在进行了用LSTM作为生成器,CNN作为判别器,超参优化等一系列步骤后,让我们对预测的数据进行测试,与真实的数据对比,看看深度学习在股票预测上的成果如何:

  • 1个epoch后预测股价和真实股价的对比:

from utils import plot_prediction

plot_prediction('Predicted and Real

price - after first epoch.')

图8. 1个epoch后预测股价(黄色)和真实股价(蓝色)的对比

  • 50个epoch后预测股价和真实股价的对比

plot_prediction('Predicted and Real

price - after first 50 epochs.')

图9. 50个epochs后预测股价(黄色)和真实股价(蓝色)的对比

  • 200个epochs后预测股价和真实股价的对比

plot_prediction('Predicted and Real

price - after first 200 epochs.')

图10. 200个epochs后预测股价(黄色)和真实股价(蓝色)的对比

  • 用RL跑10个episodes,每个episode包含200个epochs后预测股价和真实股价的对比:

plot_prediction('Final result.')

图11. 10个episodes后预测股价(黄色)和真实股价(蓝色)的对比

从上面的结果看出,我们的理论和实验得到了验证。


 

近期热招:(点击标题,即可了解详情)

1.金融科技招聘 | 技术专场

2.招聘 | 机器学习(数据分析) -50万+奖金-对冲基金-上海

3.招聘 | Java/C++架构师-60-90万/年-上海

4.招聘 | 数据科学家 (NLP)-对冲基金-上海&北京

5.招聘 | 数据开发工程师-40-48万+奖金-北京-对冲基金