
简介基于长短期记忆神经网络模型的钢铁价格预测PDF面向机器学习、深度学习及数据建模方向的学习者与行业分析人员。文档以钢铁价格预测为落地场景系统阐述长短期记忆网络的记忆单元与门控机制说明其相比传统循环神经网络在长序列依赖处理上的优势并给出数据清洗、归一化、模型构建与参数调优的完整流程。文中还采用均方误差对预测效果进行评价并与支持向量回归模型对比突出长短期记忆网络的预测性能。资源为1个PDF文件共798KB内容结构完整、公式与实验描述清晰可直接作为时间序列预测课题的研究参考或论文写作素材。目前已有220人学习下载适合需要系统理解长短期记忆原理及价格预测建模方法的读者。1. 为什么钢铁价格预测绕不开LSTM每个交易日收盘后做黑色系期货或钢材贸易的人都会反复问同一个问题下周价格怎么走。钢铁价格不是白噪声它受铁矿石成本、限产政策、库存曲线和下游开工率共同影响典型特征是趋势段和震荡段交替且单点突变多。用普通回归模型很难把这层序列关系表达出来而LSTM神经网络模型把“最近一段时间怎么演变”当作学习对象在钢铁价格预测这类中等长度时间序列上比传统统计模型更容易出效果。这篇文章按我自己的落地路径讲清楚从序列原理到数据构造再到用Python把LSTM模型跑起来最后给出参数和调优口诀。LSTM并不是唯一的选择但它的训练成本、可解释性和对中等规模数据集的适应度在钢铁价格预测这个场景里非常合适。接下来从门控机制讲起明白内部结构之后调参才不会像猜盲盒。2. 门控机制让LSTM记住价格走势的哪些信息2.1 从RNN到LSTM梯度消失会吃掉价格的历史信息普通RNN在每个时间步把当前输入和上一时刻的隐状态做一次线性变换再过一个tanh。看起来能保留历史信息但反向传播时梯度需要沿着时间步连乘。如果权重矩阵的特征值小于1连乘之后梯度迅速衰减到0网络只能学到最近两三天的影响。钢铁价格决策往往要回头看一个月甚至更久的成本端变化单靠RNN的短期记忆不够用。LSTM在隐状态之外增加一条cell state这条通道上的信息更新由加法和乘法门控组合完成让梯度可以隔代传递。与其对着RNN与LSTM图解发呆不如直接盯住cell state这条累加路径LSTM把长短期记忆分开短期记忆走hidden state长期记忆走cell state。三个门的系数都由当前输入x_t和上一时刻h_{t-1}共同计算。门的输出用sigmoid取值在0到1之间越接近1表示越要保留。2.2 三个门各自负责什么我习惯把LSTM的三个门看成三个独立的水阀遗忘门决定旧记忆还要不要输入门决定新信息写多少输出门决定当前记忆放出去多少。门/状态数学表达式略写在钢铁价格预测中的作用遗忘门f_t sigmoid(W_f · [h_{t-1}, x_t] b_f)决定上一日的库存、基差信息还要不要保留输入门i_t sigmoid(W_i · [h_{t-1}, x_t] b_i)决定今天的涨跌幅能否写入长期状态候选状态\tilde{C}t tanh(W_c · [h{t-1}, x_t] b_c)生成新的候选价格特征输出门o_t sigmoid(W_o · [h_{t-1}, x_t] b_o)决定输出给下一层的价格特征在Keras里写模型不需要手推公式但理解门控对调参有用。比如价格序列里突然出现一根异常大阳线输入门如果打开太大记忆会被突变污染反过来遗忘门长期关闭模型会把一个月前的熊市信息也搬过来。后面调正则化和dropout的时候本质上都是在约束门的激活幅度。2.3 钢铁价格序列为LSTM提供了什么样的学习样本钢铁价格数据很适合被看成时间序列每天一个报价间隔均匀价格之间存在自相关且趋势有持续性。常见做法是取过去N天的价格序列作为输入预测未来M天的价格本质上是把时间序列预测转成监督学习。相比卷积神经网络模型LSTM更擅长捕捉跨天依赖因为卷积网络更多是提取局部形态对时间步之间的顺序信息建模偏弱相比TransformerLSTM在小样本低频金融数据上更容易收敛训练成本也低。钢铁价格一天最多一两个样本几千条数据量对LSTM来说刚好在舒适区。跑一个最小化的手写LSTM前向过程能帮助理解。下面这段代码不用于生产只是把门控展开给你看import numpy as np def lstm_step(x_t, h_prev, c_prev, W, U, b): # x_t: 当前时间步特征形状 (batch, input_dim) # h_prev: 上一个时间步隐状态形状 (batch, hidden_dim) gates np.dot(x_t, W.T) np.dot(h_prev, U.T) b # 四个门的输入来源相同直接切分成四份 f, i, g, o np.split(gates, 4, axis1) f 1 / (1 np.exp(-f)) # 遗忘门 i 1 / (1 np.exp(-i)) # 输入门 g np.tanh(g) # 候选记忆 o 1 / (1 np.exp(-o)) # 输出门 c_next f * c_prev i * g # 更新长期状态 h_next o * np.tanh(c_next) # 生成短期输出 return h_next, c_next这段代码的关键在于c_next的计算前一项f * c_prev是对旧记忆的比例保留后一项i * g是写入新信息。因为是加法梯度从c_next回传时不会只依赖连乘梯度衰减被大大缓解。参数W、U、b在四个门的维度上是四倍关系这也是为什么LSTM参数量约等于普通RNN的四倍。3. 构造钢铁价格预测数据集窗口切分与归一化3.1 用滚动窗口把价格序列变成监督学习数据模型不读原始价格只读“过去N天输入、未来M天输出”的样本。假设DataFrame里有一列close收盘价我一般用切片把过去的序列变成特征。一个常见错误是直接用整段价格训练模型会看到未来信息必须保证每一条样本生成时只用t时刻之前的数据。import pandas as pd import numpy as np def make_samples(close, time_steps20, horizon1): # close是一维价格数组 x, y [], [] for i in range(time_steps, len(close) - horizon 1): x.append(close[i - time_steps:i]) y.append(close[i horizon - 1]) return np.array(x).reshape(-1, time_steps, 1), np.array(y)代码里i从time_steps开始x取i前面的time_steps个价格y取未来第horizon个收盘价。reshape成三维张量维度含义是(样本数, 时间步长, 特征数)。time_steps在钢铁价格上我一般取20对应约一个月的交易日horizon取1表示预测下个交易日的价格。想预测多天就把horizon调大但误差会明显变大。调用示例close df[close].values.astype(np.float64) X, y make_samples(close, time_steps20, horizon1)3.2 归一化与反归一化的正确位置LSTM对输入尺度敏感。钢铁价格绝对值可能在一千到六千之间波动直接喂进去会让梯度过大损失函数很容易变成NaN。常见做法是用MinMaxScaler把价格压缩到0到1之间或者用StandardScaler做标准化。需要特别注意归一化参数只能从训练集上fit然后再transform验证集和测试集绝不能整条序列一起fit否则测试集信息进入训练过程属于数据泄漏。from sklearn.preprocessing import MinMaxScaler close df[close].values.reshape(-1, 1) scaler MinMaxScaler((0, 1)) train_end int(len(close) * 0.7) val_end int(len(close) * 0.85) train_close close[:train_end] val_close close[train_end:val_end] test_close close[val_end:] scaler.fit(train_close) # 只用训练集拟合缩放参数 train_scaled scaler.transform(train_close) val_scaled scaler.transform(val_close) test_scaled scaler.transform(test_close)参数范围(0, 1)可以换成(0.1, 0.9)给网络留一点非线性空间如果用StandardScaler反归一化时也要用同一套mean和scale。反归一化放在计算误差之前否则RMSE会变成一个缩放过的人工指标。3.3 训练集、验证集、测试集的切分纪律时间序列不能像图像分类那样随机打乱。随机打乱会破坏价格的时间依赖让模型偷看未来。我一般按时间顺序切三段前70%训练中间15%验证最后15%测试。验证集用于early stopping测试集最后放出来一次。切分之后需要分别调用make_samples生成样本注意传入一维数据X_train, y_train make_samples(train_scaled[:, 0], time_steps20, horizon1) X_val, y_val make_samples(val_scaled[:, 0], time_steps20, horizon1) X_test, y_test make_samples(test_scaled[:, 0], time_steps20, horizon1)数据集时间段位置用法训练集最早70%更新模型权重验证集紧随训练集15%监控过拟合、保存最优模型测试集最后15%模拟未来只用于最终评估如果原始数据只有2000条去掉前20个时间步和待预测点后样本量仍然足够。对钢铁价格这种低频日线扩充样本的一个常用方式是同时使用多个合约价格或不同周期做多变量输入让模型看到更多截面信息。多变量输入时LSTM输入形状变成(样本数, time_steps, 特征数)第4章代码里会通过X_train.shape[2]自动适配。4. 用Python与Keras搭建LSTM价格预测模型4.1 模型结构堆叠LSTM还是单层钢铁价格序列数据量不大单层LSTM加一个Dense输出往往最稳。堆叠两层可以捕捉更抽象的特征但参数量变大在2000条样本上很容易过拟合。我的基线配置是第一层LSTM配32个单元输入形状为(time_steps, n_features)不返回序列后面接一个units为1的Dense用于回归。如果确实想堆叠就在第一层设return_sequencesTrue第二层LSTM再接Dense。数据量不到一万条时优先从单层开始把调参精力留给time_steps和batch_size。训练过程中要同时观察训练集和验证集的loss曲线。训练开始时两者一起下降后期验证集loss掉到低点后开始回升就是过拟合信号。此时早停已经在起作用我们保存的是val_loss最小的那一组权重而不是最后一个epoch的权重。4.2 训练代码与关键参数下面用TensorFlow自带的tf.keras实现完整训练流程包括早停和模型保存import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint model Sequential() model.add(LSTM(32, activationtanh, input_shape(time_steps, X_train.shape[2]))) model.add(Dense(1)) model.compile(optimizeradam, lossmse, metrics[mae]) early_stop EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) checkpoint ModelCheckpoint(steel_price_lstm.h5, monitorval_loss, save_best_onlyTrue) history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs200, batch_size32, callbacks[early_stop, checkpoint], verbose1 )代码里的LSTM层把样本维度自动识别为(batch, time_steps, features)不需要手动写batch维度。activation默认是tanh不建议改成relu因为门控候选值需要落在[-1,1]之间relu会让记忆状态快速增长。Dense层没有加激活函数保持线性输出回归值。优化器用adam足够SGD需要更多epoch且对学习率更敏感。训练参数的意义如下表参数常用值说明units32/64LSTM记忆单元数量越大拟合能力越强但太大会过拟合batch_size16/32日线数据样本相关性高小batch可以减少梯度噪声但训练变慢epochs200配合early stopping不用人工数epochpatience10连续10个epoch验证集没有改善就停time_steps20对应20个交易日可调为30/60实验4.3 输出反归一化与误差评估训练结束后模型输出的是缩放后的预测值。必须用之前fit好的scaler换回实际价格再和真实价格做差。一个常见错误是拿归一化后的数值直接算RMSE得出一个零点零零几的数看起来非常漂亮但没有业务含义。y_pred_scaled model.predict(X_test) y_pred scaler.inverse_transform(y_pred_scaled.reshape(-1, 1)) y_true scaler.inverse_transform(y_test.reshape(-1, 1)) rmse np.sqrt(np.mean((y_pred - y_true) ** 2)) mae np.mean(np.abs(y_pred - y_true)) print(fRMSE: {rmse:.2f}, MAE: {mae:.2f})如果当前价格在每吨4000元左右RMSE做到80以内可以在方向上参考做到50以内算不错。这里要注意inverse_transform接收的形状需要和fit时一致所以做了一次reshape。MAE比RMSE更直观遇到个别异常价格时不会被过分放大。预测未来时要把最近time_steps个真实价格先缩放再输入模型最后把预测结果反归一化。5. 预测滞后怎么解三个见效快的调优手法5.1 用多步滚动预测替代单步伪拟合LSTM单步预测很容易出现上一根收盘价主导输出现象预测曲线看起来与真实值贴合其实是把昨天价格平移了一天。解决方法是做滚动多步预测先用最近time_steps个点预测下一天把这个预测值追加到输入序列尾部再预测再下一天反复迭代。def dynamic_predict(model, current, steps, scaler, time_steps20): result [] current current.copy() for _ in range(steps): # 先变成二维列向量缩放后再reshape成模型输入形状 cur_2d current.reshape(-1, 1) cur_scaled scaler.transform(cur_2d).reshape(1, time_steps, 1) pred_scaled model.predict(cur_scaled, verbose0) pred scaler.inverse_transform(pred_scaled)[0, 0] result.append(pred) # 更新输入窗口去掉最旧的把新预测接在末尾 current np.roll(current, -1) current[-1] pred return np.array(result)滚动预测会暴露模型长期偏差。每步预测都会吃进上一步误差如果模型真实预测能力差滚动几步就发散。因此这个函数不仅用来预测未来7天也常用在验证集上判断模型稳定性。5.2 对预测残差做二次校正LSTM单模型会有系统性偏置例如在趋势末端总是慢半拍。可以把训练集上预测值和真实值的残差用简单回归再拟合一次让LSTM专注于非线性部分线性趋势交给校正模型。我一般取最近500个残差用AR(3)或者梯度提升树补一个增量。最终预测等于原始LSTM预测加残差模型预测。这个手法对价格均值回归阶段尤其有效能明显降低MAE。5.3 集成多个LSTM权重做鲁棒输出最后一个办法是代价低、收益稳的做法用相同训练数据跑3到5个不同随机种子或不同time_steps的LSTM保存权重后对预测值取平均或加权平均。权重可以用验证集RMSE的倒数RMSE小的模型发言权更大。# rmse_list是每个模型在验证集上的RMSE weights 1.0 / np.array(rmse_list) weights / weights.sum() ensemble_pred np.sum(pred_matrix * weights.reshape(-1, 1), axis0)集成不能靠单模型特征的微小改动撑起多样性至少要保证time_steps、units或dropout有明显差异。比如一个模型用time_steps20另一个用30另一个用60集成后才可能吸收不同记忆长度的判断。本文还有配套的精品资源点击获取