
简介面向深度学习初学者和金融时序分析爱好者这是一套多输入单输出LSTM模型的Python实现项目聚焦股票收盘价预测从历史开盘价、最高价、最低价中提取多维特征输出下一交易日的收盘价。压缩包内共10个文件包括2个.ipynb源码、2个.csv数据集、2个.yaml项目说明及4个zip打包副本整体约1.05MB轻量便于快速上手由 DeepLearning_ 发布已有618人学习。项目以Jupyter Notebook串联完整流程读者可按步骤复现数据预处理、滑动窗口样本构造、LSTM网络搭建、模型训练与验证并对比预测值与真实值同时可参考验证集与早停策略来控制过拟合深入理解LSTM细胞状态、遗忘门、输入门、输出门的工作机制以及多输入单输出建模思想。这套方案不只适用于股价还可拓展到天气、电力负荷等序列预测任务。1. 多输入单输出LSTM一个模型同时吃多个变量预测一个目标值多输入单输出LSTM说白了就是让一个模型同时吃进好几条时间序列最后只吐一个预测值。比如用一台风机的温度、转速、振动幅值、当前风速四个变量过去24小时的数据去预测下一个小时的轴承温度这就是典型的多输入单输出。这类模型在电力负荷预测、工业设备故障预警、气象要素预报里都很常见但真正卡住大多数人第一次跑通的地方不是LSTM单元本身的原理而是训练数据怎么从一张“宽表”变成模型要求的(samples, timesteps, features)三维结构以及多输入到底该走一条LSTM还是多路输入再合并。这篇文章直接按一个JupyterNotebook项目的路子从数据切分写到模型训练、从高频坑的排查写到单步预测如何向多步预测延伸代码可以直接抄走改成自己的数据。2. 多输入LSTM的数据准备把多列原始数据切成监督学习样本2.1 数据形状的底层逻辑samples、timesteps、features分别代表什么LSTM不是像XGBoost那样把每一行当成一个独立样本。它要的是“一段连续历史”作为一条样本。这种差异在最开始就会绊住人你把DataFrame直接model.fit进去Keras立刻报错说维度不对。常见的数据组织方式是这样假设你有一张表格每一行是一个时间点列是温度、转速、振动、风速这几个特征目标是轴承温度。要让LSTM用过去seq_len个时间点预测下一个时间点的轴承温度构造出来的输入张量形状必须是维度含义示例取值samples一共有多少条历史窗口1000行数据切出约990条timesteps每条样本包含几个连续时间点seq_len10features每个时间点上有几个输入变量4个特征列输出y的形状则是(samples,)或者(samples, 1)对应每个窗口之后的那一个目标值。注意Keras的惯例是把timesteps放在第二维不是第三维。你把维度顺序反了模型不会直接报错但训练出来的loss是乱的这种错误比ShapeError更难查。多输入单输出的“多输入”在绝大多数实际项目里就是指多个特征列共享一个时间轴。少数情况下几个输入序列的时间轴或采样率不一致才需要真正拆成多个输入分支。先记住这个前提先按共享时间轴来准备数据绝大多数场景用不到复杂分支。2.2 滑动窗口切分函数与最小可复现代码准备数据这一步我一般用一个通用函数输入是DataFrame输出是X, y两个numpy数组。直接看代码import numpy as np import pandas as pd def create_sequences(data: pd.DataFrame, seq_len: int, target_col: int 0): 把多列DataFrame切成监督学习样本。 data: 按时间排序的原始数据每列一个特征 seq_len: 历史窗口长度用过去seq_len个点预测下一点 target_col: 目标列在data中的列索引默认第0列 返回: X: shape (样本数, seq_len, 特征数) y: shape (样本数, 1) X, y [], [] data_arr data.to_numpy(dtypenp.float32) for i in range(len(data_arr) - seq_len): X.append(data_arr[i: i seq_len]) # 前seq_len行全部特征 y.append(data_arr[i seq_len, target_col]) # 紧接下一行的目标值 return np.array(X), np.array(y).reshape(-1, 1) # 用法示例 # df pd.read_csv(data/processed/example.csv, parse_dates[time]) # df df.set_index(time).sort_index() # X, y create_sequences(df[[wind, temp, vib, speed]], seq_len10, target_col1)这里的逻辑要拆开说。data_arr[i: i seq_len]取的是i到iseq_len-1共seq_len行y取的是iseq_len那一行的目标值。也就是说窗口和预测目标之间没有重叠不会把当前时刻的信息偷给预测目标。边界上总行数减去seq_len就是样本数最后那几行因为凑不够完整窗口被丢弃这符合预期不需要特意补。几个参数值得注意。seq_len选多大取决于你的数据采样频率和预测周期的关系。如果数据是每5分钟一条、要预测未来半小时的状态seq_len至少要能覆盖6个点起步实际工程里我倾向于取预测周期的3到5倍。target_col一定要确认指向目标列很多翻车案例是把第0列当成特征、目标列反而不在窗口里。dtypenp.float32这一步别省LSTM在混合精度或GPU上跑float64会白白增加显存和计算量。2.3 归一化的正确做法只在训练集上fit的MinMaxScalerLSTM对输入尺度极其敏感。它内部的激活函数是tanh和sigmoid输入数值一大梯度直接饱和loss像一条平线。所以归一化不是可选项是必选项。常见做法是MinMaxScaler把数据压到0到1之间from sklearn.preprocessing import MinMaxScaler # 严格按时间切分前80%训练后20%测试 split_idx int(len(df) * 0.8) train_df df.iloc[:split_idx].copy() test_df df.iloc[split_idx:].copy() # scaler只fit训练集 scaler_x MinMaxScaler(feature_range(0, 1)) train_scaled scaler_x.fit_transform(train_df.drop(columns[target])) test_scaled scaler_x.transform(test_df.drop(columns[target])) # 目标列单独做scaler方便反归一化 scaler_y MinMaxScaler(feature_range(0, 1)) train_y_scaled scaler_y.fit_transform(train_df[[target]].to_numpy()) test_y_scaled scaler_y.transform(test_df[[target]].to_numpy())这段代码里有两本文还有配套的精品资源点击获取