基于Fashion-MNIST2017服装数据集搭建卷积神经网络

摘要:

合集:AI案例-CV-传媒
数据集:服装分类Fashion-MNIST2017图片集
数据集价值:为研究人员提供了一个公平且统一的平台来测试和比较不同的机器学习和深度学习算法。
解决方案:TensorFlow1.x框架、CNN框架

一、问题描述

服装分类Fashion-MNIST图片集是一个包含Zalando商品图片的数据集——包括6万个训练样本和1万个测试样本。每个样本是一张28×28像素的灰度图像,与10个类别中的一个标签相关联。Zalando(一家德国的时尚科技公司)打算将时尚MNIST作为原始MNIST数据集的直接替代品,用于基准测试机器学习算法。它具有相同的图像大小和训练及测试分割的结构。 原始MNIST数据集包含许多手写数字。人工智能/机器学习/数据科学社区的成员喜欢这个数据集,并将其用作验证其算法的基准。实际上,MNIST通常是研究人员尝试的第一个数据集。”如果它在MNIST上不起作用,那就根本不起作用,”他们说。”好吧,如果它在MNIST上起作用,它仍然可能在其他数据集上失败。” Zalando寻求替换原始MNIST数据集。

取代MNIST数据集的原因由如下几个:

  • MNIST太简单了。很多深度学习算法在测试集上的准确率已经达到99.6%!
  • MNIST被用烂了。
  • MNIST数字识别的任务不代表现代机器学习。在MNIST上看似有效的想法没法迁移到真正的机器视觉问题上。

二、数据集内容

关键信息

数据集内容:Fashion-MNIST是一包含Zalando商品图片的数据集,总共有10个类别(数字0到9)。
创建者:Zalando(一家德国的时尚科技公司)
发布时间:2017年8月
图像大小:每张图像的尺寸是 28x28 像素。
数据集规模:包括6万个训练样本和1万个测试样本。
数据集特点:图像是标准化的,即它们被调整为固定大小,并且中心位于图像中心。图像是灰度的,即每个像素只有一个灰度值。
Fashion-MNIST的大小、格式和训练集/测试集划分与原始的MNIST完全一致。你可以直接用它来测试你的机器学习和深度学习算法性能,且不需要改动任何的代码。

这个数据集的样子大致如下(每个类别占三行):

标注

每个训练和测试样本都按照以下类别进行了标注:

标注编号描述
0T-shirt/top(T恤)
1Trouser(裤子)
2Pullover(套衫)
3Dress(裙子)
4Coat(外套)
5Sandal(凉鞋)
6Shirt(汗衫)
7Sneaker(运动鞋)
8Bag(包)
9Ankle boot(踝靴)

数据结构

每张图像的高度为28像素,宽度为28像素,总共有784个像素。每个像素都有一个与之关联的单个像素值,表示该像素的亮度或暗度,数值越高表示越暗。这个像素值是一个介于0到255之间的整数。训练和测试数据集都有785列。第一列包含类标签,代表衣物分类。其余列包含相关图像的像素值。

要在图像上定位一个像素,假设我们已经将x分解为x = i * 28 + j,其中i和j是介于0到27之间的整数。该像素位于28 x 28矩阵的第i行和第j列。例如,pixel31表示位于左边第四列,顶部第二行的像素。

可使用以下脚本,将数据集转换成 CSV 格式。文件目录data-csv中的数据集已转换成 CSV 格式:

https://pjreddie.com/projects/mnist-in-csv

CSV文件数据结构

fashion-mnist_train.csv和fashion-mnist_test.csv

  • 每一行为一个图片
  • 第一列为分类标记
  • 其他列为像素对应的值 (总共784 = 28*28).
  • 每个值代表像素的暗度 (0 to 255)

数据集引用要求

如果你在你的研究工作中使用了这个数据集,欢迎你引用这篇论文:

Fashion-MNIST: a Novel Image Dataset for Benchmarking Machine Learning Algorithms. Han Xiao, Kashif Rasul, Roland Vollgraf. arXiv:1708.07747

亦可使用Biblatex:

@online{xiao2017/online,
author       = {Han Xiao and Kashif Rasul and Roland Vollgraf},
title       = {Fashion-MNIST: a Novel Image Dataset for Benchmarking Machine Learning Algorithms},
date         = {2017-08-28},
year         = {2017},
eprintclass = {cs.LG},
eprinttype   = {arXiv},
eprint       = {cs.LG/1708.07747},
}

许可协议

The MIT License (MIT) Copyright © [2017] Zalando SE, https://tech.zalando.com

三、卷积神经网络

ConvNet(卷积神经网络)是一种深度学习模型,特别适用于处理具有类似网格结构的数据,如图像和音频信号。它的主要特点是使用卷积层来提取局部特征,从而有效地捕捉输入数据的空间层次结构。ConvNet在计算机视觉、自然语言处理等领域取得了显著的成功。以下是ConvNet的一些关键组成部分:

  1. 卷积层(Convolutional Layer)
  • 卷积操作:通过滑动一个小的权重矩阵(称为卷积核或滤波器)在输入数据上,提取局部特征。
  • 参数共享:同一个卷积核在整个输入上滑动,提取相同的特征,从而大大减少了模型的参数数量。
  1. 池化层(Pooling Layer)
  • 降维:通过聚合局部区域的特征(如取最大值或平均值),减少特征图的尺寸,同时保留重要信息。
  • 平移不变性:使模型对输入的小幅度变换具有一定的鲁棒性。
  1. 全连接层(Fully Connected Layer)
  • 特征融合:将前面卷积和池化层提取的特征映射到一个高维空间,进行全局特征的组合和分类。
  1. 激活函数(Activation Function)
  • 引入非线性:常用的激活函数包括ReLU(Rectified Linear Unit)、Sigmoid和Tanh等,增强模型的表达能力。
  1. 正则化技术
  • Dropout:在训练过程中随机丢弃一部分神经元,防止过拟合。
  • Batch Normalization:对每一层的输入进行归一化处理,加速训练并提高模型的稳定性。

应用领域

  • 图像分类:如ImageNet挑战赛中的优胜模型(AlexNet、VGG、ResNet等)。
  • 目标检测:如Faster R-CNN、YOLO等。
  • 语义分割:如U-Net、DeepLab系列。
  • 语音识别:利用卷积操作处理音频信号。

ConvNet的成功得益于其高效的特征提取能力和相对较少的参数需求,这使得它在资源受限的环境中也能够表现出色。

安装tensorflow1.14

TensorFlow1.14依赖于python3.6~3.7,选择python=3.6创建虚拟环境。同时安装工程运行需要的numpy psutil scikit-learn 开发包。

conda config --add channels anaconda
conda create -n tensorflow114-p36 python=3.6
conda activate tensorflow114-p36
conda install tensorflow-gpu=1.14
conda install numpy psutil scikit-learn

验证已安装的TensorFlow版本号:执行python

import tensorflow as tf
print(tf.__version__)
# 输出 1.14.0

加载Fashion-MNIST数据集

源码:benchmark\convnet.py

从文件目录:data\fashion 加载Fashion-MNIST数据集:

import numpy as np
import tensorflow as tf
from sklearn.utils import shuffle

from tensorflow.examples.tutorials.mnist import input_data
DATA_DIR = './data/fashion'
# Load training and eval data
mnist = input_data.read_data_sets(DATA_DIR, one_hot=False, validation_size=0)
train_data = mnist.train.images # Returns np.array
train_labels = np.asarray(mnist.train.labels, dtype=np.int32)
train_data, train_labels = shuffle(train_data, train_labels)
eval_data = mnist.test.images # Returns np.array
eval_labels = np.asarray(mnist.test.labels, dtype=np.int32)
eval_data, eval_labels = shuffle(eval_data, eval_labels)

输出:

  • train_dataeval_data 是 NumPy 数组,分别包含训练和评估的图像数据。
  • train_labels 和 `eval_labels是 NumPy 数组,分别包含训练和评估的标签数据。

训练和识别流程

以下代码展示了如何使用 TensorFlow 1.x 创建、训练和评估一个卷积神经网络(CNN)模型来进行 MNIST 手写数字识别。

1. 创建 Estimator

mnist_classifier = tf.estimator.Estimator(
   model_fn=cnn_model_fn, model_dir="./mnist_convnet_model")
  • tf.estimator.Estimator 是 TensorFlow 1.x 中用于构建和训练模型的类。
  • model_fn 参数指定了定义模型的函数 cnn_model_fn
  • model_dir 参数指定了输出的模型保存和恢复的目录。

2. 定义训练输入函数

train_input_fn = tf.estimator.inputs.numpy_input_fn(
   x={"x": train_data},
   y=train_labels,
   batch_size=400,
   num_epochs=None,
   shuffle=True)
  • tf.estimator.inputs.numpy_input_fn 用于将 NumPy 数组转换为 TensorFlow 输入函数。
  • x={"x": train_data} 指定了输入数据的键值对,其中 "x" 是模型的输入占位符名称。
  • y=train_labels 指定了标签数据。
  • batch_size=400 设置了每个批次的大小。
  • num_epochs=None 表示训练数据将被无限次地循环使用。
  • shuffle=True 表示数据将在每个 epoch 开始时被打乱。

3. 定义评估输入函数

eval_input_fn = tf.estimator.inputs.numpy_input_fn(
   x={"x": eval_data},
   y=eval_labels,
   num_epochs=1,
   shuffle=False)
  • eval_input_fn 用于评估模型性能。
  • num_epochs=1 表示评估数据将被使用一次。
  • shuffle=False 表示评估数据不会被打乱。

4. 训练和评估模型

for j in range(100):
   mnist_classifier.train(
       input_fn=train_input_fn,
       steps=2000)

   eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn)
   print(eval_results)
  • 使用 for 循环进行 100 个 epoch 的训练。
  • mnist_classifier.train(input_fn=train_input_fn, steps=2000) 进行 2000 步的训练。
  • eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn) 评估模型性能并返回结果。
  • print(eval_results) 打印评估结果。
    # Create the Estimator
  mnist_classifier = tf.estimator.Estimator(
      model_fn=cnn_model_fn, model_dir="./mnist_convnet_model")

# Train the model
  train_input_fn = tf.estimator.inputs.numpy_input_fn(
      x={"x": train_data},
      y=train_labels,
      batch_size=400,
      num_epochs=None,
      shuffle=True)

  # Evaluate the model and print results
  eval_input_fn = tf.estimator.inputs.numpy_input_fn(
      x={"x": eval_data},
      y=eval_labels,
      num_epochs=1,
      shuffle=False)

  for j in range(100):
      mnist_classifier.train(
          input_fn=train_input_fn,
          steps=2000)

      eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn)
      print(eval_results)

cnn_model_fn 定义为一个函数,该函数返回一个 tf.estimator.EstimatorSpec 对象,描述模型的结构和训练过程。以下代码定义了一个用于Fashion-MNIST图像识别的卷积神经网络(CNN)模型,使用TensorFlow 1.x的Estimator API。以下是代码的详细解释:

函数定义

def cnn_model_fn(features, labels, mode):
  • cnn_model_fn 是一个模型函数,用于定义模型的结构和行为。
  • features 是输入特征,通常是一个字典。
  • labels 是输入标签。
  • mode 表示当前的模式(训练、评估或预测)。

输入层

input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])
  • 将输入特征 features["x"] 重塑为4维张量,形状为 [batch_size, 28, 28, 1],其中 28x28 是MNIST图像的尺寸,1 表示单通道(灰度图像)。

卷积层 #1

conv1 = tf.layers.conv2d(
inputs=input_layer,
filters=32,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
  • 使用32个大小为 5x5 的卷积核对输入进行卷积操作。
  • padding="same" 表示使用填充来保持输入和输出的尺寸相同。
  • 使用ReLU激活函数。

池化层 #1

pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
  • 使用 2x2 的池化窗口和步幅为2进行最大池化操作。
  • 输入张量形状为 [batch_size, 28, 28, 32],输出张量形状为 [batch_size, 14, 14, 32]

卷积层 #2

conv2 = tf.layers.conv2d(
   inputs=pool1,
   filters=64,
   kernel_size=[5, 5],
   padding="same",
   activation=tf.nn.relu)
  • 使用64个大小为 5x5 的卷积核对输入进行卷积操作。
  • 使用ReLU激活函数。

池化层 #2

pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
  • 使用 2x2 的池化窗口和步幅为2进行最大池化操作。
  • 输入张量形状为 [batch_size, 14, 14, 64],输出张量形状为 [batch_size, 7, 7, 64]

展平层

pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
  • 将池化层的输出展平为一维向量,形状为 [batch_size, 7 * 7 * 64]

全连接层

dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
  • 使用1024个神经元的全连接层。
  • 使用ReLU激活函数。

Dropout层

Dropout层是深度学习模型中常用的正则化技术,主要用于防止神经网络过拟合(Overfitting)。其核心思想是在训练过程中随机“丢弃”(暂时禁用)一部分神经元,迫使网络不依赖任何单个神经元,从而提升泛化能力。

dropout = tf.layers.dropout(
   inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)
  • 在训练模式下,以40%的概率丢弃神经元,以防止过拟合。

输出层

logits = tf.layers.dense(inputs=dropout, units=10)
  • 输出层有10个神经元,对应10个类别的logits。

预测

predictions = {
   "classes": tf.argmax(input=logits, axis=1),
   "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
}
if mode == tf.estimator.ModeKeys.PREDICT:
   return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
  • 计算预测类别和概率。
  • 如果模式是预测模式,返回包含预测结果的 EstimatorSpec

损失计算

onehot_labels = tf.one_hot(indices=tf.cast(labels, tf.int32), depth=10)
loss = tf.losses.softmax_cross_entropy(onehot_labels=onehot_labels, logits=logits)
  • 将标签转换为one-hot编码。
  • 计算softmax交叉熵损失。

5. 训练操作

if mode == tf.estimator.ModeKeys.TRAIN:
   optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
   train_op = optimizer.minimize(loss=loss, global_step=tf.train.get_global_step())
   return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)
  • 如果模式是训练模式,定义优化器和训练操作。
  • 返回包含损失和训练操作的 EstimatorSpec

评估指标

eval_metric_ops = {
"accuracy": tf.metrics.accuracy(labels=labels, predictions=predictions["classes"])}
return tf.estimator.EstimatorSpec(mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
  • 如果模式是评估模式,定义评估指标(准确率)。
  • 返回包含损失和评估指标的 EstimatorSpec

这个模型函数定义了一个完整的CNN模型,适用于Fashion-MNIST图像识别任务,并且可以与TensorFlow 1.x的Estimator API无缝集成。

cnn_model_fn()完整代码:

def cnn_model_fn(features, labels, mode):
  """Model function for CNN."""
  # Input Layer
  # Reshape X to 4-D tensor: [batch_size, width, height, channels]
  # MNIST images are 28x28 pixels, and have one color channel
  input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])

  # Convolutional Layer #1
  # Computes 32 features using a 5x5 filter with ReLU activation.
  # Padding is added to preserve width and height.
  # Input Tensor Shape: [batch_size, 28, 28, 1]
  # Output Tensor Shape: [batch_size, 28, 28, 32]
  conv1 = tf.layers.conv2d(
      inputs=input_layer,
      filters=32,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)

  # Pooling Layer #1
  # First max pooling layer with a 2x2 filter and stride of 2
  # Input Tensor Shape: [batch_size, 28, 28, 32]
  # Output Tensor Shape: [batch_size, 14, 14, 32]
  pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

  # Convolutional Layer #2
  # Computes 64 features using a 5x5 filter.
  # Padding is added to preserve width and height.
  # Input Tensor Shape: [batch_size, 14, 14, 32]
  # Output Tensor Shape: [batch_size, 14, 14, 64]
  conv2 = tf.layers.conv2d(
      inputs=pool1,
      filters=64,
      kernel_size=[5, 5],
      padding="same",
      activation=tf.nn.relu)

  # Pooling Layer #2
  # Second max pooling layer with a 2x2 filter and stride of 2
  # Input Tensor Shape: [batch_size, 14, 14, 64]
  # Output Tensor Shape: [batch_size, 7, 7, 64]
  pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)

  # Flatten tensor into a batch of vectors
  # Input Tensor Shape: [batch_size, 7, 7, 64]
  # Output Tensor Shape: [batch_size, 7 * 7 * 64]
  pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])

  # Dense Layer
  # Densely connected layer with 1024 neurons
  # Input Tensor Shape: [batch_size, 7 * 7 * 64]
  # Output Tensor Shape: [batch_size, 1024]
  dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)

  # Add dropout operation; 0.6 probability that element will be kept
  dropout = tf.layers.dropout(
      inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)

  # Logits layer
  # Input Tensor Shape: [batch_size, 1024]
  # Output Tensor Shape: [batch_size, 10]
  logits = tf.layers.dense(inputs=dropout, units=10)

  predictions = {
      # Generate predictions (for PREDICT and EVAL mode)
      "classes": tf.argmax(input=logits, axis=1),
      # Add `softmax_tensor` to the graph. It is used for PREDICT and by the
      # `logging_hook`.
      "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
  }
  if mode == tf.estimator.ModeKeys.PREDICT:
      return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

  # Calculate Loss (for both TRAIN and EVAL modes)
  onehot_labels = tf.one_hot(indices=tf.cast(labels, tf.int32), depth=10)
  loss = tf.losses.softmax_cross_entropy(
      onehot_labels=onehot_labels, logits=logits)

  # Configure the Training Op (for TRAIN mode)
  if mode == tf.estimator.ModeKeys.TRAIN:
      optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
      train_op = optimizer.minimize(
          loss=loss,
          global_step=tf.train.get_global_step())
      return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)

  # Add evaluation metrics (for EVAL mode)
  eval_metric_ops = {
      "accuracy": tf.metrics.accuracy(
          labels=labels, predictions=predictions["classes"])}
  return tf.estimator.EstimatorSpec(
      mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)

运行结果

python ./benchmark/convnet.py

{'accuracy': 0.7077, 'loss': 0.800941, 'global_step': 2000}
{'accuracy': 0.762, 'loss': 0.67517143, 'global_step': 4000}

...

输出模型到mnist_convnet_model目录中。

源码开源协议

MIT 许可证(MIT)
版权所有 © 2017 Zalando SE,https://tech.zalando.com

四、获取案例套装

需要登录后才允许下载文件包。登录 需要登录后才允许下载文件包。登录

发表评论