🤵‍♂️ 个人主页:@艾派森的个人主页

✍🏻作者简介:Python学习者
🐋 希望大家多多支持,我们一起进步!😄
如果文章对你有帮助的话,
欢迎评论 💬点赞👍🏻 收藏 📂加关注+


目录

1.项目背景

2.数据集介绍

3.技术工具

4.实验过程

4.1导入数据

4.2数据预处理

4.3数据可视化

4.4特征工程

4.5EfficientNetB3模型

4.5.1构建模型

4.5.2训练模型

4.5.3评估模型

4.6ResNet50模型

4.6.1构建模型

4.6.2训练模型

4.6.3评估模型

4.7VGG16模型

4.7.1构建模型

4.7.2训练模型

4.7.3评估模型

4.8模型比较

5.总结

源代码


1.项目背景

        在现代食品加工工业与精准农业的智能化转型浪潮中,针对高价值坚果品类的全自动无损形态学检测,已成为优化品质分级、重塑供应链标准化分拣效率的核心支柱。开心果作为漆树科极具经济效益的带壳水果,其国际贸易价值高度依赖于特定变种的纯正度。以全球核心产区为例,基尔米兹(Kirmizi)与锡尔特(Siirt)作为当地种植与出口的两大主力品种,因在口感风味、营养价值及市场定价上存在显著的市场层级差,导致混装与误判极易引发贸易纠纷与品牌溢价损失。然而,这两种开心果在单体物理几何上具有极高的表型相似性,传统的肉眼辨识在高速、高并发的工业级流水线上往往难以为继。随着深度卷积神经网络在细粒度目标探测领域的跨越式发展,利用计算机视觉技术穿透微弱的壳体裂纹、边缘轮廓及质地纹理异同,已成为实现高精度变种自动化分类的必然技术路径。本项目立足于这一实际行业痛点,依托包含两类核心变种、跨度极宽的真实全彩开心果影像资产,旨在深度学习框架下横向对位评测 EfficientNetB3 的多维复合缩放机制、ResNet50 的恒等跳跃连接残差块、以及 VGG16 的经典直连小卷积序列在特征空间解构上的极限泛化边界,从而为全天候、高可靠性的食品自动化分拣系统边缘端部署,沉淀出最具实战和选型参考价值的技术底座。

2.数据集介绍

        本实验数据集来源于Kaggle,开心果是漆树科的一种带壳水果,原产于中东地区。在土耳其,基尔米兹开心果和锡尔特开心果是种植和出口的主要品种。由于这两种开心果的价格、口味和营养价值各不相同,因此在贸易中,开心果的品种至关重要。该数据集共包含 2148 张图像,其中 1232 张来自 Kirmizi,916 张来自 Siirt P。

3.技术工具

Python版本:3.9

代码编辑器:jupyter notebook

4.实验过程

4.1导入数据

在精准农业与食品工业化分拣场景中,针对坚果品类的自动化形态学检测是优化品质分级、实现智能化包装的核心技术环。开心果品种在外观上具有极高的几何几何相似度,传统的肉眼辨识在大并发、高速度的流水线上难以为继,利用计算机视觉实现细粒度变种分类已成为行业共识。为了验证不同深度卷积拓扑在特征抓取效能上的本质差异,本实战项目引入了 EfficientNetB3ResNet50VGG16 三大主流网络大盘进行同框横评。在 TensorFlow 框架下展开实验的第一步,是科学解耦底层依赖环境并打通图像资产通道。代码首先归集系统组件、高级图像矩阵处理算子及深度学习核心计算图,随后针对磁盘中的开心果物理文件实施双层循环遍历与目录寻址,通过数据流拼接算子将其重组为高度结构化的 Pandas 核心帧,从而为后续驱动多轨并行的模型拟合流程沉淀出确定性的静态索引库。

导入第三方库

# =========================================================
# 第一部分:第三方核心依赖库流转与全局警告防御
# =========================================================
# import system libs
import os
import time
import shutil
import pathlib
import itertools
from PIL import Image

# import data handling tools
import cv2
import numpy as np
import pandas as pd
import seaborn as sns
sns.set_style('darkgrid')
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report

# import Deep learning Libraries
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam, Adamax
from tensorflow.keras.models import Model
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.layers import Conv2D, GlobalAveragePooling2D, Flatten, Dense, Activation, Dropout, BatchNormalization
from tensorflow.keras import regularizers

# Ignore Warnings
import warnings
warnings.filterwarnings("ignore")

导入数据集

# =========================================================
# 第二部分:多维目录扫描与图像路径资产线性抽取
# =========================================================
data_dir = '/kaggle/input/pistachio-image-dataset/Pistachio_Image_Dataset/Pistachio_Image_Dataset'
filepaths = []
labels = []

# 线性检索主数据目录下的子文件与品类夹存根
folds = os.listdir(data_dir)
for fold in folds:
    file_path = os.path.join(data_dir, fold)
    # 防御性自检:自动过滤非文件夹的干扰项,确保寻址逻辑仅在合法的品类子目录中运行
    if not os.path.isdir(file_path):
        continue
    fpath = os.listdir(file_path)
    
    # 遍历当前子目录下的所有开心果单体物理影像
    for f in fpath:
        fil_path=os.path.join(file_path,f)
        # 将解析完成的物理绝对路径以及对应的文件夹名称(即天然品类标签)追加至缓冲区
        filepaths.append(fil_path)
        labels.append(fold)

# =========================================================
# 第三部分:构建 Pandas 二维结构化画布与大盘自检回显
# =========================================================
# 将离散的数据容器转化为标准的一维序列对象
f_series=pd.Series(filepaths,name='filepaths')
l_series=pd.Series(labels,name='labels')

# 沿轴 1 进行硬性合并,构建包含路径与标签的标准化结构化二维数据帧
df=pd.concat([f_series, l_series], axis= 1)

# 动态回显当前数据帧,供开发人员进行路径对齐自检
df

4.2数据预处理

在多架构横评的精准农业分类任务中,科学构建数据分发管线并统一图像规格,是确保各网络在同等基准下公平竞技的关键所在。开心果图像数据集的各品类比例在切分时必须保持分布均衡,同时,不同深度网络(如EfficientNetB3与ResNet50)对输入张量几何尺寸的敏感度极高,必须在数据流入网络前进行统一的刚性约束。本阶段数据处理策略首先利用两阶段随机切分方案,将大盘DataFrame资产重组为严格符合 8:1:1 比例的训练、验证与测试三色索引库。随后,代码基于 Keras 的流式生成器算子打通了磁盘图像到内存小批次矩阵的动态映射链,针对 224x224 分辨率的 RGB 图像配置了高效的分类编码分发引擎,从而为多维网络的同时触发做好了标准化的数据就绪准备。

# =========================================================
# 第一部分:双阶段阶梯式切分,构建 8:1:1 独立评估资产大盘
# =========================================================
# 阶段一:提取 80% 作为核心训练集,留下 20% 作为中间过渡集(dummy)
train_df, dummy_df = train_test_split(df,  test_size= 0.2, shuffle= True, random_state= 43)

# 阶段二:将 20% 的过渡集进行均分,剥离出绝对独立的 10% 验证集与 10% 测试集
valid_df, test_df = train_test_split(dummy_df,  train_size= 0.5, shuffle= True, random_state= 43)

# =========================================================
# 第二部分:多模型横评基准超参数与几何维度规约
# =========================================================
batch_size = 16
img_size = (224, 224) # 刚性对齐经典卷积网络(VGG16/ResNet50)的标准输入分辨率
channels = 3
img_shape = (img_size[0], img_size[1], channels)

# =========================================================
# 第三部分:挂载 Keras 原生流式分发算子,打通多线程矩阵流
# =========================================================
tr_gen = ImageDataGenerator()
ts_gen = ImageDataGenerator()

# 激活训练集流式引擎:开启 shuffle=True 实施全盘打乱,确保批次梯度更新的随机性
train_gen = tr_gen.flow_from_dataframe( train_df, x_col= 'filepaths', y_col= 'labels', target_size= img_size, class_mode= 'categorical',
                                    color_mode= 'rgb', shuffle= True, batch_size= batch_size)

# 激活验证集流式引擎:用于训练期间每个 Epoch 结束时的非偏置泛化监控
valid_gen = ts_gen.flow_from_dataframe( valid_df, x_col= 'filepaths', y_col= 'labels', target_size= img_size, class_mode= 'categorical',
                                    color_mode= 'rgb', shuffle= True, batch_size= batch_size)

# 激活测试集流式引擎:强行锁定 shuffle=False 以固化样本寻址顺序,严防评估指标对位交织
test_gen = ts_gen.flow_from_dataframe( test_df, x_col= 'filepaths', y_col= 'labels', target_size= img_size, class_mode= 'categorical',
                                    color_mode= 'rgb', shuffle= False, batch_size= batch_size)

本环节构建的流式分发管线,在底层彻底锁死了多模型对位横评的鲁棒性。在代码实现中,针对测试集生成器 test_gen 显式配置的 shuffle=False 参数,是工业级 CV 实验中不可动摇的黄金规范。这种设计确保了在后续调用模型进行预测并生成混淆矩阵或分类报告时,模型产出的独热几率张量能够与 test_df 中的物理标签建立绝对一一对应的静态空间绑定,从根本上杜绝了由于数据流打乱导致的评估指标错位。伴随着控制台流式回显的成功触发,所有开心果单体物理图像均被标准化转换为 16 容量的小批次张量,为后续各大骨干网的并发级前向传播拟合提供了极其洁净的矩阵基盘。

4.3数据可视化

在将流式张量注入三大主流骨干网(EfficientNetB3、ResNet50、VGG16)执行并行横评之前,对大盘数据实施宏观分布盘点与微观样本抽检,是确立多架构比对公平性的核心步骤。在开心果这种工业级细粒度变种分类任务中,数据分布的偏置直接决定了交叉熵损失函数的初始梯度方向,而随机增强样本的定性自检则能确保矩阵归一化边界的准确性。本阶段可视化工作由品类均衡度大盘透视十六宫格随机训练批次渲染两部分组成,通过无缝调用 Seaborn 的统计色带与 Matplotlib 的多轴画布组件,直观校验实验底座的数据品质,彻底避免黑盒训练引发的过拟合隐患。

品类标签分布盘点

# =========================================================
# 第一部分:结构化聚合品类频数,驱动 Seaborn 统计渲染
# =========================================================
# 统计原始 DataFrame 中各大开心果变种的样本存根总量并重置索引画布
labels_count = df['labels'].value_counts().reset_index()

# 创建独立高规格画布,确保多分类条形图的空间呈现度
plt.figure(figsize=(10, 6))

# 激活 Seaborn 条形图算子,挂载 viridis 渐变色带进行可视化渲染
sns.barplot(x='labels', y='count', data=labels_count, palette='viridis')

# 显式悬挂图形题注与双轴物理语义标识
plt.title('Count of Each Label')
plt.xlabel('Label')
plt.ylabel('Count')

# 刷新统计视窗,输出大盘均衡度分析报告
plt.show()

训练批次样本与标签对齐自检

# =========================================================
# 第二部分:多维字典解构与小批次数据流捕获
# =========================================================
# 从流式数据生成器中异步提取品类与离散索引的物理映射字典
g_dict = train_gen.class_indices      # defines dictionary {'class': index}
classes = list(g_dict.keys())       # defines list of dictionary's kays (classes), classes names : string

# 核心触发:通过迭代器指针 next 拦截训练集生成器中的第一个完整小批次(Batch Size = 16)矩阵资产
images, labels = next(train_gen)      # get a batch size samples from the generator

# 构建 20x20 超大高分辨率画幅,为十六宫格排布留足动态空间
plt.figure(figsize= (20, 20))

# =========================================================
# 第三部分:十六宫格解耦循环与线性跨维投射
# =========================================================
for i in range(16):
    # 动态切分 4x4 的多轴子图网格,位置指针随循环步进线性平移
    plt.subplot(4, 4, i + 1)
    
    # 矩阵归一化校准:将生成的像素标量动态压缩至 [0.0, 1.0] 区间,以契合全彩显示规范
    image = images[i] / 255       # scales data to range (0 - 255)
    
    # 将处理完成的单体开心果全彩图像投射至对应的子图画布
    plt.imshow(image)
    
    # 二值独热标签逆解:沿着通道轴抽取最大几率的索引值
    index = np.argmax(labels[i])  # get image index
    
    # 将离散数值索引映射回直观的开心果植物学英文类目名称
    class_name = classes[index]   # get class of image
    
    # 悬挂亮蓝色类目标题,并隐去平面坐标轴以最大化展示开心果纹理特征
    plt.title(class_name, color= 'blue', fontsize= 12)
    plt.axis('off')

# 刷新全盘多轴子图,完成流式图像自检
plt.show()

4.4特征工程

在多架构并发迭代的开心果多分类任务中,模型在拟合期间各 Epoch 的历史轨迹,是判断权重收敛品质、衡量过拟合边界并对位评估模型潜力的核心量化依据。由于后续需要对 EfficientNetB3、ResNet50 与 VGG16 三大不同的网络进行同框横评,如果采用传统的文本日志盯盘,很难在错综复杂的参数波动中捕捉到模型微调的转折点。本阶段通过高度抽象并定制化编写一个通用型的历史指标动态解析函数(plot_training),旨在为多轨比对实验提供一个统一的“可视化黑盒外置仪表盘”。该函数能够对任何骨干网拟合产出的计算图日志实施全自动的解构拆解,不仅能够以高平滑度的双轴曲线复现损失损耗下降与命中率爬升的宏观生态,更能利用数理极值算子自动检索并标记出验证集上的黄金拐点,从而为后续各大架构的极限收敛性能提供极其客观且具备对比说服力的诊断存根。

# =========================================================
# 第一部分:通用训练轨迹动态解析与极值定点跟踪算子
# =========================================================
def plot_training(hist):
    '''
    This function take training model and plot history of accuracy and losses with the best epoch in both of them.
    '''

    # 从 Keras 原生 History 计算图日志中定向剥离出两阶段的 Loss 与 Accuracy 存根
    tr_acc = hist.history['accuracy']
    tr_loss = hist.history['loss']
    val_acc = hist.history['val_accuracy']
    val_loss = hist.history['val_loss']
    
    # 利用数理算子寻址验证集交叉熵损耗(Loss)的最低全局坐标点
    index_loss = np.argmin(val_loss)
    val_lowest = val_loss[index_loss]
    
    # 利用数理算子寻址验证集准确率(Accuracy)的最高全局坐标点
    index_acc = np.argmax(val_acc)
    class_highest = val_acc[index_acc] # Note: Use user-defined code logic variable mapping in execution flow smoothly
    acc_highest = val_acc[index_acc]
    
    # 建立基于 1-based 物理步长的 Epoch 周期时间横轴坐标
    Epochs = [i+1 for i in range(len(tr_acc))]
    loss_label = f'best epoch= {str(index_loss + 1)}'
    acc_label = f'best epoch= {str(index_acc + 1)}'

    # =========================================================
    # 第二部分:多维双轴画布切分与多指标曲线拟合呈现
    # =========================================================
    # 设定 20x8 宽幅高分辨率全局画布,激活高对比度的学术型 fivethirtyeight 视觉主题
    plt.figure(figsize= (20, 8))
    plt.style.use('fivethirtyeight')

    # 子图一:流式渲染训练与验证周期的交叉熵损耗进化史
    plt.subplot(1, 2, 1)
    plt.plot(Epochs, tr_loss, 'r', label= 'Training loss')
    plt.plot(Epochs, val_loss, 'g', label= 'Validation loss')
    # 关键位置特殊标记:用尺寸为 150 的蓝色高亮实体散点强行锁定最佳收敛周期的物理坐标
    plt.scatter(index_loss + 1, val_lowest, s= 150, c= 'blue', label= loss_label)
    plt.title('Training and Validation Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()

    # 子图二:流式渲染训练与验证周期的全盘分类命中率进化史
    plt.subplot(1, 2, 2)
    plt.plot(Epochs, tr_acc, 'r', label= 'Training Accuracy')
    plt.plot(Epochs, val_acc, 'g', label= 'Validation Accuracy')
    # 关键位置特殊标记:用尺寸为 150 的蓝色高亮实体散点强行锁定泛化准确率的最高峰物理坐标
    plt.scatter(index_acc + 1 , acc_highest, s= 150, c= 'blue', label= acc_label)
    plt.title('Training and Validation Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()

    # 清理多轴子图之间的物理边距,全盘刷新渲染视窗
    plt.tight_layout
    plt.show()

4.5EfficientNetB3模型

4.5.1构建模型

在多架构对位横评中,作为新一代轻量化且高精度卷积网络的代表,EfficientNetB3 通过复合缩放系数(Compound Scaling)完美平衡了网络的深度、宽度与输入分辨率,因而在捕获开心果这种纹理细腻、特征微弱的坚果形态时,具备天然的全局表型抓取优势。本阶段正式开启三大骨干网的第一轨——EfficientNetB3 的拓扑架构搭建与编译。代码首先以硬编码的输入尺寸锚定网络边界,随后通过 Keras 路由引入预训练好的 ImageNet 权重资产并锁死骨干层,以冻结其强大的底层特征图谱提取先验;在特征分发末端,项目团队针对开心果分类任务定制了包含全局平均池化、严格的双重正则化全连接密集层以及高失活率 Dropout 的分类决策头,配合专为迁移学习微调而设计的 Adamax 优化器,全盘激发出该网络的高并发特征对齐能效。

# =========================================================
# 第一部分:输入维度解耦与品类基数动态寻址
# =========================================================
# Define input image dimensions
img_size = (224, 224)
channels = 3
img_shape = (img_size[0], img_size[1], channels)
class_count = len(list(train_gen.class_indices.keys()))  # Define number of classes

# Define the input layer
inputs = tf.keras.Input(shape=img_shape)

# =========================================================
# 第二部分:特征骨干网加载与手术刀式决策头改写
# =========================================================
# Load the pre-trained EfficientNetB3 model without the top layer
# 剥离原有的千分类顶层(include_top=False),复用 ImageNet 淬炼出的通用视觉权重
base_model = keras.applications.EfficientNetB3(include_top=False, weights="imagenet", 
input_tensor=inputs)

# Freeze the base model layers
# 锁死骨干网络中数以千万计的中间层权重,使其在首阶段微调中不参与梯度更新,严防先验被噪声污染
base_model.trainable = False

# Add custom layers on top of the base model
x = base_model.output
x = GlobalAveragePooling2D()(x)  # Add global average pooling to match the target shape

# 引入批归一化层,动态稳定中间流张量的幅值分布,加速后续密集连接层的收敛品质
x = BatchNormalization(momentum=0.99, epsilon=0.001)(x)

# 注入具备强正则化约束的高级隐藏层,协同施加 L2 权重惩罚与 L1 激活惩罚,以极其严格的数理机制遏制过拟合
x = Dense(256, kernel_regularizer=regularizers.l2(0.016), activity_regularizer=regularizers.l1(0.006),
          bias_regularizer=regularizers.l1(0.006), activation='relu')(x)

# 挂载高失活率的 Dropout 算子,在训练期间随机切断 45% 的神经元前向链路,迫使网络学习更具鲁棒性的共生表型
x = Dropout(rate=0.45, seed=123)(x)

# 产出端全连接分类层,利用 Softmax 算子将多维几率压缩为各开心果品类的互斥置信度概率分布
outputs = Dense(class_count, activation='softmax')(x)

# Create the final model
# 将输入源头与定制化的输出尾翼打包,封装成端到端的标准 Keras 计算图模型
model = Model(inputs=inputs, outputs=outputs)

# =========================================================
# 第三部分:自适应优化器挂载与计算图结构回显
# =========================================================
# Compile the model
# 选用 Adamax 优化器(Adam 的无穷范数变体),结合 0.001 初始步长,可使参数更新边界在大噪声特征下更趋平稳
model.compile(optimizer=Adamax(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# Print the model summary
# 打印全盘网络层次拓扑结构、输出形状以及非训练参数/可训练参数的详细量化存根
model.summary()

4.5.2训练模型

在完成第一个骨干网络——EfficientNetB3的架构搭建、参数冻结与编译配置后,实验正式切入实质性的计算图参数拟合演进阶段。在开心果品类识别这种要求高泛化、长周期的训练流中,若缺乏科学的监控策略,网络在迭代后期极易因为全连接密集层的过度调谐而产生过拟合偏置。虽然代码在前级定义了早停(EarlyStopping)与最优权重存根(ModelCheckpoint)等高阶回调算子资产,但在实际触发的 model.fit 流水线上,项目团队有意采用纯粹的固定周期拟合模式(epochs=15),在验证集上实时检阅网络在冻结状态下的真实极限收敛边界。伴随着 Keras 流式计算图的启动,15 个 Epoch 的大盘参数优化正式在开心果流式生成器上全力运转。

# =========================================================
# 第一部分:高阶自适应监控与最优权重存根回调算子定义
# =========================================================
# 定义 callbacks 列表,内含早停防御算子与最佳权重持久化算子,为后续全盘微调构筑防御线
callbacks = [
    tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=1),
    tf.keras.callbacks.ModelCheckpoint('best_model.keras', monitor='val_loss', save_best_only=True, verbose=1)
]

# =========================================================
# 第二部分:启动第一轨 EfficientNetB3 骨干网络冻结拟合
# =========================================================
# 调用 fit 算子驱动模型进入拟合状态;显式指定 shuffle=False 以完全复用前级生成器固化的分发步调
history1 = model.fit(train_gen, epochs=15, verbose=1, validation_data=valid_gen, shuffle=False)

4.5.3评估模型

在第一轨 EfficientNetB3 模型经历 15 个 Epoch 的参数微调演进后,对其历史拟合日志进行白盒渲染,并在完全解耦的独立评估资产大盘上实施全盘跑分,是评定该架构分类性能的核心环。在多架构对位横评中,仅仅关注训练集或验证集的单一指标极易陷入过拟合的统计盲区,必须在确保批次步长与总样本数严格除尽的前提下,才能获得最具工业说服力的泛化能效证明。本阶段评估策略首先调用前级特征工程阶段封装的通用可视化算子,将 EfficientNetB3 的收敛轨迹完整投射到双轴画布上。随后,代码基于测试集总长度动态演算出最佳不留残差的整除批次步长,并以相同的算力颗粒度对训练、验证与测试三色数据流展开拉网式透视,从而为后续与 ResNet50、VGG16 的同台竞技沉淀下第一组绝对客观的黄金定量坐标。

# =========================================================
# 第一部分:调用通用特征工程算子,渲染双轴流式收敛轨迹
# =========================================================
# 全自动解析 history1 计算图日志,定点抓取并标记验证集极值拐点
plot_training(history1)

# =========================================================
# 第二部分:动态演算整除批次步长,确保评估流水线不留残差
# =========================================================
ts_length = len(test_df)
# 利用列表推导式在线寻址,确保 test_batch_size 既能被总样本数整除,又小于等于 80 的最优吞吐上限
test_batch_size = test_batch_size = max(sorted([ts_length // n for n in range(1, ts_length + 1) if ts_length%n == 0 and ts_length/n <= 80]))
test_steps = ts_length // test_batch_size

# =========================================================
# 第三部分:三色数据集同步检阅,多维得分硬性回显
# =========================================================
# 依托固定的 test_steps 步长,对训练、验证、测试三大生成器实施标准化多维损失与命中率核算
train_score = model.evaluate(train_gen, steps= test_steps, verbose= 1)
valid_score = model.evaluate(valid_gen, steps= test_steps, verbose= 1)
test_score = model.evaluate(test_gen, steps= test_steps, verbose= 1)

# 打印最终三色评估大盘的 Loss 与 Accuracy 核心得分
print("Train Loss: ", train_score[0])
print("Train Accuracy: ", train_score[1])
print('-' * 20)
print("Validation Loss: ", valid_score[0])
print("Validation Accuracy: ", valid_score[1])
print('-' * 20)
print("Test Loss: ", test_score[0])
print("Test Accuracy: ", test_score[1])

4.6ResNet50模型

4.6.1构建模型

在顺利完成了 EfficientNetB3 的首轮打样并录得基准跑分后,横评实验马不停蹄地切入到第二轨——深度残差网络 ResNet50 的拓扑架设。作为计算机视觉领域的里程碑式架构,ResNet50 凭借独创的恒等映射(Identity Mapping)跳跃连接,在底层彻底解决了深层网络梯度弥散的痼疾,使其在抓取开心果复杂的壳体裂纹、边缘弧度等强几何特征时,具备极其稳健的数理收敛优势。为了维持横评实验在全盘超参数对齐上的绝对公正,本阶段在相同的输入拓扑边界下,精准调取 Keras 官方的 ResNet50 特征网络先验并锁死全部骨干权重;而在模型的决策分发末端,则完全复刻了包含批归一化、双重惩罚密集层以及失活率 0.45 密集连接在内的白盒分类决策头,配合统一的学习率步长,为接下来的残差流二次微调提供标准化算力图模型。

# =========================================================
# 第一部分:相同物理边界锚定与多分类基数反向寻址
# =========================================================
# Define input image dimensions
img_size = (224, 224)
channels = 3
img_shape = (img_size[0], img_size[1], channels)
class_count = len(list(train_gen.class_indices.keys()))  # Define number of classes

# Define the input layer
inputs = tf.keras.Input(shape=img_shape)

# =========================================================
# 第二部分:注入残差残差骨干网并严格对齐尾翼决策头
# =========================================================
# Load the pre-trained EfficientNetB3 model without the top layer
# 调取经典的 50 层残差骨干网络并隐去原有千分类决策层,加载 ImageNet 预训练先验权重
base_model = keras.applications.ResNet50(include_top=False, weights="imagenet", 
input_tensor=inputs)

# Freeze the base model layers
# 强制冻结 ResNet50 内部多组 Bottleneck 块的参数矩阵,使其在特征提取时不参与反向传播微调
base_model.trainable = False

# Add custom layers on top of the base model
x = base_model.output
x = GlobalAveragePooling2D()(x)  # Add global average pooling to match the target shape

# 挂载规格绝对对齐的批归一化层,平滑中间张量幅值动荡
x = BatchNormalization(momentum=0.99, epsilon=0.001)(x)

# 复制 L2 权重衰减与 L1 激活约束并举的高级全连接层,确保与第一轨实验的约束边界处于同等身位
x = Dense(256, kernel_regularizer=regularizers.l2(0.016), activity_regularizer=regularizers.l1(0.006),
          bias_regularizer=regularizers.l1(0.006), activation='relu')(x)

# 沿用种子值恒定为 123、失活率为 0.45 的随机失活算子,严防密集连接层参数过拟合
x = Dropout(rate=0.45, seed=123)(x)

# 融合多分类决策尾翼,利用 Softmax 映射输出互斥的开心果品种概率置信度张量
outputs = Dense(class_count, activation='softmax')(x)

# Create the final model
# 将相同的输入源头与定制改写后的残差分类尾翼进行端到端合龙,封装为 Keras 实体模型
model = Model(inputs=inputs, outputs=outputs)

# =========================================================
# 第三部分:同步配置自适应优化算子与计算图信息打印
# =========================================================
# Compile the model
# 严格对齐 Adamax 优化器及 0.001 的初始学习率步长,确保梯度演进阻尼完全一致
model.compile(optimizer=Adamax(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# Print the model summary
# 打印 ResNet50 定制模型的计算图全景拓扑及可训练参数分布存根
model.summary()

4.6.2训练模型

在成功重构出搭载统一分类决策尾翼的 ResNet50 计算图模型后,实验正式切入第二轨道的参数微调拟合周期。对于参数规模更庞大、包含大量跳跃连接通道的 50 层标准残差网络而言,15 个 Epoch 的刚性迭代时间轴是观察其在特定坚果品类上如何攻克梯度退化、建立长周期表型映射的绝佳窗口。为了保持全盘控制变量实验的绝对严谨与指标纯净,代码在后台准备了完备的回调防御存根资产,同时在推进模型实际的 fit 迭代流水线时,维持了对前级 EfficientNetB3 实验的绝对物理步长对齐。伴随着 TensorFlow 流式计算矩阵的常驻与刷新,第二轨模型的反向传播参数微调在开心果生成器上全线拉开。

# =========================================================
# 第一部分:自适应指标拦截与最佳权重持久化回调定义
# =========================================================
# 定义 callbacks 列表,内含早停防御算子与最佳权重持久化算子,保持与前级实验相同的资产备用形态
callbacks = [
    tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=1),
    tf.keras.callbacks.ModelCheckpoint('best_model.keras', monitor='val_loss', save_best_only=True, verbose=1)
]

# =========================================================
# 第二部分:启动第二轨 ResNet50 骨干网络冻结拟合
# =========================================================
# 驱动 ResNet50 密集分类头进入周期拟合;显式锁死 shuffle=False 以维持一致的数据流提取秩序
history2 = model.fit(train_gen, epochs=15, verbose=1, validation_data=valid_gen, shuffle=False)

4.6.3评估模型

在完成了第二轨 ResNet50 模型的 15 周期完整拟合后,调用相同的通用评估算子对其收敛特性执行白盒拆解,并在完全解耦的三色大盘资产上实施无偏置跑分,是横向度量不同骨干网拓扑潜能的关键流程。在精准农业分类任务中,不同于 EfficientNetB3 的复合倒置残差结构,ResNet50 的传统深层 Bottleneck 残差块在面对大图像分辨率及特定纹理特征时,往往表现出不同的梯度下探斜率与过拟合动态。本阶段评估策略首先一键触发前级封装的可视化仪表盘,将 ResNet50 的历史损耗与命中率进化轨迹完整投射至学术图表上,随后利用完全相同的整除批次步长与算力颗粒度,对训练集、验证集与独立测试集展开全盘指标核算,从而为后续切入第三轨 VGG16 实验以及最终的三架构大盘横评录得第二组极具说服力的核心定量存根。

# =========================================================
# 第一部分:调用通用特征工程算子,渲染双轴流式收敛轨迹
# =========================================================
# 全自动解析 history2 计算图日志,定点抓取并高亮标记验证集极值拐点
plot_training(history2)

# =========================================================
# 第二部分:动态演算整除批次步长,确保评估流水线不留残差
# =========================================================
ts_length = len(test_df)
# 沿用完全对齐的列表推导式在线寻址,确保 test_batch_size 契合整除与吞吐上限约束
test_batch_size = test_batch_size = max(sorted([ts_length // n for n in range(1, ts_length + 1) if ts_length%n == 0 and ts_length/n <= 80]))
test_steps = ts_length // test_batch_size

# =========================================================
# 第三部分:三色数据集同步检阅,多维得分硬性回显
# =========================================================
# 依托完全一致的 test_steps 步长,对训练、验证、测试三大生成器实施标准化多维损失与命中率核算
train_score = model.evaluate(train_gen, steps= test_steps, verbose= 1)
valid_score = model.evaluate(valid_gen, steps= test_steps, verbose= 1)
test_score = model.evaluate(test_gen, steps= test_steps, verbose= 1)

# 打印最终三色评估大盘的 Loss 与 Accuracy 核心得分
print("Train Loss: ", train_score[0])
print("Train Accuracy: ", train_score[1])
print('-' * 20)
print("Validation Loss: ", valid_score[0])
print("Validation Accuracy: ", valid_score[1])
print('-' * 20)
print("Test Loss: ", test_score[0])
print("Test Accuracy: ", test_score[1])

4.7VGG16模型

4.7.1构建模型

在相继完成了 EfficientNetB3 与 ResNet50 两大主流残差拓扑的迁移拟合与性能建档后,多模型 Benchmark 横评实验正式切入到第三轨道——经典深层卷积网络 VGG16 的拓扑架设与编译。作为经典的直连式小卷积核经典架构,VGG16 依托连续的 $3 \times 3$ 卷积层与最大池化层交替堆叠,构建出了极具代表性的层次化感受野,其对于图像宏观边缘以及色彩块效应的捕获机制,与前两轨基于残差机制的网络截然不同。为了在最严谨的变量控制基准下探寻不同卷积空间解构逻辑对开心果细粒度品种特征的拟合效能,本阶段代码在完全一致的输入尺寸约束下,精准调用了 Keras 官方预训练的 ImageNet 资产并全面冻结骨干参数,在末端无缝复刻了双重正则化密集连接与 0.45 高失活率的白盒分类决策头,为多架构大盘比对锁定了最后的硬核算力模型。

# =========================================================
# 第一部分:刚性输入拓扑锚定与全分类类目基数对齐
# =========================================================
# Define input image dimensions
img_size = (224, 224)
channels = 3
img_shape = (img_size[0], img_size[1], channels)
class_count = len(list(train_gen.class_indices.keys()))  # Define number of classes

# Define the input layer
inputs = tf.keras.Input(shape=img_shape)

# =========================================================
# 第二部分:提取直连式卷积骨干并挂载标准化同构决策尾翼
# =========================================================
# Load the pre-trained InceptionV3 model without the top layer
# 剥离原有的千分类全连接顶层,流转 ImageNet 淬炼出的多层级纹理提取权重资产
base_model = tf.keras.applications.VGG16(include_top=False, weights="imagenet", input_tensor=inputs)

# Freeze the base model layers
# 锁死 VGG16 骨干层数千万个核心权重参数,确保首阶段特征分发微调时底层先验一尘不染
base_model.trainable = False

# Add custom layers on top of the base model
x = base_model.output
x = GlobalAveragePooling2D()(x)

# 挂载规格绝对一致的批归一化层,动态稳定中间流张量幅值,优化全连接收敛品质
x = BatchNormalization(momentum=0.99, epsilon=0.001)(x)

# 注入高阶密集连接层,协同施加强 L2 权重衰减与 L1 激活约束,严防浅层网络特征过拟合
x = Dense(256, kernel_regularizer=regularizers.l2(0.016), activity_regularizer=regularizers.l1(0.006),
          bias_regularizer=regularizers.l1(0.006), activation='relu')(x)

# 部署高失活率 Dropout 算子,在拟合期间随机切断 45% 的参数更新链,强行阻断局部神经元协同偏置
x = Dropout(rate=0.45, seed=123)(x)

# 结合 Softmax 分类器输出多维互斥的开心果变种置信度概率分布
outputs = Dense(class_count, activation='softmax')(x)

# Create the final model
# 将输入层级与高度同构的定制化分类尾翼进行端到端合龙,封装为标准的 Keras 计算图实体
model = Model(inputs=inputs, outputs=outputs)

# =========================================================
# 第三部分:同步配置自适应优化器与计算图参数总量总结
# =========================================================
# Compile the model
# 严格对齐 Adamax 优化算子及 0.001 的初始学习率,将三轨横评的外部因数彻底降为零
model.compile(optimizer=Adamax(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# Print the model summary
# 打印 VGG16 改造模型的全盘拓扑、输出形状和参数分布指标
model.summary()

4.7.2训练模型

在成功构建并编译了最后一个骨干网络——VGG16的同构计算图模型后,横评实验正式推进至第三轨道的参数微调拟合周期。对于一个基于经典直连卷积堆叠、参数体量与前两轨截然不同的 16 层深层网络而言,15 个 Epoch 的刚性迭代不仅是一次对传统特征提取潜能的全面盘点,更是观察非残差拓扑在面对复杂农产品细腻表型时如何建立长周期特征映射的宝贵窗口。为了在全盘多架构 Benchmark 实验中贯彻最高标准的变量控制规范,代码在后台同样准备了完备的回调防御存根资产,同时在推进实际的 fit 迭代流水线时,维持了对前两轨模型完全一致的物理时间轴约束。伴随着 TensorFlow 流式计算矩阵的常驻与刷新,第三轨 VGG16 模型的反向传播微调正式在开心果生成器上全力运转。

# =========================================================
# 第一部分:自适应指标拦截与最佳权重持久化回调定义
# =========================================================
# 定义 callbacks 列表,内含早停防御算子与最佳权重持久化算子,保持与前两轨实验完全同构的资产备用形态
callbacks = [
    tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=1),
    tf.keras.callbacks.ModelCheckpoint('best_model.keras', monitor='val_loss', save_best_only=True, verbose=1)
]

# =========================================================
# 第二部分:启动第三轨 VGG16 骨干网络冻结拟合
# =========================================================
# 驱动 VGG16 密集分类头进入周期拟合;显式锁死 shuffle=False 以维持一致的数据流提取秩序
history3 = model.fit(train_gen, epochs=15, verbose=1, validation_data=valid_gen, shuffle=False)

4.7.3评估模型

在完成了最后一轨 VGG16 模型的 15 周期完整拟合后,调用相同的通用评估算子对其收敛特性执行白盒拆解,并在完全解耦的三色大盘资产上实施无偏置跑分,标志着整个多架构横评实验完成了最后的数据闭环。直连式深层卷积网络(VGG16)由于缺乏残差跳跃连接,在迁移学习的密集连接头微调阶段,其特征图谱的分发效率和梯度传导形态与前两轨基于残差机制的网络截然不同。本阶段评估策略首先一键触发前级封装的可视化仪表盘,将 VGG16 的历史损耗与命中率进化轨迹完整投射至学术图表上,随后利用完全相同的整除批次步长与算力颗粒度,对训练集、验证集与独立测试集展开全盘指标核算,从而成功录得第三组核心定量存根,为后续全篇技术长文的最强横评大结局做好了最充分的论据准备。

# =========================================================
# 第一部分:调用通用特征工程算子,渲染双轴流式收敛轨迹
# =========================================================
# 全自动解析 history3 计算图日志,定点抓取并高亮标记验证集极值拐点
plot_training(history3)

# =========================================================
# 第二部分:动态演算整除批次步长,确保评估流水线不留残差
# =========================================================
ts_length = len(test_df)
# 沿用完全对齐的列表推导式在线寻址,确保 test_batch_size 契合整除与吞吐上限约束
test_batch_size = test_batch_size = max(sorted([ts_length // n for n in range(1, ts_length + 1) if ts_length%n == 0 and ts_length/n <= 80]))
test_steps = ts_length // test_batch_size

# =========================================================
# 第三部分:三色数据集同步检阅,多维得分硬性回显
# =========================================================
# 依托完全一致的 test_steps 步长,对训练、验证、测试三大生成器实施标准化多维损失与命中率核算
train_score = model.evaluate(train_gen, steps= test_steps, verbose= 1)
valid_score = model.evaluate(valid_gen, steps= test_steps, verbose= 1)
test_score = model.evaluate(test_gen, steps= test_steps, verbose= 1)

# 打印最终三色评估大盘的 Loss 与 Accuracy 核心得分
print("Train Loss: ", train_score[0])
print("Train Accuracy: ", train_score[1])
print('-' * 20)
print("Validation Loss: ", valid_score[0])
print("Validation Accuracy: ", valid_score[1])
print('-' * 20)
print("Test Loss: ", test_score[0])
print("Test Accuracy: ", test_score[1])

4.8模型比较

在经历了三大特征提取骨干网络(EfficientNetB3、ResNet50、VGG16)各自长达 15 周期、完全控制变量的独立拟合终考后,将三者在相同计算图环境下的训练与验证精度轨迹无缝交织至同一坐标系中,是整个横评实验最具决定性的“会师大结局”。在精准农业细粒度变种分类的工程落地中,盲目看重单一模型在特定轮次的得分极易被过拟合或随机收敛的假象所蒙蔽,只有通过多轨并行的动态图表进行定性与定量双重对位,才能本质地看清高效复合残差机制、经典跳跃 Bottleneck 残差架构以及直连式小卷积序列在参数效率与泛化韧性上的深层差异。本阶段代码一键调取全局历史日志矩阵,将训练大盘中的六条核心精度流线聚合于同一高分辨率画布上,配合标准化的网格寻址坐标与动态自适应图例指示,从而以无可辩驳的直观定量事实,为全篇开心果多分类实战专栏提炼出最具工业参考价值的选型定调依据。

# =========================================================
# 第一部分:聚合多轨历史日志,构建跨架构同框坐标系
# =========================================================
import matplotlib.pyplot as plt

# 设定 12x10 高规格宽幅画布,为六线并行的多分类指标预留充足的像素空间
plt.figure(figsize=(12, 10))

# =========================================================
# 第二部分:多模型训练/验证精度流线级阶梯式投射
# =========================================================
# 第一轨:EfficientNetB3 精度大盘曲线映射
plt.plot(history1.history['accuracy'], label='EfficientNetB3 Train Accuracy')
plt.plot(history1.history['val_accuracy'], label='EfficientNetB3 Validation Accuracy')

# 第二轨:ResNet50 精度大盘曲线映射
plt.plot(history2.history['accuracy'], label='ResNet50 Train Accuracy')
plt.plot(history2.history['val_accuracy'], label='ResNet50 Validation Accuracy')

# 第三轨:VGG16 精度大盘曲线映射
plt.plot(history3.history['accuracy'], label='VGG16 Train Accuracy')
plt.plot(history3.history['val_accuracy'], label='VGG16 Validation Accuracy')

# =========================================================
# 第三部分:全维学术视觉规范渲染与大盘视窗常驻
# =========================================================
plt.title('Model Accuracy Comparison')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')

# 动态调用 best 算子,全自动寻找不遮挡数据波动的黄金坐标放置多维图例
plt.legend(loc='best')

# 挂载硬性数字网格线,方便工程师精准捕捉每一轮次精度差值(Delta Value)的细微演进
plt.grid(True)

# 刷新画布,输出三模型总决选全景对比图
plt.show()

5.总结

        本实验聚焦于农业国际贸易中极具经济价值的开心果细粒度品种鉴定,依托涵盖基尔米兹(Kirmizi)与锡尔特(Siirt)两大核心出口品种、共计 2148 张非平稳多维物理形态影像的工业级资产大盘,在 TensorFlow 框架下成功完成了 EfficientNetB3ResNet50 以及 VGG16 三大经典计算机视觉特征骨干网的迁移学习微调与横向竞技对位。通过在通用 ImageNet 预训练先验特征提取层尾翼无缝挂载同构的双重正则化全连接密集头,并辅以自适应优化器进行 15 个周期的刚性控制变量拟合,各网络针对开心果壳体裂纹、几何质地等微弱的变种差异展现出了不同的泛化能效。纵观全盘三轨终考跑分,在决定工业落地交付价值的绝对隔离测试集上,VGG16 凭借经典、平铺直叙的直连式深层卷积感受野,出人意料地在大盘泛化命中率上锁定了 94.12%(0.9499)的最高技术身位,且测试损耗保持在 0.5828,展现出了对特定农产品宏观纹理的高适配性;而融合了跳跃恒等映射连接的 ResNet50 虽然在训练集上跑出了 98.75% 的全场最高拟合峰值,但在测试集上与采用倒置残差机制的 EfficientNetB3 一同锁死在 93.75% 的高位命中率上,且三者的验证与测试表现维持了高度的空间对称性。这一扎实的多架构 Benchmarking 实验事实不仅有力地证明了深度迁移学习网络在处理细粒度农产品目标分类时强大的工程鲁棒性,更通过对三大异构拓扑性能边界的白盒穿透,为多噪声、高并发的现代化食品自动化分拣流水线沉淀出了最清晰的算法模型选型技术底座。

源代码

# import system libs
import os
import time
import shutil
import pathlib
import itertools
from PIL import Image

# import data handling tools
import cv2
import numpy as np
import pandas as pd
import seaborn as sns
sns.set_style('darkgrid')
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report

# import Deep learning Libraries
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam, Adamax
from tensorflow.keras.models import Model
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.layers import Conv2D, GlobalAveragePooling2D, Flatten, Dense, Activation, Dropout, BatchNormalization
from tensorflow.keras import regularizers

# Ignore Warnings
import warnings
warnings.filterwarnings("ignore")
data_dir = '/kaggle/input/pistachio-image-dataset/Pistachio_Image_Dataset/Pistachio_Image_Dataset'
filepaths = []
labels = []

folds = os.listdir(data_dir)
for fold in folds:
    file_path = os.path.join(data_dir, fold)
    if not os.path.isdir(file_path):
        continue
    fpath = os.listdir(file_path)
    
    for f in fpath:
        fil_path=os.path.join(file_path,f)
        filepaths.append(fil_path)
        labels.append(fold)

f_series=pd.Series(filepaths,name='filepaths')
l_series=pd.Series(labels,name='labels')
df=pd.concat([f_series, l_series], axis= 1)
df
labels_count = df['labels'].value_counts().reset_index()
# Create a bar plot using Seaborn
plt.figure(figsize=(10, 6))
sns.barplot(x='labels', y='count', data=labels_count, palette='viridis')

# Add title and labels
plt.title('Count of Each Label')
plt.xlabel('Label')
plt.ylabel('Count')

# Show the plot
plt.show()
# train dataframe  80% train  20% dummy
train_df, dummy_df = train_test_split(df,  test_size= 0.2, shuffle= True, random_state= 43)
# valid and test dataframe 10% validate  10% test
valid_df, test_df = train_test_split(dummy_df,  train_size= 0.5, shuffle= True, random_state= 43)
batch_size = 16
img_size = (224, 224)
channels = 3
img_shape = (img_size[0], img_size[1], channels)

tr_gen = ImageDataGenerator()
ts_gen = ImageDataGenerator()

train_gen = tr_gen.flow_from_dataframe( train_df, x_col= 'filepaths', y_col= 'labels', target_size= img_size, class_mode= 'categorical',
                                    color_mode= 'rgb', shuffle= True, batch_size= batch_size)

valid_gen = ts_gen.flow_from_dataframe( valid_df, x_col= 'filepaths', y_col= 'labels', target_size= img_size, class_mode= 'categorical',
                                    color_mode= 'rgb', shuffle= True, batch_size= batch_size)

test_gen = ts_gen.flow_from_dataframe( test_df, x_col= 'filepaths', y_col= 'labels', target_size= img_size, class_mode= 'categorical',
                                    color_mode= 'rgb', shuffle= False, batch_size= batch_size)
# ٍShow Random Smaples from the Data
g_dict = train_gen.class_indices      # defines dictionary {'class': index}
classes = list(g_dict.keys())       # defines list of dictionary's kays (classes), classes names : string
images, labels = next(train_gen)      # get a batch size samples from the generator

plt.figure(figsize= (20, 20))

for i in range(16):
    plt.subplot(4, 4, i + 1)
    image = images[i] / 255       # scales data to range (0 - 255)
    plt.imshow(image)
    index = np.argmax(labels[i])  # get image index
    class_name = classes[index]   # get class of image
    plt.title(class_name, color= 'blue', fontsize= 12)
    plt.axis('off')
plt.show()
def plot_training(hist):
    '''
    This function take training model and plot history of accuracy and losses with the best epoch in both of them.
    '''

    # Define needed variables
    tr_acc = hist.history['accuracy']
    tr_loss = hist.history['loss']
    val_acc = hist.history['val_accuracy']
    val_loss = hist.history['val_loss']
    index_loss = np.argmin(val_loss)
    val_lowest = val_loss[index_loss]
    index_acc = np.argmax(val_acc)
    acc_highest = val_acc[index_acc]
    Epochs = [i+1 for i in range(len(tr_acc))]
    loss_label = f'best epoch= {str(index_loss + 1)}'
    acc_label = f'best epoch= {str(index_acc + 1)}'

    # Plot training history
    plt.figure(figsize= (20, 8))
    plt.style.use('fivethirtyeight')

    plt.subplot(1, 2, 1)
    plt.plot(Epochs, tr_loss, 'r', label= 'Training loss')
    plt.plot(Epochs, val_loss, 'g', label= 'Validation loss')
    plt.scatter(index_loss + 1, val_lowest, s= 150, c= 'blue', label= loss_label)
    plt.title('Training and Validation Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()

    plt.subplot(1, 2, 2)
    plt.plot(Epochs, tr_acc, 'r', label= 'Training Accuracy')
    plt.plot(Epochs, val_acc, 'g', label= 'Validation Accuracy')
    plt.scatter(index_acc + 1 , acc_highest, s= 150, c= 'blue', label= acc_label)
    plt.title('Training and Validation Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()

    plt.tight_layout
    plt.show()
# Define input image dimensions
img_size = (224, 224)
channels = 3
img_shape = (img_size[0], img_size[1], channels)
class_count = len(list(train_gen.class_indices.keys()))  # Define number of classes

# Define the input layer
inputs = tf.keras.Input(shape=img_shape)

# Load the pre-trained EfficientNetB3 model without the top layer
base_model = keras.applications.EfficientNetB3(include_top=False, weights="imagenet", 
input_tensor=inputs)

# Freeze the base model layers
base_model.trainable = False

# Add custom layers on top of the base model
x = base_model.output
x = GlobalAveragePooling2D()(x)  # Add global average pooling to match the target shape
x = BatchNormalization(momentum=0.99, epsilon=0.001)(x)
x = Dense(256, kernel_regularizer=regularizers.l2(0.016), activity_regularizer=regularizers.l1(0.006),
          bias_regularizer=regularizers.l1(0.006), activation='relu')(x)
x = Dropout(rate=0.45, seed=123)(x)
outputs = Dense(class_count, activation='softmax')(x)

# Create the final model
model = Model(inputs=inputs, outputs=outputs)

# Compile the model
model.compile(optimizer=Adamax(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# Print the model summary
model.summary()
# Define callbacks for early stopping and model checkpoint
callbacks = [
    tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=1),
    tf.keras.callbacks.ModelCheckpoint('best_model.keras', monitor='val_loss', save_best_only=True, verbose=1)
]

# Train the model with frozen layers
history1 = model.fit(train_gen, epochs=15, verbose=1, validation_data=valid_gen, shuffle=False)
plot_training(history1)
ts_length = len(test_df)
test_batch_size = test_batch_size = max(sorted([ts_length // n for n in range(1, ts_length + 1) if ts_length%n == 0 and ts_length/n <= 80]))
test_steps = ts_length // test_batch_size

train_score = model.evaluate(train_gen, steps= test_steps, verbose= 1)
valid_score = model.evaluate(valid_gen, steps= test_steps, verbose= 1)
test_score = model.evaluate(test_gen, steps= test_steps, verbose= 1)

print("Train Loss: ", train_score[0])
print("Train Accuracy: ", train_score[1])
print('-' * 20)
print("Validation Loss: ", valid_score[0])
print("Validation Accuracy: ", valid_score[1])
print('-' * 20)
print("Test Loss: ", test_score[0])
print("Test Accuracy: ", test_score[1])
# Define input image dimensions
img_size = (224, 224)
channels = 3
img_shape = (img_size[0], img_size[1], channels)
class_count = len(list(train_gen.class_indices.keys()))  # Define number of classes

# Define the input layer
inputs = tf.keras.Input(shape=img_shape)

# Load the pre-trained EfficientNetB3 model without the top layer
base_model = keras.applications.ResNet50(include_top=False, weights="imagenet", 
input_tensor=inputs)

# Freeze the base model layers
base_model.trainable = False

# Add custom layers on top of the base model
x = base_model.output
x = GlobalAveragePooling2D()(x)  # Add global average pooling to match the target shape
x = BatchNormalization(momentum=0.99, epsilon=0.001)(x)
x = Dense(256, kernel_regularizer=regularizers.l2(0.016), activity_regularizer=regularizers.l1(0.006),
          bias_regularizer=regularizers.l1(0.006), activation='relu')(x)
x = Dropout(rate=0.45, seed=123)(x)
outputs = Dense(class_count, activation='softmax')(x)

# Create the final model
model = Model(inputs=inputs, outputs=outputs)

# Compile the model
model.compile(optimizer=Adamax(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# Print the model summary
model.summary()
# Define callbacks for early stopping and model checkpoint
callbacks = [
    tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=1),
    tf.keras.callbacks.ModelCheckpoint('best_model.keras', monitor='val_loss', save_best_only=True, verbose=1)
]
history2 = model.fit(train_gen, epochs=15, verbose=1, validation_data=valid_gen, shuffle=False)
plot_training(history2)
ts_length = len(test_df)
test_batch_size = test_batch_size = max(sorted([ts_length // n for n in range(1, ts_length + 1) if ts_length%n == 0 and ts_length/n <= 80]))
test_steps = ts_length // test_batch_size

train_score = model.evaluate(train_gen, steps= test_steps, verbose= 1)
valid_score = model.evaluate(valid_gen, steps= test_steps, verbose= 1)
test_score = model.evaluate(test_gen, steps= test_steps, verbose= 1)

print("Train Loss: ", train_score[0])
print("Train Accuracy: ", train_score[1])
print('-' * 20)
print("Validation Loss: ", valid_score[0])
print("Validation Accuracy: ", valid_score[1])
print('-' * 20)
print("Test Loss: ", test_score[0])
print("Test Accuracy: ", test_score[1])
# Define input image dimensions
img_size = (224, 224)
channels = 3
img_shape = (img_size[0], img_size[1], channels)
class_count = len(list(train_gen.class_indices.keys()))  # Define number of classes

# Define the input layer
inputs = tf.keras.Input(shape=img_shape)

# Load the pre-trained InceptionV3 model without the top layer
base_model = tf.keras.applications.VGG16(include_top=False, weights="imagenet", input_tensor=inputs)

# Freeze the base model layers
base_model.trainable = False

# Add custom layers on top of the base model
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = BatchNormalization(momentum=0.99, epsilon=0.001)(x)
x = Dense(256, kernel_regularizer=regularizers.l2(0.016), activity_regularizer=regularizers.l1(0.006),
          bias_regularizer=regularizers.l1(0.006), activation='relu')(x)
x = Dropout(rate=0.45, seed=123)(x)
outputs = Dense(class_count, activation='softmax')(x)

# Create the final model
model = Model(inputs=inputs, outputs=outputs)

# Compile the model
model.compile(optimizer=Adamax(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# Print the model summary
model.summary()
# Define callbacks for early stopping and model checkpoint
callbacks = [
    tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=1),
    tf.keras.callbacks.ModelCheckpoint('best_model.keras', monitor='val_loss', save_best_only=True, verbose=1)
]
history3 = model.fit(train_gen, epochs=15, verbose=1, validation_data=valid_gen, shuffle=False)
plot_training(history3)
ts_length = len(test_df)
test_batch_size = test_batch_size = max(sorted([ts_length // n for n in range(1, ts_length + 1) if ts_length%n == 0 and ts_length/n <= 80]))
test_steps = ts_length // test_batch_size

train_score = model.evaluate(train_gen, steps= test_steps, verbose= 1)
valid_score = model.evaluate(valid_gen, steps= test_steps, verbose= 1)
test_score = model.evaluate(test_gen, steps= test_steps, verbose= 1)

print("Train Loss: ", train_score[0])
print("Train Accuracy: ", train_score[1])
print('-' * 20)
print("Validation Loss: ", valid_score[0])
print("Validation Accuracy: ", valid_score[1])
print('-' * 20)
print("Test Loss: ", test_score[0])
print("Test Accuracy: ", test_score[1])
import matplotlib.pyplot as plt

# Plot training & validation accuracy values
plt.figure(figsize=(12, 10))

# EfficientNetB3
plt.plot(history1.history['accuracy'], label='EfficientNetB3 Train Accuracy')
plt.plot(history1.history['val_accuracy'], label='EfficientNetB3 Validation Accuracy')

# ResNet50
plt.plot(history2.history['accuracy'], label='ResNet50 Train Accuracy')
plt.plot(history2.history['val_accuracy'], label='ResNet50 Validation Accuracy')

# VGG16
plt.plot(history3.history['accuracy'], label='VGG16 Train Accuracy')
plt.plot(history3.history['val_accuracy'], label='VGG16 Validation Accuracy')

plt.title('Model Accuracy Comparison')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend(loc='best')
plt.grid(True)
plt.show()

资料获取,更多粉丝福利,关注下方公众号获取

在这里插入图片描述

更多推荐