Chainer教程 1.5 利用预训练模型以及修改预训练模型

Posted on Tue 23 October 2018 in MachineLearning • 2 min read

利用预训练模型以及修改预训练模型

预训练模型一般是指针对ImageNet分类任务中,各个研究人员设计的不同的网络架构以及训练过的该网络架构权重。

一般常见的网络架构族为VGG,ResNet,DenseNet等。

一般的将上述网络族进行适当的修改,可以很快的应用于不同数据集的分类任务中。同时在其他任务中如目标识别,人脸检测中,上述网络架构也同时存在,0这些网络架构会作为骨架(backbone)存在。

Chainer中提供的预训练模型相对不多,仅VGG16,VGG19,GoogleNet,Resnet50,Resnet101,Resnet152等,但其同时提供用于检测的模型Yolo,SDD等。详情见:Pretrain_model

这里对VGG,ResNet,DenseNet进行一下简短的说明,即为何他们成为非常著名的基准框架。

VGG: 由牛津大学的Visual Geometry Group组提出,该架构的最大贡献在于利用2个3*3的卷积来代替5*5的卷积,3个3*3的卷积代替7*7的卷积,从而减少了参数量.深入了解

ResNet: 由Facebook AI Research的研究院的何凯明提出,该架构的最大贡献为利用残差机制,残差机制是指对于一层中提取的特征$H(x)$与原始输入$x$的差$F(x)=H(x)-x$,然后利用这一关系显式的指定网络的前向传播过程为原始特征$x+$残差$F\left(x\right)$,从而使得网络层至少有恒等映射的作用(Identity mapping)(恒等映射指网络不提取任何特征直接向前传递)。该机制利用完成快捷连接(shortcut Connection),下图为快捷链接的说明。图一其数学本质可以理解为利用差分去计算微分方程。深入了解

DenseNet:由Cornell University的Gao Huang提出。该架构的最大贡献在于将每一层(包括输入数据)的特征传递至后续所有层(create short paths from early layers to later layers)从而更好地获取图像特征。深入了解图二

使用预训练模型并修改与训练模型

前面提到预训练模型往往针对ImageNet分类任务,而我们的任务往往没有那么多的标签以及输入的数据大小也不尽相同,所以我们需要对预训练模型进行一定的修改。这里主要利用VGG对Cifar10任务进行分类

In [2]:
import chainer
import chainer.functions as F
import chainer.links as L
import chainer.datasets
import matplotlib.pyplot as plt
import chainer.optimizers
from chainer.dataset import concat_examples
from chainer.cuda import to_cpu
import numpy as np
from chainer.links import VGG16Layers

class VGG(chainer.Chain):
    def __init__(self,num=10,pretrained="auto"):
        super(VGG,self).__init__()
        with self.init_scope():
            self.feature=VGG16Layers(pretrained) 
            self.fc=L.Linear(None,num)
    def forward(self,x):
        h=self.feature(x,layers=['conv5_3'])['conv5_3']#remove all fc
        y=self.fc(h) 
        return y
net=VGG()
Downloading from https://www.robots.ox.ac.uk/%7Evgg/software/very_deep/caffe/VGG_ILSVRC_16_layers.caffemodel...
Now loading caffemodel (usually it may take few minutes)

下面是VGG中所有的层,但是在上面自定义的模型中,我们不需要使用fc6,7,8 所以我们利用在网络流动过程中我们将其流动到conv5_3便完成输出。

In [3]:
for layer in L.VGG16Layers().available_layers:
    print('layer name :{}'.format(layer))
layer name :conv1_1
layer name :conv1_2
layer name :pool1
layer name :conv2_1
layer name :conv2_2
layer name :pool2
layer name :conv3_1
layer name :conv3_2
layer name :conv3_3
layer name :pool3
layer name :conv4_1
layer name :conv4_2
layer name :conv4_3
layer name :pool4
layer name :conv5_1
layer name :conv5_2
layer name :conv5_3
layer name :pool5
layer name :fc6
layer name :fc7
layer name :fc8
layer name :prob

下面的过程基本与上一章相同,但不同的是由于网络层数的增长,我们需要调节我们的Batch size使之符合我们的设备。

In [7]:
train,test=chainer.datasets.get_cifar10(ndim=3) # set shape as [n,c,x,y]
train_iter=chainer.iterators.SerialIterator(train,32)
test_iter=chainer.iterators.SerialIterator(test,32)
In [13]:
net.to_gpu()
optimizer = chainer.optimizers.Adam() #use default hyperparameters
optimizer.setup(net) #bind optimizer and model
max_epoch=20 
#set hyperparameter here
In [15]:
while train_iter.epoch<max_epoch:
        train_batch = train_iter.next() 
        image_train, target_train = concat_examples(train_batch,0,0)
        # concat_examples can split data to x and y. 
        logit_train=net(image_train) #forward step
        loss = F.softmax_cross_entropy(logit_train, target_train)
        net.cleargrads()
        loss.backward() #calculate the gradient
        optimizer.update()
        if train_iter.is_new_epoch:
            print('epoch:{:02d} train_loss:{:.04f} '.format(
                train_iter.epoch, float(to_cpu(loss.data)),), end='')
            test_losses = []
            test_accuracies = []

            while True:
                test_batch = test_iter.next()
                image_test, target_test = concat_examples(test_batch,0,0)
                logit_test = net(image_test)
                loss_test = F.softmax_cross_entropy(logit_test, target_test)
                test_losses.append(to_cpu(loss_test.data))
                accuracy = F.accuracy(logit_test, target_test)
                accuracy.to_cpu()
                test_accuracies.append(accuracy.data)

                if test_iter.is_new_epoch: #reset test_iter
                    test_iter.epoch = 0
                    test_iter.current_position = 0
                    test_iter.is_new_epoch = False
                    test_iter._pushed_position = None
                    break
            print('val_loss:{:.04f} val_accuracy:{:.04f}'.format(
                np.mean(test_losses), np.mean(test_accuracies)))
epoch:13 train_loss:0.2143 val_loss:0.7409 val_accuracy:0.7804
epoch:14 train_loss:0.1999 val_loss:0.7666 val_accuracy:0.7811
epoch:15 train_loss:0.3452 val_loss:0.7551 val_accuracy:0.7910
epoch:16 train_loss:0.0896 val_loss:0.7685 val_accuracy:0.7964
epoch:17 train_loss:0.4523 val_loss:0.7990 val_accuracy:0.7748
epoch:18 train_loss:0.0481 val_loss:0.7746 val_accuracy:0.8009
epoch:19 train_loss:0.3272 val_loss:0.8336 val_accuracy:0.7766
epoch:20 train_loss:0.2465 val_loss:0.8375 val_accuracy:0.8002

没有经过预训练的VGG是十分难以训练的(但resnet等网络相对容易得多),所以这里利用在Imagenet上训练好的模型再次训练,可以看出与之前自己搭建的5层模型相比,准确率上升了百分之20左右