版权声明:本文为博主原创文章,如需转载请贴上原博文链接:使用LSTM神经网络对股票日线行情进行回归训练(Pytorch版)-CSDN博客
前言:近期在尝试使用lstm对股票日线数据进行拟合,初见成型但是效果不甚理想(过拟合/欠拟合),原因可能有如下几点:①前传网络的架构不够完善;②网络参数组合不合理;③使用CPU而非GPU进行的训练(性能低导致效果不佳);④Epoch数过少。暂且先记录验证结果以便后续做出相对应的调整,完整代码见文末。
目录
〇、各依赖包版本
一、Pytorch中LSTM网络参数说明
二、网络结构构建及参数组合选择
2.1 网络结构构建
2.2 参数组合选择
三、验证结果展示
3.1 优化器ADAM&SGD的验证效果情况
3.2 Batch_Size=64&320的验证效果情况
3.3 num_layers=1、2和dropout=0、0.2、0.4的验证效果情况
参考文献
〇、各依赖包版本
mplfinance==0.12.9b7
pandas==1.1.5
SQLAlchemy==1.4.41
SQLAlchemy_Utils==0.41.2
tushare==1.2.85
backtrader==1.9.78.123
akshare==1.10.42
torch==1.13.1
numpy==1.21.6
matplotlib==3.5.3
一、Pytorch中LSTM网络参数说明
具体参照LSTM:其中`bias`和`proj_size`使用默认值,其余参数可在代码中自行修改。
二、网络结构构建及参数组合选择
import torch# 选择在cpu或gpu上跑训练
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# device = torch.device('cuda:0') # 如果知道gpu有多少核,可以直接指定# 如果只有cpu,则以下代码可不执行
# 加载模型到device上
model = SimpleLSTM()
model.to(device)# 加载数据到device上
X, y = X.to(device), y.to(device)
2.1 网络结构构建
LSTM网络核心的便是网络结构的构建,参考网络上有关lstm网络的文章,多数都是构建最普遍的网络结构,如下:
import torch.nn as nnclass SimpleLSTM(nn.Module):def __init__(self, INPUT_SIZE, HIDDEN_SIZE, OUTPUR_SIZE, NUM_LAYERS):super(SimpleLSTM, self).__init__()self.lstm = nn.LSTM(INPUT_SIZE, HIDDEN_SIZE, NUM_LAYERS, batch_first=True)self.fc = nn.Linear(HIDDEN_SIZE, OUTPUR_SIZE)self.sigmoid = nn.Sigmoid()def forward(self, X):X, hidden = self.lstm(X, None)X = self.fc(X[:, -1, :])X = self.sigmoid(X)return Xmodel = SimpleLSTM(INPUT_SIZE, HIDDEN_SIZE, OUTPUR_SIZE, NUM_LAYERS)
通常这样的结构完全够用了,但是为了方便后续能够对网络进行灵活的调整,还是将隐藏状态h_0、h_n及单元状态c_0、c_n开放出来,构建更加完整的网络结构,同时对输入数据进行批标准化处理(取消了对输出数据的非线性激活,待后续研究后再考虑是否增加该层),后续训练及验证都使用以下网络结构:
import torch
import torch.nn as nnclass SimpleLSTM(nn.Module):def __init__(self, INPUT_SIZE, HIDDEN_SIZE, OUTPUT_SIZE, NUM_LAYERS):super(SimpleLSTM, self).__init__()self.D = 1if BIDIRECT:self.D = 2self.h_0 = torch.randn(self.D * NUM_LAYERS, BATCH_SIZE, HIDDEN_SIZE)self.c_0 = torch.randn(self.D * NUM_LAYERS, BATCH_SIZE, HIDDEN_SIZE)self.h_n = torch.zeros(self.D * NUM_LAYERS, BATCH_SIZE, HIDDEN_SIZE)self.c_n = torch.zeros(self.D * NUM_LAYERS, BATCH_SIZE, HIDDEN_SIZE)self.lstm = nn.LSTM(INPUT_SIZE, HIDDEN_SIZE, NUM_LAYERS, batch_first=BATCH1ST, dropout=DROPOUT, bidirectional=BIDIRECT)self.fc = nn.Linear(HIDDEN_SIZE * self.D, OUTPUT_SIZE)self.bn = nn.BatchNorm1d(num_features=WINDOW_SIZE)def forward(self, input_, h_0, c_0):input_ = self.bn(input_)x, (h_n, c_n) = self.lstm(input_, (h_0, c_0))x = self.fc(x)return x, (h_n, c_n)model = SimpleLSTM(INPUT_SIZE, HIDDEN_SIZE, OUTPUT_SIZE, NUM_LAYERS)
2.2 参数组合选择
# === 定义固定超参(其中SHUFFLE、BIDIRECT可不开放出来,直接使用默认参数)
STOCK_TSCODE = '000001.SZ' # 平安YH# DATA parameters
TRAIN_SCALE = 0.8 # 训练集和测试/验证集比例=8:2
WINDOW_SIZE = 1000 # DATALOADER parameters
SHUFFLE = False # 对于有序的数据,每个Epoch不需要打乱
DROP_LAST = True # 对于含有初始隐藏状态h_0及初始单元状态c_0的网络,将多于的数据舍弃以便能完成训练(对于数据形式不规整的数据集,可以构建不含有h_0和c_0的网络结构,或者修改TRAIN_SCALE比例亦或将测试/验证集的数据量固定,使得全部数据得以测试、验证)# train patameters
EPOCHS = 1 # 只训练一个Epoch
LEARNING_RATE = 1e-2
MOMENTUM = 0.9# lstm parameters
INPUT_SIZE = 1
HIDDEN_SIZE = 128
OUTPUT_SIZE = 1BATCH1ST = True # 将batch_size参数置于首位
BIDIRECT = False # 不使用双向网络
# === 定义可变超参 LSTM parameters(可以通过修改以下参数来调整网络的训练效果,注释中提供2~3中参数可供选择)
OPTIM_NAME = '' # 'ADAM'、'SGD'
BATCH_SIZE = 64 # 64、320
NUM_LAYERS = 2 # 1、2
DROPOUT = 0.2 # 0、0.2、0.4
*当`SHUFFLE=True`
时会将时序的数据打乱,起到反作用;
*当`BIDIRECT=True`
时会消耗大量内存(用cpu跑训练不建议设置为True)且对于某些类型的网络并不需要使用双向网络模型,故虽然开放该参数但仅给一个拟合结果以作对比(见图2.1),其余需自行训练;
根据上述可变超参,对于同一种网络结构一共可以组成4种属性共24种不同的组合,如下表2-1所示:
表2-1 参数组合
lstm model | num_layers | batch_size | dropout | optim_name |
---|---|---|---|---|
lstm(input, (h_0, c_0)) | 1、2 | 64、320 | 0、0.2、0.4 | Adam、SGD |
后续所有验证结果图片名称都是以各参数名称及数值来命名的,范例如下:
lstm_model_BS64_EP1_NL1_DO0_OPADAM_DLTrue_BDFalse
| | | | | | |
| Epoch | dropout | | bidirectional
batch_size | optimizer |
num_layers drop_last
三、验证结果展示
下表3-1给出24种参数组合的验证结果,这24种验证结果使用的是都只经过一次训练的模型(即这些模型只经过一个Epoch的训练),不排除经过多次训练后的模型得出的验证效果会和下表呈现出不同的情况:
表3-1 24种参数组合验证效果
num_layers | batch_size | dropout | optim_name | 拟合效果 |
---|---|---|---|---|
1 | 64 | 0 | ADAM | √ |
1 | 64 | 0.2 | ADAM | √ |
1 | 64 | 0.4 | ADAM | √ |
1 | 320 | 0 | ADAM | × |
1 | 320 | 0.2 | ADAM | × |
1 | 320 | 0.4 | ADAM | × |
2 | 64 | 0 | ADAM | √ |
2 | 64 | 0.2 | ADAM | √ |
2 | 64 | 0.4 | ADAM | √ |
2 | 320 | 0 | ADAM | × |
2 | 320 | 0.2 | ADAM | × |
2 | 320 | 0.4 | ADAM | × |
1 | 64 | 0 | SGD | × |
1 | 64 | 0.2 | SGD | × |
1 | 64 | 0.4 | SGD | × |
1 | 320 | 0 | SGD | × |
1 | 320 | 0.2 | SGD | × |
1 | 320 | 0.4 | SGD | × |
2 | 64 | 0 | SGD | × |
2 | 64 | 0.2 | SGD | × |
2 | 64 | 0.4 | SGD | × |
2 | 320 | 0 | SGD | × |
2 | 320 | 0.2 | SGD | × |
2 | 320 | 0.4 | SGD | × |
3.1 优化器ADAM&SGD的验证效果情况
从上表可见,ADAM比SGD的效果更好,这也很符合优化器进化的顺序。在12种优化器是SGD的验证结果中,选出效果最好的一种,如图3.1所示,蓝色点是收盘价,橙色线是预测值,相差很远,这样的结果也很难运用在后面的策略中。但即便这样也不能完全说SGD不好,因为只跑了一个回合,跑多一些回合效果可能会变好甚至特定时候会超过多回合的ADAM,这得需要各位自己试验了。至此后续仅针对ADAM优化器进行讨论。
3.2 Batch_Size=64&320的验证效果情况
从表3-1可见,即便优化器是ADAM,batch_size=320的效果也没有batch_size=64的效果好,如图3.2所示,欠拟合的情况很明显,当然batch_size的选择还要根据机器的性能来设置,或许batch_size=32的效果比64更好。
3.3 num_layers=1、2和dropout=0、0.2、0.4的验证效果情况
剩下的参数组合见图3.3,共三行两列6张图,左列num_layers=1,右列=2;自上而下dropout=0、0.2、0.4,当lstm只有一层(左列),波动很大,DO0预测值和实际值贴合很近(过拟合),DO0.2欠拟合情况较为明显;而当lstm有两层的时候,预测的较大值和较小值均在实际值范围内,算是预测较准确的,但是局部依旧存在过拟合的情况,还达不到制定策略的要求。
至此lstm网络的训练基本完成,但过拟合的问题尚未解决且并不能通过控制DROPOUT系数来完全避免,后续可以尝试构建更深层的网络结构或尝试使用GRU来看是否有所改善。
相关代码见:demo_lstm_model.py,同目录下的`data`文件夹保存了全部24种参数组合的训练模型(一个Epoch),`lstm(input, (h_0, c_0))`文件夹下保存了“ADAM”和“SGD”共24张验证结果图片。
参考文献
1.基于深度学习的股票预测(完整版,有代码)
2.【python量化】基于backtrader的深度学习模型量化回测框架
3.AI金融:利用LSTM预测股票每日最高价