本来打算跟着自己的进度走,这周拿出yoloV3的代码出来,但是emmmm,有时候身不由己…害。
这里自己学习复习一下VGG模型,然后好完成任务。
VGG模型是2014年ILSVRC竞赛的第二名,但是VGG模型在多个迁移学习任务中的表现要优于googLeNet。而且,从图像中提取CNN特征,VGG模型是首选算法。它的缺点在于,参数量有140M之多,需要更大的存储空间。但是这个模型很有研究价值。(摘自百度百科)
特点:
名称 | 参数 |
---|---|
输入图像 | 224x224 |
卷积核 | 3x3为主,少量1x1 |
池化层 | 2x2 |
激活函数 | ReLu |
VGG的作者只用了两种卷积核:1x1和3x3。作者认为 两个3X3的卷积核堆叠获得的感受野大小,相当于一个5X5的卷积 ,而三个3X3的卷积核获取到的感受野相当于一个7X7的卷积
因为5X5卷积可以看做一个晓得全连接网络在5X5的区域滑动,而再用一个全连接层这个3X3的卷积输出,全连接层我们也可以看做一个3X3的卷积层,这样我们可以使用两个3X3的卷积核叠加替换一个5X5的卷积核 ,7X7的同理
效果如下图:
有些同学可能会问,用小卷积核叠加,计算量和参数量会发生怎样的一个变化,我们这里做一个计算测试,可以更直观的看到计算量的变化,这里以224x224x3的RGB图(设置pad=1,stride=4,output_channel=96)做卷积,卷积层的参数规模
卷积核 | 尺寸计算量 calc.sum | 参数量feature map |
---|---|---|
3X3 | 1600万 | 309174 |
5X5 | 4500万 | 303750 |
7X7 | 8700万 | 298374 |
9X9 | 1.4亿 | 293046 |
11X11 | 2亿 | 287766 |
这里可以看到,在卷积核的尺寸变化时,对feature map的变化不是很大,不同尺寸的kernel大小下,参数量都是30万左右,变化不是很明显。而在计算量上,变化就很惊人了…
由此可以得到选择小卷积核的优势:
VGG在最后的三个阶段都用到了1x1卷积核,选用1x1卷积核的最直接原因是在维度上继承全连接,然而作者首先认为1x1卷积可以增加决策函数(decision function,这里的决策函数就是softmax)的非线性能力,非线性是由激活函数ReLU决定的,本身1x1卷积则是线性映射,即将输入的feature map映射到同样维度的feature map。
作者在测试阶段把网络中原本的三个全连接层依次变为1个conv7x7,2个conv1x1,也就是三个卷积层。改变之后,整个网络由于没有了全连接层,网络中间的feature map不会固定,所以网络对任意大小的输入都可以处理
#######################
# 引入库 #
#######################
#coding=utf-8
from keras.applications import VGG16
from matplotlib import pyplot as plt
from PIL import Image
import os
from tqdm import tqdm_notebook
from random import shuffle
import shutil
import pandas as pd
import keras
from keras.preprocessing.image import ImageDataGenerator
from keras_tqdm import TQDMNotebookCallback
from keras.models import Sequential
from keras.layers import Dense,Activation,GlobalAveragePooling2D
from keras.layers import Dropout
from keras.layers import Flatten
from keras.constraints import maxnorm
from keras.optimizers import SGD
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
from keras.callbacks import Callback
from keras.preprocessing.image import ImageDataGenerator
from keras.layers.normalization import BatchNormalization
from keras.callbacks import TensorBoard
import numpy as np
import tensorflow as tf
#######################
# 使用GPU #
#######################
from keras import backend as K
tf.config.experimental.list_physical_devices('GPU')
import os
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="0"
#######################
# 读入数据 #
#######################
#--------读取自定义数据----------#
train_dir="D:/work/python/vgg-keras/data/train"
val_dir="D:/work/python/vgg-keras/data/train"
test_dir="D:/work/python/vgg-keras/data/test"
#--------进行数据增强---------#
train_pic_gen=ImageDataGenerator(rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
rescale=1./255,shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest',
cval=0,
channel_shift_range=0,
vertical_flip=False)
test_pic_gen=ImageDataGenerator(rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
rescale=1./255,shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest',
cval=0,
channel_shift_range=0,
vertical_flip=False)
val_pic_gen=ImageDataGenerator(rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
rescale=1./255,shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest',
cval=0,
channel_shift_range=0,
vertical_flip=False)
#---------整理准备放入训练------------#
train_flow=train_pic_gen.flow_from_directory(train_dir,(224,224),batch_size=8,class_mode='categorical')
val_flow=val_pic_gen.flow_from_directory(val_dir,(224,224),batch_size=8,class_mode='categorical')
test_flow=test_pic_gen.flow_from_directory(test_dir,(224,224),batch_size=8,class_mode='categorical')
print(train_flow.class_indices)
##########################
# 定义VGG16模型 #
##########################
model = Sequential()
model.add(Conv2D(64,(3,3),strides=(1,1),input_shape=(224,224,3),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(64,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(128,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(128,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(4096,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(4096,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(3,activation='softmax'))
#######################
# 开始训练 #
#######################
model.summary()
model.compile(optimizer=SGD(lr=0.0001, momentum=0.9,decay=0.0005),loss='categorical_crossentropy',metrics=['acc'])
#import catvsdogs.morph as mp
history=model.fit_generator(
train_flow,
steps_per_epoch=30,
epochs=1000,
validation_data=val_flow,
validation_steps=12,
callbacks=[TensorBoard(log_dir='D:/work/python/vgg-keras/log')])
model.save('D:/work/python/vgg-keras/model/vgg16_use.h5')
test_loss, test_acc = model.evaluate_generator(test_flow, 96)
print('test acc:', test_acc)
在train
和test
文件里,每个文件夹直接对应的是一个分类,这里我是一个昆虫分类的数据集,每一个子文件夹对应一个分类,可以根据自己需要进行更改