ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python Machine Learning 第6章实战指南:模型评估与超参数调优的最佳实践

Python Machine Learning 第6章实战指南:模型评估与超参数调优的最佳实践 示例工程机器学习深度学习【免费下载链接】python-machine-learning-book-2nd-editionThe Python Machine Learning (2nd edition) book code repository and info resource项目地址https://gitcode.com/gh_mirrors/py/python-machine-learning-book-2nd-edition点击查看免费下载本指南以《Python Machine Learning第2版》第6章为基础围绕机器学习建模中最关键的一环——如何科学地评估模型性能并调优超参数——展开。文中所有代码均来自本仓库 code/ch06/ch06.py 与 ch06.ipynb并以威斯康星乳腺癌WDBC数据集为实战对象。读完本文你将掌握 Pipeline 流水线、k-fold 交叉验证、学习曲线与验证曲线、网格搜索与嵌套交叉验证、混淆矩阵与 ROC 曲线以及类别不平衡问题的处理手段构建出一套可复用的模型评估与调优工作流。第6章导览第6章聚焦于一个几乎所有机器学习项目都会遇到的问题模型在训练集上表现好不代表泛化能力强。因此需要一套严谨的方法论来回答我的模型到底行不行、参数怎么调、指标怎么选。本章的完整脉络如下与 ch06.py 顶部的章节大纲一一对应用 Pipeline 精简工作流加载 Breast Cancer Wisconsin 数据集、在 Pipeline 中组合转换器transformer与估计器estimator用 k-fold 交叉验证评估模型性能holdout 方法回顾、k-fold 交叉验证用学习曲线与验证曲线调试算法诊断偏差bias与方差variance问题、处理过拟合与欠拟合用网格搜索微调模型超参数网格搜索、嵌套交叉验证进行算法选择考察不同的性能评估指标混淆矩阵、精确率与召回率优化、ROC 曲线、多分类评分指标处理类别不平衡class imbalance小结。本仓库为本章提供了两种可执行载体ch06.ipynbJupyter Notebook可逐步执行并内嵌图表输出与 ch06.py纯 Python 脚本可用任意文本编辑器查看。此外 tests/test_notebooks.py 中的test_ch06会通过jupyter nbconvert --execute完整执行该 notebook保证示例代码可复现。环境准备与运行方式安装 Jupyter Notebook本章示例以 Jupyter Notebook 为推荐载体可以逐格执行代码并把所有输出含绘图集中在一份文档中。安装方式有两种使用 Anaconda 发行版时执行conda install jupyter notebook使用 pip 时执行pip install jupyter notebook详见 code/ch01/README.md 的环境说明。安装后进入code/ch06目录执行jupyter notebook浏览器会打开一个窗口默认运行于http://localhost:8888/在其中选择ch06.ipynb即可逐格运行。依赖库与版本运行本章代码需要的核心 Python 包及其最低版本按 code/ch01/README.md 的记录为NumPy 1.12.1SciPy 0.19.0scikit-learn 0.18.1matplotlib 2.0.2pandas 0.20.1notebook 的环境记录显示其作者使用 CPython 3.7.1 scikit-learn 0.21.3 运行通过因此建议使用 Python 3 环境并保证上述版本不低于所列数值。用 Pipeline 精简建模工作流加载 Breast Cancer Wisconsin 数据集本章采用威斯康星乳腺癌诊断WDBC数据集。按 code/ch06/wdbc.names.txt 的说明该数据集包含 569 个样本、32 个属性ID、诊断标签、30 个实数特征类别分布为 357 个良性benign、212 个恶性malignant无缺失值。30 个特征是从细针穿刺FNA细胞核数字图像中计算的半径、纹理、周长、面积、平滑度、紧凑度、凹度、凹点、对称性、分形维数这 10 个量的均值、标准误与最差值worst三组统计量。ch06.py 中从 UCI 仓库读取数据若在线源临时不可用可改用仓库内自带的本地副本wdbc.datadf pd.read_csv(https://archive.ics.uci.edu/ml/ machine-learning-databases /breast-cancer-wisconsin/wdbc.data, headerNone) # 若 UCI 仓库暂时不可用取消下一行注释以从本地路径加载 # df pd.read_csv(wdbc.data, headerNone)随后把特征与标签分离并用LabelEncoder把字符串标签M恶性/B良性编码为数值X df.loc[:, 2:].values # 第 2 列之后为 30 维特征 y df.loc[:, 1].values # 第 1 列为诊断标签 le LabelEncoder() y le.fit_transform(y) le.classes_ # array([B, M]) le.transform([M, B]) # array([1, 0])注意编码结果恶性M编码为 1良性B编码为 0这一约定在后续混淆矩阵与 ROC 的分析中扮演正类定义的角色。按分层抽样的方式切分训练集与测试集保持类别比例保证训练/测试集中恶性样本占比一致X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.20, stratifyy, random_state1)在 Pipeline 中组合转换器与估计器许多实际场景中特征标准化、降维、模型训练必须按固定顺序执行。Pipeline 将这一串步骤封装为一个整体fit/predict调用会自动贯穿所有环节避免数据泄露例如缩放器只在训练数据上拟合再对测试数据做同样的变换。ch06.py 构造了标准化 → PCA 降维 → 逻辑回归的流水线pipe_lr make_pipeline(StandardScaler(), PCA(n_components2), LogisticRegression(random_state1)) pipe_lr.fit(X_train, y_train) y_pred pipe_lr.predict(X_test) print(Test Accuracy: %.3f % pipe_lr.score(X_test, y_test))make_pipeline会自动为每个步骤生成小写类名加序号的命名如standardscaler、pca、logisticregression后面网格搜索中就会使用这类命名来引用具体参数见下文svc__C的写法。使用 06_01.png 可以帮助直观理解 Pipeline 的两阶段流程先对训练数据执行fit含缩放、降维、学习得到预测模型再对测试数据调用predict输出类标签。用 k-fold 交叉验证评估模型性能holdout 方法回顾简单的 holdout 方法把数据划分为训练集、验证集、测试集用训练集训练模型用验证集反复调整超参数最后用从未参与训练的测试集评估最终性能。它的局限在于一次随机划分的结果方差较大评估结论对哪部分数据恰好进入训练集过于敏感。k-fold 交叉验证k-fold 交叉验证把训练数据等分成 k 个不相交的折fold轮流把其中一折作为验证集、其余 k-1 折作为训练集共训练 k 次最终以 k 次验证分数的均值与标准差来报告性能。06_02.png 展示了 holdout 划分与验证集在调参中的角色。ch06.py 先用手写循环展示逐折过程再用cross_val_score一行完成等价逻辑# 手写循环版逐折打印类别分布与准确率 kfold StratifiedKFold(n_splits10, random_state1).split(X_train, y_train) scores [] for k, (train, test) in enumerate(kfold): pipe_lr.fit(X_train[train], y_train[train]) score pipe_lr.score(X_train[test], y_train[test]) scores.append(score) print(Fold: %2d, Class dist.: %s, Acc: %.3f % (k1, np.bincount(y_train[train]), score)) print(\nCV accuracy: %.3f /- %.3f % (np.mean(scores), np.std(scores))) # cross_val_score 版一行完成 10 折交叉验证 scores cross_val_score(estimatorpipe_lr, XX_train, yy_train, cv10, n_jobs1) print(CV accuracy scores: %s % scores) print(CV accuracy: %.3f /- %.3f % (np.mean(scores), np.std(scores)))关键参数说明cv10折数 k。k 越大单次训练数据越多、评估越稳定但计算成本线性上升StratifiedKFold保证每一折的类别比例与整体一致对类别不平衡的数据尤为重要n_jobs1并行核数可设为-1使用全部 CPU 核以加速与普通KFold相比分层 k-foldStratifiedKFold在分类任务中能显著降低因随机划分导致的评估波动。用学习曲线与验证曲线调试算法用学习曲线诊断偏差与方差学习曲线绘制训练集大小 → 训练/验证准确率的关系是诊断偏差-方差问题的标准工具。对同一个固定模型此处的pipe_lr只做标准化 L2 逻辑回归不再降维按不同规模的训练子集重复 10 折交叉验证并绘制均值与标准差带pipe_lr make_pipeline(StandardScaler(), LogisticRegression(penaltyl2, random_state1)) train_sizes, train_scores, test_scores learning_curve( estimatorpipe_lr, XX_train, yy_train, train_sizesnp.linspace(0.1, 1.0, 10), # 10% 到 100% 的训练样本比例 cv10, n_jobs1) train_mean np.mean(train_scores, axis1) train_std np.std(train_scores, axis1) test_mean np.mean(test_scores, axis1) test_std np.std(test_scores, axis1)随后的绘图代码ch06.py用蓝色实线画训练准确率、绿色虚线画验证准确率并用fill_between画出 ±1 标准差带高偏差欠拟合训练与验证准确率都很低且彼此接近曲线趋于平坦——说明模型容量不足增加数据量也难有明显改善高方差过拟合训练准确率远高于验证准确率且二者之间存在宽大的沟——说明模型记住了训练数据需要正则化、更多数据或更简单的模型健康形态两条曲线随样本增多逐渐收敛并趋于一致。用验证曲线处理过拟合与欠拟合验证曲线固定训练数据、扫描单个超参数帮助定位正则化强度的合适区间。本章扫描逻辑回归的 L2 正则化系数CC 越小正则化越强param_range [0.001, 0.01, 0.1, 1.0, 10.0, 100.0] train_scores, test_scores validation_curve( estimatorpipe_lr, XX_train, yy_train, param_namelogisticregression__C, # Pipeline 内步骤参数用 步骤名__参数名 引用 param_rangeparam_range, cv10)注意param_namelogisticregression__C当估计器被包在 Pipeline 中时scikit-learn 用步骤名__参数名的双下划线语法定位内部步骤的参数。绘图时使用plt.xscale(log)把横轴设为对数刻度ch06.py因为 C 的取值跨越多个数量级。解读方法C 过小强正则化时两者准确率同时走低 → 欠拟合C 过大弱正则化时训练准确率高、验证准确率回落 → 过拟合选择验证准确率最高且与训练准确率差距最小的 C 区间。用网格搜索微调模型通过网格搜索调优超参数网格搜索GridSearchCV在预定义的参数组合上执行交叉验证自动选出使评分最优的参数组合。本章对标准化 SVM的 Pipeline 同时搜索线性核与 RBF 核两种配置pipe_svc make_pipeline(StandardScaler(), SVC(random_state1)) param_range [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0] param_grid [{svc__C: param_range, svc__kernel: [linear]}, {svc__C: param_range, svc__gamma: param_range, svc__kernel: [rbf]}] gs GridSearchCV(estimatorpipe_svc, param_gridparam_grid, scoringaccuracy, cv10, n_jobs-1) gs gs.fit(X_train, y_train) print(gs.best_score_) print(gs.best_params_)要点解读param_grid传的是列表而非字典两个字典分别代表线性核只调 C与RBF 核同时调 C 和 gamma两组搜索空间二者取并集避免为线性核浪费 gamma 的搜索组合scoringaccuracy指定优化目标下文会换成自定义评分器cv10表示每组参数做 10 折交叉验证n_jobs-1使用全部 CPU 并行结果对象暴露best_score_交叉验证最优得分与best_params_最优参数组合。得到最优参数后直接使用gs.best_estimator_评估测试集即可clf gs.best_estimator_ print(Test accuracy: %.3f % clf.score(X_test, y_test))ch06.py 特别注释说明无需手动clf.fit(X_train, y_train)因为GridSearchCV默认refitTrue会在搜索结束后自动用最优参数在完整训练集上重新拟合best_estimator_。用嵌套交叉验证进行算法选择网格搜索在训练集上做交叉验证选参数如果再用同一份数据报告最终性能会引入优化偏差——测试结果被参数选择过程泄漏。嵌套交叉验证nested cross-validation把调参与评估分层外层 k 折负责评估泛化性能内层折负责参数搜索。gs GridSearchCV(estimatorpipe_svc, param_gridparam_grid, scoringaccuracy, cv2) scores cross_val_score(gs, X_train, y_train, scoringaccuracy, cv5) print(CV accuracy: %.3f /- %.3f % (np.mean(scores), np.std(scores)))这里cross_val_score的外层cv5把数据切成 5 份每一份轮流做验证而其内部的GridSearchCV用cv2在剩余数据上搜索参数。同样的流程可以比较不同算法——ch06.py 用DecisionTreeClassifier搜索max_depth的取值与 SVM 的嵌套交叉验证得分对比从而在同一评估协议下完成算法选择。06_07.png 直观展示了这种外层循环评估 内层循环调参的嵌套结构。考察不同的性能评估指标阅读混淆矩阵仅看准确率可能被类别不平衡掩盖。混淆矩阵按真实类别 × 预测类别列出四种计数。先在训练集上拟合 Pipeline 并在测试集上预测pipe_svc.fit(X_train, y_train) y_pred pipe_svc.predict(X_test) confmat confusion_matrix(y_truey_test, y_predy_pred) print(confmat)由于此前LabelEncoder把良性编码为 0、恶性编码为 1在默认排序0 在前下矩阵的解读为结合 ch06.py 的结论模型正确分类了 71 个良性样本真阴性TN与 40 个恶性样本真阳性TP同时把 1 个良性误判为恶性假阳性FP把 2 个恶性误判为良性假阴性FN。若希望真阴性显示在矩阵右下角、真阳性显示在左上角可显式传入labels参数调整顺序confmat confusion_matrix(y_truey_test, y_predy_pred, labels[1, 0])优化分类模型的精确率与召回率在恶性乳腺肿瘤这类场景中漏诊假阴性的代价极高单纯优化准确率并不合理。精确率precision、召回率recall与 F1 分数分别刻画预测为正类中有多少是真的、正类中有多少被找出及二者的调和平均print(Precision: %.3f % precision_score(y_truey_test, y_predy_pred)) print(Recall: %.3f % recall_score(y_truey_test, y_predy_pred)) print(F1: %.3f % f1_score(y_truey_test, y_predy_pred))默认以标签 1恶性为正类。若要改变正类定义或把 F1 作为网格搜索的优化目标可用make_scorer包装自定义评分器scorer make_scorer(f1_score, pos_label0) c_gamma_range [0.01, 0.1, 1.0, 10.0] param_grid [{svc__C: c_gamma_range, svc__kernel: [linear]}, {svc__C: c_gamma_range, svc__gamma: c_gamma_range, svc__kernel: [rbf]}] gs GridSearchCV(estimatorpipe_svc, param_gridparam_grid, scoringscorer, cv10, n_jobs-1) gs gs.fit(X_train, y_train) print(gs.best_score_) print(gs.best_params_)绘制 ROC 曲线ROC 曲线以假阳性率FPR为横轴、真阳性率TPR为纵轴反映分类器在所有可能阈值下的判别能力曲线下面积AUC是阈值无关的综合度量。本章用 3 折分层交叉验证重复绘制并取均值曲线pipe_lr make_pipeline(StandardScaler(), PCA(n_components2), LogisticRegression(penaltyl2, random_state1, C100.0)) X_train2 X_train[:, [4, 14]] # 只取两个特征便于可视化 cv list(StratifiedKFold(n_splits3, random_state1).split(X_train, y_train)) mean_tpr 0.0 mean_fpr np.linspace(0, 1, 100) for i, (train, test) in enumerate(cv): probas pipe_lr.fit(X_train2[train], y_train[train]).predict_proba(X_train2[test]) fpr, tpr, thresholds roc_curve(y_train[test], probas[:, 1], pos_label1) mean_tpr interp(mean_fpr, fpr, tpr) mean_tpr[0] 0.0 roc_auc auc(fpr, tpr) plt.plot(fpr, tpr, labelROC fold %d (area %0.2f) % (i1, roc_auc))绘图部分ch06.py还会叠加三条参考线random guessing对角线虚线代表完全随机猜测AUC 0.5mean ROC三折 TPR 的均值曲线及其 AUC黑色虚线perfect performance经过 (0,1) 与 (1,1) 的阶梯线点线代表理想分类器。曲线越向左上角弯曲、AUC 越接近 1.0说明模型能在低假阳性率下取得高真阳性率predict_proba输出概率后由roc_curve扫描所有阈值生成曲线因此该指标不依赖某个固定决策阈值。多分类评分指标当面对多于两个类别时需要说明如何聚合各类别的指标。ch06.py 展示了用average参数控制聚合方式的示例pre_scorer make_scorer(score_funcprecision_score, pos_label1, greater_is_betterTrue, averagemicro)average的常见取值包括micro把所有类别的 TP/FP 汇总后计算受大类样本数影响、macro各类别指标先算后平均平等对待每个类别、weighted按各类样本占比加权平均。选择哪种聚合方式取决于业务上是否要求小类别与大众类获得同等关注。处理类别不平衡现实数据中正负样本常常严重失衡。本章演示了多数类欠采样 少数类过采样的思路先构造一个极端不平衡的子集保留全部 357 个良性样本、只取 40 个恶性样本然后观察一个永远预测为多数类的基线模型会得到多高的准确率借此说明准确率在类别不平衡下具有误导性X_imb np.vstack((X[y 0], X[y 1][:40])) y_imb np.hstack((y[y 0], y[y 1][:40])) y_pred np.zeros(y_imb.shape[0]) # 全部预测为良性 np.mean(y_pred y_imb) * 100 # 基线准确率依旧很高随后用自助法bootstrap对少数类做有放回重采样过采样直到两类样本数一致print(Number of class 1 samples before:, X_imb[y_imb 1].shape[0]) X_upsampled, y_upsampled resample(X_imb[y_imb 1], y_imb[y_imb 1], replaceTrue, n_samplesX_imb[y_imb 0].shape[0], random_state123) print(Number of class 1 samples after:, X_upsampled.shape[0]) X_bal np.vstack((X[y 0], X_upsampled)) y_bal np.hstack((y[y 0], y_upsampled))参数说明replaceTrue表示有放回抽样n_samples指定目标样本数这里设为多数类数量使两类完全均衡random_state固定随机种子保证可复现。重采样后再次计算全部预测为良性的准确率会明显下降从而暴露单纯用准确率评估不平衡数据的陷阱——正确做法是配合本章前面的精确率、召回率、F1、ROC/AUC 等指标综合评估。小结第6章建立了一套完整的模型评估与超参数调优工作流用 Pipeline 封装数据变换与模型训练以防止数据泄露用分层 k-fold 交叉验证获得稳健的性能估计用学习曲线与验证曲线定位偏差/方差问题并确定正则化强度用网格搜索与嵌套交叉验证在避免优化偏差的前提下寻找最优参数、比较算法再用混淆矩阵、精确率/召回率/F1、ROC/AUC 从不同业务视角评估模型最后针对类别不平衡问题给出重采样与指标选择的处理建议。本章所有代码均可在 ch06.py 与 ch06.ipynb 中直接运行验证数据集本地副本位于 code/ch06/wdbc.data数据说明见 code/ch06/wdbc.names.txt。这套方法同样适用于本仓库第7章及后续章节中的分类与深度学习模型是构建可信赖机器学习系统的基石。赞分享示例工程机器学习深度学习【免费下载链接】python-machine-learning-book-2nd-editionThe Python Machine Learning (2nd edition) book code repository and info resource项目地址https://gitcode.com/gh_mirrors/py/python-machine-learning-book-2nd-edition点击查看免费下载相关推荐Python Machine Learning 第 6 章实践指南用 Pipeline、交叉验证与网格搜索打磨模型评估和超参数调优Python Machine Learning 第 6 章实践指南用 Pipeline、交叉验证与网格搜索打磨模型评估和超参数调优 本指南基于《Python机器学习教程模型评估与优化Machine Learning with PyTorch and Scikit-Learn超参数调优终极指南模型评估与优化Machine Learning with PyTorch and Scikit Learn超参数调优终极指南 在机器学习项目中模型评估与优化示例工程机器学习深度学习PredictionIO 评估模块实战指南超参数调优、评估指标与最佳引擎部署PredictionIO 评估模块实战指南超参数调优、评估指标与最佳引擎部署 本篇指南围绕 PredictionIO一个面向开发者和 ML 工程师的机器学习机器学习后端大数据上一篇PaddleNLP 中 FNet 模型建模详解基于傅里叶变换的无注意力编码器实现下一篇颠覆传统标注体验让屏幕交互效率提升10倍的开源工具ppInk创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进