深度学习(7)--卷积神经网络项目详解

一.项目介绍:

用Keras工具包搭建训练自己的一个卷积神经网络(Simple_VGGNet,简单版VGGNet),用来识别猫/狗/羊三种图片。

数据集:

二.卷积神经网络构造

查看API文档

Convolution layers (keras.io)icon-default.png?t=N7T8https://keras.io/api/layers/convolution_layers/

# 导入所需模块
from keras.models import Sequential
from keras.layers import BatchNormalization
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.initializers import TruncatedNormal
from keras.layers import Activation
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers import Dense
from keras import backend as K


class SimpleVGGNet:
    @staticmethod
    def build(width, height, depth, classes):   # 长 宽 深度(特征图的个数)
        model = Sequential()
        inputShape = (height, width, depth)
        chanDim = -1

        if K.image_data_format() == "channels_first":
            inputShape = (depth, height, width)
            chanDim = 1

        # CONV => RELU => POOL
        model.add(Conv2D(32, (3, 3), padding="same",
            input_shape=inputShape, kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
        model.add(Activation("relu"))
        #  model.add(BatchNormalization(axis=chanDim))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        #  model.add(Dropout(0.25))

        # (CONV => RELU) * 2 => POOL
        model.add(Conv2D(64, (3, 3), padding="same", kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
        model.add(Activation("relu"))
        #  model.add(BatchNormalization(axis=chanDim))
        model.add(Conv2D(64, (3, 3), padding="same", kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
        model.add(Activation("relu"))
        #  model.add(BatchNormalization(axis=chanDim))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        #  model.add(Dropout(0.25))

        # (CONV => RELU) * 3 => POOL
        model.add(Conv2D(128, (3, 3), padding="same", kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
        model.add(Activation("relu"))
        #  model.add(BatchNormalization(axis=chanDim))
        model.add(Conv2D(128, (3, 3), padding="same", kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
        model.add(Activation("relu"))
        #  model.add(BatchNormalization(axis=chanDim))
        model.add(Conv2D(128, (3, 3), padding="same", kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
        model.add(Activation("relu"))
        #  model.add(BatchNormalization(axis=chanDim))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        #  model.add(Dropout(0.25))

        # FC层
        model.add(Flatten())
        model.add(Dense(256, kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
        model.add(Activation("relu"))
        #  model.add(BatchNormalization())
        #  model.add(Dropout(0.6))

        # softmax 分类
        model.add(Dense(classes, kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
        model.add(Activation("softmax"))

        return model


2.1.判断是否是channels first的back end

不同backend的颜色通道设置的位置可能不同,tensorflow的颜色通道在最后一个参数,有些backend的颜色通道则在第一个参数,所以需要进行一次判断。

if K.image_data_format() == "channels_first":
    inputShape = (depth, height, width)
    chanDim = 1

如果判断为真,则重新设置参数的顺序。

2.2.卷积层构造

model.add(Conv2D(32, (3, 3), padding="same",
            input_shape=inputShape,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))

model.add(Conv2D(64, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Conv2D(64, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))

model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))

拿第一层卷积层的构造为例:32是输出层的维度(即特征图个数,每个特征图大小为最开始设置height x weight)。(3,3)是卷积核的大小,即一次性读取3x3大小的特征值。padding是边界填充,padding=same表示有padding,且padding大小与步长相同,padding=valid则表示没有padding。最后再设置权重初始化方式为截断初始化。

对于卷积神经网络,需要经过池化层对数据进行压缩,而在每次经过池化层压缩后,我们希望数据的特征图个数可以翻倍。(与传统神经网络的减少不同)

如上图所示:数据的特征图个数由32→64→128.(每个特征图都是height x weight x 1的大小)

2.3.添加激活函数

model.add(Activation("relu"))

除去池化层因为只是对参数进行压缩而不进行计算,不需要添加激活函数,其他对参数进行计算了的层,例如卷积层和全连接层都需要添加一个激活函数。

2.4.池化层构造

model.add(MaxPooling2D(pool_size=(2, 2)))

此处调用的池化层是MaxPooling,表示对每个2x2大小的区域进行池化,只取出其中最大的那个权重值。

2.5.全连接FC层构造 

# FC层
model.add(Flatten())
model.add(Dense(512,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
#  model.add(BatchNormalization())
#  model.add(Dropout(0.6))

# softmax 分类
model.add(Dense(classes,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("softmax"))

经过卷积后,要通过矩阵相乘得到相应类别的概率值,所以需要将三维的图片数据拉长成一维的特征值矩阵。同时增加一层全连接层,特征值矩阵经过该全连接层剩下512个特征值。

最后再添加一层全连接层,得到的类别数量与最开始设置的classes相同,并通过softmax激活函数来分类。

三.完整代码

# 导入所需工具包
from CNN_net import Simple_VGGNet
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from keras.optimizers import SGD
from keras.preprocessing.image import ImageDataGenerator
from my_utlis import utlis_paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import random
import pickle
import cv2
import os
import keras

os.environ["CUDA_VISIBLE_DEVICES"] = "0"


# 读取数据和标签
print("------开始读取数据------")
data = []
labels = []

# 拿到图像数据路径,方便后续读取
imagePaths = sorted(list(utlis_paths.list_images('./dataset')))
random.seed(42)
random.shuffle(imagePaths)

# 遍历读取数据
for imagePath in imagePaths:
    # 读取图像数据
    image = cv2.imread(imagePath)
    image = cv2.resize(image, (64, 64))  # 将图片resize为相同尺寸
    data.append(image)
    # 读取标签
    label = imagePath.split(os.path.sep)[-2]  # 根据文件夹获取标签
    labels.append(label)

# 对图像数据做scale操作
data = np.array(data, dtype="float") / 255.0
labels = np.array(labels)

# 数据集切分
(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, random_state=42)

# 转换标签为one-hot encoding格式(三分类及以上需要,二分类不需要)
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)


# 数据增强处理
"""
aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
    height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
    horizontal_flip=True, fill_mode="nearest")
"""

# 建立卷积神经网络
model = Simple_VGGNet.SimpleVGGNet.build(width=64, height=64, depth=3, classes=len(lb.classes_))

# 设置初始化超参数
INIT_LR = 0.01
EPOCHS = 30
BS = 32

# 损失函数,编译模型
print("------准备训练网络------")
lr_schedule = keras.optimizers.schedules.ExponentialDecay(
    initial_learning_rate=INIT_LR,
    decay_steps=10,
    decay_rate=0.98)
opt = SGD(lr=lr_schedule)  # 一开始的权重参数较好,可以把学习参数设置的较大,后续权重参数变差,学习参数也设置较低
# one-hot编码用loss="CategoricalCrossentropy" 数组编码用loss="SparseCategoricalCrossentropy"
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])

# 训练网络模型
"""
H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
    validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
    epochs=EPOCHS)
"""

H = model.fit(trainX, trainY, validation_data=(testX, testY),
    epochs=EPOCHS, batch_size=32)




# 测试
print("------测试网络------")
predictions = model.predict(testX, batch_size=32)
print(classification_report(testY.argmax(axis=1),
    predictions.argmax(axis=1), target_names=lb.classes_))

# 绘制结果曲线
N = np.arange(0, EPOCHS)
plt.style.use("ggplot")
plt.figure()
plt.plot(N, H.history["loss"], label="train_loss")
plt.plot(N, H.history["val_loss"], label="val_loss")
plt.plot(N, H.history["accuracy"], label="train_acc")
plt.plot(N, H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend()
plt.savefig('./output_cnn/cnn_plot.png')

# 保存模型
print("------正在保存模型------")
model.save('./output_cnn/cnn.model')
f = open('./output_cnn/cnn_lb.pickle', "wb")
f.write(pickle.dumps(lb))
f.close()

3.1.学习率衰减设置

lr_schedule = keras.optimizers.schedules.ExponentialDecay(
    initial_learning_rate=INIT_LR,
    decay_steps=5,
    decay_rate=0.9)
opt = SGD(lr=lr_schedule)  # 一开始的权重参数较好,可以把学习参数设置的较大,后续权重参数变差,学习参数也设置较低

decay_steps表示的是每几次迭代进行一次衰减,dacay_rate表示的是衰减的程度,上述代码中即为每五次迭代进行一次学习率的衰减,即 lr*0.9。

一开始的权重参数较好,可以把学习参数设置的较大,后续权重参数变差,学习参数也相应设置的较低。

四.首次运行结果

第一次运行结果如下:

发现数据异常,有两种类没有结果值,编译器warning:UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.

出现上述warning的原因是有些样本是正确的,但是没有预测到。

博主认为出现这种warning的解决方法是修改数据集或者调整你的网络结构。

卷积神经网络的数据参数较少,所以当时有截断初始化、Dropout等操作时可能会导致结果出现异常。

此处博主删去了网络中卷积层和全连接层中的截断初始化,得到的结果:

五.数据增强对结果的影响

Data Augmentation ,基于有限的数据生成更多等价(同样有效)的数据,丰富训练数据的分布,使通过训练集得到的模型泛化能力更强。

# 数据增强处理
aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
    height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
    horizontal_flip=True, fill_mode="nearest")

# 训练网络模型
H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
    validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
    epochs=EPOCHS)

 加上数据增强的训练结果:

六.BatchNormalization对结果的影响

每次卷积层、全连接层后可以加上一个BatchNormalization层进行修正,使标准化。

BatchNormalization layer (keras.io)icon-default.png?t=N7T8https://keras.io/api/layers/normalization_layers/batch_normalization/

model.add(Conv2D(32, (3, 3), padding="same",
     input_shape=inputShape, ))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(2, 2)))

加上数据增强,BatchNormalization层的训练结果: 

七.加载模型进行测试

编写一个predict.py程序来加载模型进行测试:

# 导入所需工具包
from keras.models import load_model
import argparse
import pickle
import cv2


# 加载测试数据并进行相同预处理操作
image = cv2.imread('./cs_image/dog.jpeg')
output = image.copy()
image = cv2.resize(image, (64, 64))

# scale图像数据
image = image.astype("float") / 255.0

# 对图像进行拉平操作
image = image.reshape((1, image.shape[0], image.shape[1],image.shape[2]))

# 读取模型和标签
print("------读取模型和标签------")
model = load_model('./output_cnn/cnn.model')
lb = pickle.loads(open('./output_cnn/cnn_lb.pickle', "rb").read())

# 预测
preds = model.predict(image)

# 得到预测结果以及其对应的标签
i = preds.argmax(axis=1)[0]
label = lb.classes_[i]

# 在图像中把结果画出来
text = "{}: {:.2f}%".format(label, preds[0][i] * 100)
cv2.putText(output, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7,(0, 0, 255), 2)

# 绘图
cv2.imshow("Image", output)
cv2.waitKey(0)

增加数据增强,BatchNormalization层并训练100EPOCH得到的训练结果:

使用上述得到的网络模型进行测试:

首次运行predict程序出现如下问题:

cv2.error: OpenCV(4.6.0) C:\b\abs_f8n1j3l9l0\croot\opencv-suite_1691622637237\work\modules\highgui\src\window.cpp:1267: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvShowImage'

解决方法:在对应环境中依次输入以下代码

安装opencv-python

pip install opencv-python

安装opencv-contrib-python 

pip install opencv-contrib-python 

安装过慢可以使用国内的镜像源:

清华:https://pypi.tuna.tsinghua.edu.cn/simple

阿里云:http://mirrors.aliyun.com/pypi/simple/

中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/

华中理工大学:http://pypi.hustunique.com/

山东理工大学:http://pypi.sdutlinux.org/ 

豆瓣:http://pypi.douban.com/simple/

pip install opencv-python  -i https://pypi.tuna.tsinghua.edu.cn/simple

测试结果: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/359265.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

氢气传感器报警值:守护实验室安全的隐形卫士

随着科技的发展,我们的生活变得越来越便捷,但是与此同时,安全问题也日益凸显。其中,氢气作为一种清洁能源,被广泛应用于各个领域,但是如果不加以控制,氢气泄漏也可能带来严重的安全隐患。因此&a…

burp靶场--xss上篇【1-15】

burp靶场–xss https://portswigger.net/web-security/cross-site-scripting 1. 什么是xss: 跨站脚本 (XSS) 是一种通常出现在 Web 应用程序中的计算机安全漏洞。XSS 允许攻击者将恶意代码注入网站,然后在访问该网站的任何人的浏览器中执行该代码。这可能允许攻击…

计算机设计大赛 深度学习 opencv python 实现中国交通标志识别

文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 🔥 优质…

如何将rmvb视频转换成mp4格式?如何播放rmvb视频?

RMVB文件格式的特性及使用场景 RMVB以其独特的可变比特率压缩方式而著称。这一特性使其能够根据视频内容自动调整比特率,不仅保证了视频质量,同时高效减小了文件大小。这种优势使得RMVB常见于在线视频、电视剧以及一些高清电影资源中。 RMVB文件格式的…

exec函数族和守护进程

exec函数族 进程调用exec函数族执行某个程序 进程当前内容被指定程序替换 实现让父子进程实现不同的程序: 父进程创建子进程 子进程调用exec函数族 父进程不受影响 execl和execlp #include <stdio.h> int execl (const char * path, const char * arg , ...); i…

微信小程序(二十六)列表渲染基础核心

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.列表渲染基础写法 2.外部索引和自身索引 源码&#xff1a; index.wxml <view class"students"><view class"item"><text>序号</text><text>姓名</text…

大数据 - Hadoop系列《四》- MapReduce(分布式计算引擎)的核心思想

上一篇&#xff1a; 大数据 - Hadoop系列《三》- MapReduce&#xff08;分布式计算引擎&#xff09;概述-CSDN博客 目录 13.1 MapReduce实例进程 13.2 阶段组成 13.4 概述 13.4.1 &#x1f959;Map阶段&#xff08;映射&#xff09; 13.4.2 &#x1f959;Reduce阶段执行过…

机器学习复习(3)——分类神经网络与drop out

完整的神经网络 以分类任务为例&#xff0c;神经网络一般包括backbone和head&#xff08;计算机视觉领域&#xff09; 下面的BasicBlock不是一个标准的backbone,标准的应该是复杂的CNNs构成的 Classfier是一个标准的head,其中output_dim表示分类类别&#xff0c;一般写作num…

【科技素养题】少儿编程 蓝桥杯青少组科技素养题真题及解析第23套

少儿编程 蓝桥杯青少组科技素养题真题及解析第23套 1、英国计算机科学家艾伦图灵于 1950 年提出了著名的“图灵测试”,用于判断计算机是否具有智能。“图灵测试”是通过()的方法进行判断的 A、让两台计算机对话 B、让人类与计算机对话 C、给计算机出题 D、让计算机分辨图…

HarmonyOS使用Web组件加载页面

1、加载网络页面 在Web组件创建时&#xff0c;指定默认加载的网络页面 。在默认页面加载完成后&#xff0c;如果开发者需要变更此Web组件显示的网络页面&#xff0c;可以通过调用loadUrl()接口加载指定的网页。 默认在Web组件加载完“www.baidu.com”页面后&#xff0c;点击按…

云原生 k8s 可能使用到的端口整理【不定期更新】

k8s 因为涉及到的组件太多了&#xff0c;所以端口有很多&#xff0c;这里整理了日常所接触的接口&#xff0c;后续有新的再更新。 如果是通过公网 IP 进行安装的时候需要根据实际情况有选择的进行放开&#xff1b;一般只有云厂商会提供公网 IP 访问&#xff0c;自建的话不建议 …

APUE学习之进程间通信(IPC)(下篇)

目录 一、进程间通信&#xff08;IPC&#xff09; 二、信号量&#xff08;Semaphore&#xff09; 1、基本概念 2、同步关系与互斥关系 3、临界区与临界资源 4、信号量的工作原理 5、信号量编程 6、实战演练 三、共享内存&#xff08;Shared Memory&#xff09; 1、…

多线程c++

目录 1.join和detach区别 2.lock_guard和unique_lock 3.原子操作 4.条件变量condition_variable 5.future 和 promise 1.join和detach区别 ①不使用join和detach #include <iostream> #include <thread> #include <windows.h>using namespace std;v…

Linux文本三剑客-grep

1.grep简介&#xff1a; grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具&#xff0c;它能使用正则表达式搜索文本&#xff0c;并把匹配的行打印出来&#xff0c;都是按行处理的。 grep 最主要…

腾讯云云监控实践:使用云审计 CloudAudit SDK 精准管理腾讯云资源

文章目录 一、什么是腾讯云的操作审计 CloudAudit二、CloudAudit 有哪些优势三、CloudAudit 应用场景举例3.1 安全分析3.2 资源变更跟踪3.3 合规性审计 四、使用云审计 SDK 进行云监控4.1 安装环境包 PHP4.2 下载并解压云审计 PHP SDK4.3 创建的腾讯云持久证书&#xff08;如果…

解决:ModuleNotFoundError: No module named ‘selenium’

解决&#xff1a;ModuleNotFoundError: No module named ‘selenium’ 文章目录 解决&#xff1a;ModuleNotFoundError: No module named selenium背景报错问题报错翻译报错位置代码报错原因解决方法方法一&#xff0c;直接安装方法二&#xff0c;手动下载安装方法三&#xff0…

【Tomcat与网络2】一文理解Servlet是怎么工作的

在前面&#xff0c;我们研究了如何用idea来启动一个Servlet程序&#xff0c;今天我们就再来看一下Servlet是如何工作的。 目录 1.Servlet 介绍 2.Servlet 容器工作过程 3.Servlet的扩展 不管是电脑还是手机浏览器&#xff0c;发给服务端的就是一个 HTTP 格式的请求&#xf…

摄像头提示sd卡未格式化怎么回事?怎么解决

作为摄像头用户&#xff0c;往往会遇到或多或少的技术问题。而当摄像头显示"SD卡未格式化"的提示时&#xff0c;这可能令一些用户感到困惑和担忧。在本文中&#xff0c;我们将解释这个提示的原因&#xff0c;并提供一些建议来解决这一问题。我们相信本文会让您更加了…

select的change方法如何传递多个参数

element-ui中select的change方法传递多个参数 element-ui中的select&#xff0c;checkbox等组件的change方法的回调函数只有当前选择的val&#xff0c;如果想再传入自定义参数怎么办&#xff1f; 不能够传入自定义的参数&#xff0c;在进行某些操作时&#xff0c;会比较困难&…

[设计模式Java实现附plantuml源码~结构型]对象的间接访问——代理模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…