ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

机器学习股票预测方法研究:从特征构造到模型对比的完整实验设计

机器学习股票预测方法研究:从特征构造到模型对比的完整实验设计 简介基于机器学习的股票预测方法研究论文是面向高校计算机、金融等专业毕业设计或课程设计的写作范本。资源包内为1个docx文档体积仅35KB便于下载阅读。目前已有586人学习浏览受到相关领域学习者与毕业生关注。论文系统梳理了股票预测的两类主流研究路径一类基于历史股票数据涵盖分段线性表示、高斯过程分类、随机森林、支持向量机及深度递归神经网络等传统机器学习方法另一类引入深度学习技术涉及卷积神经网络提取短期特征、长短期记忆网络生成预测、生成对抗网络建模等。在此基础上论文进一步探讨了融合金融新闻与股民评论的非结构化文本信息如基于情感词典的情绪极性分析、事件结构化表示与神经张量网络等前沿思路并总结了宏观因素与突发事件对预测的影响。全文结构完整、论述严谨既有方法综述也有实例分析适合初学者、工程师和在校师生借鉴可为撰写相关毕设或课设论文提供专业知识、实验思路与写作参考。1. 机器学习做股票预测论文参考到底能抄到什么毕设和课设里“基于机器学习的股票预测方法研究”是出现频率最高的题目之一。拿到这个题目的同学往往不是不知道怎么做而是不知道主线怎么搭从数据爬下来到模型出结果中间隔着一堆看似能跑又说不清对错的步骤。这篇参考文档的价值就是把这条线从头到尾串一遍——什么时候做特征、什么时候切数据、模型之间怎么对比、答辩老师最常追问哪个坑都在路线里。文档主线是标准的机器学习应用流程先确定预测目标再构造特征然后选模型做对比实验最后回测评估。适合需要在一个月内搭完实验并写完论文的本科毕设、课设学生不适合指望拿一个模型直接实盘赚钱的人这点先想清楚后面所有实验设计都会围绕“论文可解释”而不是“绝对收益”。2. 任务定义与特征构造把股票预测拆成可训练的问题拿到题目的第一步先别急着爬数据。“股票预测”这个词太大直接丢给机器学习模型它只会还给你一个无法解释的结果。我一般会把标题里的“预测”拆成两个能落地的子问题一是预测价格二是预测方向。价格是连续值方向是离散值两者对应的模型选型和评估方式完全不同写进论文里的思路也不一样。这里涉及到一个很现实的问题论文的“方法研究”部分到底要研究什么。如果只是把数据塞进模型里跑一遍那论文没有灵魂答辩时老师一问就露馅。所以这一章先把问题定义和特征构造讲透这两件事决定了后面所有对比实验的走向也决定了评审老师愿不愿意给你这个“方法研究”四个字打高分。2.1 涨跌分类比收盘价回归更适合当论文主线先说结论我不建议把“预测明日收盘价的具体数值”作为论文主线。股票价格近似随机游走你今天预测它明天收在 10.12 元还是 10.15 元本质上和猜硬币没太大差别。用线性回归或神经网络直接拟合收盘价出来的 RMSE 大得没法看而且论文里完全解释不了误差来源。更稳的做法是把预测目标定义成“明日涨跌方向”明日收盘价高于今日收盘价记为 1否则记为 0。这样一来任务就从回归变成了分类评估指标可以选准确率、F1、AUC每一张图都能讲出故事。“预测涨跌”在金融上也有实际含义——它对应着仓位决策预测涨就持有预测跌就空仓或减仓。这种做法在论文里很常见也是一条成熟的“方法研究”主线。把涨跌分类作为主实验把收盘价回归作为副实验放在后面做对比既完整又不冒进。如果你连方向都预测不准去预测具体数值只会让自己陷入被动。2.2 候选机器学习算法盘点与选型理由明确了分类主线之后下一步是选模型。机器学习算法很多但不能全塞进论文里跑一遍那样既耗时间又让论文看起来像流水账。下面这张表是我给毕设同学做选型时的常用底稿按“论文友好度”从高到低排列算法适合的任务论文里的优点毕设风险逻辑回归涨跌二分类系数可解释能做特征重要性分析对非线性关系拟合不足Lasso 回归特征选择自带稀疏性可用来筛特征输出是连续值需要额外设阈值转分类RBF 核 SVM中小规模分类决策边界直观理论完备样本量大时训练慢核参数需要调随机森林表格数据分类抗过拟合能力强能输出特征重要性树的数量和深度需要反复试XGBoost表格数据分类精度上限高竞赛验证过调参维度多容易陷入调参泥潭LSTM序列建模贴合时间序列直觉能写进对比章节训练慢数据少时效果还不如随机森林如果论文只覆盖“机器学习”而不涉及深度学习最稳妥的组合是SVM 和随机森林做主角XGBoost 做加分项逻辑回归当 baseline。这套组合的优点在于四个模型覆盖了“线性—核方法—树集成”三个方向横向对比起来层次丰富答辩时可以说清楚每个模型的差异。LSTM 不是不能加但它属于深度学习模型会引入训练时间、GPU、序列长度等一堆额外变量时间紧的话建议放附录。2.3 特征怎么构造价格、成交量与技术指标的取舍模型选好之后决定预测效果上限的是特征。股票预测里常用的特征可以分成四组每组在论文里都有对应的呈现方式特征组典型字段计算方式注意点基础价格开盘价、最高价、最低价、收盘价直接取原始行情必须先做复权处理衍生收益日收益率、5 日收益率收盘价 pct_change比原始价格更平稳优先使用均线类MA5、MA10、MA20、MA60滚动均值短中长周期各取一个即可技术指标RSI、MACD、布林带位置按公式计算版本差异大务必写明计算口径量价类成交量、量比、成交额原始值或滚动均值涨跌和放量缩量结合才有意义一个常见误区是特征堆得越多越好。我见过有同学一上来构造了 80 维特征随机森林倒是能跑但 SVM 在 80 维小样本上直接过拟合训练集准确率 98%测试集 53%。对于股票日线数据这种典型的小样本场景特征数量控制在 10 到 20 个比较合适。优先选你解释得清含义的特征解释不清的单指标宁可不要。另外提醒一句技术指标算出来之后前期数据必然有 NaN需要统一丢弃不能在后面训练时把 NaN 带进模型。这一点虽然基础但在论文代码里翻车的概率非常高。3. 数据、样本与标签把原始行情变成可训练数据集任务定义和特征方案定了接下来就是把文字方案变成真实代码。这一章解决的是“数据从哪来、标签怎么打、样本怎么切”三个问题。做股票预测的论文这一章的代码质量直接决定实验能否复现也是答辩老师最常要求现场演示的部分。3.1 数据来源与字段取舍国内做 A 股日线数据常见的做法是用免费的 Python 接口直接拉取比如 akshare 或 tushare。akshare 不需要注册 token对毕设来说最省事。下面这段代码拉取平安银行从 2020 年到 2023 年的日线数据import akshare as ak # 拉取平安银行000001日线前复权 df ak.stock_zh_a_hist( symbol000001, perioddaily, start_date20200101, end_date20231201, adjustqfq, ) # 先打印列名不同版本返回字段有差异 print(df.columns.tolist()) print(df.head())参数说明symbol是股票代码perioddaily表示日线adjustqfq表示前复权。前复权的作用是消除分红送股造成的价格跳变如果不复权股价会产生虚假的暴跌特征也会被污染。拿到数据后第一件事永远先打印列名和头部数据确认下字段是不是“日期、开盘、最高、最低、收盘、成交量、成交额”这一套再决定重命名逻辑。建议拉取至少 5 到 10 年的数据。股票预测实验的样本量本身就小日线一年只有 240 条左右三年不到 800 条再扣掉计算指标和生成标签时的 NaN 丢弃最终能进模型的特征矩阵可能只有 700 行这点数据量对 SVM 和随机森林来说非常紧张。3.2 生成标签与特征滑动窗口与滞后设计数据拿回来以后先重命名列再生成标签和特征。这套流程我习惯写成一个函数方便后面换股票或换时间区间时直接复用import pandas as pd def prepare_dataset(df): # 统一列名 df df.rename(columns{ 日期: date, 开盘: open, 最高: high, 最低: low, 收盘: close, 成交量: volume }) df df[[date, open, high, low, close, volume]].copy() df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) # 标签明日收盘价高于今日收盘价记为 1 # shift(-1) 取的是下一行的值这正是我们要预测的未来信息 df[label] (df[close].shift(-1) df[close]).astype(int) # 特征收益、均线、量比 df[ret_1] df[close].pct_change(1) df[ret_5] df[close].pct_change(5) df[ma5] df[close].rolling(5).mean() df[ma20] df[close].rolling(20).mean() df[ma_ratio] df[ma5] / df[ma20] # 短期均线相对长期均线的位置 df[vol_ratio] df[volume] / df[volume].rolling(20).mean() # 丢弃计算产生的缺失值 df df.dropna().reset_index(dropTrue) return df这段代码里的关键点在注释里已经标出来了shift(-1)生成标签时用到的确实是“未来一天”的收盘价这在监督学习里是合法的因为标签本来就是我们要预测的东西。但要注意如果特征里也用了shift(-1)那模型在训练时就偷看了答案这属于严重的未来函数泄漏后面第 5 章会专门讲。rolling(5).mean()会从第 5 行开始才有值所以前 20 行左右的均线特征都是 NaN。dropna()把所有含 NaN 的行丢弃最终得到的是干净整齐的特征矩阵。这里还有一个细节vol_ratio用的是当日成交量除以过去 20 日平均成交量大于 1 说明放量小于 1 说明缩量这个特征在量价分析里比只看绝对值稳定得多。3.3 归一化与训练、验证、测试集划分特征构造好之后下一步是归一化和切分。股票数据是时间序列不能用train_test_split直接随机打乱否则模型看到的是“过去的未来”测试集评估结果会虚高。我一般手动按时间顺序切分训练集 70%、验证集 15%、测试集 15%from sklearn.preprocessing import StandardScaler feature_cols [ret_1, ret_5, ma5, ma20, ma_ratio, vol_ratio] X df[feature_cols].values y df[label].values # 按时间顺序切分绝对不能 shuffle train_end int(len(df) * 0.70) val_end int(len(df) * 0.85) X_train, y_train X[:train_end], y[:train_end] X_val, y_val X[train_end:val_end], y[train_end:val_end] X_test, y_test X[val_end:], y[val_end:] # 归一化只在训练集上 fit再 transform 验证集和测试集 scaler StandardScaler() scaler.fit(X_train) X_train scaler.transform(X_train) X_val scaler.transform(X_val) X_test scaler.transform(X_test)这段代码最值得说的是scaler.fit(X_train)这一步。标准化需要计算均值和标准差这两个统计量只能从训练集算然后再用同样的参数去变换验证集和测试集。如果先对全量数据做标准化再切分训练集里就包含了测试集的均值信息这在论文里属于数据泄漏。虽然短期股价预测里影响不一定肉眼可见但答辩老师问起来答不上“为什么先 fit 再 transform”就露怯了。如果想让论文显得更严谨可以把手动切分布换成 sklearn 的TimeSeriesSplit做 5 折时间序列交叉验证用验证集的平均指标来选模型参数。但要注意TimeSeriesSplit的每一折也是严格按时间顺序划分的不会出现后一折作为前一折训练集的问题。这部分代码不需要写进正文在论文的“实验设计”里描述清楚即可。4. 模型训练与评估用同一个切分跑平行对比实验数据准备好了模型对比就是水到渠成的事。论文里的“对比实验”最怕各跑各的——不同人写出来的代码用了不同的特征、不同的切分、不同的归一化方式结果根本没有可比性。我的做法是固定一套数据管线只更换模型对象这样指标差异完全归因于模型本身的差异写作时也省力。4.1 统一评估函数与三类核心指标机器学习分类任务里准确率、F1 和 AUC 三件套基本够用。准确率直观F1 对正负样本不平衡敏感AUC 不依赖具体阈值能从排序角度衡量模型的区分能力。下面定义统一的评估函数from sklearn.metrics import accuracy_score, f1_score, roc_auc_score def evaluate_model(model, X_val, y_val): y_pred model.predict(X_val) # 部分模型需要先取预测概率再算 AUC if hasattr(model, predict_proba): y_prob model.predict_proba(X_val)[:, 1] else: y_prob model.decision_function(X_val) return { accuracy: round(accuracy_score(y_val, y_pred), 4), f1: round(f1_score(y_val, y_pred), 4), auc: round(roc_auc_score(y_val, y_prob), 4), }参数说明accuracy_score直接比较预测类别和真实类别f1_score默认计算正类的 F1这里正类就是 label 为 1 的“上涨日”predict_proba取第 1 列代表属于正类的概率。SVM 如果不带probabilityTrue就没有predict_proba属性只能用decision_function的输出替代这一点在调用时需要做兼容处理。在论文里这三个指标要一起放不能只挑好看的写。AUC 能到 0.55 以上、准确率能到 52% 以上在股票方向预测里已经是有效信息了别拿图像分类的标准来要求金融时序那是两个世界的任务。4.2 主模型训练SVM、随机森林与 XGBoost模型训练部分我会把要对比的模型放在一个字典里循环训练并收集结果from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier models { svm_rbf: SVC(kernelrbf, C1.0, gammascale, probabilityTrue, random_state42), random_forest: RandomForestClassifier( n_estimators200, max_depth8, min_samples_leaf5, random_state42 ), } results {} for name, model in models.items(): model.fit(X_train, y_train) results[name] evaluate_model(model, X_val, y_val) print(name, results[name])SVC这里开了probabilityTrue代价是训练时间变长但换来的是可以直接算 AUC对论文来说是值得的。C1.0是正则化强度的默认值越大越容易过拟合gammascale让核宽自动依据特征数量来设置是一个比较安全的默认值。随机森林的n_estimators200表示 200 棵树max_depth8限制每棵树的深度min_samples_leaf5强制每个叶子节点至少 5 个样本这三组参数共同控制模型的复杂度是表格数据里比较稳的起点。如果你想让对比实验更饱满可以再加一个 XGBoostfrom xgboost import XGBClassifier xgb XGBClassifier( n_estimators300, learning_rate0.05, max_depth4, subsample0.8, colsample_bytree0.8, random_state42, eval_metriclogloss, ) xgb.fit(X_train, y_train) results[xgboost] evaluate_model(xgb, X_val, y_val) print(xgboost, results[xgboost])XGBoost 的四个关键参数要能解释清楚n_estimators决定树的数量learning_rate是每棵树对最终预测的贡献权重放小一点更抗过拟合subsample每轮随机抽样比例colsample_bytree每棵树随机选特征的比例这两个都是防过拟合的手段。调参的时候不要一上来就 GridSearch先用默认参数跑出结果再固定一个参数单独调否则搜索空间太大会把时间耗光。4.3 论文结果表的呈现方式与写作建议实验跑完结果表建议按下面的模板组织简单清晰模型准确率F1AUC训练耗时逻辑回归baselineRBF 核 SVM随机森林XGBoost训练耗时可以用 Python 的time.time()在 fit 前后打点记录几秒钟还是几分钟会有肉眼可见的差别。论文里不要只写“随机森林效果最好”就结束至少要补一句解释为什么树模型在这个特征集上优于 SVM常见的解释是特征间的非线性交互被树结构自动捕捉到了而 SVM 需要人工设计核函数来拟合这种交互。写作时有一个容易犯的毛病把所有指标都加粗突出自己最想说的那项。其实答辩老师不傻别把验证集指标写成测试集指标也别把“验证集调参”说成“测试集评估”一旦被追问出矛盾整篇论文的可信度都会打折扣。机器学习实战里最忌讳的就是实验过程不透明宁可指标普通一点也要保证每一步都经得起现场复现。5. 论文里最容易被答辩老师追问的 5 个坑先踩先改机器学习股票预测这个方向的论文技术栈不复杂真正的技术含量全在细节。你踩过的坑答辩老师大概率也踩过他们问的问题基本集中在“数据有没有泄漏、标签怎么打的、评估到底可不可信”这几个方向。下面五个坑是我见过频率最高的每个都按“现象—原因—解决”的顺序讲。5.1 坑 1标准化时把全样本统计量用进去了现象模型在训练集上 AUC 0.72测试集上 AUC 只有 0.51差距大得离谱论文里交代不清。原因对全量数据统一调用scaler.fit_transform(X)训练阶段已经用到了测试集的均值和方差相当于测试集信息提前进入了模型。解决严格按“先切分再只对训练集 fit再 transform 验证集和测试集”的顺序执行。代码格式见第 3.3 节最重要的是把fit和transform分开写。5.2 坑 2随机打乱样本时间序列被暗中作弊现象用train_test_split(X, y, test_size0.2, random_state42)切分结果 AUC 稳定在 0.60 以上看着很漂亮但一改成时间顺序切分立刻掉到 0.53。原因随机打乱后训练集里混入了“未来”的样本模型在训练时见过同一段行情的邻近片段测试时等于开卷考试。解决股票数据禁止随机洗牌一律按时间序切分。也可以用TimeSeriesSplit做交叉验证但本质上都必须保证训练集索引始终小于测试集索引。5.3 坑 3标签代码把当天信号当成了次日信号现象模型准确率异常高比如到 68% 以上但一画预测曲线发现所有预测都比真实信号“提前一天”。原因标签写成了(df[close].shift(-1) df[close].shift(-2))或者把当日涨跌幅直接当成了明日涨跌幅的标签。前者错位一天后者拿当天的涨跌和未来的特征对齐特征和标签时间窗口错位。解决标签统一用“下一根 K 线收盘价相对于当前收盘价的方向”也就是df[close].shift(-1) df[close]。写完代码后随机抽几行人工核对看看 label 是否真的对应了明天的涨跌。5.4 坑 4只用准确率被不均衡样本骗了现象预测结果准确率 55%看起来很有效但打印分类报告发现模型把所有样本全预测为“涨”因为数据集里上涨日占比本来就 55%。原因股票数据中涨跌天数并非严格各半当一方占多数时模型学到的只是“无脑预测多数类”这个捷径。解决至少同时报告 AUC 和 F1。AUC 不受阈值影响F1 对正类样本的捕捉能力敏感。如果正确率向多数类倾斜优先调整分类阈值用验证集上 F1 最高的阈值替代默认的 0.5。5.5 坑 5参数调到完美换一段行情立刻失效现象在 2020—2023 年数据上调出来一组“完美参数”测试集 AUC 0.58换成 2024 年行情回测立刻掉到 0.50甚至不如随机猜。原因股价数据的信噪比极低调参过程中模型可能把特定年份的行情特征当成了普适规律比如把 2020—2021 年的白马股行情特征学进去了。解决论文里明确区分“调参集”和“最终测试集”。我一般会拿最近 10% 的样本做最后的留出集调参期间完全不去碰它所有参数确定之后才跑一次最终评估。如果最终结果和验证集差异过大说明参数过拟合了行情需要回到上一个台阶重新调。6. 让实验结果更可信的两个进阶技巧如果前面的主线实验都跑通了再花半天时间做两个进阶验证论文的说服力会明显上台阶答辩时也有话可讲。6.1 用滞后基线挡住“模型没学到东西”的尴尬股票预测论文里最常被问到的问题是你的模型比“什么都不做”强在哪这时候你需要一个 baseline。最简单的基线是“惰性预测”明天涨跌方向与今天相同今天涨了预测明天也涨。实现只有一行base_line_acc (df[close].shift(1) df[close]).astype(int).mean()这个数值一般在 0.48 到 0.52 之间。你的模型准确率如果只有 51%但基线已经 50.5%那涨幅有限反过来如果模型 AUC 有 0.58、准确率 54%而 baseline 只有 50%结论就立住了。把这个数字写进实验设计章节作为评估指标的参照系比单独放一堆模型对比更有说服力。6.2 考虑手续费和滑点的简单回测分类指标只能说明预测能力不能说明策略能不能赚钱。论文里加一个小节做简化回测按预测结果模拟交易同时扣除手续费会让整体工作量看起来非常饱满。下面是一个极简回测框架def simple_backtest(close, pred, fee0.0005, threshold0.55): capital 100000.0 # 初始资金 position 0 # 持仓状态1 持有0 空仓 equity [] # 每日净值 for i in range(1, len(close)): # 预测概率高于阈值才买入避免频繁交易 if pred[i] threshold and position 0: position 1 capital * (1 - fee) # 买入扣手续费 elif pred[i] threshold and position 1: position 0 capital * (close[i] / close[i - 1]) * (1 - fee) # 卖出扣手续费 if position 1: capital * close[i] / close[i - 1] equity.append(capital) return equity参数说明fee0.0005表示单边万五的手续费threshold0.55表示只有预测上涨概率超过 55% 才开仓这是为了过滤掉那些“拿不准”的预测。回测的目的不是让你证明策略能赚钱而是让你对比“带模型”和“随机入场”两条净值曲线的差异。如果两者走势几乎一样说明预测信号没有实际交易价值这时候回去调特征或阈值比重新写模型更能解决问题。我在做自己的课程设计时吃过一次亏模型指标看着不错但一加上手续费和滑点年化收益直接变成负的。从那以后我所有股票预测实验都强制带一次回测哪怕结果是亏的至少心里清楚亏在哪里。预测方向和实际交易之间隔着成本、滑点和仓位管理把这些诚实写进论文里反而是最能体现研究态度的部分。希望这篇笔记能帮你把实验主线搭得稳一点少走点我当年走过的弯路。本文还有配套的精品资源点击获取
返回列表