【深度学习基础】PyTorch实现DPN亲身实践

1 论文关键信息

1.1 DPN block

论文指出了ResNet可以复用特征,DenseNet的结构可以帮助发现新的特征。于是作者结合二者的优点,提出了一种DPN网络结构。该结构实际上是ResNeXt与DenseNet的一种结合。

在分析的过程中论文给出了一些公式说明,ResNet,DenseNet与RNN之间的关系,如下图:
在这里插入图片描述

然后,又给出了一些公式说明它们之间的关系。这些应该是DPN网络形成的过程,讲道理,由于每台接触过RNN,上面这些图和论文中的一些公式我都没太看懂。反正作者们牛逼,我们直接来看它的结果吧:
在这里插入图片描述

我们来好好分析一下上图:首先,图中这些水管一样的图案是网络的输入输出特征,其宽度代表的是它的通道数量。
(a)图是ResNet的Bottleneck的结构,如果你了解之前的backbone就应该很清楚;(b)图是DenseNet block的结构,但是它比Dense多了一个1x1的卷积层,为什么呢? 因为它是要和ResNet结合的,加一层卷积可以让二者耦合起来;(c)然后c图就是share DenseNet block中的第一层1x1卷积,发现这样做其中会包含ResNet结构;于是论文基于这个特点,提出了DPN的结构,如(d)(e),这两个图是等效的。 我们来看一下(e)图的结构,首先上一层特征输入到1x1的卷积层,然后紧跟着一个3x3的卷积层;之后,1x1的卷积层处理的结果分成两部分——一部分结果作为残差与输入相加,另一部分与相加后的结果进行拼接。需要注意的是,这里的3x3卷积沿用ResNeXt的思想,采用分组卷积的方式形成inception结构。

1.2 网络结构

在这里插入图片描述

2 PyTorch代码

就像论文说的,DPN在ResNeXt的基础上,适当调整结构即可。

2.1 Dual Path Block

代码:

class DPN_Block(nn.Module):
    """
    Dual Path block
    """

    def __init__(self, in_chnls, add_chnl, cat_chnl, cardinality, d, stride):
        super(DPN_Block, self).__init__()
        self.add = add_chnl
        self.cat = cat_chnl
        self.chnl = cardinality * d
        self.conv1 = BN_Conv2d(in_chnls, self.chnl, 1, 1, 0)
        self.conv2 = BN_Conv2d(self.chnl, self.chnl, 3, stride, 1, groups=cardinality)
        self.conv3 = nn.Conv2d(self.chnl, add_chnl + cat_chnl, 1, 1, 0)
        self.bn = nn.BatchNorm2d(add_chnl + cat_chnl)
        self.shortcut = nn.Sequential()
        if add_chnl != in_chnls:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_chnls, add_chnl, 1, stride, 0),
                nn.BatchNorm2d(add_chnl)
            )

    def forward(self, x):
        out = self.conv1(x)
        out = self.conv2(out)
        out = self.bn(self.conv3(out))
        add = out[:, :self.add, :, :] + self.shortcut(x)
        out = torch.cat((add, out[:, self.add:, :, :]), dim=1)
        return F.relu(out)

2.2 网络结构实现和测试

class DPN(nn.Module):
    def __init__(self, blocks: object, add_chnls: object, cat_chnls: object,
                 conv1_chnl, cardinality, d, num_classes) -> object:
        super(DPN, self).__init__()
        self.cdty = cardinality
        self.chnl = conv1_chnl
        self.conv1 = BN_Conv2d(3, self.chnl, 7, 2, 3)
        d1 = d
        self.conv2 = self.__make_layers(blocks[0], add_chnls[0], cat_chnls[0], d1, 1)
        d2 = 2 * d1
        self.conv3 = self.__make_layers(blocks[1], add_chnls[1], cat_chnls[1], d2, 2)
        d3 = 2 * d2
        self.conv4 = self.__make_layers(blocks[2], add_chnls[2], cat_chnls[2], d3, 2)
        d4 = 2 * d3
        self.conv5 = self.__make_layers(blocks[3], add_chnls[3], cat_chnls[3], d4, 2)
        self.fc = nn.Linear(self.chnl, num_classes)

    def __make_layers(self, block, add_chnl, cat_chnl, d, stride):
        layers = []
        strides = [stride] + [1] * (block-1)
        for i, s in enumerate(strides):
            layers.append(DPN_Block(self.chnl, add_chnl, cat_chnl, self.cdty, d, s))
            self.chnl = add_chnl + cat_chnl
        return nn.Sequential(*layers)

    def forward(self, x):
        out = self.conv1(x)
        out = F.max_pool2d(out, 3, 2, 1)
        print("shape 1---->", out.shape)
        out = self.conv2(out)
        print("shape 2---->", out.shape)
        out = self.conv3(out)
        print("shape 3---->", out.shape)
        out = self.conv4(out)
        print("shape 4---->", out.shape)
        out = self.conv5(out)
        print("shape 5---->", out.shape)
        out = F.avg_pool2d(out, 7)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        return F.softmax(out)


def dpn_92_32x3d(num_classes=1000):
    return DPN(blocks=[3, 4, 20, 3],
               add_chnls=[256, 512, 1024, 2048],
               cat_chnls=[16, 32, 24, 128],
               conv1_chnl=64,
               cardinality=32,
               d=3,
               num_classes=num_classes)


def dpn_98_40x4d(num_classes=1000):
    return DPN(blocks=[3, 6, 20, 3],
               add_chnls=[256, 512, 1024, 2048],
               cat_chnls=[16, 32, 32, 128],
               conv1_chnl=96,
               cardinality=40,
               d=5,
               num_classes=num_classes)


def dpn_131_40_4d(num_classes=1000):
    return DPN(blocks=[4, 8, 28, 3],
               add_chnls=[256, 512, 1024, 2048],
               cat_chnls=[16, 32, 32, 128],
               conv1_chnl=128,
               cardinality=40,
               d=5,
               num_classes=num_classes)


def test():
    # net = dpn_92_32x3d()
    net = dpn_98_40x4d()
    # net = dpn_131_40_4d()
    summary(net, (3, 224, 224))

test()

DPN_98(40x4d)网络的测试输出如下:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔡逸超

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值