HarDNet

show more

Implementations

Pytorch-HarDNet

Unlike CNN models using a lot of Conv1x1 to reduce model size and number of MACs, HarDNet mainly uses Conv3x3 (with only one Conv1x1 layer for each HarDNet block) to increase the computational density.

Increased computational density changes a model from Memory-Bound to Compute-Bound.

Architecture

HarDNet Block:

  • k = growth rate (as in DenseNet)
  • m = channel multiplying factor (1.6~1.7)
  • Conv3x3 for all layers (no bottleneck layer)
  • Conv-BN-ReLU for all layers (instead of BN-ReLU-Conv in DenseNet)
  • No global dense connection (input of a HarDBlk is NOT reused as a part of the output)

Results

Results for object detection, which gives significant improvements.

Implementation in Pytorch

class HarDNet(nn.Module):
    def __init__(self, depth_wise=False, arch=85, pretrained=True, weight_path=''):
        super().__init__()
        first_ch  = [32, 64]
        second_kernel = 3
        max_pool = True
        grmul = 1.7
        drop_rate = 0.1
        
        #HarDNet68
        ch_list = [  128, 256, 320, 640, 1024]
        gr       = [  14, 16, 20, 40,160]
        n_layers = [   8, 16, 16, 16,  4]
        downSamp = [   1,  0,  1,  1,  0]
        
        if arch==85:
          #HarDNet85
          first_ch  = [48, 96]
          ch_list = [  192, 256, 320, 480, 720, 1280]
          gr       = [  24,  24,  28,  36,  48, 256]
          n_layers = [   8,  16,  16,  16,  16,   4]
          downSamp = [   1,   0,   1,   0,   1,   0]
          drop_rate = 0.2
        elif arch==39:
          #HarDNet39
          first_ch  = [24, 48]
          ch_list = [  96, 320, 640, 1024]
          grmul = 1.6
          gr       = [  16,  20, 64, 160]
          n_layers = [   4,  16,  8,   4]
          downSamp = [   1,   1,  1,   0]
          
        if depth_wise:
          second_kernel = 1
          max_pool = False
          drop_rate = 0.05
        
        blks = len(n_layers)
        self.base = nn.ModuleList([])

        # First Layer: Standard Conv3x3, Stride=2
        self.base.append (
             ConvLayer(in_channels=3, out_channels=first_ch[0], kernel=3,
                       stride=2,  bias=False) )
  
        # Second Layer
        self.base.append ( ConvLayer(first_ch[0], first_ch[1],  kernel=second_kernel) )
        
        # Maxpooling or DWConv3x3 downsampling
        if max_pool:
          self.base.append(nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
        else:
          self.base.append ( DWConvLayer(first_ch[1], first_ch[1], stride=2) )

        # Build all HarDNet blocks
        ch = first_ch[1]
        for i in range(blks):
            blk = HarDBlock(ch, gr[i], grmul, n_layers[i], dwconv=depth_wise)
            ch = blk.get_out_ch()
            self.base.append ( blk )
            
            if i == blks-1 and arch == 85:
                self.base.append ( nn.Dropout(0.1))
            
            self.base.append ( ConvLayer(ch, ch_list[i], kernel=1) )
            ch = ch_list[i]
            if downSamp[i] == 1:
              if max_pool:
                self.base.append(nn.MaxPool2d(kernel_size=2, stride=2))
              else:
                self.base.append ( DWConvLayer(ch, ch, stride=2) )
            
        
        ch = ch_list[blks-1]
        self.base.append (
            nn.Sequential(
                nn.AdaptiveAvgPool2d((1,1)),
                Flatten(),
                nn.Dropout(drop_rate),
                nn.Linear(ch, 1000) ))
                
        #print(self.base)
        
        if pretrained:
          if hasattr(torch, 'hub'):
          
            if arch == 68 and not depth_wise:
              checkpoint = 'https://ping-chao.com/hardnet/hardnet68-5d684880.pth'
            elif arch == 85 and not depth_wise:
              checkpoint = 'https://ping-chao.com/hardnet/hardnet85-a28faa00.pth'
            elif arch == 68 and depth_wise:
              checkpoint = 'https://ping-chao.com/hardnet/hardnet68ds-632474d2.pth'
            else:
              checkpoint = 'https://ping-chao.com/hardnet/hardnet39ds-0e6c6fa9.pth'

            self.load_state_dict(torch.hub.load_state_dict_from_url(checkpoint, progress=False))
          else:
            postfix = 'ds' if depth_wise else ''
            weight_file = '%shardnet%d%s.pth'%(weight_path, arch, postfix)            
            if not os.path.isfile(weight_file):
              print(weight_file,'is not found')
              exit(0)
            weights = torch.load(weight_file)
            self.load_state_dict(weights)
          
          postfix = 'DS' if depth_wise else ''
          print('ImageNet pretrained weights for HarDNet%d%s is loaded'%(arch, postfix))
          
    def forward(self, x):
        for layer in self.base:
          x = layer(x)
        return x

@PingChao, @Chao-YangKao

Real Source From

pytorchhardnetssd-hardnet
How helpful was this page?