社区计算机视觉课程文档

MobileNet

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

MobileNet

MobileNet是一种专为移动设备设计的神经网络架构。它由谷歌研究团队开发,并于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 转换器来推断各种转换器模型,如下所示:

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