在各种卷积方式汇总的文章中详细描述了分组卷积相关内容及实现方式:https://blog.csdn.net/qq_43665602/article/details/126708012
此处粗略复习一下:在进行标准卷积时,通常采用多组过滤器得到期望的输出,而每个过滤器都使用与输入特征通道数相同的多个卷积核叠加构成。而对于分组卷积,我们通过对输入的通道进行划分实现多分支特征学习,最后将所有分支提取的特征进行级联得到最后的输出。比如,输入特征为(10,7,7),采用大小为(3,3)的卷积核,期望得到大小为(20,5,5)的输出:
1)对于标准卷积而言,在此过程中,一共采用20组过滤器进行特征提取,而每个过滤器是由10个(3,3)卷积核叠加而成,每个过滤器得到一个(5,5)的单通道输出,20组过滤器则得到最后的(20,5,5)输出。
2)对于分组卷积而言,此处我设置分组大小为groups=2,则表示将输入沿着通道划分为两组(5,7,7)的输入,将整个卷积过程分为两个分支进行,每个分支采用10个过滤器提取特征,而每个过滤器由5个(3,3)卷积核叠加而成,每个过滤器得到一个(5,5)单通道输出,每个分支得到(10,5,5)的中间输出,最后将两个分支的输出结果级联得到期望的(20,5,5)输出。
import torch
import torch.nn as nn
inp=torch.randint(10,size=(1,10,4,4),dtype=torch.float32)
print("-"*25)
conv2d=nn.Conv2d(
in_channels=10,
out_channels=8,
kernel_size=2,
stride=1,
)
out=conv2d(inp)
print(out.shape)
print("-"*25)
groups_conv2d=nn.Conv2d(
in_channels=10,
out_channels=8,
kernel_size=2,
stride=1,
groups=2
)
out1=groups_conv2d(inp)
print(out1.shape)
print("-"*25)
深度卷积可以说是分组卷积的一种特殊情况:对于大小为(N,C_in,H_in,W_in)的输入,当分组卷积中groups=C_in,而输出为(N,C_out,H_out,W_out)=(N,K×C_in,H_out,W_out)时,分组卷积即为深度卷积(其中的K为一个正整数),其实在深度可分离卷积中使用的深度卷积就是K=1的特殊情况。
inp=torch.randint(10,size=(1,10,7,7),dtype=torch.float32)
in_channels=10
groups=in_channels
K=2
out_channels=K*in_channels
conv=nn.Conv2d(
in_channels=in_channels,
out_channels=out_channels,
kernel_size=3,
groups=groups
)
out=conv(inp)
print(out.shape)
K=1时,out_channels=in_channels
torch.Size([1, 10, 5, 5])
K=2时,out_channels=2*in_channels
torch.Size([1, 20, 5, 5])
这里我对K=2时的情况解释一下:通过上面对分组卷积的介绍我们知道此时意味着,将输入沿着通道分为10个分支,每个分支仅有原始输入的一个通道的数据作为输入,每个分支采用out_channels/groups=2个过滤器,每个过滤器仅由1个(3,3)卷积核构成,每个过滤器会得到1个(5,5)的单通道输出,因此每个分支可以得到1个(2,5,5)的输出,10个分支的结果级联得到(20,5,5)的输出。
注意:深度卷积中最重要的设置为groups=in_channels,out_channels=K*in_channels。