首页 > 其他分享 >论文阅读(二)Deep Residual Learning for Image Recognition

论文阅读(二)Deep Residual Learning for Image Recognition

时间:2023-02-04 15:23:11浏览次数:60  
标签:nn Image Residual Deep 残差 channels 维度 output self

目录


  • 提出ResNet模型,深度残差神经网络模型。

  • 论文发表于2015年,2016年CVPR最佳论文

  • 作者何恺明、孙剑、张翔宇、任少卿(微软亚洲研究院)

参考:

ResNet为什么能解决网络退化问题 - 同济子豪兄 - bilibili

7.6. 残差网络(ResNet) — 动手学深度学习 2.0.0 documentation (d2l.ai)

解决了什么问题

针对很深的神经网络模型很难训练这一问题,即网络退化问题(degradation)从实验数据可以看到,仅仅堆叠神经网络的层数形成的网络模型,在测试集上的表现还如不较少层的模型。

image-20230203213711276

使用残差神经网络后的情况如下

image-20230204103534565

采用了什么方法

identity mapping:恒等映射

弧线为shortcut connection

image-20230204104757561

为什么能起作用

直观理解

新加入的层可能会对模型的学习产生负面效果,也可能会产生正面效果,而使用残差网络,若新加入的层产生了负面效果就回退到原来的层,相当于直接忽略掉这一层的作用。用于解决梯度消失和梯度下降问题,使更深层的网络变得容易学习。

  • 传统线性结构网络难以拟合 “恒等映射”,什么都不做有时候很重要,skip connections可以让模型自行选择要不要更新

  • 恒等映射这一路的梯度为1,防止梯度消失

    在反向传播层层计算梯度的时候,每层计算的导数都很小,链式法则相乘之后梯度就变得几乎为0,即梯度消失,导致参数不能更新,网络停止学习。

  • 相当于多个网络的集成(类似dropout)

    image-20230204113053573

    而且剪掉右图中的几条路径并不影响整个网络的表现

  • 没什么好解释的,尝试出来的结果好就行了(炼丹嘛,不寒掺)

网络架构

在ImageNet数据集上,应用了152层的深度残差神经网络,下图为34层Resnet

image-20230204131152297

image-20230204110353745

  • 输入图片

    3通道224x224

  • 每一个卷积层之后应用批量归一化(batch normalization),激活函数之前

  • 当输入和输出维度相同时可以直接走shortcut connections(图中实线)

    当输入维度和输出维度不同时,有两种方法:1、添0 2、使用1x1的卷积改变通道数

  • 注意两个conv的接口处要完成两项任务,这两项任务是在定义网络时通过stride和padding实现的

    • 图片长宽尺寸变为原来的1/2
    • 通道数变为原来的2倍

复现网络模型

整体结构

整个残差网络模型的构建包括3部分

  • 残差块

    2个卷积层以及批量归一化和激活函数,包括对加x的处理

  • 由若干残差块构成的模块

    模块中的第一个残差块需要对输入进行额外处理,中间残差块保持数据维度不变

  • 其他部分

    预处理卷积层和全连接层

image-20230204135340013

定义残差块

一个残差块由两个卷积层构成,残差块中没有池化,但是有批量归一化BN和激活函数,其结构如下

image-20230204133106814

注意:BN先于ReLU使用,这个图中没有表现出来

残差块有两种类型

  1. 大层次内部的残差块
  2. 大层次交接处第一个残差块
image-20230204133642480

内部残差块输入维度和输出维度完全相同,且要加上x的维度和第二个卷积层输出维度完全相同,直接相加即可

交接处第一个残差块相比于内部残差块要多完成2项任务

  1. 改变输入维度,尺寸变为原来的1/2,通道数变为原来的2倍
  2. 输入x的维度与第二个卷积层输出的维度不同,要先对x使用1x1的卷积变换尺寸和通道数才能与第二个卷积层的输出相加
image-20230204134052180

pytorch实现代码如下

import torch
from torch import nn


class Residual(nn.Module):
    def __init__(self, input_channels, output_channels, use_1x1conv=False, strides=1):
        super(Residual, self).__init__()
        # conv1: 实例化Residual时如果没指定默认s=1,可以指定s=2,根据公式使得图片尺寸变为原来的1/2
        self.conv1 = nn.Conv2d(input_channels, output_channels, kernel_size=3, padding=1, stride=strides)
        # conv2: stride默认为1,padding=1,根据公式conv2输出和输入维度完全相同
        self.conv2 = nn.Conv2d(output_channels, output_channels, kernel_size=3, padding=1)
        # 实例化此类时如果指定use_1x1conv,则表明本层是两大层交接处,需要改变x的尺寸和通道数保证和输出维度相同
        if use_1x1conv:
            self.conv3 = nn.Conv2d(input_channels, output_channels, kernel_size=1, stride=strides)
        else:
            self.conv3 = None
        self.bn1 = nn.BatchNorm2d(output_channels)
        self.bn2 = nn.BatchNorm2d(output_channels)

    def forward(self, x):
        y = nn.ReLU(self.bn1(self.conv1(x)))
        y = self.bn2(self.conv2(y))
        if self.conv3:
            x = self.conv3(x)
        y = y + x
        return nn.ReLU(y)

定义模块

实现代码如下

def resnet_block(input_channels, output_channels, num_residuals, first_block=False):
    blk = []  # blk为网络中模块的集合
    for i in range(num_residuals):
        if i == 0 and not first_block:  # 模块中的第一个残差块,第一个模块的第一个残差块不需要对输入做变换处理
            blk.append(Residual(input_channels, output_channels, use_1x1conv=True, strides=2))
        else:  # 模块中的中间残差块保持数据维度不变,注意此处输入输出通道数均为output_channels
            blk.append(Residual(output_channels, output_channels))  
    return blk


# 以下定义了论文中提到的34层残差网络
# b1为预处理卷积和池化操作
b1 = nn.Sequential(
    nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),
    nn.BatchNorm2d(64), nn.ReLU(),
    nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
b2 = nn.Sequential(*resnet_block(64, 64, 6, first_block=True))  # 第一个模块
b3 = nn.Sequential(*resnet_block(64, 128, 8))
b4 = nn.Sequential(*resnet_block(128, 256, 12))
b5 = nn.Sequential(*resnet_block(256, 512, 6))

net = nn.Sequential(b1, b2, b3, b4, b5,
                    nn.AdaptiveAvgPool2d((1, 1)),
                    nn.Flatten(), nn.Linear(512, 1000))

测试

X = torch.rand(size=(1, 3, 224, 224))
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__, 'output shape:\t', X.shape)

运行结果

Sequential output shape:	 torch.Size([1, 64, 56, 56])
Sequential output shape:	 torch.Size([1, 64, 56, 56])
Sequential output shape:	 torch.Size([1, 128, 28, 28])
Sequential output shape:	 torch.Size([1, 256, 14, 14])
Sequential output shape:	 torch.Size([1, 512, 7, 7])
AdaptiveAvgPool2d output shape:	 torch.Size([1, 512, 1, 1])
Flatten output shape:	 torch.Size([1, 512])
Linear output shape:	 torch.Size([1, 1000])

标签:nn,Image,Residual,Deep,残差,channels,维度,output,self
From: https://www.cnblogs.com/dctwan/p/17091545.html

相关文章