找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

415

积分

0

好友

53

主题
发表于 2025-12-27 10:16:53 | 查看: 32| 回复: 0

TensorFlow实现NiN卷积神经网络:服饰分类Fashion-MNIST实战与全连接层取代方案 - 图片 - 1

本文将深入解析NiN(Network in Network)这一经典的卷积网络架构,并通过TensorFlow框架,在Fashion-MNIST数据集上完成一个服饰图像分类的实战项目。你将了解到NiN如何利用1×1卷积与全局平均池化来优化传统CNN的结构。

CNN结构的演进与NiN的提出

在卷积神经网络(CNN)的发展历程中,VGG等模型通过堆叠卷积层来加深网络,以提取更复杂、更抽象的特征。然而,传统的CNN架构在卷积层之后,通常会连接一个或多个全连接层,最终通过Softmax函数输出分类结果。这种结构存在一些固有的缺点。

传统CNN的局限性

  1. 参数量庞大:全连接层的神经元需要与上一层所有的特征图进行连接,这导致了巨大的参数量。例如,若上一层输出为7x7x512的特征图,那么一个仅包含1024个神经元的全连接层,其参数量就将高达7*7*512*1024,极易引发过拟合。
  2. 空间信息丢弃:在进入全连接层前,特征图通常需要被展平(Flatten)成一维向量,这彻底破坏了特征图原有的空间结构信息。
  3. 可解释性弱:全连接层如同一个“黑盒”,难以直观理解其学习到的特征与输入图像的空间对应关系。

NiN网络的创新思路

NiN网络的核心思想是用“微型网络”(即多层感知机MLP)结构来局部替代传统的线性卷积层,并使用全局平均池化(Global Average Pooling)层来彻底取代全连接层。这一思想深刻影响了后续如GoogLeNet等网络的设计。

其中,起到关键作用的便是1×1卷积。在人工智能领域的卷积神经网络设计中,1×1卷积是一种巧妙的“通道融合”与“降维/升维”工具。它可以跨通道集成信息,在不改变特征图空间尺寸的前提下,灵活地调整通道数,从而构建更复杂、非线性的特征组合。

NiN块:网络中的“网络”

NiN块是NiN网络的基本组成单元。一个标准的NiN块由多层卷积层堆叠而成,其经典结构为:

  • 第一层:普通卷积层(如11x11, 5x5等)
  • 后续层(通常为2层):1x1卷积层

这相当于在每个局部感受野后,插入了一个微型的全连接网络(MLP),增强了模型的非线性表示能力。

NiN模型架构

一个完整的NiN模型通常交替堆叠多个NiN块和步幅为2的最大池化层,以逐步缩小特征图尺寸、增加通道数。其最大的特点在于网络的末端:

  1. 最后一个NiN块输出的特征图通道数,直接设置为目标分类的类别数
  2. 对该特征图执行全局平均池化:对每个通道的特征图,计算所有像素点的平均值。由于通道数等于类别数,因此每个通道的平均值就直接代表了对应类别的置信度。
  3. 将这些平均值直接送入Softmax层得到最终分类概率。

这样做的好处是:

  • 极大减少参数:完全移除了全连接层,参数数量大幅下降,有效缓解过拟合。
  • 保留空间信息:输出与输入图像在空间上有一定的对应关系,增强了模型的解释性。
  • 更自然的正则化:全局平均池化本身对输入的空间变换具有更强的鲁棒性。

使用TensorFlow实现NiN进行服饰分类

下面我们将使用TensorFlowKeras接口,构建一个NiN网络,并在经典的计算机视觉入门数据集Fashion-MNIST上进行训练和评估。

首先,确保你的TensorFlow环境就绪。

import tensorflow as tf
print("TensorFlow version:", tf.__version__)
TensorFlow version: 2.x.x

定义NiN模型

我们使用Keras的Sequential API来构建模型。每个NiN块我们采用“卷积层 + 两个1×1卷积层”的结构。

from tensorflow.keras import layers, models

def nin_block(num_channels, kernel_size, strides, padding):
    blk = models.Sequential()
    blk.add(layers.Conv2D(num_channels, kernel_size, strides=strides, padding=padding, activation='relu'))
    blk.add(layers.Conv2D(num_channels, kernel_size=1, activation='relu'))
    blk.add(layers.Conv2D(num_channels, kernel_size=1, activation='relu'))
    return blk

model = models.Sequential([
    # 第一个NiN块, 使用较大卷积核捕获初始特征
    nin_block(96, kernel_size=11, strides=4, padding='valid'),
    layers.MaxPool2D(pool_size=3, strides=2),
    layers.Dropout(0.5),  # 添加Dropout防止过拟合

    # 第二个NiN块, 减少卷积核尺寸
    nin_block(256, kernel_size=5, strides=1, padding='same'),
    layers.MaxPool2D(pool_size=3, strides=2),
    layers.Dropout(0.5),

    # 第三个NiN块, 进一步增加通道数
    nin_block(384, kernel_size=3, strides=1, padding='same'),
    layers.MaxPool2D(pool_size=3, strides=2),

    # 第四个NiN块, 输出通道数置为类别数10
    nin_block(10, kernel_size=3, strides=1, padding='same'),

    # 全局平均池化层, 替代全连接层
    layers.GlobalAveragePooling2D(),
    # 将10个通道的平均值转换为10个类别的概率分布
    layers.Activation('softmax')
])

model.build(input_shape=(None, 224, 224, 3)) # 构建模型, 指定输入尺寸
model.summary()

读取与预处理Fashion-MNIST数据集

Fashion-MNIST图像尺寸为28x28,而我们的网络初始输入期望较大(如224x224)。为了简单演示,我们这里调整网络第一层参数,并直接使用28x28的输入。在实际复杂图像任务中,需要调整输入尺寸或网络结构。

from tensorflow.keras.datasets import fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

# 数据归一化并增加通道维度
train_images = train_images.reshape((60000, 28, 28, 1)).astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255

# 修改模型第一层, 适配28x28的输入
# 此处为演示, 简化了网络第一层的卷积核和步幅
model = models.Sequential([
    nin_block(96, kernel_size=3, strides=1, padding='same'), # 调整以适应小图片
    layers.MaxPool2D(pool_size=3, strides=2),
    nin_block(256, kernel_size=3, strides=1, padding='same'),
    layers.MaxPool2D(pool_size=3, strides=2),
    nin_block(384, kernel_size=3, strides=1, padding='same'),
    layers.MaxPool2D(pool_size=3, strides=2),
    nin_block(10, kernel_size=3, strides=1, padding='same'),
    layers.GlobalAveragePooling2D(),
    layers.Activation('softmax')
])

编译与训练模型

我们使用Adam优化器和稀疏分类交叉熵损失函数来编译模型。

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 开始训练
history = model.fit(train_images, train_labels,
                    epochs=10,
                    batch_size=64,
                    validation_split=0.2) # 使用20%训练数据作为验证集

评估与可视化结果

训练完成后,在测试集上评估模型的最终性能。

test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
print(f'\n测试准确率:{test_acc:.4f}')

小结

通过本次实战,我们实现了NiN网络的核心思想:

  1. 构建NiN块:利用1×1卷积增强非线性,模拟微型MLP。
  2. 摒弃全连接层:通过将末层特征图通道数设为类别数,并接续全局平均池化层,直接得到分类结果。这一设计在减少参数、防止过拟合的同时,提升了模型的可解释性,是深度学习模型设计中的一个重要思路。
  3. 应用于Fashion-MNIST:尽管原始NiN是为更大尺寸图像设计,我们通过调整使其能在此数据集上运行,验证了其基础架构的有效性。

NiN虽然参数量少,但在当时达到了 competitive 的性能,其提出的1×1卷积与全局平均池化思想,为后续更高效的网络架构(如ResNet、SqueezeNet等)奠定了重要基础。




上一篇:Android JetPack Room ORM 数据库框架:增删改查入门教程
下一篇:卡诺图化简法与逻辑代数:数字电路设计中的逻辑函数化简实战指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-12 01:28 , Processed in 0.342259 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表