深入浅出理解深度可分离卷积(Depthwise Separable Convolution)

news2024/12/26 10:35:15

一、参考资料

详细且通俗讲解轻量级神经网络——MobileNets【V1、V2、V3】
详细且通俗讲解轻量级神经网络——MobileNets【V1、V2、V3】
卷积神经网络中的Separable Convolution
深度学习中常用的几种卷积(下篇):膨胀卷积、可分离卷积(深度可分离、空间可分离)、分组卷积(附Pytorch测试代码)

二、相关介绍

1. 标准卷积

标准卷积,利用若干个多通道卷积核对输入的多通道图像进行处理,输出的feature map既提取了通道特征,又提取了空间特征

如下图所示,假设输入层为一个大小为64×64像素、3通道彩色图片。经过一个包含4个Filter的卷积层,最终输出4个Feature Map,且尺寸与输入层相同。此时,卷积层共4个Filter,每个Filter包含了3个Kernel,每个Kernel的大小为3×3。因此,卷积层的参数量为: N s t d = 4 × 3 × 3 × 3 = 108 N_{std} = 4 × 3 × 3 × 3 = 108 Nstd=4×3×3×3=108
在这里插入图片描述

用数学公式表达标准卷积,假设卷积核大小为 D K ∗ D K D_K*D_K DKDK,输入通道为M,输出通道为N,输出的特征图尺寸为 D F ∗ D F D_F*D_F DFDF,则通过标准卷积之后,可以计算:

参数量为: D K ∗ D K ∗ M ∗ N D_K*D_K*M*N DKDKMN

计算量为: D K ∗ D K ∗ M ∗ N ∗ D F ∗ D F D_K*D_K*M*N*D_F*D_F DKDKMNDFDF

2. 逐深度卷积(Depthwise Convolution)

逐深度卷积(Depthwise convolution,DWConv)与标准卷积的区别在于,深度卷积的卷积核为单通道模式,需要对输入的每一个通道进行卷积,这样就会得到和输入特征图通道数一致的输出特征图。即有输入特征图通道数=卷积核个数=输出特征图个数

假设,一个大小为64×64像素、3通道彩色图片,3个单通道卷积核分别进行卷积计算,输出3个单通道的特征图。所以,一个3通道的图像经过运算后生成了3个Feature map,如下图所示。其中一个Filter只包含一个大小为3×3的Kernel,卷积部分的参数量为: N d e p t h w i s e = 3 × 3 × 3 = 27 N_{depthwise} = 3 × 3 × 3 = 27 Ndepthwise=3×3×3=27
在这里插入图片描述

再比如,输入12x12x3的特征图,卷积个数为3,输出8x8x3的特征图。
在这里插入图片描述

3. 逐点卷积(Pointwise Convolution)

根据深度卷积可知,输入特征图通道数=卷积核个数=输出特征图个数,这样会导致输出的特征图个数过少(或者说输出特征图的通道数过少,可看成是输出特征图个数为1,通道数为3),从而可能影响信息的有效性。此时,就需要进行逐点卷积。

逐点卷积(Pointwise Convolution,PWConv)实质上是用1x1的卷积核进行升维。在GoogleNet中大量使用1x1的卷积核,那里主要是用来降维。1x1的卷积核主要作用是对特征图进行升维和降维。

如下图所示,从深度卷积得到的3个单通道特征图,经过4个大小为1x1x3卷积核的卷积计算,输出4个特征图,而输出特征图的个数取决于Filter的个数。因此,卷积层的参数量为: N p o i n t w i s e = 1 × 1 × 3 × 4 = 12 N_{pointwise} = 1 × 1 × 3 × 4 = 12 Npointwise=1×1×3×4=12
在这里插入图片描述

再比如,当进行深度卷积之后,得到8x8x3的特征,此时用256个大小为1x1x3的卷积进行卷积计算,输出为8x8x256。因此,卷积层的参数量为: N p o i n t w i s e = 1 × 1 × 3 × 256 = 768 N_{pointwise} = 1 × 1 × 3 × 256 = 768 Npointwise=1×1×3×256=768
在这里插入图片描述

4. 深度可分离卷积(Depthwise Separable Convolution)

深度可分离卷积(Depthwise separable convolution, DSC)由深度卷积和逐点卷积组成,深度卷积用于提取空间特征,逐点卷积用于提取通道特征。深度可分离卷积在特征维度上分组卷积,对每个channel进行独立的深度卷积(depthwise convolution),并在输出前使用一个1x1卷积(pointwise convolution)将所有通道进行聚合。

depthwise :在空间上进行卷积

pointwise : 在深度上进行卷积

深度可分离卷积( D e p t h w i s e S e p a r a b l e C o n v o l u t i o n ) = d e p t h w i s e ( 深度卷积 ) + p o i n t w i s e ( 点卷积 ) \textcolor{red}{深度可分离卷积(Depthwise Separable Convolution)=depthwise(深度卷积)+pointwise(点卷积)} 深度可分离卷积(DepthwiseSeparableConvolution=depthwise(深度卷积)+pointwise(点卷积)
在这里插入图片描述

深度可分离卷积,先对每个channel进行DWConv,然后再通过 PWConv合并所有channels为输出特征图,从而达到减小计算量、提升计算效率的目的。

4.1 参数量

逐深度卷积:深度卷积的卷积核尺寸 D K ∗ D K ∗ 1 D_K * D_K * 1 DKDK1,卷积核个数为M,所以参数量为: D K ∗ D K ∗ M D_K * D_K * M DKDKM

逐点卷积:逐点卷积的卷积核尺寸为 1 ∗ 1 ∗ M 1 * 1 * M 11M,卷积核个数为N,所以参数量为: M ∗ N M * N MN

因此,深度可分离卷积的参数量为: D K ∗ D K ∗ M + M ∗ N D_K * D_K * M + M * N DKDKM+MN

4.2 计算量

深度卷积:深度卷积的卷积核尺寸 D K ∗ D K ∗ 1 D_K * D_K * 1 DKDK1,卷积核个数为M,每个都要做 D F ∗ D F D_F * D_F DFDF次乘加运算,所以计算量为: D K ∗ D K ∗ M ∗ D F ∗ D F D_K * D_K * M * D_F * D_F DKDKMDFDF

逐点卷积:逐点卷积的卷积核尺寸为 1 ∗ 1 ∗ M 1 * 1 * M 11M,卷积核个数为N,每个都要做 D F ∗ D F D_F * D_F DFDF次乘加运算,所以计算量为: M ∗ N ∗ D F ∗ D F M*N * D_F * D_F MNDFDF

因此,深度可分离卷积的计算量为: D K ∗ D K ∗ M ∗ D F ∗ D F + M ∗ N ∗ D F ∗ D F D_K * D_K * M*D_F * D_F + M*N*D_F * D_F DKDKMDFDF+MNDFDF

4.3 与标准卷积对比

4.3.1 结构对比

深度可分离卷积的每个块构成:首先是一个3x3的深度卷积,其次是BN、Relu层,接下来是1x1的逐点卷积,最后又是BN和Relu层。
在这里插入图片描述

4.3.2 计算量和参数量对比

参数量比值: 深度可分离卷积 标准卷积 = D K × D K × M + M × N D K × D K × M × N = 1 N + 1 D K 2 \frac{深度可分离卷积}{标准卷积} = \frac{D_K\times D_K\times M+M\times N}{D_K\times D_K\times M\times N} = \frac{1}{N}+\frac{1}{{D_{K}}^{2}} 标准卷积深度可分离卷积=DK×DK×M×NDK×DK×M+M×N=N1+DK21

计算量比值: 深度可分离卷积 标准卷积 = D K × D K × M × D F × D F + M × N × D F × D F D K × D K × M × N × D F × D F = 1 N + 1 D K 2 \frac{深度可分离卷积}{标准卷积} = \frac{D_{K}\times D_{K}\times M\times D_{F}\times D_{F}+M\times N\times D_{F}\times D_{F}}{D_{K}\times D_{K}\times M\times N\times D_{F}\times D_{F}} = \frac{1}{N}+\frac{1}{{D_{K}}^{2}} 标准卷积深度可分离卷积=DK×DK×M×N×DF×DFDK×DK×M×DF×DF+M×N×DF×DF=N1+DK21
在这里插入图片描述

一般的,N较大, 1 N \frac{1}{N} N1 可忽略不计, D K D_K DK 表示卷积核的大小,若 D K = 3 D_K =3 DK=3 1 D k 2 = 1 9 \frac{1}{D_k^2}=\frac{1}{9} Dk21=91 。也就是说,如果我们使用常见的3×3的卷积核,那么使用深度可分离卷积的参数量和计算量下降到原来的九分之一左右。

4.4 深度可分离卷积的优势

相比于传统的卷积神经网络,深度可分离卷积的显著优势在于:

  • 更少的参数:可减少输入通道数量,从而有效地减少卷积层所需的参数。
  • 更快的速度:运行速度比传统卷积快。
  • 更加易于移植:计算量更小,更易于实现和部署在不同的平台上。
  • 更加精简:能够精简计算模型,从而在较小的设备上实现高精度的运算。

5. 代码示例

import torch.nn as nn

class myModel(nn.Module):
    def __init__(self):
        super(myModel, self).__init__()
        self.dwconv = nn.Sequential(
            nn.Conv2d(3, 3, kernel_size=3, stride=2, padding=1, groups=3, bias=False),
            nn.BatchNorm2d(3),
            nn.ReLU(inplace=True),
            nn.Conv2d(3, 9, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(9),
            nn.ReLU(inplace=True),
        )

yolo系列里的代码如下:

import torch.nn as nn


class DWConv(nn.Module):
    """Depthwise Conv + Conv"""
    def __init__(self, in_channels, out_channels, ksize, stride=1, act="silu"):
        super().__init__()
        self.dconv = BaseConv(
            in_channels, in_channels, ksize=ksize,
            stride=stride, groups=in_channels, act=act
        )
        self.pconv = BaseConv(
            in_channels, out_channels, ksize=1,
            stride=1, groups=1, act=act
        )
 
    def forward(self, x):
        x = self.dconv(x)
        return self.pconv(x)

三、MobileNet v1

论文:MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications
MobileNet v1 和 MobileNet v2
MobileNet网络详解
【深度学习】轻量化CNN网络MobileNet系列详解
MobileNet V1 图像分类

MobileNet v1是专注于移动端或者嵌入式设备这种计算量不是特别大的轻量级CNN网络。如图下图所示,MobileNet v1只是牺牲了一点精度,却大大减少模型的参数量和运算量。
在这里插入图片描述

1. 网络结构

MobileNet v1最主要的贡献是使用了 Depthwise Separable Convolution,它又可以拆分成Depthwise卷积和Pointwise卷积。Depthwise Separable Convolution 的分离式设计直接将模型压缩了8倍左右,但是精度并没有损失非常严重,这一点还是非常震撼的。
在这里插入图片描述

轻量级网络的MobileNet v1计算量和参数量均小于GoogleNet,同时在分类效果上比GoogleNet还要好,这就是深度可分离卷积的功劳了。VGG16的计算量参数量比MobileNet大了30倍,但是结果也仅仅只高了1%不到。
在这里插入图片描述

2. 优势

首先,MobileNet v1提出的深度可分离卷积可以大大减少计算量和参数量;其次,就是增加超参数α、ρ可以根据需求调节网络的宽度和分辨率。

具体来说,超参数α是为了控制卷积核的个数,也就是输出的channel,因此α可以减少模型的参数量;超参数ρ是为了控制图像输入的size,是不会影响模型的参数,但是可以减少计算量。

网络的宽度,代表卷积层的维度,也就是channel,例如512,1024

网络的深度,代表卷积层的层数,也就是网络有多深,例如resnet34、resnet101

3. 代码实现

3.1 搭建MobileNet v1网络模型

import torch.nn as nn
 
 
# MobileNet v1
class MobileNetV1(nn.Module):
    def __init__(self,num_classes=1000):
        super(MobileNetV1, self).__init__()
 
        # 第一层的卷积,channel->32,size减半
        def conv_bn(in_channel, out_channel, stride):
            return nn.Sequential(
                nn.Conv2d(in_channel, out_channel, 3, stride, 1, bias=False),
                nn.BatchNorm2d(out_channel),
                nn.ReLU(inplace=True)
            )
 
        # 深度可分离卷积=depthwise卷积 + pointwise卷积
        def conv_dw(in_channel, out_channel, stride):
            return nn.Sequential(
                # depthwise 卷积,channel不变,stride = 2的时候,size减半
                nn.Conv2d(in_channel, in_channel, 3, stride, padding=1, groups=in_channel, bias=False),
                nn.BatchNorm2d(in_channel),
                nn.ReLU(inplace=True),
 
                # pointwise卷积(1*1卷积) same卷积, 只改变channel
                nn.Conv2d(in_channel, out_channel, 1, 1, padding=0, bias=False),
                nn.BatchNorm2d(out_channel),
                nn.ReLU(inplace=True),
            )
 
        self.model = nn.Sequential(
            conv_bn(3, 32, 2),          # conv/s2           out=224*224*32
            conv_dw(32, 64, 1),         # conv dw +1*1      out=112*112*64
            conv_dw(64, 128, 2),        # conv dw +1*1      out=56*56*128
            conv_dw(128, 128, 1),       # conv dw +1*1      out=56*56*128
            conv_dw(128, 256, 2),       # conv dw +1*1      out=28*28*256
            conv_dw(256, 256, 1),       # conv dw +1*1      out=28*28*256
            conv_dw(256, 512, 2),       # conv dw +1*1      out=14*14*512
            conv_dw(512, 512, 1),       # 5个 conv dw +1*1 ----> size不变,channel不变,out=14*14*512
            conv_dw(512, 512, 1),
            conv_dw(512, 512, 1),
            conv_dw(512, 512, 1),
            conv_dw(512, 512, 1),
            conv_dw(512, 1024, 2),      # conv dw +1*1      out=7*7*1024
            conv_dw(1024, 1024, 1),     # conv dw +1*1      out=7*7*1024
            nn.AvgPool2d(7),            # avg pool          out=1*1*1024
        )
        self.fc = nn.Linear(1024, num_classes)      # fc
 
    def forward(self, x):
        x = self.model(x)
        x = x.view(-1, 1024)
        x = self.fc(x)
        return x

3.2 torchsummary查看网络结构

# 安装torchsummary
pip install torchsummary

使用 torchsummary 查看网络结构:

from torchsummary import summary
import torch
 
 
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
net = MobileNetV1()
net.to(DEVICE)
print(summary(net, input_size=(3, 224, 224),device=DEVICE))

输出结果:

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1         [-1, 32, 112, 112]             864
       BatchNorm2d-2         [-1, 32, 112, 112]              64
              ReLU-3         [-1, 32, 112, 112]               0
            Conv2d-4         [-1, 32, 112, 112]             288
       BatchNorm2d-5         [-1, 32, 112, 112]              64
              ReLU-6         [-1, 32, 112, 112]               0
            Conv2d-7         [-1, 64, 112, 112]           2,048
       BatchNorm2d-8         [-1, 64, 112, 112]             128
              ReLU-9         [-1, 64, 112, 112]               0
           Conv2d-10           [-1, 64, 56, 56]             576
      BatchNorm2d-11           [-1, 64, 56, 56]             128
             ReLU-12           [-1, 64, 56, 56]               0
           Conv2d-13          [-1, 128, 56, 56]           8,192
      BatchNorm2d-14          [-1, 128, 56, 56]             256
             ReLU-15          [-1, 128, 56, 56]               0
           Conv2d-16          [-1, 128, 56, 56]           1,152
      BatchNorm2d-17          [-1, 128, 56, 56]             256
             ReLU-18          [-1, 128, 56, 56]               0
           Conv2d-19          [-1, 128, 56, 56]          16,384
      BatchNorm2d-20          [-1, 128, 56, 56]             256
             ReLU-21          [-1, 128, 56, 56]               0
           Conv2d-22          [-1, 128, 28, 28]           1,152
      BatchNorm2d-23          [-1, 128, 28, 28]             256
             ReLU-24          [-1, 128, 28, 28]               0
           Conv2d-25          [-1, 256, 28, 28]          32,768
      BatchNorm2d-26          [-1, 256, 28, 28]             512
             ReLU-27          [-1, 256, 28, 28]               0
           Conv2d-28          [-1, 256, 28, 28]           2,304
      BatchNorm2d-29          [-1, 256, 28, 28]             512
             ReLU-30          [-1, 256, 28, 28]               0
           Conv2d-31          [-1, 256, 28, 28]          65,536
      BatchNorm2d-32          [-1, 256, 28, 28]             512
             ReLU-33          [-1, 256, 28, 28]               0
           Conv2d-34          [-1, 256, 14, 14]           2,304
      BatchNorm2d-35          [-1, 256, 14, 14]             512
             ReLU-36          [-1, 256, 14, 14]               0
           Conv2d-37          [-1, 512, 14, 14]         131,072
      BatchNorm2d-38          [-1, 512, 14, 14]           1,024
             ReLU-39          [-1, 512, 14, 14]               0
           Conv2d-40          [-1, 512, 14, 14]           4,608
      BatchNorm2d-41          [-1, 512, 14, 14]           1,024
             ReLU-42          [-1, 512, 14, 14]               0
           Conv2d-43          [-1, 512, 14, 14]         262,144
      BatchNorm2d-44          [-1, 512, 14, 14]           1,024
             ReLU-45          [-1, 512, 14, 14]               0
           Conv2d-46          [-1, 512, 14, 14]           4,608
      BatchNorm2d-47          [-1, 512, 14, 14]           1,024
             ReLU-48          [-1, 512, 14, 14]               0
           Conv2d-49          [-1, 512, 14, 14]         262,144
      BatchNorm2d-50          [-1, 512, 14, 14]           1,024
             ReLU-51          [-1, 512, 14, 14]               0
           Conv2d-52          [-1, 512, 14, 14]           4,608
      BatchNorm2d-53          [-1, 512, 14, 14]           1,024
             ReLU-54          [-1, 512, 14, 14]               0
           Conv2d-55          [-1, 512, 14, 14]         262,144
      BatchNorm2d-56          [-1, 512, 14, 14]           1,024
             ReLU-57          [-1, 512, 14, 14]               0
           Conv2d-58          [-1, 512, 14, 14]           4,608
      BatchNorm2d-59          [-1, 512, 14, 14]           1,024
             ReLU-60          [-1, 512, 14, 14]               0
           Conv2d-61          [-1, 512, 14, 14]         262,144
      BatchNorm2d-62          [-1, 512, 14, 14]           1,024
             ReLU-63          [-1, 512, 14, 14]               0
           Conv2d-64          [-1, 512, 14, 14]           4,608
      BatchNorm2d-65          [-1, 512, 14, 14]           1,024
             ReLU-66          [-1, 512, 14, 14]               0
           Conv2d-67          [-1, 512, 14, 14]         262,144
      BatchNorm2d-68          [-1, 512, 14, 14]           1,024
             ReLU-69          [-1, 512, 14, 14]               0
           Conv2d-70            [-1, 512, 7, 7]           4,608
      BatchNorm2d-71            [-1, 512, 7, 7]           1,024
             ReLU-72            [-1, 512, 7, 7]               0
           Conv2d-73           [-1, 1024, 7, 7]         524,288
      BatchNorm2d-74           [-1, 1024, 7, 7]           2,048
             ReLU-75           [-1, 1024, 7, 7]               0
           Conv2d-76           [-1, 1024, 7, 7]           9,216
      BatchNorm2d-77           [-1, 1024, 7, 7]           2,048
             ReLU-78           [-1, 1024, 7, 7]               0
           Conv2d-79           [-1, 1024, 7, 7]       1,048,576
      BatchNorm2d-80           [-1, 1024, 7, 7]           2,048
             ReLU-81           [-1, 1024, 7, 7]               0
        AvgPool2d-82           [-1, 1024, 1, 1]               0
           Linear-83                 [-1, 1000]       1,025,000
================================================================
Total params: 4,231,976
Trainable params: 4,231,976
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 115.43
Params size (MB): 16.14
Estimated Total Size (MB): 132.15
----------------------------------------------------------------
None

3.3 train训练模型

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from model import MobileNetV1
from torch.utils.data import DataLoader
from tqdm import tqdm
 
 
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
data_transform = {
    "train" : transforms.Compose([transforms.Resize((224,224)),
                                  transforms.ToTensor(),
                                  transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.255])]),
    "test": transforms.Compose([transforms.Resize((224,224)),
                                 transforms.ToTensor(),
                                 transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.255])])}
 
# 训练集
trainset = datasets.CIFAR10(root='./data', train=True, download=True, transform=data_transform['train'])
trainloader = DataLoader(trainset, batch_size=16, shuffle=True)
 
# 测试集
testset = datasets.CIFAR10(root='./data', train=False, download=True, transform=data_transform['test'])
testloader = DataLoader(testset, batch_size=16, shuffle=False)
 
# 样本的个数
num_trainset = len(trainset)  # 50000
num_testset = len(testset)    # 10000
 
# 构建网络
net =MobileNetV1(num_classes=10)
net.to(DEVICE)
 
# 加载损失和优化器
loss_function = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.0001)
 
best_acc = 0.0
save_path = './MobileNetV1.pth'
 
for epoch in range(10):
    net.train()     # 训练模式
    running_loss = 0.0
    for data in tqdm(trainloader):
        images, labels = data
        images, labels = images.to(DEVICE), labels.to(DEVICE)
 
        optimizer.zero_grad()
        out = net(images)  # 总共有三个输出
        loss = loss_function(out,labels)
        loss.backward()  # 反向传播
        optimizer.step()
 
        running_loss += loss.item()
 
    # test
    net.eval()      # 测试模式
    acc = 0.0
    with torch.no_grad():
        for test_data in tqdm(testloader):
            test_images, test_labels = test_data
            test_images, test_labels = test_images.to(DEVICE), test_labels.to(DEVICE)
 
            outputs = net(test_images)
            predict_y = torch.max(outputs, dim=1)[1]
            acc += (predict_y == test_labels).sum().item()
 
    accurate = acc / num_testset
    train_loss = running_loss / num_trainset
 
    print('[epoch %d] train_loss: %.3f  test_accuracy: %.3f' %
          (epoch + 1, train_loss, accurate))
 
    if accurate > best_acc:
        best_acc = accurate
        torch.save(net.state_dict(), save_path)
 
print('Finished Training')

输出结果:

Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
170499072it [00:30, 5634555.25it/s]                                
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verified
100%|██████████| 3125/3125 [02:18<00:00, 22.55it/s]
100%|██████████| 625/625 [00:12<00:00, 51.10it/s]
[epoch 1] train_loss: 0.101  test_accuracy: 0.516
100%|██████████| 3125/3125 [02:23<00:00, 21.78it/s]
100%|██████████| 625/625 [00:11<00:00, 54.31it/s]
[epoch 2] train_loss: 0.079  test_accuracy: 0.612
100%|██████████| 3125/3125 [02:20<00:00, 22.17it/s]
100%|██████████| 625/625 [00:11<00:00, 54.28it/s]
[epoch 3] train_loss: 0.066  test_accuracy: 0.672
100%|██████████| 3125/3125 [02:21<00:00, 22.09it/s]
100%|██████████| 625/625 [00:11<00:00, 55.52it/s]
[epoch 4] train_loss: 0.056  test_accuracy: 0.722
100%|██████████| 3125/3125 [02:13<00:00, 23.34it/s]
100%|██████████| 625/625 [00:11<00:00, 55.56it/s]
[epoch 5] train_loss: 0.048  test_accuracy: 0.748
100%|██████████| 3125/3125 [02:14<00:00, 23.31it/s]
100%|██████████| 625/625 [00:11<00:00, 52.19it/s]
[epoch 6] train_loss: 0.042  test_accuracy: 0.763
100%|██████████| 3125/3125 [02:14<00:00, 23.18it/s]
100%|██████████| 625/625 [00:11<00:00, 56.05it/s]
[epoch 7] train_loss: 0.035  test_accuracy: 0.781
100%|██████████| 3125/3125 [02:14<00:00, 23.27it/s]
100%|██████████| 625/625 [00:11<00:00, 55.88it/s]
[epoch 8] train_loss: 0.031  test_accuracy: 0.790
100%|██████████| 3125/3125 [02:13<00:00, 23.32it/s]
100%|██████████| 625/625 [00:11<00:00, 55.89it/s]
[epoch 9] train_loss: 0.026  test_accuracy: 0.801
100%|██████████| 3125/3125 [02:15<00:00, 22.99it/s]
100%|██████████| 625/625 [00:11<00:00, 55.95it/s]
[epoch 10] train_loss: 0.022  test_accuracy: 0.803
Finished Training

Process finished with exit code 0

显卡资源占用情况:
在这里插入图片描述

3.4 查看模型权重参数

from model import MobileNetV1
import torch
 
 
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
 
net = MobileNetV1(num_classes=10)
net.load_state_dict(torch.load('./MobileNetV1.pth'))
net.to(DEVICE)
 
 
with torch.no_grad():
    for i in range(0,14):       # 查看 depthwise 的权值
        print(net.model[i][0].weight)

3.5 在CIFAR10数据集上测试效果

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
 
import torch
import numpy as np
import matplotlib.pyplot as plt
from model import MobileNetV1
from torchvision.transforms import transforms
from torch.utils.data import DataLoader
import torchvision
 
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
 
# 预处理
transformer = transforms.Compose([transforms.Resize((224,224)),
                                  transforms.ToTensor(),
                                  transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.255])])
 
# 加载模型
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
model = MobileNetV1(num_classes=10)
model.load_state_dict(torch.load('./MobileNetV1.pth'))
model.to(DEVICE)
 
# 加载数据
testSet = torchvision.datasets.CIFAR10(root='./data', train=False, download=False, transform=transformer)
testLoader = DataLoader(testSet, batch_size=12, shuffle=True)
 
# 获取一批数据
imgs, labels = next(iter(testLoader))
imgs = imgs.to(DEVICE)
 
# show
with torch.no_grad():
    model.eval()
    prediction = model(imgs)  # 预测
    prediction = torch.max(prediction, dim=1)[1]
    prediction = prediction.data.cpu().numpy()
 
    plt.figure(figsize=(12, 8))
    for i, (img, label) in enumerate(zip(imgs, labels)):
        x = np.transpose(img.data.cpu().numpy(), (1, 2, 0))  # 图像
        x[:, :, 0] = x[:, :, 0] * 0.229 + 0.485  # 去 normalization
        x[:, :, 1] = x[:, :, 1] * 0.224 + 0.456  # 去 normalization
        x[:, :, 2] = x[:, :, 2] * 0.255 + 0.406  # 去 normalization
        y = label.numpy().item()  # label
        plt.subplot(3, 4, i + 1)
        plt.axis(False)
        plt.imshow(x)
        plt.title('R:{},P:{}'.format(classes[y], classes[prediction[i]]))
    plt.show()

结果展示:

在这里插入图片描述

四、MobileNet v2

论文:MobileNetV2: Inverted Residuals and Linear Bottlenecks

MobileNet v2主要是将残差网络和Depthwise Separable卷积进行了结合。通过分析单通道的流形特征对残差块进行了改进,包括对中间层的扩展(d)以及bottleneck层的线性激活©。
在这里插入图片描述

0. 引言

特征图的每个通道的像素值所代表的特征可以映射到一个低维子空间的流形区域上。通常,在进行卷积操作之后往往会接一层激活层,以此增加特征的非线性,一个常见的激活函数就是 ReLU。激活过程会带来信息损耗,而且这种损耗是无法恢复的,当通道数非常少时,ReLU的信息损耗更为明显。

如下图所示,其输入是一个表示流形数据的矩阵,和卷积操作类似,经过 n 个ReLU的操作得到 n 个通道的Feature Map,然后通过 n 个Feature Map还原输入数据,还原的越像说明信息损耗的越少。
在这里插入图片描述

从上图可以看出,在输入维度是2,3时,输出和输入相比丢失了较多信息;但是在输入维度是15到30时,输出则保留了输入的较多信息。总得来说,当n值较小时,ReLU的信息损耗非常严重,当n值较大时,输入流形能较好还原

根据对上面提到的信息损耗问题分析,我们可以有两种解决方案:

  1. 替换ReLU:既然是ReLU导致的信息损耗,那么可以将ReLU替换成线性激活函数
  2. 提高维度:如果比较多的通道数能减少信息损耗,那么可以通过升维将输入的维度变高

MobileNet v2的题目为 MobileNetV2: Inverted Residuals and Linear BottlenecksLinear BottlenecksInverted Residuals 就是MobileNet v2的核心,也是上述所说两种思路的描述。

1. Linear Bottlenecks

将Relu激活函数替换成线性激活函数,文章中将变换后的块称为 Linear Bottlenecks,结构如下图所示:
在这里插入图片描述

当然不能把ReLU全部换成线性激活函数,不然网络将会退化为单层神经网络,一个折中方案是在输出Feature Map的通道数较少的时候,也就是bottleneck部分使用线性激活函数,其它时候使用ReLU。Linear Bottlenecks 块的代码实现如下:

def _bottleneck(inputs, nb_filters, t):
    x = Conv2D(filters=nb_filters * t, kernel_size=(1,1), padding='same')(inputs)
    x = Activation(relu6)(x)
    x = DepthwiseConv2D(kernel_size=(3,3), padding='same')(x)
    x = Activation(relu6)(x)
    x = Conv2D(filters=nb_filters, kernel_size=(1,1), padding='same')(x)
    # do not use activation function
    if not K.get_variable_shape(inputs)[3] == nb_filters:
        inputs = Conv2D(filters=nb_filters, kernel_size=(1,1), padding='same')(inputs)
    outputs = add([x, inputs])
    return outputs

2. Inverted Residual

Inverted Residuals直译为倒残差结构,我们来看看其与正常的残差结构有什么区别和联系:通过下图可以看出,左侧为ResNet中的残差结构,其结构为:1x1卷积降维->3x3卷积->1x1卷积升维;右侧为MobileNet v2中的倒残差结构,其结构为:1x1卷积升维->3x3DW卷积->1x1卷积降维。MobileNet v2先使用1x1进行升维的原因是:高维信息通过ReLU激活函数后丢失的信息更少,因此先进行升维操作
在这里插入图片描述

这部分需要注意的是只有当s=1,即步长为1时,才有shortcut连接,步长为2是没有的,如下图所示。

在这里插入图片描述

3. 网络结构

在这里插入图片描述

MobileNet v2所用的参数更少,但mAP值和其它的差不多,甚至超过了Yolov2,其效果如下图所示:
在这里插入图片描述

4. 代码实现

MobileNet v2的实现可以通过堆叠bottleneck的形式实现,如下面代码片段:

def MobileNetV2_relu(input_shape, k):
    inputs = Input(shape = input_shape)
    x = Conv2D(filters=32, kernel_size=(3,3), padding='same')(inputs)
    x = _bottleneck_relu(x, 8, 6)
    x = MaxPooling2D((2,2))(x)
    x = _bottleneck_relu(x, 16, 6)
    x = _bottleneck_relu(x, 16, 6)
    x = MaxPooling2D((2,2))(x)
    x = _bottleneck_relu(x, 32, 6)
    x = GlobalAveragePooling2D()(x)
    x = Dense(128, activation='relu')(x)
    outputs = Dense(k, activation='softmax')(x)
    model = Model(inputs, outputs)
    return model

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1163415.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

postgresql 触发器如何生成递增序列号,从1开始,并且每天重置

大家好&#xff0c;我是三叔&#xff0c;许久不见&#xff0c;这期给大家介绍一下笔者在开发中遇到的业务处理&#xff1a;pgsql 创建触发器生成每日递增序列&#xff0c;并且第二天重置&#xff0c;根据不同的用户进行不同的控制。 1.创建生成递增序列的 table 表 -- 创建us…

新手必看的Facebook广告投放基础思路

一、广告账号要求 如果您还没有Facebook账号&#xff0c;那么第一步是准备Facebook账号。 1、配置正确的网络环境 Facebook账号需要在稳定安全的网络环境中运行&#xff0c;否则很容易导致封禁。像我们常用的是Maskfog指纹浏览器&#xff0c;可以通过自定义浏览器指纹与为环…

计算机网络第4章-网络层(1)

引子 网络层能够被分解为两个相互作用的部分&#xff1a; 数据平面和控制平面。 网络层概述 路由器具有截断的协议栈&#xff0c;即没有网络层以上的部分。 如下图所示&#xff0c;是一个简单网络&#xff1a; 转发和路由选择&#xff1a;数据平面和控制平面 网络层的作用…

Plist编辑软件 PlistEdit Pro mac中文版功能介绍

PlistEdit Pro mac是一款功能强大的Plist文件编辑软件。Plist文件是苹果公司开发的一种XML文件格式&#xff0c;用于存储应用程序的配置信息和数据。PlistEdit Pro可以帮助用户轻松地编辑和管理Plist文件。 PlistEdit Pro具有直观的用户界面和丰富的功能。用户可以使用该软件打…

【计算机网络】网络层——IP

目录 1.概念2.协议格式3.网络划分ip组成IP地址分类IP地址数量私网IP和公网IP子网掩码路由 1.概念 引入 应用层http协议是进行构建和解析请求request和响应response。 传输层的TCP/UDP协议是不提供数据的运输。传输层是为数据传输指定规则。但是&#xff0c;UDP协议并不保证数…

Java八大排序——归并排序

1.什么是归并排序 归并排序是一种基于分治思想的排序算法&#xff0c;它将待排序的序列分成若干个子序列&#xff0c;每个子序列都是有序的&#xff0c;然后再将这些有序的子序列合并成一个有序的序列。归并排序的时间复杂度为O(nlogn)&#xff0c;是一种稳定的排序算法。除了排…

数据分析和用户个性化体验:开发同城外卖APP的技术解决方案

在数字化时代&#xff0c;外卖服务已经成为人们日常生活中不可或缺的一部分。无论是忙碌的白领、学生&#xff0c;还是家庭主妇&#xff0c;都倚赖同城外卖APP来方便地满足他们的饥饿需求。然而&#xff0c;随着竞争的激烈&#xff0c;外卖APP必须不断改进&#xff0c;以满足用…

ajax调用springboot后台接口

工具 api测试工具 由于后台接口不是同一个团队编写的&#xff0c;在文档缺失的情况下&#xff0c;需要测试后台接口接收参数类型&#xff0c;可以使用这个工具&#xff0c;注册很方便 页面如下所示&#xff0c;可以选择请求方法是get&#xff0c;或者post 重点介绍两种&…

SpireCV如何利用TensorRT加速?

TensorRT简介 TensorRT是NVIDIA推出的一款高性能的深度学习推理引擎(C库)。相比于一般的深度学习框架&#xff0c;在GPU模式下其可提供36X的加速&#xff0c;极大提高了深度学习模型在边缘设备上的推断速度&#xff0c;以满足无人机、无人车等无人系统的快速视觉检测场景。 Te…

Google play开发者账号隔离用指纹浏览器还是vps?哪个防关联效果更佳?

很多谷歌安卓开发者会通过矩阵、马甲包的方式&#xff0c;在Google play应用商店上获得更多的流量和收益&#xff0c;这需要多个开发者账号&#xff0c;因此开发者账号隔离问题的重要性不言而喻。 在Google play开发者账号的隔离防关联问题上&#xff0c;使用vps和指纹浏览器是…

Oracle(12)Managing Indexes

目录 目标&#xff1a; 一、基础知识 1、Classification ofindexes 索引的分类 2、B-Tree vs Bitmap 3、Creating Indexes: Guidelines 创建索引:准则 4、Offline Index Rebuild 脱机索引重建 5、RebuildingIndexes 重建索引 6、Online Index Rebuild 在线索引重建 7…

记一次某学院的未授权渗透过程

文章现写的&#xff0c;部分内容有些着急。 0x01 测试过程 开局登录框&#xff0c;没有测试SQL注入与XSS(菜鸟&#xff0c;测也测不出来。) 有system用户&#xff0c;尝试弱口令&#xff0c;无果&#xff01; 直接namp来一下子端口探测吧 发现有3306与8888 3306 mysql测试…

《Generic Dynamic Graph Convolutional Network for traffic flow forecasting》阅读笔记

论文标题 《Generic Dynamic Graph Convolutional Network for traffic flow forecasting》 干什么活&#xff1a;交通流预测&#xff08;traffic flow forecasting &#xff09;方法&#xff1a;动态图卷积网络&#xff08;Dynamic Graph Convolutional Network&#xff09;…

【沧元图】梅元知命运逆转,但遗憾下线,孟川新形态揭晓

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料&#xff0c;《沧元图》是近年来备受期待的动画作品之一&#xff0c;其独特的画风和精彩的剧情吸引了众多观众的关注。这部动画的播出计划备受瞩目&#xff0c;据主创团队透露&#xff0c;本季共有26…

关于GPT的一些使用场景

与传统的机器翻译或语音识别技术不同&#xff0c;GPT强调的是生成新文本的能力&#xff0c;这使得它在创作、摘要、问答等场景下具有独特的优势。下面是我日常生活中用到GPT的一些场景&#xff1a; 日常闲聊 在日常闲聊中&#xff08;尤其是和运营xjj聊天的时候&#xff09;&a…

伊朗网络间谍组织针对中东金融和政府部门

导语 近日&#xff0c;以色列网络安全公司Check Point与Sygnia发现了一起针对中东金融、政府、军事和电信部门的网络间谍活动。这一活动由伊朗国家情报和安全部门&#xff08;MOIS&#xff09;支持的威胁行为者发起&#xff0c;被称为"Scarred Manticore"。该组织被认…

【EI会议征稿】第四届机械、电子电气与自动化控制国际学术会议(METMS 2024)

第四届机械、电子电气与自动化控制国际学术会议&#xff08;METMS 2024&#xff09; 2024 4th International Conference on Mechanical, Electronics and Electrical and Automation Control 2024年第四届机械、电子电气与自动化控制国际学术会议&#xff08;METMS 2024&am…

每日一练 | 网络工程师软考真题Day47

阅读以下关于Linux文件系统和Samba效劳的说明&#xff0c;答复以下【问题1】至【问题3】。 【说明】 Linux系统采用了树型多级目录来管理文件&#xff0c;树型结构的最上层是根目录&#xff0c;其他的所有目录都是从根目录生成的。通过Samba可以实现基于Linux操作系统的效劳器和…

C语言之for的执行顺序

1.for具有内部可以存放3个条件&#xff0c;比如for(int i 0&#xff1b;i < n&#xff1b;i )。for中的条件可以不写&#xff0c;但是符号“&#xff1b;”一定要存在。for(;;)的作用和while(1)是类似的&#xff0c;都是一种死循环。 对于for(int i 0;i < n;i)&#x…

vite安装Tailwind CSS

安装 - Tailwind CSS 中文网 (nodejs.cn) 这是官网&#xff0c;平常我练习一般会用vite脚手架 我们选择这个vite模块 可选择React和Vue版本的&#xff0c;这里选择react的按照操作&#xff0c;没问题的话就要出问题了 1、在npm run dev的时候我是出现了这么个问题&#xff0c…