Pytorch——ResNet结构和Mobilenetv1结构

75 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情


前言

上一篇文章中,我们进行了Cifar10图像分类实战

今天,我们来介绍如何去定义一个ResNet网络,如何去定义一个ResNet的基本单元以及和另一种非常经典的神经网络结构——Mobilenet。


  • 1.1 搭建ResNet网络结构

  • ResNet结构实际上是由一些基本的跳连结构作为它的基本单元,搭建出来的网络;

  • 在定义VGGNet的时候,采用卷积,BN,和ReLu这样的三个层作为基本的卷积单元;

  • 而在ResNet中,我们会将卷积转换为一个跳连的结构

具体的定义:

import torch
import torch.nn as nn
import torch.nn.functional as F

# 完成跳连结构的推理
class ResBlock(nn.Module):
    def __init__(self, in_channel, out_channel, stride=1):
        super(ResBlock, self).__init__()
        self.layer = nn.Sequential(
            nn.Conv2d(in_channel, out_channel,
                      kernel_size=3, stride=stride, padding=1),
            nn.BatchNorm2d(out_channel),
            nn.ReLU(),
            nn.Conv2d(in_channel, out_channel,
                      kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(out_channel),
        )
        if in_channel != out_channel or stride > 1:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channel, out_channel,
                          kernel_size=3, stride=stride, padding=1),
                nn.BatchNorm2d(out_channel),
            )
    def forward(self, x):
        out1 = self.layer(x)
        out2 = self.shortcut(x)
        out = out1 + out2
        out = F.relu(out)
        return out



class ResNet(nn.Module):

    # 实现多个基本resblock的串联
    def make_layer(self, block, out_channel, stride, num_block):
        layers_list = []
        for i in range(num_block):
            if i == 0:
                in_stride = stride
            else:
                in_stride = 1
            layers_list.append(
                block(self.in_channel,
                      out_channel,
                      in_stride))
            self.in_channel = out_channel

        return nn.Sequential(*layers_list)



    def __init__(self, Resblock):

        super(ResNet, self).__init__()
        self.in_channel = 32

        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU()
        )
        self.layer1 = \
            self.make_layer(ResBlock, 64, 1, 2)
        self.layer2 = \
            self.make_layer(ResBlock, 128, 2, 2)
        self.layer3 = \
            self.make_layer(ResBlock, 256, 2, 2)
        self.layer4 = \
            self.make_layer(ResBlock, 512, 2, 2)

        self.fc = nn.Linear(512, 10)

    def forward(self, x):
        out = self.conv1(x)
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = F.avg_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        return out


def resnet():
    return ResNet(ResBlock)

我们一共定义了9个卷积层,如果想要更好的效果,可以定义更复杂的结构,训练的结构可以看到,在第一个epoch之后,准确率是35%,在第三个epoch之后准确率是59%,可以看到模型是收敛的

如果想要搭建更复杂的resnet结构,可以将resblock的数量变多,或者把channel的数量变多,以及可以对resnet的基本单元进行调整

如果想要压缩计算量的话,可以在第一个卷积的时候将out_channel除上4,在经过第二个卷积的时候在将它计算回来,只要保证输出的outputs和我们想要的outputs一样就可以。通过这种方式来减少计算量,我们的网络性能可能会受到影响

在设计网络的时候,如果对计算量本身没有要求,可以直接采用标准的网络结构,如果对计算量要求比较高,需要加速计算量,这时就需要对网络的参数进行调整,就需要了解网络搭建的时候的一些细节。只有对细节有了解之后,才能有针对的对参数进行压缩或者分支的裁剪。

  • 1.2 定义一个mobilenet的核心单元

Mobilenet属于轻量型卷积神经网络中的一种,它首次采用了 depthwise,通过一个分组卷积和1x1的卷积来进行结合代替掉一个标准的卷积单元,进而压缩计算量和参数量,所以没通过Mobilenet可以实现更轻量型的网络结构,同时在效果上保证精度损失比较小。

其中,Mobilenet中的核心depthwise已经成为了在设计轻量型神经网络中的非常重要的模块,很多轻量型的神经网络在压缩模型计算量的时候,尤其是对标准的卷积进行拆分的时候,经常采用这种方式来代替掉标准的卷积单元

在pytorch中如何去定义一个mobilenet的核心单元

import torch
import torch.nn as nn
import torch.nn.functional as F

class mobilenet(nn.Module):

    def conv_dw(self, in_channel, out_channel, stride):
        return nn.Sequential(
            nn.Conv2d(in_channel, in_channel,
                      kernel_size=3, stride=stride, padding=1,
                      groups=in_channel, bias=False),
            nn.BatchNorm2d(in_channel),
            nn.ReLU(),

            nn.Conv2d(in_channel, out_channel,
                      kernel_size=1, stride=1, padding=0,
                      groups=in_channel, bias=False),
            nn.BatchNorm2d(out_channel),
            nn.ReLU(),
        )


    def __init__(self):
        super(mobilenet, self).__init__()

        # 我们在搭建神经网络的时候,第一个卷积层一般采用标准卷积
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU()
        )

        self.conv_dw2 = self.conv_dw(32, 32, 1)
        self.conv_dw3 = self.conv_dw(32, 64, 2)

        self.conv_dw4 = self.conv_dw(64, 64, 1)
        self.conv_dw5 = self.conv_dw(64, 128, 2)

        self.conv_dw6 = self.conv_dw(128, 128, 1)
        self.conv_dw7 = self.conv_dw(128, 256, 2)

        self.conv_dw8 = self.conv_dw(256, 256, 1)
        self.conv_dw9 = self.conv_dw(256, 512, 2)

        self.fc = nn.Linear(512, 10)

    def forward(self, x):
        out = self.conv1(x)


        out = self.conv_dw2(out)
        out = self.conv_dw3(out)
        out = self.conv_dw4(out)
        out = self.conv_dw5(out)
        out = self.conv_dw6(out)
        out = self.conv_dw7(out)
        out = self.conv_dw8(out)
        out = self.conv_dw9(out)

        out = F.avg_pool2d(out, 2)

        out = out.view(-1, 512)

        out = self.fc(out)

        return out

def mobilenetv1_small():
    return mobilenet()

注:cifar10数据训练的代码参考我之前的文章Pytorch——Cifar10图像分类中的训练模型的代码,只需要修改一下net即可。

利用mobilenet网络来训练Cifar10的数据,可以看到mobilenet的网络也是在收敛的过程中的,在训练10次之后就能够到79%.

在使用VGGNet,ResNet,Mobilenet在训练的时候一般都能够训练到90%左右,这里可以通过调节数据增强和一些训练参数来将我们的网络调到最优,下一篇文章将介绍如何调整我们的参数使我们的网络收敛到90%以上

9JQ4ZCQY3M({Q$KEN%9BFQX.png