当前位置:   article > 正文

LSTM-代码讲解(股票预测)_lstm股票预测

lstm股票预测

目录

前言:

一、代码讲解

1 导入相关资源包

2 定义模型结构

3 制作数据集

4 模型训练

5 测试与保存结果 


前言:

学习LSTM代码之前要先了解LSTM模型解决什么问题,简而言之,LSTM是RNN的升级版,擅长挖掘时序数据中的信息。本模型对ZHW_AI课题组.2021的LSTM(股票预测)代码进行讲解。本文对代码的难以理解的、重点的部分进行了注释,并对需要注意的点进行了文字提醒。同时,本文最大的特点,是结合本人自制的数据集制作图、LSTM网络结构图,深入浅出的对由数据-->网络的具体过程进行了讲解演示。

一、代码讲解

基本逻辑:导入相关资源包-->定义模型结构-->制作数据集-->模型训练-->测试与保存结果

1 导入相关资源包

这里除了安装深度学习框架pytorch之外,还需要安装matplotlib、numpy、pandas、tushare等库,安装步骤都很简单,只需输入pip3 install xxx(库名)即可。

  1. import matplotlib.pyplot as plt
  2. import numpy as np
  3. import tushare as ts
  4. import torch
  5. from torch import nn
  6. import datetime
  7. import time

2 定义模型结构

  1. class LSTM_Regression(nn.Module): #调用nn.Module的初始化函数,初始化LSTMRegression类。
  2. """
  3. 使用LSTM进行回归
  4. 参数:
  5. - input_size: feature size
  6. - hidden_size: number of hidden units
  7. - output_size: number of output
  8. - num_layers: layers of LSTM to stack
  9. """
  10. def __init__(self, input_size, hidden_size, output_size=1, num_layers=2):
  11. super().__init__()
  12. self.lstm = nn.LSTM(input_size, hidden_size, num_layers) #定义一个LSTM层,其中输入的大小为inputsize,输出的大小为hiddensize,LSTM的层数为numlayers。
  13. self.fc = nn.Linear(hidden_size, output_size) #定义一个全连接层,其中输入的大小为hiddensize,输出的大小为outputsize
  14. def forward(self, _x):
  15. x, _ = self.lstm(_x) # _x is input, size (seq_len, batch, input_size) 这行代码是在LSTMRegression类的forward函数中,将输入_x送入LSTM层中进行计算,并将输出赋值给变量x。其中x的形状为(seqlen, batch, inputsize),表示输入的序列长度为seqlen,batchsize为batch,每个时间步的特征维度为inputsize。在这里,由于不需要使用LSTM层的输出,所以将其赋值给了一个下划线变量。
  16. s, b, h = x.shape # x is output, size (seq_len, batch, hidden_size)
  17. x = x.view(s*b, h) #将x的形状改为(sb, h)。 在深度学习中,全连接层通常接受的输入是二维张量,即(batchsize, inputsize),其中batchsize表示批次大小,inputsize表示每个样本的特征维度。这个操作是为了将LSTM的输出送入全连接层中。
  18. x = self.fc(x) #将x送入全连接层中,得到输出x。
  19. x = x.view(s, b, -1) # 把形状改回来
  20. return x

参照LSTM网络模型图可知,self.lstm 包含了input_layers的定义,hidden_layers的定义

self.fc包含了对output_layers的定义,此二者即完成了整个LSTM网络模型的搭建。

注意:在深度学习中,全连接层通常接受的输入是二维张量,即(batchsize, inputsize),其中batchsize表示批次大小,inputsize表示每个样本的特征维度。

3 制作数据集

  1. def create_dataset(data, days_for_train=5) -> (np.array, np.array):
  2. """
  3. 根据给定的序列data,生成数据集
  4. 数据集分为输入和输出,每一个输入的长度为days_for_train,每一个输出的长度为1。
  5. 也就是说用days_for_train天的数据,对应下一天的数据。
  6. 若给定序列的长度为d,将输出长度为(d-days_for_train+1)个输入/输出对
  7. """
  8. dataset_x, dataset_y= [], []
  9. for i in range(len(data)-days_for_train):
  10. _x = data[i:(i+days_for_train)] #具体来说,i:(i+days_for_train)] 是一个左闭右开的区间
  11. dataset_x.append(_x)
  12. dataset_y.append(data[i+days_for_train])
  13. return (np.array(dataset_x), np.array(dataset_y))  # 数据类型变换:dataset_x和data_set_y数据类型(type)由列表(list)->数组(numpy.nparray)  
  14.     """

 为什么要将数据集由序列变为数组?序列和数据的联系和区别
        为了更高效地进行数值计算,通常会使用数组(numpy.nparray)代替列表(list)。相比于列表,数组具有以下优势:
    1. 数组支持向量化操作,即对整个数组进行操作,而不需要使用循环。这样可以大大提高计算速度;
    2. 数组支持broadcasting操作,即不同形状的数组之间的算法,这样可以简化代码,提高可读性。  

原始数据和数据集的区别

        原始数据指的是要研究的时序数据,数据集并不是把整个原始时序数据给作为一个数据,而是要结合研究目的对原始时序数据进行采样从而制作。

案例演示:input数据集的采集:原始时序数据为(n,1)的序列,每k个作为一个输入数据,共能采集(n-k)个输入数据,数据集尺寸为(n-k,k,1),label数据的采集是同样的道理。

数据集制作可视化图

4 模型训练

  1. if __name__ == '__main__':
  2. t0 = time.time() #这段代码的作用是记录当前时间并将其赋值给变量t0。通常情况下,可以通过将代码结束时的当前时间减去t0的值来测量代码的执行时间。
  3. '获取原始数据'
  4. data_close = ts.get_k_data('000001', start='2019-01-01', index=True)['close'] # 取上证指数的收盘价 ,'index=True'表示获取的是指数数据,而不是股票数据。['close']表示只获取收盘价数据。
  5. data_close.to_csv('000001.csv', index=False) #将下载的数据转存为.csv格式保存
  6. data_close = pd.read_csv('000001.csv') #读取.csv文件
  7. # df_sh = ts.get_k_data('sh', start='2019-01-01', end=datetime.datetime.now().strftime('%Y-%m-%d'))
  8. # print(df_sh.shape)
  9. # '绘制原始数据折线图'
  10. #data_close = data_close.astype('float32').values # 转换数据类型:astype('float32')将数据类型转换为float32,values将其转换为numpy数组
  11. #plt.plot(data_close) #将数据绘制成折线图
  12. #plt.savefig('data.png', format='png', dpi=200) #将图像保存,名字”data.png“,格式为png,分辨率200
  13. #plt.close() #关闭图像
  14. '数据预处理:将价格标准化到0~1'
  15. max_value = np.max(data_close)
  16. min_value = np.min(data_close)
  17. data_close = (data_close - min_value) / (max_value - min_value)
  18. '制作数据集'
  19. dataset_x, dataset_y = create_dataset(data_close, DAYS_FOR_TRAIN)
  20. '划分训练集和测试集,70%作为训练集'
  21. train_size = int(len(dataset_x) * 0.7)
  22. train_x = dataset_x[:train_size]
  23. train_y = dataset_y[:train_size]
  24. '将数据改变形状,RNN 读入的数据维度是 (seq_size, batch_size, feature_size)'
  25. train_x = train_x.reshape(-1, 1, DAYS_FOR_TRAIN) #这行代码的作用是将trainx数组的形状改变为(-1, 1, DAYSFORTRAIN)。其中,-1表示该维度的大小由程序自动计算得出,1表示batchsize为1,DAYSFORTRAIN表示featuresize为DAYSFORTRAIN。这是为了将数据转换为RNN读入的格式。
  26. train_y = train_y.reshape(-1, 1, 1)
  27. '转为pytorch的tensor对象'
  28. train_x = torch.from_numpy(train_x) #这行代码的作用是将numpy数组trainx转换为PyTorch的tensor对象。这是因为在PyTorch中,神经网络的输入和输出必须是tensor对象。通过将trainx转换为tensor对象,可以将其作为神经网络的输入。
  29. train_y = torch.from_numpy(train_y)
  30. '模型训练'
  31. model = LSTM_Regression(DAYS_FOR_TRAIN, 8, output_size=1, num_layers=2) # 导入模型并设置模型的参数输入输出层、隐藏层等
  32. model_total = sum([param.nelement() for param in model.parameters()]) # 计算模型参数: 具体来说,它使用了PyTorch中的sum函数和nelement函数,对模型的所有参数进行求和并返回总参数数量。这个值可以用来评估模型的大小和复杂度。
  33. print("Number of model_total parameter: %.8fM" % (model_total/1e6)) # 这行代码的作用是打印模型的总参数数量。我们使用了Python的百分号格式化字符串,其中%.8f表示将一个浮点数格式化为8位小数的字符串。我们将模型的总参数数量除以1e6,以将其从字节转换为兆字节。最后,我们将结果插入到字符串中,使用%f占位符。
  34. train_loss = []
  35. loss_function = nn.MSELoss() #损失函数
  36. optimizer = torch.optim.Adam(model.parameters(), lr=1e-2, betas=(0.9, 0.999), eps=1e-08, weight_decay=0) #优化器
  37. for i in range(200): #迭代200次
  38. out = model(train_x)
  39. loss = loss_function(out, train_y)
  40. loss.backward()
  41. optimizer.step()
  42. optimizer.zero_grad()
  43. train_loss.append(loss.item()) #为了在训练过程中访问损失函数的值,您可以使用loss.item()方法。该方法将损失函数的标量值作为Python浮点数返回。
  44. # 将训练过程的损失值写入文档保存,并在终端打印出来
  45. with open('log.txt', 'a+') as f: #语句打开一个名为log.txt的文档,如果该文档不存在则创建一个新的文档
  46. f.write('{} - {}\n'.format(i+1, loss.item())) #将当前的迭代次数和损失值写入到文档中
  47. if (i+1) % 1 == 0:
  48. print('Epoch: {}, Loss:{:.5f}'.format(i+1, loss.item())) #在终端打印出当前的迭代次数和损失值。其中,{:.5f}表示将一个浮点数格式化为5位小数的字符串。

模型训练的准备工作:

(1)制作并处理数据集

        1. 获取原始数据

        2. 原始数据的预处理:标准化

        3. 制作数据集:调用create_dataset( )函数制作数据集 

        4. 划分训练集和测试集

(2)网络输入维度和数据类型转换要求:

     1. RNN 读入的数据维度是 (seq_size, batch_size, feature_size)

     2.在PyTorch中,神经网络的输入和输出必须是tensor对象

模型训练完毕之后,想要对损失函数进行可视化操作、保存模型参数或计算训练时间的可进行以下操作。

  1. # 画loss曲线 matplotlib.pyplot
  2. plt.figure() # 先准备一个图板
  3. plt.plot(train_loss, 'b', label='loss') # train_loss是要绘制的列表文件,b-颜色
  4. plt.title("Train_Loss_Curve") # 标题
  5. plt.ylabel('train_loss') # y轴名称
  6. plt.xlabel('epoch_num') # x轴名称
  7. plt.savefig('loss.png', format='png', dpi=200) #将图保存为图片:图片名,格式,分辨率
  8. plt.close() #关闭图版
  9. # torch.save(model.state_dict(), 'model_params.pkl') # 可以保存模型的参数供未来使用
  10. t1 = time.time()
  11. T = t1 - t0
  12. print('The training time took %.2f' % (T / 60) + ' mins.')
  13. tt0 = time.asctime(time.localtime(t0))
  14. tt1 = time.asctime(time.localtime(t1))
  15. print('The starting time was ', tt0)
  16. print('The finishing time was ', tt1)

5 测试与保存结果 

  1. # for test
  2. model = model.eval() # 转换成测试模式
  3. # model.load_state_dict(torch.load('model_params.pkl')) # 读取参数
  4. # 注意这里用的是全集 模型的输出长度会比原数据少DAYS_FOR_TRAIN 填充使长度相等再作图
  5. dataset_x = dataset_x.reshape(-1, 1, DAYS_FOR_TRAIN) # (seq_size, batch_size, feature_size)
  6. dataset_x = torch.from_numpy(dataset_x)
  7. pred_test = model(dataset_x) # 全量训练集的模型输出 (seq_size, batch_size, output_size)
  8. pred_test = pred_test.view(-1).data.numpy()
  9. pred_test = np.concatenate((np.zeros(DAYS_FOR_TRAIN), pred_test)) # 填充0 使长度相同
  10. assert len(pred_test) == len(data_close)
  11. plt.plot(pred_test, 'r', label='prediction')
  12. plt.plot(data_close, 'b', label='real')
  13. plt.plot((train_size, train_size), (0, 1), 'g--') # 分割线 左边是训练数据 右边是测试数据的输出
  14. plt.legend(loc='best')
  15. plt.savefig('result.png', format='png', dpi=200)
  16. plt.close()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/167994
推荐阅读
相关标签
  

闽ICP备14008679号