基于CNN的多种类蝴蝶图像分类

devtools/2025/3/19 2:28:18/

基于CNN的多种类蝴蝶图像分类🦋

基于卷积神经网络对6499+2786张图像,75种不同类别的蝴蝶进行可视化分析、模型训练及分类展示

导入库

import pandas as pd
import os
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing.image import load_img, img_to_array
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras import regularizers
import warningswarnings.filterwarnings("ignore", category=UserWarning, message=r"Your `PyDataset` class should call `super().__init__\(\*\*kwargs\)`")

数据分析及可视化

df = pd.read_csv("/home/mw/input/btfl7333/btfl/btfl/Training_set.csv")
df.head(10)

在这里插入图片描述

print("查看数据信息")
print(df.describe())
print("查看空值")
print(df.isnull().sum())

在这里插入图片描述
查看各个类别包含的数据量

labelcounts = df['label'].value_counts().sort_index()
plt.figure(figsize=(14, 8))
sns.barplot(x=labelcounts.index, y=labelcounts.values, palette='viridis')
plt.title('蝴蝶类型数目详细信息')
plt.xlabel('蝴蝶类型')
plt.ylabel('类别数量')
plt.xticks(rotation=90)
plt.tight_layout()
plt.show()

在这里插入图片描述
随机查看部分图片及其对应的标签

image_dir = "/home/mw/input/btfl7333/btfl/btfl/train"
sample_images = df.sample(12, random_state=43)
fig, axes = plt.subplots(4, 3, figsize=(15, 15))for i, (index, row) in enumerate(sample_images.iterrows()):img_path = os.path.join(image_dir, row['filename'])img = load_img(img_path, target_size=(150, 150))img_array = img_to_array(img) / 255.0  ax = axes[i // 3, i % 3]ax.imshow(img_array)ax.set_title(f"类别: {row['label']}")ax.axis('off')plt.tight_layout()
plt.show()

在这里插入图片描述

数据预处理

为图像分类任务准备训练和验证数据
使用train_test_split将数据集按照80%的比例划分为训练集 (train_df) 和验证集 (val_df)。
创建训练集的数据生成器,对训练数据进行数据增强,同时将标签转换为独热编码形式
创建验证集的数据生成器,对测试数据进行像素归一化

train_df, val_df = train_test_split(df, test_size=0.2, random_state=42)train_datagen = ImageDataGenerator(rescale=1./255, # 将像素值归一化到 [0, 1] 范围rotation_range=40, # 随机旋转图片,范围为0到40度width_shift_range=0.2, # 随机水平和垂直平移图片,范围为20%height_shift_range=0.2,shear_range=0.2,zoom_range=0.2, # 随机缩放图片horizontal_flip=True,fill_mode='nearest' # 在变换时填充空白区域,使用最近邻插值
)val_datagen = ImageDataGenerator(rescale=1./255)train_generator = train_datagen.flow_from_dataframe(dataframe=train_df,directory=image_dir,x_col='filename',y_col='label',target_size=(150, 150),batch_size=32,class_mode='categorical' # 将标签转换为独热编码形式
)val_generator = val_datagen.flow_from_dataframe(dataframe=val_df,directory=image_dir,x_col='filename',y_col='label',target_size=(150, 150),batch_size=32,class_mode='categorical'
)

在这里插入图片描述

展示部分处理后的数据

上一步已经对标签进行了编码

images, labels = next(train_generator)# 设置绘图参数
plt.figure(figsize=(12, 8))# 显示前10张图片及其标签
for i in range(10):plt.subplot(5, 2, i + 1)plt.imshow(images[i])  # 显示图片plt.title(f'Label: {labels[i]}')  # 显示标签plt.axis('off')  # 不显示坐标轴plt.tight_layout()
plt.show()

在这里插入图片描述

构建模型

构建的是卷积神经网络CNN的模型,如下
输入层: 形状为 (150, 150, 3) 的图像输入。
卷积层 1: 32 个卷积核,尺寸为 (3, 3),激活函数为 ReLU。
池化层 1: 最大池化层,池化窗口为 (2, 2)。
卷积层 2: 64 个卷积核,尺寸为 (3, 3),激活函数为 ReLU。
池化层 2: 最大池化层,池化窗口为 (2, 2)。
卷积层 3: 128 个卷积核,尺寸为 (3, 3),激活函数为 ReLU。
池化层 3: 最大池化层,池化窗口为 (2, 2)。
展平层: 将多维特征图展平为一维。
全连接层 1: 128 个节点,激活函数为 ReLU。
dropout 层: 以减少过拟合,丢弃率为 0.5。
全连接层 2(输出层): 节点数与类别数相同,激活函数为 softmax

# 获取类别数量
num_classes = len(train_generator.class_indices)# 构建模型
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))  # 使用 num_classes
model.summary()

在这里插入图片描述

# 编译模型
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
# 训练模型
history = model.fit(train_generator, steps_per_epoch=train_generator.n // train_generator.batch_size, validation_data=val_generator, validation_steps=val_generator.n // val_generator.batch_size, epochs=40)

在这里插入图片描述

模型评估

plt.plot(history.history['acc'], label='Train Accuracy')
plt.plot(history.history['val_acc'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend()
plt.show()

在这里插入图片描述

plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend()
plt.show()

在这里插入图片描述

# 保存模型
model.save('butterfly_classifier.h5')

使用模型进行预测展示

# 加载之前保存的模型
model = load_model('butterfly_classifier.h5')val_images, val_labels = next(val_generator)# 进行预测
predictions = model.predict(val_images)
pred_labels = np.argmax(predictions, axis=1)
true_labels = np.argmax(val_labels, axis=1)# 获取类别映射
class_indices = val_generator.class_indices
class_names = {v: k for k, v in class_indices.items()}# 定义显示图像的函数
def display_images(images, true_labels, pred_labels, class_names, num_images=9):plt.figure(figsize=(15, 15))for i in range(num_images):plt.subplot(3, 3, i + 1)plt.imshow(images[i])true_label = class_names[int(true_labels[i])]pred_label = class_names[int(pred_labels[i])]plt.title(f"True: {true_label}\nPred: {pred_label}")plt.axis('off')plt.tight_layout()plt.show()# 调用显示函数
display_images(val_images, true_labels, pred_labels, class_names, num_images=9)

在这里插入图片描述

总结

这次这个基于cnn的图像分类,获得了高于 70% 的准确率。可以加载我保存好的模型进行预测试试,感兴趣的还可以继续调参训练

# 若需要完整数据集以及代码请点击以下链接
https://mbd.pub/o/bread/aJaVmJ9s

http://www.ppmy.cn/devtools/168215.html

相关文章

Android第三次面试(Java基础)

面试题一:在 Android 里,Array 和 ArrayList 区别? 定义与大小:数组声明时要指定大小,之后固定;ArrayList 动态,无需提前定大小。性能:二者访问元素快,时间复杂度 O(1)&…

vue/react/vite前端项目打包的时候加上时间最简单版本,防止后端扯皮

如果你是vite项目,直接写一个vite的插件,通过这个插件可以动态注入环境变量,然后当打包的时候,自动注入这个时间到环境变量中,然后在项目中App.vue中或者Main.tsx中打印出来,这就知道是什么时候编译的项目了…

C51 Proteus仿真实验17:数码管显示4×4键盘矩阵按键

说明 按下任意键时,数码管都会显示其键的序号,扫描程序首先判断按键发生在哪一列,然后根据所发生的行附加不同的值,从而得到按键的序号 Proteus仿真 注意: K0、K4、K8、KC右边引脚连接的是P1.0 K1、K5、K9、KD右边引…

通向AGI的未来之路!首篇2D/视频/3D/4D统一生成框架全景综述(港科大中山等)

文章链接: https://arxiv.org/pdf/2503.04641 摘要 理解并复现现实世界是人工通用智能(AGI)研究中的一个关键挑战。为实现这一目标,许多现有方法(例如世界模型)旨在捕捉支配物理世界的基本原理&#xff0…

使用Ajax技术进行动态网页的爬虫(pycharm)

Ajax(Asynchronous JavaScript and XML)技术在现代Web开发中广泛应用。 它允许网页在不重新加载整个页面的情况下,通过JavaScript与服务器进行异步通信,动态更新部分内容。这种技术对爬虫的功能和作用产生了显著影响,…

详细讲一下 Webpack 主要生命周期钩子流程(重难点)

1. Webpack 主要生命周期钩子流程 class LifecyclePlugin {apply(compiler) {// 1. 初始化阶段compiler.hooks.initialize.tap(LifecyclePlugin, () > {console.log(1. 初始化 Webpack);});// 2. 开始编译compiler.hooks.beforeRun.tap(LifecyclePlugin, () > {console.…

【后端】【django】抛弃 Django 自带用户管理后,能否使用 `simple-jwt`?

抛弃 Django 自带用户管理后,能否使用 simple-jwt? 一、结论 是的,即使抛弃了 Django 自带的用户管理(AbstractUser 或 AbstractBaseUser),仍然可以使用 django-rest-framework-simplejwt(简称…

Java中接口隔离原则简介和代码举例

简介: 接口隔离原则(Interface Segregation Principle,ISP)是面向对象设计SOLID原则中的“I”,其核心思想是: 定义 客户端不应被迫依赖它不使用的方法。即,一个类对另一个类的依赖应建立在最…