十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

模式识别实验Python代码全攻略:贝叶斯、KNN、聚类与PCA可运行实现

模式识别实验Python代码全攻略:贝叶斯、KNN、聚类与PCA可运行实现 简介面向《模式识别》课程学习者这份基于Python的实验代码包提供了贝叶斯分类器性别分类、Fisher线性判别、KNN近邻分类和PCA人脸识别等经典实验的完整可运行代码。每个实验均配有对应Python脚本和实验报告文档便于对照算法原理理解实现细节适合需要快速搭建实验环境、完成课程作业或复习模式识别知识的学生使用。包体共466个文件其中16个py文件为主程序400个bmp为人脸图像样本数据另有txt数据文件、docx实验报告和xml工程配置文件等大小约5.7MB目录结构清晰运行方便。当前已有1632人学习下载该资源评价普遍认为其性价比高能帮助解决一门实验课并生成规范实验报告。1. 模式识别实验从看得懂到跑得通为什么照着网上的代码抄都会报错又到了模式识别课程交实验的季节。我见过太多同学从网上找到一份基于 python 的模式识别实验可运行代码兴奋地粘进 IDE按下回车然后对着满屏红字报错发呆——问题八成不在算法而在库版本、数据形状、随机种子这些看不见的地方。这篇文章不是给你一份神秘代码包而是把模式识别实验里最高频的几类代码——贝叶斯分类器、KNN、感知机、K-Means 聚类、PCA 降维——全部写成可以直接复制运行的 Python 脚本并把每个参数怎么调、哪里会翻车、实验结果怎么整理成交作业的样子一次讲清楚。适合正在上《模式识别》或《模式识别与机器学习》课程、需要独立完成实验并写报告的同学老师只要求调库的能直接用要求手写实现的也能找到对应的从零实现。2. 跑通模式识别实验前先搭好 python 环境版本组合、最小验证与环境隔离先说结论模式识别实验不需要追最新版本。绝大多数课程实验代码就三件事——numpy 做数组运算、scikit-learn 调现成算法、matplotlib 画图。python 3.8 到 3.11 配上 scikit-learn 1.0 以上足够覆盖我后面给的所有代码。反而是无脑装最新版容易碰到两个尴尬课程提供的旧代码调用了已经废弃的 API或者某个库的新版本改了默认参数导致结果和老师讲义上的输出对不上。2.1 版本怎么选Python 3.8 到 3.11 与第三方库的稳妥组合我在不同学校的模式识别课程里见过的实验要求无非是贝叶斯决策、近邻法、感知机、聚类、PCA 这几类用到的库非常固定。下面这张表是按实验里实际干什么来列的照着装不会多也不会少库建议版本在实验里的用途python3.8 ~ 3.11解释器本体这个区间最稳numpy 1.21数组运算手写贝叶斯、感知机的基础scipy 1.7距离计算、统计分布部分算法内部依赖scikit-learn 1.0分类、聚类、PCA、评估指标的现成实现matplotlib 3.3散点图、决策边界、ROC 曲线pandas 1.3整理模型对比表、导出 CSV安装命令就一条pip 会把依赖一起装好python -m pip install --upgrade pip pip install numpy scipy scikit-learn matplotlib pandas装完之后先别急着写算法在终端里敲两行验证一下确认库真的能用python -V python -c import sklearn, numpy, matplotlib; print(sklearn.__version__, numpy.__version__)能打印出版本号说明 python 安装、pip 安装、库 import 这条链路是通的。这一步能过滤掉一大半代码明明没问题但跑不起来的情况。IDE 的话我用 VSCode 加 Python 扩展就够新手最容易栽在解释器路径选错——右下角选了全局 python 而不是虚拟环境的 python结果装好的库 import 不到这个和 vscode python 环境配置里的经典问题是一回事写代码前先确认左下角解释器指向哪。2.2 最小闭环用鸢尾花数据跑通加载→划分→训练→评估环境搭好之后第一个实验不要直接上复杂算法先跑一个最小闭环加载数据、划分训练测试、训练一个模型、输出准确率。我一般用鸢尾花数据集做这个冒烟测试它内置在 sklearn 里不需要额外下载文件样本量小、三类均衡最适合验证环境# 最小闭环加载数据 - 划分 - 训练 - 评估 from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score X, y load_iris(return_X_yTrue) # 返回特征矩阵和标签 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) clf KNeighborsClassifier(n_neighbors3) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(KNN 准确率, accuracy_score(y_test, y_pred))这段代码里值得说清楚的参数有三个。test_size0.3 表示留三成样本做测试这是模式识别实验里最常见的划分比例你也可以改成 0.2但注意训练样本变少后结果会有波动。random_state42 是固定随机划分的种子不写的话每次运行划分都不一样后面所有实验的对比都失去意义这个我后面避坑章节还会专门说。stratifyy 是按标签比例分层采样鸢尾花三类各 50 个样本划分后训练集和测试集里三类比例保持一致如果你的实验数据不均衡这个参数尤其重要。这段代码能跑通并且准确率在 0.95 左右说明环境没问题。后面所有实验都是它的变体换模型、换数据、换评估方式骨架不变。提示load_iris(return_X_yTrue) 直接返回特征和标签两个数组不用自己拼 DataFrame。如果你拿到的是 CSV 数据用 pandas 读进来后转成 numpy 数组再走同样的流程。2.3 环境隔离与复现虚拟环境加固定版本避免换机器就崩课程实验往往要跨机器跑宿舍电脑写、机房验证、最后可能还要在别的机器上演示。全局装库最容易出问题——这周给新实验升了 numpy上周的实验代码可能就崩了。这个坑我有过血泪经验在全局环境里升级一次科学计算库结果另一个课程的旧代码直接报错排查了半天才发现是版本行为变了。所以我现在的习惯是每个课程建一个独立虚拟环境用 pip 的 venv 就能搞定不需要装额外软件python -m venv patt_env # Windows 激活 patt_env\Scripts\activate # Linux / macOS 激活 source patt_env/bin/activate pip install numpy scipy scikit-learn matplotlib pandas pip freeze requirements.txtpip freeze 会把当前环境的所有库版本导出来。换机器时先按上面步骤建环境然后一条命令恢复pip install -r requirements.txt这样你报告里的参数、结果、版本号都是可复现的。如果实验室机器没有 venv 权限用 Anaconda 建独立环境也是常见做法conda create -n patt python3.9之后还是要走一遍 pip install思路一样只是把 python 环境管理交给了 conda。要点只有一个不要随意改动全局环境里的库版本环境隔离不是玄学是给未来的自己留后路。3. 四类模式识别实验的可运行代码贝叶斯、KNN、K-Means 与 PCA环境通了进入正题。下面这四类代码我在多个学校的模式识别课程实验里都见过不管是国科大、湖大还是其他高校的《模式识别》或《模式识别与机器学习》实验题目翻来覆去就是这些内容。代码按能直接跑、方便改、能出图的标准来写每段都可以独立复制运行。3.1 手写高斯朴素贝叶斯先验、似然、后验的完整代码贝叶斯分类器的原理一句话概括对每个类别用先验概率乘上似然得到后验概率哪个类别的后验大就判给哪个。高斯朴素贝叶斯在此基础上做了两个简化假设特征之间相互独立每个特征在类别内服从高斯分布。手写一遍这些计算比直接调 sklearn 更能在实验报告里讲清楚原理老师也更容易给分import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score class GaussianBayes: 手写高斯朴素贝叶斯每类估计先验、均值、方差预测时计算后验 def __init__(self): self.priors {} self.means {} self.vars {} def fit(self, X, y): self.classes_ np.unique(y) for c in self.classes_: X_c X[y c] self.priors[c] len(X_c) / len(X) # 先验 类别样本占比 self.means[c] X_c.mean(axis0) # 每个特征的均值 self.vars[c] X_c.var(axis0) 1e-6 # 加极小值防止除零 def _gaussian_pdf(self, x, mean, var): 高斯概率密度函数 return np.exp(-(x - mean) ** 2 / (2 * var)) / np.sqrt(2 * np.pi * var) def predict(self, X): preds [] for x in X: scores {} for c in self.classes_: # 朴素假设各特征独立似然 各特征概率密度连乘 likelihood np.prod(self._gaussian_pdf(x, self.means[c], self.vars[c])) scores[c] self.priors[c] * likelihood # 后验 ∝ 先验 × 似然 preds.append(max(scores, keyscores.get)) return np.array(preds) X, y load_iris(return_X_yTrue) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) clf GaussianBayes() clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(手写高斯朴素贝叶斯准确率, accuracy_score(y_test, y_pred))fit 阶段做的事情就是统计对每个类别计算样本占比得到先验计算每个特征的均值和方差作为高斯分布的参数。predict 阶段对每个待测样本把它的各特征值代入各类别的高斯密度函数连乘得到似然再乘上先验最后取分数最大的类别。代码里 vars 加 1e-6 是数值稳定的小技巧防止某个特征方差为 0 时除零。注意特征维度一高np.prod 连乘的概率值会越来越小最后可能下溢成 0导致预测结果全变成同一类。如果遇到这种怪现象把似然改成取对数后再累加即 log 似然相加数学上等价且数值稳定。对照实现也要会实验报告里可以写手写实现与 sklearn 结果对比from sklearn.naive_bayes import GaussianNB sk_clf GaussianNB() # var_smoothing 默认 1e-9作用类似手写里的 1e-6 sk_clf.fit(X_train, y_train) print(sklearn 高斯朴素贝叶斯准确率, accuracy_score(y_test, sk_clf.predict(X_test)))两者准确率通常只差零点几个百分点差异主要来自平滑项的大小。手写版的 1e-6 偏大sklearn 默认的 var_smoothing 是 1e-9在鸢尾花这种数据上几乎不影响结论。3.2 KNN 与感知机两个经典分类实验的最小实现KNN 和感知机是模式识别实验里最短平快的两个算法很多学校的实验一、实验二就是它们。KNN 没有显式的训练过程fit 只是把训练数据存下来预测时才逐样本计算距离感知机的训练则是迭代更新权重直到所有样本分类正确或达到最大迭代次数。两个放一起写能直观对比惰性学习和主动学习的区别from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.linear_model import Perceptron from sklearn.preprocessing import StandardScaler X, y load_iris(return_X_yTrue) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) # KNNK 值越小边界越细碎越大越平滑 knn KNeighborsClassifier(n_neighbors5, weightsdistance) knn.fit(X_train, y_train) print(KNN 准确率, accuracy_score(y_test, knn.predict(X_test))) # 感知机对特征尺度敏感训练前必须先标准化 scaler StandardScaler() X_train_s scaler.fit_transform(X_train) X_test_s scaler.transform(X_test) perceptron Perceptron(max_iter1000, eta00.01, random_state42) perceptron.fit(X_train_s, y_train) print(感知机准确率, accuracy_score(y_test, perceptron.predict(X_test_s)))KNN 的关键参数是 n_neighbors。K 取 1 时训练集准确率永远是 100%但测试集容易过拟合K 太大则把不同类别的边界磨平欠拟合。常见做法是画一条K 值对交叉验证准确率的曲线选拐点处的 K。weightsdistance 表示按距离加权投票最近的样本话语权更大对噪声更稳健如果用默认的 uniform每个邻居权重相同在样本不均衡时容易被多数类带偏。感知机的两个参数要注意eta0 是学习率取太大权重更新会震荡、损失不下降取太小收敛慢max_iter 是最大迭代轮数如果代码报了 ConvergenceWarning说明数据线性不可分或迭代不够先加大 max_iter同时检查是否忘了标准化——感知机对特征尺度极其敏感特征值范围相差两个数量级时大尺度特征会主导权重更新小尺度特征几乎学不到东西。鸢尾花数据里 setosa 是线性可分的但另外两类有重叠感知机在这种数据上报警告是正常现象不是代码 bug。3.3 K-Means 聚类无监督实验的完整流程与评估指标聚类实验和前面最大的区别是没有标签也可以用而且必须用无标签的视角来评估结果。K-Means 的完整流程是标准化、选 K、聚类、可视化、算指标。注意聚类前一定要标准化否则欧氏距离会被数值范围大的特征主导比如花瓣长度的绝对值比花萼宽度大好几倍聚类结果基本就只看花瓣长度了from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt X, y load_iris(return_X_yTrue) X_std StandardScaler().fit_transform(X) # 标准化后再算距离 kmeans KMeans(n_clusters3, n_init10, random_state42) kmeans.fit(X_std) # 用前两个特征画散点颜色按聚类标签 plt.scatter(X_std[:, 0], X_std[:, 1], ckmeans.labels_, cmapviridis, edgecolorsk) plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], markerx, s200, linewidths3, colorred) plt.xlabel(feature 1 (标准化后)) plt.ylabel(feature 2 (标准化后)) plt.title(K-Means 聚类结果) plt.show() print(轮廓系数, silhouette_score(X_std, kmeans.labels_))n_clusters 是 K-Means 唯一的硬超参数选 K 的常见做法是肘部法则循环 K1 到 10记录每次的 inertia样本到所属簇中心的距离平方和画折线找拐点拐点处的 K 就是收益递减的位置。n_init10 表示随机初始化 10 次取最优结果K-Means 对初始中心敏感这个参数能缓解跑两次结果不一样的问题。轮廓系数的范围是 -1 到 1越接近 1 说明簇内紧凑、簇间分离明显。但这里有一个模式识别实验里特别容易踩的坑kmeans.labels_ 输出的 0、1、2 是簇编号和真实标签 y 里的 0、1、2 没有一一对应关系——K-Means 可能把真实类别 0 的样本全部分到簇号 2 里。所以不能直接把聚类标签和真实标签比 accuracy要么用轮廓系数这类无监督指标要么做标签对齐把簇号和真实类别做最优匹配之后才能算准确率。这个坑在实验报告里写出来反而是加分项。3.4 PCA 降维可视化高维特征压到二维并解释方差占比PCA 在模式识别实验里经常不是主角而是配角——它的主要价值是把高维数据压缩到二维让你和老师都能看见数据长什么样。鸢尾花有四个特征直接画散点图只能选两个信息有损失用 PCA 降到二维保留的是方差最大的方向这张图通常是实验报告里的第一张图。标准顺序是先标准化再做 PCA再可视化from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt X, y load_iris(return_X_yTrue) X_std StandardScaler().fit_transform(X) # 不标准化PCA 会被大数值特征主导 pca PCA(n_components2) X_pca pca.fit_transform(X_std) plt.figure(figsize(6, 5)) sc plt.scatter(X_pca[:, 0], X_pca[:, 1], cy, cmapcoolwarm, edgecolorsk) plt.colorbar(sc) plt.xlabel(PC1解释方差 {:.2%}.format(pca.explained_variance_ratio_[0])) plt.ylabel(PC2解释方差 {:.2%}.format(pca.explained_variance_ratio_[1])) plt.title(PCA 降维可视化鸢尾花) plt.show() print(累计解释方差, pca.explained_variance_ratio_.cumsum())explained_variance_ratio_ 是每个主成分解释的方差占比PC1 通常占大头。累计解释方差是选维度的依据降到多少维合适常见做法是让累计方差达到 85% 到 95%。比如鸢尾花数据降到两维就能解释 95% 以上的方差说明四维特征里的信息大部分被压缩进两个主成分了。注意主成分是原始特征的线性组合没有可解释的物理含义坐标轴标签写 PC1、PC2 就好不用强行解释PC1 是花瓣长度。另外说一个实验报告的小技巧PCA 可视化之后可以叠加一句结论——从图中可以看出 setosa 与另外两类明显分离versicolor 和 virginica 有重叠这与 KNN 在测试集上的混淆结果一致。把降维图的观察和分类实验的结论互相印证比单贴一张图有说服力得多。4. 模式识别实验代码避坑指南五个最常见也最隐蔽的翻车现场下面这些坑每一个都是我在自己代码和帮同学调代码时真实遇到过的按现象 → 原因 → 解决来写方便你对照排查。它们单独看都很小但任何一个都能让一个小时的调试变成一下午。4.1 数据泄漏归一化放在划分之前测试集信息提前泄露现象实验报告里准确率高达 0.98换到老师给的新数据上直接掉到 0.7 左右或者同一个模型先归一化再划分和先划分再归一化结果差了一大截。原因出错代码长这样——先对整个数据集调用 StandardScaler().fit_transform(X)再做 train_test_split。问题在于 fit_transform 使用了全部数据的均值和方差测试集的统计量在训练开始前就被模型看到了这叫数据泄漏。测试集信息混进了预处理阶段评估结果自然虚高但模型其实没见过测试集以外的真实分布。解决先划分再只在训练集上 fit测试集只做 transform# 错误写法先全局标准化再划分 X_scaled StandardScaler().fit_transform(X) X_train, X_test, y_train, y_test train_test_split( X_scaled, y, test_size0.3, random_state42) # 正确写法先划分训练集 fit测试集仅 transform X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42) scaler StandardScaler() X_train scaler.fit_transform(X_train) X_test scaler.transform(X_test)更省心的做法是让 Pipeline 帮你管理预处理和模型fit 和 predict 自动用同一套逻辑from sklearn.pipeline import make_pipeline from sklearn.neighbors import KNeighborsClassifier pipe make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors5)) pipe.fit(X_train, y_train) # 内部会自动做只对训练集 fit 标准化器提示数据泄漏不只发生在归一化上。用 PCA、特征选择、缺失值填充时同样只能在训练集上计算参数测试集只能应用不能参与计算。4.2 随机性玄学不固定随机种子同一份代码每次结果都不同现象同一份代码上午跑和下午跑准确率不一样把代码发给室友室友跑出的结果和你的对不上。这其实不是玄学是随机性没有被固定。train_test_split 默认随机打乱数据K-Means 的初始中心是随机的感知机的样本顺序也随机这些随机性都会影响最终结果。原因代码里没写 random_state也没有设置全局随机种子。sklearn 的很多模块接受 random_state 参数但如果你不传它就使用全局随机状态。解决在所有有随机性的地方显式固定种子X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42) # 固定数据划分 kmeans KMeans(n_clusters3, n_init10, random_state42) perceptron Perceptron(max_iter1000, random_state42)随机种子就是实验报告里的后悔药写进代码、写进报告老师复现的时候结果才能和你的一致。否则你在报告里写的准确率老师一跑就对不上这会直接影响对实验结果的信任度。我的习惯是每个实验文件开头统一设一个 SEED 42所有需要随机的地方都引用它改起来只动一处。4.3 维度错误单样本预测报 Expected 2D array 的解决办法现象模型训练得好好的predict 一调用就报错Error 信息是ValueError: Expected 2D array, got 1D array instead。初看以为是数据问题其实问题出在你传进去的是一个样本而不是一批样本。sklearn 的 predict 期望输入是二维矩阵形状是 (n_samples, n_features)哪怕只有一个样本也要是 (1, n_features)。原因X_test[0] 取出来是一维数组 (n_features,)sklearn 不认识这种输入格式。解决用 reshape(1, -1) 把它变成一行# 报错写法只给一个样本 # pred clf.predict(X_test[0]) # 正确写法reshape 成 (1, n_features) pred clf.predict(X_test[0].reshape(1, -1))如果你在循环里逐个预测样本然后用 append 收集结果也会踩类似的坑——append 出来的是多个一维数组拼成的东西形状乱七八糟。常见做法是先把预测结果放进列表最后 np.array 一下或者直接把整个 X_test 一次性传给 predict不要写循环。还有单特征数据集X 本身可能是 (n,) 而不是 (n, 1)这种情况在数据读取阶段就要处理X X.reshape(-1, 1)。4.4 决策边界画不出来网格坐标和预测结果的形状对不上现象想画分类决策边界用了 np.meshgrid 生成网格点然后直接 predict结果要么报 shape 不匹配的错要么图画出来只有一个颜色。原因meshgrid 生成的是两个二维网格 xx、yy通常你需要把网格点展平成两列拼成 (N, 2) 的矩阵去预测预测结果是一维数组长度 N。这个一维结果必须 reshape 成 xx.shape画图函数才知道每个颜色点放在网格的哪个位置。忘了 reshapecontourf 就不知道颜色矩阵怎么摆。解决完整画边界的流程如下注意最后一步 reshapeimport numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier X, y load_iris(return_X_yTrue) X2 X[:, :2] # 画边界只能用两个特征这里取前两列 X2_train, X2_test, y2_train, y2_test train_test_split( X2, y, test_size0.3, random_state42, stratifyy ) knn KNeighborsClassifier(n_neighbors5) knn.fit(X2_train, y2_train) # 在特征范围内生成 200x200 的网格 xx, yy np.meshgrid( np.linspace(X2[:, 0].min() - 0.5, X2[:, 0].max() 0.5, 200), np.linspace(X2[:, 1].min() - 0.5, X2[:, 1].max() 0.5, 200) ) # 网格点按列拼成 (40000, 2) 的矩阵再逐点预测 Z knn.predict(np.c_[xx.ravel(), yy.ravel()]) Z Z.reshape(xx.shape) # 关键reshape 回 (200, 200) 才能画图 plt.contourf(xx, yy, Z, alpha0.4, cmapcoolwarm) plt.scatter(X2[:, 0], X2[:, 1], cy, edgecolorsk, cmapcoolwarm) plt.xlabel(feature 1) plt.ylabel(feature 2) plt.title(KNN 决策边界) plt.show()注意决策边界只能画在二维平面上所以必须用两个特征训练一个可视化专用模型或者先用 PCA 降到两维再训练。用四个特征训练出来的模型没法直接画边界这不是代码 bug是可视化本身的限制。KNN 的预测结果是离散的 0/1/2contourf 画出的是色块边界如果你用的是 SVM 或逻辑回归可以用 plt.contour 画决策面的概率等高线视觉效果更平滑原理类似。4.5 中文标签变成方块matplotlib 字体与负号设置现象plt.title(聚类结果) 里的中文在图上显示成一排方块或者坐标轴上的负号也变成方块。原因matplotlib 默认字体里不包含中文字符遇到中文就用方块占位。这是 matplotlib 的老问题和系统平台有关。解决在脚本最前面加上两行配置import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # Windows 用 SimHei 或 Microsoft YaHei plt.rcParams[axes.unicode_minus] False # 让负号正常显示而不是方块macOS 系统把 SimHei 换成 PingFang SC 或 Arial Unicode MSLinux 可以用文泉驿正黑。这两行建议放在脚本头部统一设置不要等到图出来了才发现中文乱码再回头重跑——生成图通常要重新训练一次模型浪费的时间完全没有必要。如果不想折腾字体最省事的方案是所有图的标签、标题一律用英文正文报告里再用中文解释这样在任何机器上都不会出问题。5. 把实验结果整理成能交作业的样子评估指标、模型对比表与参数记录代码能跑只是第一步。模式识别实验的评分很大程度上看结果呈现有没有用对指标、有没有对比实验、参数分析是不是具体。下面这套整理模板是我自己实验报告的标准结构直接套用就行。5.1 分类实验必出的四个指标与混淆矩阵很多同学的实验报告只写一个准确率这在类别均衡的数据上勉强够用但遇到不均衡数据就露馅了。分类实验至少应该给出准确率、查准率、查全率、F1 四个指标外加一张混淆矩阵。sklearn 的 metrics 模块一行一个from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import (accuracy_score, precision_score, recall_score, f1_score, ConfusionMatrixDisplay) import matplotlib.pyplot as plt X, y load_iris(return_X_yTrue) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) clf KNeighborsClassifier(n_neighbors5) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(准确率, accuracy_score(y_test, y_pred)) print(查准率, precision_score(y_test, y_pred, averagemacro)) print(查全率, recall_score(y_test, y_pred, averagemacro)) print(F1, f1_score(y_test, y_pred, averagemacro)) ConfusionMatrixDisplay.from_estimator(clf, X_test, y_test, cmapBlues) plt.title(KNN 混淆矩阵) plt.show()多分类任务里precision 和 recall 的 average 参数有讲究。macro 是先对每个类别分别计算再取平均适合鸢尾花这种类别均衡的数据如果某个类别的样本特别少macro 会被少数类拉低这时看 weighted按样本量加权或者直接逐类打印指标更能反映真实情况。混淆矩阵的读法是对角线越亮越好报告里写一句第 2 类和第 3 类有 3 个样本互相混淆说明这两个类别在特征空间中有重叠比只报一个准确率有分析深度。5.2 多模型对比表用同一份数据划分跑四个模型实验报告里最常被要求的部分是对比不同算法的性能。对比的关键前提是所有模型必须用同一份训练集和测试集随机种子一致否则对比没有意义。下面的代码用同一个数据划分跑四个模型结果汇总成一张 pandas 表import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.naive_bayes import GaussianNB from sklearn.linear_model import Perceptron from sklearn.tree import DecisionTreeClassifier from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.metrics import accuracy_score, f1_score X, y load_iris(return_X_yTrue) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) models { KNN: make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors5)), 朴素贝叶斯: GaussianNB(), 感知机: make_pipeline(StandardScaler(), Perceptron(max_iter1000, random_state42)), 决策树: DecisionTreeClassifier(random_state42), } rows [] for name, model in models.items(): model.fit(X_train, y_train) pred model.predict(X_test) rows.append({ 模型: name, 准确率: round(accuracy_score(y_test, pred), 4), F1: round(f1_score(y_test, pred, averagemacro), 4), }) print(pd.DataFrame(rows).to_string(indexFalse))这里有一个容易被忽略的细节感知机用了 make_pipeline 包一层 StandardScaler因为前面说过它对特征尺度敏感KNN 按理说也应该标准化这里包上是为了公平对比。如果某个模型没做预处理就参与对比得到差结果不能怪算法是预处理没做对。这张表可以直接贴进报告也可以用 df.to_csv(对比结果.csv) 导出方便后续排版。5.3 参数表与报告结构让老师一眼看出你改了什么实验报告最忌只贴代码和结果图没写任何参数分析。参数分析不要求长篇大论一张参数实验表就能说明你确实调过参。我的模板是实验、关键参数、实验取值、改变取值后观察到的现象四列实验关键参数实验取值改变取值后观察到的现象KNNn_neighbors5K1 时训练准确率 100%测试波动大K15 时边界过平滑KNNweightsdistance距离加权后对噪声样本的敏感性下降感知机eta00.01学习率调到 0.1 后训练震荡准确率下降感知机max_iter1000迭代不足时出现 ConvergenceWarning结果不稳定高斯贝叶斯var_smoothing1e-9调大到 1e-2 后概率估计更平滑但区分度下降报告的整体结构我建议固定为实验目的 → 算法原理 → 核心代码 → 实验结果图 → 参数分析表 → 结论。每张图下面配两到三句观察结论不要只放图不放字参数表和对比表放在结果分析部分和图表互相引用。这样一份报告老师扫一眼就知道你做了什么、发现了什么评分体验完全不同。6. 进阶技巧把实验代码封装成通用框架换算法只改一行实验做多了你会发现所有分类实验的流程都一样加载数据、划分、训练、预测、算指标。重复写五遍之后值得花十分钟把它封装成一个函数。6.1 run_experiment 函数固定流程、暴露参数from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, f1_score def run_experiment(model, X, y, test_size0.3, random_state42): 固定流程划分 - 训练 - 预测 - 返回指标 X_train, X_test, y_train, y_test train_test_split( X, y, test_sizetest_size, random_staterandom_state, stratifyy ) model.fit(X_train, y_train) pred model.predict(X_test) return { accuracy: round(accuracy_score(y_test, pred), 4), f1: round(f1_score(y_test, pred, averagemacro), 4), } # 换算法、换参数只改这一行 print(KNN, run_experiment(KNeighborsClassifier(n_neighbors3), X, y)) print(贝叶斯, run_experiment(GaussianNB(), X, y)) print(决策树, run_experiment(DecisionTreeClassifier(random_state42), X, y))传进去的 model 可以是任何实现了 fit 和 predict 接口的对象包括前面手写的 GaussianBayes。这样你手写算法和调库算法可以放进同一套评估流程对比起来特别方便。第一次写可能觉得多此一举但当你需要对比五个算法乘三组参数时这个函数就是你整理实验结果的流水线。6.2 用交叉验证替代单次划分让实验结论更可靠单次划分的结果受运气影响如果划分恰好把难分的样本都分到测试集准确率就偏低反过来就虚高。交叉验证把数据切成 K 份轮流拿一份做测试、其余训练得到 K 个分数用均值和标准差描述模型表现比单次划分客观得多代码也简单from sklearn.model_selection import cross_val_score from sklearn.neighbors import KNeighborsClassifier scores cross_val_score(KNeighborsClassifier(n_neighbors5), X, y, cv5) print(5 折交叉验证准确率, scores) print(均值 ± 标准差{:.4f} ± {:.4f}.format(scores.mean(), scores.std()))实验报告里写准确率 0.96 ± 0.02比写准确率 0.97可信得多因为前者包含了稳定性信息。交叉验证本身也有随机性数据划分顺序所以 cross_val_score 里可以传 random_state 参数或者先固定 np.random.seed保证报告里写的分数能被复现。我的一个习惯是每个新实验开始前先跑一遍上一次实验的最小闭环确认环境没坏、基线还在再动手写新代码。这个习惯帮我省下了大量改了半天下班时发现是环境坏了的冤枉时间也保证了交上去的每一份实验报告里所有数字都是当天跑出来、第二天还能复现的。代码能跑只是起点能复现、能解释、能对比才是模式识别实验真正想让你练的东西。希望帮到你。本文还有配套的精品资源点击获取
返回列表