当前位置: 首页 > news >正文

基于Keras实战项目-猫狗熊猫分类大战

😊😊😊欢迎来到本博客😊😊😊
本次博客内容将继续讲解关于OpenCV的相关知识
🎉作者简介⭐️⭐️⭐️目前计算机研究生在读。主要研究方向是人工智能和群智能算法方向。目前熟悉深度学习(keras、pytorch、yolo),python网页爬虫、机器学习、计算机视觉(OpenCV)、群智能算法。然后正在学习深度学习的相关内容。以后可能会涉及到网络安全相关领域,毕竟这是每一个学习计算机的梦想嘛!
📝目前更新🌟🌟🌟目前已经更新了关于网络爬虫的相关知识、机器学习的相关知识、计算机视觉-OpenCV、目前正在更新keras的相关内容。
💛💛💛本文摘要💛💛💛

本文我们将开始讲解神经网络keras领域项目-猫狗熊猫分类大战。

文章目录

    • 🌟项目前言
    • 🌟项目详解
      • ⭐️经典全连接网络
      • ⭐️预测模块
      • ⭐️卷积神经网络分类

在这里插入图片描述

🌟项目前言

我们从上次博客当中就结束了关于OpenCV的更新,后续可能会再次更新关于计算机视觉领域的相关内容。但是现在开始更新关于神经网络keras模块下的实战项目,首节我们以一个简单的项目为开头,然后逐个分析神经网络的小模块对整体网络架构的准确性和损失函数的影响。其中包括(Drop-out,dense等等)。

🌟项目详解

⭐️经典全连接网络

我们从训练模块开始说起,会逐行去一一的解释代码的意思是什么?并且通过绘图的方式将结果更清晰的展示出来,然后通过加入Drop-out、正则化等,然后通过对比其中的图来看看效果有什么变化。首先我们来看一下整个nn_train模块。
在train模块当中:

import matplotlib#画图
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report#综合结果对比
from keras.models import Sequential
from keras.layers import Dropout
from keras.layers.core import Dense
from keras.optimizers import SGD
from keras import initializers#初始化权重参数
from keras import regularizers#正则化
from my_utils import utils_paths#图像路径的操作
import matplotlib.pyplot as plt
import numpy as np
import argparse
import random
import pickle
import cv2
import os

首先我们导入这些即将会用得到的第三方模块,我们简单的介绍一下这些模块!

  • matplotlib:画图模块,最后我们要通过它来绘图,直观的展示train_Loss,以及train_acc的走势。
  • LabelBinarizer、train_test_split、classification_report:这三个模块分别是sklearn模块下的,其中LabelBinarizer是对标签数据进行one-hot-encoding的操作,也就是说用数组的形式展示数据,方便计算机处理。train_test_split是将数据分割成训练集和测试集的,classification_report是生成最终结果报告的。
  • Sequential、Dropout、Dense、SGD、initializers、utils_paths:这些模块就是keras下面的一些相关于神经网络的一些操作模块了。Sequential就是说顺序搭建神经网络架构的模块。Dropout就是说要随机的杀死一些神经元,使得这些神经元不参与参数更新。Dense表示全连接。SGD表示优化器。
  • os:os模块就是路径操作模块。
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=True,
	help="path to input dataset of images")
ap.add_argument("-m", "--model", required=True,
	help="path to output trained model")
ap.add_argument("-l", "--label-bin", required=True,
	help="path to output label binarizer")
ap.add_argument("-p", "--plot", required=True,
	help="path to output accuracy/loss plot")
args = vars(ap.parse_args())

这里是参数导入代码,可以看出我们需要导入数据,模型数据,标签数据,画输出结果图。这里我们再次介绍导入参数,如果使用pycharm,就在这里设置:在这里插入图片描述
设置参数就是在Modify这里设置。
在训练模块中我们填入

–dataset ./dataset
–model ./output/simple_nn_model
–label-bin ./output/simple_nn_lb.pickle
–plot ./output/simple_nn_plot.png

然后我们就将需要导入的东西导入进去了。

print("[INFO] 开始读取数据")
data = []
labels = []
imagePaths = sorted(list(utils_paths.list_images(args["dataset"])))
random.seed(42)
random.shuffle(imagePaths)

这里我们将图像数据的路径都拿到,对于猫狗熊猫每个各有1000张图像,一共3000个路径目前我们全部都拿到手了,然后种一个随机种子,就是说我这次随机分的和下次随机分的是一模一样的,不是错乱的。然后通过random.shuffle将数据打乱,相当于一个洗牌操作。其中utils_paths这个是专门来做提取图片路径的,代码是,这段代码不仅仅适合于这里,其他地方也可以进行应用,应为三方库只有os:

import os
image_types = (".jpg", ".jpeg", ".png", ".bmp", ".tif", ".tiff")
def list_images(basePath, contains=None):
    # return the set of files that are valid
    return list_files(basePath, validExts=image_types, contains=contains)
def list_files(basePath, validExts=None, contains=None):
    # loop over the directory structure
    for (rootDir, dirNames, filenames) in os.walk(basePath):
        # loop over the filenames in the current directory
        for filename in filenames:
            # if the contains string is not none and the filename does not contain
            # the supplied string, then ignore the file
            if contains is not None and filename.find(contains) == -1:
                continue
            # determine the file extension of the current file
            ext = filename[filename.rfind("."):].lower()
            # check to see if the file is an image and should be processed
            if validExts is None or ext.endswith(validExts):
                # construct the path to the image and yield it
                imagePath = os.path.join(rootDir, filename)
                yield imagePath
for imagePath in imagePaths:
	image = cv2.imread(imagePath)
	image = cv2.resize(image, (32, 32)).flatten()
	data.append(image)
	label = imagePath.split(os.path.sep)[-2]
	labels.append(label)

然后遍历图像路径,然后使用cv2读入图像路径,然后使用cv2对所有读入的图像进行一个resize操作,resize成32×32大小的图,然后是一个三维的,但是传统的神经网络无法对三维数据进行操作,当前是32×32×3。所以使用flatten进行一个图像拉长操作。使用data添加数据,然后通过路径中倒数第二个得到对应图片属于的标签,并且进行添加。

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)

然后对图像数据进行一个归一化的处理。因为像素值都是在0-255之间的数值,我们通过除以255,让所有数据在0-1之间。然后对数据集进行一个切分,也就是说把数据3000张的25%设置成测试集,也就是750张。2250张作为训练集,并且每次随机都是一致的。

lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)

这里对数据做一个one-hot-encoding的操作,也就是说猫假设对应001,狗对应010,熊猫对应100。对训练集和测试集都进行相同的处理。

model = Sequential()
model.add(Dense(512, input_shape=(3072,), activation="relu" ,kernel_initializer = initializers.TruncatedNormal(mean=0.0, stddev=0.05, seed=None),kernel_regularizer=regularizers.l2(0.01)))
model.add(Dropout(0.5))
model.add(Dense(256, activation="relu",kernel_initializer = initializers.TruncatedNormal(mean=0.0, stddev=0.05, seed=None),kernel_regularizer=regularizers.l2(0.01)))
model.add(Dropout(0.5))
model.add(Dense(len(lb.classes_), activation="softmax",kernel_initializer = initializers.TruncatedNormal(mean=0.0, stddev=0.05, seed=None),kernel_regularizer=regularizers.l2(0.01)))

这里就是按照顺序搭建神经网络架构,首先通过全连接输入3072×1的数据,转化成512,并且初始参数随机。然后继续将512的通过全连接变成256的,然后从256最后生成3种,因为这个项目就是3种类别,len(lb.classes_)这里用这个表示。

INIT_LR = 0.001
EPOCHS = 200
print("[INFO] 准备训练网络...")
opt = SGD(lr=INIT_LR)
model.compile(loss="categorical_crossentropy", optimizer=opt,
	metrics=["accuracy"])
H = model.fit(trainX, trainY, validation_data=(testX, testY),
	epochs=EPOCHS, batch_size=32)

这里我们定义学习率是0.01,然后epochs是200。对于epochs就是说我们这个模型要做多少次迭代,而一次迭代就是要迭代3000张图。对于batch呢就是说我的计算机一次需要处理多少张图像,比如batch等于32,那么就意味着我一次要处理32张图像。这里对于batch_size和epochs要分清。然后使用交叉商进行损失函数定义,然后选择优化器,以及判定标准准确率。设置完成之后我们就开始了本轮的训练。

print("[INFO] 正在评估模型")
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[150:], H.history["loss"][150:], label="train_loss")
#plt.plot(N[150:], H.history["val_loss"][150:], label="val_loss")
plt.plot(N[150:], H.history["accuracy"][150:], label="train_acc")
plt.plot(N[150:], H.history["val_accuracy"][150:], label="val_acc")
plt.title("Training Loss and Accuracy (Simple NN)")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend()
plt.savefig(args["plot"])

# 保存模型到本地
print("[INFO] 正在保存模型")
model.save(args["model"])
f = open(args["label_bin"], "wb")
f.write(pickle.dumps(lb))
f.close()

剩下的这些就是绘制结果曲线了,没什么可说的。最后一步是模型保存。
最后通过200个epochs后得到的。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

这里很明显,过拟合了!!!所以我们下面通过改变了一下学习率来继续来看一下有什么改进效果。把0.01改成0.001.我们直接来看图。
在这里插入图片描述
过拟合的现象还是有,但是效果要好不少。学习率对整个结果的影响还是很大的。然后我们再引入drop-out这个核心层,这个相当于是一个七伤拳的操作,为什么会出现过拟合,是因为神经元太多了,那么我们随机的去杀死一些神经元这样做会不会好一些呢。

model.add(Dropout(0.5))

加入了drop-out后的结果是
在这里插入图片描述
这里可以看出过拟合的趋势明显降低!!!而且也较为稳定。然后我们再看一下权重初始化的方法不同会有什么样的结果:
在这里插入图片描述
做了一个截断权重分布,另一个是均匀分布。对于过拟合现象确实有改善,并且也确实较为稳定了。对于初始化权重中,更改了一下标准差的系数。
在这里插入图片描述
然后我们有改动了一下正则化,所谓正则化就是在计算loss的时候,不仅仅只计算loss,也要加上一个权重项,也就是Loss+λ正则项,其中λ是惩罚系数。这样我们运行之后呢,就将模型保存了下来,然后我们利用保存下来的模型来预测照片的分类。

⭐️预测模块

首先导入所需要的工具包,分别是加载模型,定义参数模块,以及cv2等

from keras.models import load_model
import argparse
import pickle
import cv2

首先定义参数:

ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
	help="path to input image we are going to classify")
ap.add_argument("-m", "--model", required=True,
	help="path to trained Keras model")
ap.add_argument("-l", "--label-bin", required=True,
	help="path to label binarizer")
ap.add_argument("-w", "--width", type=int, default=28,
	help="target spatial dimension width")
ap.add_argument("-e", "--height", type=int, default=28,
	help="target spatial dimension height")
ap.add_argument("-f", "--flatten", type=int, default=-1,
	help="whether or not we should flatten the image")
args = vars(ap.parse_args())

在参数这里我们需要输入:

–image images/dog.jpg
–model output/simple_nn.model
–label-bin output/simple_nn_lb.pickle
–width 32
–height 32
–flatten 1

输入之后我们来分析一下输入的这几个参数,首先导入一张图像,然后导入之前训练好的模型,然后定义一下参数,并且要不要进行拉长操作。

image = cv2.imread(args["image"])
output = image.copy()
image = cv2.resize(image, (args["width"], args["height"]))
image = image.astype("float") / 255.0
if args["flatten"] > 0:
	image = image.flatten()
	image = image.reshape((1, image.shape[0]))
else:
	image = image.reshape((1, image.shape[0], image.shape[1],
		image.shape[2]))

然后我们读入一张图像,并且对图像进行预处理和归一化操作,如果要进行拉伸,那么还要进行一个拉伸的操作,因为传统的神经网络不能够对三维图像进行操作。如果要做卷积,那么就不用左拉伸操作。

print("[INFO] loading network and label binarizer...")
model = load_model(args["model"])
lb = pickle.loads(open(args["label_bin"], "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)

画出来结果。
在这里插入图片描述
在这里插入图片描述
这个图片就可以看得出来,预测的结果只保留预测概率最大的那一个。如果我们把epochs设置的更高的好,模型的预测效果肯定会更好。

⭐️卷积神经网络分类

我们用完经典神经网络之后,然后再用卷积神经网络去试一下做这个分类任务。
首先我们还是通过代码一步一步的去深入了解这个模型。
首先还是导入第三方库

import matplotlib
from model_name.simple_vggnet import SimpleVGGNet
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_utils import utils_paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import random
import pickle
import cv2
import os
import warnings
warnings.filterwarnings("ignore")

然后我们设置导入配置参数的函数

ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=True,
	help="path to input dataset of images")
ap.add_argument("-m", "--model", required=True,
	help="path to output trained model")
ap.add_argument("-l", "--label-bin", required=True,
	help="path to output label binarizer")
ap.add_argument("-p", "--plot", required=True,
	help="path to output accuracy/loss plot")
args = vars(ap.parse_args())

–dataset ./dataset
–model ./output_cnn/vggnet_model
–label-bin ./output_cnn/vggnet_lb.pickle
–plot ./output_cnn/vggnet_plot.png

这里设置的参数变成了vgg的模型,以及图像输出。

print("[INFO] loading images...")
data = []
labels = []
imagePaths = sorted(list(utils_paths.list_images(args["dataset"])))
random.seed(42)
random.shuffle(imagePaths)
for imagePath in imagePaths:
	image = cv2.imread(imagePath)
	image = cv2.resize(image, (64, 64))
	data.append(image)
	label = imagePath.split(os.path.sep)[-2]
	labels.append(label)

这里唯一一个不同的就是将图片resize成了64×64的。

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)
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)
model = SimpleVGGNet.build(width=64, height=64, depth=3,
	classes=len(lb.classes_))

这里面和之前都是一样的,包括归一化数据切分等!最后一个就是使用VGG卷积神经网络对图片进行操作。那么让我们来具体的看一下:
首先导入所需要的模块:

from keras.models import Sequential
from keras.layers.normalization.batch_normalization_v1 import BatchNormalization
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.initializers import TruncatedNormal
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dropout
from keras.layers.core 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

然后对于VGG需要的是wight×high×channel这个格式还是channel×wight×high这个格式,我们都先设定一下。然后就是按照顺序搭建神经网络模型了。

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)))
		#model.add(Dropout(0.25))

		# (CONV => RELU) * 2 => POOL 
		model.add(Conv2D(64, (3, 3), padding="same"))
		model.add(Activation("relu"))
		model.add(BatchNormalization(axis=chanDim))
		model.add(Conv2D(64, (3, 3), padding="same"))
		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"))
		model.add(Activation("relu"))
		model.add(BatchNormalization(axis=chanDim))
		model.add(Conv2D(128, (3, 3), padding="same"))
		model.add(Activation("relu"))
		model.add(BatchNormalization(axis=chanDim))
		model.add(Conv2D(128, (3, 3), padding="same"))
		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(512))
		model.add(Activation("relu"))
		model.add(BatchNormalization())
		#model.add(Dropout(0.6))

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

		return model

里面分别涉及到了卷积操作,卷积后需要加上relu激活函数。然后归一化处理也就是BN操作,最后进行一个pooling操作这样讲其进行卷积,最后通过全连接层来进行分类,经过softmax将图片进行分类。
在这里插入图片描述
上图对应卷积操作。
在这里插入图片描述
对应池化层操作。
卷积神经网络的loss和acc对比:
在这里插入图片描述
没有加入BN层。
在这里插入图片描述

加上BN 之后的结果图。虽然都有过拟合的现象,但是这个加上了BN之后,稳定性较强。然后加入drop-out,结果是:
在这里插入图片描述
这里可以看出由于初始化参数的设置问题,可能loss值初始比较高。
然后生成模型文件之后,我们依旧使用predict进行训练参数是:

–image images/dog.jpg
–model output_cnn/vggnet.model
–label-bin output_cnn/vggnet_lb.pickle
–width 64
–height 64

最后的结果是:
在这里插入图片描述
使用经典的神经网络没有识别出来的猫使用卷积神经网络的准确率可以达到94%。

🔎支持🎁🎁🎁如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦!这就是给予我最大的支持!

相关文章:

  • 基于 Echarts + Python Flask 动态实时大屏( 附代码)
  • 并查集原理及模拟实现
  • 【Redis】大key的处理
  • T-3.2-把Redis当作消息队列合不合适
  • 简单个人静态HTML网页设计作品 DIV布局个人介绍网页模板代码 DW个人网站制作成品 web网页制作与实现
  • java基于springboot+element的实现医院预约挂号系统 nodejs
  • 在vue项目中使用canvas实现甘特图
  • 【C++】之模板进阶
  • 100天精通Python(数据分析篇)——第58天:Pandas读写数据库(read_sql、to_sql)
  • Bean的生命周期
  • 哈希桶(详解创建)
  • 回归预测 | MATLAB实现SSA-BP多输入单输出回归预测
  • 【雅思备考】听说读写攻略 | 雅思核心词汇之科技类
  • Python-列表,从基础到进阶用法大总结,进来查漏补缺
  • JDBC模拟SQL注入和避免SQL注入
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • 230. Kth Smallest Element in a BST
  • 30天自制操作系统-2
  • CSS 专业技巧
  • input的行数自动增减
  • java8-模拟hadoop
  • Java编程基础24——递归练习
  • Java-详解HashMap
  • JS数组方法汇总
  • Redash本地开发环境搭建
  • 坑!为什么View.startAnimation不起作用?
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 智能合约Solidity教程-事件和日志(一)
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • 如何在招聘中考核.NET架构师
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • 昨天1024程序员节,我故意写了个死循环~
  • ​低代码平台的核心价值与优势
  • (33)STM32——485实验笔记
  • (WSI分类)WSI分类文献小综述 2024
  • (分布式缓存)Redis持久化
  • (六)Hibernate的二级缓存
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (五)MySQL的备份及恢复
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (转)关于pipe()的详细解析
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .Net 8.0 新的变化
  • .net core 连接数据库,通过数据库生成Modell
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .net和php怎么连接,php和apache之间如何连接
  • .pyc文件是什么?
  • @AliasFor注解
  • @PreAuthorize注解
  • []T 还是 []*T, 这是一个问题
  • [100天算法】-不同路径 III(day 73)
  • [ASP.NET MVC]Ajax与CustomErrors的尴尬