简介:这份资源面向时间序列预测与机器学习的入门及进阶学习者,用Xgboost算法打通从数据到预测的完整链路,解决时间序列预测与分类两类任务的实战落地问题。包内共3个文件,以2个Python脚本和1个CSV数据集为主,脚本分别承担模型训练、预测与结果保存等职责,CSV提供配套测试数据,压缩包约407KB,轻量易取。目前已有6030人学习下载,具备一定的参考热度。资源覆盖数据分析、特征工程、模型训练与本地保存、未来数值序列生成,以及预测值与实际观测值的对比评估,并附有逐步注释帮助理解代码逻辑。读者可据此掌握梯度提升树在金融、气象、股票等趋势预测场景中的应用思路,同时了解时间序列分类的基本做法,适合边读代码边动手复现,形成对机器学习建模流程的清晰认知。
1. 时间序列预测不是调包:从一份能直接跑的 XGBoost 案例说起
很多人第一次做时间序列,习惯性打开 Prophet 或 ARIMA,结果遇到多特征、缺失值、非线性拐点就卡住。这份 XGBoost 时间序列实战案例走的是另一条路:把序列问题转成监督学习,用梯度提升树同时吃下滞后特征、滑窗统计量和外生变量,预测和分类两条线都给了可运行脚本。它适合已经会 Python、想快速把模型跑通再回头调参的从业者,也适合带课程设计的学生照着复现。下面按「资源是什么、怎么跑、特征怎么造、坑在哪」拆开讲,重点放在能抄作业的代码和参数上。
2. 先看清资源结构:预测与分类两条线怎么分工
2.1 目录与运行入口
拿到压缩包后先别急着点运行,花两分钟看清结构能省掉后面一半的报错。这类案例通常按「数据—特征—模型—评估」四层组织,预测和分类各有一套入口脚本,共用特征工程模块。常见布局如下:
timeseries_xgb/ ├── data/ │ ├── raw/ # 原始序列,csv 为主 │ └── processed/ # 特征工程后的宽表 ├── src/ │ ├── features.py # 滞后、滑窗、日历特征 │ ├── train_reg.py # 回归(预测)入口 │ ├── train_cls.py # 分类入口 │ └── evaluate.py # 指标与残差诊断 ├── notebooks/ │ └── quickstart.ipynb # 点击即可运行的演示 ├── requirements.txt └── README.md入口脚本和 notebook 是两条并行路径:notebook 用来快速看效果,脚本用来改参数、接自己的数据。我一般先跑 notebook 确认环境没问题,再切到src/下改配置,避免一上来就在脚本里调半天发现是依赖版本不对。
2.2 预测任务与分类任务的差别
同样是 XGBoost,回归和分类的目标函数、评估指标、标签构造完全不同,混用是最常见的翻车点。预测任务输出连续值,用reg:squarederror或reg:absoluteerror,看 RMSE、MAE、MAPE;分类任务输出离散标签,用binary:logistic或multi:softprob,看准确率、F1、AUC。分类的标签往往来自对连续值的阈值切分,比如「下一时段销量是否超过均值」,这个切分点定在哪里直接决定正负样本比例,后面会细说。
| 维度 | 预测(回归) | 分类 |
|---|---|---|
| objective | reg:squarederror | binary:logistic / multi:softprob |
| 标签 | 连续值 | 0/1 或类别编码 |
| 主指标 | RMSE / MAE / MAPE | F1 / AUC / 准确率 |
| 典型坑 | 滞后特征泄漏 | 类别不平衡 |
2.3 环境依赖与版本约束
requirements.txt里通常锁了 pandas、numpy、xgboost、scikit-learn、matplotlib 几个包。XGBoost 1.x 和 2.x 在 API 上有差异,比如early_stopping_rounds在 2.x 里从fit()参数挪到了构造器或回调里,直接照旧代码跑会报TypeError。稳妥做法是建独立虚拟环境,按文件里的版本装:
python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txt python -c "import xgboost; print(xgboost.__version__)"版本打印出来先记下,后面调参时early_stopping_rounds的写法要对得上。如果文件没锁版本,我一般手动钉xgboost==1.7.6,这个版本 API 稳定、文档全,新手不容易被新接口绕晕。
3. 把序列转成监督学习:特征工程是成败关键
3.1 滞后特征与滑窗统计量
XGBoost 本身不认识时间顺序,它只认行和列。所以第一步是把「按时间排列的一列数」改造成「每行一个样本、每列一个特征」的宽表。核心是滞后特征(lag)和滑窗统计(rolling)。滞后就是把过去第 k 期的值挪到当前行,滑窗就是过去 n 期的均值、标准差、最大最小。这段逻辑我一般单独写成函数,方便预测和分类复用:
import pandas as pd import numpy as np def make_lag_features(df, target_col, lags=(1, 2, 3, 7, 14), windows=(7, 14)): """把单列序列扩成带滞后和滑窗特征的宽表""" out = df.copy() for lag in lags: out[f"{target_col}_lag{lag}"] = out[target_col].shift(lag) for w in windows: roll = out[target_col].shift(1).rolling(window=w) # shift(1) 防止用到当前值 out[f"{target_col}_roll{w}_mean"] = roll.mean() out[f"{target_col}_roll{w}_std"] = roll.std() out[f"{target_col}_roll{w}_max"] = roll.max() return out.dropna().reset_index(drop=True)这里有两个参数要盯住。lags决定模型能回看多远,周期性强(比如周度规律)就加 7、14;windows决定平滑程度,窗口太大容易把拐点抹平。shift(1)那行是血泪经验:滑窗如果不先 shift,当前行的目标值会混进均值里,模型在训练集上指标漂亮,上线就崩,这就是典型的特征泄漏。
3.2 日历特征与外生变量
纯滞后特征对「周末效应」「月初效应」不敏感,补日历特征成本很低、收益明显。把时间戳拆成星期几、月份、是否月末、是否节假日前后,用 one-hot 或直接数值编码都行。如果有促销、天气、价格这类外生变量,一并拼进宽表,XGBoost 对特征尺度不敏感,不用标准化,但类别型要提前编码。
def add_calendar(df, time_col): t = pd.to_datetime(df[time_col]) df["dow"] = t.dt.dayofweek df["month"] = t.dt.month df["is_month_end"] = t.dt.is_month_end.astype(int) df["is_weekend"] = (t.dt.dayofweek >= 5).astype(int) return df日历特征的价值在于让树模型能学到「同类日子」的共性,而不是死记某一天的具体数值。注意is_month_end这类布尔特征转 int 更稳,部分旧版本对 bool 列处理不一致。
3.3 训练集/验证集切分不能随机
时间序列最忌讳train_test_split(shuffle=True)。随机切分会让未来信息漏进训练集,验证指标虚高。正确做法是按时间点切:前 80% 做训练,后 20% 做验证;或者用滚动窗口做多折验证。分类任务同理,标签按时间顺序生成后再切,不能先切再算标签。
split = int(len(df) * 0.8) train, valid = df.iloc[:split], df.iloc[split:] X_train = train.drop(columns=["target", "date"]) y_train = train["target"] X_valid = valid.drop(columns=["target", "date"]) y_valid = valid["target"]切分点选 0.8 是经验值,序列短可以放到 0.7,序列长且有明显季节性可以留出完整周期做验证。验证集一定要保留最后一段连续时间,这样评估出来的误差才接近真实上线表现。
4. 训练与调参:让 XGBoost 在序列上稳住
4.1 回归入口的参数配置
预测任务的训练脚本核心就是构造DMatrix、设参数、带早停训练。下面这段是可直接改的骨架:
import xgboost as xgb dtrain = xgb.DMatrix(X_train, label=y_train) dvalid = xgb.DMatrix(X_valid, label=y_valid) params = { "objective": "reg:squarederror", "eta": 0.05, # 学习率,小一点更稳 "max_depth": 6, # 树深,序列任务别太深 "subsample": 0.8, # 行采样,抗过拟合 "colsample_bytree": 0.8, # 列采样 "min_child_weight": 3, # 叶子最小样本权重 "seed": 42 } model = xgb.train( params, dtrain, num_boost_round=2000, evals=[(dvalid, "valid")], early_stopping_rounds=50, verbose_eval=100 )eta设 0.05 配合 2000 轮上限和 50 轮早停,是序列任务比较通用的组合;max_depth超过 8 在滞后特征多的时候很容易记住噪声。min_child_weight调大能压住小样本叶子的抖动。如果用的是 XGBoost 2.x,早停要改成构造器参数early_stopping_rounds=50传给xgb.train的callbacks或直接在XGBRegressor里设,跑之前先确认版本。
4.2 分类入口与不平衡处理
分类脚本的差别主要在 objective 和评估。二分类用binary:logistic,输出概率后按阈值转标签;多分类用multi:softprob,记得设num_class。序列分类常遇到正负样本悬殊,比如「异常时段」只占 5%,这时光看准确率没意义,要看 F1 和召回。
params_cls = { "objective": "binary:logistic", "eval_metric": "aucpr", # 不平衡时比 auc 更敏感 "eta": 0.05, "max_depth": 5, "scale_pos_weight": 5, # 负正比约 5:1 时这样设 "subsample": 0.8, "colsample_bytree": 0.8, "seed": 42 }scale_pos_weight按负样本数除以正样本数估,估完再在验证集上微调;eval_metric选aucpr而不是auc,是因为前者对少数类更敏感。阈值不要死用 0.5,拿验证集画 PR 曲线挑一个召回和精确率平衡的点,这个动作能明显改善实际可用性。
4.3 用残差和特征重要性做诊断
训练完别只看一个 RMSE 就收工。残差按时间画出来,看有没有系统性偏移或某段集中偏大;特征重要性看gain排序,确认滞后特征和日历特征确实在起作用。如果某个外生变量重要性异常高,先怀疑它是不是泄漏了未来信息。
from xgboost import plot_importance import matplotlib.pyplot as plt pred = model.predict(xgb.DMatrix(X_valid)) resid = y_valid - pred plt.plot(resid.values) plt.title("residual over time") plt.show() plot_importance(model, importance_type="gain", max_num_features=15) plt.show()残差图如果出现「前段小、后段大」的喇叭形,说明分布漂移,考虑加时间衰减权重或缩短训练窗口;如果某几个点残差特别大,回去查那几行原始数据是不是有录入错误或极端事件。
5. 避坑与排查:这几处翻车最多
5.1 指标好得离谱,上线就崩
现象:验证集 RMSE 低到不合常理,换一批数据误差翻几倍。原因:滑窗特征没 shift,当前目标值漏进了特征;或者切分时随机打乱。解决:所有 rolling 前先shift(1),切分严格按时间顺序,训练完用「最后一段完全没参与训练的数据」再验一次。
5.2 分类正负样本比例失真
现象:模型几乎全预测为多数类,F1 接近 0。原因:标签阈值切得不合理,或者scale_pos_weight没设。解决:先统计标签分布,阈值尽量让正样本占 10%~30%;设scale_pos_weight,评估改用aucpr和 F1,阈值在验证集上单独挑。
5.3 早停参数报 TypeError
现象:照旧教程写xgb.train(..., early_stopping_rounds=50)直接报错。原因:XGBoost 2.x 改了早停接口。解决:确认版本,1.x 用xgb.train的参数,2.x 用callbacks=[xgb.callback.EarlyStopping(rounds=50)],或者统一退回 1.7.x。
5.4 预测值出现负数和异常尖峰
现象:销量、流量这类非负序列预测出负数。原因:reg:squarederror不约束输出范围。解决:预测后做clip(lower=0),或者换reg:absoluteerror降低对极端值的敏感度;尖峰多半是某个滞后特征在特定日期取值异常,回查原始数据。
5.5 换自己的数据后列名对不上
现象:跑通案例后换数据,报KeyError或特征数不一致。原因:脚本里硬编码了列名和滞后阶数。解决:把目标列名、时间列名、lags、windows抽成配置字典放文件头,换数据只改配置;训练和预测共用同一个特征函数,保证列顺序一致。
6. 进阶用法:滚动回测与上线前的最后一道检查
案例跑通只是起点,真正决定这套模型能不能用的是滚动回测。做法是按时间把数据切成多个「训练—验证」窗口,每次用前面所有数据训练、紧接的一段验证,窗口向前滚,最后把各折误差平均。这比单次切分更接近真实上线节奏,也能暴露模型在分布漂移下的稳定性。
def rolling_backtest(df, n_splits=5, valid_size=30): scores = [] for i in range(n_splits): end = len(df) - valid_size * (n_splits - i) train = df.iloc[:end] valid = df.iloc[end:end + valid_size] # 复用前面的特征与训练逻辑 rmse = fit_and_score(train, valid) scores.append(rmse) return np.mean(scores), np.std(scores)n_splits和valid_size按数据量定,序列够长就多切几折,valid_size至少覆盖一个完整周期(比如 7 天或 30 天)。均值看整体水平,标准差看稳定性,标准差大的模型宁可不用。
上线前还有一道检查我每次都强制走:拿最近一段真实数据,完全按线上流程(特征函数、模型文件、阈值)跑一遍预测,和人工核对几个点。这一步能抓出「训练脚本和预测脚本特征顺序不一致」这类隐蔽问题,比任何离线指标都管用。从那以后我每次交付时间序列模型,都先把滚动回测和端到端复跑走一遍再谈效果,省下的返工时间远超这点功夫。希望帮到你。
本文还有配套的精品资源,点击获取