前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于卷积神经网络CNN的图像分类

基于卷积神经网络CNN的图像分类

原创
作者头像
皮大大
发布2023-05-05 10:12:04
1.4K0
发布2023-05-05 10:12:04
举报
文章被收录于专栏:机器学习/数据可视化

基于卷积神经网络CNN的图像分类+基于Tkinter自制GUI界面点击分类

大家好,我是Peter~

本文主要包含两个方向的内容:

  1. 如何使用卷积神经网路对一份数据进行catsdogs的分类:图像数据生成、搭建CNN模型及可视化、模型训练与预测、损失精度可视化
  2. 将构建的CNN网络模型保存后,基于Tkinter制作一个简单的GUI界面,选择图片运行立即显示分类结果

过程详解,代码注释极其详细,源码运行即可出结果,极度适合入门。

导入库

导入建模相关的库:

代码语言:python
代码运行次数:0
复制
import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
%matplotlib inline
import random
import os

import tensorflow as tf
from keras import layers
from keras import models
from keras.models import Sequential
from keras.layers import Conv2D,MaxPooling2D,Dropout,Flatten,Dense,Activation,BatchNormalization
from keras.preprocessing.image import ImageDataGenerator,load_img
from keras.utils.np_utils import to_categorical
from sklearn.model_selection import train_test_split

数据准备

代码语言:python
代码运行次数:0
复制
filenames=os.listdir("./train")

categories=[]

for file in filenames:  # 遍历每张图片
    category=file.split('.')[0]  # 根据图片名称的首位进行判断
    
    if category=='dog':  # cat-0  dog-1
        categories.append(1)
    else:
        categories.append(0)
        
df=pd.DataFrame({
    'filename': filenames,
    'category': categories
})
df.head()

总共是2000*2

数据预处理

训练集和验证集

代码语言:python
代码运行次数:0
复制
df["category"] = df["category"].map({0:"cat",1:"dog"})

将train中的数据分成训练集 + 验证集:

代码语言:python
代码运行次数:0
复制
# 训练集和验证集
train_df, validate_df = train_test_split(df,
                                        test_size=0.2,
                                        random_state=42)

实施索引重置功能:

代码语言:python
代码运行次数:0
复制
# 索引重置
train_df.reset_index(drop=True, inplace=True)

train_df.head()

对验证集实施相同的操作:

代码语言:python
代码运行次数:0
复制
validate_df.reset_index(drop=True, inplace=True)

validate_df.head()

整体的数据量:

代码语言:python
代码运行次数:0
复制
total_train = len(train_df)
total_validate = len(validate_df)
total_train,total_validate

(1600, 400)

测试集

测试集中的图片是没有具体的分类结果的:

代码语言:python
代码运行次数:0
复制
test_filenames = os.listdir("./test1")

test_df = pd.DataFrame({"filename": test_filenames})

test_samples = len(test_df)

test_df

在测试集中是没有分类标签label的

定义图片参数

代码语言:python
代码运行次数:0
复制
image_Width=128  # 宽
image_Height=128 # 高

image_Size=(image_Width,image_Height)  # 每张图片大小
image_Channels=3   # 通道数

生成图像数据

基于ImageDataGenerator生成训练集和验证集中的图片数据:

代码语言:python
代码运行次数:0
复制
train_datagen = ImageDataGenerator(rotation_range=15,  # 旋转角度
                                rescale=1./255, # 像素值缩放比例
                                shear_range=0.1,  # 随机错切变换的角度
                                zoom_range=0.2,  # 随机缩放的角度
                                horizontal_flip=True, # 随机将一半图像进行水平翻转
                                width_shift_range=0.1,  # 水平和垂直方向的范围;相对于总宽度和高度的比例
                                height_shift_range=0.1
                                )

对训练集的生成器:

代码语言:python
代码运行次数:0
复制
# 对训练集的生成器

batch_size = 15

train_generator = train_datagen.flow_from_dataframe(train_df,
                                                   "./train",
                                                   x_col="filename",
                                                   y_col="category",
                                                   target_size=image_Size,
                                                   class_mode="categorical",
                                                   batch_size=batch_size
                                                   )

运行结果:Found 1600 validated image filenames belonging to 2 classes.

验证集实施相同的操作:

代码语言:python
代码运行次数:0
复制
validation_datagen = ImageDataGenerator(rescale=1./255)

batch_size = 15

validation_generator = validation_datagen.flow_from_dataframe(validate_df,
                                                   "./train",
                                                   x_col="filename",
                                                   y_col="category",
                                                   target_size=image_Size,
                                                   class_mode="categorical",
                                                   batch_size=batch_size
                                                   )

运行结果:Found 400 validated image filenames belonging to 2 classes.

最后对测试集也实施相同的操作:

代码语言:python
代码运行次数:0
复制
test_datagen = ImageDataGenerator(rotation_range=15,
                                rescale=1./255,
                                shear_range=0.1,
                                zoom_range=0.2,
                                horizontal_flip=True,
                                width_shift_range=0.1,
                                height_shift_range=0.1
                                )

从dataframe生成数据:

代码语言:python
代码运行次数:0
复制
batch_size = 15

test_generator = train_datagen.flow_from_dataframe(test_df,  
                                                   # directory=None,
                                                   "./test1",
                                                   x_col="filename",
                                                   y_col=None,
                                                   target_size=image_Size,
                                                   class_mode=None,
                                                   batch_size=batch_size
                                                  ) 

Found 7486 validated image filenames.

构建CNN网络

构建的CNN网络:

代码语言:python
代码运行次数:0
复制
model=Sequential()

# 卷积层1
model.add(Conv2D(32,(3,3),activation='relu',input_shape=(image_Width,image_Height,image_Channels)))
# 批标准化层
model.add(BatchNormalization()) 
# 池化层
model.add(MaxPooling2D(pool_size=(2,2)))
# Dropout层;防止过拟合
model.add(Dropout(0.25))

# 卷积层2
model.add(Conv2D(64,(3,3),activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

# 卷积层3
model.add(Conv2D(128,(3,3),activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(512,activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))

# 最后的输出层:分2个类
model.add(Dense(2,activation='softmax'))

模型编译

代码语言:python
代码运行次数:0
复制
model.compile(loss='categorical_crossentropy', # 多份类-使用交叉损失熵
              optimizer='rmsprop',  # 优化器
              metrics=['accuracy']  # 评价指标-分类精度
             )

模型可视化

提供两种Keras模型可视化的方法:

  • 基于visualkeras(先pip安装)
  • 基于plot_model模块

模型信息汇总

代码语言:python
代码运行次数:0
复制
model.summary()  # 模型基本信息

Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 126, 126, 32)      896       
                                                                 
 batch_normalization (BatchN  (None, 126, 126, 32)     128       
 ormalization)                                                   
                                                                 
 max_pooling2d (MaxPooling2D  (None, 63, 63, 32)       0         
 )                                                               
                                                                 
 dropout (Dropout)           (None, 63, 63, 32)        0         
                                                                 
 conv2d_1 (Conv2D)           (None, 61, 61, 64)        18496     
                                                                 
 batch_normalization_1 (Batc  (None, 61, 61, 64)       256       
 hNormalization)                                                 
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 30, 30, 64)       0         
 2D)                                                             
                                                                 
 dropout_1 (Dropout)         (None, 30, 30, 64)        0         
                                                                 
 conv2d_2 (Conv2D)           (None, 28, 28, 128)       73856     
                                                                 
 batch_normalization_2 (Batc  (None, 28, 28, 128)      512       
 hNormalization)                                                 
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 14, 14, 128)      0         
 2D)                                                             
                                                                 
 dropout_2 (Dropout)         (None, 14, 14, 128)       0         
                                                                 
 flatten (Flatten)           (None, 25088)             0         
                                                                 
 dense (Dense)               (None, 512)               12845568  
                                                                 
 batch_normalization_3 (Batc  (None, 512)              2048      
 hNormalization)                                                 
                                                                 
 dropout_3 (Dropout)         (None, 512)               0         
                                                                 
 dense_1 (Dense)             (None, 2)                 1026      
                                                                 
=================================================================
Total params: 12,942,786
Trainable params: 12,941,314
Non-trainable params: 1,472

可视化1

代码语言:python
代码运行次数:0
复制
import visualkeras  # 基于Keras搭建的模型可视化

visualkeras.layered_view(model)

可视化2

代码语言:python
代码运行次数:0
复制
tf.keras.utils.plot_model(model, show_shapes=True)

定义回调函数

代码语言:python
代码运行次数:0
复制
from keras.callbacks import EarlyStopping, ReduceLROnPlateau
early_stop = EarlyStopping(patience=10)

learning_rate_reduction = ReduceLROnPlateau(monitor="val_acc", 
                                            patience=2,
                                            verbose=1,
                                            factor=0.5,
                                            min_lr=0.00001
                                           )

callbacks = [early_stop, learning_rate_reduction]

模型训练

代码语言:python
代码运行次数:0
复制
epochs = 10

history = model.fit(train_generator,  # 生成数据
                    epochs=epochs,  # 迭代次数
                    validation_data=validation_generator,  # 验证集数据
                    validation_steps=total_validate // batch_size,   #  验证集步长
                    steps_per_epoch=total_train // batch_size,  # 每次迭代步长
                    callbacks=callbacks  # 回调函数
                   )

损失和精度可视化

训练过程中损失Loss和精度Accuray的可视化,包含训练集和验证集:

代码语言:python
代码运行次数:0
复制
# 损失绘图
import matplotlib.pyplot as plt

history_dict = history.history
history_dict

{'loss': [1.3272511959075928,
  0.9481741786003113,
  0.7862767577171326,
  0.7371966242790222,
  0.6606544852256775,
  0.620651125907898,
  0.596297562122345,
  0.6052179932594299,
  0.5645563006401062,
  0.5669357776641846],
 'accuracy': [0.5526813864707947,
  0.5684542655944824,
  0.620820164680481,
  0.6359621286392212,
  0.6656151413917542,
  0.6776025295257568,
  0.6883280873298645,
  0.6965299844741821,
  0.7160883545875549,
  0.7167192697525024],
 'val_loss': [1.1246546506881714,
  1.6301778554916382,
  0.8391894102096558,
  1.2209837436676025,
  0.6906123161315918,
  0.5846400260925293,
  0.5789883136749268,
  0.584494411945343,
  0.5889277458190918,
  0.6922145485877991],
 'val_accuracy': [0.5,
  0.5,
  0.5512820482254028,
  0.5076923370361328,
  0.6358974575996399,
  0.699999988079071,
  0.6846153736114502,
  0.7051281929016113,
  0.6974359154701233,
  0.6435897350311279],
 'lr': [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001]}

损失可视化

代码语言:python
代码运行次数:0
复制
loss = history_dict["loss"]
val_loss = history_dict["val_loss"]

epochs = range(1,len(loss) + 1)

plt.figure(figsize=(12,8))

plt.plot(epochs,  # 循环轮数
         loss,  # loss取值
         "r",
         label="loss"
        )

plt.plot(epochs,
         val_loss,  # val_loss
         "b",
         label="val_loss"
        )

plt.title("Loss and Val_Loss")
plt.xlabel("Epochs")
plt.legend()

plt.show()

精度可视化

代码语言:python
代码运行次数:0
复制
accuracy = history_dict["accuracy"]
val_accuracy = history_dict["val_accuracy"]

epochs = range(1,len(accuracy) + 1)

plt.figure(figsize=(12,8))

plt.plot(epochs,  # 循环轮数
         accuracy,  # loss取值
         "r",
         label="accuracy"
        )

plt.plot(epochs,
         val_accuracy,  # val_loss
         "b",
         label="val_accuracy"
        )

plt.title("Accuracy and Val_Accuracy")
plt.xlabel("Epochs")
plt.legend()

plt.show()

模型保存

一行代码将前面建立的CNN模型进行保存;后面搭建GUI时会使用。

代码语言:python
代码运行次数:0
复制
model.save("model_cats_dogs_10category.h5")

模型预测

对测试集中的图像进行预测

代码语言:python
代码运行次数:0
复制
predict = model.predict(test_generator,
                        steps=np.ceil(test_samples / batch_size))

将预测的结果转成具体的分类:

代码语言:python
代码运行次数:0
复制
test_df["category"] = np.argmax(predict, axis=-1)

test_df.head()

预测结果可视化

在测试集中选择部分数据进行可视化

代码语言:python
代码运行次数:0
复制
sample_test = test_df.head(9)

plt.figure(figsize=(12,24))

for index, row in sample_test.iterrows():
    filename = row["filename"]
    category = row["category"]
    img = load_img("./test1/" + filename,target_size=image_Size)
    plt.subplot(3,3,index+1)
    plt.imshow(img)
    plt.xlabel(filename + "(" + "{}".format(category) + ")")
    
plt.tight_layout()
plt.show()

下面是第二部分:将整个过程基于tkinter制成一个简单的GUI界面,通过点击实现图像分类。

导入库

主要是图像处理相关的库

代码语言:python
代码运行次数:0
复制
import tkinter as tk
from tkinter import filedialog
from tkinter import *
from PIL import ImageTk, Image
import numpy as np

import tensorflow as tf
from keras.models import load_model

tkinter库需要先进行安装。

导入模型

导入搭建好的CNN模型的h5文件:

代码语言:python
代码运行次数:0
复制
# 导入训练好的模型

model = load_model("model_cats_dogs_10category.h5")

图像窗口初始化

GUI界面的窗口参数初始化

代码语言:python
代码运行次数:0
复制
classes = {
    0: "It`s a cat",
    1: "It`s a dog"
}

# 初始化tkinter对象 并设置参数
代码语言:python
代码运行次数:0
复制
top = tk.Tk()
top.geometry("800x600")  # 设置图形窗口的宽和高
top.title("Cats and Dogs Classification Bases on Keras-CNN")  # 标题设置

top.configure(background="#CDCDCD")  # 背景色
# Label控件:指定的窗口top中显示的文本和图像
label = Label(top, background="#CDCDCD", font=("arial", 15, "bold"))

sign_image = Label(top)  

搭建预测分类函数

代码语言:python
代码运行次数:0
复制
def classify(file_path):
    global label_packed
    
    image = Image.open(file_path)  # 导入图片调整大小
    image = image.resize((128,128))
    # expand_dims改变图片shape axis=0在首位增加1  比如(3,4) ---> (1,3,4)
    image = np.expand_dims(image, axis=0)  
    image = np.array(image)
    image = image/255
    
    pred_ = model.predict([image])  # 每个类别的概率
    pred = np.argmax(pred_)  # 具体所属类别:确定所在索引
    
    sign = classes[pred]   # 根据自定义的字典获取分类结果
    
    label.configure(foreground="#011638",text=sign)

实现点击功能

代码语言:python
代码运行次数:0
复制
def show_classify_button(file_path):
    """
    功能:实现点击按钮
    """
    # 实例化按钮对象
    classify_b = Button(top,  # 位置
                     text="Classify Image",  # 按钮名称
                     command=lambda: classify(file_path),  # 执行回调函数
                     padx=10,  # 指定水平和垂直方向上按钮内容和边框的间距
                     pady=5
                    )
    # 对象属性
    classify_b.configure(background='#364156', # 背景色
                         foreground='white', # 前景色
                         font=('arial',10,'bold'))  # 调整字体
    # place: relx, rely代表窗口大小所对应的x, y坐标比例
    classify_b.place(relx=0.8,rely=0.5)

图片加载功能

如何使用tkinter加载本地图像?

  • tkinter.filedialog.asksaveasfilename():选择以什么文件名保存,返回文件名
  • tkinter.filedialog.asksaveasfile():选择以什么文件保存,创建文件并返回文件流对象
  • tkinter.filedialog.askopenfilename():选择打开的文件,返回文件名
  • tkinter.filedialog.askopenfile():选择打开的文件,返回IO流对象
  • tkinter.filedialog.askdirectory():选择目录,返回目录名
  • tkinter.filedialog.askopenfilenames():选择打开多个文件,以元组形式返回多个文件名
  • tkinter.filedialog.askopenfiles():选择打开多个文件,以列表形式返回多个IO流对象
代码语言:python
代码运行次数:0
复制
def upload_image():
    """
    图片加载功能
    """
    try:
        file_path = filedialog.askopenfilename()  # 打开文件返回文件名;本地目录也是整体的路径
        uploaded = Image.open(file_path)  # 打开图片,PIL类型;pytorch的顺序:(batch,c,h,w)  tf和numpy是(batch,h,w,c)
        uploaded.thumbnail(((top.winfo_width()/2.25),  # 对图片实施裁剪
                            (top.winfo_height()/2.25)))
        
        im=ImageTk.PhotoImage(uploaded)  # tk.PhotoImage(file=path_to_image)
        sign_image.configure(image=im)  # sign_image = Label(top)  Label实例对象中配置im图片
        sign_image.image=im
        label.configure(text='')
        show_classify_button(file_path)  # 调用按钮点击功能
    except:
        pass

功能调用

代码语言:python
代码运行次数:0
复制
# 调用按钮功能
buttom=Button(top,  
              text="Upload an image",
              command=upload_image,  # 回调函数
              padx=10,
              pady=5)

# 按钮属性设置
buttom.configure(background='#364156', 
                 foreground='white',
                 font=('arial',10,'bold')
                )

# pack:按钮、照片图像、标签布局设置
buttom.pack(side=BOTTOM,
            pady=50)

sign_image.pack(side=BOTTOM,expand=True)

label.pack(side=BOTTOM,expand=True)

# 标题(上面)
title = Label(top,   # 位置
                text="Cats & Dogs Classification", # 标题内容
                pady=20, # 和y轴边距
                font=('arial',30,'bold'))  # 字体设置

title.configure(background='#CDCDCD',foreground='#364156')  # 背景色和前景色
title.pack()  # 布局
top.mainloop()  # 运行图片对象 top = tk.Tk()

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导入库
  • 数据准备
  • 数据预处理
    • 训练集和验证集
      • 测试集
      • 定义图片参数
      • 生成图像数据
      • 构建CNN网络
      • 模型编译
      • 模型可视化
        • 模型信息汇总
          • 可视化1
            • 可视化2
            • 定义回调函数
            • 模型训练
            • 损失和精度可视化
              • 损失可视化
                • 精度可视化
                • 模型保存
                • 模型预测
                • 预测结果可视化
                • 导入库
                • 导入模型
                • 图像窗口初始化
                • 搭建预测分类函数
                • 实现点击功能
                • 图片加载功能
                • 功能调用
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档