Press "Enter" to skip to content

基于pytorch搭建ResNet神经网络用于花类识别

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

 

:tangerine:作者简介:秃头小苏,致力于用最通俗的语言描述问题

 

:tangerine:往期回顾: 基于pytorch搭建VGGNet神经网络用于花类识别 基于pytorch搭建AlexNet神经网络用于花类识别

 

:tangerine:近期目标:拥有5000粉丝

 

:tangerine:支持小苏:点赞:+1|type_3:、收藏:star:、留言:envelope_with_arrow:

 

基于pytorch搭建ResNet神经网络用于花类识别

 

写在前面

 

这一系列已经写了好几篇了,这篇结束后可能就会停更一系列了,因为一方面,看懂了已经更新的这些我认为其他的网络大概就是照葫芦画瓢,自己多多少少是能看明白个大概的。 【当然这是要在你对这部分网络结构的理论有充分的了解之后】 另一方面,我觉得这部分真的得你自己切切实实的钻研,自己一步步的调试,看别人的文章、甚至是视频,你可能会得到短暂的满足,但是许多细节你是体验不到的。所以这里给出 基于pytorch搭建ResNet神经网络用于花类识别 的完整代码,希望大家下去后仔细阅读:rose::rose::rose:

 

至于这一系列再次更新的话不出意外会讲讲一些轻量级网络像MobileNet、shuffleNet等,当然了这部分都已经做过理论部分的概述了:lemon::lemon::lemon:

 

还是回归到本文上来,首先你需要具备以下知识:

ResNet的理论
pytorch搭建网络基础

当然,这些我在前文都已经介绍过,大家抱着缺啥补啥的态度去看看呗:ear_of_rice::ear_of_rice::ear_of_rice:

深度学习经典网络模型汇总
使用pytorch自己构建网络模型实战
基于pytorch搭建AlexNet神经网络用于花类识别
基于pytorch搭建VGGNet神经网络用于花类识别

ResNet网络模型搭建:sparkles::sparkles::sparkles:

 

自己写文章的宗旨是致力于用最通俗的语言描述问题嘛:dart::dart::dart:但是对于一些关乎于代码的文章,有的时候单纯的文字确实很难将问题表述清楚,因此我建议你先观看此视频,对ResNet网络模型搭建的整理流程有了一个大致的了解后再来阅读此文,然后再以这篇文章为辅进行学习,这样我觉得是高效的学习方式:four_leaf_clover::four_leaf_clover::four_leaf_clover:【 当然这样还是不够的,你一定要自己去独立的阅读代码,一步步的调试运行,这一点我想我再强调也不为过】

 

ResNeta网络是有大量重复的结构堆叠而成的,它的网络层数主要有18层、34层、50层、101层和152层。对于18层和34层的网络它的基础模块为 basic block ,而对于50层、101层和152层的网络其基础模块为 bottleneck block 。我们可以分别来定义这两个基础模块,如下:

 

# 定义BasicBlock
class BasicBlock(nn.Module):
    expansion = 1
    def __init__(self, in_channel, out_channel, stride=1, downsample=None, **kwargs):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,
                               kernel_size=3, stride=stride, padding=1, bias=False)       # 特征图尺寸不变
        self.bn1 = nn.BatchNorm2d(out_channel)              # BN层建议放在卷积和激活层之间
        self.relu = nn.ReLU()
        self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel,
                               kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channel)
        self.downsample = downsample
    def forward(self, x):
        identity = x
        if self.downsample is not None:
            identity = self.downsample(x)
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.conv2(out)
        out = self.bn2(out)
        out += identity
        out = self.relu(out)
        return out

 

# 定义Bottleneck
class Bottleneck(nn.Module):
    """
    注意:原论文中,在虚线残差结构的主分支上,第一个1x1卷积层的步距是2,第二个3x3卷积层步距是1。
    但在pytorch官方实现过程中是第一个1x1卷积层的步距是1,第二个3x3卷积层步距是2,
    这幺做的好处是能够在top1上提升大概0.5%的准确率。
    可参考Resnet v1.5 https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch
    """
    expansion = 4
    def __init__(self, in_channel, out_channel, stride=1, downsample=None,
                 groups=1, width_per_group=64):
        super(Bottleneck, self).__init__()
        width = int(out_channel * (width_per_group / 64.)) * groups
        self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=width,
                               kernel_size=1, stride=1, bias=False)  # squeeze channels
        self.bn1 = nn.BatchNorm2d(width)
        # -----------------------------------------
        self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, groups=groups,
                               kernel_size=3, stride=stride, bias=False, padding=1)
        self.bn2 = nn.BatchNorm2d(width)
        # -----------------------------------------
        self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel*self.expansion,
                               kernel_size=1, stride=1, bias=False)  # unsqueeze channels
        self.bn3 = nn.BatchNorm2d(out_channel*self.expansion)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
    def forward(self, x):
        identity = x
        if self.downsample is not None:
            identity = self.downsample(x)
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)
        out = self.conv3(out)
        out = self.bn3(out)
        out += identity
        out = self.relu(out)
        return out

 

接着我们就可以来定义我们的ResNet网络了:

 

class ResNet(nn.Module):
    def __init__(self,
                 block,
                 blocks_num,
                 num_classes=1000,
                 include_top=True,
                 groups=1,
                 width_per_group=64):
        super(ResNet, self).__init__()
        self.include_top = include_top
        self.in_channel = 64
        self.groups = groups
        self.width_per_group = width_per_group
        self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2,
                               padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(self.in_channel)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, blocks_num[0])
        self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2)
        self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2)
        self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2)
        if self.include_top:
            self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # output size = (1, 1)
            self.fc = nn.Linear(512 * block.expansion, num_classes)
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
    
    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        if self.include_top:
            x = self.avgpool(x)
            x = torch.flatten(x, 1)
            x = self.fc(x)
        return x               
                

 

我们可以看出再ResNet 的定义中有这样的函数:

 

 

该函数表示对ResNet的每个基础模块一个整合,即layer1对应conv2_x、layer2对应conv3_x、layer3对应conv4_x、layer4对应conv5_x:rice::rice::rice:

 

 

_make_layer 函数的定义如下:

 

def _make_layer(self, block, channel, block_num, stride=1):
    downsample = None
    if stride != 1 or self.in_channel != channel * block.expansion:
        downsample = nn.Sequential(
            nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),
            nn.BatchNorm2d(channel * block.expansion))
    layers = []
    layers.append(block(self.in_channel,
                        channel,
                        downsample=downsample,
                        stride=stride,
                        groups=self.groups,
                        width_per_group=self.width_per_group))
    self.in_channel = channel * block.expansion
    for _ in range(1, block_num):
        layers.append(block(self.in_channel,
                            channel,
                            groups=self.groups,
                            width_per_group=self.width_per_group))
    return nn.Sequential(*layers)
def forward(self, x):
    x = self.conv1(x)
    x = self.bn1(x)
    x = self.relu(x)
    x = self.maxpool(x)
    x = self.layer1(x)
    x = self.layer2(x)
    x = self.layer3(x)
    x = self.layer4(x)
    if self.include_top:
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)
    return x

 

最后我们来看看如何定义一个具体的网络:

 

def resnet34(num_classes=1000, include_top=True):
    # https://download.pytorch.org/models/resnet34-333f7ec4.pth
    return ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top)
def resnet50(num_classes=1000, include_top=True):
    # https://download.pytorch.org/models/resnet50-19c8e357.pth
    return ResNet(Bottleneck, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top)
def resnet101(num_classes=1000, include_top=True):
    # https://download.pytorch.org/models/resnet101-5d3b4d8f.pth
    return ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes, include_top=include_top)

 

训练结构展示

 

ResNet34训练结果:

 

 

ResNet50训练结果:

 

 

ResNet101训练结果:

 

 

迁移学习使用ResNet34预加载模型:

 

 

下面给出各种模型生成的权重文件,如下:

 

 

小结

 

这一部分我感到有一些的奇怪,即上文用resnet训时,resnet101和resnet50的效果要比resnet34效果差,但是理论部分不是说resnet层数深效果越好嘛,这是什幺原因呢?希望知道的可以告知。:herb::herb::herb:

 

问了一些大佬,对于上述问题他们的解答是:这个和自己任务也有关系,简单的任务可能用小网络效果更好。

​​

参考视频: www.bilibili.com/video/BV14E…

 

如若文章对你有所帮助,那就

 

咻咻咻咻~~duang~~点个赞呗

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。