python量化之搭建Transformer模型用于股票价格预测

目录
  • 前言
  • 1、Transformer模型
  • 2、环境准备
  • 3、代码实现
    • 3.1. 导入库以及定义超参
    • 3.2. 模型构建
    • 3.3. 数据预处理
    • 3.4. 模型训练以及评估
    • 3.5. 模型运行
  • 4、总结

前言

下面的这篇文章主要教大家如何搭建一个基于Transformer的简单预测模型,并将其用于股票价格预测当中。原代码在文末进行获取。

1、Transformer模型

Transformer 是 Google 的团队在 2017 年提出的一种 NLP 经典模型,现在比较火热的 Bert 也是基于 Transformer。Transformer 模型使用了 Self-Attention 机制,不采用 RNN 的顺序结构,使得模型可以并行化训练,而且能够拥有全局信息。这篇文章的目的主要是将带大家通过Pytorch框架搭建一个基于Transformer的简单股票价格预测模型。

Transformer的基本架构:

具体地,我们用到了上证指数的收盘价数据为例,进行预测t+1时刻的收盘价。需要注意的是,本文只是通过这样一个简单的基本模型,带大家梳理一下数据预处理,模型构建以及模型评估的流程。模型还有很多可以改进的地方,例如选择更有意义的特征,如何进行有效的多步预测等。

2、环境准备

本地环境:

Python 3.7
IDE:Pycharm

库版本:

numpy 1.18.1
pandas 1.0.3 
sklearn 0.22.2
matplotlib 3.2.1
torch 1.10.1

3、代码实现

3.1. 导入库以及定义超参

首先,需要导入用到库,以及模型的一些超参数的设置。其中,input_window和output_window分别用于设置输入数据的长度以及输出数据的长度。当然,这些参数大家也可以根据实际应用场景进行修改。

import torch
import torch.nn as nn
import numpy as np
import time
import math
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
torch.manual_seed(0)
np.random.seed(0)

input_window = 20
output_window = 1
batch_size = 64
device = torch.
device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

3.2. 模型构建

Transformer中很重要的一个组件是提出了一种新的位置编码的方式。我们知道,循环神经网络本身就是一种顺序结构,天生就包含了词在序列中的位置信息。当抛弃循环神经网络结构,完全采用Attention取而代之,这些词序信息就会丢失,模型就没有办法知道每个词在句子中的相对和绝对的位置信息。因此,有必要把词序信号加到词向量上帮助模型学习这些信息,位置编码(PositionalEncoding)就是用来解决这种问题的方法。它的原理是将生成的不同频率的正弦和余弦数据作为位置编码添加到输入序列中,从而使得模型可以捕捉输入变量的相对位置关系。

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
       super(PositionalEncoding, self).__init__()
        pe = torch.zeros(max_len, d_model)
          position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
           div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
              pe[:, 0::2] = torch.sin(position * div_term)
               pe[:, 1::2] = torch.cos(position * div_term)
                pe = pe.unsqueeze(0).transpose(0, 1)
                  self.register_buffer('pe', pe)
    def forward(self, x):
    return x + self.pe[:x.size(0), :]

之后,搭建Transformer的基本结构,在Pytorch中有已经实现的封装好的Transformer组件,可以很方便地进行调用和修改。其中需要注意的是,文中并没有采用原论文中的Encoder-Decoder的架构,而是将Decoder用了一个全连接层进行代替,用于输出预测值。另外,其中的create_mask将输入进行mask,从而避免引入未来信息。

class TransAm(nn.Module):
 def __init__(self, feature_size=250, num_layers=1, dropout=0.1):
  super(TransAm, self).__init__()
  self.model_type = 'Transformer'
  self.src_mask = None
  self.pos_encoder = PositionalEncoding(feature_size)
  self.encoder_layer = nn.TransformerEncoderLayer(d_model=feature_size, nhead=10, dropout=dropout)        self.transformer_encoder = nn.TransformerEncoder(self.encoder_layer, num_layers=num_layers)
  self.decoder = nn.Linear(feature_size, 1)
  self.init_weights()
    def init_weights(self):
    initrange = 0.1
    self.decoder.bias.data.zero_()
    self.decoder.weight.data.uniform_(-initrange, initrange)
    def forward(self, src):
     if self.src_mask is None or self.src_mask.size(0) != len(src):
        device = src.device
         mask = self._generate_square_subsequent_mask(len(src)).to(device)
          self.src_mask = mask
        src = self.pos_encoder(src)
         output = self.transformer_encoder(src, self.src_mask)
           output = self.decoder(output)
             return output
           def _generate_square_subsequent_mask(self, sz):
            mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
            mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
             return mask

3.3. 数据预处理

接下来需要对数据进行预处理,首先定义一个窗口划分的函数。它的作用是将输入按照延迟output_windw的方式来划分数据以及其标签,文中是进行单步预测,所以假设输入是1到20,则其标签就是2到21,以适应Transformerseq2seq的形式的输出。

def create_inout_sequences(input_data, tw):
inout_seq = []
 L = len(input_data)
  for i in range(L - tw):
  train_seq = input_data[i:i + tw]
    train_label = input_data[i + output_window:i + tw + output_window]
     inout_seq.append((train_seq, train_label))
     return torch.FloatTensor(inout_seq)

之后划分训练集和测试集,其中前70%条数据用于模型训练,后面的数据用于模型测试。具体地,我们用到了前input_window个收盘价来预测下一时刻的收盘价数据。

def get_data():
    series = pd.read_csv('./000001_Daily.csv', usecols=['Close'])
    # series = pd.read_csv('./daily-min-temperatures.csv', usecols=['Temp'])
    scaler = MinMaxScaler(feature_range=(-1, 1))
      series = scaler.fit_transform(series.values.reshape(-1, 1)).reshape(-1)
    train_samples = int(0.7 * len(series))    train_data = series[:train_samples]
    test_data = series[train_samples:]
    train_sequence = create_inout_sequences(train_data, input_window)
     train_sequence = train_sequence[:-output_window]
    test_data = create_inout_sequences(test_data, input_window)
     test_data = test_data[:-output_window]
    return train_sequence.to(device), test_data.to(device)

接下来实现一个databatch generator,便于从数据中按照batch的形式进行读取数据。

def get_batch(source, i, batch_size):
 seq_len = min(batch_size, len(source) - 1 - i)
   data = source[i:i + seq_len]
    input = torch.stack(torch.stack([item[0] for item in data]).chunk(input_window, 1))
   target = torch.stack(torch.stack([item[1] for item in data]).chunk(input_window, 1))
      return input, target

3.4. 模型训练以及评估

下面是模型训练的代码。具体地,就是通过遍历训练集,通过既定的loss,对参数进行反向传播,其中用到了梯度裁剪的技巧用于防止梯度爆炸,然后每间隔几个间隔打印一下loss。

def train(train_data):
  model.train()
    for batch_index, i in enumerate(range(0, len(train_data) - 1, batch_size)):
     start_time = time.time()
     total_loss = 0
      data, targets = get_batch(train_data, i, batch_size)
        optimizer.zero_grad()
         output = model(data)
          loss = criterion(output, targets)
            loss.backward()
              torch.nn.utils.clip_grad_norm_(model.parameters(), 0.7)
                optimizer.step()
        total_loss += loss.item()
          log_interval = int(len(train_data) / batch_size / 5)
           if batch_index % log_interval == 0 and batch_index > 0:
           cur_loss = total_loss / log_interval
           elapsed = time.time() - start_time
           print('| epoch {:3d} | {:5d}/{:5d} batches | lr {:02.6f} | {:5.2f} ms | loss {:5.5f} | ppl {:8.2f}'
           .format(epoch, batch_index, len(train_data) // batch_size, scheduler.get_lr()[0], elapsed * 1000 / log_interval, cur_loss, math.exp(cur_loss)))

接下来是对模型进行评估的代码。

def evaluate(eval_model, data_source):
 eval_model.eval()
 total_loss = 0
 eval_batch_size = 1000
  with torch.no_grad():
   for i in range(0, len(data_source) - 1, eval_batch_size):
   data, targets = get_batch(data_source, i, eval_batch_size)
   output = eval_model(data)
   total_loss += len(data[0]) * criterion(output, targets).cpu().item()
   return total_loss / len(data_source)

最后,是模型运行过程的可视化:

def plot_and_loss(eval_model, data_source, epoch):
  eval_model.eval()
  total_loss = 0.
  test_result = torch.Tensor(0)
  truth = torch.Tensor(0)
    with torch.no_grad():
   for i in range(0, len(data_source) - 1):
    data, target = get_batch(data_source, i, 1)
    output = eval_model(data)
    total_loss += criterion(output, target).item()
     test_result = torch.cat((test_result, output[-1].view(-1).cpu()), 0)
      truth = torch.cat((truth, target[-1].view(-1).cpu()), 0)

    plt.plot(test_result, color="red")    plt.plot(truth, color="blue")
    plt.grid(True, which='both')
     plt.axhline(y=0, color='k')
       plt.savefig('graph/transformer-epoch%d.png' % epoch)
        plt.close()
    return total_loss / i

3.5. 模型运行

最后,对模型进行运行。其中用到了mse作为loss,adam作为优化器,以及设定学习率的调度器,最后运行200个epoch,每隔10个epoch在测试集上评估一下模型。

train_data, val_data = get_data()
model = TransAm().to(device)
criterion = nn.MSELoss()
lr = 0.005
optimizer = torch.optim.AdamW(model.parameters(), lr=lr)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.95)
epochs = 200

for epoch in range(1, epochs + 1):
epoch_start_time = time.time()
train(train_data)

    if (epoch % 10 is 0):
     val_loss = plot_and_loss(model, val_data, epoch)
     else:
          val_loss = evaluate(model, val_data)
    print('-' * 89)
     print('| end of epoch {:3d} | time: {:5.2f}s | valid loss {:5.5f} | valid ppl {:8.2f}'.format(epoch, (                time.time() - epoch_start_time), val_loss, math.exp(val_loss)))
     print('-' * 89)    scheduler.step()

下面是运行的结果,可以看到loss明显降低了:

cuda| epoch   1 |     2/   10 batches | lr 0.005000 |  7.83 ms | loss 39.99368 | ppl 233902099994043520.00| epoch   1 |   
  4/   10 batches | lr 0.005000 |  7.81 ms | loss 7.20889 | ppl  1351.39| epoch   1 |     6/   10 batches | lr 0.005000 | 11.10 ms | loss 1.68758 | ppl     5.41| epoch   1 |   
  8/   10 batches | lr 0.005000 |  9.35 ms | loss 0.00833 | ppl     1.01| epoch   1 |    10/   10 batches | lr 0.005000 |  7.81 ms | loss 1.18041 | ppl     3.26-----------------------------------------------------------------------------------------| end of epoch   1 | time:  1.96s | valid loss 2.58557 | valid ppl    13.27
...
| end of epoch 198 | time:  0.30s | valid loss 0.00032 | valid ppl     1.00-----------------------------------------------------------------------------------------| epoch 199 |   
  2/   10 batches | lr 0.000000 | 15.62 ms | loss 0.00057 | ppl     1.00| epoch 199 |     4/   10 batches | lr 0.000000 | 15.62 ms | loss 0.00184 | ppl     1.00| epoch 199 | 
    6/   10 batches | lr 0.000000 | 15.62 ms | loss 0.00212 | ppl     1.00| epoch 199 |     8/   10 batches | lr 0.000000 |  7.81 ms | loss 0.00073 | ppl     1.00| epoch 199 |    10/   10 batches | lr 0.000000 | 
 7.81 ms | loss 0.00057 | ppl     1.00-----------------------------------------------------------------------------------------| end of epoch 199 | time:  0.30s | valid loss 0.00032 | valid ppl     1.00-----------------------------------------------------------------------------------------| epoch 200 |     2/   10 batches | lr 0.000000 | 15.62 ms | loss 0.00053 | ppl     1.00| epoch 200 |  
   4/   10 batches | lr 0.000000 |  7.81 ms | loss 0.00177 | ppl    
 1.00| epoch 200 |     6/   10 batches | lr 0.000000 |  7.81 ms | loss 0.00224 | ppl     1.00| epoch 200 |     8/   10 batches | lr 0.000000 | 15.62 ms | loss 0.00069 | ppl     1.00| epoch 200 |    10/   10 batches | lr 0.000000 |  7.81 ms | loss 0.00049 | ppl     1.00-----------------------------------------------------------------------------------------| end of epoch 200 | time:  0.62s | valid loss 0.00032 | valid ppl     
1.00-----------------------------------------------------------------------------------------

最后是模型的拟合效果,从实验结果中可以看出我们搭建的简单的Transformer模型可以实现相对不错的数据拟合效果。

4、总结

在这篇文章中,我们介绍了如何基于Pytorch框架搭建一个基于Transformer的股票预测模型,并通过真实股票数据对模型进行了实验,可以看出Transformer模型对股价预测具有一定的效果。另外,文中只是做了一个简单的demo,其中仍然有很多可以改进的地方,如采用更多有意义的输入数据,优化其中的一些组件等。除此之外,目前基于Transformer的模型层出不穷,其中也有很多值得我们去学习,大家也可以采用更先进的Transformer模型进行试验。

到此这篇关于python量化之搭建Transformer模型用于股票价格预测的文章就介绍到这了,更多相关python搭建Transformer模型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python+OpenCV实战之利用 K-Means 聚类进行色彩量化

    目录 前言 利用 K-Means 聚类进行色彩量化 完整代码 显示色彩量化后的色彩分布 前言 K-Means 聚类算法的目标是将 n 个样本划分(聚类)为 K 个簇,在博文<OpenCV与机器学习的碰撞>中,我们已经学习利用 OpenCV 提供了 cv2.kmeans() 函数实现 K-Means 聚类算法,该算法通过找到簇的中心并将输入样本分组到簇周围,同时通过简单的示例了解了 K-Means 算法的用法.在本文中,我们将学习如何利用 K-Means 聚类进行色彩量化,以减少图像中颜色数量.

  • python使用pandas进行量化回测

    下面文章描述可能比excel高级一点,距离backtrader这些框架又差一点.做最基础的测试可以,如果后期加入加仓功能,或者是止盈止损等功能,很不合适.只能做最简单的技术指标测试. 导包,常用包导入: import os import akshare as ak import requests import numpy as np import pandas as pd import matplotlib.pyplot as plt import talib as ta %matplotlib

  • python wav模块获取采样率 采样点声道量化位数(实例代码)

    安装: pip install wave 在wav 模块中 ,主要介绍一种方法:getparams(),该方法返回的结果如下: _wave_params(nchannels=1, sampwidth=2, framerate=48000, nframes=171698592, comptype='NONE', compname='not compressed') 参数解释: nchannels:声道数 sampwidth:量化位数(byte) framerate:采样频率 nframes:采样点

  • python下对hsv颜色空间进行量化操作

    更新:优化了代码,理由numpy的ufunc函数功能替换了之前的双重for循环,测试图片大小为692*1024*3,优化前运行时间为6.9s,优化后为0.8s. 由于工作需要,需要计算颜色直方图来提取颜色特征,但若不将颜色空间进行量化,则直方图矢量维数过高,不便于使用.但是看了opencv API后并未发现提供了相关函数能够在计算颜色直方图的同时进行量化,因此这部分功能只能自己实现.下面分为两个部分进行介绍: 一.颜色空间量化表 由于RGB模型不够直观,不符合人类视觉习惯,因此在进行颜色特征提取

  • Python图像处理之图像量化处理详解

    目录 一.图像量化处理原理 二.图像量化实现 三.图像量化等级对比 四.K-Means聚类实现量化处理 五.总结 一.图像量化处理原理 量化(Quantization)旨在将图像像素点对应亮度的连续变化区间转换为单个特定值的过程,即将原始灰度图像的空间坐标幅度值离散化.量化等级越多,图像层次越丰富,灰度分辨率越高,图像的质量也越好:量化等级越少,图像层次欠丰富,灰度分辨率越低,会出现图像轮廓分层的现象,降低了图像的质量.图8-1是将图像的连续灰度值转换为0至255的灰度级的过程[1-3]. 如果

  • Python量化交易详细简介

    目录 一.量化交易概述(algo-trading) 1.历史评估 2.效率 3.没有任意的输入 4.更高的频率 二.交易系统 1.回测-backtest 2.交易所 3.交易费 一.量化交易概述(algo-trading) 算法交易是使用自动化系统来执行交易,这些交易是通过特定的算法以预测的方式执行的没有任何人为干预.依据数学模型对大数据进行概率分析,使得长期收益期望最大化. 量化交易优点: 1.历史评估 创建自动化战略最重要的优势在于其性能可以通过历史市场数据来确定来代表未来市场的数据.这个过

  • python量化之搭建Transformer模型用于股票价格预测

    目录 前言 1.Transformer模型 2.环境准备 3.代码实现 3.1. 导入库以及定义超参 3.2. 模型构建 3.3. 数据预处理 3.4. 模型训练以及评估 3.5. 模型运行 4.总结 前言 下面的这篇文章主要教大家如何搭建一个基于Transformer的简单预测模型,并将其用于股票价格预测当中.原代码在文末进行获取. 1.Transformer模型 Transformer 是 Google 的团队在 2017 年提出的一种 NLP 经典模型,现在比较火热的 Bert 也是基于

  • PyTorch搭建LSTM实现时间序列负荷预测

    目录 I. 前言 II. 数据处理 III. LSTM模型 IV. 训练 V. 测试 VI. 源码及数据 I. 前言 在上一篇文章深入理解PyTorch中LSTM的输入和输出(从input输入到Linear输出)中,我详细地解释了如何利用PyTorch来搭建一个LSTM模型,本篇文章的主要目的是搭建一个LSTM模型用于时间序列预测. 系列文章: PyTorch搭建LSTM实现多变量多步长时序负荷预测 PyTorch搭建LSTM实现多变量时序负荷预测 PyTorch深度学习LSTM从input输入

  • Python实现Keras搭建神经网络训练分类模型教程

    我就废话不多说了,大家还是直接看代码吧~ 注释讲解版: # Classifier example import numpy as np # for reproducibility np.random.seed(1337) # from keras.datasets import mnist from keras.utils import np_utils from keras.models import Sequential from keras.layers import Dense, Act

  • python+django+selenium搭建简易自动化测试

    该平台会集成UI自动化及api自动化,里面也会涉及到一些简单的HTML等前端,当然都是很基础的东西.在以后的博客里,我会一点点的尽量写详细,帮助一些测试小白一起成长,当然我也是个小菜鸡. 第一章 django 搭建平台. 1.1搭建环境 Django 官方网站:https://www.djangoproject.com/ Python 官方仓库下载地址:https://pypi.python.org/pypi/Django 这里我们通过pip来安装django ,这里版本用1.10.3. Pyt

  • python神经网络Keras搭建RFBnet目标检测平台

    目录 什么是RFBnet目标检测算法 RFBnet实现思路 一.预测部分 1.主干网络介绍 2.从特征获取预测结果 3.预测结果的解码 4.在原图上进行绘制 二.训练部分 1.真实框的处理 2.利用处理完的真实框与对应图片的预测结果计算loss 训练自己的RFB模型 一.数据集的准备 二.数据集的处理 三.开始网络训练 四.训练结果预测 什么是RFBnet目标检测算法 RFBnet是SSD的一种加强版,主要是利用了膨胀卷积这一方法增大了感受野,相比于普通的ssd,RFBnet也是一种加强吧 RF

  • 浅谈Python基础之I/O模型

    一.I/O模型 IO在计算机中指Input/Output,也就是输入和输出.由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘.网络等,就需要IO接口. 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别? 这个问题其实不同的人给出的答案都可能不同,比如wiki,就认为asynchronous IO和non-blockin

  • Python中函数参数匹配模型详解

    当我们的函数接收参数为任意个,或者不能确定参数个数时,我们,可以利用 * 来定义任意数目的参数,这个函数调用时,其所有不匹配的位置参数会被赋值为元组,我们可以在函数利用循环或索引进行使用 def f(*args): # 直接打印元组参数 print(args) print('-'*20) # 循环打印元组参数 [print(i) for i in args] ... # 传递一个参数 f(1) print('='*20) # 传递5个参数 f(1, 2, 3, 4, 5) 示例结果: (1,)

  • Python实现RabbitMQ6种消息模型的示例代码

    RabbitMQ与Redis对比 ​ RabbitMQ是一种比较流行的消息中间件,之前我一直使用redis作为消息中间件,但是生产环境比较推荐RabbitMQ来替代Redis,所以我去查询了一些RabbitMQ的资料.相比于Redis,RabbitMQ优点很多,比如: 具有消息消费确认机制 队列,消息,都可以选择是否持久化,粒度更小.更灵活. 可以实现负载均衡 RabbitMQ应用场景 异步处理:比如用户注册时的确认邮件.短信等交由rabbitMQ进行异步处理 应用解耦:比如收发消息双方可以使用

  • 浅谈keras 模型用于预测时的注意事项

    为什么训练误差比测试误差高很多? 一个Keras的模型有两个模式:训练模式和测试模式.一些正则机制,如Dropout,L1/L2正则项在测试模式下将不被启用. 另外,训练误差是训练数据每个batch的误差的平均.在训练过程中,每个epoch起始时的batch的误差要大一些,而后面的batch的误差要小一些.另一方面,每个epoch结束时计算的测试误差是由模型在epoch结束时的状态决定的,这时候的网络将产生较小的误差. [Tips]可以通过定义回调函数将每个epoch的训练误差和测试误差并作图,

随机推荐