文章目录
前言
深度学习近年来在计算机视觉、自然语言处理等领域取得了巨大成功,而卷积神经网络(CNN)作为其核心技术之一,经历了从LeNet到AlexNet、VGG等经典模型的演进。在这些模型中,卷积层和全连接层的组合成为主流设计。然而,2014年提出的“网络中的网络”(Network in Network, NiN)模型打破了这一传统,通过引入1x1卷积和全局平均池化,显著减少了参数量并提升了模型性能。
本文将通过PyTorch实现NiN模型,结合代码逐步解析其设计思想和实现细节,帮助读者从代码层面理解深度学习模型的构建与训练过程。我们将使用Fashion-MNIST数据集进行实验,完整代码和训练过程都将在文中呈现。
一、NiN(Network in Network)简介
“网络中的网络”(Network in Network, NiN)是由林敏等人于2014年提出的创新性卷积神经网络架构,旨在解决传统CNN模型中全连接层带来的参数冗余和表达能力不足的问题。NiN的设计理念突破了当时的主流思路,引入了1x1卷积和全局平均池化,成为后续许多深度学习模型(如Inception和ResNet)的灵感来源。
1.1 NiN的核心思想
传统CNN(如AlexNet)通常在卷积层后接全连接层,将特征图展平后进行分类。这种方法虽然有效,但存在两个主要问题:
- 参数量巨大:全连接层需要将高维特征图映射到分类输出,导致参数数量激增,增加计算成本和过拟合风险。
- 空间信息丢失:展平操作丢弃了特征图的空间结构,限制了模型对局部模式的感知能力。
NiN通过以下创新解决了这些问题:
- 1x1卷积:NiN提出在卷积层后添加1x1卷积层,相当于在通道维度上进行特征变换。这种操作类似于一个微型全连接网络(因此得名“网络中的网络”),既增强了模型的非线性表达能力,又避免了传统全连接层的参数爆炸。
- 全局平均池化:NiN摒弃了全连接层,转而使用全局平均池化(Global Average Pooling)将特征图的空间维度压缩为1x1,直接输出分类结果。这不仅大幅减少了参数量,还保留了空间信息的语义。
1.2 NiN的网络结构
NiN的典型结构由多个NiN块(NiN Block)组成,每个NiN块包含一个标准卷积层和两个1x1卷积层,后面通常接最大池化层以减小特征图尺寸。完整网络的最后通过一个NiN块输出与类别数相同的通道数,再通过全局平均池化生成分类向量。
例如,在本文的实现中,NiN网络包括:
- 初始NiN块:从单通道输入(灰度图)扩展到96通道,使用大卷积核(11x11)提取特征。
- 中间NiN块:逐步增加通道数(256、384),使用较小的卷积核(5x5、3x3)细化特征。
- 最终NiN块:输出10通道(对应10个类别),通过全局平均池化生成分类结果。
1.3 NiN的优点与意义
- 参数效率:相比传统CNN,NiN通过1x1卷积和全局平均池化显著减少了参数量,使模型更轻量化。
- 表达能力强:1x1卷积增强了通道间的特征交互,提升了模型的非线性拟合能力。
- 泛化性好:去掉全连接层并加入Dropout,NiN有效降低了过拟合风险。
NiN虽然在当时并未广泛流行,但其思想深刻影响了后续的深度学习研究。例如,1x1卷积成为瓶颈层(bottleneck)的核心组件,全局平均池化也被广泛用于现代架构中。理解NiN不仅能帮助我们掌握深度学习的发展脉络,还能为设计高效模型提供启发。
在下一节,我们将通过PyTorch代码逐步实现NiN,并结合Fashion-MNIST数据集展示其实际效果。
二、数据加载与预处理
在开始构建模型之前,我们需要准备数据。Fashion-MNIST是一个常用的图像分类数据集,包含10类服装图像,每张图像为28x28的灰度图。以下代码实现了数据的加载和预处理:
import torchvision
import torchvision.transforms as transforms
from torch.utils import data
import multiprocessingdef get_dataloader_workers():"""使用电脑支持的最大进程数来读取数据"""return multiprocessing.cpu_count()def load_data_fashion_mnist(batch_size, resize=None):"""下载Fashion-MNIST数据集,然后将其加载到内存中。参数:batch_size (int): 每个数据批次的大小。resize (int, 可选): 图像的目标尺寸。如果为 None,则不调整大小。返回:tuple: 包含训练 DataLoader 和测试 DataLoader 的元组。"""# 定义变换管道trans = [transforms.ToTensor()]if resize:trans.insert(0, transforms.Resize(resize))trans = transforms.Compose(trans)# 加载 Fashion-MNIST 训练和测试数据集mnist_train = torchvision.datasets.FashionMNIST(root="./data",train=True,transform=trans,download=True)mnist_test = torchvision.datasets.FashionMNIST(root="./data",train=False,transform=trans,download=True)# 返回 DataLoader 对象return (data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers()),data.DataLoader(mnist_test,batch_size,shuffle=False,num_workers=get_dataloader_workers()))# 使用示例
batch_size = 128
train_iter, test_iter = load_data_fashion_mnist(batch_size, resize=224)
在这段代码中:
transforms.ToTensor()
将图像转换为张量格式,范围从[0, 255]归一化到[0, 1]。resize=224
将图像调整为224x224,以适配NiN模型的输入需求(原始Fashion-MNIST图像较小,需放大)。DataLoader
提供了批量加载数据的功能,shuffle=True
确保训练数据随机打乱,num_workers
使用多进程加速数据读取。
三、NiN模型设计
NiN的核心创新在于用1x1卷积替代传统全连接层,并通过全局平均池化直接输出分类结果。以下是NiN块和完整模型的实现:
3.1 NiN块
NiN块是模型的基本组成单元,包含一个标准卷积层和两个1x1卷积层:
import torch
from torch import nndef nin_block(in_channels, out_channels, kernel_size, strides, padding):"""定义 NiN(Network in Network)块参数:in_channels: 输入通道数out_channels: 输出通道数kernel_size: 卷积核大小strides: 步幅padding: 填充"""return nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding),nn.ReLU(),nn.Conv2d(out_channels, out_channels, kernel_size=1),nn.ReLU(),nn.Conv2d(out_channels, out_channels, kernel_size=1),nn.ReLU())
- 第一个卷积层提取空间特征,参数由输入输出通道数、卷积核大小等决定。
- 两个1x1卷积层相当于在通道维度上进行特征变换,增强模型的非线性