彻底理解传统卷积,深度可分离卷积
1. 标准卷积(Standard Convolution)
计算过程详解
# 标准卷积示例 import torch.nn as nn standard_conv = nn.Conv2d(in_channels=3, # 输入通道数out_channels=256, # 输出通道数 kernel_size=3, # 卷积核大小 ------------ 看作卷积体 3 * 3 * 3 stride=1,padding=0 # valid模式,无填充 ) # 输入: [batch_size, 3, 224, 224] # 输出: [batch_size, 256, 222, 222]
计算量分析
输出特征图大小: 222 × 222 × 256
每个输出点的计算: 3 × 3 × 3 = 27次乘加运算
总计算量: 222 × 222 × 256 × 3 × 3 × 3 = 85,228,224次乘法
# 步骤1: 卷积核内部累加 • temp1 = x1 * w1 # 1乘法 • temp2 = temp1 + (x2 * w2) # 1乘法 + 1加法 • temp3 = temp2 + (x3 * w3) # 1乘法 + 1加法 • temp4 = temp3 + (x4 * w4) # 1乘法 + 1加法 • temp5 = temp4 + (x5 * w5) # 1乘法 + 1加法 • temp6 = temp5 + (x6 * w6) # 1乘法 + 1加法 • temp7 = temp6 + (x7 * w7) # 1乘法 + 1加法 • temp8 = temp7 + (x8 * w8) # 1乘法 + 1加法 • conv_result = temp8 + (x9 * w9) # 1乘法 + 1加法 # 步骤2: 加上偏置 final_output = conv_result + b # 1加法 return final_output
参数数量
参数数量 = (kernel_h × kernel_w × in_channels + 1) × out_channels= (3 × 3 × 3 + 1) × 256 = 28 × 256 = 7,168个参数(包含偏置项,每个输出通道一个偏置)
2. 深度可分离卷积(Depthwise Separable Convolution)
两步分解的直观理解
第一步:深度卷积 - 空间特征提取
# 深度卷积 - 提取每个通道的空间特征 depthwise_conv = nn.Conv2d(in_channels=3,out_channels=3, # 关键:输出通道=输入通道kernel_size=3,stride=1,padding=0,groups=3 # 分组数=输入通道数,每个通道独立卷积 ) # 相当于3个独立的2D卷积,分别处理RGB三个通道
第二步:逐点卷积 - 通道特征融合
# 逐点卷积 - 融合不同通道的信息 pointwise_conv = nn.Conv2d(in_channels=3,out_channels=256,kernel_size=1, # 1×1卷积,只改变通道数stride=1,padding=0 ) # 相当于全连接层在通道维度上的操作
计算量对比
标准卷积计算量:
MACs = H_out × W_out × C_out × K_h × K_w × C_in= 222 × 222 × 256 × 3 × 3 × 3= 85,228,224
深度可分离卷积计算量:
# 深度卷积 depthwise_MACs = H_out × W_out × C_in × K_h × K_w × 1= 222 × 222 × 3 × 3 × 3 × 1= 1,332,216 # 逐点卷积 pointwise_MACs = H_out × W_out × C_out × 1 × 1 × C_in= 222 × 222 × 256 × 1 × 1 × 3= 37,907,712 # 总计算量 total_MACs = 1,332,216 + 37,907,712 = 39,239,928
计算量减少比例
计算量比例 = 深度可分离卷积计算量 / 标准卷积计算量= 39,239,928 / 85,228,224 ≈ 46%减少比例 ≈ 1 - (1/C_out + 1/(K_h×K_w)) ≈ 1 - (1/256 + 1/9) ≈ 54%
3. 实际应用和优势
在YOLOv5中的应用
# YOLOv5中深度可分离卷积的实现 class DepthwiseSeparableConv(nn.Module):def __init__(self, in_channels, out_channels, kernel_size=3, stride=1):super().__init__()self.depthwise = nn.Conv2d(in_channels, in_channels, kernel_size,stride, kernel_size//2, groups=in_channels, bias=False)self.pointwise = nn.Conv2d(in_channels, out_channels, 1, 1, 0, bias=False)def forward(self, x):return self.pointwise(self.depthwise(x))
优势总结
参数效率: 795 vs 7,168个参数,减少约89%
计算效率: 计算量减少约54%
内存友好: 适合移动端和嵌入式设备
过拟合风险低: 参数少,正则化效果更好
