Chainer教程 1.4 简单的CNN模型

Posted on Fri 20 July 2018 in MachineLearning • 1 min read

Chainer实现CNN模型

CNN全称卷积神经网络(Convolution neurual networks),其只要由两部分卷积(Conv)与池化(Pooling)组成。 卷积与池化均来源于泛函中的一种数学算子即通过两个函数f和g生成第三个函数的一种数学算子。卷积与池化最大的区别在于卷积需要训练并且卷积的步长为1,池化则不固定。

这里对步长进行一个小的说明,步长是指滤波器(卷积核)在图像上滑动时一次滑动多少单位的剂量,如下图是一个步长为2的卷积。

卷积

而池化一般设置为与池化核心大小相同的值,保证池化区域互不相交。

这里再说一个卷积用数学公式的表示方法$f=\omega*x + \beta$, 可以看出整体形式与全连接几乎相同,仅添加了一卷积符号。

Chainer 自构建CNN模型

下面利用一个小的自构建模型对Cifar10数据集进行分类

In [31]:
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
class simple_bn(chainer.Chain):
    def __init__(self,num=10):
        super(simple_bn,self).__init__()
        with self.init_scope():
            self.conv1=L.Convolution2D(in_channels=None,out_channels=6,ksize=3,pad=1)
            self.conv2=L.Convolution2D(in_channels=None,out_channels=24,ksize=3,pad=1)
            self.conv3=L.Convolution2D(in_channels=None,out_channels=48,ksize=3,pad=1)
            self.conv4=L.Convolution2D(in_channels=None,out_channels=96,ksize=3,pad=1)
            self.conv5=L.Convolution2D(in_channels=None,out_channels=256,ksize=3,pad=1)
            self.bn1=L.BatchNormalization(6)
            self.bn2=L.BatchNormalization(24)
            self.bn3=L.BatchNormalization(48)
            self.bn4=L.BatchNormalization(96)
            self.bn5=L.BatchNormalization(256)
            self.fc=L.Linear(None,num)
    def forward(self,x):
        h=self.conv1(x)
        h=F.relu(h)
        h=self.bn1(h)
        h=F.max_pooling_2d(h,2,2)
        h=self.conv2(x)
        h=F.relu(h)
        h=self.bn2(h)
        h=F.max_pooling_2d(h,2,2)
        h=self.conv3(x)
        h=F.relu(h)
        h=self.bn3(h)
        h=F.max_pooling_2d(h,2,2)
        h=self.conv4(x)
        h=F.relu(h)
        h=self.bn4(h)
        h=F.max_pooling_2d(h,2,2)
        h=self.conv5(h)
        h=F.relu(h)
        h=self.bn5(h)
        y=self.fc(h)
        return y
# Define a network class
In [14]:
train,test=chainer.datasets.get_cifar10(ndim=3) # set shape as [n,c,x,y]
print('shape:{}'.format(train[1][0].shape))
train_iter=chainer.iterators.SerialIterator(train,128)
test_iter=chainer.iterators.SerialIterator(test,128)
plt.imshow(train[1][0].transpose(1,2,0))
#get iterator of dataset
shape:(3, 32, 32)
Out[14]:
<matplotlib.image.AxesImage at 0x7f22339d61d0>
In [22]:
net=simple_bn() #instantiate a model
net.to_gpu(0) #Use gpu and  0 is gpu id
optimizer = chainer.optimizers.Adam() #use default hyperparameters
optimizer.setup(net) #bind optimizer and model
max_epoch=20 
#set hyperparameter here

Chainer框架提供两种训练方式。一种为显式的调用循环,另一种即为将循环封装起来。这里主要利用显示的方法进行训练,因为更加利于在训练网络的过程中添加一些值或其他的东西。如果希望了解封装式的训练方式请进入trainer

下面是模型的训练过程,需要注意调用concat_examples方法是如果device设置不为None,这数据将会储存与Gpu中。同时该方法会自动把一个batch的数据pad成为统一大小,也就是说如果你原始的image_train中有2组数据大小分别为[3,24,32]以及[3,12,48] 那么所有的数据都会pad成为[3,24,48]的大小

In [ ]:
while train_iter.epoch<max_epoch:
        train_batch = train_iter.next() 
        image_train, target_train = concat_examples(train_batch,device=0,padding=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:05 train_loss:0.0952 val_loss:1.6678 val_accuracy:0.6791
epoch:06 train_loss:0.1923 val_loss:1.8688 val_accuracy:0.6727
epoch:07 train_loss:0.3112 val_loss:2.0504 val_accuracy:0.6691
epoch:08 train_loss:0.2755 val_loss:2.2125 val_accuracy:0.6643

如果需要对模型保存以及加载,则需要使用以下代码

from chainer import serializers
serializers.save_npz('where to save',net)
serializers.load_npz('where to load',net)