SKNet学习和使用-pytorch

2023-10-29 19:40
文章标签 学习 使用 pytorch sknet

本文主要是介绍SKNet学习和使用-pytorch,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Selective Kernel Networks

论文:https://arxiv.org/abs/1903.06586?context=cs

代码:https://github.com/pppLang/SKNet

其灵感来源是,我们在看不同尺寸不同远近的物体时,视觉皮层神经元接受域大小是会根据刺激来进行调节的。那么对应于CNN网络,一般来说对于特定任务特定模型,卷积核大小是确定的,那么是否可以构建一种模型,使网络可以根据输入信息的多个尺度自适应的调节接受域大小呢?

SK单元用不同卷积核提取特征,然后通过每个分支引导的不同信息构成的softmax进行融合。

SK单元包括三个方面:Split, Fuse, Select

  • Split:阶段使用不同的卷积核对原图进行卷积;
  • Fuse:组合并聚合来自多个路径的信息,以获得选择权重的全局和综合表示;
  • Select:根据选择权重聚合不同大小的内核的特征映射。

Split:

✔️ 对于任意输入的feature map,首先进行两个变化,得到 ,使用的kernel size分别为 3x3 和 5x5,其中 5x5 的卷积核替换为一个dilation为2的3x3的卷积核。

Fuse:

✔️ 该步骤主要通过门控机制将上一层的输出进行有选择的筛选,使每一个分支都携带不同的信息流进入下一个神经元。

  1. 对不同分支的输出进行融合,即逐元素进行相加(输出的尺寸和通道数必须是一样的);

2. 对两个输出进行全局平均池化(global average pooling )操作,获得每一个通道上的全局信息;

3. 对输出 s 做全连接找到每一个通道占的比重大小;

δ 是relu函數,B表示批正则化处理.

4. 为了验证W中d的作用,引入了一个衰减率r,如下,其中C代表通道数。

Select:

✔️ 通道间的soft attention可以选择不同尺寸的信息,其被紧凑的特征信息z引导,在channel-wise应用softmax操作。

网络结构

✔️ 每个SK单元由一个1x1的卷积,SK卷积,及1x1卷积组成,原网络中所有具有较大尺寸的卷积核都替换为SK卷积从而可以使网络选择合适的感受野大小。

✔️ 在SK单元中,存在三个重要参数:

  • M 用于决定路径的数量,即选择不同卷积核尺寸进行融合的数量;
  • G 用于控制每个路径的基数;
  • r 用于控制fuse操作中的参数数量。

实验结果

SKNet代码

import torch.nn as nn
import torch
from functools import reduce
class SKConv(nn.Module):def __init__(self,in_channels,out_channels,stride=1,M=2,r=16,L=32):''':param in_channels:  输入通道维度:param out_channels: 输出通道维度   原论文中 输入输出通道维度相同:param stride:  步长,默认为1:param M:  分支数:param r: 特征Z的长度,计算其维度d 时所需的比率(论文中 特征S->Z 是降维,故需要规定 降维的下界):param L:  论文中规定特征Z的下界,默认为32'''super(SKConv,self).__init__()d=max(in_channels//r,L)   # 计算向量Z 的长度dself.M=Mself.out_channels=out_channelsself.conv=nn.ModuleList()  # 根据分支数量 添加 不同核的卷积操作for i in range(M):# 为提高效率,原论文中 扩张卷积5x5为 (3X3,dilation=2)来代替。 且论文中建议组卷积G=32self.conv.append(nn.Sequential(nn.Conv2d(in_channels,out_channels,3,stride,padding=1+i,dilation=1+i,groups=32,bias=False),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True)))self.global_pool=nn.AdaptiveAvgPool2d(1) # 自适应pool到指定维度    这里指定为1,实现 GAPself.fc1=nn.Sequential(nn.Conv2d(out_channels,d,1,bias=False),nn.BatchNorm2d(d),nn.ReLU(inplace=True))   # 降维self.fc2=nn.Conv2d(d,out_channels*M,1,1,bias=False)  # 升维self.softmax=nn.Softmax(dim=1) # 指定dim=1  使得两个全连接层对应位置进行softmax,保证 对应位置a+b+..=1def forward(self, input):batch_size=input.size(0)output=[]#the part of splitfor i,conv in enumerate(self.conv):#print(i,conv(input).size())output.append(conv(input))#the part of fusionU=reduce(lambda x,y:x+y,output) # 逐元素相加生成 混合特征Us=self.global_pool(U)z=self.fc1(s)  # S->Z降维a_b=self.fc2(z) # Z->a,b 升维  论文使用conv 1x1表示全连接。结果中前一半通道值为a,后一半为ba_b=a_b.reshape(batch_size,self.M,self.out_channels,-1) #调整形状,变为 两个全连接层的值a_b=self.softmax(a_b) # 使得两个全连接层对应位置进行softmax#the part of selectiona_b=list(a_b.chunk(self.M,dim=1))#split to a and b   chunk为pytorch方法,将tensor按照指定维度切分成 几个tensor块a_b=list(map(lambda x:x.reshape(batch_size,self.out_channels,1,1),a_b)) # 将所有分块  调整形状,即扩展两维V=list(map(lambda x,y:x*y,output,a_b)) # 权重与对应  不同卷积核输出的U 逐元素相乘V=reduce(lambda x,y:x+y,V) # 两个加权后的特征 逐元素相加return V
class SKBlock(nn.Module):'''基于Res Block构造的SK BlockResNeXt有  1x1Conv(通道数:x) +  SKConv(通道数:x)  + 1x1Conv(通道数:2x) 构成'''expansion=2 #指 每个block中 通道数增大指定倍数def __init__(self,inplanes,planes,stride=1,downsample=None):super(SKBlock,self).__init__()self.conv1=nn.Sequential(nn.Conv2d(inplanes,planes,1,1,0,bias=False),nn.BatchNorm2d(planes),nn.ReLU(inplace=True))self.conv2=SKConv(planes,planes,stride)self.conv3=nn.Sequential(nn.Conv2d(planes,planes*self.expansion,1,1,0,bias=False),nn.BatchNorm2d(planes*self.expansion))self.relu=nn.ReLU(inplace=True)self.downsample=downsampledef forward(self, input):shortcut=inputoutput=self.conv1(input)output=self.conv2(output)output=self.conv3(output)if self.downsample is not None:shortcut=self.downsample(input)output+=shortcutreturn self.relu(output)
class SKNet(nn.Module):'''参考 论文Table.1 进行构造'''def __init__(self,nums_class=1000,block=SKBlock,nums_block_list=[3, 4, 6, 3]):super(SKNet,self).__init__()self.inplanes=64# in_channel=3  out_channel=64  kernel=7x7 stride=2 padding=3self.conv=nn.Sequential(nn.Conv2d(3,64,7,2,3,bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True))self.maxpool=nn.MaxPool2d(3,2,1) # kernel=3x3 stride=2 padding=1self.layer1=self._make_layer(block,128,nums_block_list[0],stride=1) # 构建表中 每个[] 的部分self.layer2=self._make_layer(block,256,nums_block_list[1],stride=2)self.layer3=self._make_layer(block,512,nums_block_list[2],stride=2)self.layer4=self._make_layer(block,1024,nums_block_list[3],stride=2)self.avgpool=nn.AdaptiveAvgPool2d(1) # GAP全局平均池化self.fc=nn.Linear(1024*block.expansion,nums_class) # 通道 2048 -> 1000self.softmax=nn.Softmax(-1) # 对最后一维进行softmaxdef forward(self, input):output=self.conv(input)output=self.maxpool(output)output=self.layer1(output)output=self.layer2(output)output=self.layer3(output)output=self.layer4(output)output=self.avgpool(output)output=output.squeeze(-1).squeeze(-1)output=self.fc(output)output=self.softmax(output)return outputdef _make_layer(self,block,planes,nums_block,stride=1):downsample=Noneif stride!=1 or self.inplanes!=planes*block.expansion:downsample=nn.Sequential(nn.Conv2d(self.inplanes,planes*block.expansion,1,stride,bias=False),nn.BatchNorm2d(planes*block.expansion))layers=[]layers.append(block(self.inplanes,planes,stride,downsample))self.inplanes=planes*block.expansionfor _ in range(1,nums_block):layers.append(block(self.inplanes,planes))return nn.Sequential(*layers)
def SKNet50(nums_class=1000):return SKNet(nums_class,SKBlock,[3, 4, 6, 3]) # 论文通过[3, 4, 6, 3]搭配出SKNet50
def SKNet101(nums_class=1000):return SKNet(nums_class,SKBlock,[3, 4, 23, 3])
if __name__=='__main__':x = torch.rand(2, 3, 224, 224)model=SKNet50()y=model(x)print(y) # shape [2,1000]

参考文档:https://zhuanlan.zhihu.com/p/76033612

这篇关于SKNet学习和使用-pytorch的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/302928

相关文章

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Java使用Spire.Doc for Java实现Word自动化插入图片

《Java使用Spire.DocforJava实现Word自动化插入图片》在日常工作中,Word文档是不可或缺的工具,而图片作为信息传达的重要载体,其在文档中的插入与布局显得尤为关键,下面我们就来... 目录1. Spire.Doc for Java库介绍与安装2. 使用特定的环绕方式插入图片3. 在指定位

Springboot3 ResponseEntity 完全使用案例

《Springboot3ResponseEntity完全使用案例》ResponseEntity是SpringBoot中控制HTTP响应的核心工具——它能让你精准定义响应状态码、响应头、响应体,相比... 目录Spring Boot 3 ResponseEntity 完全使用教程前置准备1. 项目基础依赖(M

Java使用Spire.Barcode for Java实现条形码生成与识别

《Java使用Spire.BarcodeforJava实现条形码生成与识别》在现代商业和技术领域,条形码无处不在,本教程将引导您深入了解如何在您的Java项目中利用Spire.Barcodefor... 目录1. Spire.Barcode for Java 简介与环境配置2. 使用 Spire.Barco

Android使用java实现网络连通性检查详解

《Android使用java实现网络连通性检查详解》这篇文章主要为大家详细介绍了Android使用java实现网络连通性检查的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录NetCheck.Java(可直接拷贝)使用示例(Activity/Fragment 内)权限要求

C# 预处理指令(# 指令)的具体使用

《C#预处理指令(#指令)的具体使用》本文主要介绍了C#预处理指令(#指令)的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录1、预处理指令的本质2、条件编译指令2.1 #define 和 #undef2.2 #if, #el

C#中Trace.Assert的使用小结

《C#中Trace.Assert的使用小结》Trace.Assert是.NET中的运行时断言检查工具,用于验证代码中的关键条件,下面就来详细的介绍一下Trace.Assert的使用,具有一定的参考价值... 目录1、 什么是 Trace.Assert?1.1 最简单的比喻1.2 基本语法2、⚡ 工作原理3

C# IPAddress 和 IPEndPoint 类的使用小结

《C#IPAddress和IPEndPoint类的使用小结》本文主要介绍了C#IPAddress和IPEndPoint类的使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定... 目录一、核心作用网络编程基础类二、IPAddress 类详解三种初始化方式1. byte 数组初始化2. l

C语言逗号运算符和逗号表达式的使用小结

《C语言逗号运算符和逗号表达式的使用小结》本文详细介绍了C语言中的逗号运算符和逗号表达式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习... 在C语言中逗号“,”也是一种运算符,称为逗号运算符。 其功能是把两个表达式连接其一般形式为:表达