深度可分离卷积的具体理解：MobileNetV2设计灵感

This article was original written by Jin Tian, welcome re-post, first come with https://jinfagang.github.io . but please keep this copyright info, thanks, any question could be asked via wechat: jintianiloveu

Depthwise separable convolution

假设有一个3x3大小的卷积层，输入通道是16, 输出通道是32,那么既然输出通道是32, 就可以使用32个3x3的卷积核去遍历输入，这里面要用到的参数是 16x32x3x3=4608 个


先用 16个 3x3 的卷积核遍历16个通道里面的数据，得到16个特征图谱，在用32个1x1的卷积核去遍历这16个特征图谱，进行相加融合，这个过程使用的参数是 16x3x3 + 16x32x1x1 = 656 个参数。


  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47  class InvertedResidual(nn.Module): def __init__(self, x, output, stride, expand_ratio): """ this is the core of MobileNet, something just like ResNet but not very much alike. it is called Inverted Residual, the opposite residual operation, how does it operation anyway? Only when stride == 1 && input == output, using residual connect other wise normal convolution what does this expand_ratio for? this value is the middle expand ratio when you transfer input channel to output channel ( you will get a middle value right? so there it is) :param x: :param output: :param stride: :param expand_ratio: """ super(InvertedResidual, self).__init__() self.stride = stride assert stride in [1, 2], 'InsertedResidual stride must be 1 or 2, can not be changed' self.user_res_connect = self.stride == 1 and x == output # this convolution is the what we called Depth wise separable convolution # consist of pw and dw process, which is transfer channel and transfer shape in 2 steps self.conv = nn.Sequential( # pw nn.Conv2d(x, x * expand_ratio, 1, 1, 0, bias=False), nn.BatchNorm2d(x * expand_ratio), nn.ReLU6(inplace=True), # dw nn.Conv2d(x * expand_ratio, x * expand_ratio, 3, stride, 1, groups=x*expand_ratio, bias=False), nn.BatchNorm2d(x*expand_ratio), nn.ReLU6(inplace=True), # pw linear nn.Conv2d(x*expand_ratio, output, 1, 1, 0, bias=False), nn.BatchNorm2d(output), ) def forward(self, x): if self.user_res_connect: return x + self.conv(x) else: return self.conv(x)