社区计算机视觉课程文档

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 上更新