ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

LSTM与注意力机制融合:从上证指数看金融时序预测精度提升

LSTM与注意力机制融合:从上证指数看金融时序预测精度提升 简介深度学习在金融时间序列预测中的应用研究资料以PDF形式呈现面向金融数据分析、量化研究及人工智能交叉领域的读者也适合作为相关课题的参考文献与专业指导。内容围绕上证指数预测场景系统讲解LSTM长短期记忆网络的结构与门控机制并引入注意力机制构建AM-LSTM模型通过RMSE与MAE指标对比两种方法的预测精度给出数学公式推导与实验设计思路。压缩包内含1个PDF文件大小1.55MB属于单篇学术文献便于查阅与存档已有391人学习下载。从实际价值看读者可获得深度学习时序建模的完整方法论理解注意力机制如何改进LSTM在非平稳、非线性金融数据上的表现同时回顾了ARIMA、SVM、MLP等常见方法的局限与对比对金融产品波动性、价格预测及模型选型具有直接参考意义。预览部分还展示了LSTM门控公式与AM权重计算细节可帮助研究者快速掌握模型内部机制是一份适合入门与进阶的精炼文献。1. 金融时序预测为什么绕不开深度学习从上证指数十年数据说起金融时序预测这件事入门时大多数人会直接想到ARIMA但上证指数这种非平稳、带噪声、隔三差五出现跳空的数据传统统计模型很难讨到便宜。这篇论文做了一件很干净的事用2010到2019年上证指数2432个交易日的开盘、收盘、最高、最低和成交量五项数据分别训练LSTM与引入注意力机制的AM-LSTM做收盘价预测在序列长度为70的条件下AM-LSTM的均方根误差是0.16102LSTM是0.27305精度提升了约四成。这份资源对想做深度学习方向和金融时序预测研究的人有用核心是三件事一套完整的数据预处理规范两个可以照抄的模型结构设计以及一组经得起复现的对比数据。论文末尾还整理了从LSTM原始文献到近年金融市场预测的参考文献可以直接拿来做文献综述的起点。2. LSTM的门控机制拆解三组公式与金融数据的适配逻辑论文第2节把LSTM的三个门和候选值公式都列了出来看起来很吓人但拆开看就是三句话产生门控信号、结合门控信号更新记忆、按门控信号读出记忆。为什么金融时序预测里LSTM能压过MLP、SVM、GARCH这些经典方法因为金融数据本质上是非平稳、非线性的统计模型一般假设线性关系或同方差LSTM不需要做这些假设它是直接从数据里学门控规律。2.1 三个门的公式输入门、遗忘门、输出门各自控制什么输入门的输出 (i_t \sigma(W_i x_t U_i h_{t-1} b_i))它表示当前时刻的输入 (x_t) 和上一时刻隐藏状态 (h_{t-1}) 经过线性变换后被sigmoid压缩到0到1之间的一个值。这个值代表“当前信息值得被记住的比例”0表示全部丢弃1表示全部写入单元状态。遗忘门 (f_t \sigma(W_f x_t U_f h_{t-1} b_f)) 控制的是上一时刻的单元状态 (c_{t-1}) 有多少能存活到现在。金融场景里遗忘门学到的往往是“让我忘掉昨天那种无意义的横向小幅波动让我记住上周那个放量突破”。输出门 (o_t \sigma(W_o x_t U_o h_{t-1} b_o)) 决定从当前单元状态中读取多少信息给隐藏层隐藏层 (h_t o_t \odot \tanh(c_t))。候选值 (\tilde{c}t \tanh(W_c x_t U_c h{t-1} b_c)) 是真正要被写进单元状态的新内容tanh把中间结果映射到-1到1让记忆内容可以有方向性——涨和跌在这个空间里就是正负号的区别。落到代码上用Keras搭建一个两层LSTM回归网络参数和论文公式是一一对应的from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense model Sequential() # input_shape(滑窗长度70, 特征数5) model.add(LSTM(units64, input_shape(70, 5), activationtanh, # 对应候选值公式里的tanh recurrent_activationsigmoid, # 对应三个门公式里的sigma return_sequencesTrue)) # 输出所有时间步的隐藏状态 model.add(LSTM(units32, return_sequencesFalse)) model.add(Dense(1)) # 输出归一化后的收盘价 model.compile(optimizeradam, lossmse)代码逻辑说明input_shape里的70表示每次喂入70个交易日的序列5表示五项输入指标。units是隐层神经元数量对应LSTM内部的记忆容量第一层64、第二层32是常见配置。activationtanh对应候选值计算中的tanh激活recurrent_activationsigmoid对应门控计算里的σ激活函数这两个参数不要随意改动改动后就不是标准LSTM的行为模式了。return_sequencesTrue让第一层输出每个时间步的隐藏状态序列第二层才把序列压缩成单个向量。参数说明units太小模型记不住长期依赖太大训练变慢且容易过拟合一般从32到128这个区间去试。优化器选Adam、损失函数用均方差这两条和论文实验设置保持一致。2.2 单元状态的更新路径为什么遗忘门能解决梯度消失单元状态的更新公式 (c_t f_t \odot c_{t-1} i_t \odot \tilde{c}t) 是整篇论文公式里最值得盯住的一条。上一时刻的 (c{t-1}) 乘以遗忘门后直接加到当前 (c_t)中间没有经过任何非线性压缩所以反向传播时梯度可以沿着这条通路长距离回传。RNN之所以在长序列上梯度消失是因为每一步都经过一个tanh压缩连续乘几步梯度就趋近于0前面的信息根本传不到后面。LSTM有了这条线性通路信息才能在几十个时间步之间流动。理解这一条对金融时序的意义很直接上证指数的趋势不是靠单一交易日决定的而是几十个交易日的合力LSTM能跨过短期波动去捕捉中期趋势靠的就是这条“copy线路”。2.3 从RNN到LSTM为什么长序列预测离不开门控20世纪90年代初学者们就开始尝试用神经网络做时间序列预测最早用的是RNN。RNN把整个历史压缩成一个隐藏状态向量序列一长前面的信息在反复的非线性压缩中就丢了。1997年Hochreiter和Schmidhuber提出LSTM核心贡献就是引入门控机制和单元状态这条旁路。21世纪以后算力上来了LSTM的训练速度和效果才真正达到可用水平。在金融时序的实证对比里欧阳红兵等人用LSTM、MLP、SVM、K近邻、GARCH五种方法预测道琼斯工业指数日收盘价LSTM效果最佳李洁等人用ARIMA、GARCH、SVM、LSTM、EMD-LPP-BPNN等多种方法预测四种指数LSTM同样占优。这一组对比说明LSTM不是“另一种可选模型”而是这类数据下最稳的基线。你想在论文基础上做改进先跑通LSTM基线是必要前提。3. 注意力机制融合LSTM的实操路径权重计算与模型结构改动LSTM解决了长期依赖但它有一个问题它把每个时间步的信息一视同仁地压缩进最后的隐藏状态。这就像一个人把过去70个交易日的每一天都背得一样熟但在做预测时被某天的小阳线干扰忽略了关键的趋势转折点。注意力机制就是来纠正这件事的。3.1 注意力机制要解决什么长序列中的信息过载论文里对引入注意力的解释是金融时序预测需要“过去时序中的重要信息”模型要学习不同时间点对预测结果的关键程度。2014年Bahdanau等人先把注意力机制用到机器翻译领域2017年Cinar等人把注意力与RNN结合做单变量和多变量时序预测。在金融预测场景里注意力机制天然适配有两个理由一是行情数据噪声比例高需要用权重做软性去噪二是趋势转折点对后续走势的影响比其他日期大得多注意力机制给了模型一个显式的工具去强调这些关键点。换句话说AM-LSTM不是把注意力机制当作装饰而是让模型自己学会“哪些天的信息更值钱”。3.2 权重因子与上下文向量一段可以复用的计算流程按论文公式走一遍完整的注意力计算流程。第一步对每个历史时刻 (t)计算当前预测时刻对它的重要程度分数 (e_t \tanh(V^T(W h_t U h_{j-1})))。这里的 (W) 是编码器状态的参数矩阵(U) 是解码器隐状态的参数矩阵(V^T) 是注意力参数矩阵(h_t) 是LSTM在 (t) 时刻的隐藏层输出。第二步对所有历史时刻的 (e_t) 做softmax归一化得到权重因子 (a_t)softmax保证所有权重之和为1。第三步把每个时刻的隐藏层状态按权重加权求和得到总权重因子 (E_j \sum_{t} a_t h_t)这个上下文向量就是模型从历史中提纯出来的关键信息。第四步把 (E_j) 与LSTM状态结合通过输出层得到预测值 (y_j)。用TensorFlow实现一个时间步注意力层是复现AM-LSTM的标准做法import tensorflow as tf from tensorflow.keras.layers import Layer class TemporalAttention(Layer): def __init__(self, units): super().__init__() self.W tf.keras.layers.Dense(units) # 编码器状态变换 self.U tf.keras.layers.Dense(units) # 解码器隐状态变换 self.V tf.keras.layers.Dense(1) # 输出标量score def call(self, encoder_outputs, decoder_state): # encoder_outputs: (batch, seq_len, units) # decoder_state: (batch, units) score self.V(tf.nn.tanh( self.W(encoder_outputs) self.U(decoder_state)[:, tf.newaxis, :])) attn_weights tf.nn.softmax(score, axis1) # (batch, seq_len, 1) context tf.reduce_sum(attn_weights * encoder_outputs, axis1) return context, attn_weights代码逻辑说明encoder_outputs是LSTM各时间步的隐藏状态矩阵形状为(batch, seq_len, units)。self.W把每个时间步的隐藏状态投影到注意力空间self.U把当前解码器状态也投影过去加一个维度后广播到每个时间步。两者相加经过tanh后用self.V压缩成标量scoresoftmax沿时间步维度归一化得到每个历史时刻的权重。tf.reduce_sum(attn_weights * encoder_outputs, axis1)得到加权求和后的上下文向量。参数说明units是注意力投影维度一般与LSTM隐层维度保持一致比如64或128。投影维度太小注意力分数区分度不够太大则参数量增加但收益不明显。在Keras里调用这个层时需要把LSTM的两个输出都传进来context, attn TemporalAttention(64)(encoder_outputs, decoder_state)再把context拼接到解码器输入上。3.3 对比结果解读注意力机制在短序列上收益更大论文的核心实验结论可以直接从两组数据里读出来。序列长度70时AM-LSTM的RMSE是0.16102LSTM是0.27305相对提升约41%序列长度50时AM-LSTM是0.24492LSTM是0.31379序列长度30时AM-LSTM是0.33941LSTM是0.51562相对提升约34%。MAE数据与此一致长度70时从0.22105降到0.11932接近腰斩。这里有一个值得注意的规律序列越短噪声占比越高注意力机制去噪带来的相对收益越明显。所以如果你想在自己负责的数据集上复现优先对比AM-LSTM而不是只做LSTM——注意力机制在金融时序里不是锦上添花而是真正把权重用在了刀刃上。4. 上证指数2432天数据预处理全流程从线性插补到滑窗切分金融时序预测的成败数据预处理占一半。论文在数据上花了不小的篇幅讲缺失值处理和归一化这两步看起来基础但顺序错了、方法错了后面模型再高级都白搭。4.1 数据集的基本盘2010到2019年上证指数日线数据论文使用上证指数2010-01-04到2019-12-31的日线数据剔除节假日和无效数据后共2432个交易日。输入特征是五项开盘价、收盘价、最高价、最低价、成交量。按3:1比例切分1824天做训练集608天做测试集。这里提醒一个容易忽略的细节原始数据下载下来一般有date、open、close、high、low、volume六列数据来源用tushare、akshare或者Yahoo Finance的CSV都可以关键是字段要对齐、日期要用北京时间且按升序排列避免切分后出现日期错位。模型不关心数据来源但关心列的顺序和索引这直接影响到后面滑窗生成样本时取哪一列当预测目标。4.2 缺失值处理线性插补为什么比删除和均值填充都靠谱论文专门指出时间序列数据中缺失项不能轻易删除。原因很直接时间序列是上证指数在过去不同时刻产生的数据删除缺失数据会导致数据结构不完整无法反映市场在一定时间内的变化情况。实际遇到的场景是某一天停牌或者数据源漏了记录——比如1月10日和1月14日有数据1月13日停牌直接删行会让模型认为10日后面紧跟着14日这在金融逻辑上是错误的跳跃。线性插补的做法是取缺失日期的前后两个有效值按时间位置比例线性内插。用pandas实现import pandas as pd # 读入数据后先按日期排序 df pd.read_csv(shanghai_index.csv, parse_dates[date], index_coldate) df df.sort_index() # 时间序列缺失值用线性插补不能用均值填充更不能直接删行 df df.interpolate(methodlinear, limit_directionboth) df df.dropna()代码逻辑说明df.interpolate(methodlinear)会按索引顺序对每一列的NaN做线性插补取缺失位置前后两个有效值连成的直线上的中间点。limit_directionboth是为了处理序列头部和尾部的缺失如果只用默认的forward头部连续缺失会被保留为NaN。后面的dropna()是兜底防止头部或尾部实在插补不出来时残留空值影响训练。参数说明methodlinear适合价格类数据价格在相邻交易日之间通常近似连续变化。如果换成成交量这类有周期波动的字段可以试methodspline但要注意样条插补在缺口处可能产生负值成交量出现负值时要手动矫正。4.3 归一化与切分顺序这一步错了一切都白搭归一化公式是论文里的min-max公式把原始值压缩到0到1区间。关键不是公式而是执行顺序。正确的顺序是先按时间切分再fit训练集然后transform训练集和测试集。如果在全量数据上做了归一化再切分训练过程已经看到了测试集的min和max属于典型的数据泄露。论文里虽然没强调这一步但复现时必须注意否则测试集效果会虚高。import numpy as np from sklearn.preprocessing import MinMaxScaler # 按论文列序排列open, close, high, low, volume cols [open, close, high, low, volume] train_size int(len(df) * 0.75) # 1824天 train_raw df.iloc[:train_size] test_raw df.iloc[train_size:] scaler MinMaxScaler(feature_range(0, 1)) train_scaled scaler.fit_transform(train_raw[cols]) # 只用训练集拟合min/max test_scaled scaler.transform(test_raw[cols]) # 测试集复用同一套min/max # 滑窗生成输入输出对 def make_sequences(data, window70): X, y [], [] for i in range(window, len(data)): X.append(data[i-window:i, :]) # 过去window天的五项指标 y.append(data[i, 1]) # 当前天的收盘价close列索引是1 return np.array(X), np.array(y) X_train, y_train make_sequences(train_scaled, 70) X_test, y_test make_sequences(test_scaled, 70)代码逻辑说明train_raw只取前1824行test_raw是后608行切分严格按照时间顺序不做任何shuffle。cols列表的顺序决定了缩放后数组的列索引open是0、close是1、high是2、low是3、volume是4所以预测目标收盘价取索引1。make_sequences的循环从第window行开始取窗口保证每个样本都有完整的70天上下文X_train的形状是(样本数, 70, 5)。参数说明feature_range(0, 1)对应论文的归一化公式window这一参数在论文里对比了70、50、30三组最优是70。不同标的的最优窗口不同后面第6章会展开说怎么扫这个参数。训练完预测时别忘了用scaler.inverse_transform把预测结果还原成真实价格再计算RMSE和MAE这样算出来的指标在业务上才直观。5. 复现避坑指南五个翻车点与补救方案这个章节是复现论文时最容易踩坑的地方。有些问题不解决模型结构再对结果也对不上论文。5.1 缺失数据直接删行测试集误差莫名翻倍现象用dropna删掉缺失日期后训练损失表现还行但测试集RMSE比论文基准高出一大截尤其在停牌密集的月份附近预测连续偏航。原因删除行之后相邻两个交易日的时间间隔不再一致模型被迫学习“跳过一天直接预测”的错误映射。金融序列的间隔是交易结构的一部分删行破坏了这种结构。解决用4.2节的线性插补先补齐再检查是否还有NaN残留。补充一点如果某段数据缺口特别长比如连续停牌超过一周线性插补会失真这种极端情况建议直接把该段数据裁剪掉不参与训练但要记录裁剪的时间区间避免测试集错位。5.2 先归一化再切分典型的数据泄露测试分数虚高现象训练和测试表现都好得离谱但换成新数据做真实预测时立刻露馅。原因全量数据fit出的min和max已经包含了测试集的取值范围模型在训练时间接接触了测试集的信息边界。解决严格按“先切分、再fit训练集、再transform两边”的顺序来。我的习惯是在代码里加一个断言检查train_scaled和test_scaled使用的scaler是否为同一个对象且scaler.data_min_来自训练集。5.3 滑窗长度抄别人的在上证指数上70好换数据不一定现象原封不动照搬长度70换到另一支股票或指数时RMSE比论文高很多。原因不同标的的有效记忆长度不同。上证指数是综合指数走势相对平滑长期趋势信息占比高如果是波动率大的创业板指数过长的窗口反而引入了大量不相关的短线噪声。解决把序列长度当超参数跑一个30到90、步长10的网格搜索用验证集RMSE选最优值。论文里70最优、50次之、30最差只说明上证指数这个样本下的规律不代表所有金融时序数据的通用结论。5.4 迭代次数6次不理解直接照搬或盲目加大都会翻车现象有人觉得6次太少改成50次训练loss下降不少测试RMSE反而上升。原因金融时序噪声占比高模型迭代多轮后进入过拟合区开始记忆噪声里的随机模式。解决用早停每轮训练完看验证loss连续3轮不下降就提前终止。论文里设6次是因为它的模型结构简单、数据量小6轮已经收敛你的数据量级如果不同收敛轮数自然不同。把6次当成“论文环境下的经验值”而不是通用参数。5.5 TensorFlow版本兼容论文是1.1.5你装的是2.x现象网上找到的旧代码跑起来直接报错tf.placeholder、tf.nn.rnn_cell这些API在TF 2.x里已经全部移除AttributeError一个接一个。原因TensorFlow 1.x是静态图编程模型2.x默认动态图核心API做了重构。解决复现论文思路不需要死守1.x版本用TF 2.x的tensorflow.keras.layers.LSTM重写即可第2章给出的代码就是TF 2.x的写法。如果你确实要跑论文原始代码建议单独建一个python3.7虚拟环境装tensorflow 1.1.5与主环境隔离不要混用。6. 序列长度与评价指标的调优把RMSE从0.27压到0.16论文最后给出的三组对比数据其实把调优思路写透了。复现这篇论文最值钱的不是某个模型代码而是这组结果背后的规律。6.1 两组核心实验数据表1展示的是RMSE均方根误差对比序列长度越长两个模型的表现都越好模型序列长度70序列长度50序列长度30LSTM0.273050.313790.51562AM-LSTM0.161020.244920.33941表2展示的是MAE平均绝对误差对比趋势与RMSE完全一致但数值整体更低模型序列长度70序列长度50序列长度30LSTM0.221050.289820.41347AM-LSTM0.119320.222030.316296.2 怎么读这张表第一序列长度越长两个模型都越准。这说明上证指数的可预测信息不止分布在最近几天里滑窗70比滑窗30更接近趋势的完整周期。第二AM-LSTM在长度30那组相对优势最大RMSE从0.51562降到0.33941降幅约34%说明序列越短噪声占比越高注意力机制的去噪收益越明显。第三两种指标要结合看MAE比RMSE小是正常的因为RMSE对大误差更敏感。如果RMSE明显大于MAE说明预测中存在少数离群误差往往是跳空跌停这类极端行情日造成的如果两者接近说明误差分布均匀模型没有明显的偏航。6.3 复现时的调参建议序列长度跑滑窗搜索用验证集定版不要直接抄70。归一化的min和max只来自训练集测试集只用transform常写代码的人都知道这一步错不得。训练轮数用早停代替固定epoch验证loss连续3轮不降就停。预测完必须把结果用scaler.inverse_transform还原成真实价格再计算RMSE和MAE。我在复现时加了一个习惯把预测值和真实值按日期画在一张图上用眼睛扫一遍比任何指标都直观——指标只能给你一个数字图能告诉你模型在哪些行情阶段系统性失误。写完这段说个我自己的教训第一次复现这类论文时报错最多的地方不是模型结构而是数据预处理阶段。从那以后我做任何时间序列项目都强制走一遍“先切分、再归一化、再做滑窗”的顺序迭代轮数一律用早停代替拍脑袋。LSTM给深度学习发了张进入金融时序预测的入场券注意力机制才真正把精度从“能看”拉到“能用”。这两者的组合值不值得下载研究开头那组0.16对0.27的数字其实已经给出了答案。希望帮到你。本文还有配套的精品资源点击获取
返回列表