ResNet《Deep Residual Learning for Image Recognition》

ResNet论文学习

  • 引言
  • Deep Residual Learning 深度残差学习
    • Residual Learning 残差学习
    • Identity Mapping by Shortcuts 通过捷径来恒等映射
    • 网络结构
    • Plain Network
    • Residual Network
    • 实现细节
  • 实验
  • 总结
  • 代码复现
    • Building block
    • Bottleneck
    • Resnet 18
    • Resnet 34
    • Resnet 50

引言

深度网络自然地以端到端的多层方式集成低/中/高级特征和分类器,特征的“级别”可通过堆叠层的数量来丰富

随着网络层数加深,提取的特征越强,但是

网络层数越深就一定性能越好吗?答案是否定的

深层网络存在的问题

  • 梯度消失/梯度爆炸
    这个问题已经通过 normalized initialization 和 intermediate normalization layers 得到了很大程度的解决,这使得具有数十层的网络能够开始收敛随机梯度下降(SGD)与反向传播

  • 网络退化
    随着网络层数的增加,准确率趋于饱和,然后迅速下降。这说明,并不是所有的网络结构都同样容易优化。

为了解释这一现象,作者考虑了一个较浅的网络结构及其对应的更深版本,后者在前者的基础上增加了更多的层。

理论上,对于这个更深的网络,可以构造一个解:新增加的层执行 identity mapping (即直接将输入传递到输出),而其他层则复制自较浅网络中已经学习到的层(就是把较浅的网络搬过来)。这样构造出的解应该能使得更深的网络不会比其较浅的对应网络有更高的训练误差。

然而,实验显示,目前使用的优化算法似乎无法找到与这个构造解相当好或更好的解,或者在合理的时间内无法找到这样的解。这意味着,尽管理论上更深的网络不应该有更差的性能,但在实际优化过程中却遇到了困难。这也是引入残差学习(ResNet)的初衷之一,即通过学习残差来缓解这种优化难题。

为了解决深度神经网络训练中的退化问题,作者提出了深度残差学习框架。

在这个框架中,不再期望网络的几层直接拟合一个 underlying mapping,而是让这些层去拟合一个残差映射(residual mapping)。

具体而言,

假设 underlying mapping 是 H ( x ) \mathcal{H}(x) H(x),那么让堆叠的非线性层去拟合另一个映射 F : = H ( x ) − x \mathcal{F}:=\mathcal{H}(x)-x F:=H(x)x,那么原始的映射就变成了 F ( x ) + x \mathcal{F}(x)+x F(x)+x

假设优化残差映射 (residual mapping) 比优化原始的、未引用的映射更容易。

在这里插入图片描述

图中展示了残差学习的一个基本构建块。右侧的分支直接将输入 x x x 通过一个“快捷连接”传递到输出,这就是所谓的恒等映射(identity mapping)。

F ( x ) \mathcal{F}(x) F(x) 表示的是一个由 权重层和非线性激活函数(例如ReLU)组成的映射,

其目的是学习输入 x x x 和输出 H ( x ) \mathcal{H}(x) H(x) 之间的残差,即 F ( x ) : = H ( x ) − x \mathcal{F}(x) := \mathcal{H}(x) - x F(x):=H(x)x

图中所示的构建块的输出是通过将 F ( x ) \mathcal{F}(x) F(x) x x x 相加得到的,即 F ( x ) + x \mathcal{F}(x) + x F(x)+x,这里的 F ( x ) \mathcal{F}(x) F(x) 是通过两个权重层和非线性激活函数ReLU构成的子网络来学习的。

这样设计的好处是,如果 F ( x ) \mathcal{F}(x) F(x) 的理想输出是0,即不存在残差,那么 shortcut connection 可以使得恒等映射成为可能,即直接输出 x x x,这对于网络训练的稳定性和加速有很大帮助。

Deep Residual Learning 深度残差学习

Residual Learning 残差学习

即使理论上更深的网络应该不会比较浅的网络性能更差,因为更深的网络至少可以通过学习 恒等映射 来达到和较浅网络一样的性能。

但在实际中,传统的优化算法(solvers)在训练时 很难通过多个非线性层来近似恒等映射

如果恒等映射是最优的,solvers 可以简单地 将多个非线性层的权重推向零(就是 F ( x ) \mathcal{F}(x) F(x) 这个映射趋于 0 0 0) 来近似恒等映射,而不是去学习一个复杂的映射关系

Identity Mapping by Shortcuts 通过捷径来恒等映射

作者对若干个堆叠层应用残差学习,
在这里插入图片描述

具体而言,一个 building block 被定义为:
y = F ( x , { w i } ) + x \textbf{y}=\mathcal{F}(\textbf{x},\{w_i\})+\textbf{x} y=F(x,{wi})+x 其中, x , y \textbf{x},\textbf{y} x,y 是输入和输出向量, F ( x , { w i } ) \mathcal{F}(\textbf{x},\{w_i\}) F(x,{wi}) 代表被学习的残差映射

就像图 2,有 2 个层,那么 F = W 2 σ ( W 1 x ) \mathcal{F}=W_2\sigma(W_1 \textbf{x}) F=W2σ(W1x), 其中 σ \sigma σ 代表 ReLU 函数,为了简化符号,省略了 bias

F + x \mathcal{F}+\textbf{x} F+x 操作 是通过一个 shortcut connections 和 element-wise 加法来执行

加法执行后采用第二个 ReLU 函数应用非线性,如图 2

其中 x \textbf{x} x F \mathcal{F} F 的维度必须一致,如果不一致,

可以执行一个线性投影 W s W_s Ws 匹配维度然后 shortcut connections: y = F ( x , { w i } ) + W s x \textbf{y}=\mathcal{F}(\textbf{x},\{w_i\})+W_s \textbf{x} y=F(x,{wi})+Wsx

残差函数 F \mathcal{F} F 的形式是灵活的,本文的实验中,涉及有 2 层或 3 层的残差函数 F \mathcal{F} F ,但是只有 1 层的残差函数 F \mathcal{F} F 近似于线性层 y = W 1 x + x \textbf{y}=W_1\textbf{x}+\textbf{x} y=W1x+x,作者并未观察到其优越性。

尽管为了简化表述,这一部分的描述和公式是基于全连接层的情形,但是这些概念和方法也同样适用于卷积层。

在实际的深度残差网络(ResNet)中,作者主要是在讨论卷积层的应用,因为卷积层是现代卷积神经网络中的核心组件。

函数 F ( x , { w i } ) \mathcal{F}(\textbf{x},\{w_i\}) F(x,{wi}) 可以表示多个卷积层,逐个通道地在两个特征图上执行元素加法。

网络结构

请添加图片描述

Plain Network

plain baselines 主要受 VGG nets 启发,卷积层大多具有3×3滤波器,并遵循两个简单的设计规则:

(i) 对于相同的输出特征图大小,各层具有相同数量的滤波器;

(ii) 如果特征图大小减半,则滤波器的数量增加一倍,以保持每层的时间复杂度。

通过步长为 2 的卷积层直接执行下采样。网络以一个全局平均池化层和一个带有 softmax 的 1000 路全连接层结束。图3(中间部分)加权层总数为34层。

作者的模型比VGG网络具有更少的卷积滤波器和更低的复杂度。

Residual Network

在 Plain Network 的基础上,插入 shortcut connections 将网络转换为对应的残差版本。

当输入和输出维度相同时 (图3中的实线 shortcuts),可以直接使用 y = F ( x , { w i } ) + x \textbf{y}=\mathcal{F}(\textbf{x},\{w_i\})+\textbf{x} y=F(x,{wi})+x

当维度增加时 (图3中的虚线 shortcuts),作者考虑两种选择:

(A) shortcut 仍然执行恒等映射,增加维度时填充额外的零项。这个选项不引入额外的参数;

(B) y = F ( x , { w i } ) + W s x \textbf{y}=\mathcal{F}(\textbf{x},\{w_i\})+W_s \textbf{x} y=F(x,{wi})+Wsx 中的投影用于匹配维度 (通过1×1卷积完成)。

对于这两个选项,当 shortcuts 跨越不同大小的特征图时,卷积核的步长为 2。

实现细节

对 ImageNet 的实现,随机采样图像的短边,以进行尺度增强。

从图像或其水平翻转中随机采样224×224裁剪,并减去每像素平均值。

使用标准颜色增强。在每次卷积之后在激活函数之前采用批归一化(BN)。

初始化权值,并从头开始训练所有的 plain/residual 网络。

使用SGD的小批量大小为256。学习率从0.1开始,当误差趋于平稳时除以10,模型的训练次数可达60 × 104次。

使用0.0001的权重衰减和0.9的动量。 没有使用dropout

在测试中,采用标准的 10 种作物测试进行比较研究。

为了获得最佳结果,采用了全卷积形式,并在多个尺度上平均得分(图像被调整大小)。

实验

首先评估了18层和34层的普通网。34层平面网如图3(中)所示。

18层的平面网也是类似的形式。参见表1了解详细的体系结构。

在这里插入图片描述
上表是用于测试 ImageNet 的架构。方括号中显示了构建块(参见图5),并显示了堆叠块的数量。下采样由conv3_1、conv4_1和conv5_1进行,步长为2。

在这里插入图片描述
在这里插入图片描述

总结

残差连接最核心的地方是 Identity Mapping by Shortcuts

就是说 resnet 是结合了 identify mappingshortcut connection

shortcut connection 是那些跳过一个或多个层的连接。在本文中, shortcut connection 只是执行 identify mapping,其输出被添加到堆叠层的输出中。

identify mapping 连接既不增加额外的参数,也不增加计算复杂度。整个网络仍然可以通过反向传播的SGD进行端到端训练。

下面这个残差块中,旁边的分支首先是一个 shortcut connection,而在这个 shortcut connection 之上,执行了 identify mapping 将输入恒等映射到输出端,并与 underlying mapping 进行相加
在这里插入图片描述

代码复现

Building block

import torch
import torch.nn as nn
import torch.nn.functional as Fclass BuildingBlock(nn.Module):def __init__(self,inchannel,outchannel,stride=1):super(BuildingBlock,self).__init__()self.left = nn.Sequential(nn.Conv2d(inchannel,outchannel,kernel_size=3,stride=stride,padding=1,bias=False),nn.BatchNorm2d(outchannel),nn.ReLU(inplace=True),nn.Conv2d(outchannel,outchannel,kernel_size=3,stride=1,padding=1,bias=False),nn.BatchNorm2d(outchannel))self.shortcut = nn.Sequential()# 卷积核步长输入和输出通道不同时需要进行下采样if stride != 1 or inchannel != outchannel:self.shortcut = nn.Sequential(nn.Conv2d(inchannel,outchannel,kernel_size=1,stride=stride,bias=False),nn.BatchNorm2d(outchannel)) def forward(self,x):out = self.left(x)out += self.shortcut(x)out = F.relu(out)return out
  1. 不带参数的 nn.Sequential() 会返回什么?

    不带参数的 nn.Sequential() 会返回一个空的顺序容器。这意味着如果你通过这个空的 nn.Sequential() 容器传递一个张量,它将直接返回这个张量而不做任何处理。在上面代码中,self.shortcut 初始化为一个空的 nn.Sequential(),这意味着如果没有进行下采样(即 stride == 1 并且 inchannel == outchannel),那么 shortcut 分支将简单地返回输入张量 x

  2. nn.Conv2d(inchannel, outchannel, kernel_size=3, stride=stride, padding=1, bias=False) 中等号右边的 stride 来自哪里?

    在这段代码中,strideBuildingBlock 类的构造函数的一个参数,默认值为 1。当你创建一个 BuildingBlock 实例时,你可以指定 stride 的值,这个值将用于卷积层中的步长(stride 参数)。nn.Conv2d(inchannel, outchannel, kernel_size=3, stride=stride, padding=1, bias=False) 中,等号右边的 stride 来自于 BuildingBlock 类的构造函数 def __init__(self, inchannel, outchannel, stride=1) 中的 stride 参数。

  3. if stride != 1 or inchannel != outchannel: 时,是如何实现下采样的?

    stride != 1inchannel != outchannel 时,需要对输入进行下采样以匹配主分支 (left) 的输出维度。这是通过 self.shortcut 分支实现的,它包含一个步长为 stride1x1 卷积层和一个批量归一化层。1x1 卷积用于改变通道数(如果需要),而步长 stride 用于减小特征图的空间尺寸(宽度和高度)。这样,shortcut 分支的输出就可以与主分支的输出相加了。

  4. 上面的代码中,已经在def __init__(self, inchannel, outchannel, stride=1)中指定了stride=1,为什么还需要对stride != 1进行判断?

    当创建 BuildingBlock 实例时,可以指定 stride 参数的值,包括 stride != 1 的情况。因此,需要对 stride != 1 进行判断,以决定是否需要进行下采样以匹配主分支和残差分支的输出尺寸。

在ResNet的基本块(building block)中,对于输入和输出的维度不同的情况,会使用 padding=1 来使维度相同,这样可以方便进行残差连接。

这样做的目的是为了确保在每个基本块中,输入和输出的尺寸相同,以便能够将它们相加。

nn.Conv2d() 方法用于创建一个二维卷积层。其中,bias 是一个布尔值参数,用于指定是否在卷积操作中使用偏置项(bias)。如果设置为 True,则会添加一个可学习的偏置项到输出;如果设置为 False,则不会添加偏置项。默认值为 True。

偏置(bias)是一个可学习的参数,用于调整每个卷积核在卷积操作中的作用偏置的作用类似于线性回归中的截距,它允许模型学习在没有输入时的输出值。具体地说,在卷积操作中,偏置被添加到每个卷积核的输出中,然后通过激活函数进行处理,以产生最终的特征图

偏置的引入可以增加模型的灵活性,使其能够更好地拟合训练数据。通过学习适当的偏置值,模型可以更好地捕获数据中的偏移和非线性关系。

Bottleneck

class Bottleneck(nn.Module):def __init__(self, inchannel, outchannel, stride=1):super(Bottleneck, self).__init__()self.left = nn.Sequential(nn.Conv2d(inchannel, int(outchannel / 4), kernel_size=1, stride=stride, padding=0, bias=False),nn.BatchNorm2d(int(outchannel / 4)),nn.ReLU(inplace=True),nn.Conv2d(int(outchannel / 4), int(outchannel / 4), kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(int(outchannel / 4)),nn.ReLU(inplace=True),nn.Conv2d(int(outchannel / 4), outchannel, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(outchannel),)self.shortcut = nn.Sequential()if stride != 1 or inchannel != outchannel:self.shortcut = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(outchannel))def forward(self, x):out = self.left(x)y = self.shortcut(x)out += self.shortcut(x)out = F.relu(out)
  • 为什么self.left 结构中,三个卷积层除了第一层的输入通道数和第三层的输出通道数以外,全部是outchannel / 4

在 ResNet 的 Bottleneck 结构中,前两个卷积层的输出通道数被设为 int(outchannel / 4) 是为了减少计算量和参数数量,同时保持网络的表达能力。

Bottleneck 结构由三个卷积层组成:第一个和第三个卷积层使用 1x1 卷积核,主要用于改变通道数;第二个卷积层使用 3x3 卷积核,负责提取空间特征。在这种设计中,第一个 1x1 卷积层将输入通道数减少到 outchannel / 4,这样第二个 3x3 卷积层就可以在较小的通道数上操作,从而减少计算量。最后,第三个 1x1 卷积层再次将通道数增加到 outchannel,恢复到原来的维度。

通过这种方式,Bottleneck 结构能够在减少计算量的同时,保持足够的表达能力,这是 ResNet 在深层网络中有效减少参数数量和计算复杂度的关键设计之一。

  • 为什么三个卷积层的padding分别是0,1,0?

在这个 Bottleneck 结构中,三个卷积层的 padding 设置是为了控制特征图的空间尺寸:

  1. 第一个卷积层使用 kernel_size=1padding=0。因为 1x1 卷积不会改变特征图的空间尺寸,所以不需要填充(padding=0)。

  2. 第二个卷积层使用 kernel_size=3padding=13x3 卷积会减小特征图的尺寸,除非使用填充。为了保持特征图的空间尺寸不变,需要使用 padding=1(当 stride=1 时)。这样,卷积操作后,特征图的宽度和高度保持不变。

  3. 第三个卷积层再次使用 kernel_size=1padding=0,原因同第一个卷积层一样,1x1 卷积不改变特征图的空间尺寸,所以不需要填充。

通过这种设置,Bottleneck 结构可以在不改变特征图空间尺寸的情况下,有效地进行特征提取和通道数的变换。

Resnet 18

在这里插入图片描述

import torch
import torch.nn as nn
import torch.nn.functional as Fdef conv3x3(in_channels, out_channels, stride=1):return nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)def conv1x1(in_channels, out_channels, stride=1):return nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False)class BasicBlock(nn.Module):expansion = 1def __init__(self, in_channels, out_channels, stride=1, downsample=None):super(BasicBlock, self).__init__()self.conv1 = conv3x3(in_channels, out_channels, stride)self.bn1 = nn.BatchNorm2d(out_channels)self.relu = nn.ReLU(inplace=True)self.conv2 = conv3x3(out_channels, out_channels)self.bn2 = nn.BatchNorm2d(out_channels)self.downsample = downsampledef forward(self, x):identity = xout = 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 += identityout = self.relu(out)return outclass ResNet(nn.Module):def __init__(self, block, layers, num_classes=1000):super(ResNet, self).__init__()self.in_channels = 64self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = self._make_layer(block, 64, layers[0])self.layer2 = self._make_layer(block, 128, layers[1], stride=2)self.layer3 = self._make_layer(block, 256, layers[2], stride=2)self.layer4 = self._make_layer(block, 512, layers[3], stride=2)self.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(512 * block.expansion, num_classes)def _make_layer(self, block, out_channels, blocks, stride=1):downsample = Noneif stride != 1 or self.in_channels != out_channels * block.expansion:downsample = nn.Sequential(conv1x1(self.in_channels, out_channels * block.expansion, stride),nn.BatchNorm2d(out_channels * block.expansion),)layers = []layers.append(block(self.in_channels, out_channels, stride, downsample))self.in_channels = out_channels * block.expansionfor _ in range(1, blocks):layers.append(block(self.in_channels, out_channels))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)x = self.avgpool(x)x = torch.flatten(x, 1)x = self.fc(x)return xdef resnet18(num_classes=1000):return ResNet(BasicBlock, [2, 2, 2, 2], num_classes)# 示例化模型
model = resnet18(num_classes=1000)

下面这个代码实现之所以一些参数和论文网络结构原图不一样,是因为这是一个简化/修改版的 ResNet18 结构。例如,在处理较小的图像数据集时(比如 CIFAR-10,其图像尺寸为 32x32),可能不需要在一开始就迅速减小空间尺寸,这种情况下使用较小的卷积核和不使用步长为 2 的操作可能更为合适。

在原始的 ResNet 结构中,较大的卷积核和步长用于减小输出特征图的尺寸,同时增加感受野,使得网络可以捕捉到更大范围的输入信息。这在处理较大的图像(如 ImageNet 数据集中的图像,尺寸通常为 224x224)时是有优势的。因此,如果代码是针对不同尺寸的数据集设计的,那么这种调整是合理的。

'''----------ResNet18----------'''
class ResNet_18(nn.Module):def __init__(self, ResidualBlock, num_classes=10):super(ResNet_18, self).__init__()self.inchannel = 64self.conv1 = nn.Sequential(nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(64),nn.ReLU(),)self.layer1 = self.make_layer(ResidualBlock, 64, 2, stride=1)self.layer2 = self.make_layer(ResidualBlock, 128, 2, stride=2)self.layer3 = self.make_layer(ResidualBlock, 256, 2, stride=2)self.layer4 = self.make_layer(ResidualBlock, 512, 2, stride=2)self.fc = nn.Linear(512, num_classes)def make_layer(self, block, channels, num_blocks, stride):strides = [stride] + [1] * (num_blocks - 1)  # strides=[1,1]layers = []for stride in strides:layers.append(block(self.inchannel, channels, stride))self.inchannel = channelsreturn nn.Sequential(*layers)def forward(self, x):  # 3*32*32 3是通道数,32*32是特征图尺寸out = self.conv1(x)  # 64*32*32 卷积层conv1输出通道是64#layer1的输出通道是64,且strile=1,所以输出的通道数不改变out = self.layer1(out)  # 64*32*32 #layer2 strile=2,特征图尺寸宽度和高度各减小一半,通道数翻倍out = self.layer2(out)  # 128*16*16  out = self.layer3(out)  # 256*8*8 layer3同理out = self.layer4(out)  # 512*4*4 layer4同理out = F.avg_pool2d(out, 4)  # 512*1*1out = out.view(out.size(0), -1)  # 512out = self.fc(out)return out
  1. 为什么 self.inchannel = 64

    在 ResNet18 的初始层中,卷积层 conv1 将输入的 RGB 图像(3 通道)转换为 64 通道的特征图。因此,设置 self.inchannel = 64 是为了记录当前特征图的通道数,以便在构建后续的残差层时使用。这个值会随着网络的深入而更新,以匹配不同残差层的输入通道数。

  2. make_layer 中的参数 channels 是输入通道还是输出通道?

    make_layer 方法中,参数 channels 指的是输出通道数。每个残差块(ResidualBlock)的输出通道数由这个参数确定,而输入通道数则由 self.inchannel 维护和提供。

  3. 详细地解释 strides = [stride] + [1] * (num_blocks - 1)*layers

    • strides = [stride] + [1] * (num_blocks - 1):这行代码用于生成一个列表,其中第一个元素是 stride(可以是 1 或 2),其余元素都是 1。列表的长度等于 num_blocks。这是因为在每个残差层中,只有第一个残差块可能会改变特征图的尺寸(通过 stride 控制),而后续的残差块则保持特征图尺寸不变(即 stride=1)。例如,对于 self.layer2strides 将为 [2, 1],意味着第一个残差块将特征图尺寸减半,而第二个残差块保持尺寸不变。

    • *layers:这是 Python 中的解包操作符,用于将 layers 列表中的元素作为独立的参数传递给 nn.Sequential。这样,nn.Sequential 可以接收一个由多个残差块组成的序列,从而构建出一个完整的残差层。在这个例子中,layers 列表包含了一个残差层中的所有残差块。

在PyTorch中,avg_pool2d函数用于对二维信号(例如图像)进行平均池化操作。函数的调用形式一般是:

torch.nn.functional.avg_pool2d(input, kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True, divisor_override=None)

其中,input是输入的四维张量,形状为(N, C, H, W),分别代表批大小、通道数、高度和宽度。

在你提到的avg_pool2d(out, 4)中:

  • out:输入的四维张量,即要进行池化的数据。
  • 4kernel_size参数,表示池化窗口的大小。如果是一个单一的整数,那么池化窗口的高度和宽度都将是这个值。在这个例子中,池化窗口的大小是4x4。

其他参数在这个例子中使用的是默认值:

  • stride:窗口移动的步长。默认情况下,stride等于kernel_size
  • padding:在输入数据周围添加的零填充的数量。默认为0。
  • ceil_mode:是否使用向上取整来计算输出大小。默认为False。
  • count_include_pad:在计算平均值时是否包括填充的零。默认为True。
  • divisor_override:如果设置了,它将用作除数,否则使用池化区域的大小。默认为None。

因此,avg_pool2d(out, 4)将对输入张量out执行4x4的平均池化操作。

4x4的平均池化操作将会根据池化窗口的大小和步长来减小特征图(feature maps)的尺寸。在avg_pool2d(out, 4)的情况下,池化窗口的大小是4x4,且如果没有另外指定步长(stride),步长默认也是4。

假设输入特征图的尺寸是(N, C, H, W),其中:

  • N是批大小(batch size)
  • C是通道数(channel number)
  • H是特征图的高度
  • W是特征图的宽度

经过4x4的平均池化操作后,输出特征图的尺寸将变为(N, C, H/4, W/4)。这是因为池化窗口沿着特征图的高度和宽度每次移动4个单位,因此,输出特征图的高度和宽度都将是输入特征图的高度和宽度除以4(假设H和W都能被4整除)。如果H或W不能被4整除,实际的输出尺寸还会受到填充(padding)和向上取整(ceil_mode)等参数的影响。

Resnet 34

Resnet 50

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/281761.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(vue)新闻列表与图片对应显示,体现选中、移入状态

(vue)新闻列表与图片对应显示&#xff0c;体现选中、移入状态 项目背景&#xff1a;郑州院XX项目首页-新闻展示模块&#xff0c;鼠标移入显示对应图片&#xff0c;且体现选中和移入状态 首次加载&#xff1a; 切换列表后&#xff1a; html: <el-row :gutter"20"…

vue/vite添加地图

最简单的方式&#xff0c;不论vue2、vue3、vite均适用&#xff0c;例如以高德为例&#xff1a; index.html 引入 <scriptsrc"https://webapi.amap.com/maps?v1.4.15&key您的key&pluginAMap.ToolBar,AMap.MouseTool,AMap.DistrictSearch,AMap.ControlBar&quo…

IP代理技术革新:探索数据采集的新路径

引言&#xff1a; 随着全球化进程不断加深&#xff0c;网络数据采集在企业决策和市场分析中扮演着愈发重要的角色。然而&#xff0c;地域限制和IP封锁等问题常常给数据采集工作带来了巨大挑战。亿牛云代理服务凭借其强大的网络覆盖和真实住宅IP资源&#xff0c;成为解决这些问…

ElasticSearch使用(一)

文章目录 一、简介1. 数据类型2. 倒排索引3. Lucene4. ElasticSearch5. Solar VS ElasticSearch 二、ElasticSearch入门1. 简介2. 分词器3. 索引操作4. 文档操作5. ES文档批量操作 二、ElasticSearch的DSL1. 文档映射Mapping2. Index Template3. DSL 一、简介 1. 数据类型 结…

API(时间类)

一、Date类 java.util.Date类 表示特定的瞬间&#xff0c;精确到毫秒。 Date常用方法&#xff1a; public long getTime() 把日期对象转换成对应的时间毫秒值。 public void setTime(long time) 把方法参数给定的毫秒值设…

TikTok账号用什么IP代理比较好?

对于运营TikTok的从业者来说&#xff0c;IP的重要性自然不言而喻。 在其他条件都正常的情况下&#xff0c;拥有一个稳定&#xff0c;纯净的IP&#xff0c;你的视频起始播放量很可能比别人高出不少&#xff0c;而劣质的IP轻则会限流&#xff0c;重则会封号。那么&#xff0c;如何…

实时数仓之实时数仓架构(Doris)

目前比较流行的实时数仓架构有两类,其中一类是以Flink+Doris为核心的实时数仓架构方案;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对Flink+Doris架构进行介绍,这套架构的特点是组件涉及相对较少,架构简单,实时性更高,且易于Lambda架构实现,Doris本身可以支…

供应链投毒预警 | 开源供应链投毒202402月报发布啦

概述 悬镜供应链安全情报中心通过持续监测全网主流开源软件仓库&#xff0c;结合程序动静态分析方式对潜在风险的开源组件包进行动态跟踪和捕获&#xff0c;发现大量的开源组件恶意包投毒攻击事件。在2024年2月份&#xff0c;悬镜供应链安全情报中心在NPM官方仓库&#xff08;…

软考高级:软件架构评估-质量属性:可用性概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

Spark Rebalance hint的倾斜的处理(OptimizeSkewInRebalancePartitions)

背景 本文基于Spark 3.5.0 目前公司在做小文件合并的时候用到了 Spark Rebalance 这个算子&#xff0c;这个算子的主要作用是在AQE阶段的最后写文件的阶段进行小文件的合并&#xff0c;使得最后落盘的文件不会太大也不会太小&#xff0c;从而达到小文件合并的作用&#xff0c;…

美食杂志制作秘籍:引领潮流,引领味蕾

美食杂志是一种介绍美食文化、烹饪技巧和美食体验的杂志&#xff0c;通过精美的图片和生动的文字&#xff0c;向读者展示各种美食的魅力。那么&#xff0c;如何制作一本既美观又实用的美食杂志呢&#xff1f; 首先&#xff0c;你需要选择一款适合你的制作软件。比如FLBOOK在线制…

sentinel系统负载自适应流控

系统负载自适应流控 规则配置 规则创建 public class SystemRule extends AbstractRule {private double highestSystemLoad -1;private double highestCpuUsage -1;private double qps -1;private long avgRt -1;private long maxThread -1; }SystemRule类包含了以下几…

Springboot笔记(web开启)-08

有一些日志什么的后续我会补充 1.使用springboot: 创建SpringBoot应用&#xff0c;选中我们需要的模块&#xff1b;SpringBoot已经默认将这些场景配置好了&#xff0c;只需要在配置文件中指定少量配置就可以运行起来自己编写业务代码&#xff1b; 2.SpringBoot对静态资源的映…

c语言基础笔记(1)进制转换以及++a,a++,取地址和解引用

一进制转换 OCT - 八进制 DEC - 十进制 HEX - 十六进制 0520&#xff0c;表示八进制 0x520表示16进制 unsigned 无符号&#xff0c;只有正的 signed 有正有负数 char默认是signed 类型 #include <stdio.h>int main(void) { //字符转换成数字char a 5;int a1 a- 4…

HarmonyOS入门学习

HarmonyOS入门学习 前言快速入门ArkTS组件基础组件Image组件Text组件TextInput 文本输入框Buttonslider 滑动组件 页面布局循环控制ForEach循环创建组件 List自定义组件创建自定义组件Builder 自定义函数 状态管理Prop和LinkProvide和ConsumeObjectLink和Observed ArkUI页面路由…

从后端获取文件数据并导出

导出文件的公共方法 export const download (res, tools) > {const { message, hide } tools;const fileReader: any new FileReader();console.log(fileReader-res>>>, res);fileReader.onload (e) > {if (res?.data?.type application/json) {try {co…

数字孪生与智慧城市:重塑城市生活的新模式

随着信息技术的迅猛发展&#xff0c;数字孪生作为一种新兴的技术理念&#xff0c;正在逐渐改变城市建设和管理的传统模式。智慧城市作为数字孪生技术应用的重要领域&#xff0c;正在以其独特的优势和潜力&#xff0c;重塑着城市生活的方方面面。本文将从数字孪生的概念、智慧城…

Java-SSM电影购票系统

Java-SSM电影购票系统 1.服务承诺&#xff1a; 包安装运行&#xff0c;如有需要欢迎联系&#xff08;VX:yuanchengruanjian&#xff09;。 2.项目所用框架: 前端:JSP、layui、bootstrap等。 后端:SSM,即Spring、SpringMvc、Mybatis等。 3.项目功能点: 3-1.后端功能: 1.用户管…

解决GNURadio自定义C++ OOT块-导入块时报错问题

文章目录 前言一、问题描述二、解决方法1、安装依赖2、配置环境变量3、重新编译及安装三、结果1、添加结果2、运行结果前言 本文记录在 GNURadio 自定义 C++ OOT 块后导入块时报错 AttributeError: module myModule has no attribute multDivSelect。 一、问题描述 参考官方教…

作品展示ETL

1、ETL 作业定义、作业导入、控件拖拽、执行、监控、稽核、告警、报告导出、定时设定 欧洲某国电信系统数据割接作业定义中文页面&#xff08;作业顶层&#xff0c;可切英文&#xff0c;按F1弹当前页面帮助&#xff09; 涉及文件拆分、文件到mysql、库到库、数据清洗、数据转…