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删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

Ubuntu如何分配​​未使用的空间

《Ubuntu如何分配​​未使用的空间》Ubuntu磁盘空间不足,实际未分配空间8.2G因LVM卷组名称格式差异(双破折号误写)导致无法扩展,确认正确卷组名后,使用lvextend和resize2fs... 目录1:原因2:操作3:报错5:解决问题:确认卷组名称​6:再次操作7:验证扩展是否成功8:问题已解