深度学习实战-基于InceptionV3的香蕉叶斑病图像分类识别模型
♂️ 个人主页艾派森的个人主页✍作者简介Python学习者 希望大家多多支持我们一起进步如果文章对你有帮助的话欢迎评论 点赞 收藏 加关注目录1.项目背景2.数据集介绍3.技术工具4.实验过程4.1导入数据4.2数据可视化4.3构建模型4.4训练模型4.5模型评估5.总结源代码1.项目背景在现代农业生产体系中香蕉作为全球重要的经济作物与粮食安全保障其产量的稳定性直接关系到农业经济的可持续发展。然而受气候变化与跨境病害传播的影响香蕉叶斑病等真菌性病害频发尤其是 Sigatoka 等病害具有传播速度快、潜伏期长的特点传统的肉眼巡检模式不仅耗时耗力且往往在病害进入暴发期后才被察觉导致农药误用与大面积减产。如何在资源受限的农田环境下利用便携式成像设备实现病害的早期精准识别已成为智慧植保领域亟待突破的技术瓶颈。本项目旨在探索一种基于InceptionV3架构的深度学习诊断方案通过模拟植物病理学专家的判读逻辑构建自动化的病害分类模型。InceptionV3 凭借其独特的“多尺度卷积”设计能够像显微镜一样同时审视病斑的微观边缘细节与宏观分布形态这对于区分视觉特征高度相似的多种叶斑病至关重要。通过整合多维度的图像增强技术与代价敏感学习策略模型能够克服田间自然光照不均、背景杂乱等干扰因素精准捕捉病灶的核心语义。本实战展示了从农业影像的边缘特征分析到多指标模型性能评估的完整闭环不仅验证了轻量化深度学习模型在田间辅助决策中的可行性更为提升农田智能化管理效率提供了一套高精度的数字化工具。2.数据集介绍本实验数据集来源于Kaggle香蕉叶斑病BananaLSD数据集。该数据集包含一系列图像展示了三种主要的香蕉叶斑病分别是SigatokaCordanaPestalotiopsis这些图片是2021年6月在孟加拉国邦戈班杜·谢赫·穆吉布·拉赫曼农业大学及其附近香蕉田使用智能手机摄像头拍摄的。所有图片均由植物病理学专家标注。该数据集由两个子集构成。a) 原始数据集包含 937 张 RGB 图像分为 4 个类别格式为 JPG。b) 增强数据集在原始数据集的基础上每个类别增加了 400 张图像总共包含 1600 张图像。我们采用了高斯模糊、水平翻转、裁剪、线性对比度调整、剪切、平移和旋转剪切等数据增强技术提高了数据集的多样性。所有图像的标准分辨率均为 224 x 224 像素。3.技术工具Python版本:3.9代码编辑器jupyter notebook4.实验过程4.1导入数据在处理农业植保图像时环境的稳定性与数据加载的效率是模型训练成功的基石。我们首先集成了 TensorFlow、Keras 以及图像处理核心库引入了专为病害分类优化的InceptionV3预训练接口并配置了warnings过滤机制以确保实验日志的焦点。针对香蕉叶片图像的存储特性我们通过ImageDataGenerator构建了高效的数据流水线利用rescale对像素值执行归一化处理将图像统一映射至 $[0, 1]$ 区间以缓解深层神经网络在反向传播过程中的梯度消失问题。同时我们采用了 8:2 的科学配比自动划分训练集与验证集这种“流式加载”技术不仅避免了对物理内存的过度占用也确保了模型在训练过程中能够接触到具有统计代表性的多样化病害样本。import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization, GlobalAveragePooling2D from tensorflow.keras.applications import ResNet50, InceptionV3 from tensorflow.keras.applications.resnet50 import preprocess_input from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint from tensorflow.keras.utils import to_categorical from sklearn.utils import class_weight from keras.optimizers import Adam from sklearn.metrics import classification_report, confusion_matrix, precision_score, recall_score, f1_score import numpy as np from PIL import Image from keras.preprocessing.image import img_to_array, load_img import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import matplotlib.pyplot as plt import numpy as np from scipy.ndimage import sobel from skimage.filters import scharr import warnings warnings.filterwarnings(ignore) # 定义数据集根路径 data_dir /kaggle/input/banana-disease-recognition-dataset/Banana Disease Recognition Dataset/Augmented images/Augmented images # 构建数据生成器集成归一化与验证集划分逻辑 datagen ImageDataGenerator(validation_split0.2, rescale1./255) # 流式加载训练数据调整分辨率以适配网络输入 train_generator datagen.flow_from_directory( data_dir, target_size(128, 128), batch_size32, class_modecategorical, subsettraining ) # 流式加载验证数据用于监控模型泛化能力 val_generator datagen.flow_from_directory( data_dir, target_size(128, 128), batch_size32, class_modecategorical, subsetvalidation )4.2数据可视化在开始深度特征提取前我们首先从训练流中抽取一个批次的样本进行直观展示。通过构建 4 x 8 的图像矩阵我们可以清晰地观察到不同类别的香蕉叶片在色彩饱和度、受损程度以及光照条件下的表现差异。每个子图上方的索引代表了病害的类别标签这种高密度的视觉核验有助于我们确认ImageDataGenerator是否正确执行了归一化并判断数据集中是否存在低质量样本。对于香蕉叶斑病而言观察病斑在叶片上的分布规律是理解 InceptionV3 多尺度特征提取必要性的重要依据。# --- 1. 获取一个批次的数据样本 --- images, labels next(train_generator) # --- 2. 定义原始图像矩阵绘图函数 --- def plot_images(images_arr, labels_arr): # 创建 4行 8列 的大型画布 fig, axes plt.subplots(4, 8, figsize(20, 10)) axes axes.flatten() # 遍历图像、标签与坐标轴 for img, lbl, ax in zip(images_arr, labels_arr, axes): ax.imshow(img) ax.axis(off) # 隐藏坐标轴 # 显示类别索引One-hot 编码转整数 ax.set_title(np.argmax(lbl)) plt.tight_layout() plt.show() # 执行可视化 plot_images(images, labels)为了深入探究病斑的几何结构我们对图像进行了 Sobel 边缘检测处理。由于香蕉叶斑病的病灶通常伴随着明显的边缘组织坏死其梯度变化远比正常组织剧烈。通过将彩色图像转换为灰度图并利用 Sobel 算子在水平和垂直方向提取梯度模值我们成功勾勒出了病斑的轮廓。这种可视化方式模拟了模型底层卷积核边缘检测器的工作原理让我们能够预判 InceptionV3 是否能通过这些高频特征准确识别出病害的严重程度与演变趋势。# --- 3. 定义 Sobel 边缘特征提取逻辑 --- def apply_sobel(images_arr): sobel_images [] for img in images_arr: # 将 RGB 图像转换为灰度图适配梯度计算 gray_img np.dot(img[...,:3], [0.2989, 0.5870, 0.1140]) # 计算水平(X)与垂直(Y)方向的梯度 sobel_x sobel(gray_img, axis0, modeconstant) sobel_y sobel(gray_img, axis1, modeconstant) # 计算梯度幅值合成最终的边缘检测图 sobel_img np.hypot(sobel_x, sobel_y) sobel_images.append(sobel_img) return np.array(sobel_images) # 执行边缘提取处理 sobel_images apply_sobel(images) # --- 4. 绘制边缘特征对比图 --- def plot_sobel_images(original_images, sobel_images, labels_arr): fig, axes plt.subplots(4, 8, figsize(20, 10)) axes axes.flatten() for orig_img, sob_img, lbl, ax in zip(original_images, sobel_images, axes): # 使用 gray 映射以增强梯度视觉对比 ax.imshow(sob_img, cmapgray) ax.axis(off) ax.set_title(np.argmax(lbl)) plt.tight_layout() plt.show() # 执行可视化 plot_sobel_images(images, sobel_images, labels)通过这组边缘特征图可以看出病斑位置的像素梯度呈现出强烈的明暗对比而叶片的正常部分则相对平滑。这种特征分布正是卷积神经网络CNN最擅长处理的信息。InceptionV3内部的多种尺寸卷积核可以分别捕捉到微小的边缘毛刺与大面积的枯死轮廓。4.3构建模型本阶段我们采用了迁移学习Transfer Learning的高效范式。首先我们加载了在 ImageNet 上训练成熟的InceptionV3卷积基座并移除其原有的千类分类头专注于提取香蕉叶片的视觉语义。为了防止预训练权重在训练初期被破坏我们将其设定为冻结状态trainable False。在特征输出端我们引入了GlobalAveragePooling2D来实现特征的空间降维随后通过一个包含 128 个神经元的密集层进行特征整合。为了确保模型在复杂农田背景下的稳定性我们加入了BatchNormalization与Dropout(0.5)组合这不仅加速了收敛过程更有效抑制了由于病变样本差异性导致的过拟合风险。# --- 1. 载入预训练 InceptionV3 基座 --- # include_topFalse 表示剔除原有的分类层input_shape 适配我们预处理的尺寸 base_model_inception InceptionV3( weightsimagenet, include_topFalse, input_shape(128, 128, 3) ) # --- 2. 组建顺序网络模型 --- model_inception Sequential([ base_model_inception, # 卷积特征提取引擎 GlobalAveragePooling2D(), # 全局平均池化压缩空间维度 Dense(128, activationrelu), # 全连接层进行高阶语义融合 BatchNormalization(), # 批标准化稳定梯度分布 Dropout(0.5), # 正则化随机失活 50% 神经元 Dense(7, activationsoftmax) # 输出层对应 7 类香蕉病害分布 ]) # --- 3. 锁定预训练权重 --- # 仅训练顶层的自定义分类逻辑保留预训练的通用特征提取本能 base_model_inception.trainable False # --- 4. 配置编译策略 --- # 使用 Adam 优化器同步监控多维性能指标准确率、精确率、召回率 model_inception.compile( optimizeradam, losscategorical_crossentropy, metrics[accuracy, tf.keras.metrics.Precision(), tf.keras.metrics.Recall()] ) # 打印参数概览确认可训练参数量级 model_inception.summary()通过模型摘要Summary可以看到虽然总参数量达到了千万级但由于冻结了卷积基座实际需要训练的参数仅集中在顶层的分类头。这种“四两拨千斤”的设计在智慧农业实战中非常关键它让模型能够借用 InceptionV3 对纹理、边缘和形状的敏感性而我们只需要教会它如何将这些特征与具体的“黑星病”、“黄条叶斑病”等标签关联起来。此外同步监控Precision与Recall指标能够让我们在训练过程中实时掌握模型在避免“误诊”与防止“漏诊”之间的平衡状态这对于植保决策的科学性至关重要。4.4训练模型本阶段的训练核心在于平衡与稳定性。由于香蕉叶斑病数据集各类别间存在样本量差异我们首先调用了 Scikit-learn 的compute_class_weight算子根据训练集中各标签的频率自动计算补偿系数。这意味着在反向传播过程中模型对于样本量较少的病害类型产生的误差会表现得更加“敏感”。在fit执行阶段我们将最大迭代周期设定为 50 轮并实时监控验证集的反馈。通过将计算出的权重字典注入训练过程InceptionV3 能够在每一批次的梯度更新中针对性地强化对难辨识、稀缺病斑特征的记忆最终在多元化的农田环境下达到全局最优的判别状态。# --- 1. 计算类别权重应对样本分布不均 --- # balanced 模式会自动根据样本频率分配权重使得少数类在训练中获得更多关注 class_weights class_weight.compute_class_weight( balanced, classesnp.unique(train_generator.classes), ytrain_generator.classes ) # 将权重数组转换为字典格式适配 Keras 训练接口 class_weights {i : class_weights[i] for i in range(len(class_weights))} # --- 2. 启动模型拟合程序 --- # 结合训练生成器与验证流开启为期 50 轮的特征强化训练 history_inception model_inception.fit( train_generator, epochs50, validation_dataval_generator, class_weightclass_weights # 注入类别权重实现代价敏感学习 )在训练过程中观察loss与val_loss的交织下降是判定模型健康度的关键。对于香蕉叶片这种纹理复杂的样本类权重的介入有效防止了模型因贪婪追求整体准确率而忽略了像“早期潜伏病害”这类关键样本。随着训练的深入我们可以发现验证集上的Precision和Recall能够同步攀升这证明了 InceptionV3 已经跨越了过拟合的陷阱开始真正理解香蕉叶片从健康到各类病变转化的内在视觉逻辑。这种经过加权修正的权重文件将为后续的田间实测提供坚实的分类性能保障。4.5模型评估为了直观呈现模型在 50 轮迭代中的学习轨迹我们编写了专用的性能可视化函数对比训练集与验证集的表现差异。通过观察 Accuracy 曲线我们可以判定模型是在稳步提取特征还是陷入了局部最优而 Loss 曲线的收敛态势则揭示了优化器在梯度更新过程中的效率。随后我们引入了混淆矩阵Confusion Matrix与详细的分类报告利用热力图直观展示模型在 7 类香蕉病害之间的判别精度。这种矩阵式的反馈能够精准暴露模型对哪些形态相似的病斑如早期叶斑与普通病害纹理存在误判从而为后续的模型调优提供明确的数据导向。# --- 1. 定义性能轨迹绘图函数 --- def plot_performance(history, title): 绘制模型在训练周期内的准确率与损失值对比图 plt.figure(figsize(14, 5)) # 子图1准确率曲线对比 plt.subplot(1, 2, 1) plt.plot(history.history[accuracy], labeltrain_accuracy) plt.plot(history.history[val_accuracy], labelval_accuracy) plt.title(f{title} Accuracy) plt.xlabel(Epochs) plt.ylabel(Accuracy) plt.legend() # 子图2损失函数收敛曲线 plt.subplot(1, 2, 2) plt.plot(history.history[loss], labeltrain_loss) plt.plot(history.history[val_loss], labelval_loss) plt.title(f{title} Loss) plt.xlabel(Epochs) plt.ylabel(Loss) plt.legend() plt.show() # --- 2. 定义多维指标评估函数 --- def evaluate_model(model, generator): 计算混淆矩阵并生成分类性能报告 y_true generator.classes # 获取模型对验证集的预测概率分布 y_pred model.predict(generator) # 取概率最大的索引作为预测类别 y_pred_classes np.argmax(y_pred, axis1) # 构建混淆矩阵与文字报告 cm confusion_matrix(y_true, y_pred_classes) cr classification_report(y_true, y_pred_classes, target_namesgenerator.class_indices.keys()) # 可视化混淆矩阵热力图 plt.figure(figsize(8, 6)) sns.heatmap( cm, annotTrue, fmtd, cmapBlues, xticklabelsgenerator.class_indices.keys(), yticklabelsgenerator.class_indices.keys() ) plt.title(Confusion Matrix) plt.xlabel(Predicted) plt.ylabel(True) plt.show() print(cr) # --- 3. 执行评估流程 --- plot_performance(history_inception, InceptionV3) evaluate_model(model_inception, val_generator)5.总结本实验基于在孟加拉国实地采集的 BananaLSD 香蕉叶斑病数据集成功构建了基于InceptionV3的病害识别模型。该数据集涵盖了 Sigatoka、Cordana 及 Pestalotiopsis 三种主要病害并通过高斯模糊、线性对比度调整等多种增强技术将样本扩充至 1600 张显著提升了模型对复杂农田环境的适应力。实验结果显示模型在训练集上表现卓越准确率达到了98.91%且精确率与召回率均突破98.8%充分验证了 Inception 架构在多尺度病斑特征捕捉上的核心优势。尽管验证集准确率76.45%与训练集之间存在一定跨度反映出真实自然场景下病害识别的高度复杂性但模型已能够精准锁定专家标注的关键病理纹理。本研究不仅证明了利用智能手机终端进行田间病害初筛的技术可行性也为后续通过模型微调进一步优化泛化能力、实现智慧农业监测设备的端侧部署提供了重要的实证参考。源代码import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization, GlobalAveragePooling2D from tensorflow.keras.applications import ResNet50, InceptionV3 from tensorflow.keras.applications.resnet50 import preprocess_input from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint from tensorflow.keras.utils import to_categorical from sklearn.utils import class_weight from keras.optimizers import Adam from sklearn.metrics import classification_report, confusion_matrix, precision_score, recall_score, f1_score import numpy as np from PIL import Image from keras.preprocessing.image import img_to_array, load_img import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import matplotlib.pyplot as plt import numpy as np from scipy.ndimage import sobel from skimage.filters import scharr import warnings warnings.filterwarnings(ignore) data_dir /kaggle/input/banana-disease-recognition-dataset/Banana Disease Recognition Dataset/Augmented images/Augmented images datagen ImageDataGenerator(validation_split0.2, rescale1./255) train_generator datagen.flow_from_directory(data_dir, target_size(128, 128), batch_size32, class_modecategorical, subsettraining) val_generator datagen.flow_from_directory(data_dir, target_size(128, 128), batch_size32, class_modecategorical, subsetvalidation) images, labels next(train_generator) def plot_images(images_arr, labels_arr): fig, axes plt.subplots(4, 8, figsize(20, 10)) axes axes.flatten() for img, lbl, ax in zip(images_arr, labels_arr, axes): ax.imshow(img) ax.axis(off) ax.set_title(np.argmax(lbl)) plt.tight_layout() plt.show() plot_images(images, labels) def apply_sobel(images_arr): sobel_images [] for img in images_arr: gray_img np.dot(img[...,:3], [0.2989, 0.5870, 0.1140]) sobel_x sobel(gray_img, axis0, modeconstant) sobel_y sobel(gray_img, axis1, modeconstant) sobel_img np.hypot(sobel_x, sobel_y) sobel_images.append(sobel_img) return np.array(sobel_images) sobel_images apply_sobel(images) def plot_sobel_images(original_images, sobel_images, labels_arr): fig, axes plt.subplots(4, 8, figsize(20, 10)) axes axes.flatten() for orig_img, sob_img, lbl, ax in zip(original_images, sobel_images, labels_arr, axes): ax.imshow(sob_img, cmapgray) ax.axis(off) ax.set_title(np.argmax(lbl)) plt.tight_layout() plt.show() plot_sobel_images(images, sobel_images, labels) base_model_inception InceptionV3(weightsimagenet, include_topFalse, input_shape(128, 128, 3)) model_inception Sequential([ base_model_inception, GlobalAveragePooling2D(), Dense(128, activationrelu), BatchNormalization(), Dropout(0.5), Dense(7, activationsoftmax) ]) base_model_inception.trainable False model_inception.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy, tf.keras.metrics.Precision(), tf.keras.metrics.Recall()]) model_inception.summary() class_weights class_weight.compute_class_weight(balanced, classesnp.unique(train_generator.classes), ytrain_generator.classes) class_weights {i : class_weights[i] for i in range(len(class_weights))} history_inception model_inception.fit(train_generator, epochs50, validation_dataval_generator, class_weightclass_weights) def plot_performance(history, title): plt.figure(figsize(14, 5)) plt.subplot(1, 2, 1) plt.plot(history.history[accuracy], labeltrain_accuracy) plt.plot(history.history[val_accuracy], labelval_accuracy) plt.title(f{title} Accuracy) plt.legend() plt.subplot(1, 2, 2) plt.plot(history.history[loss], labeltrain_loss) plt.plot(history.history[val_loss], labelval_loss) plt.title(f{title} Loss) plt.legend() plt.show() def evaluate_model(model, generator): y_true generator.classes y_pred model.predict(generator) y_pred_classes np.argmax(y_pred, axis1) cm confusion_matrix(y_true, y_pred_classes) cr classification_report(y_true, y_pred_classes, target_namesgenerator.class_indices.keys()) plt.figure(figsize(8, 6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsgenerator.class_indices.keys(), yticklabelsgenerator.class_indices.keys()) plt.title(Confusion Matrix) plt.xlabel(Predicted) plt.ylabel(True) plt.show() plot_performance(history_inception, InceptionV3) evaluate_model(model_inception, val_generator)资料获取更多粉丝福利关注下方公众号获取