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

相关文章

PyTorch核心方法之state_dict()、parameters()参数打印与应用案例

《PyTorch核心方法之state_dict()、parameters()参数打印与应用案例》PyTorch是一个流行的开源深度学习框架,提供了灵活且高效的方式来训练和部署神经网络,这篇文章主要介绍... 目录前言模型案例A. state_dict()方法验证B. parameters()C. 模型结构冻

Java中的ConcurrentBitSet使用小结

《Java中的ConcurrentBitSet使用小结》本文主要介绍了Java中的ConcurrentBitSet使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、核心澄清:Java标准库无内置ConcurrentBitSet二、推荐方案:Eclipse

Go语言结构体标签(Tag)的使用小结

《Go语言结构体标签(Tag)的使用小结》结构体标签Tag是Go语言中附加在结构体字段后的元数据字符串,用于提供额外的属性信息,这些信息可以通过反射在运行时读取和解析,下面就来详细的介绍一下Tag的使... 目录什么是结构体标签?基本语法常见的标签用途1.jsON 序列化/反序列化(最常用)2.数据库操作(

Java中ScopeValue的使用小结

《Java中ScopeValue的使用小结》Java21引入的ScopedValue是一种作用域内共享不可变数据的预览API,本文就来详细介绍一下Java中ScopeValue的使用小结,感兴趣的可以... 目录一、Java ScopedValue(作用域值)详解1. 定义与背景2. 核心特性3. 使用方法

spring中Interceptor的使用小结

《spring中Interceptor的使用小结》SpringInterceptor是SpringMVC提供的一种机制,用于在请求处理的不同阶段插入自定义逻辑,通过实现HandlerIntercept... 目录一、Interceptor 的核心概念二、Interceptor 的创建与配置三、拦截器的执行顺

C#中checked关键字的使用小结

《C#中checked关键字的使用小结》本文主要介绍了C#中checked关键字的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录✅ 为什么需要checked? 问题:整数溢出是“静默China编程”的(默认)checked的三种用

C#中预处理器指令的使用小结

《C#中预处理器指令的使用小结》本文主要介绍了C#中预处理器指令的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录 第 1 名:#if/#else/#elif/#endif✅用途:条件编译(绝对最常用!) 典型场景: 示例

Mysql中RelayLog中继日志的使用

《Mysql中RelayLog中继日志的使用》MySQLRelayLog中继日志是主从复制架构中的核心组件,负责将从主库获取的Binlog事件暂存并应用到从库,本文就来详细的介绍一下RelayLog中... 目录一、什么是 Relay Log(中继日志)二、Relay Log 的工作流程三、Relay Lo

使用Redis实现会话管理的示例代码

《使用Redis实现会话管理的示例代码》文章介绍了如何使用Redis实现会话管理,包括会话的创建、读取、更新和删除操作,通过设置会话超时时间并重置,可以确保会话在用户持续活动期间不会过期,此外,展示了... 目录1. 会话管理的基本概念2. 使用Redis实现会话管理2.1 引入依赖2.2 会话管理基本操作

Springboot请求和响应相关注解及使用场景分析

《Springboot请求和响应相关注解及使用场景分析》本文介绍了SpringBoot中用于处理HTTP请求和构建HTTP响应的常用注解,包括@RequestMapping、@RequestParam... 目录1. 请求处理注解@RequestMapping@GetMapping, @PostMappin