IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 机器学习笔记:ResNet 及残差连接 -> 正文阅读

[人工智能]机器学习笔记:ResNet 及残差连接

1 为什么会有ResNet?

? ? ? ?自从深度神经网络在ImageNet大放异彩之后,后来问世的深度神经网络就朝着网络层数越来越深的方向发展。直觉上我们不难得出结论:增加网络深度后,网络可以进行更加复杂的特征提取,因此更深的模型可以取得更好的结果。

????????但事实并非如此,人们发现随着网络深度的增加,模型精度并不总是提升,并且这个问题显然不是由过拟合(overfitting)造成的,因为网络加深后不仅测试误差变高了,它的训练误差竟然也变高了。【注:过拟合应该是训练误差很小,但是测试误差很大;这里训练误差都很大,说明不是过拟合的问题】

????????这可能是因为更深的网络会伴随梯度消失/爆炸问题,从而阻碍网络的收敛。这种加深网络深度但网络性能却下降的现象被称为退化问题(degradation problem)。

? ? ? ? 从上图我们可以看出,当传统神经网络的层数从20增加为56时,网络的训练误差和测试误差均出现了明显的增长,也就是说,网络的性能随着深度的增加出现了明显的退化。

????????ResNet就是为了解决这种退化问题而诞生的。

2 ResNet原理

????????随着网络层数的增加,梯度爆炸和梯度消失问题严重制约了神经网络的性能,研究人员通过提出包括Batch normalization在内的方法,已经在一定程度上缓解了这个问题,但依然不足以满足需求。

? ? ? ? ResNet提出了使用恒等映射(Identity mapping)来解决这个问题。

????????问题解决的标志是:增加网络层数,但训练误差不增加。

????????那怎么构建恒等映射呢?

????????简单地说,原先的网络输入x,希望输出H(x)。

????????现在我们改一改,我们令H(x)=F(x)+x,那么我们的网络就只需要学习输出一个残差F(x)=H(x)-x。

? ? ? ? ResNet作者提出,学习残差F(x)=H(x)-x会比直接学习原始特征H(x)简单的多。

?3 ResNet(各变体)网络结构

????????ResNet有很多变体,比较著名的有五种主要形式:Res18,Res34,Res50,Res101,Res152。

????????

? ? ? ? 如上图所示,ResNet及其变体主要包括三个主要部分:输入部分(input)+中间卷积(stage1~stage4)+输出部分(output)

3.1 网络整体结构

? ? ? ? 以ResNet18为例:?

class ResNet(nn.Module):
    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)
        #卷积部分
        #ResNet18和其他的ResNet网络的区别主要就在这四层里面

        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        #输出部分

        return x

# 生成一个res18网络(看是否有预训练的参数)
def resnet18(pretrained=False, **kwargs):
    model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
    if pretrained:
        model.load_state_dict(model_zoo.load_url(model_urls['resnet18']))
    return model

?3.2 网络输入部分

        self.conv1 = nn.Conv2d(
            3, 
            64, 
            kernel_size=7, 
            stride=2, 
            padding=3, 
            bias=False)
'''
size=7*7,stride=2的卷积核
'''
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(
            kernel_size=3, 
            stride=2, 
            padding=1
'''
size=3x3, stride=2的最大池化
'''
            )

pytorch 笔记:torch.nn.Conv2d_UQI-LIUWJ的博客-CSDN博客pytorch笔记:torch.nn.MaxPool2d_UQI-LIUWJ的博客-CSDN博客中,我们说到,经过Conv2d和MaxPool2d之后的channel数为:

????????在ResNet中,输入部分是一个224x224的图像,经过输入部分之后,变成了56x56大小的特征图,极大减少了存储所需大小。

?

3.3 中间卷积部分

????????中间卷积部分主要是下图中的蓝框部分,通过3*3卷积的堆叠来实现信息的提取。红框中的[2, 2, 2, 2]和[3, 4, 6, 3]等则代表了bolck的重复堆叠次数。?

preview

? ? ? ? ?红框前面的是卷积核的大小,以及输出的channel数

????????刚刚我们调用的resnet18( )函数中有一句

model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)

????????这里的[2, 2, 2, 2]与图中红框是一致的,如果你将这行代码改为 ResNet(BasicBlock, [3, 4, 6, 3], **kwargs), 那你就会得到一个res34网络。

3.4? 残差块

? ? ? ??下面我们来具体看一下一个残差块是怎么实现的。

????????如下图所示的basic-block,输入数据分成两条路,一条路经过两个3*3卷积,另一条路直接短接,二者相加经过relu输出,十分简单。

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(BasicBlock, self).__init__()
        self.conv1 = conv3x3(
            inplanes, 
            planes, 
            stride)
        self.bn1 = nn.BatchNorm2d(planes)
        self.relu = nn.ReLU(inplace=True)

        self.conv2 = conv3x3(
            planes, 
            planes)
        self.bn2 = nn.BatchNorm2d(planes)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity
        out = self.relu(out)

        return out

?

preview

?3.5 输出部分

?????????网络输出部分很简单,通过全局自适应平滑池化,把所有的特征图拉成1*1。

????????对于res18来说,就是1x512x7x7 的输入数据拉成 1x512x1x1,然后接全连接层输出,输出节点个数与预测类别个数一致。

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512 * block.expansion, num_classes)

4 bottleneck 结构?

????????ResNet50起,就采用Bottleneck结构,主要是引入1x1卷积。

????????我们来看一下这里的1x1卷积有什么作用:

  • 对通道数进行升维和降维(跨通道信息整合),实现了多个特征图的线性组合,同时保持了原有的特征图大小;
  • 相比于其他尺寸的卷积核,可以极大地降低运算复杂度;
  • 如果使用两个3x3卷积堆叠,只有一个relu,但使用1x1卷积就会有两个relu,引入了更多的非线性映射;

?

????????我们来计算一下1*1卷积的计算量优势:首先看上图右边的bottleneck结构,对于256维的输入特征,参数数目:1x1x256x64+3x3x64x64+1x1x64x256=69632.

????????如果同样的输入输出维度但不使用1x1卷积,而使用两个3x3卷积的话,参数数目为(3x3x256x256)x2=1179648。

????????简单计算下就知道了,使用了1x1卷积的bottleneck将计算量简化为原有的5.9%,收益超高。

?参考文献:ResNet及其变种的结构梳理、有效性分析与代码解读 - 知乎 (zhihu.com)

?

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-08-31 15:27:21  更:2021-08-31 15:27:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 17:52:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码