基于LSTM和RNN写的简单的股票预测小项目
前言
一、RNN和LSTM是什么?
1.RNN是什么?
1.总结
RNN是用来做时序任务的循环神经网络,具有短期记忆
2.RNN的简单讲解
先用一张图来了解一下RNN

假设我们有一句话是"hey how are you",从图中可以看出RNN的时序推测就是从前往后一个时间步一个时间步的推测,而每一个时间步都会从隐藏层出现一个箭头指向下一个时间步,这则是RNN最重要的地方,也是RNN能短期记忆的关键,被称为隐藏状态,公式为h(t)=f(W∗x(t)+U∗h(t−1))由当前输入的值乘上权重矩阵,加上上一个时间步的隐藏状态的值乘上对应的权重矩阵再经过激活函数得到现在的隐藏状态的值,隐藏状态的计算通过将当前时间步的输入与前一个时间步的隐藏状态相结合,从而使网络能够捕获时间序列中的信息传递和依赖关系
3.RNN的缺陷
RNN随着推测数据量的增加反向传播导致梯度累积出现梯度消失和梯度爆炸的情况,通过一张图来了解是怎么发生的


第一张图是举一个从S0到S3只有三步的一个短时间步的例子,第二张图就是对第一张图的方向传播的链式求导,看公式可能不是很好理解,那我们看下面的线式连接图,在先取出,激活函数和偏置的情况下,只追求w,从L1这个loss输出来求△y1再求出S1最后得出w,从L1到w有3步,而L2既能通过S1求w,也能通过S2求w,可以看出随着推测的数量增多,梯度累积越复杂,再通过几张图来了解梯度消失和梯度爆炸是怎么来的

之前的公式我是去掉了激活函数来简化梯度累积的过程,那我们再加回激活函数,这张图告诉我们的是tanh和sigmoid激活函数的前向传播和反向传播的形状和取值范围先说tanh,![]()
![]()
这是tanh的前向和后向的公式,通过这个和图能够了解tanh的偏导范围是(0,1),接下来是sigmoid
![]()
![]()
这是sigmoid的前向和后向的公式,通过这个和图能够了解sigmoid的偏导范围是(0,0.25)

通过之前的公式可以简化为∂Sk+1/∂Sk=σ⋅w,当激活函数是sigmoid的时候,只有w>4以上才能使梯度接近1,只要w<4梯度就小于1,随着连乘梯度会趋向于零导致梯度消失,而tanh则缓解了梯度消失,但w>1时则还是会导致梯度爆炸的问题
2.LSTM是什么?
1.总结
LSTM是在RNN基础上提出的能解决RNN无法长依赖(也就是长时记忆)的循环神经网络
2.LSTM的简单讲解
先看LSTM的结构图

简单介绍一下LSTM的各个门的作用和流程,ht-1和Xt就是RNN那讲的输入和上一个的隐藏状态值,经过forget gate也就是遗忘门,通过sigmoid的函数来选择遗忘程度,然后和Ct-1(细胞状态)结合根据sigmoid的值来决定保留的信息,接下来是输入门sigmoid决定要不要存入新信息作为一个阀门,tanh压缩信息避免数值过大或过小影响训练,最后和细胞状态相加存入新信息,输出门先通过sigmoid决定哪些信息可以输出,输出来源来自细胞状态所存的信息,相结合就是输出值
二、环境和效果展示
1.环境

python=3.8
2.效果展示
主界面

LSTM的预测和评估

因为是先进行的RNN,没怎么改LSTM的参数和专门强化训练,可以通过增加训练轮次来增强效果
RNN和LSTM对比

三、代码分析
1.数据爬取
import akshare as ak
import pandas as pd
df = ak.stock_zh_a_hist(
symbol="600519", # 股票代码
period="daily", # 日线数据
start_date="20100101", # 起始日期(足够长的训练数据)
end_date="20250101", # 结束日期
adjust="qfq" # 前复权,保证价格连续性
)
# 数据清洗(只保留你需要的列)
df = df[['日期', '开盘', '最高', '最低', '收盘', '涨跌幅']]
df.columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Change']
df['Adj Close'] = df['Close'] # 复权价用Close代替(AKShare的qfq已经处理过)
df = df.dropna().reset_index(drop=True)
print(f"获取到真实数据共 {len(df)} 行")
print(df.head())
# -------------------------- 2. 划分训练集和测试集 --------------------------
# 前90%作为训练集,后10%作为测试集(数据量足够,不会出现29行的问题)
train_size = int(0.9 * len(df))
df_train = df[:train_size]
df_test = df[train_size:]
# 保存为CSV文件,直接替换你原来的文件
df_train.to_csv("./guoiao/train/train.csv", index=False)
df_test.to_csv("./guoiao/test/test.csv", index=False)
print(f"\n训练集: {len(df_train)} 行")
print(f"测试集: {len(df_test)} 行")
2.读入数据
# 负责数据加载、归一化、构建时序序列
class DataProcessor:
def __init__(self):
self.scaler_X = StandardScaler() # 输入特征标准化器
self.scaler_y = StandardScaler() # 标签标准化器
self.seq_length = 10 # 时序序列长度(用10天数据预测第11天)
def load_and_preprocess(self, train_path, test_path):
"""
加载并预处理训练集和测试集
:param train_path: 训练集路径
:param test_path: 测试集路径
:return: 处理好的张量数据
"""
# 读取CSV数据
df_train = pd.read_csv(train_path)
df_test = pd.read_csv(test_path)
# 去除空值 + 删除日期列(非数值特征)
df_train = df_train.dropna().drop("Date", axis=1)
df_test = df_test.dropna().drop("Date", axis=1)
# 分离输入特征(High, Low, Close)和输出目标(Adj Close)
input_features = df_train[['High', 'Low', 'Close']].values
output_target = df_train[['Adj Close']].values
test_input_features = df_test[['High', 'Low', 'Close']].values
test_output_target = df_test[['Adj Close']].values
# 标准化(归一化到0均值1方差)
input_scaled = self.scaler_X.fit_transform(input_features)
test_input_scaled = self.scaler_X.transform(test_input_features)
output_scaled = self.scaler_y.fit_transform(output_target)
test_output_scaled = self.scaler_y.transform(test_output_target)
# 构建时序序列(输入序列 + 对应标签)
X_seq, y_seq = self.create_sequences(input_scaled, output_scaled)
test_X_seq, test_y_seq = self.create_sequences(test_input_scaled, test_output_scaled)
# 转换为PyTorch张量
input_tensor = torch.tensor(X_seq, dtype=torch.float32)
output_tensor = torch.tensor(y_seq, dtype=torch.float32)
test_input_tensor = torch.tensor(test_X_seq, dtype=torch.float32)
test_output_tensor = torch.tensor(test_y_seq, dtype=torch.float32)
return (input_tensor, output_tensor), (test_input_tensor, test_output_tensor)
3.模型架构
# LSTM股票预测模型(长短期记忆网络,适合时序数据)
class StockLSTM(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, out_size, dropout=0.2):
"""
初始化LSTM模型
:param input_size: 输入特征维度(这里是3:最高价、最低价、收盘价)
:param hidden_size: LSTM隐藏层神经元数量
:param num_layers: LSTM层数
:param out_size: 输出维度(预测值,这里是1)
:param dropout: 防止过拟合的随机失活率
"""
super(StockLSTM, self).__init__()
# 定义LSTM层
self.lstm = nn.LSTM(
input_size,
hidden_size,
num_layers,
batch_first=True, # 输入形状为(batch, seq_len, feature)
dropout=dropout if num_layers > 1 else 0 # 多层才启用dropout
)
self.batch_norm = nn.BatchNorm1d(hidden_size) # 批归一化,加速训练
self.dropout = nn.Dropout(dropout) # Dropout层
# 全连接层1:隐藏层 -> 隐藏层/2
self.fc1 = nn.Linear(hidden_size, hidden_size // 2)
# 全连接层2:隐藏层/2 -> 输出
self.fc2 = nn.Linear(hidden_size // 2, out_size)
self.relu = nn.ReLU() # 激活函数
def forward(self, x):
"""前向传播(模型推理)"""
# LSTM前向计算,out输出所有时间步结果
out, (hidden, cell) = self.lstm(x)
# 只取最后一个时间步的输出用于预测
out = out[:, -1, :]
# 归一化 + Dropout + 全连接层
out = self.batch_norm(out)
out = self.dropout(out)
out = self.relu(self.fc1(out))
out = self.fc2(out)
return out
# 简单RNN模型(循环神经网络,用于和LSTM对比)
class SimpleRNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size, num_layers=2, dropout=0.2):
"""
初始化RNN模型
参数含义与LSTM一致
"""
super(SimpleRNN, self).__init__()
# 定义RNN层
self.rnn = nn.RNN(
input_size,
hidden_size,
num_layers=num_layers,
batch_first=True,
dropout=dropout if num_layers > 1 else 0
)
self.dropout = nn.Dropout(dropout)
# 全连接层:隐藏层 -> 输出
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
"""RNN前向传播"""
out, hidden = self.rnn(x)
out = out[:, -1, :] # 取最后一个时间步
out = self.dropout(out)
out = self.fc(out)
return out


被折叠的 条评论
为什么被折叠?



