書いてる理由
- pytorchを基礎からもう一回
参考
https://arxiv.org/pdf/1612.01105.pdf
詳細
pytorch_work/network.py at master · ys201810/pytorch_work · GitHub
PSPNetでセマンティックセグメンテーションする。
今回からネットワークの部分に入る。
PSPNetは、上の画像の通り、inputの画像(a)をFeature Map(b)で特徴抽出して、Pyramid Pooling Moudle(c)で4つの異なるサイズのFeature Mapを元のFeature MAPの大きさにUpsamplingした上でconcatしてから予測する。
これをパーツごとに作成していく。
まずはFeature Map(b)を作成するところ。
全部説明はきついので、部分部分。
# Feature Map作成部分 class PSPNet(nn.Module): def __init__(self, n_classes): super(PSPNet, self).__init__() block_config = [3, 4, 6, 3] # resnet50用 img_size = 475 img_size_8 = 60 self.feature_conv = FeatureMapConvolution() self.feature_res1 = ResidualBlockPSP(n_blocks=block_config[0], in_channels=128, mid_channels=64, out_channels=256, stride=1, dilation=1) self.feature_res2 = ResidualBlockPSP(n_blocks=block_config[1], in_channels=256, mid_channels=128, out_channels=512, stride=2, dilation=1) self.feature_dilated_res1 = ResidualBlockPSP(n_blocks=block_config[2], in_channels=512, mid_channels=256, out_channels=1024, stride=1, dilation=2) self.feature_dilated_res2 = ResidualBlockPSP(n_blocks=block_config[3], in_channels=1024, mid_channels=512, out_channels=2048, stride=1, dilation=4)
Feature Map作成部分は、(conv -> batchnorm -> relu) * 3 + maxpoolingの箇所と、ResNetの箇所からなる。
FeatureMapConvolutionが(conv -> batchnorm -> relu) * 3 + maxpoolingの箇所
ResidualBlockPSPがResNetの箇所
FeatureMapConvolutionはそのままで、以下の定義
Conv2DBatchNormReluはConv2d -> BatchNorm -> Reluをひとまとめにしたもの。
これを単純に3回繰り返して最後にmaxpoolして終了。
入力は画像で、475[width] * 475[height] * 3[channel(RGB)]
出力は、119[width] * 119[height] * 128[channel]
class FeatureMapConvolution(nn.Module): def __init__(self): super(FeatureMapConvolution, self).__init__() # conv1 conv1_conf = [3, 64, 3, 2, 1, 1, False] in_channels, out_channels, kernel_size, stride, padding, dilation, bias = conv1_conf self.cbnr1 = Conv2DBatchNormRelu(in_channels, out_channels, kernel_size, stride, padding, dilation, bias) # conv2 conv2_conf = [64, 64, 3, 1, 1, 1, False] in_channels, out_channels, kernel_size, stride, padding, dilation, bias = conv2_conf self.cbnr2 = Conv2DBatchNormRelu(in_channels, out_channels, kernel_size, stride, padding, dilation, bias) # conv3 conv3_conf = [64, 128, 3, 1, 1, 1, False] in_channels, out_channels, kernel_size, stride, padding, dilation, bias = conv3_conf self.cbnr3 = Conv2DBatchNormRelu(in_channels, out_channels, kernel_size, stride, padding, dilation, bias) self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) def forward(self, x): x = self.cbnr1(x) x = self.cbnr2(x) x = self.cbnr3(x) outputs = self.maxpool(x) return outputs
ResNetのところは、BottleNeckPSPとBottleNeckIdentifyPSPを通って作る。
Identifyは通常のResidual Blockの通り出力yが、y = x + F(x)で作られて
Identifyじゃない方は出力yが、y = Conv(x) + F(x)のように、xにConvとBatchNormを通してサイズを一緒にして差を取って次の層に伝播させる感じで作られている。
class BottleNeckIdentifyPSP(nn.Module): """ 入力と同じサイズの出力。 出力はresidualで特徴抽出したもの + 入力 """ def __init__(self, in_channels, mid_channels, stride, dilation): super(BottleNeckIdentifyPSP).__init__() self.cbr1 = Conv2DBatchNormRelu(in_channels, mid_channels, kernel_size=1, stride=1, padding=0, dilation=1, bias=False) self.cbr2 = Conv2DBatchNormRelu(mid_channels, mid_channels, kernel_size=3, stride=1, padding=dilation, dilation=dilation, bias=False) self.cb3 = Conv2DBatchNorm(mid_channels, in_channels, kernel_size=1, stride=1, padding=0, dilation=1, bias=False) self.relu = nn.ReLU(inplace=True) def forward(self, x): conv = self.cb3(self.cbr2(self.cbr1(x))) residual = x return self.relu(conv + residual) # y = x + F(x) class BottleNeckPSP(nn.Module): """ residualだが、convを挟んでresidulaをするかしないかがIdentifyとの違い """ def __init__(self, in_channels, mid_channels, out_channels, stride, dilation): super(BottleNeckPSP).__init__() self.cbr1 = Conv2DBatchNormRelu(in_channels, mid_channels, kernel_size=1, stride=1, padding=0, dilation=1, bias=False) self.cbr2 = Conv2DBatchNormRelu(mid_channels, mid_channels, kernel_size=3, stride=stride, padding=dilation, dilation=dilation, bias=False) self.cb3 = Conv2DBatchNorm(mid_channels, out_channels, kernel_size=1, stride=1, padding=0, dilation=1, bias=False) self.cb_residual = Conv2DBatchNorm(in_channels, out_channels, kernel_size=1, stride=stride, padding=0, dilation=1, bias=False) self.relu = nn.ReLU(inplace=True) def forward(self, x): conv = self.cb3(self.cbr2(self.cbr1(x))) residual = self.cb_residual(x) return self.relu(conv + residual) # y = Conv(x) + F(x) ConvとBatchNormを通った奴を足してる。
今日はここまで。