第 6 章 · 01 ML 工作流 本节摘要:本节讲机器学习的端到端工作流,以及在 ML4T(机器学习用于交易)中的特化。机器学习不是「丢数据进模型」一步到位,而是一条由数据划分、特征选择、模型训练、超参调优、交叉验证、误差评估串起来的流水线。本节用 sklearn 的 KNN 回归/分类做演示载体,讲清五件事:数据要按目标做对数变换让长尾变对称、用 Pipeline 把预处理和模型绑在一起防止泄露、用交叉验证做超参搜索、用回归/分类误差指标度量、用学习曲线和验证曲线诊断欠/过拟合。重点强调:金融 ML 的每一步都要严防「未来数据进入训练」。 内容来源:原项目 ,汉化并套用体系化模板。 ⚠️ 风险提示:本节用房价数据(King County)做演示,概念同样适用金融。
本节摘要:本节讲机器学习的端到端工作流,以及在 ML4T(机器学习用于交易)中的特化。机器学习不是「丢数据进模型」一步到位,而是一条由数据划分、特征选择、模型训练、超参调优、交叉验证、误差评估串起来的流水线。本节用 sklearn 的 KNN 回归/分类做演示载体,讲清五件事:数据要按目标做对数变换让长尾变对称、用 Pipeline 把预处理和模型绑在一起防止泄露、用交叉验证做超参搜索、用回归/分类误差指标度量、用学习曲线和验证曲线诊断欠/过拟合。重点强调:金融 ML 的每一步都要严防「未来数据进入训练」。
内容来源:原项目
ch06/01_machine_learning_workflow.ipynb,汉化并套用体系化模板。
⚠️ 风险提示:本节用房价数据(King County)做演示,概念同样适用金融。金融数据有强时序结构和低信噪比,工作流的每一步都要比房价数据更小心。
阅读完本节,你应当能够:
一个完整的 ML 项目包含六个阶段:
| 阶段 | 关键动作 | 常见陷阱 |
|---|---|---|
| 数据 | 清洗、缺失、异常值 | 幸存者偏差、回填未来信息 |
| 特征 | 变换、编码、筛选 | 用未来算特征(泄露) |
| 划分 | 训练/验证/测试 | 随机切时序数据(泄露) |
| 训练 | 选模型、fit | 没标准化距离类模型 |
| 调优 | GridSearch/RandomSearch | 在测试集上调参(泄露) |
| 评估 | 多指标 + 学习曲线 | 只看 R² 不看残差 |
💡 核心心法:每一步的核心问题都是「这里有没有偷看未来?」。金融 ML 八成的「神奇高收益」都来自某种形式的泄露。
资产价格、房价这类数据常有长尾右偏,直接回归会被极端值带偏。先对目标取对数让分布对称:
import numpy as np X_all = house_sales.drop('price', axis=1) y = np.log(house_sales.price) # 关键:对数变换
💡 对数变换的另一好处:误差变成「百分比误差」而非绝对误差,更符合金融「收益率」语义。预测完用
np.exp还原到原始尺度。
第 02 节会详讲互信息。这里先用它从所有特征里挑出与目标最相关的 top 10:
from sklearn.feature_selection import mutual_info_regression mi_reg = pd.Series(mutual_info_regression(X_all, y), index=X_all.columns).sort_values(ascending=False) X = X_all.loc[:, mi_reg.iloc[:10].index]
互信息比相关系数更强:它能捕捉非线性关系。这是金融因子筛选的关键工具(详见第 02 节)。
KNN 这类基于距离的模型对特征尺度敏感,必须标准化。关键陷阱:如果先在全数据上 fit_transform,再切训练/测试,就把测试集的均值方差泄露给了训练。正确做法是把 scaler 和模型绑进 Pipeline,让交叉验证在每个 fold 内独立 fit/transform:
from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsRegressor pipe = Pipeline([('scaler', StandardScaler()), ('knn', KNeighborsRegressor(n_neighbors=15))])
Pipeline 把「标准化 + 训练」绑成一个整体,cross_val_score 会对每个 fold 用该 fold 的训练数据 fit scaler、再 transform 测试数据,严格无泄露。
KNN 的核心超参是 n_neighbors(邻居数)。手写循环搜索也行,但 sklearn 的 GridSearchCV 更标准——它内置交叉验证:
from sklearn.model_selection import GridSearchCV from sklearn.metrics import make_scorer, mean_squared_error def rmse(y_true, pred): return np.sqrt(mean_squared_error(y_true=y_true, y_pred=pred)) rmse_score = make_scorer(rmse) param_grid = {'knn__n_neighbors': tuple(range(5, 101, 5))} estimator = GridSearchCV(estimator=pipe, param_grid=param_grid, cv=5, scoring=rmse_score) estimator.fit(X=X, y=y)
注意参数名是 'knn__n_neighbors'——Pipeline 内部模型参数用「步骤名__参数名」寻址。cv_results_ 含每个 fold 的分数,可画 validation curve 找最优 k。
from sklearn.metrics import (mean_squared_error, mean_absolute_error, median_absolute_error, explained_variance_score) scores = dict( rmse=np.sqrt(mean_squared_error(y_true=y, y_pred=y_pred)), mean_ae=mean_absolute_error(y_true=y, y_pred=y_pred), median_ae=median_absolute_error(y_true=y, y_pred=y_pred), r2=explained_variance_score(y_true=y, y_pred=y_pred), )
| 指标 | 含义 | 抗异常值 |
|---|---|---|
| RMSE | 均方根误差,放大大误差 | 差 |
| MAE | 平均绝对误差 | 中 |
| MedAE | 中位绝对误差 | 强 |
| R² | 解释方差占比 | 中 |
金融里 RMSE 常被极端收益污染,MedAE 和 IC(信息系数)更稳健。
二分类常用 ROC-AUC(对类别不平衡和阈值无关)、precision/recall、F1。KNN 分类:
y_binary = (y > y.median()).astype(int) # 把回归目标二分类 param_grid = {'knn__n_neighbors': tuple(range(5, 151, 10))} estimator = GridSearchCV(pipe, param_grid, cv=5, scoring='roc_auc')
⚠️ 金融里「涨/跌」二分类的正负样本常接近 1:1,但预测「大涨」(top 5%)时严重不平衡,要用 AUC 或 precision@k 而非 accuracy。
yellowbrick 库把这两张图封装好:
from yellowbrick.model_selection import ValidationCurve, LearningCurve val_curve = ValidationCurve(KNeighborsRegressor(), param_name='n_neighbors', param_range=n_neighbors, cv=5, scoring=rmse_score) val_curve.fit(X, y) l_curve = LearningCurve(KNeighborsRegressor(n_neighbors=best_k), train_sizes=np.arange(.1, 1.01, .1), scoring=rmse_score, cv=5) l_curve.fit(X, y)
步骤名__参数名。下一节,我们深入互信息——一种能捕捉非线性依赖的特征选择工具,补足相关系数的短板。