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
采用分组卷积: groups = 32,所以输入channel的数值必须是group的整数倍
'''
super(SKConv,self).__init__()
d=max(in_channels//r,L) # 计算从向量C降维到 向量Z 的长度d
# print(d)
self.M=M
self.out_channels=out_channels
self.conv=nn.ModuleList() # 根据分支数量 添加 不同核的卷积操作
for i in range(M):
# 为提高效率,原论文中 扩张卷积5x5为 (3X3,dilation=2)来代替。 且论文中建议组卷积G=32
self.conv.append(nn.Sequential(nn.Conv1d(in_channels,out_channels,3,stride,padding=1+i,dilation=1+i,groups=32,bias=False),
nn.BatchNorm1d(out_channels),
nn.ReLU(inplace=True)))
self.global_pool=nn.AdaptiveAvgPool1d(output_size = 1) # 自适应pool到指定维度 这里指定为1,实现 GAP
self.fc1=nn.Sequential(nn.Conv1d(out_channels,d,1,bias=False),
nn.BatchNorm1d(d),
nn.ReLU(inplace=True)) # 降维
self.fc2=nn.Conv1d(d,out_channels*M,1,1,bias=False) # 升维
self.softmax=nn.Softmax(dim=1) # 指定dim=1 使得两个全连接层对应位置进行softmax,保证 对应位置a+b+..=1
def forward(self, input):
batch_size=input.size(0)
output=[]
#the part of split
for i,conv in enumerate(self.conv):
#print(i,conv(input).size())
output.append(conv(input)) #[batch_size,out_channels,H,W]
#the part of fusion
U=reduce(lambda x,y:x+y,output) # 逐元素相加生成 混合特征U [batch_size,channel,H,W]
print(U.size())
s=self.global_pool(U) # [batch_size,channel,1,1]
print(s.size())
z=self.fc1(s) # S->Z降维 # [batch_size,d,1,1]
print(z.size())
a_b=self.fc2(z) # Z->a,b 升维 论文使用conv 1x1表示全连接。结果中前一半通道值为a,后一半为b [batch_size,out_channels*M,1,1]
print(a_b.size())
a_b=a_b.reshape(batch_size,self.M,self.out_channels,-1) #调整形状,变为 两个全连接层的值[batch_size,M,out_channels,1]
print(a_b.size())
a_b=self.softmax(a_b) # 使得两个全连接层对应位置进行softmax [batch_size,M,out_channels,1]
#the part of selection
a_b=list(a_b.chunk(self.M,dim=1))#split to a and b chunk为pytorch方法,将tensor按照指定维度切分成 几个tensor块 [[batch_size,1,out_channels,1],[batch_size,1,out_channels,1]
print(a_b[0].size())
print(a_b[1].size())
a_b=list(map(lambda x:x.reshape(batch_size,self.out_channels,1),a_b)) # 将所有分块 调整形状,即扩展两维 [[batch_size,out_channels,1,1],[batch_size,out_channels,1,1]
V=list(map(lambda x,y:x*y,output,a_b)) # 权重与对应 不同卷积核输出的U 逐元素相乘[batch_size,out_channels,H,W] * [batch_size,out_channels,1,1] = [batch_size,out_channels,H,W]
V=reduce(lambda x,y:x+y,V) # 两个加权后的特征 逐元素相加 [batch_size,out_channels,H,W] + [batch_size,out_channels,H,W] = [batch_size,out_channels,H,W]
print(V)
return V # [batch_size,out_channels,H,W]
"""
x = torch.Tensor(64,32,20000)
64:表示第一个维度,即batch size,代表有64个样本。
32:表示第二个维度,即特征维度,代表每个样本有32个特征。
20000:表示第三个维度,即时间维度,代表每个样本有20000个时间步。
conv = SKConv(32,64,1,2,16,32)
print(conv(x).size())
"""
微信赞赏 支付宝赞赏
My spouse and I stumbled over here by a different page and thought I might as
well check things out. I like what I see so i am just following you.
Look forward to going over your web page yet again.
Very nice post. I just stumbled upon your blog
and wished to say that I have truly enjoyed surfing around
your blog posts. After all I will be subscribing to your rss
feed and I hope you write again soon!
https://skdjht3eigjsfdgfddf.com
您好,请教下例子中的输入x第三个维度时间步是指什么呢?我理解的例如时域一维信号,一个样本有1024个数据点,表示为(1,1024),假如batch size是64,输入就是(64,1024),希望作者帮忙解答,谢谢。
就是你一维数据的长度啊,一般就是个时频数据,那就是有20000个时间步
实际上你(1,1024)的1024就是这个时间步,而第二个参数特征数简单来说就是你上一次卷积的输出通道数,也就是这一次的输入通道数。因为你每次卷积的输入通道数是上一次的输出,但是输出通道数是你自己进行调整的。