**深度可分离卷积(Depthwise Separable Convolution)**是一种特别的卷积方法,它将常规卷积操作分解成两个阶段:
这种分解使得卷积层的计算量大大减少,同时在某些情况下能保持较好的性能。
深度可分离卷积首次在 Xception 网络中得到广泛应用,并且是 MobileNet 和 EfficientNet 等轻量级网络架构的核心组成部分。
普通卷积对输入特征图的每个通道执行卷积操作,之后将所有通道的卷积结果进行混合。而深度可分离卷积则分解为两个阶段:
对每个输入通道单独进行卷积操作。也就是说,对于每个输入通道,我们使用一个卷积核来处理该通道的所有像素。
通过 1 × 1 1 \times 1 1×1 卷积,将深度卷积输出的各个通道进行组合。这个卷积实际上相当于执行了线性组合,对各通道的信息进行融合。
给定输入张量 X ∈ R H × W × C i n X \in \mathbb{R}^{H \times W \times C_{in}} X∈RH×W×Cin,卷积核 K ∈ R k × k × C i n × C o u t K \in \mathbb{R}^{k \times k \times C_{in} \times C_{out}} K∈Rk×k×Cin×Cout,输出为:
Y [ i , j , c o u t ] = ∑ m = 0 k − 1 ∑ n = 0 k − 1 ∑ c i n = 0 C i n − 1 X [ i + m , j + n , c i n ] ⋅ K [ m , n , c i n , c o u t ] Y[i, j, c_{out}] = \sum_{m=0}^{k-1} \sum_{n=0}^{k-1} \sum_{c_{in}=0}^{C_{in}-1} X[i+m, j+n, c_{in}] \cdot K[m, n, c_{in}, c_{out}] Y[i,j,cout]=m=0∑k−1n=0∑k−1cin=0∑Cin−1X[i+m,j+n,cin]⋅K[m,n,cin,cout]
1. 深度卷积:
对每个输入通道单独进行卷积,每个卷积核的形状为 k × k k \times k k×k,卷积核的数量为 C i n C_{in} Cin,输出为:
Y d e p t h w i s e [ i , j , c i n ] = ∑ m = 0 k − 1 ∑ n = 0 k − 1 X [ i + m , j + n , c i n ] ⋅ K d e p t h w i s e [ m , n , c i n ] Y_{depthwise}[i, j, c_{in}] = \sum_{m=0}^{k-1} \sum_{n=0}^{k-1} X[i+m, j+n, c_{in}] \cdot K_{depthwise}[m, n, c_{in}] Ydepthwise[i,j,cin]=m=0∑k−1n=0∑k−1X[i+m,j+n,cin]⋅Kdepthwise[m,n,cin]
2. 逐点卷积:
通过 1 × 1 1 \times 1 1×1 卷积将每个深度卷积的输出进行组合,输出形状为 H × W × C o u t H \times W \times C_{out} H×W×Cout:
Y p o i n t w i s e [ i , j , c o u t ] = ∑ c i n = 0 C i n − 1 X d e p t h w i s e [ i , j , c i n ] ⋅ K p o i n t w i s e [ c i n , c o u t ] Y_{pointwise}[i, j, c_{out}] = \sum_{c_{in}=0}^{C_{in}-1} X_{depthwise}[i, j, c_{in}] \cdot K_{pointwise}[c_{in}, c_{out}] Ypointwise[i,j,cout]=cin=0∑Cin−1Xdepthwise[i,j,cin]⋅Kpointwise[cin,cout]
特性 | 普通卷积 | 深度可分离卷积 |
---|---|---|
计算量 | 较大 | 较小 |
参数量 | 较大 | 较小 |
执行效率 | 较慢 | 较快 |
适用场景 | 精度要求较高的场景 | 轻量化网络、实时应用 |
对于 H × W × C i n H \times W \times C_{in} H×W×Cin 的输入和 k × k × C i n × C o u t k \times k \times C_{in} \times C_{out} k×k×Cin×Cout 的卷积核,普通卷积的计算量为:
计算量 普通卷积 = H × W × C i n × k × k × C o u t \text{计算量}_{\text{普通卷积}} = H \times W \times C_{in} \times k \times k \times C_{out} 计算量普通卷积=H×W×Cin×k×k×Cout
而深度可分离卷积的计算量是两个阶段的和:
计算量 深度卷积 = H × W × C i n × k × k \text{计算量}_{\text{深度卷积}} = H \times W \times C_{in} \times k \times k 计算量深度卷积=H×W×Cin×k×k
计算量 逐点卷积 = H × W × C i n × C o u t \text{计算量}_{\text{逐点卷积}} = H \times W \times C_{in} \times C_{out} 计算量逐点卷积=H×W×Cin×Cout
总计算量为:
计算量 深度可分离卷积 = H × W × C i n × k × k + H × W × C i n × C o u t \text{计算量}_{\text{深度可分离卷积}} = H \times W \times C_{in} \times k \times k + H \times W \times C_{in} \times C_{out} 计算量深度可分离卷积=H×W×Cin×k×k+H×W×Cin×Cout
在 PyTorch 中,深度可分离卷积通常可以通过 nn.Conv2d
配合 groups
参数来实现,或者直接使用 nn.SeparableConv2d
(如果库中有这样的实现)。
import torch
import torch.nn as nn
class DepthwiseSeparableConv(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, padding):
super(DepthwiseSeparableConv, self).__init__()
# 深度卷积
self.depthwise = nn.Conv2d(in_channels, in_channels, kernel_size=kernel_size,
padding=padding, groups=in_channels)
# 逐点卷积
self.pointwise = nn.Conv2d(in_channels, out_channels, kernel_size=1)
def forward(self, x):
x = self.depthwise(x) # 深度卷积
x = self.pointwise(x) # 逐点卷积
return x
# 示例:输入 (batch_size=1, channels=32, height=224, width=224)
model = DepthwiseSeparableConv(32, 64, kernel_size=3, padding=1)
input_tensor = torch.randn(1, 32, 224, 224)
output = model(input_tensor)
print(output.shape) # 应该是 (1, 64, 224, 224)
MobileNetV1 架构的每一层都使用了深度可分离卷积,尤其是在输入层和中间层,显著减少了计算量。
# 示例:MobileNetV1 中的一部分实现
class MobileNetV1(nn.Module):
def __init__(self, num_classes=1000):
super(MobileNetV1, self).__init__()
self.features = nn.Sequential(
DepthwiseSeparableConv(3, 32, kernel_size=3, padding=1),
DepthwiseSeparableConv(32, 64, kernel_size=3, padding=1),
# 更多层...
)
self.classifier = nn.Linear(1024, num_classes)
def forward(self, x):
x = self.features(x)
x = x.mean([2, 3]) # Global Average Pooling
x = self.classifier(x)
return x
深度可分离卷积通过将标准卷积分解成深度卷积和逐点卷积两个操作,显著减少了模型的计算量和参数量,特别适合应用在计算资源有限的设备上,如移动设备、嵌入式系统等。