首页 / 资讯中心 / 文章详情

Python实现:逻辑回归、梯度提升与LSTM对比预测股票月度收益

Python实现:逻辑回归、梯度提升与LSTM对比预测股票月度收益 ★ FEATURED ARTICLE
简介一份面向金融量化与机器学习学习者的Python实战项目结合逻辑回归、梯度提升与LSTM三种模型基于Quandl数据库中的基本面与情绪特征预测下个月股票收益正负并构建市场中性多空策略。包内共有20个文件包含可运行的策略脚本strategy.py、Notebook分析流程、英文说明文档及答辩PPT另有16张收益分布、回撤、月度标准差等策略表现图表整个项目文件组织清晰图片结果对应不同参数情景可辅助理解模型灵敏度与稳健性。整体压缩包仅1.8MB体量轻便下载与解压都很快捷已有115人学习。内容覆盖数据读取、特征工程、模型训练、策略回测与结果可视化适合计科、人工智能、金融科技等专业的课设或毕设参考代码经测试可运行如遇运行问题可联系作者远程指导。1. 基于逻辑回归、梯度提升和LSTM预测下个月股票收益这份Python源码项目到底解决了什么问题这份项目zip里放了三样东西Python源码、说明文档、答辩PPT。它的核心不是“预测下个月股票收益”这个听起来很玄学的任务而是用同一份特征数据把逻辑回归、梯度提升和LSTM三种模型放到同一个时间序列框架里做对比。源码完成的事可以概括为拉取行情 → 构造月度特征与下月收益标签 → 分别训练三个模型 → 输出评估指标和预测方向。说明文档和答辩PPT则回答“为什么用这三个模型、结果怎么看、缺陷在哪”。适合做金融数据挖掘课程设计、量化方向毕业设计以及第一次接触时间序列建模的入门者。2. 三个模型为什么放在一起比逻辑回归、梯度提升和LSTM的建模边界这个项目最有价值的地方不是某一个模型预测有多准而是把三类差异非常大的模型放进同一份源码、同一套评估流程里。逻辑回归是线性模型梯度提升是树模型LSTM是序列模型它们在数学形态上完全不同对特征的要求也完全不同。放在一起对比能让你看到“同一个任务在不同模型假设下会差多少”。2.1 逻辑回归适合当概率基线的线性分类器逻辑回归名字里虽然带“回归”在机器学习里它其实是分类器。它的做法是先把特征做线性加权再经过sigmoid函数映射成一个0到1之间的概率。损失函数是交叉熵不是MSE这一点刚接触时容易搞混。它的优点是非常稳定训练快、参数少、结果可解释特征的系数正负能直观说明“动量因子越大下个月上涨概率越高”还是“越低”。在预测下个月股票收益的代码里我一般把逻辑回归当“下限模型”用。也就是先跑它如果逻辑回归的测试集AUC都低于0.55那说明特征构造或切分方式一定有问题先别急着调LSTM。逻辑回归的缺点也很明显它假设特征和收益的对数几率是线性关系无法自动处理非线性交互。因此必须做标准化把特征压缩到相近的量纲否则正则化强度C对不同特征的作用会失衡。2.2 梯度提升表格特征里的默认强者梯度提升GBDT不是某一个算法而是一族算法的统称常见实现有XGBoost、LightGBM和scikit-learn里的HistGradientBoostingClassifier。它的思路是串行训练许多棵决策树每棵树去拟合前面所有树的残差最终把所有树的输出累加起来。它对特征量纲不敏感能自动处理缺失值也能拟合复杂非线性关系所以是“拿到表格特征后默认先跑一遍”的模型。但梯度提升不是没有代价。月度股票数据通常只有几百个样本树模型容量又大非常容易把训练集噪声当成规律记住。常见现象是训练集AUC接近0.98测试集AUC只有0.52。因此源码里一定要设置early_stopping、限制叶子节点数并且用时间序列切分而不是随机切分来验证。2.3 LSTM为序列依赖准备的深度模型LSTM是循环神经网络的一种通过输入门、遗忘门、输出门控制信息在时间步之间流动理论上可以记住较长时间之前的模式。用它预测下月股票收益常见做法是把过去N个月构成的窗口作为输入输出下个月上涨的概率。代码上要处理成三维张量形状是样本数序列长度特征数量。这里要说句泼冷水的话LSTM并不等于“能预测涨跌的魔法”。金融月度序列信噪比极低LSTM在这种数据上经常表现不如梯度提升甚至不如逻辑回归。它的作用更像是“深度基线”证明你已经考虑过时序依赖建模并展示深度学习在低信噪比小样本上的局限性。答辩时这反而是个加分项。2.4 先用模拟数据跑通三个模型的最小对比脚本在进入真实行情前我建议先在一个假数据集上把流程跑通避免一上来就被yfinance的数据清洗卡住。下面这个最小脚本包含时间序列切分、三个模型训练和AUC对比是整个项目源码的最小骨架。import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.ensemble import HistGradientBoostingClassifier from sklearn.metrics import roc_auc_score rng np.random.default_rng(42) # 模拟120个月20个特征 X rng.standard_normal((120, 20)) # 构造一点弱信号让第一个特征和第二个特征影响上涨概率 linear 0.5 * X[:, 0] 0.2 * X[:, 1] - 0.1 prob 1 / (1 np.exp(-linear)) y (rng.random(120) prob).astype(int) # 时间序列切分前80个月训练后40个月测试 train_end 80 X_train, X_test X[:train_end], X[train_end:] y_train, y_test y[:train_end], y[train_end:] # 逻辑回归 lr LogisticRegression(C1.0, max_iter1000) lr.fit(X_train, y_train) lr_auc roc_auc_score(y_test, lr.predict_proba(X_test)[:, 1]) print(LR AUC:, round(lr_auc, 4)) # 梯度提升用直方图版本训练快且自带缺失值处理 gb HistGradientBoostingClassifier( learning_rate0.1, max_iter100, max_leaf_nodes15, early_stoppingFalse, random_state42 ) gb.fit(X_train, y_train) gb_auc roc_auc_score(y_test, gb.predict_proba(X_test)[:, 1]) print(GB AUC:, round(gb_auc, 4))这段代码里的切分顺序是固定的没有用train_test_split里的shuffle。逻辑回归直接用原始特征也能跑是因为模拟数据本身就接近正态真实行情数据不行必须标准化。梯度提升里的max_leaf_nodes限制每棵树的叶子数量叶子越少模型越保守early_stopping这一版先关掉因为样本太少后面真实数据里要打开。再看LSTM最小实现。这里需要先构造滑动窗口再切训练和测试import torch import torch.nn as nn def make_windows(X, y, lookback12): Xs, ys [], [] for i in range(lookback, len(X)): Xs.append(X[i - lookback:i]) ys.append(y[i]) return np.array(Xs), np.array(ys) class StockLSTM(nn.Module): def __init__(self, input_size, hidden_size8): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, batch_firstTrue) self.fc nn.Linear(hidden_size, 1) def forward(self, x): out, _ self.lstm(x) return self.fc(out[:, -1, :]).squeeze(-1) # 窗口要在切分之前构造让训练和测试样本天然按时间排开 X_seq, y_seq make_windows(X, y, lookback12) X_seq_train, X_seq_test X_seq[:train_end - 12], X_seq[train_end - 12:] y_seq_train, y_seq_test y_seq[:train_end - 12], y_seq[train_end - 12:] model StockLSTM(input_sizeX.shape[1], hidden_size8) optimizer torch.optim.Adam(model.parameters(), lr0.01) loss_fn nn.BCEWithLogitsLoss() for epoch in range(30): model.train() logits model(torch.tensor(X_seq_train, dtypetorch.float32)) loss loss_fn(logits, torch.tensor(y_seq_train, dtypetorch.float32)) optimizer.zero_grad() loss.backward() optimizer.step() model.eval() with torch.no_grad(): logits model(torch.tensor(X_seq_test, dtypetorch.float32)) probs torch.sigmoid(logits).numpy() lstm_auc roc_auc_score(y_seq_test, probs) print(LSTM AUC:, round(lstm_auc, 4))LSTM的输入形状是样本数, 12, 20意思是每个样本由过去12个月、每个月20个特征组成。训练时用的BCEWithLogitsLoss把最后一层sigmoid和交叉熵合在一起数值上比单独用MSE再算概率稳定得多。这个最小脚本跑通以后再替换成真实行情数据和真实特征逻辑不会变。3. 用Python搭“预测下个月收益”的完整流程数据、特征与三份模型源码从模拟数据切到真实数据主要多出三件事行情获取、月度特征构造、标签对齐。这三件事如果顺序搞错后面模型调参全是白费。3.1 拉取行情并构造下月收益标签别让未来数据混进特征建议先用yfinance拉日线再降采样成月线。降采样的好处是能让特征和标签的频率天然对齐不用自己处理交易日历。import yfinance as yf import pandas as pd # 示例股票代码可换成自己的标的 ticker 600519.SS df yf.download(ticker, start2012-01-01, end2023-12-31, auto_adjustTrue) # 月线收盘价取每个月最后一个交易日 monthly_close df[Close].resample(ME).last() # 月成交量把当月成交量求和 monthly_volume df[Volume].resample(ME).sum() # 下个月收益当前月末收盘价 - 下月末收盘价 next_month_close monthly_close.shift(-1) next_month_return next_month_close / monthly_close - 1 # 分类标签下个月是否上涨 label (next_month_return 0).astype(int)这段代码的关键是shift(-1)它把下个月的收盘价贴到当前行。第t行的label含义是t月末看得到的特征去预测t1月末的收益方向。很多人会在这里把pct_change的默认值和shift方向搞反导致标签和特征同一时刻变成“用当月收盘价预测当月收益”这属于未来函数。检查方法很简单打印label的索引和monthly_close的索引看是否错开一个月。注意resample(ME)需要pandas 2.2以上版本旧版本用M也能跑但会提示弃用建议直接改成ME。3.2 量价与动量特征滞后收益、波动率、RSI下月收益预测常用的特征是过去的滞后收益、波动率、动量和成交量变化。这些特征全部在当月结束时就已知不会偷看未来。features pd.DataFrame(indexmonthly_close.index) features[ret_1] monthly_close.pct_change(1) features[ret_3] monthly_close.pct_change(3) features[ret_6] monthly_close.pct_change(6) features[ret_12] monthly_close.pct_change(12) features[vol_6] monthly_close.pct_change().rolling(6).std() features[vol_12] monthly_close.pct_change().rolling(12).std() features[volume_ratio] monthly_volume / monthly_volume.rolling(6).mean() # 用RSI做动量特征 def rsi(close, period14): delta close.diff() gain delta.clip(lower0).rolling(period).mean() loss (-delta.clip(upper0)).rolling(period).mean() return 100 - 100 / (1 gain / loss) features[rsi_14] rsi(monthly_close, 14) # 合并特征和标签去掉前12行因为滞后特征不完整 dataset pd.concat([features, label.rename(label)], axis1).dropna() dataset dataset.reset_index(dropTrue)特征列里ret_1是上一个月收益率ret_3是过去三个月累计收益率vol_6是过去六个月收益率的滚动标准差volume_ratio反映本月成交量相对过去六个月的倍数。这些特征之间存在相关性逻辑回归不需要做PCA但标准化一定要做梯度提升可以不做LSTM也建议做因为LSTM里的tanh和sigmoid对输入尺度很敏感。3.3 逻辑回归与梯度提升的实现一个干净的时间切分拿到dataset后先按日期切分。不能随机切分要按照时间顺序把前80%做训练、后20%做测试。下面代码把2022年1月作为分界点。from sklearn.model_selection import train_test_split from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.ensemble import HistGradientBoostingClassifier split_date 2022-01-01 train_set dataset.loc[:split_date] test_set dataset.loc[split_date:] X_train train_set.drop(columns[label]) y_train train_set[label] X_test test_set.drop(columns[label]) y_test test_set[label] # 逻辑回归标准化 逻辑回归组成pipeline lr_model make_pipeline( StandardScaler(), LogisticRegression(C0.5, max_iter1000, solverlbfgs) ) lr_model.fit(X_train, y_train) gb_model HistGradientBoostingClassifier( learning_rate0.05, max_iter200, max_leaf_nodes15, early_stoppingTrue, validation_fraction0.15, random_state42 ) gb_model.fit(X_train, y_train) for name, model in [(LR, lr_model), (GB, gb_model)]: prob model.predict_proba(X_test)[:, 1] auc roc_auc_score(y_test, prob) print(f{name} test AUC: {auc:.4f})这里有个细节为什么逻辑回归要用pipeline而梯度提升不用。因为逻辑回归需要StandardScalerpipeline保证scaler只用训练集的数据来fit再transform测试集不影响测试集。梯度提升是树模型不需要标准化。C0.5是正则化强度越小惩罚越重。月度股票数据通常特征不多C在0.1到1之间都比较安全太小容易欠拟合太大容易让系数跟着极端值走。HistGradientBoostingClassifier里的validation_fraction0.15表示从训练集尾部抽出15%做early_stopping的验证集这样不会泄漏测试集信息。3.4 LSTM的实现窗口、标准化和训练循环LSTM不能直接吃这种“一行是一个月”的表格要先构造窗口。窗口长度叫lookback我习惯设12个月也就是用过去一年预测下一个月。import numpy as np import torch import torch.nn as nn def build_lstm_data(features, labels, lookback12): Xs, ys [], [] for i in range(lookback, len(features)): Xs.append(features[i - lookback:i]) ys.append(labels[i]) return np.array(Xs, dtypenp.float32), np.array(ys, dtypenp.float32) X_all dataset.drop(columns[label]).values y_all dataset[label].values X_seq, y_seq build_lstm_data(X_all, y_all) # 找到2022-01在数组里的位置 train_rows train_set.shape[0] X_seq_train X_seq[:train_rows - 12] X_seq_test X_seq[train_rows - 12:] y_seq_train y_seq[:train_rows - 12] y_seq_test y_seq[train_rows - 12:] # 标准化只对训练样本拟合scaler再transform测试样本 scaler StandardScaler() train_2d X_seq_train.reshape(-1, X_seq_train.shape[2]) test_2d X_seq_test.reshape(-1, X_seq_test.shape[2]) scaler.fit(train_2d) X_seq_train scaler.transform(train_2d).reshape(X_seq_train.shape) X_seq_test scaler.transform(test_2d).reshape(X_seq_test.shape)为什么窗口要在切分之前构造而不是之后因为如果先切分、再对测试集单独构造窗口前12个测试样本的窗口会丢掉而且切分边界容易错开。先构造窗口、再按位置切分能让“第train_rows-12个样本”正好是第一个测试样本因为前12个月只能作为历史窗口没有标签。网络结构和训练循环如下class StockLSTM(nn.Module): def __init__(self, input_size, hidden_size16, dropout0.2): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, batch_firstTrue) self.classifier nn.Sequential( nn.Linear(hidden_size, 8), nn.ReLU(), nn.Dropout(dropout), nn.Linear(8, 1) ) def forward(self, x): out, _ self.lstm(x) last out[:, -1, :] return self.classifier(last).squeeze(-1) model StockLSTM(input_sizeX_seq.shape[2], hidden_size16, dropout0.2) optimizer torch.optim.Adam(model.parameters(), lr0.001) loss_fn nn.BCEWithLogitsLoss() epochs 80 batch_size 32 train_dataset torch.utils.data.TensorDataset( torch.tensor(X_seq_train), torch.tensor(y_seq_train) ) train_loader torch.utils.data.DataLoader(train_dataset, batch_sizebatch_size, shuffleFalse) for epoch in range(epochs): model.train() epoch_loss 0.0 for xb, yb in train_loader: optimizer.zero_grad() logits model(xb) loss loss_fn(logits, yb) loss.backward() optimizer.step() epoch_loss loss.item() if (epoch 1) % 20 0: print(fepoch {epoch 1}, loss {epoch_loss / len(train_loader):.4f})这里没有加验证集早停真实项目建议模仿上一节梯度提升留15%训练集尾做验证。shuffleFalse也很重要月度序列有连续记忆shuffleTrue会让LSTM看到“未来段”的统计规律。虽然窗口样本不一定严格时序相关但保险起见时序数据Loader默认不shuffle。4. 模型验证与超参数设置时间序列切分和三个关键参数模型训练完之后最容易被答辩老师问的问题就是“你怎么防止数据泄漏”这一章讲验证方法以及每个模型最值得调的参数。4.1 用扩展窗口回测而不是一次性切分前面用单一切分点只是方便演示。每个月数据只有一个样本单次切分太依赖那一个月的运气。更稳的做法是walk-forward扩展窗口验证训练集从起点开始不断增加每次向后预测12个月然后记录这一段的AUC。from sklearn.metrics import roc_auc_score def walk_forward_auc(X, y, model_factory, initial_size60, step12): auc_list [] for end in range(initial_size, len(X) - step, step): X_train X.iloc[:end] y_train y.iloc[:end] X_test X.iloc[end:end step] y_test y.iloc[end:end step] model model_factory() model.fit(X_train, y_train) prob model.predict_proba(X_test)[:, 1] auc_list.append(roc_auc_score(y_test, prob)) return auc_list这个函数每次把训练集扩展一个窗口再预测往后12个月。相比随机K折它保持了时间顺序避免未来信息回渗。逻辑回归和梯度提升都能直接套这个函数只需要传入不同的model_factory。LSTM因为要构造窗口需要单独包装思路完全一样只是序列切分点上要减掉lookback。4.2 逻辑回归和梯度提升的关键参数怎么设下表是我的习惯经验值适合月度股票收益这种小样本场景。模型参数建议值说明逻辑回归C0.1~1.0正则化强度C越小惩罚越大逻辑回归solverlbfgs中小样本默认收敛稳定梯度提升learning_rate0.02~0.1调小能让训练更稳但需要更多树梯度提升max_iter100~300树的总数量配early_stopping使用梯度提升max_leaf_nodes15~31限制单棵树大小是防过拟合最关键的参数梯度提升early_stoppingTrue从训练集尾部抽验一部分做早停梯度提升最容易被忽视的是max_leaf_nodes。默认的31在表格竞赛里没问题但在月度数据上样本量小叶子太多会让每片叶子几乎只覆盖一条样本。我一般先设15看测试AUC如果稳定就把learning_rate降一点、max_iter加一点。4.3 LSTM的三个关键参数lookback、hidden_size和epochsLSTM有三个参数对结果影响最大。lookback决定一个样本里包含多少个月的历史。lookback太小LSTM看不到中期趋势lookback太大比如24或36月度样本会急剧减少而且LSTM很容易记住长长的噪声。月度预测一般6到12个月足够。hidden_size控制LSTM隐状态的维度。16对月度金融数据够用32也行但再大很容易过拟合。hidden_size越大参数量呈平方增长训练越慢收益越差。配合dropout一起用dropout设0.2到0.5能显著降低训练集和测试集AUC的差距。epochs不是越大越好。我会在训练循环里打印每个epoch的lossloss在30轮后还在下降不代表测试AUC会变好。更可控的办法是从训练集尾部抽10%~15%当验证集验证AUC连续10个epoch不升就停止。4.4 评估指标别只盯着准确率月度上涨和下跌样本比例接近55%对45%准确率本身意义不大。重点看AUC和F1。逻辑回归和梯度提升输出概率后阈值可以不用0.5而是根据训练集的正例比例调整。from sklearn.metrics import accuracy_score, f1_score, roc_auc_score cutoff y_train.mean() # 用训练集上涨比例当阈值 pred (lr_model.predict_proba(X_test)[:, 1] cutoff).astype(int) print(cutoff:, round(cutoff, 3)) print(AUC:, round(roc_auc_score(y_test, lr_model.predict_proba(X_test)[:, 1]), 3)) print(F1:, round(f1_score(y_test, pred), 3)) print(Accuracy:, round(accuracy_score(y_test, pred), 3))cutoff使用训练集正例比例比固定0.5更能适配样本不平衡。AUC不受阈值影响所以三个模型之间优先比AUCF1则反映在业务阈值下的实际命中质量。5. 避坑指南股票收益预测最容易翻车的5个地方这部分按“现象 → 原因 → 解决”来写全是做月度预测时踩过的真坑。5.1 现象用全量数据标准化测试集信息泄漏现象训练前把X_all整体放进StandardScaler结果测试集AUC异常高LSTM甚至冲到0.75逻辑回归也超过0.65。换棵股票后AUC又掉回0.5非常不稳定。原因StandardScaler的mean和std来自整个数据集包括测试期数据。等于是把未来走势的统计信息塞进了训练特征。这不是模型变强而是泄漏。解决先切分后标准化。任何缩放器都只在训练集上fit再transform测试集。最简单的是用pipeline把StandardScaler和逻辑回归绑在一起LSTM就手动拆成train_2d和test_2d两步。5.2 现象随机打乱样本梯度提升AUC虚高现象用train_test_split(shuffleTrue)训练梯度提升测试AUC 0.68很兴奋。换成按时间切分后AUC只有0.53答辩直接被问住。原因月度收益存在自相关性相邻月份特征相似。随机打乱会把相近月份的样本分别放进训练和测试模型等于在记忆近邻而不是预测未来。解决一律用时间序列切分或walk-forward。判断标准是“训练集时间必须全部早于测试集时间”。源码里尽量别出现shuffleTrue除非你明确在做随机截面的选题。5.3 现象LSTM用MSE回归预测下月收益率输出几乎全是0现象把任务定义成“预测下个月收益率的数值”用MSELoss训练LSTMloss降到很低但预测值全部落在平均值附近方向命中率只有50%。原因月度收益率接近随机信噪比低。MSE准则下最优预测是条件期望在噪声很大的场景里条件期望就是样本均值模型自然学出一套“躺平”的解。这其实是模型做对了但任务定义错了。解决改成二分类任务用BCEWithLogitsLoss预测上涨概率。如果一定要做回归不要用MSE评估改用RankIC或者把收益分成五档分类让模型学习排序而不是精确数值。5.4 现象梯度提升训练集AUC接近0.99测试集AUC不到0.5现象训练集AUC非常漂亮一测测试集就崩甚至低于随机水平。原因一是树模型过拟合样本量少模型把训练集噪声背了下来二是验证截断点选择太晚模型训练样本里包含了一些和测试期风格完全不同的年份时间漂移导致失效。解决先调小max_leaf_nodes或者max_depth把定位在普通风险模型而不是记忆机器。打开early_stopping用训练集尾部15%做验证。最后用walk-forward滚动多次得到的多段AUC取中位数比单次结果可信得多。5.5 现象逻辑回归系数符号在不同时间窗口里翻转现象动量特征ret_6的系数在一段时间里是正数换到另外几年变成负数甚至显著为负。原因股票市场的因子收益本来就不稳定特别是月度数据里动量效应时强时弱。单一样本外验证窗口里系数符号受少数极端月份影响非常大。解决不要用单一时间窗口的解释去答辩。可以分段训练把2020到2023年每年单独训练一组模型输出“系数热力图”或滚动窗口的AUC曲线说明该特征在不同市场环境下的稳定性。这个展示方式比“我的模型AUC是0.6”更有说服力。6. 让答辩与落地更扎实把三种模型结果整合成一份可解释的月度股票预测报告当你把三个模型都跑完下一步不是堆AUC而是把结果整理成一张能让读者五分钟看懂的表同时也方便自己检查。6.1 生成三模型对齐的预测表先把测试期间每月真实标签和三模型的预测概率放成一列输出CSV之后无论画图还是答辩都直接读这张表。result test_set[[label]].copy() result[month] test_set.index result[lr_prob] lr_model.predict_proba(X_test)[:, 1] result[gb_prob] gb_model.predict_proba(X_test)[:, 1] result[lstm_prob] torch.sigmoid(torch.tensor(X_seq_test, dtypetorch.float32)...).numpy() result.to_csv(monthly_predictions.csv, indexFalse) print(result.tail())这段代码里LSTM的probs需要从前面训练好的model里取为了简短做了省略。实际项目里建议把三份预测结果合并成一个DataFrame列名对齐。输出CSV后先肉眼检查“概率高月份的真实收益是不是也偏高”这一步比任何指标都更能暴露标签错位。6.2 生成答辩PPT三张图项目中附带的答辩PPT最需要三张图ROC曲线对比、月度AUC趋势、混淆矩阵。ROC曲线能在一张图里同时放三个模型呈现给非专业听众最直观。import matplotlib.pyplot as plt from sklearn.metrics import roc_curve plt.figure(figsize(6, 5)) for name, prob in [(LR, result[lr_prob]), (GB, result[gb_prob]), (LSTM, result[lstm_prob])]: fpr, tpr, _ roc_curve(result[label], prob) auc roc_auc_score(result[label], prob) plt.plot(fpr, tpr, labelf{name} (AUC{auc:.3f})) plt.plot([0, 1], [0, 1], linestyle--, colorgray) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.legend() plt.tight_layout() plt.savefig(roc_compare.png, dpi150)这张图保存下来直接放进说明文档和答辩PPT。如果三个模型的ROC曲线挤成一团说明特征信息量不足该回去补特征工程如果逻辑回归和梯度提升接近、LSTM偏低说明线性关系已经捕捉了大头时序记忆帮助不大。6.3 把“单股预测”升级成“月末选股池”最后一个落地价值点是“截面选股”不要只预测一只股票而是对几十只股票做同一套特征预测它们各自的下月上涨概率每个月末选择概率最高的前10%组成一个虚拟组合。这个做法能让单只股票的高噪声互相抵消更接近真实量化策略的流程。具体改动很简单循环每只股票沿用前面构造特征的方法得到每个股票在每个月底的预测概率最后按月份分组排序取每组前10%的股票作为“买入候选池”。答辩时可以加一句这已经不是预测涨跌而是做相对排序AUC不会再因个别股票剧烈波动而失真。我自己的习惯是每次跑完模型立刻把“预测概率、真实标签、月份、模型名称”全部存成CSV单独放在一个history目录下。这样过几天再打开还能说清楚当时的切分日期和训练区间不会满屏参数讲不清来源。这个习惯也分享给你希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站