简介这份资源围绕鸢尾花数据集的KNN分类实验展开面向正在学习机器学习基础、需要完成课程作业或入门实战的读者。包内共1个文件为Python脚本压缩包约5KB轻量易读可直接运行并对照修改。内容覆盖从数据获取到模型评估的完整链路先用箱式图观察样本分布再以两种方式做特征预处理并按80%与20%随机划分训练集和测试集随后基于5折交叉验证在K3至9之间选择近邻数以总体预测错误率为指标绘制K值与错误率的关系曲线最后在测试集上评价模型生成混淆矩阵并计算各类别的查准率、查全率、F1分值以及宏平均指标。已有328人学习适合希望理解KNN调参流程、掌握分类评估指标计算方式的初学者也可作为实验报告与代码复现的参考模板。1. iris_KNN.rar 里到底装了什么从一份压缩包拆出可复现的 KNN 分类流程你拿到一个叫iris_KNN.rar的压缩包解压后大概率看到的是 iris 数据、一份 KNN 脚本、以及按 k 折切分训练/测试集的代码。它解决的不是“KNN 是什么”这种教科书问题而是“我手上这份 iris 数据怎么用 KNN 跑出一个能解释、能复现、能改参数的分类结果”。适合两类人刚接触 knn 算法案例、需要照着交作业或做实验的新手以及想把 iris 数据集当成基线快速验证特征工程或距离度量改动的熟手。核心链路只有四步读数据、切 k 折、训练 KNN、在测试集/预测集上出指标。后面每一章都围绕这条链路展开把参数、坑和验证方法讲透。2. iris 数据集与 KNN 的选型理由为什么它俩总被绑在一起2.1 iris 数据集的四个特征与三个类别iris 数据集一共 150 条样本三个类别各 50 条setosa、versicolor、virginica。每条样本四个特征花萼长度、花萼宽度、花瓣长度、花瓣宽度单位都是厘米。这个数据集的特殊性在于setosa 和另外两类在花瓣长度上几乎线性可分而 versicolor 和 virginica 在二维平面上有重叠。KNN 作为基于距离的算法恰好能把这个重叠区域的决策边界暴露出来——你调 k 值、换距离度量指标会明显抖动这正是它适合做教学基线的理由。很多人第一次跑 iris 会得到 0.95 以上的准确率然后以为模型没问题。但如果你只做一次随机切分这个数字没有统计意义。常见做法是固定随机种子或者直接上 k 折交叉验证。iris_KNN.rar这个命名里带k fold说明作者至少意识到了单次切分的不可靠。我一般会先把数据加载进来确认没有缺失值和异常编码再做后续切分。from sklearn.datasets import load_iris import numpy as np iris load_iris() X, y iris.data, iris.target print(样本数:, X.shape[0], 特征数:, X.shape[1]) print(类别分布:, np.bincount(y)) print(特征范围:, X.min(axis0), X.max(axis0))这段代码做三件事确认样本量和特征维度、检查类别是否均衡、看特征量纲是否接近。iris 四个特征都在厘米级别量纲一致所以不做标准化也能跑。但如果你后面换成其他数据集量纲差异会直接毁掉欧氏距离的公平性这是 KNN 最容易被忽略的前提。2.2 KNN 在 iris 上的决策逻辑与 k 值影响KNN 的预测过程很直白对新样本计算它到所有训练样本的距离取最近的 k 个按多数投票决定类别。k1 时决策边界极度贴合训练点训练集准确率 100%但测试集容易受噪声影响k 增大边界变平滑偏差上升、方差下降。iris 只有 150 条k 取太大比如 50会把整个类别的先验比例带进来导致少数类被淹没。我一般先在 1 到 20 之间扫一遍 k看交叉验证均值和标准差的变化曲线。如果 k3 到 k7 之间指标平稳就选中间值如果某个 k 突然掉下去通常是某个折里的局部样本分布被过度放大。iris_KNN.rar里如果只写了一个固定 k建议你至少补一个循环验证否则换一批测试集结论就可能翻车。from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import cross_val_score for k in range(1, 21): knn KNeighborsClassifier(n_neighborsk) scores cross_val_score(knn, X, y, cv5, scoringaccuracy) print(fk{k:2d} 均值{scores.mean():.4f} 标准差{scores.std():.4f})cross_val_score的cv5表示 5 折交叉验证scoringaccuracy是分类准确率。输出里重点看均值和标准差的比值均值高且标准差小说明这个 k 稳定均值高但标准差大说明结果依赖切分方式需要固定随机种子或增加折数。这段代码不涉及训练集/测试集的手动切分适合先摸清 k 的大致范围。3. 把 iris 切成 k 折训练集、测试集与预测集的边界3.1 k 折交叉验证的切分逻辑与代码实现k 折交叉验证把数据分成 k 份每次取其中一份做验证其余 k-1 份做训练循环 k 次后取平均。它的价值在于每个样本都当过一次验证数据指标不再依赖某一次随机切分。iris_KNN.rar标题里的k fold和测试集预测集其实指向两个不同阶段交叉验证用于选参数最终评估需要留出一个从未参与训练的测试集。我通常的做法是先用分层 k 折选 k 值和距离度量再单独留 20% 做最终测试。分层的意思是每个折里三类样本比例保持一致避免某一折全是 setosa。下面这段代码同时展示分层切分和交叉验证的衔接方式。from sklearn.model_selection import StratifiedKFold, train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score, classification_report # 先留出最终测试集stratify 保证类别比例 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 在训练集内部做分层 k 折 skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) knn KNeighborsClassifier(n_neighbors5) fold_scores [] for train_idx, val_idx in skf.split(X_train, y_train): knn.fit(X_train[train_idx], y_train[train_idx]) pred knn.predict(X_train[val_idx]) fold_scores.append(accuracy_score(y_train[val_idx], pred)) print(各折准确率:, [f{s:.4f} for s in fold_scores]) print(平均准确率:, np.mean(fold_scores))test_size0.2表示测试集占 20%random_state42保证每次切分结果一致stratifyy是分层的关键。StratifiedKFold的shuffleTrue会在切分前打乱顺序避免原始数据按类别排序导致折内分布偏移。循环里每次重新fit确保验证折没有参与训练。最终测试集只在最后用一次不能拿它反复调参否则测试集就变成了验证集。3.2 测试集预测与预测集输出的区别测试集是有标签的用来算准确率、召回率、混淆矩阵预测集通常指没有标签、只输出预测类别的数据。iris_KNN.rar标题里把“测试集预测集”并列说明作者可能既做了评估也做了新样本预测。这两步的代码结构不同评估需要y_true和y_pred对比预测只需要predict或predict_proba输出结果。我一般会在最终评估后把模型保存下来再对无标签数据做批量预测。如果预测集的特征顺序和训练集不一致KNN 会直接给出错误结果而且不会报错。这是血泪经验特征列顺序错位是静默失败指标看起来正常实际全错。# 最终评估 knn_final KNeighborsClassifier(n_neighbors5) knn_final.fit(X_train, y_train) y_pred knn_final.predict(X_test) print(测试集准确率:, accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred, target_namesiris.target_names)) # 模拟无标签预测集必须保持特征顺序一致 X_new X_test[:5] y_new knn_final.predict(X_new) print(预测集输出:, iris.target_names[y_new])classification_report会输出每个类别的精确率、召回率和 F1比单一准确率更能暴露类别不平衡问题。X_new这里直接取测试集前五条做演示实际使用时要用X_new.columns或特征名列表和训练集对齐。如果预测集有缺失值KNN 无法处理需要先填充或删除。4. 避坑与排查iris KNN 跑不通时先看这五条4.1 现象准确率异常高或异常低原因异常高通常是数据泄露比如测试集参与了训练或者用全部数据做了交叉验证后再报告测试集准确率异常低通常是特征顺序错位、标签编码错误或者 k 值取到了类别数量的整数倍导致投票平局。解决检查fit和predict的数据来源确认测试集从未进入训练流程打印X_train.shape和X_test.shape确认特征列顺序一致k 值避免取 3 的倍数iris 三类平局时 sklearn 会选索引较小的类别。4.2 现象交叉验证标准差很大原因折数太少或者没有分层某一折里某个类别样本极少。iris 只有 150 条5 折时每折 30 条分层后每类 10 条已经比较紧。如果shuffleFalse且原始数据按类别排序第一折可能全是 setosa。解决用StratifiedKFold并开启shuffleTrue固定random_state如果标准差仍然大增加到 10 折但注意每折验证样本变少指标波动本身会变大。4.3 现象预测集输出全是同一类原因预测集特征量纲和训练集不一致比如训练集是厘米预测集是毫米或者预测集特征列顺序被打乱或者 k 值过大多数投票被先验类别主导。解决打印训练集和预测集的前几行做对比用sklearn.preprocessing.StandardScaler在训练集上拟合后分别转换训练集和预测集k 值从 3 到 7 重新扫一遍。4.4 现象换距离度量后结果剧烈变化原因iris 特征量纲一致欧氏距离和曼哈顿距离差异不大。如果换成马氏距离或余弦距离需要检查数据是否适合。余弦距离关注方向而非绝对距离iris 的四个特征都是正数余弦距离会压缩差异。解决默认用欧氏距离如果要做距离度量对比固定 k 值和切分方式只改metric参数观察交叉验证均值变化。4.5 现象模型保存后重新加载预测结果不一致原因保存模型时没有保存训练数据的特征顺序和类别编码映射或者用了pickle但 sklearn 版本不一致。解决保存模型时同时保存iris.target_names和特征名列表加载后先用训练集的一条样本验证预测结果是否和保存前一致跨版本部署时固定 sklearn 版本或者用joblib替代pickle。5. 把 KNN 从 iris 推到可用距离权重、概率输出与阈值调整5.1 距离加权投票与概率校准默认 KNN 是等权投票近邻不管多近都算一票。weightsdistance让距离越近的邻居权重越大通常能提升边界样本的预测稳定性。在 iris 上versicolor 和 virginica 的重叠区域会受益。但距离加权对异常值更敏感如果某个训练点标错它的权重会被放大。我一般先跑等权再跑距离加权对比交叉验证均值和标准差如果距离加权没有明显提升就保持等权少一个参数少一个坑。from sklearn.neighbors import KNeighborsClassifier for weights in [uniform, distance]: knn KNeighborsClassifier(n_neighbors5, weightsweights) scores cross_val_score(knn, X, y, cv5, scoringaccuracy) print(fweights{weights:8s} 均值{scores.mean():.4f} 标准差{scores.std():.4f})weightsuniform是等权weightsdistance是距离倒数加权。输出里如果两者均值差在 0.01 以内选等权如果距离加权均值高且标准差没有变大可以选距离加权。predict_proba在距离加权下输出的概率更平滑适合需要置信度的场景。5.2 用预测概率做阈值调整predict_proba返回每个类别的概率默认取最大概率的类别。如果业务上对某一类的召回率要求更高可以手动调阈值。比如把 virginica 的预测阈值从 0.5 降到 0.4让更多样本被判为 virginica提高召回但降低精确率。iris 三类均衡阈值调整空间不大但这个技巧在真实数据里很实用。knn KNeighborsClassifier(n_neighbors5, weightsdistance) knn.fit(X_train, y_train) proba knn.predict_proba(X_test) # 默认取最大概率类别 y_pred_default knn.classes_[np.argmax(proba, axis1)] # 手动调整如果 virginica 概率超过 0.4 就判为 virginica y_pred_adjusted y_pred_default.copy() virginica_idx list(knn.classes_).index(2) mask proba[:, virginica_idx] 0.4 y_pred_adjusted[mask] 2 print(默认准确率:, accuracy_score(y_test, y_pred_default)) print(调整后准确率:, accuracy_score(y_test, y_pred_adjusted))knn.classes_是类别编码顺序np.argmax取每行最大概率的索引。调整阈值后准确率可能下降但 virginica 的召回率会上升。实际项目里要根据业务代价决定阈值不能只看准确率。这段代码在 iris 上只是演示真实数据里需要画精确率-召回率曲线来选阈值。5.3 一个我常用来验证模型是否真的学会了的技巧把训练集和测试集的准确率放在一起看。如果训练集 100%、测试集 95%说明 k 太小模型记住了训练点如果训练集 96%、测试集 94%说明模型泛化正常如果训练集 90%、测试集 89%说明 k 太大或特征区分度不够。iris 上 k1 时训练集必然 100%所以不要用训练集准确率来选 k。我习惯在交叉验证循环里同时记录训练折和验证折的准确率差值超过 5 个百分点就警惕过拟合。最后一句话是我自己的习惯每次跑完 KNN先把random_state、k 值、weights、折数四个参数写进实验记录再去看指标。少了任何一个下周复现时就会多花半小时找原因。希望帮到你。本文还有配套的精品资源点击获取