基于LSTM的股价预测
num_layers=1,output_size=1): # init定义当前lstm模型中有什么样的结构# 输入层神经元数量,隐藏层记忆细胞数量,层数,输出结果的尺寸(回归类一定是1)super(LSTMModel,self).__init__() # 继承nn.Module定义的内容 通常用来保证算法正常运行 有时可不用# 定义超参数#LSTM层# 输出层定义当前架构里面所有的数据流是怎么进行
一、LSTM简介
长短期记忆网络(Long Short-Term Memory, LSTM)是一种特殊类型的循环神经网络(RNN)。传统RNN在处理长序列数据时,存在梯度消失和梯度爆炸的问题,导致模型难以捕捉到序列中的长期依赖关系。而LSTM模型正是为了解决这一问题而诞生的,它能够更好地处理长序列数据,捕捉到序列中的长期依赖关系。
二、LSTM的核心结构
LSTM模型的核心是记忆细胞(Cell),它负责存储和传递信息。每个记忆细胞都包含三个门控单元:输入门、遗忘门和输出门。
- 输入门:控制有多少新的信息被存储在记忆细胞中。它像是一个过滤器,只允许部分信息进入记忆细胞。
- 遗忘门:控制记忆细胞中有多少信息被保留,多少信息被遗忘。它根据当前输入和上一个时间步的隐藏状态来决定遗忘多少信息。
- 输出门:决定有多少信息从记忆细胞中输出。它像是一个阀门,控制着信息的流出
三、LSTM的工作原理
- 信息输入:首先,输入门会决定哪些新的信息被允许进入记忆细胞。这通常是通过一个sigmoid函数来实现的,该函数将输入的信息转化为0到1之间的值,表示信息被允许进入的程度。
- 信息遗忘:接着,遗忘门会决定记忆细胞中的哪些信息被保留,哪些被遗忘。这同样是通过一个sigmoid函数来实现的,该函数根据当前输入和上一个时间步的隐藏状态来计算遗忘的程度。
- 细胞状态更新:然后,记忆细胞会根据输入门和遗忘门的结果来更新自己的状态。这通常是通过一个加法操作来实现的,将遗忘后的细胞状态与新的信息相加,得到更新后的细胞状态。
- 信息输出:最后,输出门会决定有多少信息从记忆细胞中输出。这同样是通过一个sigmoid函数来实现的,该函数将当前时间步的细胞状态和输入转化为0到1之间的值,表示信息被允许输出的程度。然后,这个值会与通过tanh函数激活后的细胞状态相乘,得到最终的输出。
具体可以参考神经网络之lstm-CSDN博客
四、一个基础的基于LSTM的股价预测项目
1.获取数据集
在Kaggle上下载项目所需的数据集:
https://www.kaggle.com/datasets/henryhan117/sp-500-historical-data?resource=download
下载后得到的文件名为"SPX.cxv",在Pycharm中通过可以通过以下代码获得数据的基本信息:
import pandas as pd # 导入这个包用来读取文件
sp500_data=pd.read_csv('SPX.csv',parse_dates=True) # 用pandas读取数据
print(sp500_data.info()) # 输出数据的基本信息
print(sp500_data.head()) # 输出前五行的数据
可以看到运行后得到如下结果:
初步信息
23323 entries:表示1970-2020年每日的数据有23323条。
7 columns:数据集有7列。
Dtype:数据类型。
Non-Null count:非零的计数,这里都是23323,说明没有缺失值。
可以通过以下代码将1927年-2020年调整后得收盘价(Adj Close)用图像的方式绘制出来:
timeseries = sp500_data[["Adj Close"]].values.astype('float32')
plt.figure(dpi=300,figsize=(10,3))
plt.plot(timeseries)
plt.show()
运行后能更直观的看出股票收盘价的走势,可以更好的分析数据。
2.分割数据集
为了熟悉整个流程,我将数据简化成了单维变量数据,即只有时间和收盘价。因为前面数据导入用的是pandas数据,所以要先转化成tensors数据,并且是三维的数据。
# 不能改变顺序,所以没有使用train_test_split等操作(只能过去预测未来,不能打乱时间顺序)
train_size=int(len(timeseries)*0.67) # 前2/3作为训练集
test_size =len(timeseries)-train_size # 剩下作为测试集
train, test =timeseries[:train_size],timeseries[train_size:] # 通过索引的方式把训练集和测试集索引出来
# 注意:样本太大 实际上预测这种方式股票数据不准
# 将pandas的数据类型转化成 Tensors
train_tensor =torch.FloatTensor(train).view(-1,train.shape[0],1) # 还要转换为三维数据,lstm处理的数据是三维数据
test_tensor =torch.FloatTensor(test).view(-1,test.shape[0],1)
print(train_tensor.shape) # batch_size,time_step,input_dimension
print(test_tensor.shape)
# 转化出来的结构应该是 [batch_size,time_step,input_dimension]
可以看到数据被分为训练集15626条,测试集7697条。
3.定义网络架构
以下是一个简单的网络,只给了一个LSTM层和一个Linear线性层:
class LSTMModel(nn.Module):
def __init__(self,input_size=1,hidden_size=50
,num_layers=1,output_size=1): # init定义当前lstm模型中有什么样的结构
# 输入层神经元数量,隐藏层记忆细胞数量,层数,输出结果的尺寸(回归类一定是1)
super(LSTMModel,self).__init__() # 继承nn.Module定义的内容 通常用来保证算法正常运行 有时可不用
# 定义超参数
self.hidden_size=hidden_size
self.num_layer=num_layers
#LSTM层
self.lstm=nn.LSTM(input_size,hidden_size
,num_layers,batch_first=True)
# 输出层
self.fc = nn.Linear(hidden_size,output_size)
定义当前架构里面所有的数据流是怎么进行的
def forward(self,x): # 定义当前架构里面所有的数据流是怎么进行的
# 初始化h0和c0
h0 =torch.rand(self.num_layer,x.size(0),self.hidden_size).requires_grad_()
c0 = torch.rand(self.num_layer, x.size(0), self.hidden_size).requires_grad_()
#LSTM的向前传播,此时要关注是所有时间步上的输出,还是最后一个时间步上的输出
#所有时间上的输出,因此要输出的是output,而不是hn和cn
output,(_,_)=self.lstm(x,(h0.detach(),c0.detach()))
#output的结构为[batch_size,seq_len,hidden_size],因此要取出对我们有用的维度
out=self.fc(output[:,:,:])
return out
4.训练模型
在训练模型,要先进行实例化,并且查看输出数据的结构:
model =LSTMModel() #实例化
model(train_tensor).shape#尝试查看输出数据的结构(一定要做)
超参数设置+训练模型:
# 超参数设置
input_size=1
hidden_size=50
num_layer=1
output_size=1
learning_rate=0.01
num_epochs=300
#实例化模型
model1=LSTMModel(input_size,hidden_size,num_layer,output_size)
#定义损失模型与优化算法
criterion=nn.MSELoss(reduction="mean")
optimizer=torch.optim.Adam(model1.parameters(),lr=learning_rate)
# 开始进行训练的循环
for epoch in range(num_epochs):
outputs=model1(train_tensor)
optimizer.zero_grad()
loss=criterion(outputs,train_tensor[:, :, :])
loss.backward()
optimizer.step()
if(epoch+1)%50==0:
print(f'Epoch[{epoch+1}/{num_epochs}],loss:{loss.item():.4f}')
print("训练完成")
model1.eval()
test_outputs=model1(test_tensor).detach().numpy()
print(MSE(test,test_outputs))
得到的输出结果如下:
问题
训练后问题:
1.损失什么情况下才算好?
用下面这个方法,得出均值,只要训练的损失比均值小才行。
#不用神经网络预测,只将均值当做预测标签计算MSE
def MSE(Y_ture,Y_Predict):
return ((Y_ture-Y_Predict)**2).sum()/Y_ture.shape[0]
print(MSE(train,train.mean()))#训练集数据放到mse
print(MSE(test,test.mean()))#测试集数据放到mse
而我们运行之后发现得到的损失值不是很理想,说明模型的学习能力以及初始化能力可能有很大问题。所以要增加LSTM模型的复杂度。最简单的方式,就是加层。原本50个神经元现在加到100个,1个隐藏层变成5个。
2.损失下降速率缓慢
说明学习效率和迭代效率有问题。可以提升学习率,同时也可以提升batch_size(如果有的话)。
3.迭代次数够不够?
如果不够,可以尝试增加总迭代次数,给模型更多的训练空间。epochs300变1500
4.测试集的损失也很高
修改后得参数如下,再次运行后的过程十分缓慢。
#调整后的超参数,其他代码都不变
input_size=1
hidden_size=100
num_layer=5
output_size=1
learning_rate=0.01
num_epochs=1500
5.过拟合问题
在使用LSTM进行训练时,可能会出现过拟合的情况,即模型在训练集上表现良好,但在测试集上表现不佳。为了解决LSTM的过拟合问题,可以减少模型容量,比如减少神经元数量、隐藏层数量,降低模型复杂度; 提前停止训练,如果明显出现过拟合,就可以提前停止训练等等。
更多推荐
所有评论(0)