个人博客:http://www.chenjianqu.com/

原文链接:http://www.chenjianqu.com/show-57.html

本文是CNN经典论文《VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION》Karen Simonyan∗ & Andrew Zisserman,ICLR2015 的阅读笔记。

论文笔记

1.解决了什么

提高大规模图像分类的精度。

2.使用的方法

搭建更深的卷积神经网络:使用3x3卷积核,模型达到16-19层,16层的被称为VGG16,19层的被称为VGG19。

使用Single-Scale和Multi-Scale训练和评估模型。

3.实验结果

该模型获得ImageNet Challenge 2014的图像localization第一名,图像分类第二名。

4.待解决的问题

该模型还不够深,只达到19层便饱和了,而且没有探索卷积核宽度对网络性能的影响。同时网络参数过多,达到1.3亿参数以上。

VGGNet

VGG名字来源于Visual Geometry Group, Department of Engineering Science, University of Oxford。论文里对多种不同深度的网络进行了测试,分别称为为A-E网络,从11-19层,其中D和E被称为VGG16和VGG19。各网络结构如下:

来一个VGG16的立体图:

各个网络的宽度都小,刚开始为64,最后达到512通道。各网络的总参数如下:

最多的VGG19有1.4亿参数。。我的GTX960M肯定是跑不动了。A-E都使用通用的配置:

网络输入:224x224的RGB图像;

输入图像预处理:减去训练集的像素均值;

卷积核大小:使用3x3的卷积核,这是the smallest size to capture the notion of left/right, up/down,

center。使用1x1卷积核,对输入通道的进行线性变换。

卷积步长:1,卷积时padding。

最大池化层:size=2x2,stride=2

跟AlexNet一样,卷积层后接两层的全连接层和一个一千神经元的输出层。

所有的隐层都使用ReLU作为激活函数。

网络不包含(除了A_LRN网络)局部响应归一化层(LRN),因为发现LRN没卵用而且还费时费力。

VGGNet将AlexNet中的大卷积核都替换为小的卷积核,使用的卷积核size=3x3,stride=1。因为两个3x3的卷积叠加等价于一个5x5的卷积,3个3x3的卷积叠加等价于一个7x7的卷积叠加。下图可说明这点:

小卷积核替换大卷积核的优点:

  1. 本来只有一个非线性层,替换后增加到3个,这增加了网络的深度和非线性,有利于决策函数辨别。

  2. 减少了参数数量,本来有7x7=49,减少到3x3x3=27,这可以看做是对7x7卷积滤波器进行正则化,迫使他们分解为3x3滤波器。

网络C里面加入了1x1的卷积核,这是在不影响感受野的情况下增加决策函数的非线性的方法。输入通道和输出通道相同,因此是一个线性映射,激活函数的存在引入了非线性。

训练细节

权重初始化

权重的初始化很重要,初始化不好会导致学习的停滞。这里初始化的方法是:首先训练网络A,网络A足够浅以至于可以随机初始化权重。训练好A之后,其它更深的网络的前四层和后两个全连接层使用A的权重进行初始化,其它层的权重随机初始化,且随机初始化的参数为:高斯分布,均值为0,标准差为0.01,偏置权重为0。后来发现使用Xavier初始化跟预训练权重的效果一样好。

训练参数

跟AlexNet一样,使用带动量的梯度下降训练,动量系数为0.9,batch_size=128;使用L2正则化,惩罚系数为5x10^-4;在全连接层使用droopout,系数为0.5。学习率初始化为0.01,当验证准确率不再上升时,将学习率除以10。总共迭代370k,共74epochs。

AlexNet当时用了90epochs,VGGNet参数更多卻训练的更快,作者猜想原因是使用小卷积核,有隐性正则化,此外,某些层被预初始化了因此收敛更快。

数据增强

在AlexNet中,图片由原始图片缩放至2256x2265,再裁剪至224x224。令S是图片缩放至某长宽后的短边,缩放后可以从中裁剪出输入图片224x224,则S>=224。S不能太小,否则数据多样性不足,不能太大,否则只能包含原始图片的一小部分。

这里使用两种方法:

第一是单尺度训练。这里首先使用S=256预训练网络,接着降低学习率至0.001,再使用S=384训练网络。

第二是多尺度训练。每个训练图片被独立的随机缩放,S在[Smin,Smax]范围内,这里的Smin=256,Smax=512。原始训练集上每张图片中目标大小是不确定的,因此采用这一方法是有效的,其实这也可以看做是通过抖动缩放来增加训练集。出于速度考虑,先预训练S=384的单尺度模型,再微调多尺度模型。

测试细节

论文在测试时,将全连接层转换为卷积层。第一个全连接层转换为7x7的卷积层,最后两个全连接层转换为1x1的卷积层。示意图如下:

只是把权重的维度变换和拓展了。经过转换的网络就没有了全连接层,这样网络就可以接受任意尺寸的输入,而不是像之前之能输入固定大小的输入。

这样网络的输出是一个class score map,map的每个通道表示每个分类,map的分辨率是可变的,取决于输入图片的大小。为了获得输出的向量,需要对class score map进行spatially averaged。

代码实现

使用tensorflow.slim实现vgg16的代码如下:

import tensorflow as tf
from tensorflow.contrib.layers import xavier_initializerslim = tf.contrib.slimREGULARIZER=0.0005def VGG16(inputs):with slim.arg_scope([slim.conv2d], stride=1, kernel_size=3,activation_fn=tf.nn.relu,padding='SAME',weights_initializer=xavier_initializer(),weights_regularizer=slim.l2_regularizer(REGULARIZER),biases_regularizer=slim.l2_regularizer(REGULARIZER),biases_initializer=tf.zeros_initializer()):net = slim.conv2d(inputs, num_outputs=64,scope='conv1')net = slim.conv2d(net, num_outputs=64,scope='conv2')net=slim.max_pool2d(net,[2,2],2,padding='SAME',scope='maxpooling1')net = slim.conv2d(net, num_outputs=128,scope='conv3')net = slim.conv2d(net, num_outputs=128,scope='conv4')net=slim.max_pool2d(net,[2,2],2,padding='SAME',scope='maxpooling2')net = slim.conv2d(net, num_outputs=256,scope='conv5')net = slim.conv2d(net, num_outputs=256,scope='conv6')net = slim.conv2d(net, num_outputs=256,scope='conv7')net=slim.max_pool2d(net,[2,2],2,padding='SAME',scope='maxpooling3')net = slim.conv2d(net, num_outputs=512,scope='conv8')net = slim.conv2d(net, num_outputs=512,scope='conv9')net = slim.conv2d(net, num_outputs=512,scope='conv10')net=slim.max_pool2d(net,[2,2],2,padding='SAME',scope='maxpooling4')net = slim.conv2d(net, num_outputs=512,scope='conv11')net = slim.conv2d(net, num_outputs=512,scope='conv12')net = slim.conv2d(net, num_outputs=512,scope='conv13')net=slim.max_pool2d(net,[2,2],2,padding='SAME',scope='maxpooling5')net=slim.flatten(net,scope='flatten')with slim.arg_scope([slim.fully_connected],activation_fn=tf.nn.relu,weights_initializer=xavier_initializer(),weights_regularizer=slim.l2_regularizer(REGULARIZER),biases_initializer=tf.zeros_initializer(),biases_regularizer=slim.l2_regularizer(REGULARIZER)):net=slim.fully_connected(net,num_outputs=4096,scope='fc1')net = slim.dropout(net, 0.5, scope='dropout1')net=slim.fully_connected(net,num_outputs=4096,scope='fc2')net = slim.dropout(net, 0.5, scope='dropout2')out=slim.fully_connected(net,num_outputs=1000,activation_fn=None,scope='out')return out

定义训练参数

from tensorflow import name_scope as namespaceBATCH_SIZE=128
DATA_LEN=50000x = tf.placeholder(tf.float32, shape=[None, 224, 224, 3], name='input')
y_ = tf.placeholder(tf.float32, [None, 1000], name='labels')global_step=tf.Variable(0,trainable=False)y=VGG16(x)with namespace('loss'):#softmax并计算交叉熵#print(y.get_shape().as_list() )ce_loss = slim.losses.softmax_cross_entropy(y, y_) #交叉熵损失regularization_loss = tf.add_n(slim.losses.get_regularization_losses())#正则损失loss=ce_loss+regularization_losswith namespace('train'):#使用指数衰减学习率learning_rate=tf.train.exponential_decay(0.01,#初始学习率global_step,DATA_LEN/BATCH_SIZE,#多少次更新一次学习率0.99,#学习率衰减率staircase=True#学习率阶梯下降)train_step=tf.train.MomentumOptimizer(learning_rate,0.9,#动量系数).minimize(loss,global_step=global_step)
with namespace('acc'):correct_prediction=tf.equal(tf.argmax(y,1),tf.argmax(y_,1))accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))  tf.summary.scalar('loss',loss)
tf.summary.scalar('accuracy',accuracy)
merged=tf.summary.merge_all();

使用Keras数据生成器进行数据增强,这部分没有遵照原文。训练代码如下:

#定义数据生成器
from keras.preprocessing import imagetrain_dir=r'F:\BaiduNetdiskDownload\mini-imagenet\images_normal'
steps=100000train_gen=image.ImageDataGenerator(featurewise_center=True,#输入数据数据减去数据集均值width_shift_range=0.2,#水平平移height_shift_range=0.2,#垂直平移horizontal_flip=True,#水平翻转zoom_range=[0.5, 1.5],#缩放范围brightness_range=[-0.1,0.1] #亮度变化范围
)
tg=train_gen.flow_from_directory(train_dir,target_size=(224,224),batch_size=128,class_mode='categorical'
)with tf.Session() as sess:init_op=tf.global_variables_initializer()sess.run(init_op)writer=tf.summary.FileWriter('D:/Jupyter/cv/VGGNet_log',sess.graph)saver=tf.train.Saver()for i in range(steps):next_data,next_label=next(tg)summary,_,loss_value,step=sess.run([merged,train_step,loss,global_step],feed_dict={x:next_data,y_:next_label})writer.add_summary(summary,step)print('step%d loss:%f'%(step,loss_value))writer.close()

Fine-tuning

 1.数据集准备

使用和AlexNet原理和实现一样的数据集,下载下来并按文件夹分好类,如下:

上面用作训练集,需要从每个分类里面移一些用于验证的数据出来,代码如下:

import os
import shutil
import tqdmbasedir=r'F:\BaiduNetdiskDownload\mini-imagenet\images_normal'
newdir=r'F:\BaiduNetdiskDownload\mini-imagenet\image_normal_test'NUM=100
for clsdir in tqdm.tqdm(os.listdir(basedir)):#创建文件夹newpath=os.path.join(newdir,clsdir)if(os.path.exists(newpath)==False):os.makedirs(newpath)oldpath=os.path.join(basedir,clsdir)for fileName in (os.listdir(oldpath))[:NUM]:fileOldPath=os.path.join(oldpath,fileName)fileNewPath=os.path.join(newpath,fileName)shutil.move(fileOldPath,fileNewPath)

2.加载模型,并在bottleneck加上自定义的全连接层。

from keras.applications.vgg16 import VGG16
from keras.layers import *# bulid network
inputs = Input(shape=[224, 224, 3])
base_model = VGG16(include_top=False, weights='imagenet', input_tensor=inputs)
from keras.models import Model, load_model
from keras.utils import plot_model#首先冻结预训练模型的参数
for layer in base_model.layers:layer.trainable=False#搭建自己的全连接层
flatten=Flatten()(base_model.output)
fc1=Dense(512,activation='relu')(flatten)
dropout1=Dropout(rate=0.5)(fc1)
fc2=Dense(512,activation='relu')(dropout1)
dropout2=Dropout(rate=0.5)(fc2)
fc3=Dense(100,activation='softmax')(dropout2)model=Model(inputs=inputs,outputs=fc3)model.summary()
plot_model(model,to_file='VGG16.png',show_shapes=True)

3.定义数据增强器,并训练全连接层

from keras.preprocessing import image
from keras import initializers
from keras import optimizersBATCH_SIZE=32
EPOCHS=20#定义训练集生成器
train_gen=image.ImageDataGenerator(featurewise_center=True,#输入数据数据减去数据集均值width_shift_range=0.2,#水平平移height_shift_range=0.2,#垂直平移horizontal_flip=True,#水平翻转brightness_range=[-0.1,0.1],#亮度变化范围zoom_range=[0.5,1.5] #缩放的比例范围
)
train_dir=r'F:\BaiduNetdiskDownload\mini-imagenet\images_normal'
x=train_gen.flow_from_directory(train_dir,target_size=(224,224),batch_size=BATCH_SIZE,class_mode='categorical'
)#定义验证集生成器
val_datagen = image.ImageDataGenerator()
val_dir=r'F:\BaiduNetdiskDownload\mini-imagenet\image_normal_test'
validation_generator = val_datagen.flow_from_directory(val_dir,target_size=(224, 224),batch_size=BATCH_SIZE,class_mode='categorical'
)#编译模型
model.compile(loss='categorical_crossentropy',optimizer=optimizers.SGD(lr=1e-3,momentum=0.9,decay=0.005),metrics=['acc'])#训练模型
history=model.fit_generator(x,steps_per_epoch=int(50000/BATCH_SIZE),#每回合的步数epochs=EPOCHS,validation_data=validation_generator,validation_steps=int(10000/BATCH_SIZE),shuffle=True,
)
#保存模型
model.save(filepath='D:/Jupyter/cv/VGGNet_FT_log/vgg16.h5')

4.微调所有层

BATCH_SIZE=64
EPOCHS=20#解冻网络
for layer in base_model.layers:layer.trainable=True
#编译模型
model.compile(loss='categorical_crossentropy',optimizer=optimizers.SGD(lr=1e-5,momentum=0.9,decay=0.005),metrics=['acc'])
#训练模型
history=model.fit_generator(x,steps_per_epoch=int(50000/BATCH_SIZE),#每回合的步数epochs=EPOCHS,validation_data=validation_generator,validation_steps=int(10000/BATCH_SIZE),shuffle=True,
)#保存模型
model.save(filepath='D:/Jupyter/cv/VGGNet_FT_log/vgg16.h5')

参考文献

[1] Karen Simonyan∗ & Andrew Zisserman.VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION》.ICLR2015

[2]露秋.VGG 论文阅读记录.https://zhuanlan.zhihu.com/p/42233779?utm_source=qq&utm_medium=social&utm_oi=556883753528516608.2018-08-19

VGGNet原理和实现相关推荐

  1. 【深度学习】VGGNet原理解析及实现

    [深度学习]VGGNet原理解析及实现 VGGNet由牛津大学的视觉几何组(Visual Geometry Group)和Google DeepMind公司的研究员共同提出,是ILSVRC-2014中 ...

  2. VGGNet原理及tensorflow实现

    VGGNet介绍: VGGNet是牛津大学计算机视觉组和Google DeepMind一起研发的卷积神经网络,VGGNet探索了卷积神经网络的深度与其性能之间的关系.VGG的结构特点是通过反复堆叠3x ...

  3. 吴恩达深度学习笔记(83)-LeNet-5、AlexNet和VGGNet网络知多少

    https://www.toutiao.com/a6646734819151577608/ 2019-01-22 07:12:38 经典网络(Classic networks) 这节课,我们来学习几个 ...

  4. CVPR 2022|从原理和代码详解FAIR的惊艳之作:全新的纯卷积模型ConvNeXt

    本文首发于极市平台,作者科技猛兽,转载请获得授权并标明出处. 本文目录 7 匹敌 Transformer 的2020年代的卷积网络 (来自 FAIR,UCB) 7.1 ConvNeXt 原理分析 7. ...

  5. 万字长文,从原理角度科普人工智能技术点!!

    前一段时间应客户邀请,给客户进行人工智能相关知识的科普,客服群体是研究院的同志,提出要求,想要我讲解干货多一些.能够讲到概念的同时最好也能够从原理出发的进行科普.于是我从各种资料中总结出这样的一份科普 ...

  6. AI绘画火了!一文看懂背后技术原理

    导语 | 近些年AI蓬勃发展,在各行各业都有着不同方式的应用.而AI创作艺术和生产内容无疑是今年以来最热门的话题,AI创作到底发生过什么,原理又是如何,是噱头还是会有对我们有用的潜在应用场景呢?我们旨 ...

  7. SSD系列算法原理讲解----(1)SSD系列算法介绍(主干网络、多尺度Feature Map预测)(笔记)

    SSD系列算法原理介绍 SSD算法介绍: Single Shot MultiBox Detector(One-stage方法) - Wei Liu在ECCV 2016提出 - 直接回归目标类别和位置 ...

  8. AlexNet VGGNet ResNet 对比 简介

    AlexNet 网络结构 多GPU Relu Dropout 层叠池化 图片的随机采样 其他 VGGNet 网络结构 3*3 卷积核 1*1 卷积核 LRN 其他 ResNet 退化问题 残差学习 残 ...

  9. 深度学习入门笔记(二十):经典神经网络(LeNet-5、AlexNet和VGGNet)

    欢迎关注WX公众号:[程序员管小亮] 专栏--深度学习入门笔记 声明 1)该文章整理自网上的大牛和机器学习专家无私奉献的资料,具体引用的资料请看参考文献. 2)本文仅供学术交流,非商用.所以每一部分具 ...

最新文章

  1. 机器狗常州巡逻防疫,喊话“不扎堆不聚集”,网友:给孩子安个狗头吧
  2. 【git】 重置文件
  3. elasticSearch6源码分析(2)模块化管理
  4. SilverLigth的Chart不要图例(Legend)的方法
  5. 关于zookeeper中session timeout
  6. CYQ.Data 轻量数据层之路 使用篇-MAction 取值赋值 视频[带音乐] F (二十四)
  7. eval在python中是什么意思_如何在Python中使用eval ?
  8. MySQL数据库权限管理
  9. Xcode代码提示联想功能失效,按command键点不进去类库,提示“?”
  10. 数据结构-1-顺序表的实现
  11. 算法:特殊二维数组查询key值是否存在
  12. 通过MFC实现数码管显示功能
  13. Unity Shader入门精要学习笔记 - 第11章 让画面动起来
  14. 浅谈Android事件分发
  15. haproxy配置sni实现https多域名代理
  16. 反催收凉透 马上、平安普惠、招联、中银消费金融等联手打击
  17. OpenGL-聚光灯-spot
  18. 选定目标和非功利性学习
  19. wps姓名隐藏为星号_wps表格怎么把重要数字隐藏用星号显示?
  20. CCS7.3烧写DSP的on-chip FLASH时,如何只擦除部分FLASH Sector(一块DSP芯片的片上FLASH烧写两个工程)

热门文章

  1. dax 筛选 包含某个字_Power BI 利器——DAX
  2. python朴素贝叶斯分类MNIST数据集
  3. 重温Android中的消息机制
  4. reveal end of document
  5. MongoDB(一)-- 简介、安装、CRUD
  6. 在XNA 3.0 项目添加声音——播放MP3或WMA声音文件
  7. 以二进制的形式保存在数据库中
  8. 公开最近开发的OA的框架图
  9. Transformer开始往动态路由的方向前进了!厦大华为提出TRAR,在VQA、REC上性能SOTA!(ICCV 2021)...
  10. 【堪萨斯州立大学】电子和计算机工程系智能能源研究室、硬件安全实验室招募博士,提供多个全额奖学金机会...