简介:一篇题为《基于机器学习的股票预测方法研究》的论文,围绕股市预测这一复杂任务,系统梳理了从传统机器学习到深度学习的演进路线,并重点介绍结合金融新闻、股民评论等非结构化文本的预测思路。文中覆盖分段线性表示、高斯过程分类、随机森林、支持向量机、卷积神经网络、双重注意力机制及事件结构化表示等关键技术,适合机器学习初学者、算法工程师及正在开展毕业设计或课程设计的高校师生参考,可作为课题研究与论文写作的框架范本。压缩包内仅含1个docx文档,大小约35KB,内容为完整论文正文与摘要,结构规范、逻辑紧密。目前已有586人学习/下载。读者可以从中掌握股票预测实验的设计方法、多源数据整合策略以及模型优缺点的分析思路,为同类研究提供直接借鉴。
1. 股票预测不是玄学:这份综述把机器学习的两条技术路线讲透了
股票预测在我接触过的所有机器学习课题里,属于典型的“看着简单、跑起来全是坑”的方向。数据好拿,模型好写,但预测结果每次都不一样,回测曲线和实盘曲线经常一个天上一个地下。这份《基于机器学习的股票预测方法研究》论文参考,本质上不是教你调参,而是把这一领域 2014 到 2019 年间最有代表性的 16 篇工作梳理成了两条清晰的技术路线——纯历史数据驱动,以及历史数据加金融新闻文本的多源驱动。对那些正在做毕业设计或课程设计的人,它最值钱的不是结论,而是给你一张“这个领域已经有人做到什么程度、主流方法各自边界在哪”的地图。读完之后,你至少不会再犯“拿一个 LSTM 就去预测全部股票”这种外行错误。
2. 纯历史数据路线:从 PLR 转折点预测到 GAN 生成收盘价,传统模型和深度模型的边界在哪
这一章对应论文的第 1 部分。它讲的是只用历史股票数据做预测的那一类工作,这类工作最大的特点是输入干净、处理简单,但预测精度天花板明显。
2.1 传统机器学习方法:PLR 分段线性表示是这套路线的灵魂
论文里提到 Tang C、李丰等人使用分段线性表示(PLR)来确定历史数据的波谷与波峰,再配合高斯过程分类或随机森林去预测价格转折点。PLR 被反复使用不是偶然,它能把一段连续的价格曲线压缩成若干个直线段,每一段保留斜率变化信息,这样一来模型的输入就不再是几百个价格点,而是几十个“转折段”,特征维度大幅下降,训练速度也快。
我一般在复现这类方法时会把它拆成三步:
- 对收盘价序列做 PLR 分段,得到转折点集合;
- 以转折点左右两侧的统计特征(窗口内均值、方差、斜率)构造特征向量;
- 用高斯过程分类或随机森林对“下一时刻是上升还是下降”做二分类。
文献[3]的做法更进了一步:用遗传算法去优化时间窗口的取值,然后交给支持向量机去决策买入、持有还是卖出。这个方法给了我一个很重要的启发——时间窗口不该是拍脑袋定死的,它是一个可以参与优化的超参数。这也是后面我在第 4 章要展开的避坑点。
这类传统模型的优缺点在论文里写得很直白:优点是只用结构化数据,预处理简单,运行速度快;缺点是无法解释不同公司股价之间的潜在动态关系,而且股票数据是带时间属性的,线性统计模型没法捕获时序特征。用大白话说,传统模型等于把股价当静态数据看,赚钱是运气,亏钱是常态。
2.2 深度学习方法:CNN 提短期特征、PLR 提长期特征、GAN 做生成
论文的 1.2 节介绍了两种深度学习的典型用法。
CHEN Y 等人提出的思路是:用卷积神经网络(CNN)提取短期特征,同时用 PLR 对时间序列分割以提取长期特征,最后用双重注意力机制把长短期特征做加权融合。这种设计的巧妙之处在于它承认了一件事——股价的短期波动和长期趋势是由不同因素驱动的,短期看资金博弈,长期看基本面变化,单一模型要同时兼顾两者,不如让两个特征提取器各管一段,再用注意力机制决定当下谁更重要。
ZHANG K 等人则把生成对抗网络引入了股票预测,用多层感知机(MLP)当判别器,长短期记忆网络(LSTM)当生成器来预测收盘价。这是一个很有想象力但也很有争议的做法。GAN 本来是用来生成图像的,训练目标是让判别器分不清真假,但股票预测的目标是数值逼近,生成器和判别器的对抗未必能收敛到理想的预测效果。论文里也提到这个方法只和基准做了对比,没有做深入改进,属于“思路值得学、直接拿来用要谨慎”的类型。
我把这两篇文献放在一起读的时候,产生了一个判断:深度学习方法的真正优势不在于“用哪个网络”,而在于它能把时序特征显式建模。LSTM 的门控机制天然适合价格序列,CNN 的卷积核则擅长捕捉局部形态,这两种能力都是传统 SVM 和随机森林不具备的。
2.3 传统模型和深度模型的选型边界
这一节我想给一个能直接用于方案选型的判断框架,因为论文里综述了很多文献,但没告诉你哪种情况该选哪条路。
| 判断维度 | 传统机器学习(SVM / 随机森林 / PLR+高斯过程) | 深度学习(LSTM / CNN / GAN) |
|---|---|---|
| 数据量要求 | 几百到几千条即可 | 通常需要几万条以上,否则容易过拟合 |
| 时序特征建模 | 基本不具备,靠人工构造滞后特征 | 原生支持,门控结构 / 卷积核天然处理序列 |
| 训练速度 | 分钟级 | 小时级,且需要 GPU |
| 可解释性 | 特征重要性可查,规则明确 | 黑匣子,注意力权重只能算弱解释 |
| 适用场景 | 做转折点判断、涨跌分类 | 做收盘价回归、多步滚动预测 |
我自己选型的习惯是:如果训练数据只有一两年的日线数据(约 250 到 500 条),优先用传统模型;如果要预测的标的是流动性较好的股票且有五年以上的历史数据,再考虑 LSTM。数据量是硬门槛,模型再先进也补不回来。
3. 让新闻文本参与预测:事件结构化、情感极性与文本向量的三条实现路径
这是整份论文信息量最大的一部分,对应论文的第 2 部分。它讲的不是“要不要用新闻”,而是“怎么把新闻变成模型吃得下的特征”。论文按特征提取方式把相关工作分成了三类,我逐一展开。
3.1 新闻事件的结构化表示:四元组是文本与股票数据对齐的钥匙
Xiao Ding 等人提出用结构化的四元组 E = (O1, P, O2, T) 表示新闻事件,其中 O1 是参与者,P 是行动,O2 是动作对象,T 是时间戳。以“苹果公司发布新款 iPhone”这条新闻为例,结构化之后就是(苹果公司,发布,新款 iPhone,2024-09-10)。有了这种表示,新闻就不再是一段文字,而是一条带时间戳的、和股票数据维度对齐的记录。
这个方法的价值在于解决了文本预测中最大的一个工程难题——对齐。股价数据是分钟级甚至秒级的,新闻是离散的,如果不对齐,模型根本不知道“这条新闻影响了哪一天的股价”。四元组把事件打上时间戳之后,就可以按时间窗口把同一时间段内的事件向量和价格特征拼在一起,作为模型的输入。
在实践中我一般会这样做:先用命名实体识别把新闻里的公司名、人名抽出来,再用依存句法分析提取主谓宾关系,最后对照一个预设的金融动词表(收购、减持、起诉、中标等)把动作标准化。整个过程可以用现成的中文 NLP 工具完成,最终输出就是一张事件表,每一行是一个四元组。
3.2 新闻的情感极性分析:词典法为基础的“利好利空”判断
这一类方法的思路更直观——先建一个金融情感词典,然后对新闻做情感打分,最后结合情感得分、历史股价和相邻日收盘价的方差来预测涨跌。论文里把 Dev Shah 等人的工作归到了这一类,Hegde 等人还在此基础上引入了 LSTM 来捕获时间序列信息,同时加了一个基于 CNN 分类器的推荐模块,用背包投资模型来做投资组合优化。
情感极性分析看起来比事件结构化简单,但里面藏着几个很实际的坑:
第一,通用情感词典在金融场景下会翻车。“下跌”“减持”这类词在通用词典里是中性甚至偏正向的,但在金融语境里是明确利空,所以必须用金融语料重新构建领域词典。第二,光看句子级情感不够,还要看情感主体的指向。一篇新闻说“A 公司起诉 B 公司”,对 A 是利好,对 B 是利空,如果只算整体情感极性,这条信息就被浪费了。第三,情感得分和股价涨跌的关系不是线性的,很多时候“利好落地就是利空”,模型需要考虑情感变化的幅度和历史价格的相对位置。
3.3 文本向量直接表示加深度学习:维度失衡是最大的工程陷阱
Ryo Akita 等人的方法代表了一条更“省事”但也更容易翻车的路线:把每一篇新闻文档用固定长度的向量表示,然后和标准化处理后的股票价格拼接,一起送入 LSTM。这样做的好处是端到端,不需要手工设计事件结构或情感词典;坏处是文本向量维度远大于价格数值的维度,LSTM 会偏向文本特征,导致预测精度严重失衡。
论文里提到的解决方案是对向量做缩放,让文本向量和价格向量维度一致。这个细节在任何一个复现项目里都是关键。我复现这类结构时通常的做法是:先用 Word2Vec 训练一个 64 维的词向量模型,把所有新闻标题向量化并拼接成文档向量,然后对文档向量做 PCA 降维到 32 维,同时对价格特征做标准化,最后在拼接层之前再各过一层全连接,把两路特征的维度对齐。
第三类方法里还有一个值得记住的细节:Che-Yu Lee 等人的模型把短期窗口内的所有事件向量做平均池化,得到一个“事件袋”表示,再和价格序列一起送入模型。这种做法的逻辑是,单条新闻的影响是有噪声的,但一组新闻的平均情绪倾向是有统计意义的。
4. 避坑与常见问题:为什么模型预测结果每次都不一样
这部分是我自己想加的一章,因为论文里综述的很多方法,在实际复现时几乎都会撞到同一堵墙——结果不可复现、验证集上表现好、换一段时间就失效。我整理了五条最典型的踩坑记录。
4.1 现象:同样的代码跑两次,预测结果差异很大
原因:深度学习模型初始化权重是随机的,训练过程中的 Dropout 也会引入随机性,如果不固定随机种子,两次训练几乎不可能得到完全一致的结果。
解决:统一固定全局随机种子,三个地方都要固定,Python 的 random 模块、NumPy 的随机数生成器、PyTorch 或 TensorFlow 的种子。我习惯把种子值设置为 42,并在训练脚本开头集中写入这三行。很多初学者只固定了模型部分的种子,忽略了数据加载时的随机打乱逻辑,导致结果依然漂移。
4.2 现象:文本特征维度远大于价格特征维度,预测几乎只看新闻不看价格
原因:拼接输入时,新闻向量如果是 256 维,价格向量只有 5 维,LSTM 会认为文本信息更重要,梯度更新也主要受文本分支影响,价格信息被淹没。
解决:论文里 Ryo Akita 提出的“向量缩放”是直接有效的方案。我的一般做法是对文本向量做降维(PCA 降到 32 维),对价格特征构造更多衍生特征(过去 5 日收益率、波动率、成交量变化率),让两条分支的输出维度都在同一个量级,然后再拼接。
4.3 现象:训练集上准确率 90%,验证集上 60%,实盘更差
原因:时间序列数据存在严重的自相关性,相邻日期的数据几乎相同。如果直接按 sklearn 的 train_test_split 默认参数随机切分,训练集和验证集里会出现大量“同时间段”的数据,相当于让模型在考试时见到了答案。
解决:使用按时间顺序切分。我用的是手工切分方式——按日期排序后,前 80% 的时间区间做训练,中间 10% 做验证,最后 10% 做测试。滚动窗口回测的具体做法在第 6 章展开。
4.4 现象:CNN 提取时序特征时表现不稳定,换一段股票数据就差很多
原因:文献[1]和文献[2]都提到了时间窗口固定带来的精度问题。CNN 只能看到窗口内的局部特征,窗口设置过短会丢失趋势信息,窗口过长又混入大量与当前涨跌无关的历史噪音。
解决:把窗口长度当作超参数参与调优,而不是拍脑袋定死。文献[3]用遗传算法做优化,我一般用网格搜索,对窗口长度在 5 到 60 天之间按步长 5 遍历,对比验证集上的表现来选最优值。
4.5 现象:模型预测“涨”和“跌”的分类准确率不错,但收益曲线仍然亏钱
原因:二分类问题只看方向,不看幅度。预测“明天涨”是对的,但只涨了 0.1%,而交易手续费和滑点已经吃掉了这部分收益。模型把“涨”和“大涨”混为一谈。
解决:把二分类改造成三分类或回归——上涨超过 1% 定义为“大涨”,下跌超过 1% 定义为“大跌”,中间为“持平”,或者直接用回归目标预测收益率的具体数值。这个改动会让模型从“预测方向”升级为“预测幅度”,更贴近交易决策的实操需求。
5. 把综述变成毕设/课设:文献脉络、实验设计与最小可复现路径
论文之所以适合当毕业设计和课程设计的参考,是因为它的文献脉络足够清晰。这一章我讲三件事:怎么写文献综述、怎么设计实验、怎么快速搭出一个最小可复现的基线系统。
5.1 文献综述怎么蒸:从 16 篇参考文献里提炼演进脉络
论文末尾给出了 16 篇参考文献,时间跨度从 2014 到 2019,基本覆盖了机器学习股票预测的发展主线。我不建议按时间轴平铺直叙地罗列文献,那样答辩时老师会觉得你没有消化。
更好的组织方式是按“技术方法”分块——这也是论文本身采用的结构。你可以把文献分成三组:传统机器学习方法、深度学习方法、文本加历史数据融合方法。每一组内部再按“要解决的问题、提出的方法、方法的局限”三个角度去组织段落。当老师问“你为什么选 LSTM 而不是随机森林”,你要能回答出文献[4]和文献[5]各自的边界。
5.2 实验设计:数据、特征、模型、评估四件套怎么配
毕设论文答辩时,评审最看重的是“你的实验是不是完整闭环”。我把实验设计拆成四件套,每一件从论文里都能找到对应的依据:
- 数据:历史日线数据加金融新闻标题。历史数据用公开的股票数据接口获取,新闻用财经网站的标题抓取。注意时间范围要一致,论文里文献[4]使用 30 天作为长期窗口,这就是一个合理的数据切分依据。
- 特征:价格侧构造收益率、波动率、成交量变化率;文本侧先做分词和 Word2Vec 向量化,再降维到和价格特征同一量级。把两个分支的特征拼接成最终输入。
- 模型:基线用随机森林或 SVM,对照模型用 LSTM,如果数据量足够还可以加 CNN 分支。至少要有两个模型对比,才能体现你理解了传统模型和深度模型的边界。
- 评估:不能只看准确率,要看方向准确率、均方误差、回测收益率三个指标。论文里文献[7]和文献[16]的输出目标都是涨跌分类,你可以沿用这个设定,再额外输出回归的误差值体现深度。
5.3 最小可复现路径:LSTM 加情感特征的一版基线
很多拿到这份论文的朋友期望里面有源码,但它在定位上是一篇综述型的参考文献,源码并不在资源包内。所以我把复现路径直接写在下面,让你在读完综述后能快速跑通第一个版本。
import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler from tensorflow.keras.models import Model from tensorflow.keras.layers import Input, LSTM, Dense, concatenate # 1. 构造特征:价格特征 + 情感特征 def build_features(df, sentiment_scores, lookback=20): """ df: 历史股价DataFrame,需含close列 sentiment_scores: 每日新闻情感得分,与df按日期对齐 lookback: 时间窗口长度,对应文献[4]的短期窗口设置 """ price_feats = df['close'].pct_change().fillna(0) # 收益率 price_feats = StandardScaler().fit_transform(price_feats.values.reshape(-1, 1)) # 标准化 sentiment = StandardScaler().fit_transform(sentiment_scores.values.reshape(-1, 1)) # 情感得分标准化 X_price, X_sent, y = [], [], [] for i in range(lookback, len(df)): X_price.append(price_feats[i - lookback: i]) # 过去20天的收益率序列 X_sent.append(sentiment[i - lookback: i]) # 过去20天的情感序列 y.append(1 if df['close'].iloc[i] > df['close'].iloc[i - 1] else 0) # 涨跌二分类 return np.array(X_price), np.array(X_sent), np.array(y) # 2. 定义双分支LSTM模型 def build_model(lookback=20): price_input = Input(shape=(lookback, 1), name='price') sent_input = Input(shape=(lookback, 1), name='sentiment') lstm_price = LSTM(32, return_sequences=False)(price_input) # 价格序列分支 lstm_sent = LSTM(16, return_sequences=False)(sent_input) # 情感序列分支 merged = concatenate([lstm_price, lstm_sent]) # 拼接两路特征,对应文献[15]的做法 output = Dense(1, activation='sigmoid')(merged) model = Model(inputs=[price_input, sent_input], outputs=output) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) return model # 3. 训练与评估(按时间顺序切分,不做随机打乱) X_price, X_sent, y = build_features(df, sentiment_scores) split = int(len(X_price) * 0.8) # 前80%训练,后20%测试 model = build_model() model.fit([X_price[:split], X_sent[:split]], y[:split], epochs=10, batch_size=32, validation_split=0.1, verbose=1) loss, acc = model.evaluate([X_price[split:], X_sent[split:]], y[split:]) print(f'测试集方向准确率: {acc:.4f}')这段代码的每个环节都有对应论文依据。lookback=20对应文献[4]里短期特征窗口的设置,你可以按第 2.1 节里“窗口是可优化参数”的思路改成 30 或 60 对比效果。双分支 LSTM 拼接对应文献[15]的做法,区别是我把价格和情感分开建模再拼接,而不是直接把两组向量拼成一个长输入,这样能避免第 3.3 节提到的维度失衡问题。validation_split=0.1是在训练集内部再切 10% 做实时验证,配合早停策略使用效果更好。
6. 从复现到可信:滚动窗口回测与评估指标要这样设
论文在第 3 章结语部分提到一个重要观察:股票预测领域大多数研究是“确定并跟踪持续的价格趋势”,关注趋势逆转的研究相对较少。这个观察直接指向了回测评估的一个核心问题——你验证模型的方式必须是滚动式的,不能拿一段固定数据训练完就宣布模型有效。
我自己的习惯是做滚动窗口回测,核心思路是:用过去 240 个交易日的数据训练模型,预测接下来 5 个交易日的涨跌方向,然后把训练窗口向前推进 5 天,重新训练再预测。这样整个回测过程模拟的是真实交易中“每天接收新数据、定期重新训练”的节奏,而不是一次性学完之后永久使用。
def rolling_backtest(X_price, X_sent, y, train_days=240, step=5): predictions, y_true = [], [] for start in range(0, len(X_price) - train_days, step): end = start + train_days model = build_model() # 每轮重新初始化模型 model.fit([X_price[start:end], X_sent[start:end]], y[start:end], epochs=5, batch_size=32, verbose=0) for t in range(end, min(end + step, len(X_price))): p = model.predict([X_price[t:t+1], X_sent[t:t+1]])[0][0] predictions.append(p) y_true.append(y[t]) # 方向准确率:预测概率大于0.5视为看涨 acc = np.mean((np.array(predictions) > 0.5) == np.array(y_true)) return acc, predictions, y_true参数train_days=240对应一年左右的交易日数量,step=5对应每周重训一次。这两个参数的设置逻辑是:训练窗口太短,模型学不到足够的历史模式;窗口太长,模型又会被过时的市场风格带偏。重训步长太短则训练开销过大,太长则模型无法及时适应市场变化、例如风格切换或波动结构改变。我第一次做这种回测的时候,模型是上线前的历史回测里表现稳定,但实盘中遇到一次市场风格的急剧切换,模型连续一周预测失败,问题就出在训练窗口没有及时滚动更新。
从那以后,我每次做股票相关的模型验证都会强制走一遍滚动窗口流程,并且在记录实验时把随机种子、窗口长度、重训步长这三个配置一起写进实验日志。评估指标我也会同时看方向准确率、累计收益曲线和最大回撤三个维度,因为单独一个方向准确率骗不了别人但骗得了自己。如果你也在这条路上摸索,希望这篇综述的拆解和这套复现路径能帮到你少走一段弯路。
本文还有配套的精品资源,点击获取