1.模型介绍
VGG模型是2014年ILSVRC竞赛的第二名,第一名是GoogLeNet。但是VGG模型在多个迁移学习任务中的表现要优于googLeNet。而且,从图像中提取CNN特征,VGG模型是首选算法。它的缺点在于,参数量有140M之多,需要更大的存储空间。但是这个模型很有研究价值。模型的名称——“VGG”代表了牛津大学的Oxford Visual Geometry Group,该小组隶属于1985年成立的Robotics Research Group,该Group研究范围包括了机器学习到移动机器人。
下面是一段来自网络对同年GoogLeNet和VGG的描述:
“GoogLeNet和VGG的Classification模型从原理上并没有与传统的CNN模型有太大不同。大家所用的Pipeline也都是:训练时候:各种数据Augmentation(剪裁,不同大小,调亮度,饱和度,对比度,偏色),剪裁送入CNN模型,Softmax,Backprop。测试时候:尽量把测试数据又各种Augmenting(剪裁,不同大小),把测试数据各种Augmenting后在训练的不同模型上的结果再继续Averaging出最后的结果。”
需要注意的是,在VGGNet的6组实验中,后面的4个网络均使用了pre-trained model A的某些层来做参数初始化。虽然提出者没有提该方法带来的性能增益。先来看看VGG的特点:
小卷积核。作者将卷积核全部替换为3x3(极少用了1x1);
小池化核。相比AlexNet的3x3的池化核,VGG全部为2x2的池化核;
层数更深特征图更宽。基于前两点外,由于卷积核专注于扩大通道数、池化专注于缩小宽和高,使得模型架构上更深更宽的同时,计算量的增加放缓;
全连接转卷积。网络测试阶段将训练阶段的三个全连接替换为三个卷积,测试重用训练时的参数,使得测试得到的全卷积网络因为没有全连接的限制,因而可以接收任意宽或高为的输入。
2.实战代码
import tensorflow as tf
from tensorflow.keras import layers,optimizers,datasets,Sequential
import os
#前期准备
#这里使用的卷积神经网络叫做VGG13
#使用数据集是cifar100,也就是100*100的情景,也就是100类数据,b*32*32*3的shape
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
tf.random.set_seed(1234)#里面的数字只是一个符号而已,设置稍大一些即可
''' 当在代码中使用了随机数,但是希望代码在不同时间或者不同的机器上运行能够得到相同的随机数
以至于能够得到相同的结果,那么需要到设置随机函数
在每次执行代码时,使每次切分后的训练集、验证集输入结果相同,便于验证学习参数的有效性和查找问题
a = tf.random.normal([1])
b = tf.random.normal([1])
print(a,b)#这里的打印结果来讲我们每次执行代码的结果都是一样的,就是每次在点击运行的时候(即使是不同电脑)
运行的random结果也是一样的tf.Tensor([0.8369314],dtype=float32) tf.Tensor([1.1468066],dtype=float32)
但是如果我们再写一遍
a = tf.random.normal([1])
b = tf.random.normal([1])
print(a,b)
tf.Tensor([0.95883226],dtype=float32) tf.Tensor([-0.37767014],dtype=float32)
这里结果并不一样,因为这个seed只对不同电脑上的/不同次点击运行有效果'''
#预处理函数
#预处理函数--在这里我只是对他们的类型做处理
def preprocess(x,y):
x = tf.cast(x,dtype=tf.float32)/255.
y = tf.cast(y,dtype=tf.int32)
return x,y
#定义数据集
(x,y),(x_test,y_test)=datasets.cifar100.load_data()
y = tf.squeeze(y,axis=1)
y_test = tf.squeeze(y_test,axis=1)
db_train = tf.data.Dataset.from_tensor_slices((x,y))
db_train = db_train.map(preprocess).shuffle(50000).batch(128)#shuffle后面是buffer-存放缓冲区
db_test = tf.data.Dataset.from_tensor_slices((x_test,y_test))
db_test = db_test.map(preprocess).shuffle(10000).batch(128)
sample=next(iter(db_train))
sample_test=next(iter(db_test))
print(sample_test[0].shape,sample_test[1].shape)#(128, 32, 32, 3) (128,)
#定义卷积层list
#卷积神经网络-卷积层--VGG13(conv1和conv2这两个完全一样的卷积层算一层,maxpooling层算一层)
'''input(b*32*32*3)->conv1->conv2->maxpool->conv1->conv2->maxpool->conv1->conv2->maxpool
->conv1->conv2->maxpool->conv1->conv2->maxpool->[b,256]-outputs->b,128-outputs->b,100-outputs'''
conv_Layers = [#一个unit-module就是两个一模一样的卷积层+一个maxpooling层
#1-unit-module-这里没有要求加上bias
layers.Conv2D(64,kernel_size=[3,3],padding='same',activation=tf.nn.relu),#kernelsize随便选择就好了
layers.Conv2D(64,kernel_size=3,padding='same',activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2,2],strides=2,padding='VALID'),#[2,2]相当于2
# 2-unit-module-这里没有要求加上bias
layers.Conv2D(128, kernel_size=3, padding='same', activation=tf.nn.relu),
layers.Conv2D(128, kernel_size=3, padding='same', activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='VALID'),
# 3-unit-module-这里没有要求加上bias
layers.Conv2D(256, kernel_size=3, padding='same', activation=tf.nn.relu),
layers.Conv2D(256, kernel_size=3, padding='same', activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='VALID'),
# 4-unit-module-这里没有要求加上bias
layers.Conv2D(512, kernel_size=3, padding='same', activation=tf.nn.relu),
layers.Conv2D(512, kernel_size=3, padding='same', activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='VALID'),
# 5-unit-module-这里没有要求加上bias
# x = Conv2D(10, (3, 3), strides = (1, 1))(inputs)
layers.Conv2D(512, kernel_size=3, padding='same', activation=tf.nn.relu),
layers.Conv2D(512, kernel_size=3, padding='same', activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='VALID')]
'''当padding=‘SAME’,计算avg_pool时,每次的计算是除以图像被filter框出的非零元素的个数
而不是filter元素的个数,maxpooling做padding=same没有实际效果'''
#主函数入口--这里包括网络的创建和定义
def main():
#定义网络
#[b,32,32,3]->[b,1,1,512]
conv_net = Sequential(conv_Layers)
fc_net = Sequential([
layers.Dense(256,activation=tf.nn.relu),
layers.Dense(128,activation=tf.nn.relu),
layers.Dense(100,activation=None)
])
#构建网络
conv_net.build(input_shape=[None,32,32,3])#这里是与其他数据集不一样的地方,不能做相乘处理
fc_net.build(input_shape=[None,512])#通过其他的手段将(b,1,1,512)->(b,512)
#优化器和所有参数准备
variables = conv_net.trainable_variables + fc_net.trainable_variables
optimizer = optimizers.Adam(lr=1e-4)
#[1,2] + [3,4] = [1,2,3,4] -- list法则
#print([1,2]+[2,3]) -- [1, 2, 2, 3]
'''x = tf.random.normal([4,32,32,3])
out = conv_net(x)
print(out.shape)#(4, 1, 1, 512)'''
#训练与测试过程
for epoch in range(5):
#训练数据
for step,(x,y) in enumerate(db_train):
with tf.GradientTape() as tape:
#[b,32,32,3]->[b,1,1,512]
out = conv_net(x)
out = tf.reshape(out,[-1,512])
#[b,512]->[b,100]
logits = fc_net(out)
#[b]->[b,100]
y_onehot = tf.one_hot(y,depth=100)
#compute loss
loss = tf.losses.categorical_crossentropy(y_onehot,logits,from_logits=True)
loss = tf.reduce_mean(loss)
#梯度计算
grads = tape.gradient(loss,variables)
#梯度更新
optimizer.apply_gradients(zip(grads,variables))
#打印训练loss结果
if step % 100 == 0:
print(epoch,step,'loss:',loss.numpy())
#测试模型
total_num = 0
total_correct = 0
for _,(x,y) in enumerate(db_test):#这里都是一个batch一个batch的做测试,所以有step的概念存在
out = conv_net(x)
out = tf.reshape(out,[-1,512])
logits = fc_net(out)#[b,100]
prob = tf.nn.softmax(logits,axis=1)
pred = tf.argmax(prob,axis=1)
pred = tf.cast(pred,dtype=tf.int32)
correct = tf.cast(tf.equal(y,pred),dtype=tf.int32)
correct = tf.reduce_sum(correct)
total_num += x.shape[0]#每个batch的x.shape[0]不一样
total_correct += int(correct)
acc = total_correct / total_num
#打印每个epoch的test-set的acc结果
print(epoch,'acc:',acc)
#主函数播放--进入main()函数
if __name__ == '__main__':
main()
3.实验结果
在1080Ti上进行50轮训练和测试,最终acc为0.7369。
文章转载自快来自习室,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




