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及实现花卉分类相关推荐

  1. Python教程之使用 Gradio 部署 Keras 花卉分类模型

    深度学习模型,尤其是 CNN(卷积神经网络),被用来在标记图像的帮助下对不同的对象进行分类.使用这些图像对模型进行非常准确的训练.测试,然后进行部署以提高性能.例如,经过训练的图像分类模型接受汽车图像 ...

  2. Keras使用VGG16模型预测自己的图片

    Keras使用VGG16模型预测自己的图片 环境 Win10 Miniconda3 Pycharm2018.02 代码如下 from keras.applications.vgg16 import V ...

  3. NS之VGG(Keras):基于Keras的VGG16实现之《复仇者联盟3》灭霸图像风格迁移设计(A Neural Algorithm of Artistic Style)

    NS之VGG(Keras):基于Keras的VGG16实现之<复仇者联盟3>灭霸图像风格迁移设计(A Neural Algorithm of Artistic Style) 导读 通过代码 ...

  4. 【华为云技术分享】基于ModelArts AI市场算法MobileNet_v2实现花卉分类,支持CPU、GPU、Ascend推理

    概述 MobileNetsV2是基于一个流线型的架构,它使用深度可分离的卷积来构建轻量级的深层神经网,此模型基于 MobileNetV2: Inverted Residuals and Linear ...

  5. 从keras看VGG16结构图

    vgg16训练 上面放了一个keras用vgg16训练测试的例子,我也试过用vgg16训练然后测试自己的例子,效果一般,这里我们来分析一下vgg16的网络结果 keras代码如下 def VGG_16 ...

  6. CNN卷积神经网络:花卉分类

    文章目录 简介 一.CNN卷积神经网络基础知识 二.数据集介绍 三.代码实现 读取数据 数据处理 搭建网络 训练网络 测试网络 保存网络 结果展示 总结 简介 本篇文章利用pytorch搭建CNN卷积 ...

  7. 基于Keras搭建CNN、TextCNN文本分类模型

    基于Keras搭建CNN.TextCNN文本分类模型 一.CNN 1.1 数据读取分词 1.2.数据编码 1.3 数据序列标准化 1.4 构建模型 1.5 模型验证 二.TextCNN文本分类 2.1 ...

  8. tensorflow入门实战----VGG16完成猫狗分类

    其中模型和训练集需要提前下好的 from keras.application.vgg16 import VGG16 from keras.models import Sequential from k ...

  9. 调用tensorflow2中的tf.keras.applications.VGG16并进行微调

    调用tensorflow2中的tf.keras.applications.VGG16并进行微调 环境配置:tensorflow版本2.3.0:cuda 10.1:python3.8 数据集:cifar ...

最新文章

  1. 刷新UITableView
  2. 操作系统和Web服务器那点事儿
  3. java 动态拼接sql_动态SQL拼接工具类
  4. android BSP
  5. Spring源码分析-循环依赖
  6. JSP和Servlet相同点和不同点?
  7. 用VB使用花式画笔实现流动的选取框
  8. dos 批量copy文件
  9. 大数据质量管理策略有哪些
  10. 《机器人编程实战》一一2.1 为什么需要更多努力
  11. python可视化网页设计_python可视化工具
  12. 信息安全工程师(中级)—重要知识点总结
  13. Kali学习 | 密码攻击:6.10 创建密码字典
  14. UE4编辑器下Tick的实现
  15. 计算机教程无线路由器桥接上网,两个路由器无线桥接
  16. 行政人员需要了解的基本企业财务知识
  17. Git Branching基础操作学习笔记
  18. [轉]ERP系统之比较——SAP、Oracle、BAAN、JDE、SSA
  19. HDMI协议解析-从软硬件角度分析
  20. jquery 处理页面的事件详解

热门文章

  1. 前端基础知识(更新中)
  2. VS2013图片控件bug
  3. 十以内的加减乘除运算题
  4. 格力电器参与闻泰科技收购半导体,推进500亿“造芯”计划
  5. Matlab:动态正则表达式
  6. 计算机操作系统(第3版) (2018年清华大学出版社出版的图书)
  7. 第7章第35节:五图排版:一张背景四张交错对齐布局 [PowerPoint精美幻灯片实战教程]
  8. 为毛你深陷故障驱动式开发
  9. The Cluster ID xxx doesn't match stored clusterId Some(xxx) in meta.properties. The broker is trying
  10. Windows10电脑开机蓝屏解决方法