神经网络卷积反卷积及池化计算公式、特征图通道数(维度)变化实例

卷积神经网络,要特别注意输入图像的尺寸,如果想套用某个网络结构,需要先通过网络结构计算出输入图像尺寸,将自己的图像调整为所需要的尺寸;也可以根据自己的图像尺寸适当调整网络结构。以下是具体操作方法。

目录

一,要想计算图像尺寸,先要了解基础卷积等计算公式

二,了解神经网络的网络结构

三,举例分析一下代码的输出尺寸

四,获取每一层的输出张量


一,要想计算图像尺寸,先要了解基础卷积等计算公式


1.计算公式
设:
图像宽为W,高为H,通道数为C;
卷积核尺寸为K,通道数为D,个数为N;
卷积运算步长为S,0填充大小为P;
输入和输出量分别以1和2表示。

卷积:
W2 = (W1 - K + 2×P) / S + 1
H2 = (H1 - K + 2×P) / S + 1
C2 = N

反卷积:
W2 = (W1-1) × S + K - 2×P
H2 = (H2-1) × S + K - 2×P
C2 = N

池化:
W2 = (W1 - K) / S + 1.
H2 = (H1 - K) / S + 1.
C2 = C1

上采样UpSampling2D:

上采样相当于放大多少倍,size=倍数

Width_out=W*size

Height_out=H*size

卷积、反卷积和池化操作均可改变图像尺寸大小,但池化操作不会改变图像的通道数,而卷积与反卷积通常会改变图像的通道数。

2.特征图通道数(维度)变化
经过卷积运算输出的特征图通道数仅由卷积核的个数决定,随各卷积层中卷积核的个数变化而变化,即C2=N,例如32×32×3经过5个5×5×3的卷积核运算后输出的特征图通道数即为5,如28×28×5。

3.卷积核大小与深度
卷积核通道数
卷积核的通道数只与输入图像的通道数有关,在进行卷积运算时,要求用于运算的卷积核的通道数应与输入图像的通道数一致,即D=C1,例如对32×32×3大小的输入图像进行卷积计算,卷积核通道数也为3,如5×5×3;

卷积核深度
卷积核深度就是卷积核的个数,每个卷积核中各通道与输入图像各通道相对应,所有通道同时分别进行运算,然后形成输出特征图其中的一个通道,多个卷积核运算后便形成多个通道,因此卷积核的深度也就是输出图像的通道数。
再结合上一条,则卷积核的深度同时也决定了下一卷积层中卷积核的通道数,因为本卷积层输出图像就是下一层的输入图像


二,了解神经网络的网络结构

例:
 

class ConvNet(nn.Module):
    def __init__(self):
        super().__init__()
        # 1,28x28
        self.conv1=nn.Conv2d(1,10,5) # 24x24
        self.pool = nn.MaxPool2d(2,2) # 12x12
        self.conv2=nn.Conv2d(10,20,3) # 10x10
        self.fc1 = nn.Linear(20*10*10,500)
        self.fc2 = nn.Linear(500,10)
    def forward(self,x):
        in_size = x.size(0)
        out = self.conv1(x) #24
        out = F.relu(out)
        out = self.pool(out)  #12
        out = self.conv2(out) #10
        out = F.relu(out)
        out = out.view(in_size,-1)
        out = self.fc1(out)
        out = F.relu(out)
        out = self.fc2(out)
        out = F.log_softmax(out,dim=1)
        return out

在写代码时,可以将每一层的输入输出尺寸标注出来,以便之后修改等。
Conv2d、max_pool2d的参数:
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, 
dilation=1, groups=1, bias=True)

in_channels:输入通道数
out_channels:输出通道数(卷积核的个数)
kernel_size:卷积核的尺寸
stride:卷积步长(默认值为1)
padding:输入在每一条边补充0的层数(默认不补充)
dilation:卷积核元素之间的间距(默认值为1)
groups:从输入通道到输出通道的阻塞连接数(默认值是1)
bias:是否添加偏置(默认是True)


 

torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)


kernel_size:池化的窗口大小
stride:窗口移动的步长,默认值是kernel_size
padding:输入的每一条边补充0的层数
dilation:一个控制窗口中元素步幅的参数
return_indices:如果等于True,会返回输出最大值的序号,对于上采样操作会有帮助
ceil_mode:如果等于True,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作


三,举例分析一下代码的输出尺寸

class ConvNet(nn.Module):
    def __init__(self):
        super().__init__()
        # 1,28x28
        self.conv1=nn.Conv2d(1,10,5) # 24x24
        self.pool = nn.MaxPool2d(2,2) # 12x12
        self.conv2=nn.Conv2d(10,20,3) # 10x10
        self.fc1 = nn.Linear(20*10*10,500)
        self.fc2 = nn.Linear(500,10)
   def forward(self,x):
        in_size = x.size(0)
        out = self.conv1(x) #24
        out = F.relu(out)
        out = self.pool(out)  #12
        out = self.conv2(out) #10
        out = F.relu(out)
        out = out.view(in_size,-1)
        out = self.fc1(out)
        out = F.relu(out)
        out = self.fc2(out)
        out = F.log_softmax(out,dim=1)
        return out


读别人已写好的代码时,要结合前向传播的结构来分析,不能只看代码的上面部分,可能还含有其他能改变图像尺寸的结构
在上述代码中,只有以下代码部分会改变输出图像尺寸大小。

       # 1,28x28
        self.conv1=nn.Conv2d(1,10,5) # 24x24
        self.pool = nn.MaxPool2d(2,2) # 12x12
        self.conv2=nn.Conv2d(10,20,3) # 10x10
        self.fc1 = nn.Linear(20*10*10,500)
        self.fc2 = nn.Linear(500,10)


假设输入图像尺寸为28x28
第一层卷积卷积核尺寸大小为5x5,步长为1。所以输出图像尺寸:1+(28-5)/1=24
即代码第二行注释标注:经过第一层卷积层后,输出图像尺寸变为24x24
同理,在池化层,池化窗口大小为2x2,移动步长为2,所以经过池化层后,输出图像尺寸变为12x12
第二层卷积尺寸大小为3x3,步长为1,所以输出图像尺寸:1+(12-3)/1=10
经过第二层卷积后,输出图像尺寸为10x10,输出维度为20,所以全连接层的输入固定为201010
所以此网络只接受输入图像尺寸为28x28。
如果,不想改变输入图像的尺寸,可以通过以上步骤,计算出全连接层的输入,适当改变神经网络结构,已达到理想的效果。
现在很多卷积神经网络模型,都用卷积层替代全连接层,就是因为这个原因。因为全连接层的参数的尺寸与输入图像的尺寸有关,故输入图像尺寸改变,模型参数初始化尺寸亦改变。若采用卷积层替代全连接层,则输入图像尺寸改变,模型不用做任何改变。

四,获取每一层的输出张量

在神经网络中,查看每一层输出的图片维度可以帮助我们更好地理解网络的结构和运作。

例如:

outputs = self.conv2(outputs)
print("Shape of conv2 weight tensor:", self.conv2.weight.shape)

outputs = self.relu(outputs)
print("Shape of outputs tensor:", outputs.shape)

在相应的代码下使用直接输出张量形状

另一种常见的方法是使用钩子函数(hook functions),在前向计算过程中获取每一层的输出张量,并打印其形状信息。

在 PyTorch 中,可以使用 register_forward_hook() 方法注册钩子函数,来获取每一层的输出张量。具体操作如下:

1.定义钩子函数,获取输出张量的形状信息,并打印出来:

def print_shape(module, input, output):
    print('输出张量形状:', output.shape)

2.注册钩子函数,将其绑定到网络的每一层上:

# 遍历网络模型的每一层,将钩子函数注册到每一层上
for name, module in model.named_modules():
    handle = module.register_forward_hook(print_shape)

将输入数据 x 传递给模型进行前向计算,观察每一层输出张量的形状信息:


3.将输入数据传递给模型进行前向计算

output = model(x)
需要注意的是,钩子函数会在每一次前向计算时被调用,因此可能会产生大量的输出信息。为了避免输出信息过多,可以在钩子函数中添加一些条件判断,只打印特定层的输出张量信息,或者只在特定的迭代次数或轮数时打印输出信息。另外,如果网络模型非常复杂,可能需要更加细致地设置钩子函数,以便观察每一层的输出张量信息。

你可能感兴趣的:(机器学习,深度学习,计算机视觉,opencv,神经网络)