2017年11月21日火曜日

論文読み 2014, Going Deeper with Convolutions

Introduction

Szegedy et al. (2014)1ではより深いDNNを実現するためにInception moduleを導入し,22層からなるGoogLenetを開発し,ほぼ同じ計算量で性能の向上を実現している.VGGNet2は精度の点ではGoogLeNetと同程度だが,従来のCNNを深くしただけで,非常に計算量が大きいという欠点が有る.
ただ単にDNNを大きくすることは計算量を増大させると同時に必要なtraining dataを莫大にしてしまう. これを克服するためにsparsityを用いるのだが,sparse行列の演算は現在の計算機では扱いづらい. 従来は”spatial domain”においてconvolutionを導入してsparsityを実現してきたが,著者は”filter-level”でsparsityを実現し,しかも複数のsparse行列の演算を1つのdense行列の演算として近似して効率よく計算する方法を開発した.
Inception moduleとは通常のconvolutionの代わりに挿入される,複数のconvolutionをひとまとめにしたレイヤーと言える.
CNNでは局所的な統計量のクラスターが前のレイヤーから後ろのレイヤーに渡されていくわけだが,その受け渡されたレイヤーでももとの局所性は保存されているから,ついにはその局所的な統計量たちの殆どがfeature mapのうちの1x1の部分のそれぞれに格納されることになる. Inception moduleでは1x1のフィルタに加え,1x1の部分に収まりきらない局所統計量をとらえるため,3x3と5x5のフィルタも加え,ついでにpoolingも加えている(fig.1.a), .
大量のfeature mapが有る上に5X5のconvolutionを行うのは非常に計算が重いので,1x1のconvolutionを適当な枚数のfeature mapを作るように掛けて次元圧縮してからそれに3x3や5x5のconvolutionを施す(fig.1.b), (table1, n5x5redやn3x3redが次元圧縮した後の次元数).
計算上の理由から,Inception moduleはDNNのhigher layerにおいてのみ挿入される. GoogLeNetは合計で22のlayerからなるが,当時としてはあまりに深いネットワークなので,中頃の層にもclassifierを追加して(fig.2)勾配消失を軽減するとともに,regularizationを実現していると著者は考えている.

enter image description here
figure1

Table1, PyTorch実装,kuangliu, MIT license

import torch
import torch.nn as nn
import torch.nn.functional as F

from torch.autograd import Variable


class Inception(nn.Module):
    def __init__(self, in_planes, n1x1, n3x3red, n3x3, n5x5red, n5x5, pool_planes):
        super(Inception, self).__init__()
        # 1x1 conv branch
        self.b1 = nn.Sequential(
            nn.Conv2d(in_planes, n1x1, kernel_size=1),
            nn.BatchNorm2d(n1x1),
            nn.ReLU(True),
        )

        # 1x1 conv -> 3x3 conv branch
        self.b2 = nn.Sequential(
            nn.Conv2d(in_planes, n3x3red, kernel_size=1),
            nn.BatchNorm2d(n3x3red),
            nn.ReLU(True),
            nn.Conv2d(n3x3red, n3x3, kernel_size=3, padding=1),
            nn.BatchNorm2d(n3x3),
            nn.ReLU(True),
        )

        # 1x1 conv -> 5x5 conv branch
        self.b3 = nn.Sequential(
            nn.Conv2d(in_planes, n5x5red, kernel_size=1),
            nn.BatchNorm2d(n5x5red),
            nn.ReLU(True),
            nn.Conv2d(n5x5red, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5),
            nn.ReLU(True),
            nn.Conv2d(n5x5, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5),
            nn.ReLU(True),
        )

        # 3x3 pool -> 1x1 conv branch
        self.b4 = nn.Sequential(
            nn.MaxPool2d(3, stride=1, padding=1),
            nn.Conv2d(in_planes, pool_planes, kernel_size=1),
            nn.BatchNorm2d(pool_planes),
            nn.ReLU(True),
        )

    def forward(self, x):
        y1 = self.b1(x)
        y2 = self.b2(x)
        y3 = self.b3(x)
        y4 = self.b4(x)
        return torch.cat([y1,y2,y3,y4], 1)


class GoogLeNet(nn.Module):
    def __init__(self):
        super(GoogLeNet, self).__init__()
        self.pre_layers = nn.Sequential(
            nn.Conv2d(3, 192, kernel_size=3, padding=1),
            nn.BatchNorm2d(192),
            nn.ReLU(True),
        )

        self.a3 = Inception(192,  64,  96, 128, 16, 32, 32)
        self.b3 = Inception(256, 128, 128, 192, 32, 96, 64)

        self.maxpool = nn.MaxPool2d(3, stride=2, padding=1)

        self.a4 = Inception(480, 192,  96, 208, 16,  48,  64)
        self.b4 = Inception(512, 160, 112, 224, 24,  64,  64)
        self.c4 = Inception(512, 128, 128, 256, 24,  64,  64)
        self.d4 = Inception(512, 112, 144, 288, 32,  64,  64)
        self.e4 = Inception(528, 256, 160, 320, 32, 128, 128)

        self.a5 = Inception(832, 256, 160, 320, 32, 128, 128)
        self.b5 = Inception(832, 384, 192, 384, 48, 128, 128)

        self.avgpool = nn.AvgPool2d(8, stride=1)
        self.linear = nn.Linear(1024, 10)

    def forward(self, x):
        out = self.pre_layers(x)
        out = self.a3(out)
        out = self.b3(out)
        out = self.maxpool(out)
        out = self.a4(out)
        out = self.b4(out)
        out = self.c4(out)
        out = self.d4(out)
        out = self.e4(out)
        out = self.maxpool(out)
        out = self.a5(out)
        out = self.b5(out)
        out = self.avgpool(out)
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

# net = GoogLeNet()
# x = torch.randn(1,3,32,32)
# y = net(Variable(x))
# print(y.size())

enter image description here
figure 2

figureは原論文より


  1. Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich, Going Deeper with Convolutions, In CVPR, 2014
  2. K. Simonyan and A. Zisserman. Very deep convolutionalnetworks for large-scale image recognition.arXiv preprint arXiv:1409.1556, 2014.

0 件のコメント:

コメントを投稿