10.5 VGG16模型复用

10.5.1 微调(Finetuining)

(1) trainable参数变动

在进行Finetuning对模型重新训练时,对于部分不需要训练的层可以通过设置trainable=False来确保其在训练过程中不会被修改权值。

(2) 全连接层的神经元个数

预训练的VGG是在ImageNet数据集上进行训练的,对1000个类别进行判定,由于需要进行的是猫狗分类,所以应该将最后的分类类别改为两类即可。

10.5.2 代码变动

在类的初始化时,将需要共享的参数传递进来。

卷积操作设置参数为不可训练,后面会将预训练模型的参数进行传入。

全连接操作可以进行训练,对我们新加入的数据集进行拟合。

对卷积层进行改动,其中的参数均不可训练,使用预训练模型的参数。

设定最后三层全连接均可训练,将最后的输出改为2,因为我们的问题是个二分类问题。

10.5.3 载入权重

权重文件:https://www.cs.toronto.edu/~frossard/vgg16/vgg16_weights.npz

分类文件: https://www.cs.toronto.edu/~frossard/vgg16/imagenet_classes.py

> 示例代码

# -*- coding: utf-8 -*-
import numpy as np
import tensorflow as tf


#tf.reset_default_graph()
# 修改VGG模型: 全连接层的神经元个数;trainable参数变动
# (1)预训练的VGG是在ImageNet数据集上进行训练的,对1000个类别进行判定
#     若希望利用已训练模型用于其他分类任务,需要修改最后的全连接层     
# (2)在进行Finetuning对模型重新训练时,对于部分不需要训练的层可以通过设置trainable=False来确保其在训练过程中不会被修改权值
class vgg16:
    def __init__(self, imgs):
        self.parameters = [] # 在类的初始化时加入全局列表,将所需共享的参数加载进来
        self.imgs = imgs
        self.convlayers()
        self.fc_layers()
        self.probs = tf.nn.softmax(self.fc8)# 输出每个属于各个类别的概率值

    def saver(self):
        return tf.train.Saver()

    def maxpool(self,name,input_data):
        out = tf.nn.max_pool(input_data,[1,2,2,1],[1,2,2,1],padding="SAME",name=name)
        return out

    def conv(self,name, input_data, out_channel,trainable=False): # trainable参数变动
        in_channel = input_data.get_shape()[-1]
        
        with tf.variable_scope(name):  
            kernel = tf.get_variable("weights", [3, 3, in_channel, out_channel], dtype=tf.float32, trainable=False) # trainable参数变动
            biases = tf.get_variable("biases", [out_channel], dtype=tf.float32, trainable=False) # trainable参数变动
            conv_res = tf.nn.conv2d(input_data, kernel, [1, 1, 1, 1], padding="SAME")
            res = tf.nn.bias_add(conv_res, biases)
            out = tf.nn.relu(res, name=name)
        self.parameters += [kernel, biases] # 将卷积层定义的参数(kernel, biases)加入列表
        return out

    def fc(self,name,input_data,out_channel,trainable=True): # trainable参数变动
        shape = input_data.get_shape().as_list()
        if len(shape) == 4:
            size = shape[-1] * shape[-2] * shape[-3]
        else:size = shape[1]
        input_data_flat = tf.reshape(input_data,[-1,size])
        with tf.variable_scope(name):
            weights = tf.get_variable(name="weights",shape=[size,out_channel],dtype=tf.float32, trainable=trainable) # trainable参数变动
            biases = tf.get_variable(name="biases",shape=[out_channel],dtype=tf.float32, trainable=trainable) # trainable参数变动
            res = tf.matmul(input_data_flat,weights)
            out = tf.nn.relu(tf.nn.bias_add(res,biases))
        self.parameters += [weights, biases] # 将全连接层定义的参数(weights, biases)加入列表
        return out

    def convlayers(self):
        #conv1
        self.conv1_1 = self.conv("conv1re_1",self.imgs,64,trainable=False)# trainable参数变动
        self.conv1_2 = self.conv("conv1_2",self.conv1_1,64,trainable=False)# trainable参数变动
        self.pool1 = self.maxpool("poolre1",self.conv1_2)

        #conv2
        self.conv2_1 = self.conv("conv2_1",self.pool1,128,trainable=False)# trainable参数变动
        self.conv2_2 = self.conv("convwe2_2",self.conv2_1,128,trainable=False)# trainable参数变动
        self.pool2 = self.maxpool("pool2",self.conv2_2)

        #conv3
        self.conv3_1 = self.conv("conv3_1",self.pool2,256,trainable=False)# trainable参数变动
        self.conv3_2 = self.conv("convrwe3_2",self.conv3_1,256,trainable=False)# trainable参数变动
        self.conv3_3 = self.conv("convrew3_3",self.conv3_2,256,trainable=False)# trainable参数变动
        self.pool3 = self.maxpool("poolre3",self.conv3_3)

        #conv4
        self.conv4_1 = self.conv("conv4_1",self.pool3,512,trainable=False)# trainable参数变动
        self.conv4_2 = self.conv("convrwe4_2",self.conv4_1,512,trainable=False)# trainable参数变动
        self.conv4_3 = self.conv("conv4rwe_3",self.conv4_2,512,trainable=False)# trainable参数变动
        self.pool4 = self.maxpool("pool4",self.conv4_3)

        #conv5
        self.conv5_1 = self.conv("conv5_1",self.pool4,512,trainable=False)# trainable参数变动
        self.conv5_2 = self.conv("convrwe5_2",self.conv5_1,512,trainable=False)# trainable参数变动
        self.conv5_3 = self.conv("conv5_3",self.conv5_2,512,trainable=False)# trainable参数变动
        self.pool5 = self.maxpool("poorwel5",self.conv5_3)

    def fc_layers(self):

        self.fc6 = self.fc("fc1", self.pool5, 4096, trainable=False)# trainable参数变动
        self.fc7 = self.fc("fc2", self.fc6, 4096, trainable=False) # trainable参数变动
        self.fc8 = self.fc("fc3", self.fc7, 2,trainable=True) #fc8正是我们需要训练的,因此trainable=True;2是n_class

    def load_weights(self, weight_file, sess): # 这个函数将获取的权重载入VGG模型中
        weights = np.load(weight_file)
        keys = sorted(weights.keys())
        for i, k in enumerate(keys):
            if i not in [30, 31]:# 剔除不需载入的层
                sess.run(self.parameters[i].assign(weights[k]))
        print("-----------weights loaded---------------")

Last updated