ResNet网络结构详解,网络搭建,迁移学习

这篇具有很好参考价值的文章主要介绍了ResNet网络结构详解,网络搭建,迁移学习。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:

参考内容来自up:6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili

up的代码和ppt:https://github.com/WZMIAOMIAO/deep-learning-for-image-processing

一、简介

ResNet 网络是在 2015年 由微软实验室提出,斩获当年ImageNet竞赛中分类任务第一名,目标检测第一名。获得COCO数据集中目标检测第一名,图像分割第一名。

  • 原论文地址:[1512.03385] Deep Residual Learning for Image Recognition (arxiv.org)

  • 在ResNet网络的创新点:

  • 搭建超深的网络结构(可突破1000层)

  • 提出 Residual 结构(残差结构 )

  • 使用 Batch Normalization 加速训练(丢弃dropout)

ResNet网络结构详解,网络搭建,迁移学习

下图是ResNet34层模型和VGG模型的对比结构简图:

ResNet网络结构详解,网络搭建,迁移学习

二、详解

1 . 为什么使用残差模块 ?

在ResNet网络提出之前,传统的卷积神经网络都是通过将一系列卷积层与池化层进行堆叠得到的。

一般我们会觉得网络越深,特征信息越丰富,模型效果应该越好。但是实验证明,当网络堆叠到一定深度时,会出现两个问题:

  • 梯度消失或梯度爆炸

关于梯度消失和梯度爆炸,其实看名字理解最好:
若每一层的误差梯度小于1,反向传播时,网络越深,梯度越趋近于0
反之,若每一层的误差梯度大于1,反向传播时,网路越深,梯度越来越大

这个问题通常通过对数据进行标准化处理,权重初始化,BN处理等方法。

  • 退化问题(degradation problem):在解决了梯度消失、爆炸问题后,仍然存在深层网络的效果可能比浅层网络差的现象

这个问题通过残差结构解决

总结就是,当网络堆叠到一定深度时,反而会出现深层网络比浅层网络效果差的情况

如下图所示,20层网络比56层网络的误差更小:

ResNet网络结构详解,网络搭建,迁移学习

在原论文中:

  • 提出通过数据的预处理以及在网络中使用 BNBatch Normalization)层来解决梯度消失或梯度爆炸问题。

  • 提出了 residual结构残差结构)来减轻退化问题,下图是使用residual结构的卷积网络,可以看到随着网络的不断加深,效果并没有变差,而是变的更好了。(虚线是train error,实线是test error)

ResNet网络结构详解,网络搭建,迁移学习

2 . 什么是残差网络 ?

为了解决深层网络中的退化问题,可以人为地让神经网络某些层跳过下一层神经元的连接,隔层相连,弱化每层之间的强联系。这种神经网络被称为 残差网络 (ResNets)。

残差块

假设 F(x) 代表某个只包含有两层的映射函数, x 是输入, F(x)是输出。假设他们具有相同的维度。在训练的过程中我们希望能够通过修改网络中的 w和b去拟合一个理想的 H(x)(从输入到输出的一个理想的映射函数)。也就是我们的目标是修改F(x) 中的 w和b逼近 H(x) 。如果我们改变思路,用F(x) 来逼近 H(x)-x ,那么我们最终得到的输出就变为 F(x)+x(这里的加指的是对应位置上的元素相加,也就是element-wise addition),这里将直接从输入连接到输出的结构也称为shortcut,那整个结构就是残差块,ResNet的基础模块。

ResNet网络结构详解,网络搭建,迁移学习

ResNet沿用了VGG全3x3卷积层的设计。残差块里首先有2个有相同输出通道数的3x3卷积层。每个卷积层后接BN层和ReLU激活函数,然后将输入直接加在最后的ReLU激活函数前,这种结构用于层数较少的神经网络中,比如ResNet34。若输入通道数比较多,就需要引入1x1卷积层来调整输入的通道数,这种结构也叫作瓶颈模块,通常用于网络层数较多的结构中。如下图所示:

ResNet网络结构详解,网络搭建,迁移学习

注意:主分支与shortcut的输出特征矩阵shape必须相同

可以计算一下,假设两个残差结构的输入特征和输出特征矩阵的深度都是256维,如下图:(注意左侧结构的改动)

ResNet网络结构详解,网络搭建,迁移学习

那么两个残差结构所需的参数为:
左侧:3 × 3 × 256 × 256 + 3 × 3 × 256 × 256 = 1 , 179 , 648
右侧:1 × 1 × 256 × 64 + 3 × 3 × 64 × 64 + 1 × 1 × 64 × 256 = 69 , 632
注:CNN参数个数 = 卷积核尺寸×卷积核深度 × 卷积核组数 = 卷积核尺寸 × 输入特征矩阵深度 × 输出特征矩阵深度
明显搭建深层网络时,使用右侧的残差结构更合适。

3 . 网路结构

观察下图34层网络,可以发现有些残差块的 short cut 是实线的,而有些则是虚线的。

这些虚线的 short cut 上通过1×1的卷积核进行了维度处理(特征矩阵在长宽方向降采样,深度方向调整成下一层残差结构所需要的channel)。

ResNet网络结构详解,网络搭建,迁移学习

下图是原论文给出的不同深度的ResNet网络结构配置,注意表中的残差结构给出了主分支上卷积核的大小与卷积核个数,表中 残差块×N 表示将该残差结构重复N次。

ResNet网络结构详解,网络搭建,迁移学习

原文的表注中已说明,conv3_x, conv4_x, conv5_x所对应的一系列残差结构的第一层残差结构都是虚线残差结构。因为这一系列残差结构的第一层都有调整输入特征矩阵shape的作用(将特征矩阵的高和宽缩减为原来的一半,将深度channel调整成下一层残差结构所需要的channel)

需要注意的是,对于ResNet50/101/152,其实conv2_x所对应的一系列残差结构的第一层也是虚线残差结构,因为它需要调整输入特征矩阵的channel。根据表格可知通过3x3的max pool之后输出的特征矩阵shape应该是[56, 56, 64],但conv2_x所对应的一系列残差结构中的实线残差结构它们期望的输入特征矩阵shape是[56, 56, 256](因为这样才能保证输入输出特征矩阵shape相同,才能将捷径分支的输出与主分支的输出进行相加)。所以第一层残差结构需要将shape从[56, 56, 64] --> [56, 56, 256]。注意,这里只调整channel维度,高和宽不变(而conv3_x, conv4_x, conv5_x所对应的一系列残差结构的第一层虚线残差结构不仅要调整channel还要将高和宽缩减为原来的一半)。

ResNet

残差结构

浅层网络

ResNet18/34

BasicBlock

深层网络

ResNet50/101/152

Bottleneck

ResNet 18/34 具体的残差结构图:

ResNet网络结构详解,网络搭建,迁移学习

ResNet 50/101/152 具体的残差结构图:

ResNet网络结构详解,网络搭建,迁移学习

4 . Batch Normalization原理

我们在图像预处理过程中通常会对图像进行标准化处理,这样能够加速网络的收敛,如下图所示,对于Conv1来说输入的就是满足某一分布的特征矩阵,但对于Conv2而言输入的feature map就不一定满足某一分布规律了注意这里所说满足某一分布规律并不是指某一个feature map的数据要满足分布规律,理论上是指整个训练样本集所对应feature map的数据要满足分布规律)。而我们Batch Normalization的目的就是使我们的feature map满足均值为0,方差为1的分布规律

使用BN时需要注意的问题

(1)训练时要将traning参数设置为True,在验证时将trainning参数设置为False。在pytorch中可通过创建模型的model.train()和model.eval()方法控制。

(2)batch size尽可能设置大点,设置小后表现可能很糟糕,设置的越大求的均值和方差越接近整个训练集的均值和方差。

(3)建议将bn层放在卷积层(Conv)和激活层(例如Relu)之间,且卷积层不要使用偏置bias,因为没有用

三、迁移学习

迁移学习(Transfer Learning)是一种机器学习方法,是把一个领域(源领域)的知识,迁移到另外一个领域(目标领域),使得目标领域能够取得更好的学习效果。

在迁移学习中,我们希望利用源任务(Source Task)学到的知识帮助学习目标任务 (Target Task)。例如,一个训练好的图像分类网络能够被用于另一个图像相关的任务。再比如,一个网络在仿真环境学习的知识可以被迁移到真实环境的网络。迁移学习一个典型的例子就是载入训练好VGG网络,这个大规模分类网络能将图像分到1000个类别,然后把这个网络用于另一个任务,如医学图像分类。

如下图所示,神经网络逐层提取图像的深层信息,这样,预训练网络就相当于一个特征提取器。

ResNet网络结构详解,网络搭建,迁移学习
  • 使用迁移学习的优势

  1. 能够快速的训练出一个理想的结果

  1. 当数据集较小时也能训练出理想的效果

   注意:使用别人预训练好的模型参数时,要注意别人的预处理方式。

  • 常见的迁移学习方式

  1. 载入权重后训练所有参数

  1. 载入权重后只训练最后几层参数

  1. 载入权重后在原网络基础上再添加一层全连接层,仅训练最后一个全连接层

四、网络搭建

1 model.py

import torch.nn as nn
import torch

# ResNet18/34的残差结构,用的是2个3x3的卷积
class BasicBlock(nn.Module):
    expansion = 1  # 残差结构中,主分支的卷积核个数是否发生变化,不变则为1

    #初始化层结构,downsample使得既有实现的功能又有虚线的功能,conv3_x, conv4_x, conv5_x所对应的一系列残差结构的第一层都有调整输入特征矩阵shape的作用
    def __init__(self, in_channel, out_channel, stride=1, downsample=None, **kwargs):  # downsample对应虚线残差结构
        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)     #stride为传入进来的,1为视线,不需要改变大小,2为虚线结构,使用BN时不使用偏置
                                                                                        #stride=1,output=(input-3+2*1)/ 1 + 1 = input   输入和输出的高和宽时一样的
                                                                                        #stride=2,output=(input-3+2*1)/ 2 + 1 = input = input/2 + 0.5 = input/2(向下取整)
        self.bn1 = nn.BatchNorm2d(out_channel)
        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)  # 捷径分支 short cut

        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

# ResNet50/101/152的残差结构,用的是1x1+3x3+1x1的卷积
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  # 残差结构中第三层卷积核个数是第一/二层卷积核个数的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)
                               #stride=stride根据传入的进行调整,因为实线中的第二层是1,虚线中是2
        self.bn2 = nn.BatchNorm2d(width)
        # -----------------------------------------
        self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel*self.expansion,      #卷积核个数变为4倍
                               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)  # 捷径分支 short cut

        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

#整个网络的框架部分
class ResNet(nn.Module):

    # block = BasicBlock or Bottleneck
    # block_num为残差结构中conv2_x~conv5_x中残差块个数,是一个列表,如34层中的是3,4,6,3
    def __init__(self,
                 block,     
                 blocks_num,
                 num_classes=1000,
                 include_top=True,      #方便再resnet网络的基础上搭建其他网络,这里用不到
                 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])        #对应conv2所有的一切残差结构,通过_make_layer函数生成
        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')

    # channel为残差结构中第一层卷积核个数,block_num表示该层一共包含多少个残差结构,如34层中的是3,4,6,3
    def _make_layer(self, block, channel, block_num, stride=1):
        downsample = None
        # ResNet50/101/152的残差结构,block.expansion=4
        if stride != 1 or self.in_channel != channel * block.expansion:     #layer2,3,4都会经过这个结构
            downsample = nn.Sequential(     #生成下采样函数,这里只需要调整conv2的特征矩阵的深度
                nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(channel * block.expansion))

        layers = []         #空列表     #首先将第一层残差结构添加进去,block = BasicBlock or Bottleneck
        layers.append(block(self.in_channel,        #输入特征矩阵的深度64
                            channel,                #残差结构对应主分支上的第一个卷积层的卷积核个数
                            downsample=downsample,  #50/101/152对应的是高宽不变,深度4倍,对应的虚线残差结构
                            stride=stride,          #对于layer1,stride=1,是高宽不变,深度4倍
                            groups=self.groups,
                            width_per_group=self.width_per_group))
        self.in_channel = channel * block.expansion

        for _ in range(1, block_num):               #通过循环将剩下的一系列实线残差结构压入,从1开始,因为0层上面已经搭建好了
            layers.append(block(self.in_channel,
                                channel,
                                groups=self.groups,
                                width_per_group=self.width_per_group))

        return nn.Sequential(*layers)       #转换为非关键字参数传入,Sequential将一系列结构组合再一起并返回到layer1

    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)


def resnext50_32x4d(num_classes=1000, include_top=True):
    # https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth
    groups = 32
    width_per_group = 4
    return ResNet(Bottleneck, [3, 4, 6, 3],
                  num_classes=num_classes,
                  include_top=include_top,
                  groups=groups,
                  width_per_group=width_per_group)


def resnext101_32x8d(num_classes=1000, include_top=True):
    # https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth
    groups = 32
    width_per_group = 8
    return ResNet(Bottleneck, [3, 4, 23, 3],
                  num_classes=num_classes,
                  include_top=include_top,
                  groups=groups,
                  width_per_group=width_per_group)

由于ResNet网络较深,直接训练的话会非常耗时,因此用迁移学习的方法导入预训练好的模型参数:

下载预训练的模型参数的链接在上方代码中文章来源地址https://www.toymoban.com/news/detail-439668.html

2. train.py

import os
import sys
import json

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms, datasets
from tqdm import tqdm

from model import resnet34


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))

    data_transform = {
        "train": transforms.Compose([transforms.RandomResizedCrop(224),
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),
        "val": transforms.Compose([transforms.Resize(256),      #原图的长宽比固定不动,把最小边长缩放到256
                                   transforms.CenterCrop(224),      #中心裁剪
                                   transforms.ToTensor(),
                                   transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}

    data_root = os.path.abspath(os.path.join(os.getcwd(), "../"))  # get data root path
    image_path = os.path.join(data_root, "data_set", "flower_data")  # flower data set path
    assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])
    train_num = len(train_dataset)

    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    flower_list = train_dataset.class_to_idx
    cla_dict = dict((val, key) for key, val in flower_list.items())
    # write dict into json file
    json_str = json.dumps(cla_dict, indent=4)
    with open('class_indices.json', 'w') as json_file:
        json_file.write(json_str)

    batch_size = 4
    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    print('Using {} dataloader workers every process'.format(nw))

    train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=nw)

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
                                            transform=data_transform["val"])
    val_num = len(validate_dataset)
    validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                                  batch_size=batch_size, shuffle=False,
                                                  num_workers=nw)

    print("using {} images for training, {} images for validation.".format(train_num,
                                                                           val_num))
    
    net = resnet34()
    # load pretrain weights
    # download url: https://download.pytorch.org/models/resnet34-333f7ec4.pth
    model_weight_path = "./resnet34-pre.pth"
    assert os.path.exists(model_weight_path), "file {} does not exist.".format(model_weight_path)
    net.load_state_dict(torch.load(model_weight_path, map_location='cpu'))
    # for param in net.parameters():
    #     param.requires_grad = False

    # change fc layer structure
    in_channel = net.fc.in_features
    net.fc = nn.Linear(in_channel, 5)
    net.to(device)

    # define loss function
    loss_function = nn.CrossEntropyLoss()

    # construct an optimizer
    params = [p for p in net.parameters() if p.requires_grad]
    optimizer = optim.Adam(params, lr=0.0001)

    epochs = 3
    best_acc = 0.0
    save_path = './resNet34.pth'
    train_steps = len(train_loader)
    for epoch in range(epochs):
        # train
        net.train()
        running_loss = 0.0
        train_bar = tqdm(train_loader, file=sys.stdout)
        for step, data in enumerate(train_bar):
            images, labels = data
            optimizer.zero_grad()
            logits = net(images.to(device))
            loss = loss_function(logits, labels.to(device))
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()

            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)

        # validate
        net.eval()
        acc = 0.0  # accumulate accurate number / epoch
        with torch.no_grad():
            val_bar = tqdm(validate_loader, file=sys.stdout)
            for val_data in val_bar:
                val_images, val_labels = val_data
                outputs = net(val_images.to(device))
                # loss = loss_function(outputs, test_labels)
                predict_y = torch.max(outputs, dim=1)[1]
                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()

                val_bar.desc = "valid epoch[{}/{}]".format(epoch + 1,
                                                           epochs)

        val_accurate = acc / val_num
        print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
              (epoch + 1, running_loss / train_steps, val_accurate))

        if val_accurate > best_acc:
            best_acc = val_accurate
            torch.save(net.state_dict(), save_path)

    print('Finished Training')


if __name__ == '__main__':
    main()

3 predict.py

import os
import json

import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt

from model import resnet34


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose(
        [transforms.Resize(256),
         transforms.CenterCrop(224),
         transforms.ToTensor(),
         transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])

    # load image
    img_path = "../tulip.jpg"
    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
    img = Image.open(img_path)
    plt.imshow(img)
    # [N, C, H, W]
    img = data_transform(img)
    # expand batch dimension
    img = torch.unsqueeze(img, dim=0)

    # read class_indict
    json_path = './class_indices.json'
    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)

    with open(json_path, "r") as f:
        class_indict = json.load(f)

    # create model
    model = resnet34(num_classes=5).to(device)

    # load model weights
    weights_path = "./resNet34.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path, map_location=device))

    # prediction
    model.eval()
    with torch.no_grad():
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()
        predict = torch.softmax(output, dim=0)
        predict_cla = torch.argmax(predict).numpy()

    print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],
                                                 predict[predict_cla].numpy())
    plt.title(print_res)
    for i in range(len(predict)):
        print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],
                                                  predict[i].numpy()))
    plt.show()


if __name__ == '__main__':
    main()

到了这里,关于ResNet网络结构详解,网络搭建,迁移学习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ResNet详解:网络结构解读与PyTorch实现教程

    本文深入探讨了深度残差网络(ResNet)的核心概念和架构组成。我们从深度学习和梯度消失问题入手,逐一解析了残差块、初始卷积层、残差块组、全局平均池化和全连接层的作用和优点。文章还包含使用PyTorch构建和训练ResNet模型的实战部分,带有详细的代码和解释。 关注

    2024年02月10日
    浏览(46)
  • 【pytorch】ResNet18、ResNet20、ResNet34、ResNet50网络结构与实现

    选取经典的早期Pytorch官方实现代码进行分析 https://github.com/pytorch/vision/blob/9a481d0bec2700763a799ff148fe2e083b575441/torchvision/models/resnet.py 各种ResNet网络是由BasicBlock或者bottleneck构成的,它们是构成深度残差网络的基本模块 ResNet的大部分各种结构是1层conv+4个block+1层fc 需要注意的是最后

    2024年02月02日
    浏览(46)
  • torchvision中的标准ResNet50网络结构

    注:仅用以记录学习 打印出来的网络结构如下: 结构: 修改最后一层(fc层)代码: 用于特定的分类任务,其中最后一层全连接层的输出类别数量被指定为输入参数

    2024年02月04日
    浏览(43)
  • YOLOX网络结构详解

    论文名称:YOLOX: Exceeding YOLO Series in 2021 论文下载地址:https://arxiv.org/abs/2107.08430 论文对应源码地址:https://github.com/Megvii-BaseDetection/YOLOX 在bilibili上的视频讲解:https://www.bilibili.com/video/BV1JW4y1k76c 在之前文章中我们已经聊过YOLO v5了,今天我们再来聊聊YOLOX。YOLOX是旷视科技在2

    2024年02月04日
    浏览(41)
  • VGG网络结构详解

    参考论文:Very Deep Convolutional Networks for Large-Scale Image Recognition 论文中提供的6种网络配置: 整个网络有5个vgg-block块和5个maxpool层逐个相连,然后进入FC层,直到最后1000个softmax输出。多种VGG网络设计都很统一,都有相同的224×224×3的input层+5个maxpool层+3层fc全连接层,区别在于中

    2024年02月07日
    浏览(37)
  • 红外小目标:DNANet网络结构与模型搭建

    Target Detection) 目标很小 由于成像距离长,红外目标一般都很小,在图像中从一个像素到几十个像素不等。 昏暗 红外目标通常信杂比较低,容易陷入强噪声和杂波背景中。 无形状 红外小目标形状特征有限。 可变 不同场景下红外目标的大小和形状变化很大。 不能使用为通用

    2024年02月07日
    浏览(36)
  • PointNet++详解(二):网络结构解析

    如有错误,恳请指出。 在之前对PointNet与PointNet++网络进行了介绍,接下来是对其代码的解析。 1. 论文阅读笔记 | 三维目标检测——PointNet 2. 论文阅读笔记 | 三维目标检测——PointNet++ 参考的github项目为:https://github.com/yanx27/Pointnet_Pointnet2_pytorch 这篇博客的内容主要是将PointN

    2024年02月02日
    浏览(50)
  • AlexNet网络结构的实现和详解

    AlexNet网络结构特点: 1.首次利用GPU进行网络加速训练 (GPU处理速度是CPU的20-50倍) 2.使用了ReLU激活函数,而不是传统的Sigmoid激活函数(缺点:求导麻烦、容易梯度消失)以及tanh激活函数  3.使用了LRN(Local Response Normalization)局部相应归一化 4.在全连接层的前两层中使用了Dr

    2024年02月05日
    浏览(39)
  • Swin-Transformer网络结构详解

    Swin Transformer是2021年微软研究院发表在ICCV上的一篇文章,并且已经获得 ICCV 2021 best paper 的荣誉称号。Swin Transformer网络是Transformer模型在视觉领域的又一次碰撞。该论文一经发表就已在多项视觉任务中霸榜。该论文是在2021年3月发表的,现在是2021年11月了,根据官方提供的信息

    2024年02月04日
    浏览(39)
  • YOLOv8详解 【网络结构+代码+实操】

    🚀🚀🚀 目标检测——Yolo系列(YOLOv1/2/v3/4/5/x/6/7) ✨✨✨ YOLOv8改进——引入可变形卷积DCNv3 YOLOv8是目前YOLO系列算法中最新推出的检测算法,YOLOv8可以完成检测、分类、分割任务,其检测和分割网络结构图如下。 YOLOv8 算法的核心特性和改动可以归结为如下: 提供了一个全新

    2024年02月01日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包