机器学习项目实战指南:从回归到聚类源码包解析
简介面向数据分析师和机器学习初学者的一套源代码合集按九个章节递进展开分别对应回归、分类、决策树与随机森林、支持向量机、聚类、主成分分析降维、神经网络、集成学习及模型调优能够帮助读者把常用算法从理论理解推进到可运行的实际代码。整个压缩包共80个文件大小约54.72MB其中36个ipynb教学笔记是主体另有py/pyc辅助脚本、dot/png可视化图、mp4操作演示视频以及csv数据文件和joblib模型文件适合边看视频边运行代码。已有1054人学习下载。各章代码实例贴近数据分析场景例如媒体投放回归、车辆回归、逻辑回归练习、KNN应用、决策树可视化、随机森林案例、主成分分析案例解析和KMeans聚类等部分还附带树结构图与演示视频。通过学习这些分章节源码可以完整经历数据预处理、特征缩放、模型训练、验证与调优的过程对想提升实践能力的数据分析师是一份很有价值的参考资料。1. 这个源码包能帮你跑通什么从回归到聚类的机器学习源代码经常有人问我机器学习学了几个月算法书翻了好几遍一打开编辑器还是不知道写什么。我最近拆了一个能直接回答这个问题的资源包——《数据分析师专栏中的机器学习源代码》。它把九个章节的 notebook、CSV 数据、dot 树文件、工具函数全放在一起覆盖线性回归、逻辑回归、决策树、朴素贝叶斯、KNN、SVM、随机森林、PCA、K-Means 这条完整链路。它解决的不是“看懂公式”而是“把代码跑起来、把模型训出来、把结果可视化出来”这个真问题。适合刚学完机器学习基础但不会下手写代码的新手也适合带新人做数据项目的带教者当工程模板用。2. 源码包结构与运行环境先把 notebook 和工具模块理清楚2.1 九个章节文件分别对应哪些算法拆包第一步我建议先按 chapter 编号把文件归类再决定从哪条线开始跑。这个包里的文件命名不算规整既有“KNN实例2(带有svm的算法).ipynb”这种长名也有“贝叶斯_高斯.ipynb”“kmean算法.ipynb”这种正常命名还有一堆 dot 文件和 mp4 视频混在目录里第一眼容易看花。我按内容把文件对到算法上chapter1 是线性回归家族包含“线性回归分析-案例1-媒体投放.ipynb”“车辆回归分析模型.ipynb”“多项式回归.ipynb”“L1和L2惩罚.ipynb”和两个时序数据分析文件chapter2 是逻辑回归练习chapter3 是决策树带 titanic_lv2.dot、titanic_lv6.dot、music-dt.dot 这些可视化导出文件还有 dt.mp4 讲解视频chapter4 是朴素贝叶斯伯努利、高斯、多项式三个 notebook 恰好对应三种特征分布假设chapter5 和 chapter6 是 KNN 和 SVM 的组合chapter5 有 KNN实例1到3chapter6 里的“KNN实例2(带有svm的算法).ipynb”说明 KNN 和 SVM 常在同一场景里对比chapter7 回到决策树并引入随机森林附带 bagging.mp4 和 boost.mp4chapter8 是 PCA 案例解析chapter9 是 K-Means 聚类配了 Mall_Customers.csv 零售客户数据。所以尽量别按目录编号线性看。更建议分三条线读回归线读 chapter1 和 chapter2分类线读 chapter3、chapter4、chapter5、chapter6、chapter7无监督线读 chapter8 和 chapter9。包里的 mp4 视频是录制讲解不看不影响代码运行但建议跑完对应 notebook 之后再倍速看一遍能把“随机森林为什么比单棵决策树稳”这类问题讲得更直观。2.2 运行环境与 adspy_shared_utilities 工具模块这份源码对环境要求不高Python 3.8 加 pandas、numpy、scikit-learn、matplotlib 就够跑。从包里的 pyc 文件名 adspy_shared_utilities.cpython-38.pyc 能看出原作者当时用的是 CPython 3.8本地 Python 版本最好不低于 3.8。建议先建一个干净虚拟环境再一次性装齐依赖python -m venv ml-dojo source ml-dojo/bin/activate # Windows 下用 ml-dojo\Scripts\activate pip install pandas numpy scikit-learn matplotlib graphviz这里单独提醒一句graphviz 不能只装 Python 包。pip install graphviz 装的只是客户端本体要另外装。macOS 用 brew install graphvizUbuntu 用 apt install graphvizWindows 去 graphviz.org 下安装包。装完在终端敲 dot -version 能出版本号才算真正配好。再看 adspy_shared_utilities.py。这个文件是从《机器学习基础教程》配套工具函数改出来的公共模块在 KNN 和 PCA 的 notebook 里被反复 import。它主要提供两类函数一类是绘图辅助比如 plot_2d_separator、plot_2d_scores另一类是数据集加载比如 load_data。chapters 5 和 6 目录下有多个版本的 adspy_shared_utilities.py、adspy_shared_utilities2.py还有对应的 cpython-38.pyc 缓存这是原作者调试不同可视化效果时复制改出来的不影响主流程。唯一要注意的是如果某个 notebook 里写的是 from adspy_shared_utilities import plot_2d_separator但当前目录没有这个文件优先去其他 chapter 目录复制一份。pyc 文件不用管它只是编译缓存删掉后 Python 会自动重新生成。第一次跑所有 notebook 前我习惯先执行一段环境自检脚本import sys import sklearn import pandas as pd import numpy as np import matplotlib print(Python:, sys.version.split()[0]) print(pandas:, pd.__version__) print(numpy:, np.__version__) print(scikit-learn:, sklearn.__version__) print(matplotlib:, matplotlib.__version__)这段脚本没有业务逻辑就是做“体检”。scikit-learn 版本和 notebook 记录不一致时某些参数默认值会不同复现结果会有偏差。比如旧版 KNeighborsClassifier 的 n_neighbors 默认是 5新版数据量小会直接报警告这类问题后面避坑章节还会展开。3. 回归与逻辑回归媒体投放、多项式特征与 L1/L2 惩罚3.1 媒体投放回归案例先跑通线性回归的完整链路chapter1 里的“线性回归分析-案例1-媒体投放.ipynb”是整份包最友好的入门文件演示典型的广告投放渠道与销量之间的关系目标变量是连续值所以用线性回归。我的习惯是拿到回归 notebook 先不看结论从数据导入开始重跑一遍确认每一步执行成功再回头读注释不然容易被自带的输出带偏。常见做法是把数据读进 DataFrame先用 df.info() 和 df.describe() 看缺失值和数值范围再调用 sklearn 的 LinearRegression 拟合import pandas as pd from sklearn.linear_model import LinearRegression # 假设数据文件放在同目录列名为 TV、Radio、Newspaper、Sales data pd.read_csv(advertising.csv) X data[[TV, Radio, Newspaper]] y data[Sales] model LinearRegression() model.fit(X, y) print(截距:, model.intercept_) print(系数:, model.coef_) print(R2:, model.score(X, y))这里的参数和输出分别怎么理解intercept_ 是截距coef_ 是每个特征的回归系数score 默认返回 R2 决定系数。R2 越接近 1说明当前特征对销量变化的解释能力越强。如果某份数据的 Newspaper 系数接近 0 且 R2 没有明显提升常见做法是重新检查线性关系假设——线性回归默认特征与目标之间有线性关系、误差独立且同方差违反任一假设R2 都会变得难看。媒体投放数据里 TV 和销量往往呈明显线性关系但 Newspaper 在很多真实数据集里相关性很弱这种时候就不该硬把 Newspaper 留在模型里。源码包里没有直接给出 advertising.csv原 notebook 用的是媒体投放示例数据。本地没有这份 CSV 的话可以自己造一份模拟数据先跑通流程再把文件路径换成真实数据。做回归我建议把 train_test_split 加上虽然原文件里没强调这一步from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 )test_size0.2 表示留出 20% 样本做测试random_state 固定随机种子保证每次切分一致。回归验证不要只看训练集 R2要把测试集 R2 和训练集 R2 对比相差过大就是过拟合。R2 从 0.96 掉到 0.72 这种场景很常见问题往往不在模型而在数据切分不稳定。3.2 多项式回归与 L1/L2 惩罚欠拟合与过拟合的平衡同一章里的“多项式回归.ipynb”和“L1和L2惩罚.ipynb”要连着看。线性回归假设特征和目标之间是直线关系但现实很多问题是非线性的。多项式回归的做法是给原始特征增加高次项把 x 变成 x^2、x^3再用线性回归拟合扩展后的特征from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression poly PolynomialFeatures(degree3, include_biasFalse) X_poly poly.fit_transform(X) model_poly LinearRegression() model_poly.fit(X_poly, y)degree3 表示最高次数到三次方include_biasFalse 是不额外生成常数项 1。多项式阶数不是越高越好degree1 时可能欠拟合degree10 时模型会疯狂震荡把噪声都学进去这就是过拟合。判断方法看训练集和验证集的误差曲线训练集误差一路下降、验证集误差先降后升基本就是过拟合的信号。这时候该看 L1 和 L2 惩罚了。L1 正则对应 LassoL2 正则对应 Ridge两者的共同点是在损失函数后面加惩罚项限制系数不能太大区别是 L1 会把部分系数压成 0起到特征选择作用L2 只是让系数整体变小但不会归零from sklearn.linear_model import Lasso, Ridge lasso Lasso(alpha0.1) lasso.fit(X_poly, y) print(Lasso 系数:, lasso.coef_) ridge Ridge(alpha1.0) ridge.fit(X_poly, y) print(Ridge 系数:, ridge.coef_)alpha 是惩罚强度越大系数被压缩得越狠。调 alpha 不要凭感觉我一般用 GridSearchCV 扫一组对数尺度的候选值比如 np.logspace(-4, 2, 20)。Lasso 的系数里出现大量 0 是正常现象说明这些多项式项不重要。现在面试爱问“L1 为什么能产生稀疏解”其实把这两个 notebook 跑一遍直观看到系数变化比死记答案有用得多。3.3 逻辑回归练习分类任务的评估比训练更重要chapter2 的两个逻辑回归练习文件是“逻辑回归练习1 .ipynb”和“逻辑回归练习#2.ipynb”。注意第一个文件名里有个空格后面带个点加载 notebook 时别找错文件。逻辑回归虽然名字里有“回归”实际解决的是分类问题它通过 sigmoid 函数把线性输出映射到 0 到 1 之间得到一个概率值。跑逻辑回归可以用这段代码from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, stratifyy, random_state42 ) clf LogisticRegression(max_iter1000, C1.0) clf.fit(X_train, y_train) print(训练集准确率:, clf.score(X_train, y_train)) print(测试集准确率:, clf.score(X_test, y_test))两个关键参数要解释max_iter1000 是最大迭代次数数据量大或特征没标准化时默认 100 次可能不收敛会报 ConvergenceWarningC 是正则化强度的倒数C 越小正则化越强。stratifyy 表示按标签比例分层抽样保证训练集和测试集正负样本比例接近。分类任务只看准确率不够如果正负样本不平衡准确率会骗人。源码练习里如果打印混淆矩阵和 F1 分数记得重点看少数类的查准率和查全率而不是只看准确率一个数。逻辑回归对特征尺度敏感跑之前先对 X 做 StandardScaler尤其在用 L1 惩罚时标准化后系数才有可比性。4. 分类与无监督算法实操决策树、朴素贝叶斯、KNN、SVM、PCA、K-Means4.1 决策树与随机森林用 dot 文件反向找过拟合信号chapter3 和 chapter7 都涉及决策树。chapter3 里的“决策树01.ipynb”“决策树02.ipynb”“决策树03.ipynb”是逐步深入的三个练习导出了 titanic.dot、titanic_lv2.dot、titanic_lv6.dot 和 music-dt.dot 这些树结构文件。chapter7 的“决策树01.ipynb”和“随机森林案例演示.ipynb”可以看作同一系列的进阶。决策树的核心是递归切分每次选一个特征和一个阈值让切分后的子节点尽可能“纯”。sklearn 里用 criterion 参数控制纯度指标默认是 gini也可以换 entropyfrom sklearn.tree import DecisionTreeClassifier, export_graphviz tree DecisionTreeClassifier(max_depth3, criteriongini, random_state42) tree.fit(X_train, y_train) export_graphviz( tree, out_filetree_titanic.dot, feature_namesX_train.columns, class_names[0, 1], filledTrue, roundedTrue )max_depth3 限制树的深度最多 3 层目的是防过拟合。不限制的话树会一路长到每个叶子只有一个样本训练集准确率接近 100%测试集一塌糊涂。跑完 export_graphviz 之后在命令行用 dot -Tpng tree_titanic.dot -o tree.png 渲染成图片。源码包里的 titanic.dot、music-dt.dot 都是这么来的。看树的图片时重点看根节点选的是哪个特征根节点特征往往是区分能力最强的那个。如果把 max_depth 从 3 调到 6树会明显变复杂这就是为什么源码里同时放了 titanic_lv2.dot 和 titanic_lv6.dot对比着看能直观理解“深度越大越容易过拟合”。随机森林是多棵决策树的集成用 bagging 思想让每棵树在随机抽样的子数据集上训练最后投票表决。chapter7 里的“随机森林案例演示.ipynb”和 bagging.mp4、boost.mp4 视频讲的就是这个。sklearn 用法很简单from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators100, max_depth5, random_state42, n_jobs-1 ) rf.fit(X_train, y_train)n_estimators 是树的数量100 是常用起点再增大收益递减且训练变慢。n_jobs-1 表示用满所有 CPU 核心。随机森林比单棵决策树稳主要因为它引入两个随机性每棵树用的样本是 bootstrap 抽样每个节点切分时只在部分特征里选。这两个随机性让树与树之间相关性降低投票结果更稳。想看特征重要性直接输出 rf.feature_importances_按分数排序就能知道哪些字段在驱动预测这比翻几十棵树的图像高效得多。4.2 朴素贝叶斯与 KNN从概率假设到距离判据chapter4 里的三个 notebook 是“贝叶斯_伯努利.ipynb”“贝叶斯_高斯.ipynb”“贝叶斯_多项式.ipynb”对应 sklearn 的 BernoulliNB、GaussianNB 和 MultinomialNB。它们的共同理论基础是贝叶斯定理差别只在特征分布假设。GaussianNB 假设每个特征的类条件分布是高斯分布适合连续型特征比如身高、温度、得分。BernoulliNB 假设特征是二值的比如“某个词是否出现”输出 0/1。MultinomialNB 假设特征是计数值适合文本分类的词频统计。选择没有悬念连续数值选 GaussianNB0/1 布尔特征选 BernoulliNB计数特征选 MultinomialNB。选错也能训练但概率估计会失真准确率明显下降。以高斯贝叶斯为例from sklearn.naive_bayes import GaussianNB gnb GaussianNB(var_smoothing1e-9) gnb.fit(X_train, y_train)var_smoothing 是方差平滑项默认 1e-9防止某个特征在训练集里恰好方差为 0 时出现除零错误。数据里有大量重复常数特征时可以调到 1e-6 或 1e-5。朴素贝叶斯的“朴素”在假设特征之间完全独立真实数据里很少成立所以通常当 baseline 用。我一般先拿它跑一遍定准确率下限再上随机森林或 SVM如果后者没比朴素贝叶斯高多少说明数据里有效信号本来有限。KNN 和朴素贝叶斯走的完全是另一条路。源码包 chapter5 的“KNN实例1.ipynb”“KNN实例2.ipynb”“KNN实例3.ipynb”配了 shuiguo.jpg 和 shuiguo.txt是水果图像识别相关的特征文件。KNN 原理简单新样本类别由最近 K 个训练样本投票决定。但 KNN 有个致命前提——特征必须同尺度。如果特征 A 范围是 0 到 100特征 B 范围是 0 到 1距离计算会被特征 A 主导邻居选择就失去意义。跑 KNN 之前的固定动作是标准化from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier scaler StandardScaler() X_train_s scaler.fit_transform(X_train) X_test_s scaler.transform(X_test) knn KNeighborsClassifier(n_neighbors5, weightsuniform) knn.fit(X_train_s, y_train)n_neighbors5 是常用起点但小数据集上应该用交叉验证再选一次比如遍历 1 到 15 的奇数个邻居。weightsuniform 表示所有邻居等权投票改成 distance 则距离越近权重越大。标准化这一步有讲究fit_transform 只对训练集用测试集只用 transform否则测试数据的信息被“偷看”进训练过程验证结果会虚高这在机器学习的应用流程里是基本功级别的问题。4.3 SVM、PCA 与 K-Means从决策边界到降维聚类SVM 和 KNN 经常被放在一起对比因为都需要标准化且都能画出清晰的决策边界。chapter6 的“KNN实例2(带有svm的算法).ipynb”就是这种对比实验。SVM 的目标是找间隔最大的分界面核函数让它能处理线性不可分数据。跑 SVM 时默认用 RBF 核重点调 C 和 gammafrom sklearn.svm import SVC svm SVC(kernelrbf, C1.0, gammascale, random_state42) svm.fit(X_train_s, y_train)C 是误分类惩罚C 越大模型越严格贴合训练集容易过拟合。gamma 控制 RBF 核的影响半径gamma 越大每个样本影响范围越小决策边界越复杂。“scale” 是让 sklearn 根据特征数量自动算初始值适合先跑通流程。如果 SVM 的训练时间异常长优先降特征维度而不是硬调 max_iter。降维和聚类属于无监督线。chapter8 的“PCA案例解析.ipynb”是整份包可视化最强的章节导出过 iris_2_component_PCA.png、standardization.png、PCA_timingNew.png 这些中间图。PCA 把高维数据投影到低维空间投影方向保留最大方差from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X) pca PCA(n_components2, random_state42) X_pca pca.fit_transform(X_scaled) print(解释方差比:, pca.explained_variance_ratio_) print(累计解释方差:, pca.explained_variance_ratio_.sum())n_components2 降到 2 维方便画散点图。解释方差比是关键输出第一个值表示第一主成分保留多少信息第二个是第二主成分两者之和接近 1 说明降维几乎没损失。如果只是为了可视化降到 2 没问题为了压缩特征我一般让累计解释方差超过 0.85 的最小维度或者画碎石图看曲线拐点。源码里 PCA_timingOLD.png 和 PCA_timingNew.png 的对比说明不同实现版本耗时差别很大数据量大时优先考虑 sklearn 里的 randomized solver 或增量 PCA。K-Means 在 chapter9配 Mall_Customers.csv 商场客户数据。跑 K-Means 第一步是选 K很多初学者拍脑袋设 K3正确做法是用肘部法看惯性值from sklearn.cluster import KMeans inertia_list [] for k in range(1, 11): km KMeans(n_clustersk, random_state42, n_initauto) km.fit(X_scaled) inertia_list.append(km.inertia_)n_init 是新版 sklearn 的必填参数控制 K-Means 用多少个不同初始中心跑最后取最优解。老代码只写 KMeans(n_clusters3) 在新版本上会报未来警告建议统一写成 n_initauto 或 n_init10。inertia_ 是每个样本到所属中心距离的平方和K 越大惯性越小曲线拐点处就是相对合理的 K。在 Mall_Customers 数据上只选 Age 和 Spending Score 聚类时拐点通常在 5 左右。还能用 silhouette_score 计算轮廓系数辅助验证轮廓系数在 -1 到 1越接近 1 说明簇内外分离越明显。聚类后画散点图看效果import matplotlib.pyplot as plt plt.figure(figsize(8, 6)) plt.scatter(X_scaled[:, 0], X_scaled[:, 1], ckm.labels_, cmapviridis, alpha0.6) plt.scatter(km.cluster_centers_[:, 0], km.cluster_centers_[:, 1], markerx, colorred, s200) plt.show()km.labels_ 保存每个样本的簇编号km.cluster_centers_ 保存 K 个中心点坐标。画出来的簇边界不干净通常两个原因没做标准化或者 K 选大了把本应连在一起的数据硬拆成两簇。5. 常见问题排查环境、可视化与模型评价的五个坑5.1 import 与文件层面的坑第一个典型问题是导入 adspy_shared_utilities 时 ModuleNotFoundError。现象是打开 KNN 或 PCA 的 notebook第一行 from adspy_shared_utilities import ... 就中断。原因通常是当前 chapter 目录下没有这个 py 文件或者本地 Python 版本和 cpython-38.pyc 缓存不匹配导致加载失败。解决方法是先确认当前工作目录用 os.getcwd() 看一遍再把能找到的 adspy_shared_utilities2.py 复制成 adspy_shared_utilities.py或者直接放到站点包目录下最后删除pycache文件夹后重新运行让 Python 重新生成缓存。pyc 文件是黑匣子别去手动改删掉重跑往往是最快的后悔药。第二个典型问题是决策树导出的 dot 文件中文乱码。现象是 titanic.dot 渲染成图片后特征名或类别名变成方块。原因不是数据出错而是 graphviz 默认字体不支持中文字符。解决有两个方向在 export_graphviz 里加 fontnameSimHei 或 Microsoft YaHei更省事的做法是在数据预处理阶段就把特征列名改成英文比如把“年龄”改成 Age。源码包里导出的 dot 文件名多是英文原作者大概率用的就是英文特征名自己复现时保持这个习惯能省掉不少渲染烦恼。5.2 数据与模型评估的坑第三个典型问题是 PCA 第一主成分占比高得离谱。现象是 explained_variance_ratio_ 第一个值超过 0.98但降维后散点图分不出任何类别。原因几乎可以断定是没做标准化PCA 按方差最大方向投影数值范围大的特征天然占主导。解决方法是先 StandardScaler 再 PCA并检查标准化后的方差是否都在 1.0 附近。另外 PCA 不能处理缺失值数据里有 NaN 要先用 SimpleImputer 填掉再降维。第四个典型问题是 K-Means 聚类结果和业务认知完全不符。现象是跑 Mall_Customers.csv 时每一簇的年龄段和消费分都混在一起轮廓系数低。原因可能是把 Gender 这种类别型字段也塞进了聚类特征K-Means 用欧氏距离不适合直接处理 0/1 变量。解决方法是聚类前只选连续数值特征或者对类别特征做 one-hot 再聚类。另一个原因是 K 没选对要跑完肘部法确认拐点而不是只看一次 km.inertia_ 就下结论。第五个典型问题是随机森林和逻辑回归的结果差距与预想中不同。现象是随机森林训练集准确率高测试集反而不如逻辑回归。原因之一是预处理策略不统一决策树对特征尺度不敏感但逻辑回归敏感如果在同一份代码里复用同一个标准化器逻辑回归会受影响原因之二是随机森林的 n_estimators 太小或 max_depth 太深泛化能力不足。解决方法是两个模型分别用 GridSearchCV 调参用交叉验证得分对比而不是拿单次切分的结果下结论。这类问题在机器学习入门里最典型因为每个算法对预处理和参数的要求都不一样不能一套配置通吃。整体建议是每跑完一个 notebook先把输出清空再从头执行一遍。这个二次执行能暴露隐藏状态——比如某个变量在前面被赋过值后面被继续使用清空后才知道哪行代码真正依赖前面的状态。多环境切换时优先盯住 Python 版本和 scikit-learn 版本这两个不一致会导致大量莫名其妙的警告。6. 从 Notebook 到脚本把专栏代码变成自己的复现工具资源落地的最后一公里是把这些 notebook 转成可复用的 py 脚本。jupyter 自带 nbconvert一行命令就能把 ipynb 里所有代码块拼出来jupyter nbconvert --to script kmean算法.ipynb执行后同目录会生成 kmean算法.py但代码顺序和 notebook 一模一样直接逐行跑通常会出错因为 notebook 允许“先用到变量再定义”这种非顺序依赖。转出来的脚本需要人工整理import 全提到文件顶部关键变量改成函数参数fit、predict、评估三段用空行隔开。整理后我习惯把每个算法封装成独立函数def train_kmeans(df, features, k): from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler X df[features] scaler StandardScaler() X_scaled scaler.fit_transform(X) km KMeans(n_clustersk, random_state42, n_initauto) km.fit(X_scaled) return km, scaler这样写好以后换一份 Excel 或 CSV 也能直接调用函数入口只有数据框、特征列名和 K 三个参数后续调参不用翻整个 notebook。模型训练好后用 joblib 保存import joblib joblib.dump({model: km, scaler: scaler}, kmeans_model.joblib)下次加载只需两行joblib.load 拿回字典再用 scaler.transform 对新样本做同样的标准化最后把结果喂给 km.predict。这样比在 notebook 里反复重跑训练省时间也不会因为下次少跑一个单元格而出错。最后一个习惯是每次调完模型把训练集、验证集的关键指标统一打印比如 train_acc、test_acc、R2、inertia存成 CSV 或直接输出。刚开始我会偷懒只看最后一格准确率后来发现改动一个预处理步骤后模型可能在测试集上从 0.85 掉到 0.79而准确率数字不像 loss 曲线那样容易察觉。只有把每次实验的关键指标都留下来才能及时发现性能下滑。从那以后我每次新建机器学习工程都会先写好这段评估打印逻辑再开始调参也算吃过大亏后养成的习惯。希望这套源码包能让你像拆玩具一样把所有算法跑一遍也帮你少踩几个坑。本文还有配套的精品资源点击获取