社区计算机视觉课程文档

MobileNet

Hugging Face's logo
加入 Hugging Face 社区

并获得增强文档体验

开始使用

MobileNet

MobileNet 是一种专为移动设备设计的神经网络架构。它由 Google 的研究团队开发,并于 2017 年首次推出。MobileNet 的主要目标是在智能手机、平板电脑和其他资源受限的设备上提供高性能、低延迟的图像分类和目标检测。

MobileNet 通过使用深度可分离卷积来实现这一点,深度可分离卷积是标准卷积的一种更有效的替代方案。深度可分离卷积将计算分解为两个独立的步骤:深度卷积,然后是逐点卷积。这导致参数和计算复杂度显着降低,使 MobileNet 能够在移动设备上高效运行。

MobileNet 上的卷积类型

通过用这些深度可分离卷积和逐点卷积替换常规卷积层,MobileNet 在最大限度地减少计算开销的同时实现了高精度,使其非常适合移动设备和其他资源有限的平台。MobileNet 使用两种类型的卷积

深度可分离卷积

在传统的卷积层中,每个滤波器会同时在其所有输入通道上应用其权重。深度可分离卷积将其分解为两个步骤:深度卷积,然后是逐点卷积。

此步骤使用小型滤波器(通常为 3x3)分别对输入图像中的每个通道(单色或特征)执行卷积。此步骤的输出与输入大小相同,但通道更少。

逐点可分离卷积

这种类型的卷积在输入和输出层的所有通道上应用单个滤波器(通常为 1x1)。它比常规卷积具有更少的参数,可以看作是全连接层的替代方案,使其适用于计算资源有限的移动设备。

在深度卷积之后,此步骤使用另一个 1x1 卷积层组合来自先前步骤的过滤输出。此操作有效地将深度卷积学习到的特征聚合到一组较小的特征中,从而降低整体复杂度,同时保留重要信息。

为什么我们使用这些卷积而不是常规卷积?

为了更好地理解,让我们简化一下并进行解释

常规卷积,一个大的、一体化的滤波器

想象一下,你有一个又大又厚的滤波器(就像一块有很多层的海绵)。此滤波器应用于整个图像。它同时处理图像的所有部分及其所有特征(如颜色)。这需要大量的工作(计算)和一个大的滤波器(内存)。

深度可分离卷积 - 两步,更轻的处理过程:

MobileNet 执行此基本过程。它将大滤波器拆分为两个更小、更简单的步骤

  • 步骤 1 - 深度卷积:首先,它为图像的每个特征分别使用薄滤波器(如海绵的单层)(例如,分别处理每种颜色)。这工作量较小,因为每个滤波器都小而简单。

  • 步骤 2 - 点卷积:然后,它使用另一个小的过滤器(只是一个微小的点)将这些特征混合在一起。这一步就像对第一个过滤器找到的内容进行总结。

这些步骤是关于什么的?

MobileNet 使用这两个较小的步骤来代替一个大的步骤,这就像对常规卷积中所需的工作进行轻量级版本。它更高效,尤其是在功能不太强大的设备(如智能手机)上。

使用较小的过滤器,MobileNet 不需要那么多内存。这就像需要一个更小的盒子来存放所有工具,使其更容易适应较小的设备。

1x1 卷积与普通卷积相比是如何工作的?

普通卷积

  • 普通卷积使用更大的内核(如 3x3 或 5x5)来一次查看图像中的一组像素。这就像观察图片的一小块区域来理解场景的一部分。
  • 这些卷积擅长理解边缘、角点和纹理等特征,方法是分析像素彼此排列的方式。

1x1 卷积

  • 1x1 卷积一次查看一个像素。它不会试图理解相邻像素的排列方式。
  • 即使它只查看一个像素,它也会考虑来自不同通道的所有信息(例如彩色图像中的 RGB 通道)。它结合这些通道来创建该像素的新版本。
  • 1x1 卷积可以增加或减少通道数。这意味着它可以简化信息(通过减少通道)或创建更复杂的信息(通过增加通道)。

主要区别

  • 关注领域:普通卷积分析一组像素以理解模式,而 1x1 卷积则专注于单个像素,并结合来自不同通道的信息。
  • 用途:普通卷积用于检测图像中的模式和特征。相反,1x1 卷积主要用于改变通道深度,有助于调整神经网络中后续层的 信息复杂度,从而提高弱设备的效率。

MobileNet 还采用了通道线性瓶颈层等技术,这些技术可以在减少参数数量的同时提高模型准确性。该架构专为各种硬件平台进行了优化,包括 CPU、GPU,甚至 Google 的张量处理单元 (TPU) 等专用硬件。

通道线性瓶颈层

通道线性瓶颈层有助于进一步减少参数数量和计算成本,同时在图像分类任务中保持较高的准确性。

通道线性瓶颈层由三个主要操作组成,这些操作按顺序应用

  1. 深度卷积:此步骤对输入图像中每个通道(单个颜色或特征)分别执行卷积,使用一个小的过滤器(通常为 3x3)。此步骤的输出与输入大小相同,但通道较少。
  2. 批归一化:此操作对每个通道的激活值进行归一化,有助于稳定训练过程并提高泛化性能。
  3. 激活函数:通常,在批归一化之后使用 ReLU(校正线性单元)激活函数,以在网络中引入非线性。

ReLU 是做什么的?

训练过程中可能会出现一些问题。我们将首先解释它们,然后解释 ReLU 对这些问题做了什么。

梯度消失问题

在深度神经网络中,尤其是在反向传播过程中,可能会出现梯度消失问题。当梯度(用于更新网络权重的梯度)在回传到网络各层时变得非常小,就会发生这种情况。如果这些梯度变得太小,它们就会“消失”,使网络难以有效地学习和调整其权重。

ReLU 对于正值具有线性、非饱和的形式(如果输入为正,则它简单地输出输入),它确保梯度不会变得太小,从而实现更快的学习和更有效的网络权重调整。

非线性

如果没有非线性,无论神经网络有多少层,它都将作为一个线性模型,无法学习复杂的模式。

像ReLU这样的非线性函数使神经网络能够捕获和学习数据中的复杂关系。

推理

您可以使用 Hugging Face transformers 对各种 transformers 模型进行推理,如下所示

from transformers import AutoImageProcessor, AutoModelForImageClassification
from PIL import Image
import requests

url = "http://images.cocodataset.org/val2017/000000039769.jpg"
image = Image.open(requests.get(url, stream=True).raw)

# initialize processor and model
preprocessor = AutoImageProcessor.from_pretrained("google/mobilenet_v2_1.0_224")
model = AutoModelForImageClassification.from_pretrained("google/mobilenet_v2_1.0_224")

# preprocess the inputs
inputs = preprocessor(images=image, return_tensors="pt")

# get the output and the class labels
outputs = model(**inputs)
logits = outputs.logits

predicted_class_idx = logits.argmax(-1).item()
print("Predicted class:", model.config.id2label[predicted_class_idx])
Predicted class: tabby, tabby cat

使用 PyTorch 的示例实现

您可以在下面找到 MobileNet 的 PyTorch 示例实现

import torch
import torch.nn as nn
import torch.nn.functional as F


class DepthwiseSeparableConv(nn.Module):
    def __init__(self, in_channels, out_channels, stride):
        super().__init__()
        self.depthwise = nn.Conv2d(
            in_channels,
            in_channels,
            kernel_size=3,
            stride=stride,
            padding=1,
            groups=in_channels,
        )
        self.pointwise = nn.Conv2d(
            in_channels, out_channels, kernel_size=1, stride=1, padding=0
        )

    def forward(self, x):
        x = self.depthwise(x)
        x = self.pointwise(x)
        return x


class MobileNet(nn.Module):
    def __init__(self, num_classes=1000):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1)

        # MobileNet body
        self.dw_conv2 = DepthwiseSeparableConv(32, 64, 1)
        self.dw_conv3 = DepthwiseSeparableConv(64, 128, 2)
        self.dw_conv4 = DepthwiseSeparableConv(128, 128, 1)
        self.dw_conv5 = DepthwiseSeparableConv(128, 256, 2)
        self.dw_conv6 = DepthwiseSeparableConv(256, 256, 1)
        self.dw_conv7 = DepthwiseSeparableConv(256, 512, 2)

        # 5 depthwise separable convolutions with stride 1
        self.dw_conv8 = DepthwiseSeparableConv(512, 512, 1)
        self.dw_conv9 = DepthwiseSeparableConv(512, 512, 1)
        self.dw_conv10 = DepthwiseSeparableConv(512, 512, 1)
        self.dw_conv11 = DepthwiseSeparableConv(512, 512, 1)
        self.dw_conv12 = DepthwiseSeparableConv(512, 512, 1)

        self.dw_conv13 = DepthwiseSeparableConv(512, 1024, 2)
        self.dw_conv14 = DepthwiseSeparableConv(1024, 1024, 1)

        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Linear(1024, num_classes)

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)

        x = self.dw_conv2(x)
        x = F.relu(x)
        x = self.dw_conv3(x)
        x = F.relu(x)
        x = self.dw_conv4(x)
        x = F.relu(x)
        x = self.dw_conv5(x)
        x = F.relu(x)
        x = self.dw_conv6(x)
        x = F.relu(x)
        x = self.dw_conv7(x)
        x = F.relu(x)

        x = self.dw_conv8(x)
        x = F.relu(x)
        x = self.dw_conv9(x)
        x = F.relu(x)
        x = self.dw_conv10(x)
        x = F.relu(x)
        x = self.dw_conv11(x)
        x = F.relu(x)
        x = self.dw_conv12(x)
        x = F.relu(x)

        x = self.dw_conv13(x)
        x = F.relu(x)
        x = self.dw_conv14(x)
        x = F.relu(x)

        x = self.avg_pool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x


# Create the model
mobilenet = MobileNet(num_classes=1000)
print(mobilenet)

您还可以在此 HuggingFace 链接 上找到预训练的 MobileNet 模型检查点。

< > 在 GitHub 上更新