Skip to content

Instantly share code, notes, and snippets.

@YimianDai
Last active August 21, 2019 05:26
Show Gist options
  • Save YimianDai/fa5b39b0e8936c3226d6f41b1961063c to your computer and use it in GitHub Desktop.
Save YimianDai/fa5b39b0e8936c3226d6f41b1961063c to your computer and use it in GitHub Desktop.
SSD
extra_spec = {
    300: [((256, 1, 1, 0), (512, 3, 2, 1)),
          ((128, 1, 1, 0), (256, 3, 2, 1)),
          ((128, 1, 1, 0), (256, 3, 1, 0)),
          ((128, 1, 1, 0), (256, 3, 1, 0))],

    512: [((256, 1, 1, 0), (512, 3, 2, 1)),
          ((128, 1, 1, 0), (256, 3, 2, 1)),
          ((128, 1, 1, 0), (256, 3, 2, 1)),
          ((128, 1, 1, 0), (256, 3, 2, 1)),
          ((128, 1, 1, 0), (256, 4, 1, 1))],
}
def __init__(self, layers, filters, extras, batch_norm=False, **kwargs):
    super(VGGAtrousExtractor, self).__init__(layers, filters, batch_norm, **kwargs)
    with self.name_scope():
        self.extras = nn.HybridSequential()
        for i, config in enumerate(extras):
            extra = nn.HybridSequential(prefix='extra%d_'%(i))
            with extra.name_scope():
                for f, k, s, p in config:
                    extra.add(nn.Conv2D(f, k, s, p, **self.init))
                    if batch_norm:
                        extra.add(nn.BatchNorm())
                    extra.add(nn.Activation('relu'))
            self.extras.add(extra)

for i, config in enumerate(extras): 每个 config 都是 ((256, 1, 1, 0), (512, 3, 2, 1)) 这样的 Tuple,里面还嵌套着两个 Tuple。

extra.add(nn.Conv2D(f, k, s, p, **self.init))

nn.Conv2D 前四个参数分别是 channels, kernel_size, strides=(1, 1), padding=(0, 0)

可以看到 extra_spec[300] 前两个 extra 层会做一个 stride 是 2 的 conv

1x512x60x60 1x1024x30x30 1x512x15x15 1x256x8x8 1x256x6x6 1x256x4x4

60*60*4 + 30*30*6 + 15*15*6 + 8*8*6 + 6*6*4 + 4*4*4 = 21742

对于 VGGAtrousBase 类

with self.name_scope():
    # we use pre-trained weights from caffe, initial scale must change
    init_scale = mx.nd.array([0.229, 0.224, 0.225]).reshape((1, 3, 1, 1)) * 255
    self.init_scale = self.params.get_constant('init_scale', init_scale)
    self.stages = nn.HybridSequential()
    for l, f in zip(layers, filters):
        stage = nn.HybridSequential(prefix='')
        with stage.name_scope():
            for _ in range(l):
                stage.add(nn.Conv2D(f, kernel_size=3, padding=1, **self.init))
                if batch_norm:
                    stage.add(nn.BatchNorm())
                stage.add(nn.Activation('relu'))
        self.stages.add(stage)

    # use dilated convolution instead of dense layers
    stage = nn.HybridSequential(prefix='dilated_')
    with stage.name_scope():
        stage.add(nn.Conv2D(1024, kernel_size=3, padding=6, dilation=6, **self.init))
        if batch_norm:
            stage.add(nn.BatchNorm())
        stage.add(nn.Activation('relu'))
        stage.add(nn.Conv2D(1024, kernel_size=1, **self.init))
        if batch_norm:
            stage.add(nn.BatchNorm())
        stage.add(nn.Activation('relu'))
    self.stages.add(stage)

    # normalize layer for 4-th stage
    self.norm4 = Normalize(filters[3], 20)

可以看到,VGGAtrousBase 类的网络结构由 self.stages 和 self.norm4 两块构成,其中 self.stages 还是前后分为 用传统 卷积 和 用 dilated convolution 两块

vgg_spec = {
    11: ([1, 1, 2, 2, 2], [64, 128, 256, 512, 512]),
    13: ([2, 2, 2, 2, 2], [64, 128, 256, 512, 512]),
    16: ([2, 2, 3, 3, 3], [64, 128, 256, 512, 512]),
    19: ([2, 2, 4, 4, 4], [64, 128, 256, 512, 512])
}

因为我们是 16 ,所以 layers 是 [2, 2, 3, 3, 3],filters 是 [64, 128, 256, 512, 512]

在传统 卷积阶段,因为 kernel_size=3, padding=1,所以并不改变 feature map 的大小,因此 就是添加了 13 个 conv 层,每个之后都有 relu,在 relu 之前,可选要不要 BatchNorm,默认是关闭的

正常的是 dilation=(1, 1) kernel 的间隔数量,我觉得应该下 多少个 是另外一个 kernel 的点,正常的就是 下 1 个,当我 dilation=6 是表示下 6 个才是 kernel,相当于 这个 kernel 如果以 传统的 kernel 看是 (13, 13) 的大小,所以 padding = 6 是对的,以此 dialated conv 也不会改变 feature map 的大小

对于第一个 Layer 的输出

for stage in self.stages[:3]:
    x = stage(x)
    x = F.Pooling(x, pool_type='max', kernel=(2, 2), stride=(2, 2),
                  pooling_convention='full')
x = self.stages[3](x)
norm = self.norm4(x)
outputs.append(norm)

因为 stage 并不会改变 feature map 的大小,所有即使做了三次 pooling,因此,第一个 Layer 的 feature map 宽和高是原来的 1/8,如果输入是 (480, 480),那么 layer 1 的大小是 (60, 60)

对于第二个 Layer 的输出

x = F.Pooling(x, pool_type='max', kernel=(2, 2), stride=(2, 2),
              pooling_convention='full')
x = self.stages[4](x)
x = F.Pooling(x, pool_type='max', kernel=(3, 3), stride=(1, 1), pad=(1, 1),
              pooling_convention='full')
x = self.stages[5](x)
outputs.append(x)

虽然做了两次 max pooling,但是需要注意的是第二次 max pooling 的 stride 是 1,且做了 padding,因此第二层 Layer 的 feature map 的宽和高是上一层的 1/2,是输入的 1/16,如果输入是 (480, 480),那么 layer 2 的大小是 (30, 30)

第 3 - 6 层 Layer 都是 extras

从 extra_spec[300] 的数值看,layer 3 会做一个 stride = 2,pad = 1 的 conv,因此会把 feature map 进一步变小一半,因此第 3 层 Layer 的 feature map 的宽和高是上一层的 1/2,是输入的 1/32,如果输入是 (480, 480),那么 layer 3 的大小是 (15, 15)

layer 4 会继续做一个 stride = 2,pad = 1 的 conv,因此会把 feature map 进一步变小一半,因此第 4 层 Layer 的 feature map 的宽和高是上一层的 1/2,是输入的 1/64,如果输入是 (480, 480),那么 layer 4 的大小是 (8, 8)

layer 5 会做一个 Kernel size 是 3,stride = 1,但是 pad = 0 的 conv,因此 feature map 的大小会 -2,如果输入是 (480, 480),layer 4 是 (8, 8),那么 layer 5 的大小是 (6, 6)

layer 6 和 layer 5 一样会做一个 Kernel size 是 3,stride = 1,但是 pad = 0 的 conv,因此 feature map 的大小会 -2,如果输入是 (480, 480),layer 6 是 (6, 6),那么 layer 6 的大小是 (4, 4)

为什么 VGGAtrousBase 里面要做一个 dilation=6 的 dilated conv?use the atrous algorithm to fill the “holes” 什么意思?哪里来的 holes?看这篇博客SSD 里的 atrous为了要重用 VGG 在 ImageNet 上的权重,但又因为去掉了 2*2-s2 为了让其感受野一样才有的做法

change pool5 from 2 × 2 − s2 to 3 × 3 − s1

原来的 感受野是

       g 
    
   a   b   c      2*2 pooling, stride 1
 
  x y z p q e      3 * 3 conv, stride 1
  
0 1 2 3 4 5 6 0  

gluoncv/model_zoo/ssd/presets.py 中定义了一堆 SSD 网络,其实代码基本都一样,都是调用 get_ssd 函数,需要指定

  1. Backbone: name, base_size (用作 SSDAnchorGenerator 中的 im_size), features, filters
  2. Anchor: sizes (Anchor Size), ratios (Anchor Ratio), steps (Anchor 铺排的 stride,也是下采样倍数)
  3. Predictor: classes (用于构建 Prediction 类别数)
  4. Parameters: pretrained, pretrained_base, dataset (指定 pretrained 权重文件名)

...

  1. vgg16_atrous
    • 300
      • voc
        • sizes=[30, 60, 111, 162, 213, 264, 315]
      • coco
        • sizes=[21, 45, 99, 153, 207, 261, 315]
      • custom
        • sizes=[21, 45, 99, 153, 207, 261, 315] 说明 custom 是按照 coco 来的
    • 512
      • voc
        • sizes=[51.2, 76.8, 153.6, 230.4, 307.2, 384.0, 460.8, 537.6]
      • coco
        • sizes=[51.2, 76.8, 153.6, 230.4, 307.2, 384.0, 460.8, 537.6]
      • custom
        • sizes=[51.2, 76.8, 153.6, 230.4, 307.2, 384.0, 460.8, 537.6]
  2. resnet18_v1
    • 512
      • voc
        • sizes=[51.2, 102.4, 189.4, 276.4, 363.52, 450.6, 492]
      • coco
        • sizes=[51.2, 102.4, 189.4, 276.4, 363.52, 450.6, 492]
      • custom
        • sizes=[51.2, 102.4, 189.4, 276.4, 363.52, 450.6, 492]
  3. resnet50_v1
    • 512
      • voc
        • sizes=[51.2, 102.4, 189.4, 276.4, 363.52, 450.6, 492]
      • coco
        • sizes=[51.2, 133.12, 215.04, 296.96, 378.88, 460.8, 542.72]
      • custom
        • sizes=[51.2, 133.12, 215.04, 296.96, 378.88, 460.8, 542.72]
  4. resnet101_v2
    • 512
      • voc
        • sizes=[51.2, 102.4, 189.4, 276.4, 363.52, 450.6, 492]
  5. resnet152_v2
    • 512
      • voc
        • sizes=[51.2, 76.8, 153.6, 230.4, 307.2, 384.0, 460.8, 537.6]
  6. mobilenet1_0
    • 512
      • voc
        • sizes=[51.2, 102.4, 189.4, 276.4, 363.52, 450.6, 492]
      • coco
        • sizes=[51.2, 102.4, 189.4, 276.4, 363.52, 450.6, 492]
      • custom
        • sizes=[51.2, 102.4, 189.4, 276.4, 363.52, 450.6, 492]
  7. mobilenet0_25
    • 300
      • voc
        • sizes=[21, 45, 99, 153, 207, 261, 315]
      • coco
        • sizes=[21, 45, 99, 153, 207, 261, 315]
      • custom
        • sizes=[21, 45, 99, 153, 207, 261, 315]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment