news 2026/11/2 17:40:26

XGBoost混合模型股票预测:DWT降噪与ARIMA残差修正实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
XGBoost混合模型股票预测:DWT降噪与ARIMA残差修正实践

简介:这份PDF为兰州理工大学软件工程硕士学位论文,聚焦基于XGBoost的混合模型在股票价格预测中的应用,适合金融数据分析、机器学习与模式识别方向的学习者和研究者参考。资源包共1个PDF文件,大小约3.48MB,内容完整呈现论文封面、目录、绪论、理论基础、模型设计与实验分析等章节。论文从股票预测的背景与难点切入,梳理集成学习中的决策树、CART、GBDT与XGBoost原理,并介绍AR、MA、ARMA、ARIMA等时间序列模型,进而讨论混合模型的构建思路、特征工程与参数调整。实验部分采用MSE、MAE和R²等指标对比不同模型表现,分析非线性关系捕捉、异常点处理与预测稳定性,同时提及过拟合等局限及后续改进方向。已有661人学习,可为相关课题的模型选型、实验设计与论文写作提供较完整的参考框架。

1. 先别急着上深度学习:这套 XGBoost 混合模型把股票预测拆成了三段

很多人做股票预测第一反应是上深度学习,但我拆完这份 XGBoost 混合模型资料后,反而先把 LSTM 放一边。它把问题拆成三段:先用网格搜索把 XGBoost 的参数压到可控区间,再用离散小波变换给价格序列降噪,最后用 ARIMA 抓线性趋势、XGBoost 啃残差。整套实验从单模型基线一路对比到 DWT-ARIMA-GSXGB,评价指标、数据切分和参数寻优都落在同一套流程里。适合想复现金融时序混合模型、补特征工程和实验对比的人,也适合拿它当论文复现或二次开发底座。别把它当交易信号,它更像一张可执行的实验路线图。

2. 从论文目录到可复现流水线:数据、特征、模型、评估四层怎么拆

2.1 资源提供的五层实验结构:不是单个模型,而是一条递进对比链

这份资料最值钱的地方不是某一个模型公式,而是它把实验做成了递进链。第一层是问题定义和理论基础,讲清楚股票价格序列为什么难预测:非平稳、噪声大、线性和非线性混在一起。第二层是 GS-XGBoost,用网格搜索给 XGBoost 找参数组合,先把单模型基线打稳。第三层是 DWT-GS-XGBoost,在进入模型之前用离散小波变换做多尺度分解和降噪,再重构回价格序列。第四层是 DWT-ARIMA-GSXGB,把 ARIMA 对线性趋势的捕捉能力和 XGBoost 对残差非线性关系的拟合能力拼起来。第五层是统一评价和对比,把 RMSE、MAE、MAPE、R² 放在同一张表里看相对排序。

这种结构对二次开发很友好,因为每次只加一个变量。你先跑原始 XGBoost,再跑 GS-XGBoost,能看出网格搜索带来的增益;再加 DWT,能看出降噪有没有用;最后加 ARIMA 残差修正,能看出线性部分被剥离后 XGBoost 是否更专注。很多人一上来就把三个模型揉在一起,结果指标涨了也不知道是谁的功劳,指标跌了更不知道拆哪一块,这种黑匣子式实验在论文复现里很容易翻车。

技术栈方面,常见组合是 Python 打底,pandas 和 numpy 做数据整理,scikit-learn 做切分、网格搜索和评价,xgboost 做回归主模型,statsmodels 做 ARIMA,PyWavelets 做离散小波变换,matplotlib 出对比图。这套栈的好处是每个环节都能单独替换,不绑死某个深度学习框架,跑在普通笔记本上也能完成中小规模实验。XGBoost 对表格特征、缺失值和特征交互比较友好,ARIMA 对线性自相关有明确解释,DWT 能把价格序列里的高频噪声压下去,网格搜索虽然暴力,但胜在可复现,参数空间写进配置文件就能反复跑。

层次常见工具在项目里的职责改动建议
数据层pandas、numpy读取行情、对齐日期、构造目标列换数据源时先统一列名和频率
特征层pandas、numpy滞后特征、滚动统计量、收益率所有特征先 shift,再进模型
分解层PyWavelets小波分解、阈值降噪、重构先固定小波基和层数做消融
模型层xgboost、statsmodelsXGBoost 回归、ARIMA 线性建模先基线后混合,别一次堆满
评估层scikit-learn、matplotlibRMSE、MAE、MAPE、R²、对比图切分方式和口径写进配置

2.2 目录结构:按数据、特征、模型、评估四层拆

我一般会把这类实验拆成四层目录,不把所有逻辑塞进一个 notebook。notebook 适合看数据分布和画图,真正要反复跑的流程放进 src,参数放进 configs,输出放进 outputs。这样你改小波层数、改网格范围、换评价指标时,不用满文件找变量。下面这份目录清单是按论文里的三组模型整理出来的复现骨架,文件职责写得很直白,照着建就能跑。

路径职责备注
data/raw/原始行情文件只读,不在这里改列名
data/processed/清洗后的训练数据保留时间戳和原始价格
configs/params.yaml网格、窗口、小波参数实验口径统一放这里
src/features.py滞后特征、滚动统计量所有 shift 集中管理
src/dwt.py小波分解、阈值、重构注意长度对齐
src/arima_model.pyARIMA 拟合和残差输出阶数可配置
src/xgb_model.pyXGBoost 训练和网格搜索基线模型也走这里
src/train.py串起数据、特征、模型、评估命令行入口
src/evaluate.py指标计算和对比表口径只写一次
outputs/metrics.csv每次实验的指标记录建议加时间戳
outputs/figures/预测对比图、残差图论文插图来源

配置文件不要写得太花,能把关键参数固定住就行。下面这段 YAML 是常见写法,model控制跑哪一档实验,split控制时间序列切分,xgb_grid控制网格搜索范围,dwt控制小波基和层数。你后面做消融实验时,只改一个块,其他块不动,指标才有可比性。

# configs/params.yaml model: gs_xgb # 可选:xgb / gs_xgb / dwt_gs_xgb / dwt_arima_gsxgb target_col: close split: n_splits: 5 test_size: 20 xgb_grid: n_estimators: [200, 400] max_depth: [3, 5] learning_rate: [0.03, 0.1] subsample: [0.8] colsample_bytree: [0.8] dwt: wavelet: db4 level: 3 threshold_scale: 1.0 arima: order: [2, 1, 2]

这段配置的逻辑是把“实验条件”从代码里挪出来。model是实验开关,方便你先跑基线再跑混合模型;n_splits和test_size决定时间序列交叉验证的折数和每折测试长度;xgb_grid是网格搜索的空间,先放小一点,跑通后再扩;dwt里的wavelet和level直接影响降噪强度,threshold_scale是阈值缩放系数,调大压噪更狠,但也可能把有效波动一起削掉;arima.order是 ARIMA 的 p、d、q,先固定一组能收敛的阶数,再谈优化。

2.3 环境准备与最小运行命令

环境不用追求花哨,能稳定复现就行。我一般先建独立虚拟环境,再装基础包,最后用一条命令跑基线。下面这条命令先跑gs_xgb,确认数据读取、特征构造、网格搜索、指标输出整条链路是通的,再去跑dwt_arima_gsxgb。很多人一开始就冲最复杂的混合模型,结果报错堆在 ARIMA 索引对齐上,连基线有没有跑通都不知道,这种顺序很吃亏。

python -m venv .venv source .venv/bin/activate pip install -U pip pip install pandas numpy scikit-learn xgboost statsmodels PyWavelets matplotlib pyyaml # 先跑 GS-XGBoost 基线 python -m src.train --config configs/params.yaml --model gs_xgb # 基线通了再跑 DWT-ARIMA-GSXGB python -m src.train --config configs/params.yaml --model dwt_arima_gsxgb

命令里的--config指向参数文件,--model覆盖配置里的实验开关,这样同一份代码可以跑四档模型。失败时先看三件事:PyWavelets的包名是PyWavelets,导入时写pywt;xgboost和scikit-learn的版本要能对上,网格搜索的 scoring 名称在不同版本里有差异;ARIMA 的预测索引是按训练序列长度往后推的,start和end写错会直接报越界。先把gs_xgb跑通,再逐层加 DWT 和 ARIMA,排查成本会低很多。

提示:每次实验把outputs/metrics.csv另存一份带时间戳的文件,别让新结果覆盖旧结果。混合模型的对比结论全靠这些记录撑着。

3. GS-XGBoost 参数寻优:网格搜索和时间序列交叉验证的落地细节

3.1 为什么先做 GS-XGBoost:基线不稳,后面混合模型都是空中楼阁

GS-XGBoost 是整份资料的第一个可运行模型,也是后面所有对比的锚点。XGBoost 本身参数不少,学习率、树深、采样比例、正则项都会影响拟合能力。手工拍参数不是不行,但论文复现最怕“参数靠感觉”,今天跑一个结果,明天换台机器又对不上。网格搜索的价值不是一定找到全局最优,而是把参数搜索过程固定下来,让每次实验都能复现同一组候选组合和同一套评价口径。

参数空间不要一上来铺满。常见做法是先粗后细:第一轮只调max_depth、learning_rate、n_estimators,把模型容量定在一个合理区间;第二轮再动subsample、colsample_bytree、min_child_weight;第三轮才看gamma、reg_alpha、reg_lambda这些正则项。股票价格序列样本量通常不大,树太深、学习率太小、树太多,训练集 loss 会很好看,测试集一塌糊涂。网格搜索如果一次铺几千组,跑得慢不说,还容易在验证集上过拟合。

参数作用常用范围改动建议
n_estimators树的数量200~800先固定中等值,配合学习率调
max_depth单棵树深度3~7股票数据先从 3 或 4 起
learning_rate学习率0.01~0.1越小越稳,但树要更多
subsample行采样比例0.7~0.9防过拟合,别低于 0.6
colsample_bytree列采样比例0.7~0.9特征不多时别压太狠
min_child_weight叶子最小样本权重1~5样本少时往上加
gamma分裂最小损失下降0~0.3指标抖就加一点
reg_alpha / reg_lambdaL1 / L2 正则0~0.1 / 1~5先动 lambda,再动 alpha

3.2 参数网格与时间序列交叉验证代码

下面这段代码是 GS-XGBoost 的核心骨架。TimeSeriesSplit保证每折训练集都在测试集前面,GridSearchCV负责遍历参数组合,XGBRegressor用平方误差做回归。注意scoring用负 RMSE,因为 scikit-learn 的网格搜索默认是分数越大越好,误差指标要取负号。refit=True表示找到最优参数后自动用全量训练集重训一次,后面直接拿search.best_estimator_预测。

import numpy as np from xgboost import XGBRegressor from sklearn.model_selection import GridSearchCV, TimeSeriesSplit def train_gs_xgb(X_train, y_train, n_splits=5, test_size=20): # 时间序列交叉验证:训练集始终在测试集之前 tscv = TimeSeriesSplit(n_splits=n_splits, test_size=test_size) # 先给小网格,跑通后再扩 param_grid = { "n_estimators": [200, 400], "max_depth": [3, 5], "learning_rate": [0.03, 0.1], "subsample": [0.8], "colsample_bytree": [0.8], "min_child_weight": [1, 3], } base = XGBRegressor( objective="reg:squarederror", random_state=42, n_jobs=-1, ) search = GridSearchCV( estimator=base, param_grid=param_grid, scoring="neg_root_mean_squared_error", cv=tscv, n_jobs=-1, verbose=1, refit=True, ) search.fit(X_train, y_train) return search.best_estimator_, search.best_params_, search.best_score_

这段代码的逻辑是先用TimeSeriesSplit把训练集切成“前面训练、后面验证”的滚动折,再用GridSearchCV对每个参数组合做交叉验证,最后按负 RMSE 选最优。n_splits控制折数,折数太少结果不稳,折数太多每折训练样本变短;test_size控制每折验证长度,要和你的预测周期匹配,做 5 日预测就别把test_size设成 1。param_grid里先只放 6 个参数,跑通后再加gamma、reg_alpha、reg_lambda。random_state=42固定随机性,n_jobs=-1用满 CPU,verbose=1能看到搜索进度。

注意:别用KFold(shuffle=True)做股票时序交叉验证。随机打乱会把未来样本混进训练集,指标会虚高,后面上真实回测会直接翻车。

3.3 训练、预测与评价指标口径

模型训完只是第一步,指标口径不统一,后面三组模型没法比。下面这段评价函数把 RMSE、MAE、MAPE、R² 放在一起算,safe_mape对真实值接近零的样本做了保护。股票价格水平值一般不会为零,但如果你把目标换成收益率或差分序列,零值就会出现,直接算 MAPE 会炸出无穷大。R² 在非平稳价格序列上容易虚高,因为价格水平本身带趋势,模型只要跟着趋势走就能拿到不错的拟合优度,所以它只能作为辅助指标,不能单独拿来下结论。

import numpy as np from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score def safe_mape(y_true, y_pred, eps=1e-8): y_true = np.asarray(y_true, dtype=float) y_pred = np.asarray(y_pred, dtype=float) mask = np.abs(y_true) > eps if mask.sum() == 0: return np.nan return np.mean(np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])) * 100 def evaluate(y_true, y_pred): rmse = np.sqrt(mean_squared_error(y_true, y_pred)) mae = mean_absolute_error(y_true, y_pred) mape = safe_mape(y_true, y_pred) r2 = r2_score(y_true, y_pred) return {"RMSE": rmse, "MAE": mae, "MAPE": mape, "R2": r2}

这段代码里,RMSE对大误差更敏感,MAE更直观,MAPE是百分比误差,方便跨价格量级比较,R²看解释方差比例。eps是零值保护阈值,别设太大,否则会把正常小值也过滤掉。真正做模型对比时,我一般会同时记录价格水平和一阶差分两套指标:价格水平看整体拟合,差分看方向变化。只报价格水平 R²,很容易把“跟着趋势走”误判成“模型学到了规律”。

3.4 实验结果怎么读:别只看 R²

GS-XGBoost 的实验结果要放在同一张表里读,不能只看单个数字。第一列写模型,第二列写切分方式,第三列写预测目标,后面才是 RMSE、MAE、MAPE、R²。原始 XGBoost 和 GS-XGBoost 的差别只在参数有没有搜过,其他条件必须完全一致:同一段数据、同一个特征集、同一个训练测试切分、同一个随机种子。否则你看到指标涨了,可能只是测试集换了一段行情。

模型切分方式预测目标RMSEMAEMAPER²备注
XGBoostTimeSeriesSplit收盘价记录记录记录记录手工参数
GS-XGBoostTimeSeriesSplit收盘价记录记录记录记录网格搜索
GS-XGBoostTimeSeriesSplit一阶差分记录记录记录记录看方向变化

读结果时先看相对排序,再看绝对数值。如果 GS-XGBoost 在价格水平上提升明显,但一阶差分上没提升,说明网格搜索主要优化了趋势拟合,对短期波动帮助有限。如果训练集误差很低、测试集误差很高,先别怀疑数据不够,先查特征有没有泄漏、切分有没有打乱、标准化有没有在全量数据上做。参数搜索的最优组合只是验证集上的最优,不是未来行情上的最优,这一点在股票时序里尤其要认。

4. DWT 与 ARIMA-GSXGB 混合:降噪、残差修正和特征防泄漏

4.1 DWT-GS-XGBoost 的数据流:先分解再重构,别把未来信息混进来

DWT 这一层的作用是把价格序列拆成不同频率的分量,低频保留趋势,高频压噪声,再用阈值处理细节系数,最后重构回一条更平滑的序列。常见做法是用pywt.wavedec做多层分解,对细节系数做软阈值,再用pywt.waverec重构。小波基选db4比较常见,层数一般 2 到 4 层,层数太多会把有效波动也当成噪声削掉。模式用periodization可以保证重构长度和原序列对齐,省掉后面截断的麻烦。

import numpy as np import pywt def dwt_denoise(series, wavelet="db4", level=3, threshold_scale=1.0): arr = np.asarray(series, dtype=float) # periodization 模式便于重构后长度对齐 coeffs = pywt.wavedec(arr, wavelet=wavelet, level=level, mode="periodization") # 用最高频细节系数估计噪声水平 sigma = np.median(np.abs(coeffs[-1])) / 0.6745 uthresh = threshold_scale * sigma * np.sqrt(2 * np.log(len(arr))) # 近似分量保留趋势,细节分量做软阈值 coeffs[1:] = [pywt.threshold(c, value=uthresh, mode="soft") for c in coeffs[1:]] rec = pywt.waverec(coeffs, wavelet=wavelet, mode="periodization") return rec[: len(arr)]

这段代码的关键参数是wavelet、level和threshold_scale。wavelet换小波基会改变分解形状,db4是常用起点;level控制分解层数,层数越多低频越平滑;threshold_scale控制阈值强度,调大压噪更狠,调小保留更多波动。sigma用最高频细节系数的中位数估计噪声,uthresh是通用阈值。注意这段代码是在整条序列上估计阈值,严格来说有前视偏差,做论文复现可以先用它跑通,做真实回测时要改成滚动窗口:每个窗口只用窗口内数据估阈值,或者训练集估一次阈值,测试集沿用训练阈值。

4.2 ARIMA 抓线性趋势,XGBoost 啃残差:DWT-ARIMA-GSXGB 的组合方式

DWT-ARIMA-GSXGB 的组合思路不复杂:先用 DWT 降噪,再用 ARIMA 拟合线性部分,把 ARIMA 的残差交给 GS-XGBoost 去学非线性关系,最后把 ARIMA 预测和 XGBoost 残差预测相加。这样 ARIMA 负责它擅长的自相关和趋势,XGBoost 负责它擅长的特征交互和残差修正。下面这段代码是核心骨架,order先用配置文件里固定的一组阶数,跑通后再做阶数搜索。arima.predict的start和end是按训练序列长度往后推的,测试集预测从len(y_train)开始。

import numpy as np from statsmodels.tsa.arima.model import ARIMA from xgboost import XGBRegressor def fit_arima_xgb_hybrid(y_train, y_test, X_train, X_test, order=(2, 1, 2)): # ARIMA 拟合线性部分 arima = ARIMA(y_train, order=order).fit() arima_train_pred = arima.predict(start=0, end=len(y_train) - 1) arima_test_pred = arima.predict( start=len(y_train), end=len(y_train) + len(y_test) - 1, ) # 残差交给 XGBoost resid_train = np.asarray(y_train) - np.asarray(arima_train_pred) xgb = XGBRegressor( objective="reg:squarederror", n_estimators=400, max_depth=4, learning_rate=0.05, subsample=0.8, colsample_bytree=0.8, random_state=42, n_jobs=-1, ) xgb.fit(X_train, resid_train) resid_test_pred = xgb.predict(X_test) # 线性预测 + 非线性残差修正 final_pred = np.asarray(arima_test_pred) + np.asarray(resid_test_pred) return final_pred, arima_test_pred, resid_test_pred

这段代码里,order=(2,1,2)是 ARIMA 的 p、d、q,d=1表示做一阶差分,适合非平稳价格序列;arima_train_pred是样本内拟合值,用来算训练残差;arima_test_pred是样本外预测;resid_train是 ARIMA 没解释掉的部分;xgb用特征矩阵去拟合残差;final_pred是两部分相加。这里最容易出错的是索引对齐:X_train和y_train长度必须一致,X_test和y_test长度必须一致,DWT 重构后的序列要截到原长度,否则 ARIMA 和 XGBoost 的残差对不上。跑失败时先打印四个长度,别急着调参。

4.3 特征工程与防泄漏:滞后特征、滚动统计量怎么造

混合模型再复杂,特征泄漏一样白搭。股票时序最常见的泄漏是拿当天收盘价预测当天收盘价,或者用全量数据算滚动均值再切分。我一般会把所有特征统一放在features.py里,价格类特征一律先shift(1),滚动统计量也先 shift 再 rolling,确保第 t 行特征只能看到 t-1 及以前的信息。下面这段代码给了滞后价格、滚动均值、滚动标准差和一日收益率,列名清楚,后面换数据源也好改。

import pandas as pd def make_lag_features(df, target_col="close", lags=(1, 2, 3, 5, 10), windows=(5, 10, 20)): out = df.copy() for lag in lags: out[f"lag_{lag}"] = out[target_col].shift(lag) # 先 shift 再 rolling,避免把当前值算进窗口 for w in windows: out[f"roll_mean_{w}"] = out[target_col].shift(1).rolling(w).mean() out[f"roll_std_{w}"] = out[target_col].shift(1).rolling(w).std() out["ret_1"] = out[target_col].pct_change().shift(1) return out.dropna()

这段代码的lags控制回看天数,windows控制滚动窗口长度,shift(1)是所有防泄漏动作的核心。dropna()会删掉前面因为滞后产生的空行,所以特征构造完之后再切训练测试集,别先切再各自构造,否则两边行数对不上。滚动标准差可以当波动率代理,收益率特征可以缓解价格非平稳。特征不是越多越好,滞后阶数铺太大,样本量会被吃掉,XGBoost 也容易在噪声特征上过拟合。

4.4 三组模型怎么排同一张表

对比表要把“每次只加一个变量”写清楚。原始 XGBoost 是纯非线性基线;GS-XGBoost 只加网格搜索;DWT-GS-XGBoost 在 GS-XGBoost 前面加小波降噪;DWT-ARIMA-GSXGB 再加 ARIMA 残差修正。表格里除了指标,还要写输入序列、线性建模、非线性建模和关键参数,这样别人看表就知道差异在哪。

模型输入线性部分非线性部分关键参数评价重点
XGBoost原始价格+特征无XGBoost手工参数基线
GS-XGBoost原始价格+特征无GS-XGBoost网格搜索参数增益
DWT-GS-XGBoost小波降噪后价格+特征无GS-XGBoost小波基、层数、阈值降噪增益
DWT-ARIMA-GSXGB小波降噪后价格+特征ARIMAGS-XGBoost 拟合残差ARIMA 阶数、XGB 参数混合增益

读这张表时先看消融顺序。如果 DWT 加进去后 RMSE 反而变大,先别否定小波,检查层数是不是太深、阈值是不是太狠、重构长度有没有截对。如果 ARIMA 残差修正后指标变差,检查 ARIMA 阶数是不是拍脑袋定的,残差是不是还有明显自相关。混合模型不是堆得越多越好,每一层都要在验证集上证明自己有用,不然就是给自己增加排查成本。

5. 避坑与排查:股票时序项目最容易翻车的五个地方

5.1 数据切分与泄漏类

现象:交叉验证分数很漂亮,一上样本外测试就崩,RMSE 突然翻倍,预测曲线明显滞后。
原因:用了随机KFold或shuffle=True,把未来样本混进训练集;或者在全量数据上做标准化、算滚动统计量、估小波阈值,测试集信息提前漏进训练过程。
解决:时间序列一律用TimeSeriesSplit或自己写 walk-forward,所有统计量只在训练窗口内 fit,测试窗口只 transform。小波阈值也一样,训练集估一次,测试集沿用,别在整条序列上重新估。下面这段 walk-forward 骨架可以直接替换随机切分。

import numpy as np def walk_forward_splits(n_samples, train_size, test_size): start = train_size while start + test_size <= n_samples: yield np.arange(0, start), np.arange(start, start + test_size) start += test_size

这段代码按时间顺序滚动产出训练索引和测试索引,train_size是初始训练窗口,test_size是每次向前推进的测试窗口。它不保证每折训练集等长,但保证训练永远在测试之前。你可以把它包成cv传给网格搜索,也可以在外层循环里手动训练和评估。排查泄漏时先打印每折的训练最大日期和测试最小日期,确认没有重叠。

5.2 小波与 ARIMA 的模型类问题

现象:DWT 降噪后模型反而更差,或者 ARIMA 残差图里还有明显周期。
原因:小波层数太深,把有效波动当噪声削掉;阈值缩放系数太大,细节系数被压平;ARIMA 阶数靠猜,残差不是白噪声,XGBoost 拟合的是还有结构的残差。
解决:小波先固定db4,层数从 2 开始,一层一层加,每加一层看验证集 RMSE;阈值系数先设 1.0,再试 0.5 和 1.5,别一次跳太大。ARIMA 先用 ADF 检验看差分阶数,再看 ACF/PACF 定 p 和 q,残差画自相关图,残差还有峰就加阶数或换模型。混合模型里 ARIMA 不是越复杂越好,残差越接近白噪声,XGBoost 越容易学到东西。

现象:网格搜索跑了几百组,最优参数在验证集上很好,测试集一塌糊涂。
原因:参数空间铺太大,验证集被反复使用,等价于在验证集上过拟合;树太深、学习率太小、树太多,训练集拟合过度。
解决:先小网格后大网格,每轮只动 2 到 3 个参数;加early_stopping_rounds或直接限制n_estimators;固定random_state,每次实验只改一个变量。别把“网格越大越专业”当真理,股票时序样本少,参数搜索的边际收益很快会掉下来。

5.3 工程复现与指标口径类

现象:换一台机器跑,指标对不上;或者 MAPE 突然变成inf。
原因:随机种子没固定,数据列顺序变了,特征构造顺序变了;MAPE 分母出现零或接近零的值,收益率序列里很常见。
解决:配置文件里固定random_state,数据读进来先按日期排序再按列名排序,特征构造函数只在一个文件里改。MAPE 用safe_mape过滤接近零的真实值,同时记录 RMSE 和 MAE,别只盯一个百分比指标。每次实验把配置文件、数据版本、指标结果一起存档,后面写对比表才不会对不上账。

提示:把“切分方式、预测目标、评价指标”写进实验记录模板,比事后回忆可靠。股票时序实验最怕口径漂移,跑着跑着就不知道在比什么了。

6. 二次开发:把三模型流水线改成自己的实验台

这套资料最适合二次开发的地方,是它已经把“基线—调参—降噪—残差混合”拆成了可替换模块。你可以先换数据源,把收盘价目标换成收益率或对数差分,观察模型在平稳序列上的表现;再换分解方法,把 DWT 换成经验模态分解或变分模态分解,比较不同降噪路径对残差的影响;最后换基模型,把 XGBoost 换成 LightGBM 或 CatBoost,保持 ARIMA 残差框架不变。每次只动一个模块,其他模块锁死,指标才有解释力。

验证方法我一般用 walk-forward 加固定口径。下面这段骨架把训练窗口、测试窗口和模型训练串起来,外层滚动,内层只做时间序列验证。你可以把它包成函数,传入不同的模型工厂,跑同一张对比表。

def run_walk_forward(X, y, model_factory, train_size=200, test_size=20): records = [] for train_idx, test_idx in walk_forward_splits(len(y), train_size, test_size): X_train, X_test = X.iloc[train_idx], X.iloc[test_idx] y_train, y_test = y.iloc[train_idx], y.iloc[test_idx] model = model_factory() model.fit(X_train, y_train) pred = model.predict(X_test) records.append(evaluate(y_test, pred)) return records

这段代码的model_factory是模型工厂,返回任何带fit和predict的对象;train_size和test_size控制滚动窗口;evaluate沿用前面的统一指标函数。改造时最容易犯的错是只改模型不改切分,结果新旧模型不在同一口径上比较。我的习惯是先把切分和评价写进配置,再

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/2 17:40:14

域群运营机器人技术架构与实战应用解析

1. 域群运营机器人核心价值解析在大规模社群运营中&#xff0c;人工管理往往面临三大痛点&#xff1a;消息响应延迟、规则执行不统一、运营数据碎片化。域群运营机器人正是为解决这些问题而生的自动化工具&#xff0c;其核心价值体现在三个维度&#xff1a;第一是标准化管理能力…

作者头像 李华
网站建设 2026/11/2 17:37:52

RAG系统构建实战:从知识库到检索增强生成

1. RAG系统构建概述检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;简称RAG&#xff09;是当前大模型应用中最热门的技术方向之一。它通过将外部知识检索与生成模型相结合&#xff0c;有效解决了传统大模型存在的"幻觉"问题和知识更新滞后等痛点…

作者头像 李华
网站建设 2026/11/2 17:37:39

PowerShell -nop 参数详解:跳过 Profile 加载的原理、应用与安全实践

1. 从一个命令行参数说起&#xff1a;为什么值得单独聊-nop如果你平时在终端里敲过 PowerShell&#xff0c;大概率见过别人在快捷方式、计划任务或者某些脚本启动器里写上一长串参数&#xff0c;其中就有-nop。第一次看到它的人往往会愣一下&#xff1a;这仨字母到底干嘛的&…

作者头像 李华
网站建设 2026/11/2 17:35:21

物联网平台选型指南:JetLinks与Enjoy-iot对比分析

1. 物联网平台选型的关键考量因素在工业物联网和智慧城市快速发展的今天&#xff0c;选择一款合适的物联网平台成为企业数字化转型的关键决策。面对市场上众多的物联网平台产品&#xff0c;技术决策者需要从多个维度进行综合评估。JetLinks和Enjoy-iot作为国内两款主流的物联网…

作者头像 李华
网站建设 2026/11/2 17:32:56

openCode与Oh My OpenCode智能体生态解析

1. 项目概述&#xff1a;openCode与Oh My OpenCode智能体生态在开发者工具领域&#xff0c;openCode正逐渐成为新一代集成开发环境的代表。与其配套的Oh My OpenCode插件体系通过四大核心智能体——Sisyphus&#xff08;持续集成专家&#xff09;、Prometheus&#xff08;监控告…

作者头像 李华
网站建设 2026/11/2 17:32:56

openCode智能体生态解析:AI驱动的开发新范式

1. 项目概述&#xff1a;openCode与Oh My OpenCode智能体生态 在开发者工具领域&#xff0c;openCode正逐渐成为新一代集成开发环境的标杆。它通过模块化架构和开放插件体系&#xff0c;为开发者提供了高度可定制的工作流。而Oh My OpenCode作为其官方扩展套件&#xff0c;最引…

作者头像 李华