首页 > 其他分享 >深度可分离卷积原理分析回顾、代码实践与优缺点对比学习记录

深度可分离卷积原理分析回顾、代码实践与优缺点对比学习记录

时间:2024-12-20 09:26:58浏览次数:5  
标签:kernel 回顾 output 卷积 优缺点 channels 深度 input

最近在项目开发中有需要用到轻量化相关的内容,那必定是绕不开深度可分离卷积的,这里记录自己的学习记录和实践内容。

深度可分离卷积(Depthwise Separable Convolution)是一种轻量化的卷积操作,广泛应用于移动设备和嵌入式设备上的深度学习模型(如 MobileNet 和 Xception)。它将标准卷积分解为两步:深度卷积(Depthwise Convolution, DW) 和 逐点卷积(Pointwise Convolution, PW)

1. 深度可分离卷积的原理

(1)标准卷积

标准卷积的计算复杂度较高,尤其是当输入通道数和输出通道数较大时。

(2)深度可分离卷积

深度可分离卷积将标准卷积分解为两步:

2. 深度可分离卷积的实现

(1)深度卷积(DW)的实现

深度卷积的实现步骤:

  1. 对每个输入通道独立进行卷积。

  2. 每个输入通道的卷积结果保持独立。

(2)逐点卷积(PW)的实现

逐点卷积的实现步骤:

  1. 使用 1×1 的卷积核在通道维度上进行线性组合。

  2. 输出特征图的通道数由逐点卷积核的输出通道数决定。

3. 深度可分离卷积的优缺点

(1)优点

1. 参数量大幅减少
  • 深度可分离卷积的参数量显著低于标准卷积。

  • 参数量计算公式:

2. 计算量大幅减少
  • 深度可分离卷积的计算量显著低于标准卷积。

  • 计算量计算公式:

3. 适合轻量化模型
  • 深度可分离卷积广泛应用于移动设备和嵌入式设备上的轻量化模型(如 MobileNet 和 Xception)。

  • 它能够在保持较好特征提取能力的同时,大幅减少参数量和计算量。

(2)缺点

1. 特征表达能力受限
  • 深度卷积(DW)对每个输入通道独立进行卷积,缺乏通道间的信息交互。

  • 逐点卷积(PW)虽然能够进行通道间的信息融合,但其计算量仍然较大。

2. 不适合所有任务
  • 深度可分离卷积在某些任务(如图像分类)中表现优异,但在某些任务(如图像分割)中可能表现不如标准卷积。

4. 深度可分离卷积的应用

(1)MobileNet

  • MobileNet 是深度可分离卷积的经典应用。

  • 它通过深度可分离卷积大幅减少了参数量和计算量,同时保持了较好的特征提取能力。

(2)Xception

  • Xception 是另一种广泛使用深度可分离卷积的模型。

  • 它通过深度可分离卷积实现了高效的特征提取。

(3)轻量化模型

  • 深度可分离卷积广泛应用于移动设备和嵌入式设备上的轻量化模型。

  • 它能够在保持较好特征提取能力的同时,大幅减少参数量和计算量。

5. 总结

深度可分离卷积的原理

  • 将标准卷积分解为深度卷积(DW)和逐点卷积(PW)。

  • 深度卷积对每个输入通道独立进行卷积,逐点卷积在通道维度上进行线性组合。

深度可分离卷积的实现

  • 深度卷积(DW):对每个输入通道独立进行卷积。

  • 逐点卷积(PW):使用 1×11×1 的卷积核在通道维度上进行线性组合。

深度可分离卷积的优缺点

  • 优点:参数量和计算量大幅减少,适合轻量化模型。

  • 缺点:特征表达能力受限,不适合所有任务。

深度可分离卷积的应用

  • 广泛应用于移动设备和嵌入式设备上的轻量化模型(如 MobileNet 和 Xception)。

6. 代码实践

回顾了深度可分离卷积的相关理论内容后,接下来通过动手实践来进一步加深理解。

1、首先我们不使用任何深度学习模型的框架来实现深度可分离卷积

这里主要是包括:DW卷积实现、PW卷积实现和深度可分离卷积实现,基于Nump模块来完成对应的开发工作,代码如下:

def depthwiseConv2d(input_feature_map, depthwise_kernel, stride=1, padding=0):
    """
    深度卷积(Depthwise Convolution)
    :param input_feature_map: 输入特征图,形状为 (H, W, C_in)
    :param depthwise_kernel: 深度卷积核,形状为 (K_h, K_w, 1, 1)
    :param stride: 卷积步幅
    :param padding: 填充大小
    :return: 输出特征图,形状为 (H', W', C_in)
    """
    H, W, C_in = input_feature_map.shape
    K_h, K_w, _, _ = depthwise_kernel.shape
    # 填充输入特征图
    if padding > 0:
        input_feature_map = np.pad(
            input_feature_map,
            ((padding, padding), (padding, padding), (0, 0)),
            mode="constant",
        )
        H_pad, W_pad, _ = input_feature_map.shape
    else:
        H_pad, W_pad = H, W
    # 计算输出特征图的尺寸
    H_out = (H_pad - K_h) // stride + 1
    W_out = (W_pad - K_w) // stride + 1
    # 初始化输出特征图
    output_feature_map = np.zeros((H_out, W_out, C_in))
    # 执行深度卷积
    for c in range(C_in):
        for i in range(0, H_pad - K_h + 1, stride):
            for j in range(0, W_pad - K_w + 1, stride):
                # 提取局部区域
                region = input_feature_map[i : i + K_h, j : j + K_w, c]
                # 计算卷积结果
                output_feature_map[i // stride, j // stride, c] = np.sum(
                    region * depthwise_kernel[:, :, 0, 0]
                )
    return output_feature_map


def pointwiseConv2d(input_feature_map, pointwise_kernel):
    """
    逐点卷积(Pointwise Convolution)
    :param input_feature_map: 输入特征图,形状为 (H, W, C_in)
    :param pointwise_kernel: 逐点卷积核,形状为 (1, 1, C_in, C_out)
    :return: 输出特征图,形状为 (H, W, C_out)
    """
    H, W, C_in = input_feature_map.shape
    _, _, _, C_out = pointwise_kernel.shape
    # 初始化输出特征图
    output_feature_map = np.zeros((H, W, C_out))
    # 执行逐点卷积
    for c_out in range(C_out):
        for c_in in range(C_in):
            output_feature_map[:, :, c_out] += (
                input_feature_map[:, :, c_in] * pointwise_kernel[0, 0, c_in, c_out]
            )
    return output_feature_map


def depthwiseSeparableConv2dNumpy(
    input_feature_map, depthwise_kernel, pointwise_kernel, stride=1, padding=0
):
    """
    深度可分离卷积(Depthwise Separable Convolution)
    :param input_feature_map: 输入特征图,形状为 (H, W, C_in)
    :param depthwise_kernel: 深度卷积核,形状为 (K_h, K_w, 1, 1)
    :param pointwise_kernel: 逐点卷积核,形状为 (1, 1, C_in, C_out)
    :param stride: 卷积步幅
    :param padding: 填充大小
    :return: 输出特征图,形状为 (H', W', C_out)
    """
    # 第一步:深度卷积(DW)
    depthwise_output = depthwiseConv2d(
        input_feature_map, depthwise_kernel, stride, padding
    )
    # 第二步:逐点卷积(PW)
    pointwise_output = pointwiseConv2d(depthwise_output, pointwise_kernel)
    return pointwise_output

实例调用执行代码如下:

# 基于numpy实现
input_feature_map = np.random.randn(32, 32, 3)  # 输入特征图,形状为 (32, 32, 3)
# 深度卷积核
depthwise_kernel = np.random.randn(3, 3, 1, 1)  # 深度卷积核,形状为 (3, 3, 1, 1)
# 逐点卷积核
pointwise_kernel = np.random.randn(1, 1, 3, 16)  # 逐点卷积核,形状为 (1, 1, 3, 16)
# 执行深度可分离卷积
output_feature_map = depthwiseSeparableConv2dNumpy(
    input_feature_map, depthwise_kernel, pointwise_kernel, stride=1, padding=1
)
print("输出特征图的形状:", output_feature_map.shape)
# 计算参数量
depthwise_params = np.prod(depthwise_kernel.shape)  # 深度卷积的参数量
pointwise_params = np.prod(pointwise_kernel.shape)  # 逐点卷积的参数量
total_params = depthwise_params + pointwise_params  # 总参数量
# 计算计算量
H, W, C_in = input_feature_map.shape
K_h, K_w, _, _ = depthwise_kernel.shape
_, _, _, C_out = pointwise_kernel.shape
H_out = (H + 2 * 1 - K_h) // 1 + 1  # 深度卷积的输出特征图大小
W_out = (W + 2 * 1 - K_w) // 1 + 1
depthwise_flops = H_out * W_out * K_h * K_w * C_in  # 深度卷积的计算量
pointwise_flops = H_out * W_out * C_in * C_out  # 逐点卷积的计算量
total_flops = depthwise_flops + pointwise_flops  # 总计算量
print("深度卷积的参数量:", depthwise_params)
print("逐点卷积的参数量:", pointwise_params)
print("总参数量:", total_params)
print("深度卷积的计算量:", depthwise_flops)
print("逐点卷积的计算量:", pointwise_flops)
print("总计算量:", total_flops)

运行输出如下所示:

2、使用Pytorch框架来实现

这里主要开始借助于主流的深度学习框架比如Pytorch来进行深度可分离卷积块的实现,并分析预支对应的参数量和计算量,代码实现如下所示:

class DepthwiseSeparableConvPytorch(nn.Module):
    def __init__(
        self, input_channels, output_channels, kernel_size=3, stride=1, padding=1
    ):
        super(DepthwiseSeparableConvPytorch, self).__init__()
        # 深度卷积(Depthwise Convolution)
        self.depthwise = nn.Conv2d(
            in_channels=input_channels,
            out_channels=input_channels,
            kernel_size=kernel_size,
            stride=stride,
            padding=padding,
            groups=input_channels,  # 分组卷积,每个通道独立卷积
            bias=False,
        )
        # 逐点卷积(Pointwise Convolution)
        self.pointwise = nn.Conv2d(
            in_channels=input_channels,
            out_channels=output_channels,
            kernel_size=1,
            stride=1,
            padding=0,
            bias=False,
        )
        # 批归一化
        self.bn = nn.BatchNorm2d(output_channels)
        # 激活函数
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        # 深度卷积
        x = self.depthwise(x)
        # 逐点卷积
        x = self.pointwise(x)
        # 批归一化
        x = self.bn(x)
        # 激活函数
        x = self.relu(x)
        return x

实例调用执行如下所示:

#使用Pytorch来实现
input_channels = 3        # 输入通道数
output_channels = 16      # 输出通道数
model = DepthwiseSeparableConvPytorch(input_channels, output_channels)
# 打印模型结构
print(model)
# 测试模型
input_tensor = torch.randn(1, 3, 32, 32)  # 输入张量,形状为 (batch_size, C_in, H, W)
output_tensor = model(input_tensor)
print("输出张量的形状:", output_tensor.shape)
#计算参数量
summary(model, input_size=(3, 32, 32))

执行输出如下所示:

3、使用Keras框架来实现

Keras框架本身内置的有深度可分离卷积层可以直接使用,如下所示:

def depthwise_separable_conv_keras(
    input_shape, output_channels, kernel_size=3, stride=1, padding="same"
):
    """
    使用 Keras 实现深度可分离卷积
    :param input_shape: 输入特征图的形状 (H, W, C_in)
    :param output_channels: 输出通道数
    :param kernel_size: 卷积核大小
    :param stride: 卷积步幅
    :param padding: 填充方式 ('same' 或 'valid')
    :return: Keras 模型
    """
    # 输入层
    inputs = Input(shape=input_shape)
    # 深度可分离卷积层
    x = SeparableConv2D(
        filters=output_channels,
        kernel_size=kernel_size,
        strides=stride,
        padding=padding,
        depth_multiplier=1,  # 深度卷积的输出通道数是输入通道数的倍数
        use_bias=False,
    )(inputs)
    # 批归一化
    x = BatchNormalization()(x)
    # 激活函数
    x = ReLU()(x)
    # 构建模型
    model = Model(inputs, x)
    return model

实例调用执行如下所示:

#使用Keras内置的SeparableConv2D层来实现
input_shape = (32, 32, 3)  # 输入特征图的形状 (H, W, C_in)
output_channels = 16       # 输出通道数
model = depthwise_separable_conv_keras(input_shape, output_channels)
model.summary()

代码执行输出如下所示:

4、不使用Keras内置的SeparableConv2D层进行实现

这里主要是参照Pytorch的实现思路使用普通的卷积层来实现深度可分离卷积,代码实现如下所示:

class DepthwiseSeparableConvKeras(Layer):
    def __init__(self, output_channels, kernel_size=3, stride=1, padding="same"):
        """
        自定义深度可分离卷积层
        :param output_channels: 输出通道数
        :param kernel_size: 卷积核大小
        :param stride: 卷积步幅
        :param padding: 填充方式 ('same' 或 'valid')
        """
        super(DepthwiseSeparableConvKeras, self).__init__()
        self.output_channels = output_channels
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding

    def build(self, input_shape):
        # 输入通道数
        input_channels = input_shape[-1]
        # 深度卷积(Depthwise Convolution)
        self.depthwise_conv = Conv2D(
            filters=input_channels,  # 输出通道数与输入通道数相同
            kernel_size=self.kernel_size,
            strides=self.stride,
            padding=self.padding,
            groups=input_channels,  # 分组卷积,每个通道独立卷积
            use_bias=False,
        )
        # 逐点卷积(Pointwise Convolution)
        self.pointwise_conv = Conv2D(
            filters=self.output_channels,  # 输出通道数
            kernel_size=1,
            strides=1,
            padding="same",
            use_bias=False,
        )
        # 批归一化
        self.bn = BatchNormalization()
        # 激活函数
        self.relu = ReLU()

    def call(self, inputs):
        # 深度卷积
        x = self.depthwise_conv(inputs)
        # 逐点卷积
        x = self.pointwise_conv(x)
        # 批归一化
        x = self.bn(x)
        # 激活函数
        x = self.relu(x)
        return x

实例调用执行如下所示:

#使用Keras普通卷积Conv2D层来实现
input_shape = (32, 32, 3)  # 输入特征图的形状 (H, W, C_in)
output_channels = 16       # 输出通道数
# 输入层
inputs = Input(shape=input_shape)
# 自定义深度可分离卷积层
x = DepthwiseSeparableConvKeras(output_channels)(inputs)
# 构建模型
model = Model(inputs, x)
model.summary()

执行输出如下所示:

通过对比二者结果不难发现,参数量是一致的:

SeparableConv2D层结果:
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 input_1 (InputLayer)        [(None, 32, 32, 3)]       0

 separable_conv2d (Separabl  (None, 32, 32, 16)        75
 eConv2D)

 batch_normalization (Batch  (None, 32, 32, 16)        64
 Normalization)

 re_lu (ReLU)                (None, 32, 32, 16)        0

=================================================================
Total params: 139 (556.00 Byte)
Trainable params: 107 (428.00 Byte)
Non-trainable params: 32 (128.00 Byte)
_________________________________________________________________


自主实现结果:
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 input_1 (InputLayer)        [(None, 32, 32, 3)]       0

 depthwise_separable_conv_k  (None, 32, 32, 16)        139
 eras (DepthwiseSeparableCo
 nvKeras)

=================================================================
Total params: 139 (556.00 Byte)
Trainable params: 107 (428.00 Byte)
Non-trainable params: 32 (128.00 Byte)
_________________________________________________________________

从而也能印证借助于普通的Conv2d卷积层实现的深度可分离卷积块与内置的SeparableConv2D层达到的效果是一致的。

整体回顾学习与实践记录就到这里,感兴趣的话可以参考一下!

标签:kernel,回顾,output,卷积,优缺点,channels,深度,input
From: https://blog.csdn.net/Together_CZ/article/details/144581295

相关文章

  • 【后端面试总结】Redis的三种模式原理介绍及优缺点
    Redis作为一款高性能的键值对数据库,提供了多种模式以满足不同场景下的需求。本文将详细介绍Redis的三种主要模式:主从复制模式、哨兵模式(Sentinel)和集群模式(Cluster),包括它们的原理、配置、优缺点以及应用场景。一、主从复制模式(Master-Slave)原理介绍主从复制模式是Redis最......
  • java 快速排序,原理、算法分析、实现细节、优缺点以及一些实际应用场景
    更多资源推荐:http://sj.ysok.net/jydoraemon提取码:JYAM实用优质资源/教程公众号【纪元A梦】 ###快速排序的详细解析探讨快速排序,包括其工作原理、算法分析、实现细节、优缺点以及一些实际应用场景。####1.基本概念快速排序是一种基于分治法的高效排序算法。其基本思想是选......
  • 基于vgg16和efficientnet卷积神经网络的天气识别系统(pytorch框架) 图像识别与分类 前
    基于vgg16和efficientnet卷积神经网络的天气识别系统(pytorch框架)前端界面:flask+python,UI界面:pyqt5+python这是一个完整项目,包括代码,数据集,模型训练记录,前端界面,ui界面,各种指标图:包括准确率,精确率,召回率,F1值,损失曲线,准确率曲线等卷积模型采用vgg16模型或efficien......
  • 当前AIGC研究回顾—CV类
    AI技术本质是数据驱动(Data-Driven),模型能有效学习庞大数据,需要与数据规模相匹配的可学习参数规模(也要有对应的算力)因此,技术可分为两条主线:数据数据如果在模型外,主要就是数据集,更多是质量和标注的问题,即预处理数据如果在模型内,则主要操作的是潜空间(LatentSpace),......
  • java 归并排序,原理、算法分析、实现细节、优缺点以及一些实际应用场景
    更多资源推荐:http://sj.ysok.net/jydoraemon提取码:JYAM实用优质资源/教程公众号【纪元A梦】###归并排序的详细解析探讨归并排序,包括其工作原理、算法分析、实现细节、优缺点以及一些实际应用场景。####1.基本概念归并排序是一种基于分治法的高效排序算法。它的基本思想是将......
  • 说说你对推荐算法的理解,它有哪些运用场景?你认为它的优缺点是什么?
    推荐算法是计算机专业中的一种重要算法,它通过一些数学方法,基于用户的历史行为数据和物品特征信息,推测出用户可能感兴趣的内容,并向用户进行推荐。这种算法在各个领域都有广泛的应用,以下是我对推荐算法的理解以及它的运用场景、优缺点的分析:一、理解推荐算法的核心思想是利用用户......
  • java 插入排序,原理、算法分析、实现细节、优缺点以及一些实际应用场景
    更多资源推荐:http://sj.ysok.net/jydoraemon提取码:JYAM实用优质资源/教程公众号【纪元A梦】 ###插入排序的详细解析探讨插入排序,包括其工作原理、算法分析、实现细节、优缺点以及一些实际应用场景。####1.基本概念插入排序是一种简单的排序算法,其核心思想是将数组分为已排......
  • 深度学习——卷积神经网络(六)
    本章介绍的卷积神经⽹络(convolutionalneuralnetwork,CNN)是⼀类强⼤的、为处理图像数据⽽设计的神经⽹络。卷积神经⽹络需要的参数少于全连接架构的⽹络,⽽且卷积也很容易⽤GPU并⾏计算。因此卷积神经⽹络除了能够⾼效地采样从⽽获得精确的模型,还能够⾼效地计算。在本章的......
  • (即插即用模块-Convolution部分) 六、(TIP 2020) SlimConv 超薄卷积
    文章目录1、SlimConv2、代码实现paper:SlimConv:ReducingChannelRedundancyinConvolutionalNeuralNetworksbyWeightsFlippingCode:https://github.com/JiaxiongQ/SlimConv1、SlimConv为了解决卷积神经网络(CNN)中特征图通道冗余的问题,通道冗余是指特......
  • MyBatis-Plus的优缺点?
    优点:1.简化开发:MyBatis-Plus封装了很多CRUD操作,使得我们不需要手写大量的SQL语句,从而减少了开发时间和代码量。2.提高性能:MyBatis-Plus的分页插件和缓存插件等能够提高SQL执行的效率和性能。3.提供了代码生成器:MvBatis-Plus提供了一款强大的代码生成器,能够根据数据库表自......