
简介这份资源是面向高校学生与深度学习入门者的计算机视觉大作业完整方案围绕Python、TensorFlow与CNN实现花卉图像识别适合课程设计、期末大作业及新手练手参考。压缩包共13个文件约10.82MB以6个py源码文件为核心涵盖模型定义、数据读取、训练与GUI界面等模块另附docx实验报告、pptx演示文稿、yaml与txt环境配置、md说明文档及rar数据压缩包结构清晰、便于按模块查阅。目前已有615人学习下载具备一定参考热度。代码含详细注释读者可据此理解卷积神经网络的数据预处理、模型搭建、训练调参与识别流程并借助实验报告与演示文稿快速梳理实验思路、撰写文档降低部署与调试门槛对需要完成同类图像分类任务的学习者具有较高的实用价值。1. 花卉识别大作业从数据集到 CNN 模型一套能跑通的 Python 方案做计算机视觉大作业最怕的不是模型不会写而是环境装到一半报错、数据集格式对不上、训练完准确率上不去还找不到原因。花卉图像识别这个题目在课程设计里出现频率极高原因是它同时踩中了几个关键点类别多但边界清晰、数据量适中、CNN 特征提取能力刚好够用而且最终效果肉眼可见答辩时好展示。这套方案基于 Python TensorFlow CNN覆盖从数据准备、模型搭建、训练调参到实验报告撰写的完整链路。适合正在做计算机视觉大作业的本科生也适合刚入门深度学习、想找一个完整项目练手的开发者。下面按实际动手顺序展开每一步都给出可复现的代码和参数说明。2. 数据集准备与预处理把花卉图片变成 CNN 能吃的张量2.1 选哪个数据集、为什么花卉识别最常用的公开数据集是 Oxford Flowers 102包含 102 类花卉、每类 40 到 258 张不等总计约 8000 张图片。这个数据集的好处是类别足够多能体现 CNN 的层次特征提取能力缺点是类别间样本不均衡直接训练容易偏向样本多的类。如果大作业要求没那么高也可以自己从网上爬取 5 到 10 类常见花卉玫瑰、向日葵、郁金香等每类 200 张左右用python爬虫批量下载后人工清洗。自己爬的数据集更贴近实际工程场景但要注意图片质量和标注准确性。我一般建议如果时间充裕用自建数据集因为答辩时能讲清楚数据来源和清洗过程如果时间紧直接用 Oxford Flowers 102把精力放在模型和调参上。2.2 目录结构与数据划分TensorFlow 的image_dataset_from_directory要求数据按类别分文件夹存放目录结构如下flowers/ ├── train/ │ ├── daisy/ │ ├── rose/ │ ├── sunflower/ │ └── tulip/ ├── val/ │ ├── daisy/ │ ├── rose/ │ ├── sunflower/ │ └── tulip/ └── test/ ├── daisy/ ├── rose/ ├── sunflower/ └── tulip/划分比例建议 7:1.5:1.5即训练集 70%、验证集 15%、测试集 15%。注意验证集和测试集要严格分开验证集用于调参和早停测试集只在最终评估时用一次。2.3 用 TensorFlow 加载与增强import tensorflow as tf IMG_SIZE (224, 224) BATCH_SIZE 32 # 训练集带数据增强 train_ds tf.keras.utils.image_dataset_from_directory( flowers/train, image_sizeIMG_SIZE, batch_sizeBATCH_SIZE, label_modecategorical, shuffleTrue, seed42 ) # 验证集不做增强只做归一化 val_ds tf.keras.utils.image_dataset_from_directory( flowers/val, image_sizeIMG_SIZE, batch_sizeBATCH_SIZE, label_modecategorical, shuffleFalse ) # 数据增强层随机翻转、旋转、缩放 data_augmentation tf.keras.Sequential([ tf.keras.layers.RandomFlip(horizontal), tf.keras.layers.RandomRotation(0.2), tf.keras.layers.RandomZoom(0.2), ]) # 归一化把像素值从 [0,255] 映射到 [0,1] normalization tf.keras.layers.Rescaling(1./255) # 构建最终输入管道 train_ds train_ds.map(lambda x, y: (normalization(data_augmentation(x, trainingTrue)), y)) val_ds val_ds.map(lambda x, y: (normalization(x), y)) # 预取数据减少 I/O 等待 train_ds train_ds.prefetch(buffer_sizetf.data.AUTOTUNE) val_ds val_ds.prefetch(buffer_sizetf.data.AUTOTUNE)这段代码做了三件事加载图片并按类别编码标签、对训练集做在线增强、对像素值做归一化。image_dataset_from_directory会自动根据子文件夹名生成类别索引label_modecategorical表示输出 one-hot 编码配合categorical_crossentropy损失函数使用。RandomFlip、RandomRotation、RandomZoom是三种最基础也最有效的增强手段能显著降低过拟合。prefetch让 CPU 在 GPU 训练时提前准备下一批数据避免 GPU 空转。注意验证集和测试集绝对不能做数据增强否则评估结果会失真。归一化则必须做且训练集、验证集、测试集要用同一个归一化参数。3. CNN 模型搭建从三层卷积到迁移学习的选型对比3.1 从零搭一个 CNN结构设计与参数含义from tensorflow.keras import layers, models def build_cnn(num_classes): model models.Sequential([ # 第一层卷积提取边缘、纹理等低级特征 layers.Conv2D(32, (3, 3), activationrelu, paddingsame, input_shape(224, 224, 3)), layers.MaxPooling2D((2, 2)), # 第二层卷积提取花瓣形状、颜色分布等中级特征 layers.Conv2D(64, (3, 3), activationrelu, paddingsame), layers.MaxPooling2D((2, 2)), # 第三层卷积提取更复杂的组合特征 layers.Conv2D(128, (3, 3), activationrelu, paddingsame), layers.MaxPooling2D((2, 2)), # 第四层卷积进一步抽象 layers.Conv2D(128, (3, 3), activationrelu, paddingsame), layers.MaxPooling2D((2, 2)), # 全局平均池化替代 Flatten减少参数量 layers.GlobalAveragePooling2D(), # 全连接层做最终分类 layers.Dense(256, activationrelu), layers.Dropout(0.5), layers.Dense(num_classes, activationsoftmax) ]) return model model build_cnn(num_classes5) model.summary()Conv2D的第一个参数是卷积核数量决定了输出特征图的通道数。3×3 是最常用的卷积核尺寸在感受野和计算量之间取得平衡。paddingsame保证输出尺寸和输入一致避免边缘信息丢失。MaxPooling2D每次把特征图尺寸减半逐步扩大感受野。GlobalAveragePooling2D把每个通道的特征图压缩成一个值比Flatten参数量少得多也不容易过拟合。Dropout(0.5)在训练时随机丢弃一半神经元是防止过拟合的经典手段。3.2 迁移学习什么时候该用预训练模型如果自己搭的 CNN 训练几轮后验证集准确率卡在 60% 上不去或者训练集准确率远高于验证集说明模型容量不够或者数据量不足。这时候迁移学习是更务实的选择。用EfficientNetB0或MobileNetV2在 ImageNet 上的预训练权重冻结卷积基只训练分类头base_model tf.keras.applications.EfficientNetB0( include_topFalse, weightsimagenet, input_shape(224, 224, 3) ) base_model.trainable False # 冻结卷积基 model models.Sequential([ base_model, layers.GlobalAveragePooling2D(), layers.Dropout(0.3), layers.Dense(num_classes, activationsoftmax) ]) model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), losscategorical_crossentropy, metrics[accuracy] )冻结卷积基意味着反向传播不会更新预训练权重只训练最后的分类层。这样做的好处是训练快、不容易过拟合适合数据量小于 5000 张的场景。如果数据量超过 1 万张可以解冻最后几个卷积块做微调学习率要调小到 1e-5 量级。3.3 编译与训练优化器、学习率、早停怎么设callbacks [ tf.keras.callbacks.EarlyStopping( monitorval_loss, patience5, restore_best_weightsTrue ), tf.keras.callbacks.ReduceLROnPlateau( monitorval_loss, factor0.5, patience3, min_lr1e-6 ) ] history model.fit( train_ds, validation_dataval_ds, epochs50, callbackscallbacks )EarlyStopping在验证损失连续 5 轮不下降时停止训练并恢复最佳权重。ReduceLROnPlateau在验证损失停滞时把学习率减半帮助模型跳出局部最优。这两个回调配合使用基本不需要手动调训练轮数。Adam优化器默认学习率 1e-3 适合从零训练微调时改成 1e-5。损失函数用categorical_crossentropy对应 one-hot 标签如果是整数标签则用sparse_categorical_crossentropy。4. 训练过程避坑准确率不涨、loss 震荡、显存爆了的排查手册4.1 训练集准确率 99%验证集只有 50%现象训练几轮后训练集准确率飙升到 99% 以上但验证集准确率在 50% 到 60% 之间徘徊验证损失先降后升。原因典型过拟合。模型记住了训练集的噪声和细节没有学到泛化特征。常见诱因是数据量太少、模型参数量太大、没有做数据增强或 Dropout。解决先加数据增强RandomFlip、RandomRotation、RandomZoom再在卷积层后加 Dropout0.3 到 0.5最后考虑减小模型容量或改用迁移学习。如果这三步做完还不行说明数据本身有问题检查是否有标注错误或类别混淆。4.2 loss 震荡剧烈准确率忽高忽低现象训练损失在 0.5 到 2.0 之间反复跳动准确率每轮波动超过 10%。原因学习率太大或者 batch size 太小导致梯度估计方差大。另外如果数据没有打乱同一批数据顺序固定也会造成震荡。解决把学习率降低一个数量级从 1e-3 降到 1e-4batch size 从 16 提到 32 或 64。确认image_dataset_from_directory的shuffleTrue只对训练集开启。如果用了ReduceLROnPlateau观察学习率是否在正常下降。4.3 GPU 显存不足报 OOM 错误现象训练开始几秒后报ResourceExhaustedError: OOM when allocating tensor。原因batch size 太大、图片分辨率太高、模型参数量太大三者叠加超出显存容量。解决按优先级依次尝试——把 batch size 减半、把输入尺寸从 224 降到 128、改用 MobileNetV2 等轻量模型、开启混合精度训练tf.keras.mixed_precision.set_global_policy(mixed_float16)。如果显存实在太小用梯度累积模拟大 batch。4.4 验证集准确率比训练集还高现象前几轮验证集准确率高于训练集之后才慢慢反转。原因训练时开启了 Dropout 和数据增强验证时关闭了这两者导致训练集评估更“难”。这是正常现象不是 bug。解决不用处理。只要最终验证集准确率收敛到合理水平即可。如果验证集准确率始终高于训练集且差距很大检查验证集是否和训练集有重叠图片。4.5 模型保存后加载预测结果全错现象训练完保存模型重新加载后对同一张图片预测结果和保存前不一致。原因保存时只保存了权重没有保存预处理逻辑或者加载时没有重建相同的模型结构。解决用model.save(flower_model.h5)保存完整模型结构权重优化器状态加载时用tf.keras.models.load_model(flower_model.h5)。如果只保存了权重必须先用相同的代码重建模型结构再load_weights。另外预测时的预处理必须和训练时完全一致包括归一化参数和图片尺寸。5. 实验报告怎么写从准确率曲线到混淆矩阵的完整模板5.1 训练过程可视化loss 和 accuracy 双曲线import matplotlib.pyplot as plt def plot_history(history): fig, (ax1, ax2) plt.subplots(1, 2, figsize(12, 4)) ax1.plot(history.history[loss], labelTrain Loss) ax1.plot(history.history[val_loss], labelVal Loss) ax1.set_title(Loss Curve) ax1.set_xlabel(Epoch) ax1.set_ylabel(Loss) ax1.legend() ax2.plot(history.history[accuracy], labelTrain Acc) ax2.plot(history.history[val_accuracy], labelVal Acc) ax2.set_title(Accuracy Curve) ax2.set_xlabel(Epoch) ax2.set_ylabel(Accuracy) ax2.legend() plt.tight_layout() plt.savefig(training_curve.png, dpi150) plt.show() plot_history(history)这张图是实验报告的核心素材。训练损失和验证损失如果同步下降且最终接近说明模型泛化良好如果验证损失先降后升说明过拟合需要在报告中分析原因并给出改进措施。准确率曲线同理。5.2 混淆矩阵与分类报告import numpy as np from sklearn.metrics import confusion_matrix, classification_report import seaborn as sns # 在测试集上预测 test_ds tf.keras.utils.image_dataset_from_directory( flowers/test, image_size(224, 224), batch_size32, label_modecategorical, shuffleFalse ) test_ds test_ds.map(lambda x, y: (normalization(x), y)) y_pred model.predict(test_ds) y_pred_classes np.argmax(y_pred, axis1) y_true np.concatenate([y for x, y in test_ds], axis0) y_true_classes np.argmax(y_true, axis1) # 混淆矩阵 cm confusion_matrix(y_true_classes, y_pred_classes) plt.figure(figsize(8, 6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues) plt.xlabel(Predicted) plt.ylabel(True) plt.title(Confusion Matrix) plt.savefig(confusion_matrix.png, dpi150) plt.show() # 分类报告 print(classification_report(y_true_classes, y_pred_classes, target_namesclass_names))混淆矩阵能直观看出哪些类别容易被混淆。比如玫瑰和郁金香如果互相误判率高说明两者的颜色和形状特征在 CNN 提取的特征空间中距离较近可以在报告中分析原因并提出改进方案增加这两类的训练样本、引入注意力机制等。classification_report给出每个类别的精确率、召回率和 F1 分数是实验报告里必须放的表格。5.3 报告结构建议实验报告按以下结构写每部分控制在 300 到 500 字章节内容要点实验目的掌握 CNN 原理、TensorFlow 使用、图像分类流程数据集介绍来源、类别数、样本量、划分比例、预处理方式模型结构每层类型、参数、输出尺寸、参数量统计训练配置优化器、学习率、batch size、epochs、回调策略实验结果loss/accuracy 曲线、混淆矩阵、分类报告问题与分析遇到的过拟合/震荡/显存问题及解决过程改进方向迁移学习、注意力机制、超参搜索等报告里不要只贴代码重点写清楚“为什么这样选”和“遇到问题怎么排查”。答辩老师最关注的是你对模型行为的理解而不是代码本身。6. 把准确率从 85% 推到 95% 的三个技巧第一个技巧是分层学习率。用迁移学习时卷积基的学习率设成 1e-5分类头设成 1e-3这样预训练权重只做微调分类头快速收敛。实现方式是用tf.keras.optimizers.Adam配合参数分组或者分阶段训练——先冻结卷积基训练 10 轮再解冻最后 20 层用低学习率训练 20 轮。我一般用后者代码简单且效果稳定。第二个技巧是测试时增强TTA。对同一张测试图片做多次增强翻转、旋转、缩放分别预测后取平均概率。这个技巧在数据量小的时候提升明显通常能涨 1 到 3 个百分点。代码实现只需要在预测时循环调用增强层把多次预测结果做平均。def predict_with_tta(model, image, n_aug5): preds [] for _ in range(n_aug): augmented data_augmentation(tf.expand_dims(image, 0), trainingTrue) pred model.predict(augmented, verbose0) preds.append(pred) return np.mean(preds, axis0)第三个技巧是类别权重平衡。如果数据集类别不均衡用class_weight参数给样本少的类别更高权重避免模型偏向多数类。计算方式是用sklearn.utils.class_weight.compute_class_weight生成权重字典传给model.fit的class_weight参数。这三个技巧按优先级排序先做分层学习率再做类别权重最后上 TTA。每一步做完都在验证集上评估确认有提升再继续。不要一次性全加上否则出了问题不知道是哪个环节导致的。我在这个方向上踩过最大的坑是一开始追求模型复杂度堆了十几层卷积结果训练慢、过拟合严重、调参调到怀疑人生。后来换成 EfficientNetB0 加简单的分类头半天就跑到 95% 以上。教训是——大作业的核心不是模型多深而是流程完整、分析到位、结果可复现。希望帮到你。本文还有配套的精品资源点击获取