经典神经网络 -- DPN : 设计原理与pytorch实现

原理

       DPN这个模型融入了三种基础模型,inception,resnet,densenet。有inception的宽度,又有resnet的shortcut利用,和densenet的浅层特征重复利用

       tensor的切片操作:a 是 [2,3,5,5], 那么 a[:,:1,:,:] 就是 [2,1,5,5]。a,b 都是 [2,3,5,5] ,那么 torch.cat([a[:,:1,:,:] + b[:,:1,:,:]], dim=1) 还是 2, 1, 5, 5], 数据相加,值大小的变化。但 torch.cat([a[:,:1,:,:], b[:,:1,:,:]], dim=1) 是 [2, 2, 5, 5],拼接了一个特征图。通道数的改变。

代码实现

# DPN这个模型融入了三种基础模型,inception,resnet,densenet。
# 有inception的宽度,又有resnet的shortcut利用,和densenet的浅层特征重复利用

# tensor的切片操作:a 是 [2,3,5,5], 那么 a[:,:1,:,:] 就是 [2,1,5,5]
# a,b 都是 [2,3,5,5] ,那么 torch.cat([a[:,:1,:,:] + b[:,:1,:,:]], dim=1) 还是 2, 1, 5, 5], 数据相加,值大小的变化
# 但 torch.cat([a[:,:1,:,:], b[:,:1,:,:]], dim=1) 是 [2, 2, 5, 5],拼接了一个特征图。通道数的改变


from time import sleep
import torch
import torch.nn as nn


class block(nn.Module):
    def __init__(self, in_channels, mid_channels, out_channels, dense_channels, stride, is_shortcut=False):
        # in_channels,是输入通道数,mid_channel是中间经历的通道数,out_channels是经过一次板块之后的输出通道数。
        # dense_channels设置这个参数的原因就是一边进行着resnet方式的卷进运算,另一边也同时进行着dense的卷积计算,之后特征图融合形成新的特征图
        super().__init__()
        self.is_shortcut = is_shortcut
        self.out_channels = out_channels
        self.relu = nn.ReLU(inplace=True)
        self.conv1 = nn.Sequential(
            nn.Conv2d(in_channels, mid_channels, kernel_size=1, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU()
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(mid_channels, mid_channels, kernel_size=3, stride=stride, padding=1, groups=32, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU()
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(mid_channels, out_channels+dense_channels, kernel_size=1, bias=False),
            nn.BatchNorm2d(out_channels+dense_channels)
        )
        if self.is_shortcut:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels+dense_channels, kernel_size=3, padding=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels+dense_channels)
            )
    
    def forward(self, x):
        a = x
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        if self.is_shortcut:
            a = self.shortcut(a)
        d = self.out_channels
        x = torch.cat([a[:,:d,:,:] + x[:,:d,:,:], a[:,d:,:,:], x[:,d:,:,:]], dim=1) # 记住拼接方法
        # 有一步的  a[:,d:,:,:], x[:,d:,:,:]  不对  原因:conv3 的设置不对
        x = self.relu(x)
        return x


def DPN92():
    cfg = {
        'mid_channels': (96,192,384,768),
        'out_channels': (256,512,1024,2048),
        'num': (3,4,20,3),
        'dense_channels': (16,32,24,128),
        'classes': (10)
    }
    return DPN(cfg)


class DPN(nn.Module):
    def __init__(self, cfg):
        super().__init__()
        mid_channels = cfg['mid_channels']
        out_channels = cfg['out_channels']
        num = cfg['num']
        dense_channels = cfg['dense_channels']
        self.in_channels = 64 # 输入通道64
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, 7, 2, 3, bias=False), # 把3变成64
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(3, 2, 1)
        )
        self.conv2 = self._make_layers(mid_channels[0], out_channels[0], dense_channels[0], 1, num[0]) # stride=1
        self.conv3 = self._make_layers(mid_channels[1], out_channels[1], dense_channels[1], 2, num[1])
        self.conv4 = self._make_layers(mid_channels[2], out_channels[2], dense_channels[2], 2, num[2])
        self.conv5 = self._make_layers(mid_channels[3], out_channels[3], dense_channels[3], 2, num[3])
        self.global_avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(cfg['out_channels'][3] + (num[3]+1) * cfg['dense_channels'][3], cfg['classes']) # fc层需要计算
    
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = self.conv5(x)
        x = self.global_avgpool(x)
        x = torch.flatten(x, 1) # 展开
        x = self.fc(x)
        return x

    def _make_layers(self, mid_channels, out_channels, dense_channels, stride, num):
        layers = []
        layers.append(block(self.in_channels, mid_channels, out_channels, dense_channels, stride=stride, is_shortcut=True))
        # block_1里面is_shortcut=True就是resnet中的shortcut连接,将浅层的特征进行一次卷积之后与进行三次卷积的特征图相加
        # 后面几次相同的板块is_shortcut=False简单的理解就是一个多次重复的板块,第一次利用就可以满足浅层特征的利用。后面重复的不在需要
        self.in_channels = out_channels + dense_channels*2
        # self.in_channels = out_channels + 2*dense_channels由于里面包含dense这种一直在叠加的特征图计算,
        # 所以第一次是2倍的dense_channels,每次一都会多出一倍,所以有(i+2)*dense_channels
        for i in range(1,num):
            layers.append(block(self.in_channels, mid_channels, out_channels, dense_channels, stride=1))
            self.in_channels = out_channels + (i+2)*dense_channels
        return nn.Sequential(*layers)


if __name__ == '__main__':
    net = DPN92()
    x = torch.rand((10, 3, 224, 224))
    for name,layer in net.named_children():
        if name != 'fc':
            x = layer(x)
            print(name, 'output shape:', x.shape)
        else:
            # x = x.view(x.size(0), -1)
            x = torch.flatten(x, 1) # 一模一样
            x = layer(x)
            print(name, 'output shape:', x.shape)

参考文章:

pytorch实现DPN 最详细的全面讲解_视觉盛宴的博客-CSDN博客 

你可能感兴趣的:(求职,CV-计算机视觉,pytorch,人工智能)