Keras复现VGG16及实现花卉分类
VGG网络
VGG网络是2014年ILSVRC竞赛的第二名(第一名是GoogleNet,即后来的inception),由牛津大学的计算机视觉实验组提出。原始论文地址https://arxiv.org/abs/1409.1556
VGG的出现证明了网络深度的增加可以提升模型的效果,它由一系列的卷积、池化堆叠而成,在论文中作者搭建了不同深度的6种VGG模型,命名A-E,网络深度依次加深。通常VGG16和VGG19是指D、E两个版本。
下表是原始论文中的6个版本VGG结构,A-LRN是在A版本的基础上使用了AlexNet中的LRN层,用来对比。
VGG的另一个特点是使用的卷积只有11和33的卷积,2个33的卷积代替55卷积,3个33卷积代替77的卷积,尽管它们的感受野相同,但使用多个小卷积代替大卷积,在保证感受野不变的情况下可以使参数量减小,且提升了网络的深度。比如55的卷积需要的参数量是25个,而用2个小的33卷积代替只需要18个。
此外VGG中使用了1*1的卷积,作者认为1x1卷积可以增加决策函数的非线性能力,非线性是由激活函数决定的,1x1卷积是线性映射,在同样的维度上映射特征图。
VGG的输入是224*224尺寸的图像,且经过特殊处理后才输入网络,处理不仅包括归一化等常规操作,并且每个像素值还减去了在总的训练集的均值。
在Keras中内置了处理输入的函数。
from keras.applications.imagenet_utils import preprocess_input
img = preprocess_input(img) # img是要输入的图像
# 查看preprocess_input函数的部分源码如下
# 该函数针对TensorFlow、caffe、Torch的处理并不相同,对tf只需将图像像素值缩放到-1和1之间,caffe则将图片通道变为BGR,做中心化但不缩放像素值范围,Torch缩放像素值在0到1之间,然后标准化。if mode == 'tf':x /= 127.5x -= 1.return x
if mode == 'torch':x /= 255.mean = [0.485, 0.456, 0.406]std = [0.229, 0.224, 0.225]
else:if data_format == 'channels_first':# 'RGB'->'BGR'if backend.ndim(x) == 3:x = x[::-1, ...]else:x = x[:, ::-1, ...]else:# 'RGB'->'BGR'x = x[..., ::-1]mean = [103.939, 116.779, 123.68]std = None
Keras搭建VGG16并进行花卉分类
一个在线可视化vgg16结构的网站: https://dgschwend.github.io/netscope/#/preset/vgg-16
数据集来自谷歌的一个鲜花数据集,网址http://download.tensorflow.org/example_image/flower_photo.tgz
该数据集比较小,包含五类鲜花的照片,chamomile(甘菊)、 tulip(郁金香)、rose(玫瑰)、sunflower(向日葵)、dandelion(蒲公英),每一类图片有800张左右,且大小不一,但图像都比较小约为,400x300。
对数据集的处理,采用npy格式存储数据。OpenCV读取图像后,直接使用preprocess_input 函数处理,然后存入npy文件。标签采用to_categorical进行独热编码转换标签。
使用Keras搭建VGG16,比较方便,按照论文中的结构一层层累积即可。每层卷积使用relu激活函数,最后输出softmax分类,类别为5,优化器采用sgd(在网上一个博客看到说采用Adam优化效果更好,但我试了没有sgd效果好?),分类交叉熵损失categorical_crossentropy。
Keras的另一个好处是keras.applications模块内置了许多模型,包括VGG、ResNet、InceptionV3、DenseNet121等。因此我们可以使用内置的VGG16,只需修改最后的全连接层输出类别即可。
import numpy as np
from keras.models import Model, load_model
from keras.layers import Dropout, Flatten, Dense, Input
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.utils import plot_model,to_categorical
from keras import optimizers
import os
import cv2
from keras.applications import VGG16
from keras.applications.vgg16 import preprocess_input
from keras.callbacks import TensorBoardclass vgg():def __init__(self, shape, num_classes, data_path, label_path, model_path):self.shape = shapeself.num_classes = num_classesself.data_path = data_pathself.label_path = label_pathself.model_path = model_pathself.log_path = "./logs"self.classes = self.classname()def classname(self, prepath="D://Datasets//flower_photos//flower_photos//"):# 数据集的类别序号和对应名称,注意的是序号是从1开始,而label中编码实际是从0开始classes = os.listdir(prepath) # 类别序号和名称class_dict = {int(Class.split(".")[0]): Class.split(".")[1] for Class in classes[0:5]}return class_dictdef generate_data(self, prepath="D://Datasets//flower_photos//flower_photos//"):classes = os.listdir(prepath) # 类别序号和名称data_path = self.data_pathlabel_path = self.label_pathdatas = []labels = []for i, abspath in enumerate(classes): # prepath的每一个文件目录img_names = os.listdir(prepath + abspath)for img_name in img_names: # 子目录中的每一张图片img = cv2.imread(os.path.join(prepath + abspath, img_name)) # cv2读取if not isinstance(img, np.ndarray):print("read img error")continueimg = cv2.resize(img, (224, 224)) # 尺寸变换224*224# img = img.astype(np.float32) # 类型转换为float32img = preprocess_input(img)label = to_categorical(i, self.num_classes)labels.append(label)datas.append(img)datas = np.array(datas)labels = np.array(labels)np.save(data_path, datas)np.save(label_path, labels)return Truedef vgg_model(self): # 自行构建VGG16input_1 = Input(shape=self.shape) # 输入224*224*3# 第一部分# 卷积 64深度,大小是3*3 步长为1 使用零填充 激活函数relu# 2次卷积 一次池化 池化尺寸2*2 步长2*2x = Conv2D(filters=64, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(input_1)x = Conv2D(filters=64, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x) # 64 224*224x = MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding="SAME")(x) # 64 112*112# 第二部分 2次卷积 一次池化# 卷积 128深度 大小是3*3 步长1 零填充x = Conv2D(filters=128, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x)x = Conv2D(filters=128, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x) # 128 112*112x = MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding="SAME")(x) # 128 56*56# 第三部分 3次卷积 一次池化 卷积256 3*3x = Conv2D(filters=256, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x)x = Conv2D(filters=256, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x)x = Conv2D(filters=256, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x) # 256 56*56x = MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding="SAME")(x) # 256 28*28# 第四部分 3次卷积 一次池化 卷积 512 3*3x = Conv2D(filters=512, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x)x = Conv2D(filters=512, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x)x = Conv2D(filters=512, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x) # 512 28*28x = MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding="SAME")(x) # 512 14*14# 第五部分 3次卷积 一次池化 卷积 512 3*3x = Conv2D(filters=512, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x)x = Conv2D(filters=512, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x)x = Conv2D(filters=512, kernel_size=(3, 3), strides=(1, 1), padding="SAME", activation="relu")(x) # 512 14*14x = MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding="SAME")(x) # 512 7*7x = Flatten()(x) # 扁平化,用在全连接过渡# 第六部分 三个全连接# 第一个和第二个全连接相同 输出4096 激活relu 使用dropout,随机丢弃一半x = Dense(4096, activation="relu")(x)Dropout(0.5)(x)x = Dense(4096, activation="relu")(x)Dropout(0.5)(x) # 输出 4096 1*1# 第三个全连接层 输出 softmax分类out_ = Dense(self.num_classes, activation="softmax")(x)model = Model(inputs=input_1, outputs=out_)# print(model.summary())sgd = optimizers.sgd(lr=0.001, momentum=0.9, nesterov=True)model.compile(sgd, loss="categorical_crossentropy", metrics=["accuracy"])# plot_model(model,"model.png")return modeldef pretrain_vgg(self): # 采用预训练的VGG16,修改最后一层model_vgg = VGG16(include_top=False, weights='imagenet', input_shape=(224, 224, 3)) # 不包含最后一层model = Flatten(name='Flatten')(model_vgg.output)model = Dense(self.num_classes, activation='softmax')(model) # 最后一层自定义model_vgg = Model(inputs=model_vgg.input, outputs=model, name='vgg16')sgd = optimizers.SGD(lr=0.001, momentum=0.9, nesterov=True)# sgd效果比Adam更好# adam = optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)model_vgg.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])# model_vgg.summary()return model_vggdef train(self, load_pretrain=False, batch_size=32, epoch=50):if load_pretrain:model = self.pretrain_vgg()else:model = self.vgg_model()# TensorBoard查看日志logs = TensorBoard(log_dir=self.log_path, write_graph=True, write_images=True)data_path = self.data_pathlabel_path = self.label_pathsave_path = self.model_pathx = np.load(data_path)y = np.load(label_path)# 必须打乱 否则验证集loss和acc会出问题np.random.seed(200)np.random.shuffle(x)np.random.seed(200)np.random.shuffle(y)model.fit(x, y, batch_size=batch_size, epochs=epoch, verbose=1, validation_split=0.3,callbacks=[logs])model.save(save_path)def predict(self, img_path="test.jpg"):# model = vgg_model((224,224,3),5)model_path = self.model_pathmodel = load_model(model_path)test_img = cv2.imread(img_path)test_img = cv2.resize(test_img, (224, 224))test_img = preprocess_input(test_img)ans = model.predict(test_img.reshape(1, 224, 224, 3))max_index = np.argmax(ans, axis=1) # 预测结果是值范围0-4的行向量,因此对应的类别序号要+1print("预测结果是%s"%(self.classes[max_index[0] + 1]))data = r"D:\Datasets\flower_photos\flower_photos\train_data.npy"
label = r"D:\Datasets\flower_photos\flower_photos\labels.npy"
mode_path = r"D:\Datasets\flower_photos\flower_photos\flowers_5classes.h5"
vgg16 = vgg((224,224,3), 5, data, label, mode_path)
# vgg16.generate_data()
# vgg16.train(batch_size=32,epoch=50)
# vgg16.predict(imgpath)
为了节省时间只训练了30轮,收敛挺快。
TensorBoard查看模型结构如下:
训练过程acc和loss变化曲线:
预测也基本可以成功,懒得放图了。
此外遇到的一个问题记录一哈,刚开始在model.fit之前没有加入打乱data和label,我想着fit本身有个参数shuffle=True,结果训练过程中训练集的acc不断提高,甚至会稳持续在1.000,loss降不下来,始终在十点多,验证集更离谱,有时acc为0,loss保持不变,有时则是acc稳定在0.2,loss降不下。
后来查了发现这个打乱是打乱一个batch里面的数据顺序,在投入数据集训练之前,整个数据集的顺序应该被随机打乱才行。
np.random.seed(200)
np.random.shuffle(x)
np.random.seed(200)
np.random.shuffle(y)
Keras复现VGG16及实现花卉分类相关推荐
- Python教程之使用 Gradio 部署 Keras 花卉分类模型
深度学习模型,尤其是 CNN(卷积神经网络),被用来在标记图像的帮助下对不同的对象进行分类.使用这些图像对模型进行非常准确的训练.测试,然后进行部署以提高性能.例如,经过训练的图像分类模型接受汽车图像 ...
- Keras使用VGG16模型预测自己的图片
Keras使用VGG16模型预测自己的图片 环境 Win10 Miniconda3 Pycharm2018.02 代码如下 from keras.applications.vgg16 import V ...
- NS之VGG(Keras):基于Keras的VGG16实现之《复仇者联盟3》灭霸图像风格迁移设计(A Neural Algorithm of Artistic Style)
NS之VGG(Keras):基于Keras的VGG16实现之<复仇者联盟3>灭霸图像风格迁移设计(A Neural Algorithm of Artistic Style) 导读 通过代码 ...
- 【华为云技术分享】基于ModelArts AI市场算法MobileNet_v2实现花卉分类,支持CPU、GPU、Ascend推理
概述 MobileNetsV2是基于一个流线型的架构,它使用深度可分离的卷积来构建轻量级的深层神经网,此模型基于 MobileNetV2: Inverted Residuals and Linear ...
- 从keras看VGG16结构图
vgg16训练 上面放了一个keras用vgg16训练测试的例子,我也试过用vgg16训练然后测试自己的例子,效果一般,这里我们来分析一下vgg16的网络结果 keras代码如下 def VGG_16 ...
- CNN卷积神经网络:花卉分类
文章目录 简介 一.CNN卷积神经网络基础知识 二.数据集介绍 三.代码实现 读取数据 数据处理 搭建网络 训练网络 测试网络 保存网络 结果展示 总结 简介 本篇文章利用pytorch搭建CNN卷积 ...
- 基于Keras搭建CNN、TextCNN文本分类模型
基于Keras搭建CNN.TextCNN文本分类模型 一.CNN 1.1 数据读取分词 1.2.数据编码 1.3 数据序列标准化 1.4 构建模型 1.5 模型验证 二.TextCNN文本分类 2.1 ...
- tensorflow入门实战----VGG16完成猫狗分类
其中模型和训练集需要提前下好的 from keras.application.vgg16 import VGG16 from keras.models import Sequential from k ...
- 调用tensorflow2中的tf.keras.applications.VGG16并进行微调
调用tensorflow2中的tf.keras.applications.VGG16并进行微调 环境配置:tensorflow版本2.3.0:cuda 10.1:python3.8 数据集:cifar ...
最新文章
- 刷新UITableView
- 操作系统和Web服务器那点事儿
- java 动态拼接sql_动态SQL拼接工具类
- android BSP
- Spring源码分析-循环依赖
- JSP和Servlet相同点和不同点?
- 用VB使用花式画笔实现流动的选取框
- dos 批量copy文件
- 大数据质量管理策略有哪些
- 《机器人编程实战》一一2.1 为什么需要更多努力
- python可视化网页设计_python可视化工具
- 信息安全工程师(中级)—重要知识点总结
- Kali学习 | 密码攻击:6.10 创建密码字典
- UE4编辑器下Tick的实现
- 计算机教程无线路由器桥接上网,两个路由器无线桥接
- 行政人员需要了解的基本企业财务知识
- Git Branching基础操作学习笔记
- [轉]ERP系统之比较——SAP、Oracle、BAAN、JDE、SSA
- HDMI协议解析-从软硬件角度分析
- jquery 处理页面的事件详解
热门文章
- 前端基础知识(更新中)
- VS2013图片控件bug
- 十以内的加减乘除运算题
- 格力电器参与闻泰科技收购半导体,推进500亿“造芯”计划
- Matlab:动态正则表达式
- 计算机操作系统(第3版) (2018年清华大学出版社出版的图书)
- 第7章第35节:五图排版:一张背景四张交错对齐布局 [PowerPoint精美幻灯片实战教程]
- 为毛你深陷故障驱动式开发
- The Cluster ID xxx doesn't match stored clusterId Some(xxx) in meta.properties. The broker is trying
- Windows10电脑开机蓝屏解决方法