简介:面向计算机视觉课程设计与毕业设计的SVM手写数字识别完整资源,适合初学者系统入门,也可作为数字图像分类项目的可直接参考模板。项目基于MNIST数据集构建,划分60000张训练图片与10000张测试图片,所有图像统一为28x28像素,代码覆盖数据读取、训练集/测试集划分、SVM模型构建、训练与识别全流程;同时配有实验报告和可交互Notebook,方便对照理解特征提取、参数选择与评估结果。压缩包共含6个文件,主要类型包括Python源码、Jupyter Notebook、PDF实验报告、Markdown说明文档和HTML预览页,整体仅1.02MB,轻量易用且结构清晰。目前已有774人学习浏览,实用性已得到一定验证。通过源码、报告与说明文档配套阅读,学习者既能直接复用完整识别流程,也能围绕SVM在图像分类中的核心思路进行扩展调试,适合作为课程设计、毕业设计或入门实践素材。
1. 基于SVM手写数字识别,不是“跑通代码”就完事
很多人在做基于SVM手写数字识别代码和实验报告时,第一步是运行网上的脚本,看到准确率98%就复制进报告,结果答辩时一句“为什么选RBF核”就卡住。这个任务的真正价值在于:用一组可以解释的代码,把“手写数字图片-特征向量-SVM模型-评估指标”这条链路完整打通。我会从数据预处理、SVM调参、实验报告三个层面,把常见做法和踩过的坑讲清楚。适合正在做课程设计、毕业设计,或者想快速验证传统机器学习在图像分类上表现的开发者。如果你手里只有一份代码,却不知道报告里该写什么,这篇内容能帮你把每一段代码映射到报告对应的章节里。
2. 为什么手写数字识别用 SVM:问题建模与 MNIST 数据准备
2.1 手写数字识别是一个 10 类分类问题,别把它想复杂了
先说清楚问题本身。一张手写数字图片在 MNIST 中是 28x28 的灰度图,展开后是一个 784 维的向量,每个维度的取值是 0 到 255 的灰度值。我们要做的是学一个函数,输入 784 维向量,输出 0 到 9 中的一个数字标签。这本质上是 10 类分类问题,而 SVM 原本是二分类器,所以在 sklearn 的实现里,扩展方式是在任意两个类别之间都训练一个二分类器,最后通过投票得到类别,也就是 one-vs-one 策略。
那为什么在这个任务上还值得用 SVM?如果你的样本量不大,比如几千到几万,SVM 的 RBF 核可以拿到和简单神经网络接近的准确率,而且不需要 GPU,不用调网络结构、学习率、batch size。训练时真正要关心的只有惩罚系数 C 和核函数参数 gamma,这对实验报告来说非常友好,因为所有超参数都能写清楚,答辩时也容易讲明白。
在动手写代码之前,我习惯先把问题界定清楚,而不是直接 import SVC 就开始 fit。界定内容包括:训练样本有多少、类别是否均衡、特征尺度是否一致、用哪个核函数、用什么评估指标。这五个点决定了后面每一步的选择,也是在实验报告里必须交代的前提。很多人跳过这一步,后面所有的参数搜索都变成了玄学。
2.2 数据集读取与归一化:MNIST 的 CSV 怎么喂给 SVM
常见的 MNIST 获取方式有两种:一种是直接用深度学习框架内置的数据加载接口,另一种是下载 CSV 版本自己用 pandas 读取。这里我以下载好的 train.csv 为例,它通常包含一列 label 和 784 列像素值,像素值顺序是行优先展开。
import pandas as pd import numpy as np from sklearn.model_selection import train_test_split # 读取本地 CSV,第一列是标签,后面 784 列是 28x28 的灰度值 df = pd.read_csv('train.csv') # 如果 CSV 第一行是列名,下面按位置取最稳;如果没有列名,用 iloc 也是一样 X = df.iloc[:, 1:].values.astype(np.float32) # 转为 float32,SVM 需要浮点输入 y = df.iloc[:, 0].values.astype(np.int64) # 标签转为 int,后面 sklearn 才能识别 # 灰度值归一化到 [0,1],这一步不做,RBF 核基本白训练 X /= 255.0 # 分层采样划分训练集/验证集,保持 10 个类别的比例 X_train, X_val, y_train, y_val = train_test_split( X, y, test_size=0.2, stratify=y, random_state=42 ) print("训练集形状:", X_train.shape, "验证集形状:", X_val.shape)这段代码里有几个关键点。astype(np.float32) 不是可选项,pandas 默认读进来的可能是 int64,SVM 在计算核函数时会对每个元素做浮点运算,int 输入虽然也能跑,但内存占用更大,某些实现里还可能出现隐式类型转换的坑。X /= 255.0 这一步是整个预处理里最重要的一行,因为 RBF 核的距离计算是直接基于特征数值的,如果灰度值的 0-255 和归一化后的 0-1 相比,大的灰度值会完全主导距离,导致分类边界的几何意义被破坏。
train_test_split 里我用了 stratify=y,意思是按原始标签分布进行分层抽样。MNIST 原本各类样本数接近,但如果不分层,随机划分可能让训练集里某个数字偏多,另一个数字偏少,最后模型的召回率会出现波动。random_state=42 是固定随机种子,目的是让实验可复现,写实验报告时必须保留这一行。有些人喜欢把这个随机种子拿掉,美其名曰“真实效果”,结果实验报告里的准确率每次都不一样,老师复现不了就会质疑结果。
2.3 要不要 PCA 降维:784 维对 SVM 不是灾难,但确实是负担
MNIST 的 784 维特征对 SVM 来说不算高维灾难,因为样本量通常有数万,但 RBF 核需要在每对样本之间计算高斯距离,当训练集达到 20000 以上时,训练时间会明显拉长。常见做法是先用原始像素训练一个基线,再用 PCA 降维后训练一个对比模型,把两组时间写进实验报告。PCA 还有一个实际好处:降维后的特征相当于去除了像素之间的大量冗余相关性,有些情况下准确率反而会比原始像素略高一点。
from sklearn.decomposition import PCA # 先在训练集上拟合 PCA,再用同一套变换处理验证集,避免数据泄漏 pca = PCA(n_components=0.95, random_state=42) X_train_pca = pca.fit_transform(X_train) X_val_pca = pca.transform(X_val) print("保留 95% 方差后,训练集维度:", X_train_pca.shape[1])n_components=0.95 表示保留 95% 的主成分方差,MNIST 上通常能把 784 维压到 100 维左右。这样 RBF 核的计算量会明显下降,但准确率可能会损失 1 到 2 个百分点。如果你的实验报告预算时间有限,我建议先把原始像素的模型跑完,再做 PCA 对比。如果一开始就降维,老师问“你为什么不直接试原始像素”时,你很难解释清楚。
这里特别提醒:PCA 的 fit 只能在训练集上调用,然后用同一个 pca 对象 transform 验证集和测试集。如果你在全部数据上先做 PCA 再划分,验证集的信息已经被模型看到,实验报告里的准确率会失真,这是数据泄漏里很典型的一种。类似的问题也出现在标准化、归一化这些需要统计全局均值和方差的步骤上,规则只有一个:所有从数据里学到的变换参数,都只能来自训练集。
3. 用 sklearn 实现 SVM 手写数字识别:从线性基线到 RBF 调参
3.1 最小可用代码:先用线性 SVM 跑通一条流水线
进入实现阶段后,我倾向于先跑一个线性 SVM 作为基线。线性核的好处是只有一个 C 参数,而且对 784 维的原始像素不会出现高维核矩阵爆炸,短短几分钟就能看到结果,先确认数据预处理和评估链路是通的。这一步本质上是冒烟测试,在跑昂贵的 RBF 核之前,用线性核把代码链路上的坑全部排掉。
from sklearn.svm import SVC from sklearn.metrics import accuracy_score import time start = time.time() # 用前 5000 个训练样本做冒烟测试,代码跑通后再扩大数据量 model = SVC(kernel='linear', C=1.0, random_state=42) model.fit(X_train[:5000], y_train[:5000]) val_pred = model.predict(X_val) elapsed = time.time() - start print("linear SVM accuracy: {:.4f}".format(accuracy_score(y_val, val_pred))) print("训练耗时: {:.2f}s".format(elapsed))取 X_train[:5000] 是冒烟测试里很常用的手法,先验证 fit 和 predict 不会报错,再跑全量。核函数是 linear,C 设为 1.0,这个值在大部分分类任务里是一个比较中性的起点。random_state=42 在 SVC 里对结果影响很小,但保留它可以让记录习惯保持一致。
这段代码跑完后,在 MNIST 上线性 SVM 准确率通常在 91% 到 93% 之间。如果明显低于这个区间,问题基本不在模型,而在预处理。最常遇到的是没有归一化,或者训练集没打乱。我见过有人用前 5000 个样本没 stratify,导致训练集里全是 0 和 1,验证时准确率只有 20%,那不是 SVM 的问题,是数据切分的问题。另外,如果你的机器内存不大,建议先把 X 转换为 float32,float64 会让样本矩阵内存翻倍,训练速度也会变慢。
3.2 为什么 RBF 核是手写数字识别的默认选择:C 和 gamma 怎么理解
线性 SVM 在 MNIST 上能到 92% 左右,但再往上走就需要非线性核。RBF 核(径向基核)是手写数字识别中最常用的选择,它通过高斯函数把样本映射到更高维空间,从而处理像素之间的非线性关系。gamma 控制单个训练样本的影响半径,gamma 越小,决策边界越平滑;C 是误分类惩罚项,C 越大,模型越倾向于把所有训练样本分类正确,也越容易过拟合。
| 参数 | 作用 | 常见取值 |
|---|---|---|
| kernel | 核函数类型 | linear / rbf / poly |
| C | 误分类惩罚,越大越严格 | 1, 10, 100 |
| gamma | RBF 的核宽度,越小越平滑 | scale 或 0.001, 0.01, 0.1 |
| decision_function_shape | 多分类的决策函数形状 | ovo / ovr |
这里要注意一个问题:sklearn 的 SVC 在底层是用 one-vs-one 完成多分类训练的,也就是说每两个数字之间都会训练一个二分类器,也就是 45 个分类器。decision_function_shape 只改变 decision_function 输出矩阵的形状,不影响 predict 的结果。很多人在网上看到 ovr 和 ovo 的讨论,以为切换这个参数能改变准确率,这是误解。实验报告里如果写了这个参数,应该说明它只影响决策函数的输出格式,而不是分类策略本身。
svm_rbf = SVC( kernel='rbf', C=10.0, gamma='scale', # gamma 根据输入特征方差自动计算 random_state=42 ) start = time.time() svm_rbf.fit(X_train[:10000], y_train[:10000]) rbf_pred = svm_rbf.predict(X_val) print("rbf SVM accuracy: {:.4f}".format(accuracy_score(y_val, rbf_pred))) print("训练耗时: {:.2f}s".format(time.time() - start))gamma='scale' 是 sklearn 的默认值,等价于 1 / (n_features * X.var()),在归一化后的 MNIST 数据上是一个不错的起点。C=10.0 是我比较常用的初始值,它比 C=1 更积极地拟合训练集,在 RBF 核下通常能把准确率推到 96% 以上。等你拿到一组候选参数后,再用网格搜索去细调。这里的耗时输出很重要,实验报告里一定要写训练时间,因为 SVM 在样本量增大时训练时间是非线性增长的,写上时间才能体现你做实验的真实成本。
3.3 实验报告里至少要有混淆矩阵和分类报告:别只写一个准确率
很多实验报告只写一行“准确率 98%”,这是这个任务中最容易翻车的地方。因为准确率是宏观指标,完全看不出模型在哪些数字上互相混淆。比如 3 和 8,7 和 2,这类手写体在视觉上本身就很接近,SVM 分错它们非常正常,但如果没有混淆矩阵,你就无法分析。实验报告的意义不是堆叠准确率,而是让读者理解模型在什么场景下会失效。
from sklearn.metrics import confusion_matrix, classification_report import pandas as pd # 构建带行列标签的混淆矩阵,方便实验报告直接复制 cm = confusion_matrix(y_val, rbf_pred) cm_df = pd.DataFrame(cm, index=[str(i) for i in range(10)], columns=[str(i) for i in range(10)]) print("混淆矩阵:") print(cm_df) print("\n分类报告:") print(classification_report(y_val, rbf_pred, digits=3))混淆矩阵的第 i 行第 j 列表示真实标签为 i 但被预测成 j 的样本数。对角线越“亮”,说明分类越准确。分类报告里每一行有 precision、recall、f1-score 和 support,其中 support 是每一类的测试样本数。写实验报告时会特别关注 recall 最低的类别,因为那意味着这类数字容易被漏判。比如某次实验里数字 9 的召回率是 0.97,其他数字都在 0.99 以上,那分析部分就可以围绕“为什么 9 更容易被误判”展开。
建议把上面两个输出保存成文本文件,和参数设置一起放进实验报告的附录,这比临时跑一次要可靠得多。另外,混淆矩阵最好用图片形式展示,比如用热力图,但要注意从 pandas DataFrame 转换时的行列标签,避免把横纵坐标写反。我见过有人把混淆矩阵的 x 轴和 y 轴标反,导致论文里的分析完全对不上,这是很低级的错误。
4. 实验报告怎么写:把“准确率 98%”变成可复现的实验结果
4.1 实验报告的标准结构:从问题定义到结论
实验报告不是把代码打印出来一遍就行,它需要让别人在没见过你代码的情况下,也能复现你的实验。我一般按 8 个模块组织内容:摘要、问题描述、数据集与预处理、方法与原理、实验设计、结果、分析与讨论、结论。摘要里写清楚任务、数据和最佳结果,问题描述解释为什么选 SVM 而不是其他模型,数据集部分记录样本量、图像大小、归一化方式。
方法部分不要抄 SVM 的数学定义,重点是说明你选了什么核函数、C 和 gamma 的范围以及依据。实验设计部分要写训练/验证/测试集的比例、随机种子、交叉验证折数,这些细节决定实验是否可信。结果部分放准确率、训练时间、混淆矩阵和分类报告,分析与讨论部分针对错误样本和参数影响展开,结论收束全文。这个结构不一定要完全照搬,但缺了“实验设计”和“数据分析”,报告就会变成代码说明书。
一个容易被忽略的点是环境依赖的记录。Python 版本、sklearn 版本、numpy 版本都会影响结果,尤其是 SVM 在较新版本里的默认参数有调整,gamma 从默认 0.1 改为 scale 就是一次典型变化。实验报告里最好专门有一段环境说明,哪怕只有三行,也能避免别人复现对不上时怀疑你造假。另外,一定要把随机种子写进实验设计,否则交叉验证结果每一次跑都不一样,别人复现不了你的数字,再好的分析也没有意义。
4.2 用 GridSearchCV 做参数搜索:在小样本上搜完,再上全量
网格搜索是确定 C 和 gamma 最直接的手段。但如果你直接把几万样本丢进 GridSearchCV,通常在半小时内很难跑完,因为 RBF 核的复杂度随样本量增长很快。正确做法是先在训练集的一个子集上搜索,确定最佳参数区域后,再用全量数据训练最终模型。这也是我在做这类实验时最常用的做法,既省时间,又能保证参数不是拍脑袋定的。
from sklearn.model_selection import GridSearchCV from sklearn.svm import SVC # 先拿 5000 个样本搜索,不然 GridSearchCV 会在全量数据上跑很久 X_sub = X_train[:5000] y_sub = y_train[:5000] param_grid = { 'C': [1, 10, 100], 'gamma': [0.001, 0.01, 0.1], 'kernel': ['rbf'] } svm = SVC(random_state=42) grid = GridSearchCV(svm, param_grid, cv=3, scoring='accuracy', n_jobs=-1, verbose=1) grid.fit(X_sub, y_sub) print("最佳参数:", grid.best_params_) print("交叉验证最佳准确率: {:.4f}".format(grid.best_score_))param_grid 里的 C 和 gamma 都按指数增长,这是 SVM 调参的基本直觉,因为 C 和 gamma 对决策边界的影响是对数量级的,等距取值反而浪费时间。cv=3 表示在训练子集上做 3 折交叉验证,这样选出来的参数不是只适配某一次随机划分。n_jobs=-1 让所有 CPU 核并行训练,能明显提速。这里我一般不会让用户去等全量搜索,而是先用小样本把搜索空间缩窄,再针对最佳点附近加细。
搜索完成后,用 grid.best_params_ 去初始化一个新的 SVC,再在完整训练集上训练。注意不要在网格搜索对象上直接 fit 全量数据,因为 GridSearchCV 的 fit 会在内部做交叉验证,浪费算力。这一步常见错误是把测试集也放进网格搜索里,导致参数选择时“看”了测试集的结果,测试集就失去了意义。正确的关系是:测试集只能出现一次,就是在最终评估的时候。
4.3 数据可视化:错误样本和决策边界到底要不要放
实验报告里配图能提升可读性,但不是什么图都值得放。我的建议是错误样本图一定要放,它能直观展示模型失败在什么地方;二维决策边界图慎放,因为 SVM 在高维空间里的真实边界无法用二维图呈现,强行画只能表达 PCA 投影后的近似结果。放一张误导性很强的二维边界图,反而不如不放。
错误样本图的代码很简单,从验证集预测结果中挑出预测错的样本,用 matplotlib 把图片和真实/预测标签一起画出来。
import matplotlib.pyplot as plt import numpy as np mis_idx = np.where(rbf_pred != y_val)[0] plt.figure(figsize=(10, 5)) for i, idx in enumerate(mis_idx[:10]): plt.subplot(2, 5, i + 1) plt.imshow(X_val[idx].reshape(28, 28), cmap='gray') plt.title(f"true:{y_val[idx]}/pred:{rbf_pred[idx]}") plt.axis('off') plt.tight_layout() plt.savefig("misclassified.png", dpi=150)这段代码里 mis_idx 是预测错误样本的下标,取前 10 个可以避免图片数量太多看不清楚。保存成 PNG 文件,分辨率 150dpi 足够在 Word 里打印。在实验报告里通常配合这几张图写一句:这些错误样本大多数比较模糊,人眼也难以辨认,这说明当前模型的错误更多来自数据本身质量,而不是特征表达不足。这个结论对老师来说很有说服力,因为它证明你分析过错误样本,而不是只看了准确率。
至于决策边界,如果一定要放,我建议只取 0 和 1 两个数字做 PCA 投影,然后用等高线画出 SVM 的决策区域,并明确标注“这是二维投影下的边界,不代表高维真实边界”。如果实验报告里只有二维决策边界而没有说明,答辩时很容易被追问。二维投影可能会把原本可分的数据画成重叠在一起,让 SVM 看起来效果很差。
5. SVM 手写数字识别避坑指南:5 个让准确率翻车的问题
做基于 SVM 手写数字识别的过程,翻车点非常集中。我整理了几个高频问题,按“现象-原因-解决”的方式写出来,方便你直接对照排错。这些问题在实验报告里也会出现,提前避开能省很多时间。每条都是真实项目中遇到过或者帮别人排查过的,不是从文档里抄来的理论。
5.1 坑一:灰度值没归一化,SVM 训练慢且精度低
现象:模型训练时间从几分钟涨到几十分钟,准确率一直卡在 80% 出头。原因:0-255 的灰度值相比 0-1 的归一化值,数值范围大很多,RBF 核计算距离时,几个最亮的像素点就完全主导了距离,其他像素的贡献被淹没。更麻烦的是,SVM 的优化问题在尺度不一致的特征上收敛很慢,训练时间也会变长。
解决:在拟合 SVM 之前执行 X /= 255.0,这是最简单的灰度归一化。如果你后续还要做其他处理,可以用 StandardScaler 做标准化,但注意 StandardScaler 要先 fit 在训练集上,不能对整个数据集 fit。我一般会在代码里把归一化放在 train_test_split 之后,避免有人误把归一化参数也从全量数据里学出来,导致验证集信息泄漏。
# 正确的顺序:划分后再归一化,或者先归一化再划分但保持相同的变换参数 X_val_scaled = X_val / 255.0 # 这里如果训练集是 X_train / 255.0,验证集同样除以 255 即可5.2 坑二:标签是字符串,直接塞进分类器报错
现象:执行 fit 时抛出 ValueError: unknown label type,有时候还会显示“continuous is not supported”。原因:pandas 读取 CSV 时,label 列可能是字符串,比如 '0'、'1',而 sklearn 的 SVC 只接受数值型标签。另一种常见情况是 CSV 里标签列前面或后面有空格,读出来是 ' 0',肉眼看不出来,但 sklearn 会把它当成两个不同的类别。
解决:在读取后立即执行 y = df.iloc[:, 0].values.astype(np.int64),把标签转成整型。如果标签列有空格,先 df.iloc[:, 0] = df.iloc[:, 0].astype(str).str.strip() 再转 int。如果你遇到的是 MultiLabelBinarizer 相关的报错,说明你把多标签格式当成普通分类了,需要检查数据形状是不是 2D 的 (n_samples, n_classes),而不是 (n_samples, 1)。
5.3 坑三:训练集太小,准确率忽高忽低
现象:同一套代码跑两次,第一次 94%,第二次 91%,随机性很大。原因:SVM 在高维稀疏特征上对小数据集非常敏感,如果训练集只有 2000 个样本,随机划分出来的类别比例稍微变一下,决策边界就会明显移动。另一个原因是有些人用 df.sample(frac=1) 打乱数据后没有固定 random_state,每次实验的划分都不同。
解决:用 train_test_split 的 stratify=y 参数保证分层,同时固定 random_state。如果数据量确实小,不要用单次划分的准确率作为报告指标,而是用 cross_val_score 做 5 折或 10 折交叉验证,取平均值和标准差。例如“平均准确率 95.3% ± 0.6%”,这样比“准确率 95.3%”可信得多,也能在报告里体现出统计稳定性。
from sklearn.model_selection import cross_val_score # 小样本时用交叉验证评估稳定性,取均值 ± 标准差 scores = cross_val_score(SVC(kernel='rbf', C=10, gamma='scale'), X_train[:5000], y_train[:5000], cv=5) print(f"cross val accuracy: {scores.mean():.4f} ± {scores.std():.4f}")5.4 坑四:在 784 维像素上硬套多项式核,训练时间长到怀疑人生
现象:kernel='poly',degree=3,运行超过一个小时还在 fit,甚至内存占用持续上涨。原因:多项式核会计算特征的高阶组合,在 784 维特征空间里,三阶组合的维度极其巨大,计算开销呈指数级增长。MNIST 的 784 维度虽然不算特别高,但配合几万样本,poly 核的核矩阵计算和二次规划会非常痛苦。
解决:数字识别任务里优先用 RBF 核,因为它在大多数实验里准确率更高、训练时间更可控。如果必须对比多项式核,先 PCA 降维到 50 到 100 维,再尝试 degree=2 或 3。注意多项式核还存在数值溢出风险,degree 太高时核函数值会放大到浮点数上限,导致收敛失败。如果你看到 loss 异常或者 predict 全是一个类,先检查核函数是不是 poly。
5.5 坑五:报告只写准确率,老师一问哪类分得不好就答不上来
现象:实验报告里只有准确率 98%,答辩被问“哪个数字最容易识别错”时沉默。原因:准确率是全局指标,掩盖了不同类别之间的差异。MNIST 上数字 1 通常准确率最高,而 4 和 9、7 和 2 之间的混淆往往最严重。如果报告里没有混淆矩阵和分类报告,读者就默认你只跑了一个黑匣子,完全不知道模型内部行为。
解决:在实验报告里附上 classification_report 和混淆矩阵,分析 recall 最低和混淆最多的数字对,并在讨论部分解释这些错误是否来自笔迹模糊或特征重叠。比如你可以写:“数字 4 的召回率只有 0.97,主要被误判为 9,这与二者在像素形状上的高度相似性有关。”这种分析不需要很长,但能让老师一眼看出你真的理解了模型输出。
6. 从冒烟测试到全量训练:复现一个可靠的 SVM 手写数字识别实验
最后一章我分享一个自己常用的实验流程,它能把上面所有内容串起来,也能让你的实验报告经得起复现。第一步是数据预处理并固定随机种子;第二步是取 1000 个样本跑冒烟测试,确认代码链路没有报错;第三步是在 5000 个样本上做网格搜索,确定 C 和 gamma;第四步用最优参数在全量训练集上训练,并在从未参与训练和参数搜索的验证集上评估;第五步记录混淆矩阵、分类报告、训练耗时和实验环境。
这个流程里最核心的技巧是“数据集分层使用”。训练集、验证集、测试集的划分要时刻保持一条线:网格搜索只能用训练集和内部的交叉验证,验证集只在最后一轮评估时出现。如果数据量有限,我一般不用单独测试集,而是用 5 折交叉验证的结果平均值作为最终指标,比如最优参数下交叉验证准确率 98.2% ± 0.3%,这样比单次运行准确率更有说服力。加个标准差,老师一看就知道你做的是可重复实验,而不是碰运气。
每次调参时,我会在本地保存一份文本日志,包含时间、样本数量、random_state、C、gamma、准确率和训练耗时,格式很简单,就是一行一行的键值对。这样写实验报告时不需要重新跑代码,只要把日志整理成表格就行。这也是我吃过亏之后养成的习惯,之前有次改了数据预处理,忘了记录参数,结果报告里的数据和代码怎么都对不上,只能重新跑一遍。后来所有实验都先写日志,再动代码。这个习惯让“实验报告”从代码的附属品变成了真正的产物。
说实话,我在最开始做这个任务时也踩过不少坑。有一次用全量数据 GridSearchCV,等了半小时没出结果,后来才发现 param_grid 里写了 30 个参数组合,而每个组合都在 60000 张图上训练。后来养成了先降采样搜索、再全量训练的习惯,实验速度至少提升了 5 倍。希望这个习惯也能帮到你。
本文还有配套的精品资源,点击获取