赞
踩
大卷积核:
小卷积核:
在参数计算(输入输出图片通道数必须保持一致才能比较)
设输入尺寸为5X5X10,输出尺寸为1X1X10
在前面的ResNet上说过这个问题,可以去前面参考一下前面的文章
如果输入和输出通道数不同,那么上面的参数量减少的情况还存在吗?当然不存在,一个简单的栗子:
如果输入尺寸是15X15X3,输出尺寸为11X11X64
1个5X5卷积 5X5X3X64
2个3X3卷积 3X3X3X64 + 3X3X64X64
卷积核的感受野的计算方法:
RF(后一个感受野) = RF(前一个感受野) + (卷积核大小 - 1) X 步长
栗子:
一个5X5卷积:RF = 5
两个3X3卷积:3+(3-1) X 1 = 5
能否让固定大小的卷积核看到更大范围的区域?
当然得是空洞卷积 标准的3X3卷积核只能看到对应区域3X3大小,但是为了让卷积核看到更大范围,空洞卷积成为可能,池化操作导致的信息丢失是不可逆的,这不利于像素级任务,用空洞卷积代替pool的作用(成倍的增加感受野)更适用于语义分割。
卷积核一定得是正方形吗?(非对称卷积)
将标准3X3卷积拆分成为1X3卷积和3X1卷积,在不改变感受野大小的情况下,可减少计算量
标准卷积计算量:9X9=81次乘法
非对称卷积计算量:3X15 + 3X9 = 72次乘法
标准卷积与非对称卷积感受野对比
卷积只能在同一组进行吗?(组卷积&深度可分离卷积)
组卷积是对输入特征图进行分组,每组分别进行卷积。
分组卷积能否对通道进行随机分组?(shffleNet)
为达到特征之间的相互通信,除了采用dense pointwise convolution,还可以使用channel shuffle,就是对分组卷积之后特征图进行重组,这样可以保证下面的卷积其输入来自不同的组,因此信息可以在不同组之间流转。
每层卷积只能用一种尺寸的卷积核吗?inception结构
通道间的特征都是平等的吗?(SE Net)
无论是在Inception、DenseNet或者ShuffleNet里面,我们对所有通道产生的特 征都是不分权重直接结合的,那为什么要认为所有通道的特征对模型的作用都是相等 的呢?一个卷积层中往往有数以千计的卷积核,每个卷积核都对应了特征,于是那么 多特征要怎么区分?这个方法就是通过学习的方式来自动获取到每个特征通道的重要 程度,然后依照计算出来的重要程度去提升有用的特征并抑制对当前任务用处不大的 特征。
卷积核形状得一定是矩形吗?(可变形卷积)
规则形状的卷积核(比如一般用的正方形3*3卷积)可能会限制特征的提取,如果 赋予卷积核形变的特性,让网络根据label反传下来的误差自动的调整卷积核的形状, 适应网络重点关注的感兴趣的区域,就可以提取更好的特征。例如,网络会根据原位 置(a),学习一个offset偏移量,得到新的卷积核(b)©(d),那么一些特殊 情况就会成为这个更泛化的模型的特例,例如图©表示从不同尺度物体的识别,图 (d)表示旋转物体的识别
总结:
首先导入需要使用的库
import torch
import torch.nn as nn
from torchvision import models
我们这里使用的是ResNet152,直接通过torchvision.models
导入
resnet152_pretrained = models.resnet152(pretrained=False)
首先从小的模块开始实现,这个就是总体结构图中的GCN部分,主要作用是对resnet的不同特征图使用非对称卷积,进行特征提取,然后进行特征融合。
class GCM(nn.Module):
def __init__(self, in_channels, num_class, k=15):
super(GCM, self).__init__()
# 为了保持输入和输出图像的大小一致,我们需要对输入图片进行填充
pad = (k-1) // 2
self.conv1 = nn.Sequential(nn.Conv2d(in_channels, num_class, kernel_size=(1, k), padding=(0, pad), bias=False),
nn.Conv2d(num_class, num_class, kernel_size=(k, 1), padding=(pad, 0), bias=False))
self.conv2 = nn.Sequential(nn.Conv2d(in_channels, num_class, kernel_size=(k, 1), padding=(pad, 0), bias=False),
nn.Conv2d(num_class, num_class, kernel_size=(1, k), padding=(0, pad), bias=False))
def forward(self, x):
x1 = self.conv1(x)
x2 = self.conv2(x)
# 特征进行相加的话,得保持两根线输出的特征图的大小是一样维度的
assert x1.shape == x2.shape
return x1 + x2
接着实现BR层
可能是为了防止特征提取过程中,造成像素的损失,采用了跳层连接的结构,在提取特征的同时,能保持平移同变性。
class BR(nn.Module):
def __init__(self, num_class):
super(BR, self).__init__()
self.shortcut = nn.Sequential(nn.Conv2d(num_class, num_class, 3, padding=1, bias=False),
nn.ReLU(),
nn.Conv2d(num_class, num_class, 3, padding=1, bias=False))
def forward(self, x):
return x + self.shortcut(x)
接着实现每一个上采样模块,从下往上看,我们可以看出,在每一层都存在GCN + BR 然后最后一层比较特殊,没有相加这个模块,然后我们可以在反向传播模块,传入两个参数,作为判断条件,如果第二个参数为空的话,就是最后一层,如果不为空,就是前面两个GCN+BR然后和下面一层的上采样部分进行相加特征融合,融合之后经过BR和Deconv,依次这样恢复原来的尺寸。
class GCN_BR_BR_Deconv(nn.Module):
def __init__(self, in_channels, num_class, k=15):
super(GCN_BR_BR_Deconv, self).__init__()
self.gcn = GCM(in_channels, num_class, k)
self.br = BR(num_class)
self.deconv = nn.ConvTranspose2d(num_class, num_class, 4, 2, 1, bias=False)
def forward(self, x1, x2=None):
x1 = self.gcn(x1)
x1 = self.br(x1)
if x2 is None:
x = self.deconv(x1)
else:
x = x1 + x2
x = self.br(x)
x = self.deconv(x)
return x
最后我们总的来实现一个GCN网络,为了清晰,我们从forward看:
def forward(self, input):
x0 = self.layer0(input); print('x0:', x0.size()) # x0: torch.Size([1, 64, 176, 240])
x1 = self.layer1(x0); print('x1:', x1.size()) # x1: torch.Size([1, 256, 88, 120])
x2 = self.layer2(x1); print('x2:', x2.size()) # x2: torch.Size([1, 512, 44, 60])
x3 = self.layer3(x2); print('x3:', x3.size()) # x3: torch.Size([1, 1024, 22, 30])
x4 = self.layer4(x3); print('x4:', x4.size()) # x4: torch.Size([1, 2048, 11, 15])
branch4 = GCN_BR_BR_Deconv(x4.shape[1], self.num_class, self.k)
branch3 = GCN_BR_BR_Deconv(x3.shape[1], self.num_class, self.k)
branch2 = GCN_BR_BR_Deconv(x2.shape[1], self.num_class, self.k)
branch1 = GCN_BR_BR_Deconv(x1.shape[1], self.num_class, self.k)
branch4 = branch4(x4); print('branch4:', branch4.size()) # torch.Size([1, 12, 22, 30])
branch3 = branch3(x3, branch4); print('branch3:', branch3.size()) # torch.Size([1, 12, 44, 60])
branch2 = branch2(x2, branch3); print('branch2:', branch2.size()) # torch.Size([1, 12, 88, 120])
branch1 = branch1(x1, branch2); print('branch1:', branch1.size()) # torch.Size([1, 12, 176, 240])
x = self.br(branch1)
x = self.deconv(x)
x = self.br(x)
return x
__init__部分
def __init__(self, num_classes, k=15):
super(GCN, self).__init__()
self.num_class = num_classes
self.k = k
self.layer0 = nn.Sequential(resnet152_pretrained.conv1, resnet152_pretrained.bn1, resnet152_pretrained.relu)
self.layer1 = nn.Sequential(resnet152_pretrained.maxpool, resnet152_pretrained.layer1)
self.layer2 = resnet152_pretrained.layer2
self.layer3 = resnet152_pretrained.layer3
self.layer4 = resnet152_pretrained.layer4
self.br = BR(self.num_class)
# 一般卷积核为4,步长为2,padding为1时,是将原始图像通过转置卷积放大为原来的两倍
self.deconv = nn.ConvTranspose2d(self.num_class, self.num_class, 4, 2, 1, bias=False)
最后是测试,我们用torch模拟一张图片的输入,然后打印出每一层的size大小,可以更直观的看到图像尺寸的变化。
if __name__ == "__main__":
import torch as t
rgb = t.randn(1, 3, 512, 512)
net = GCN(21)
out = net(rgb)
print(out.shape)
输出结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。