Keras-VGG16-图片分类
VGG模型的名称,来源于牛津大学的Oxford Visual Geometry Group,该网络模型是他们在ILSVRC 2014上的相关工作,主要是证明了增加网络的深度能够在一定程度上影响网络最终的性能。
1、原始数据集
原始图片文件夹train:包含1200张猫的图片,1200张狗的图片,尺寸不尽相同。
将2400张rgb图片做灰度处理,得到2400张灰度图,再将它们resize到(224,224)。打乱数据集中图片顺序,取前2000张作为训练集,后400张作为测试集。
train_x : 2000, 224, 224
train_y : 2000, 2
test_x : 400, 224, 224
test_y : 400, 2
2、VGG16网络细节
VGG16网络结构如下:
VGG的结构非常有规律,整个网络都使用了相同大小的(3,3)卷积核尺寸和(2,2)最大池化尺寸。VGG主要包括五轮卷积,三轮全连接。
初始层:
第1层:输入层,Input(224,224,1)。
第一轮卷积:
第2层:64个卷积核, kernel_size=3, strides=1, padding=‘same’。
第3层:Relu激励。
第4层:64个卷积核, kernel_size=3, strides=1, padding=‘same’。
第5层:最大池化,pool_size=2, strides=2, padding=‘same’。
第6层:Relu激励。
第7层:标准化归一层,BatchNormalization。
第二轮卷积:
第8层:128个卷积核, kernel_size=3, strides=1, padding=‘same’。
第9层:Relu激励。
第10层:128个卷积核, kernel_size=3, strides=1, padding=‘same’。
第11层:最大池化,pool_size=2, strides=2, padding=‘same’。
第12层:Relu激励。
第13层:标准化归一层,BatchNormalization。
第三轮卷积:
第14层:256个卷积核, kernel_size=3, strides=1, padding=‘same’。
第15层:Relu激励。
第16层:256个卷积核, kernel_size=3, strides=1, padding=‘same’。
第17层:Relu激励。
第18层:256个卷积核, kernel_size=3, strides=1, padding=‘same’。
第19层:最大池化,pool_size=2, strides=2, padding=‘same’。
第20层:Relu激励。
第21层:标准化归一层,BatchNormalization。
第四轮卷积:
第22层:512个卷积核, kernel_size=3, strides=1, padding=‘same’。
第23层:Relu激励。
第24层:512个卷积核, kernel_size=3, strides=1, padding=‘same’。
第25层:Relu激励。
第26层:512个卷积核, kernel_size=3, strides=1, padding=‘same’。
第27层:最大池化,pool_size=2, strides=2, padding=‘same’。
第28层:Relu激励。
第29层:标准化归一层,BatchNormalization。
第五轮卷积:
第30层:512个卷积核, kernel_size=3, strides=1, padding=‘same’。
第31层:Relu激励。
第32层:512个卷积核, kernel_size=3, strides=1, padding=‘same’。
第33层:Relu激励。
第34层:512个卷积核, kernel_size=3, strides=1, padding=‘same’。
第35层:最大池化,pool_size=2, strides=2, padding=‘same’。
第36层:Relu激励。
第37层:标准化归一层,BatchNormalization。
第一轮全连接:
第38层:feature map拉直成向量,Flatten()。
第39层:全连接,Dense(4096)。
第40层:Relu激励。
第41层:Dropout(0.5)。
第二轮全连接:
第42层:全连接,Dense(4096)。
第43层:Relu激励。
第44层:Dropout(0.5)。
第三轮全连接:
第45层:全连接,Dense(2)。
第46层:outputs = softmax激励层。
3、模型求解结果
因为VGG每轮epoch训练时间过长,自己又担心epoch设置太大容易过拟合,于是分了六次训练,每次训练小一段就保存下来,后面再接着训练。
第1次训练了5个epoch,第2次训练了10个epoch,第3次训练了10个epoch,第4次训练了10个epoch,第5次训练了10个epoch,第6次训练了10个epoch,总共花费了约12h。
可能因为训练的数据集太少,电脑内存不够只能加载了2k张图片进行训练,VGG模型没训练出理想效果,分类精度反而略逊色于浅层网络。
4、对模型的一些深入思考
思考1:VGG模型相比于Alexnet模型,亮点是什么?
VGG就是加深版的AlexNet,都是卷积与池化的叠加,再加两层全连接,softmax输出。VGG16有5轮卷积,每轮卷积由2~3个卷积层后加1个最大池化组成,卷积核的数量也随着层数的增加而增多。
相较与AlexNet,VGG最大的改进就是用小size的Filter代替大size的Filter。在VGG中,下面这种结构很常见,用两个(3,3)的卷积核代替一个(5,5)的卷积核。
这样做的好处有:
(1)需要训练的参数减少。如果卷积层C2的卷积核数量是N,上一层C1卷积核数量是M,则用一个(5,5)的卷积核实现该层卷积的参数数量是:25NM;如果用两个(3,3)的卷积核代替一个(5,5)的卷积核,此时的参数数量是:18NM。
(2)增加非线性变换的次数,增加了对特征的学习能力。一个(5,5)卷积核,经过一次卷积,只进行一次非线性变换,两个(3,3)卷积核,则经过两次非线性变换。
思考2:什么情况下使用深层VGG,什么情况下使用浅层Alexnet?。
(1)对计算资源的考虑。AlexNet模型参数要比VGG、Resnet模型参数少,训练和推理速度快,可以部署到算力弱的设备上运行。
(2)所研究问题的数据集太小。使用VGG或者ResNet参数太多,如果训练集数据过少,可能导致得到的模型过拟合,反而不如参数略少的AlexNet模型。
思考3:深层网络VGG训练时碰到的问题,借此积累经验。
(1)main函数一开始运行时loss就很大,而且训练几次后无降低的趋势,一直是几十的loss,模型参数并未得到优化。
一般情况下是因为学习率过高,需要降低学习率,强烈建议换一个Optimizer。自己之前训练时都是用“Adam”优化器,换成了“SGD”、“Adadelta”优化器后没有再出现上面情况,loss开始缓慢降低,模型开始不断优化。
(2)深层网络VGG训练真的非常慢。自己之前训练LeNet网络每个epoch大概1min,训练Alexet网络每个epoch大概3min,而VGG训练每个epoch要13min。
(3)从运行结果上来看,VGG最后达到的分类精度也就95%左右,远低于浅层网络分类精度。自己认为,以后如果要搭建用大型网络模型,一定要喂入大量大量的数据,才能训练出一个效果好的网络。单单只用几千张图片进行训练,深层网络很容易就过拟合了,参数还没来得及得到充分优化,数据就已经不够用了,效果反而不好。
5、具体代码
数据集加工:
import cv2
import numpy as np
import random
def read_data():
data_x = np.zeros((2400, 224, 224))
data_y = np.zeros((2400, 2))
for i in range(1200):
cat_image = cv2.imread("/home/archer/CODE/PF/data/train/cat." + str(i + 1) + ".jpg")
cat_gray_image = cv2.cvtColor(cat_image, cv2.COLOR_BGR2GRAY)
cat_resize_image = cv2.resize(cat_gray_image, (224, 224), interpolation=cv2.INTER_AREA)
data_x[i, :, :] = cat_resize_image / 255
data_y[i, :] = np.array([1, 0])
print('the cat images have been download !')
for i in range(1200, 2400):
dog_image = cv2.imread("/home/archer/CODE/PF/data/train/dog." + str(i - 1199) + ".jpg")
dog_gray_image = cv2.cvtColor(dog_image, cv2.COLOR_BGR2GRAY)
dog_resize_image = cv2.resize(dog_gray_image, (224, 224), interpolation=cv2.INTER_AREA)
data_x[i, :, :] = dog_resize_image / 255
data_y[i, :] = np.array([0, 1])
print('the dog images have been download !')
return data_x, data_y
def make_network_data():
data_x, data_y = read_data()
random_index = np.arange(0, 2400, 1)
random.shuffle(random_index)
train_x = np.zeros((2000, 224, 224))
train_y = np.zeros((2000, 2))
test_x = np.zeros((400, 224, 224))
test_y = np.zeros((400, 2))
for i in range(2000):
index = random_index[i]
train_x[i, :, :] = data_x[index, :, :]
train_y[i, :] = data_y[index, :]
for i in range(400):
index = random_index[2000 + i]
test_x[i, :, :] = data_x[index, :, :]
test_y[i, :] = data_y[index, :]
return train_x, train_y, test_x, test_y
VGG16网络搭建:
import numpy as np
import keras
from keras.models import load_model
# VGG network
def create_network():
inputs = keras.layers.Input((224, 224, 1))
# First convolution
conv1 = keras.layers.Conv2D(64, kernel_size=3, strides=1, padding='same')(inputs)
lk1 = keras.layers.LeakyReLU()(conv1)
conv2 = keras.layers.Conv2D(64, kernel_size=3, strides=1, padding='same')(lk1)
pool1 = keras.layers.MaxPooling2D(pool_size=2, strides=2, padding='same')(conv2)
lk2 = keras.layers.LeakyReLU()(pool1)
bn1 = keras.layers.BatchNormalization()(lk2)
# Second convolution
conv3 = keras.layers.Conv2D(128, kernel_size=3, strides=1, padding='same')(bn1)
lk3 = keras.layers.LeakyReLU()(conv3)
conv4 = keras.layers.Conv2D(128, kernel_size=3, strides=1, padding='same')(lk3)
pool2 = keras.layers.MaxPooling2D(pool_size=2, strides=2, padding='same')(conv4)
lk4 = keras.layers.LeakyReLU()(pool2)
bn2 = keras.layers.BatchNormalization()(lk4)
# Third convolution
conv5 = keras.layers.Conv2D(256, kernel_size=3, strides=1, padding='same')(bn2)
lk5 = keras.layers.LeakyReLU()(conv5)
conv6 = keras.layers.Conv2D(256, kernel_size=3, strides=1, padding='same')(lk5)
lk6 = keras.layers.LeakyReLU()(conv6)
conv7 = keras.layers.Conv2D(256, kernel_size=3, strides=1, padding='same')(lk6)
pool3 = keras.layers.MaxPooling2D(pool_size=2, strides=2, padding='same')(conv7)
lk7 = keras.layers.LeakyReLU()(pool3)
bn3 = keras.layers.BatchNormalization()(lk7)
# Fourth convolution
conv8 = keras.layers.Conv2D(512, kernel_size=3, strides=1, padding='same')(bn3)
lk8 = keras.layers.LeakyReLU()(conv8)
conv9 = keras.layers.Conv2D(512, kernel_size=3, strides=1, padding='same')(lk8)
lk9 = keras.layers.LeakyReLU()(conv9)
conv10 = keras.layers.Conv2D(512, kernel_size=3, strides=1, padding='same')(lk9)
pool4 = keras.layers.MaxPooling2D(pool_size=2, strides=2, padding='same')(conv10)
lk10 = keras.layers.LeakyReLU()(pool4)
bn4 = keras.layers.BatchNormalization()(lk10)
# Fifth convolution
conv11 = keras.layers.Conv2D(512, kernel_size=3, strides=1, padding='same')(bn4)
lk11 = keras.layers.LeakyReLU()(conv11)
conv12 = keras.layers.Conv2D(512, kernel_size=3, strides=1, padding='same')(lk11)
lk12 = keras.layers.LeakyReLU()(conv12)
conv13 = keras.layers.Conv2D(512, kernel_size=3, strides=1, padding='same')(lk12)
pool5 = keras.layers.MaxPooling2D(pool_size=2, strides=2, padding='same')(conv13)
lk13 = keras.layers.LeakyReLU()(pool5)
bn5 = keras.layers.BatchNormalization()(lk13)
fl = keras.layers.Flatten()(bn5)
# First fully connected
dense1 = keras.layers.Dense(4096)(fl) # 4096
lk14 = keras.layers.LeakyReLU()(dense1) # 4096
drop1 = keras.layers.Dropout(0.5)(lk14) # 4096
# Second fully connected
dense2 = keras.layers.Dense(4096)(drop1) # 4096
lk15 = keras.layers.LeakyReLU()(dense2) # 4096
drop2 = keras.layers.Dropout(0.5)(lk15) # 4096
# Third fully connected
dense3 = keras.layers.Dense(2)(drop2) # 2
outputs = keras.layers.Activation('softmax')(dense3)
model = keras.models.Model(inputs=inputs, outputs=outputs)
model.summary()
return model
# batch generator: reduce the consumption of computer memory
def generator(train_x, train_y, batch_size):
while 1:
row = np.random.randint(0, len(train_x), size=batch_size)
x = train_x[row]
y = train_y[row]
yield x, y
# create model and train and save
def train_network(train_x, train_y, test_x, test_y, epoch, batch_size):
train_x = train_x[:, :, :, np.newaxis]
test_x = test_x[:, :, :, np.newaxis]
model = create_network()
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
history = model.fit_generator(generator(train_x, train_y, batch_size),
epochs=epoch, steps_per_epoch=len(train_x) // batch_size)
model.save('first_model.h5')
calculate_test_accuracy(test_x, test_y, 'first_model.h5')
# Load the partially trained model and continue training and save
def load_network_then_train(train_x, train_y, test_x, test_y, epoch, batch_size, input_name, output_name):
train_x = train_x[:, :, :, np.newaxis]
test_x = test_x[:, :, :, np.newaxis]
model = load_model(input_name)
history = model.fit_generator(generator(train_x, train_y, batch_size),
epochs=epoch, steps_per_epoch=len(train_x) // batch_size)
model.save(output_name)
calculate_test_accuracy(test_x, test_y, ouut_name)
# calculate the accuracy in test set
def calculate_test_accuracy(test_x, test_y, output_name):
model = load_model(output_name)
test_result = model.predict(test_x)
accuracy_number = 0
for i in range(len(test_x)):
if np.argmax(test_result[i, :]) == 0 and test_y[i, 0] == 1:
accuracy_number = accuracy_number + 1
if np.argmax(test_result[i, :]) == 1 and test_y[i, 0] == 0:
accuracy_number = accuracy_number + 1
print('The accuracy in test set is :')
print(accuracy_number/len(test_x))
主函数调用
import getdata as gt
import VGG as VGG
if __name__ == "__main__":
train_x, train_y, test_x, test_y = gt.make_network_data()
VGG.train_network(train_x, train_y, test_x, test_y, epoch=5, batch_size=16)
VGG.load_network_then_train(train_x, train_y, test_x, test_y, epoch=10, batch_size=16,
input_name='first_model.h5', output_name='second_model.h5')
VGG.load_network_then_train(train_x, train_y, test_x, test_y, epoch=10, batch_size=16,
input_name='second_model.h5',output_name='third_model.h5')
VGG.load_network_then_train(train_x, train_y, test_x, test_y, epoch=10, batch_size=16,
input_name='third_model.h5', output_name='fourth_model.h5')
VGG.load_network_then_train(train_x, train_y, test_x, test_y, epoch=10, batch_size=16,
input_name='fourth_model.h5', output_name='fifth_model.h5')
VGG.load_network_then_train(train_x, train_y, test_x, test_y, epoch=10, batch_size=16,
input_name='fifth_model.h5', output_name='sixth_model.h5')
# first_model.h5 : epoch = 5 accuracy = 0.6275
# second_model.h5 : epoch = 15 accuracy = 0.925
# third_model : epoch = 25 accuracy = 0.965
# fourth_model.h5 : epoch = 35 accuracy = 0.96
# fifth_model.h5 : epoch = 45 accuracy = 0.95
# sixth_model.h5 : epoch = 55 accuracy = 0.95
6、项目链接
如果代码跑不通,或者想直接使用训练好的模型,可以去下载项目链接:
https://blog.youkuaiyun.com/Twilight737