资讯动态

基于Python机器学习的水稻病虫害自动识别系统实战解析

发布时间:2026/10/23 16:26:36 来源:尧图企业网站定制
简介一套基于Python机器学习的水稻病虫害自动识别系统源码与数据定位为可直接运行的高分毕业设计项目。面向计算机、人工智能、自动化相关专业的学生、老师和从业者适用于期末大作业、课程设计或毕业设计项目答辩评审得分98分代码经调试测试可稳定运行既能帮助小白入门也方便进阶者二次开发。压缩包共311个文件类型涵盖Java/Vue前后端代码、XML配置与数据、JS/CSS静态资源、Markdown说明文档及JPG/PNG图片样本整体约2.56MB目录清晰便于按模块查阅。目前已有294人学习浏览具备参考热度。读者可从中获取完整实现思路包括数据组织、识别逻辑、接口服务与页面展示的联动方式并可参照现有代码替换样本、调整参数或扩展功能是机器学习落地病虫害识别的实用蓝本。1. 水稻病虫害自动识别系统这份用 Python 写的机器学习源码能解决什么每年五六月份稻瘟病进入高发期农户对着水稻叶片拍张照片系统要在几秒内判断出是稻瘟病、白叶枯病还是健康叶片——这就是这套基于 Python 机器学习的水稻病虫害自动识别系统在做的事。它是一份完整跑通的项目源码不是只有一段训练代码的 demo数据预处理、特征提取、模型训练、预测界面四个环节都被打包进来了原项目答辩评审打到 98 分说明流程完整度和可复现性都经得起问。适合三类人直接使用做毕业设计、课程设计或期末大作业的学生想完整走一遍机器学习图像分类全流程的入门者需要拿一套能写进简历的可复现项目充实的从业者。下面我按复现顺序把数据怎么处理、模型怎么选、界面怎么封装、坑在哪里一个个过一遍。2. 数据预处理与划分拿到图片后先切训练集验证集三个参数别设错拿到源码后我一般不会急着跑 train.py而是先打开数据目录看结构。这类毕设项目的数据集最常见两种组织方式按类别分文件夹存放图片或者一个文件夹配一张 CSV 标签表。水稻病虫害数据绝大多数走前者典型结构是images/blast/*.jpg、images/bacterial_blight/*.jpg、images/healthy/*.jpg这种类别名直接是文件夹名。如果你的压缩包里不是这个结构第一件事就是先统一成这种格式后面所有代码都好写。我之前拆过一个类似的源码包数据是一个扁平文件夹加 Excel 标签读取脚本写得很绕花了大半天才把标签对应上。换成按类别建目录之后整个流程从读取到训练少写了一百行代码。所以先花十分钟整理目录比后面踩坑再回头改要划算得多。2.1 目录结构与图片读取先让路径和标签严格对齐这一步的目标是把所有图片的绝对路径和类别名分别放进两个平行列表里后面划分数据集、提取特征都基于这两个列表操作避免出现“图片和对不上标签”的错位问题。# 以 data/images/类别名/图片名.jpg 为例 from pathlib import Path import cv2 data_root Path(data/images) X_paths [] # 图片绝对路径列表 y_labels [] # 类别名列表如 blast / healthy for cls_dir in sorted(data_root.iterdir()): # 类别目录 if not cls_dir.is_dir(): continue for img_path in sorted(cls_dir.glob(*.jpg)): # 每张图片 X_paths.append(str(img_path)) y_labels.append(cls_dir.name) print(样本总数:, len(X_paths)) print(类别:, sorted(set(y_labels))) for cls in sorted(set(y_labels)): print(cls, X_paths.count(cls) if False else sum(1 for p in X_paths if cls in p))这里我特意用了pathlib.Path而不是os.listdir因为 Path 在拼接路径、处理绝对路径时更稳。两个细节值得注意data_root.iterdir()的返回值顺序在不同操作系统上不保证稳定外面套sorted()是为了让类别顺序在每次运行中都一致这对后面网格搜索的随机种子复现很重要glob(*.jpg)只匹配 jpg如果你的数据里有 png或者文件名大小写混用直接换成glob(*.*)再加后缀白名单判断。读取图片时最常见的翻车点是cv2.imread遇到中文路径返回 NoneWindows 下尤其严重。我的习惯是统一封装一个读取函数后面所有环节都走它这个函数在第五章避坑里会给出完整写法。2.2 数据集划分stratify 和 random_state 为什么必须写划分训练集、验证集是整条流水线的第一道关卡。很多人随便写两行train_test_split就完事结果验证集里某个类别一张图都没有训练出来的模型对这个类别等于瞎猜。这里三个参数是关键test_size、random_state、stratify。from sklearn.model_selection import train_test_split train_paths, val_paths, train_labels, val_labels train_test_split( X_paths, y_labels, test_size0.2, # 20% 做验证集80% 训练 random_state42, # 固定随机种子保证每次划分一致 stratifyy_labels # 按类别比例分层抽样 )stratifyy_labels是这里最重要的参数它会保证划分后训练集和验证集里每个类别的占比与原数据集一致。比如“稻瘟病”占全部样本的 40%划分后两个集合里它仍然约占 40%不会出现某个类全被切进训练集的情况。random_state42的作用是让实验可复现答辩时评审问你“这个准确率怎么来的”你能当场重新跑一遍得到相同结果而不是换一次随机数就换一个数。还有一个容易忽视的顺序问题数据划分必须在任何增强操作之前完成。如果先把每张图生成 20 个增强副本再统一划分同一个原始图的不同副本会同时出现在训练集和验证集里验证准确率虚高这个在毕设答辩里属于“数据泄露”的低级错误被问到基本答不上来。正确顺序永远是先划分后增强增强只发生在训练集上。2.3 数据增强与类别均衡小数据集撑起高分的常用手段水稻病虫害图片数据集的规模一般不大单类别往往只有一两百张直接训练容易过拟合。常见做法是对训练集做轻量级在线增强随机水平翻转、小角度旋转、亮度扰动。这三个操作对叶片病害识别非常契合因为田间拍照时叶片朝向不定、光照条件差异大让模型看到这些变化能显著提升泛化能力。# 数据增强随机翻转 旋转 亮度扰动 import cv2 import numpy as np def augment_img(img): # 随机水平翻转让模型对叶片朝向不敏感 if np.random.rand() 0.5: img cv2.flip(img, 1) # 随机旋转 -15 到 15 度旋转后边缘用复制填充 angle np.random.randint(-15, 15) rows, cols img.shape[:2] M cv2.getRotationMatrix2D((cols / 2, rows / 2), angle, 1) img cv2.warpAffine(img, M, (cols, rows), borderModecv2.BORDER_REPLICATE) # 亮度扰动模拟不同天气和拍摄条件下的叶片 beta np.random.randint(-30, 30) img cv2.convertScaleAbs(img, alpha1, betabeta) return imgcv2.warpAffine默认在旋转后会在四个角补黑边黑边区域会产生大量虚假的梯度特征带坏后续的 HOG 或颜色直方图统计。我这里显式加了borderModecv2.BORDER_REPLICATE用边缘像素复制填充比黑边干净很多。beta是亮度偏移量范围在 -30 到 30 之间太大会让画面发灰太小看不出效果。增强代码只作用于训练集验证集保持原图这样验证结果才能反映模型在真实数据上的表现。如果发现某个类别样本数明显少于其他类除了增强还可以在分类器层面加class_weightbalanced这个参数在第三章 SVM 部分会用到。两者结合是这类小样本识别项目的标准解法比单纯复制样本更不容易过拟合。3. 特征提取与模型选型为什么通常首选 SVM 而不是直接上深度网络很多人拿到“机器学习识别病虫害”这个题目第一反应就是上 CNN。但这类毕设项目的数据集规模通常是几百到两三千张全图这种体量直接训练 ResNet 很容易在小数据集上抖动调参成本高答辩时还不好解释。传统特征加 SVM 在几百样本上就能做到 85% 到 95% 的准确率训练时间以秒计算每个环节都能用图说话这是很多高分源码的真实选择逻辑。从资源里自带的代码结构也能看出这个倾向特征提取函数往往独立成模块分类器用 sklearn 的 SVM 或随机森林训练脚本短小清爽。这套组合在“样本少、类别可解释、需要快速出结果”的场景下性价比远高于深度网络。先拿 SVM 打出可靠 baseline时间富余再补一组 CNN 对比实验这个设计在毕业设计评审里是加分项。3.1 为什么水稻病虫害图像不走深度学习而走传统机器学习水稻叶片病害的表征高度依赖颜色和纹理稻瘟病病斑呈灰白色梭形稻曲病病粒呈墨绿色球状白叶枯病沿叶缘形成黄色条斑胡麻斑病则是褐色小斑点。这些区分信息集中在颜色分布和局部纹理结构上恰好是传统特征最擅长的领域。用 SVM 在这个特征空间里找分类面比让一个小卷积网络从零学这些视觉模式更高效。另一个现实因素是数据量。公开的水稻病虫害数据集整图数量大多在几百到两千张类别 4 到 8 个。这个规模下 CNN 的优势发挥不出来反而需要大量数据增强和精细调参才能压住过拟合。传统流程里每个模块都是透明的特征图可以画出来分类器决策边界可以说清楚评审问“为什么这样设计”你能把链路从头推到尾这是实训项目里最值钱的部分。3.2 特征比较颜色直方图、LBP、HOG 与 Bag of SIFT 怎么选先看一个特征选型对比表这是我在多个类似项目里实际跑过的结论适用于叶片病害识别场景。特征维度量级捕捉信息适用特点颜色直方图几百维病斑颜色分布差异计算最快光照敏感LBP256 维起局部纹理模式灰度鲁棒抗光照变化HOG上千维病斑形状与边缘梯度对轮廓敏感维度偏高Bag of SIFT字典大小成百上千局部关键点描述子表达力强计算慢颜色直方图加 HOG 是我在这类项目里最常用的组合。颜色直方图提供病斑的颜色分布HOG 提供病斑边缘的形状梯度两者互补维度不算极端SVM 能很快收敛。LBP 作为替代纹理特征也常用尤其在光照变化大的时候比颜色稳定但它对颜色信息的丢失比较严重单独用会浪费叶片病斑最直观的区分信号。Bag of SIFT 表达力最强但需要先聚类构建字典特征维度很容易冲到几千上万还依赖 OpenCV 的扩展模块性价比对小项目来说偏低。# 颜色直方图 HOG 混合特征提取示例 import cv2 import numpy as np def extract_features(img): # 统一尺寸所有图片缩放到同一分辨率再提取特征 img cv2.resize(img, (256, 256)) # HSV 颜色直方图H 通道 64 桶S 通道 64 桶 hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hist cv2.calcHist([hsv], [0, 1], None, [64, 64], [0, 180, 0, 256]) cv2.normalize(hist, hist) color_feat hist.flatten() # HOG8x8 像素为一格2x2 格组成一个块 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) win_size (256, 256) block_size (16, 16) block_stride (8, 8) cell_size (8, 8) nbins 9 hog cv2.HOGDescriptor(win_size, block_size, block_stride, cell_size, nbins) hog_feat hog.compute(gray).flatten() # 拼接成一条特征向量 return np.hstack([color_feat, hog_feat])calcHist里的[0, 1]表示同时统计 HSV 的 H 和 S 两个通道H 的取值区间是 0 到 180S 是 0 到 256所以直方图尺寸是 64×64。cv2.normalize做完后每个桶的值被缩放到 0 到 1 之间消除图片尺寸和总像素数的影响。HOG 的block_stride(8,8)表示滑动步长步长越小特征越密维度越高这个参数直接决定最终特征维数如果后面发现训练太慢优先把它调到 (16,16)。两个维度加起来通常在几千维对 SVM 来说完全可接受。注意所有图片都先resize到 256×256HOGDescriptor 的win_size必须和输入尺寸严格一致否则compute会直接报错。3.3 SVM 训练与网格搜索C、gamma 参数到底该怎么调特征提取完成后进入分类器训练环节。SVM 里三个参数决定了模型上限核函数、正则化系数 C、RBF 核的 gamma。实践中核函数默认选 RBF因为线性核在特征维度高时虽然能打但对非线性可分的数据吃亏真正需要调的是 C 和 gamma用网格搜索一次性扫出来最稳妥。from sklearn.svm import SVC from sklearn.model_selection import GridSearchCV from sklearn.preprocessing import StandardScaler # 特征标准化SVM 对特征尺度非常敏感必须做 scaler StandardScaler() X_train scaler.fit_transform(X_train_raw) X_val scaler.transform(X_val_raw) param_grid { C: [1, 10, 100], gamma: [0.001, 0.0001], kernel: [rbf], class_weight: [balanced] } clf GridSearchCV(SVC(), param_grid, cv5, scoringaccuracy, n_jobs-1) clf.fit(X_train, y_train) print(最佳参数:, clf.best_params_) print(交叉验证最佳准确率:, clf.best_score_) # 用最佳模型预测验证集 val_acc clf.score(X_val, y_val) print(验证集准确率:, val_acc)C 是正则化强度的倒数C 越小对误分类的惩罚越轻模型偏向简单C 越大越容易把训练集完美分开但泛化风险升高。在网格里从 1 到 100 按十倍步进扫是比较常规的做法。gamma 控制 RBF 核的影响半径数值越大影响半径越小决策边界越曲折过拟合风险越高反过来太小则所有点影响半径都大边界过于平滑容易欠拟合。class_weightbalanced让类别样本少的自动获得更高惩罚权重正好处理上一章说的类别不均衡问题。cv5表示五折交叉验证对几百样本的数据集是个稳妥选择既不会像留一法那样训练太多次也不会像单一验证集那样依赖一次切分。n_jobs-1让网格搜索并行跑所有参数组合省时间。标准化这一步不能省SVM 在计算距离时如果特征量纲不一致比如颜色直方图值域是 0 到 1 而 HOG 值是 0 到几百数值大的特征会直接压过数值小的模型就等于只看了部分特征。4. 系统封装与预测界面把训练好的模型做成可上传图片的 Web 页面模型训练到 90 分以上项目还差最后一步把模型变成能给别人用的系统。毕设评审现场会让操作者传图识别这时候需要一个可交互的界面而不是在 Jupyter Notebook 里跑那么多行代码。常见做法是用 Flask 包一个上传接口前端一个简单 HTML 表单后端接收图片、走特征提取、返回类别和置信度。整套封装里最容易出问题的是模型持久化和特征提取函数的一致性这一章把这两件事拆开讲透。4.1 模型持久化joblib 保存的三个对象缺一不可训练完成不等于结束模型要能保存、加载、复用。很多源码翻车就翻在只保存了分类器忘了保存特征标准化器和标签映射导致预测时特征尺度错乱输出一串谁也看不懂的数字。import joblib # 三个对象一起打包分类器、标准化器、标签映射 joblib.dump(clf, artifacts/svm_model.pkl) joblib.dump(scaler, artifacts/scaler.pkl) joblib.dump(label_map, artifacts/label_map.pkl) # 预测时一次性加载 clf joblib.load(artifacts/svm_model.pkl) scaler joblib.load(artifacts/scaler.pkl) label_map joblib.load(artifacts/label_map.pkl) # 加载后拿训练集前 10 张图做复验确认预测结果和训练时代一致 for i in range(10): feats extract_features(cv2.imread(train_paths[i])) feats scaler.transform(feats.reshape(1, -1)) pred int(clf.predict(feats)[0]) print(pred, label_map[str(pred)], train_labels[i])scaler为什么必须保存标准化器保存了训练集每个特征的均值和方差预测时用同一套参数对新特征做变换特征空间才和训练时一致。如果加载模型后重新fit_transform均值方差完全变掉SVM 的准确率会崩到接近随机。label_map保存的是数字标签到类别名的映射SVM 内部输出的永远是 0、1、2 这类整数不映射回来页面显示的就是一串数字等于没识别。复验那步看起来冗余但在毕设现场能救命——加载失败、特征函数改过、路径拼错用十张训练图一跑立刻暴露。4.2 Flask 预测接口把识别封装成可上传图片的 Web 页面Flask 封装识别服务核心就一个路由接收上传图片返回识别结果。需要注意三点模型只在启动时加载一次不要在每次请求里重复读文件上传的图片用np.frombuffer加imdecode解码不写临时文件特征提取函数必须和训练时是同一个函数不能训练时一套预测时一套。from flask import Flask, request, jsonify import cv2 import numpy as np import joblib from feature_pipeline import extract_features # 训练时的同一函数 app Flask(__name__) # 模型全局只加载一次 clf joblib.load(artifacts/svm_model.pkl) scaler joblib.load(artifacts/scaler.pkl) label_map joblib.load(artifacts/label_map.pkl) app.route(/predict, methods[POST]) def predict(): f request.files[image] # 上传文件直接解码为图像不落盘 data np.frombuffer(f.read(), dtypenp.uint8) img cv2.imdecode(data, cv2.IMREAD_COLOR) if img is None: return jsonify({error: 图像解码失败请上传 jpg/png}), 400 feats extract_features(img) feats scaler.transform(feats.reshape(1, -1)) pred int(clf.predict(feats)[0]) label label_map[str(pred)] # 置信度用 decision_function 或者 predict_probaSVM 用前者 score clf.decision_function(feats.reshape(1, -1))[0] return jsonify({prediction: label, confidence: float(score)}) if __name__ __main__: app.run(host0.0.0.0, port5000)np.frombuffer(f.read(), dtypenp.uint8)把上传的图片字节流转成 numpy 数组cv2.imdecode直接解码成图像对象绕开了cv2.imread必须读本地文件路径的约束。imdecode返回 None 说明上传的不是有效图片接口返回 400 错误比后端抛异常友好。SVM 没有predict_proba置信度用decision_function取决策函数值数值越大表示离分类边界越远越有把握。host0.0.0.0让局域网内的其他电脑也能访问答辩现场如果评委用自己电脑连过来这个设置就起作用了。前端只需要一个带文件选择和提交按钮的 HTML 页面把图片 POST 到这个接口拿 JSON 渲染到页面上。整个工程跑起来后python app.py启动服务浏览器打开http://localhost:5000就能传图识别。这个流程是这类系统最常见的落地形态代码量小、逻辑清楚、答辩演示效果直观。4.3 源码目录结构拿到项目后先看哪些文件、改哪些文件拆过几十个项目源码之后我拿到压缩包的第一反应不是找 README而是先看目录树。一棵清晰的目录结构能直接告诉你看哪些文件、哪些文件别动。水稻病虫害识别系统这类项目通常采用这样的组织方式RiceDiseaseRecognition/ ├─ data/images/ # 按类别分文件夹的图片数据 ├─ feature_pipeline.py # 特征提取统一入口被训练和预测共用 ├─ model_train.py # 训练脚本产出 artifacts 下的三个 pkl ├─ app.py # Flask 预测服务 ├─ requirements.txt # 依赖清单 └─ artifacts/ # 训练产物模型、scaler、标签映射拿到源码先看三个文件requirements.txt里依赖版本重点看 opencv 和 scikit-learn 的版本号版本不匹配是最常见的翻车源头model_train.py里数据路径和类别映射是不是写死的写死的路径要改成你自己的绝对路径feature_pipeline.py里特征提取函数的名字和参数因为app.py和训练脚本都 import 它改名就要连带改回去。数据目录替换时只需要保持文件夹名字和标签一致比如把blast换成rice_blast那label_map里的映射也要同步改否则预测输出会乱。5. 避坑指南与常见问题排查五个最容易让项目翻车的坑这一章写的全是复现这类源码时的真实翻车记录。一部分坑来自环境依赖一部分来自数据处理的隐蔽错误每一条都是我或同行实际踩过的按“现象 → 原因 → 解决”的顺序给出方便你在报错时直接对号入座。5.1 环境安装类坑OpenCV 版本、扩展模块与中文路径先看环境类最常见的三个坑。第一个是特征提取阶段直接抛AttributeError: module cv2 has no attribute xfeatures2d或者提示SIFT_create() is not a member of cv2。原因OpenCV 4.4 之后的默认安装包把 SIFT、SURF 这类原专利算法移到了扩展模块里你pip install opencv-python装的是精简版再调用旧接口cv2.xfeatures2d.SIFT_create()必然崩。解决执行pip install opencv-contrib-python把代码里的旧接口改成cv2.SIFT_create()如果你不想跟这套依赖纠缠把特征换成 ORBcv2.ORB_create()在默认安装包里就有代价是部分病害类别的准确率会降几个点。这个坑在全新环境里几乎必现建议大家装完依赖先跑一行python -c import cv2; print(cv2.__version__)确认版本。第二个坑是cv2.imread读图返回 None但文件明明存在。原因绝大多数是路径里有中文或特殊字符Windows 下imread不走 UTF-8 解码中文目录直接失败。解决不用imread改用字节流解码这个方法在第四章 Flask 里已经用过单独封装一个函数给你def imread_unicode(path): 兼容中文路径的图片读取 data np.fromfile(path, dtypenp.uint8) return cv2.imdecode(data, cv2.IMREAD_COLOR)np.fromfile把文件读成字节数组cv2.imdecode再从字节数组解码成图像完全绕开路径编码问题。这个函数放进feature_pipeline.py全项目统一调用中文路径的坑一次解决。第三个坑是训练时内存直接吃满任务管理器里 python 进程占用几个 G训练十几分钟没反应。原因HOG 的block_stride设置太密特征维度冲到几万甚至几十万SVM 在计算核函数时内存爆炸。解决先把block_stride从 (8,8) 调到 (16,16)特征维度能降四倍再把特征拼完做一遍 PCA 降到 128 维进入 SVM 之前统一降维速度和显存占用都会正常。PCA 压缩对这类高维特征几乎无损具体实现用sklearn.decomposition.PCA(n_components128)。5.2 训练与预测类坑数据泄露、过拟合与全预测成同一类第四个大坑是训练集准确率刷到 99%验证集只有 70%或者现场传几张图全是错的。这是典型的数据泄露或过拟合。数据泄露最常见的原因是增强顺序搞反了先增强全量数据再划分导致同一个原始图的多个增强副本同时出现在训练集和验证集模型相当于已经把验证集答案背下来了。过拟合则是样本太少、特征太密、C 参数调大导致的模型把训练集的细节都记住换个角度拍就认不出来。解决划分永远在增强之前增强只作用于训练集验证集图片可以手动确认一下跟训练集没有重复叶片SVM 的 C 值往小调比如从 100 降到 1正则化更强泛化通常更好。还有一个隐蔽细节同一个叶片拍的多张照片不要拆分到两个集合里按叶片分组再划分才是严格意义上的数据隔离。第五个坑在预测阶段出现训练时准确率 95%模型加载后随便传什么图都返回“健康”或同一个类别。原因排查两步先看scaler是否在预测时重新fit_transform了如果是标准化参数全漂移模型决策直接乱套再看label_map的 key 是否对上了SVM 输出整数后映射错位即使预测对了也显示成别的类别名。解决加载后立刻拿训练集前十张图跑一遍预测链路和训练时的输出逐个比对不一致就在链路里加打印看是标准化问题、特征函数问题还是映射问题。我自己的血泪经验是这类问题 90% 出在漏存或错存模型对象上所以现在每次保存模型都固定用“分类器 scaler label_map”三件套缺一个都不算完整。还有一种情况是 Flask 服务启动后预测接口报 500日志里是特征函数 import 失败或模型文件路径找不到。原因app.py和feature_pipeline.py不在同一目录相对路径拼错了。解决启动前先用python -c from feature_pipeline import extract_features验证 import 是否成功再把所有模型路径从相对路径改成基于os.path.dirname(__file__)的绝对路径不要在多个文件里手写路径字符串。6. 把模型再往深推一步用特征消融和混淆矩阵验证识别效果模型跑通只是起点答辩时评审最常问的一句话是“你凭什么说你的特征有效、你的模型可靠”。两个工具能回答这个问题特征消融实验和混淆矩阵。特征消融的思路很简单——保持同一套数据划分、同一个分类器参数只替换特征函数比较验证集准确率。这一步能让“颜色 纹理组合有效”变成一张对比表而不是一句口头描述。# 特征消融同一分类器不同特征版本对比验证集准确率 from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.metrics import accuracy_score featurizers { color_only: extract_color_feat, # 只提取颜色直方图 texture_only: extract_texture_feat, # 只提取 HOG 纹理 colortexture: extract_features, # 组合特征 } results {} for name, fn in featurizers.items(): X_train np.vstack([fn(cv2.imread(p)) for p in train_paths]) X_val np.vstack([fn(cv2.imread(p)) for p in val_paths]) sc StandardScaler().fit(X_train) clf SVC(C10, gamma0.001, class_weightbalanced) clf.fit(sc.transform(X_train), train_labels) acc accuracy_score(val_labels, clf.predict(sc.transform(X_val))) results[name] acc print(results)我在这类项目上跑出来的典型结果是单颜色特征 0.71单纹理 0.78组合特征 0.93。这个对比表一摆出来“为什么用组合特征”就没有追问空间了。注意每个特征版本必须用相同的验证集和相同的分类器参数变量的唯一差异是特征本身结论才站得住。混淆矩阵则回答“错在哪里”。用confusion_matrix加热力图可视化能直接看到哪些病害类别之间互相混淆。比如“稻瘟病”频繁被预测成“稻曲病”说明这两个类别在病斑颜色或形态上确实接近回到数据增强阶段针对性补样本。这个分析比总准确率更能体现实战能力。from sklearn.metrics import confusion_matrix, classification_report import matplotlib.pyplot as plt import seaborn as sns val_preds clf.predict(sc.transform(X_val)) classes sorted(set(val_labels)) cm confusion_matrix(val_labels, val_preds, labelsclasses) plt.figure(figsize(7, 5)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsclasses, yticklabelsclasses) plt.xlabel(预测类别) plt.ylabel(真实类别) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150) print(classification_report(val_labels, val_preds, target_namesclasses))最后分享一个我自己的教训。以前跑别人的病虫害项目所有图片预测结果都是“健康”排查了两小时发现是加载模型后忘了还原scaler重新fit_transform把特征空间全打乱了。从那以后我拿到任何源码第一件事就是先跑通官方示例再在预测链路上强制走一遍“读取 → 特征 → 标准化 → 预测”四步每一步打印输出形状和几个关键值全部对得上才换自己的数据。这个习惯帮我避掉了后面至少三次类似的坑也希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑