跳到主要内容

网络中的网络(NiN)

LeNet、AlexNet和 VGG 的问题?

LeNet、AlexNet和 VGG 都有一个共同的设计模式:通过一系列的卷积层与汇聚层来提取空间结构特征;然后通过全连接层对特征的表征进行处理。 AlexNet 和 VGG 对 LeNet 的改进主要在于如何扩大和加深这两个模块。 或者,可以想象在这个过程的早期使用全连接层。然而,如果使用了全连接层,可能会完全放弃表征的空间结构。

提示

如果使用了全连接层,可能会完全放弃表征的空间结构,这是因为全连接层不考虑输入特征的空间布局。例如,它不知道某个特征是从图像的左上角还是右下角提取的。这意味着全连接层可能会丢失一些空间信息。

网络中的网络(NiN)提供了一个非常简单的解决方案:在每个像素的通道上分别使用多层感知机

感知机怎么工作的?

首先,我们需要明确一点:在 NiN(Network in Network)的上下文中,当我们说“感知机”或“多层感知机(MLP)”,我们实际上是指 1x1 的卷积操作。这种操作在每个像素位置上独立地应用于输入特征图,允许我们在每个空间位置上进行非线性变换。

现在,让我们解释为什么这种操作可以捕获到表征的空间结构:

  1. 保留空间信息:1x1 的卷积操作不会改变特征图的空间尺寸。这意味着,与全连接层不同,我们没有放弃任何空间信息。每个像素位置上的输出都与相同位置上的输入相关联。

  2. 增加通道间的交互:1x1 的卷积操作允许输入特征图的不同通道之间进行交互。这是因为它可以将一个通道的信息线性地组合到另一个通道。这种通道间的交互增强了模型的能力,使其能够捕获更复杂的模式。

  3. 非线性变换:通过在 1x1 的卷积操作后应用非线性激活函数(如 ReLU),我们可以在每个像素位置上进行非线性变换。这增加了模型的表达能力,使其能够捕获更复杂的空间结构。

  4. 多层操作:在 NiN 中,我们不仅仅使用一个 1x1 的卷积层,而是使用多个连续的 1x1 的卷积层。这种多层操作进一步增强了模型的能力,使其能够捕获更高级的空间结构。

总的来说,通过在每个像素位置上独立地应用 1x1 的卷积操作(或称为“感知机”),NiN 能够在保留空间信息的同时捕获到表征的空间结构。这与传统的全连接层不同,后者会放弃这种空间结构。

1x1 的卷积操作有什么意义?

1x1 的卷积操作,也称为“点卷积”,在卷积神经网络中有多个重要的用途和意义:

  1. 通道数变换:1x1 的卷积可以用来增加或减少特征图的通道数。例如,如果你有一个 64 通道的特征图,并使用一个输出通道数为 128 的 1x1 卷积,那么输出的特征图将有 128 个通道。这为网络设计提供了灵活性,允许我们根据需要调整通道数。

  2. 增加网络深度而不改变空间尺寸:1x1 的卷积可以增加网络的深度,而不会改变特征图的空间尺寸。这意味着我们可以增加更多的非线性层,从而增强网络的表达能力,而不会影响到空间结构。

  3. 通道间交互:1x1 的卷积允许输入特征图的不同通道之间进行交互。这是因为它可以将一个通道的信息线性地组合到另一个通道。这种通道间的交互可以帮助网络捕获更复杂的模式。

  4. 计算效率:1x1 的卷积在计算上是非常高效的,尤其是与更大的卷积核(如 3x3 或 5x5)相比。这使得我们可以在不增加太多计算负担的情况下增加网络的深度。

  5. 在 Inception 结构中的使用:在 GoogLeNet 的 Inception 模块中,1x1 的卷积被用作瓶颈层,以减少计算量。在进行更大的卷积操作之前,先使用 1x1 的卷积来减少通道数,从而使整体计算更加高效。

  6. 网络中的网络 (NiN):如前所述,1x1 的卷积在 NiN 结构中被用来代替全连接层,从而保留空间信息。

总的来说,1x1 的卷积在深度学习中是一个强大且多功能的工具,它为网络设计提供了很大的灵活性,并有助于提高模型的性能和计算效率。

1x1 的卷积是怎么计算的?

1x1 的卷积操作与传统的卷积操作在计算上是相似的,但由于其卷积核的大小为 1x1,计算过程更为简单。它主要用于在通道间进行线性组合。

假设我们有一个输入特征图的尺寸为 H x W x C_in,其中 H 是高度,W 是宽度,C_in 是通道数。我们想要使用 1x1 的卷积将其转换为 H x W x C_out 的输出特征图,其中 C_out 是输出通道数。

对于每一个输出通道,我们都有一个 1x1xC_in 的卷积核。这个卷积核与输入特征图的每一个位置进行点积操作,生成一个输出特征图的像素值。

下面是数学表达式:

假设 F 是输入特征图,K 是 1x1 的卷积核,O 是输出特征图。对于输出特征图的每一个位置 (i, j) 和每一个输出通道 c,其值计算如下:

O(i,j,c)=c=1CinF(i,j,c)×K(1,1,c,c)O(i, j, c) = \sum_{c'=1}^{C_in} F(i, j, c') \times K(1, 1, c', c)

NiN 块是什么?

网络中的网络(Network in Network,简称 NiN)是一种卷积神经网络架构,它的核心思想是在传统的卷积层后面使用一个微型的多层感知机(MLP)来替代传统的激活函数。这个微型 MLP 可以被视为一个“块”,通常称为“NiN 块”。

NiN 块的主要组成部分是:

  1. 一个卷积层。
  2. 一个或多个全连接层,这些层在每个像素位置上独立地应用于卷积层的输出。这些全连接层通常使用 ReLU 激活函数。

在 PyTorch 中,NiN 块可以使用 nn.Sequential 来表示。以下是一个简单的 NiN 块的实现:

import torch.nn as nn

class NiNBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding):
super(NiNBlock, self).__init__()
self.block = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding),
nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=1), # 1x1 convolution
nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=1), # 1x1 convolution
nn.ReLU()
)

def forward(self, x):
return self.block(x)

在上面的代码中,我们首先使用一个传统的卷积层,然后使用两个 1x1 的卷积层来模拟 MLP 的效果。这两个 1x1 的卷积层在每个像素位置上独立地应用于前一个卷积层的输出,从而实现了 NiN 的核心思想。

提示

使用两个 1×11 \times 1 卷积层来模拟多层感知机(MLP)的效果是为了增加模型的非线性和表示能力。确实,你可以只使用一个 1×11 \times 1 卷积层,但是两层(或更多层)可以提供更强大的模型能力。

考虑以下几点:

  1. 增加非线性:每个卷积层后都跟随一个激活函数,如 ReLU。通过增加更多的层,我们可以增加更多的非线性到模型中,从而使模型能够学习更复杂的函数。

  2. 模拟 MLP:在传统的多层感知机中,我们通常有多个隐藏层。在 NiN 中,使用两个 1×11 \times 1 卷积层是为了模拟这种多层结构,从而在每个像素位置上独立地应用一个小的 MLP。

  3. 表示能力:虽然 1×11 \times 1 卷积主要用于跨通道的信息整合,但通过增加更多的这样的层,我们可以增加模型的表示能力,使其能够学习更复杂的通道间关系。

总的来说,虽然一个 1×11 \times 1 卷积层可以提供一些模型能力,但是通过增加更多的层,我们可以进一步增强模型的非线性和表示能力。然而,这也带来了一个权衡:增加的层数可能会导致更多的参数、更高的计算成本和可能的过拟合。因此,在实际应用中,选择合适数量的 1×11 \times 1 卷积层需要根据具体任务和数据进行调整。

NiN 模型是怎么样的?

网络中的网络(Network in Network,简称 NiN)是一种卷积神经网络架构,由 Min Lin、Qiang Chen 和 Shuicheng Yan 在 2013 年提出。NiN 提出了一个重要的概念,即使用微型神经网络来执行卷积,而不是传统的线性卷积操作。这种微型神经网络通常由一个或多个全连接层组成,这些层在每个像素位置上独立地应用于输入数据。

NiN 的主要结构特点如下:

  1. 卷积层替换:在传统的卷积神经网络中,卷积层由线性卷积操作 followed by a non-linear activation function(如 ReLU)组成。在 NiN 中,这些卷积层被替换为包含多层感知机的卷积层,其中多层感知机由多个 1×11 \times 1 卷积层组成。

  2. 全局平均池化:NiN 去掉了传统卷积神经网络中的全连接层,而是在最后一个卷积层后使用全局平均池化层来得到网络的输出。这大大减少了模型的参数数量。

  3. 深度和宽度:由于使用了 1×11 \times 1 卷积,NiN 可以有更多的层(更深)和更多的通道(更宽)。

具体的 NiN 模型结构可能会有所不同,但通常包括多个 NiN 块,每个块由一个传统的卷积层 followed by one or more 1×11 \times 1 卷积层组成,后面可能还有一个池化层。

以下是一个简化的 NiN 模型的示例:

  1. 输入层
  2. NiN 块:卷积(11×1111 \times 11, 96 通道)-> ReLU -> 1×11 \times 1 卷积 -> ReLU -> 1×11 \times 1 卷积 -> ReLU
  3. 最大池化
  4. NiN 块:卷积(5×55 \times 5, 256 通道)-> ReLU -> 1×11 \times 1 卷积 -> ReLU -> 1×11 \times 1 卷积 -> ReLU
  5. 最大池化
  6. NiN 块:卷积(3×33 \times 3, 384 通道)-> ReLU -> 1×11 \times 1 卷积 -> ReLU -> 1×11 \times 1 卷积 -> ReLU
  7. 最大池化
  8. 全局平均池化
  9. 输出层

这只是一个示例,实际的 NiN 结构可能会有所不同。但关键的思想是使用 1×11 \times 1 卷积来模拟在每个像素位置上的多层感知机。

PyTorch 里定义模型

使用 PyTorch 创建一个 NiN 模型的示例如下:

import torch.nn as nn

class NiNBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding):
super(NiNBlock, self).__init__()
self.block = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=1), # 1x1 convolution
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=1), # 1x1 convolution
nn.ReLU(inplace=True)
)

def forward(self, x):
return self.block(x)

class NiN(nn.Module):
def __init__(self, num_classes=10):
super(NiN, self).__init__()

# features 模块包含了输入图像到特征图的转换过程
self.features = nn.Sequential(
# 第一个 NiN 块:使用 11x11 的卷积核,步长为 4,没有填充。
# 输入通道为 1(灰度图像),输出通道为 96。
NiNBlock(1, 96, kernel_size=11, stride=4, padding=0),

# 最大池化层:3x3 的池化核,步长为 2。
nn.MaxPool2d(kernel_size=3, stride=2),

# 第二个 NiN 块:使用 5x5 的卷积核,步长为 1,填充为 2。
# 输入通道为 96,输出通道为 256。
NiNBlock(96, 256, kernel_size=5, stride=1, padding=2),

# 最大池化层:3x3 的池化核,步长为 2。
nn.MaxPool2d(kernel_size=3, stride=2),

# 第三个 NiN 块:使用 3x3 的卷积核,步长为 1,填充为 1。
# 输入通道为 256,输出通道为 384。
NiNBlock(256, 384, kernel_size=3, stride=1, padding=1),

# 最大池化层:3x3 的池化核,步长为 2。
nn.MaxPool2d(kernel_size=3, stride=2),

# Dropout 层:在训练过程中随机丢弃一部分特征,防止过拟合。
nn.Dropout(0.5)
)

# classifier 模块包含了特征图到最终类别预测的转换过程
self.classifier = nn.Sequential(
# 最后一个 NiN 块:使用 3x3 的卷积核,步长为 1,填充为 1。
# 输入通道为 384,输出通道为 num_classes(类别数)。
NiNBlock(384, num_classes, kernel_size=3, stride=1, padding=1),

# 全局平均池化层:将每个通道的特征图平均到一个标量。
# 输出的尺寸为 (batch_size, num_classes, 1, 1)。
nn.AdaptiveAvgPool2d((1, 1)),

# Flatten 层:将输出展平为 (batch_size, num_classes)。
nn.Flatten()
)

def forward(self, x):
# 将输入 x 通过 features 模块转换为特征图
x = self.features(x)
# 将特征图通过 classifier 模块转换为类别预测
x = self.classifier(x)
return x


model = NiN()
print(model)

这个模型定义了一个基本的 NiN 结构。请注意,在最后使用了全局平均池化(AdaptiveAvgPool2d)来替代传统的全连接层,这是 NiN 的一个关键特点。

补充说明:在卷积神经网络中,输入通道数通常与输入数据的通道数相匹配。对于灰度图像,只有一个通道,所以输入通道数为1。对于彩色图像(如RGB图像),有三个通道,所以输入通道数为3。

输出通道数,也称为卷积核的数量或滤波器的数量,是一个超参数,意味着它是手动选择的,而不是通过算法计算出来的。输出通道数决定了模型在该层学习的特征数量。更多的输出通道意味着模型可以学习更多的特征,但也会增加计算复杂性和模型的参数数量。

在AlexNet、VGG、NiN等早期的卷积神经网络中,输出通道数的选择往往是基于实验和经验的。例如,AlexNet的第一个卷积层使用了96个输出通道,这可能是通过多次实验和调整得到的最佳配置。

总之,输出通道数96并不是通过某种特定的计算得到的,而是基于实验、经验和网络设计者的选择。在实际应用中,可以根据问题的复杂性、数据的数量和可用的计算资源来调整这个值。