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

目录

一.项目介绍

二.卷积神经网络构造

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

2.2.卷积层构造

2.3.添加激活函数

2.4.池化层构造

2.5.全连接FC层构造 

三.完整代码

3.1.学习率衰减设置

四.首次运行结果

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

六.BatchNormalization对结果的影响

七.加载模型进行测试


一.项目介绍

用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/364196.html

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

相关文章

LeetCode: 160.相交链表(令人赞叹的优雅)

160. 相交链表 - 力扣(LeetCode) 目录 官方双指针解法: 博主的辣眼代码: 每日一表情包: 博主还未学习哈希表,所以介绍的是双指针法,此题的哈希表解法时O(nm)空O&…

R语言入门笔记2.0

1.创建数据框 在R语言中,可以使用data.frame函数来创建数据框。以下是一个简单的示例,这段R语言代码创建了一个名为student的数据框,其中包含了学生的ID、性别、姓名和出生日期,首先创建一个包含学生出生日期的向量,再…

网络时间协议NTP

网络时间协议NTP(Network Time Protocol)是TCP/IP协议族里面的一个应用层协议。NTP用于在一系列分布式时间服务器与客户端之间同步时钟。NTP的实现基于IP和UDP。NTP报文通过UDP传输,端口号是123。 随着网络拓扑的日益复杂,整个网络内设备的时钟同步将变得十分重要。如果依靠…

Skywalking的Trace Profiling 代码级性能剖析功能应用详解

代码级性能剖析 Skywalking 提供了Trace Profiling功能对具体出现问题的span进行代码级性能剖析。 代码级性能剖析就是利用方法栈快照,并对方法执行情况进行分析和汇总。并结合有限的分布式追踪 span 上下文,对代码执行速度进行估算。性能剖析激活时&a…

配置支持 OpenAPI 的 ASP.NET Core 应用

写在前面 Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。 本文记录如何配置基于Swagger 的 ASP.NET Core 应用程序的 OpenAPI 规范。 需要从NuGet 安装 Swashbuckle.AspNetCore 包 代码实现 var builder WebApplicati…

ChatLaw:基于LLaMA微调的法律大模型

文章目录 动机数据组成模型框架模型评估 北大团队发布首个的中文法律大模型落地产品ChatLaw,为大众提供普惠法律服务。模型支持文件、语音输出,同时支持法律文书写作、法律建议、法律援助推荐。 github地址:https://github.com/PKU-YuanGroup…

纯html+css+js静态汽车商城

首页代码 <!DOCTYPE html> <html class"no-js" lang"zxx"><head><meta charset"utf-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content&qu…

【前端】快速掌握CSS-flex布局

文章目录 一、标准流二、浮动1. 基本使用2. 产品区域布局(1) HTML标签(2) CSS样式 3. 清除浮动(1) 场景搭建(2) 额外标签法(3) 单伪元素法(4) 双伪元素法(5) overflow法 三、Flex布局1. Flex组成2. 主轴对齐方式3. 侧轴对齐方式4. 修改主轴方向5. 弹性伸缩比6. 弹性盒子换行7. …

手写栈【解析数学表达式,重复字符串解码】

目录 解析数学表达式 字符串解码/智能重复 解析数学表达式 const stock []; // 先进后出&#xff0c;每一次出栈&#xff0c;即一对 () const parenthesesPairPosition {}// 剔除两侧空格 const removeBlank (expression, l, r) > {while (expression[l] ) {l}while…

【算法详解 | 二分查找】详解二分查找 \ 折半查找高效搜索算法 | 顺序数组最快搜索算法 | 递归循环解决二分查找问题

二分查找 by.Qin3Yu 本文需要读者掌握 顺序表 的操作基础&#xff0c;完整代码将在文章末尾展示。 顺序表相关操作可以参考我的往期博文&#xff1a; 【C数据结构 | 顺序表速通】使用顺序表完成简单的成绩管理系统.by.Qin3Yu 文中所有代码使用 C 举例&#xff0c;且默认已使用…

Windows存储空间不足局域网文件共享 Dism备份系统空间不足

问题情景 在日常使用中难免遇到Windows的空间不足的情况&#xff0c;常用办法是清理垃圾释放空间&#xff0c;部分场景例如我们需要使用Dism备份完整系统&#xff0c;所以需要非常大的存储空间不够&#xff0c;如果空间不够什么才是最有效的方案呢&#xff1f; 我们假设身边没有…

【HarmonyOS 4.0 应用开发实战】TypeScript入门之模块化详讲

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

如何在Raspberry Pi上启用SSH并结合cpolar内网穿透实现公网远程访问本地树莓派

文章目录 如何通过 SSH 连接到树莓派步骤1. 在 Raspberry Pi 上启用 SSH步骤2. 查找树莓派的 IP 地址步骤3. SSH 到你的树莓派步骤 4. 在任何地点访问家中的树莓派4.1 安装 Cpolar4.2 cpolar进行token认证4.3 配置cpolar服务开机自启动4.4 查看映射到公网的隧道地址4.5 ssh公网…

Oracle函数使用

ROW_NUMBER函数 ROW_NUMBER() OVER(PARTITION BY column1 ORDER BY column2 DESC) -- 根据column1分组按column2降序排序生成序号&#xff0c;序号由小到大,会生成一个唯一的序号 -- 例如column2中有两列值都为1,那他们的序号会有一个在上一个在下ROW_NUMBER() OVER(ORDER BY …

Redis-持久机制

文章目录 为什么有持久化什么是持久化RDB文件创建SAVEBGSAVE 文件载入 优缺点 AOF日志步骤 对比数据恢复 总结 Redis是一个开源的内存数据结构存储系统&#xff0c;被广泛应用于Web应用中&#xff0c;可以用作数据库和缓存服务器。它具有高性能、高并发、高可用性等特点&#x…

计网——应用层

应用层 应用层协议原理 网络应用的体系结构 客户-服务器&#xff08;C/S&#xff09;体系结构 对等体&#xff08;P2P&#xff09;体系结构 C/S和P2P体系结构的混合体 客户-服务器&#xff08;C/S&#xff09;体系结构 服务器 服务器是一台一直运行的主机&#xff0c;需…

零基础小白到底要不要学习鸿蒙,看完这篇再决定吧~

随着华为鸿蒙系统的问世&#xff0c;不少技术小白在是否学习鸿蒙的问题上犹豫不决。鸿蒙作为华为自主研发的操作系统&#xff0c;拥有许多独特的技术优势和市场前景。但对于小白来说&#xff0c;是否值得投入时间和精力去学习鸿蒙开发呢&#xff1f; 1.鸿蒙系统开发&#xff1…

短视频去水印教程,免费一键获取视频、图片、文案【迅风去水印】

自媒体行业的蓬勃发展&#xff0c;让越来越多的创作者涌入其中。然而&#xff0c;剪辑过程中常常遭遇到一个令人头疼的问题&#xff0c;那就是视频或图片上的水印。这些水印不仅会影响到作品的美感&#xff0c;还可能侵犯到版权。为了帮大家解决这一难题&#xff0c;分享一个免…

新手不会Git也能玩Github吗?

新手不会Git也能玩Github吗&#xff1f; 前言使用Github的准备步骤使用一种访问外网资源的方法&#xff08;这一步才是新手最容易&#xff09;注册账号 创建一个自己的仓库创建完仓库后的界面 搜索你想要的代码类型以搜索坦克大战为例以下载烟花代码为例 总结 前言 说到Github&…

认识 SYN Flood 攻击

文章目录 1.什么是 SYN Flood 攻击&#xff1f;2.半连接与全连接队列3.如何防范 SYN Flood 攻击&#xff1f;参考文献 1.什么是 SYN Flood 攻击&#xff1f; SYN Flood 是互联网上最原始、最经典的 DDoS&#xff08;Distributed Denial of Service&#xff09;攻击之一。 SYN…