基本分类:对服装图像进行分类

在 TensorFlow.org 上查看 在 Google Colab 中运行 在 GitHub 上查看源代码 下载笔记本

本指南训练了一个神经网络模型,用于对服装图像(如运动鞋和衬衫)进行分类。如果您不了解所有细节,也没关系;这是一个快速概述完整的 TensorFlow 程序,并在您学习的过程中解释详细信息。

本指南使用 tf.keras,这是一个在 TensorFlow 中构建和训练模型的高级 API。

# TensorFlow and tf.keras
import tensorflow as tf

# Helper libraries
import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)
2024-07-13 04:19:34.844149: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:479] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
2024-07-13 04:19:34.871376: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:10575] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
2024-07-13 04:19:34.871421: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1442] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
2.16.2

导入 Fashion MNIST 数据集

本指南使用 Fashion MNIST 数据集,该数据集包含 70,000 张 10 类别的灰度图像。这些图像显示了低分辨率(28x28 像素)的单个服装,如下所示

Fashion MNIST sprite
图 1. Fashion-MNIST 样本(由 Zalando 提供,MIT 许可)。
 

Fashion MNIST 旨在作为经典 MNIST 数据集的直接替代品,后者通常用作计算机视觉机器学习程序的“Hello, World”。MNIST 数据集包含手写数字(0、1、2 等)的图像,其格式与您在此处使用的服装图像相同。

本指南使用 Fashion MNIST 来增加多样性,因为它比常规 MNIST 更具挑战性。这两个数据集都比较小,用于验证算法是否按预期工作。它们是测试和调试代码的良好起点。

这里,使用 60,000 张图像来训练网络,使用 10,000 张图像来评估网络学习对图像进行分类的准确率。您可以直接从 TensorFlow 访问 Fashion MNIST。从 TensorFlow 导入并 加载 Fashion MNIST 数据

fashion_mnist = tf.keras.datasets.fashion_mnist

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

加载数据集将返回四个 NumPy 数组

  • The train_imagestrain_labels 数组是训练集——模型用来学习的数据。
  • 模型使用测试集进行测试,即 test_imagestest_labels 数组。

这些图像为 28x28 的 NumPy 数组,像素值范围为 0 到 255。标签是一个整数数组,范围为 0 到 9。这些对应于图像所代表的服装的类别

标签 类别
0 T 恤/上衣
1 裤子
2 套头衫
3 连衣裙
4 外套
5 凉鞋
6 衬衫
7 运动鞋
8
9 踝靴

每张图像都映射到一个唯一的标签。由于类别名称未包含在数据集中,因此请在此处存储它们,以便稍后在绘制图像时使用

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

探索数据

在训练模型之前,让我们探索一下数据集的格式。以下内容表明训练集中有 60,000 张图像,每张图像都表示为 28 x 28 像素

train_images.shape
(60000, 28, 28)

同样,训练集中有 60,000 个标签

len(train_labels)
60000

每个标签都是 0 到 9 之间的整数

train_labels
array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)

测试集中有 10,000 张图像。同样,每张图像都表示为 28 x 28 像素

test_images.shape
(10000, 28, 28)

测试集包含 10,000 个图像标签

len(test_labels)
10000

预处理数据

在训练网络之前,必须预处理数据。如果您检查训练集中的第一张图像,您将看到像素值范围为 0 到 255

plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.grid(False)
plt.show()

png

在将这些值馈送到神经网络模型之前,请将其缩放到 0 到 1 的范围内。为此,请将值除以 255。重要的是,训练集测试集的预处理方式相同

train_images = train_images / 255.0

test_images = test_images / 255.0

为了验证数据是否处于正确的格式,以及您是否已准备好构建和训练网络,让我们显示训练集中的前 25 张图像,并在每张图像下方显示类别名称。

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    plt.xlabel(class_names[train_labels[i]])
plt.show()

png

构建模型

构建神经网络需要配置模型的层,然后编译模型。

设置层

神经网络的基本构建块是 。层从馈送到它们的数据中提取表示。希望这些表示对当前问题有意义。

深度学习的大部分内容都包括将简单的层链接在一起。大多数层,例如 tf.keras.layers.Dense,都具有在训练期间学习的参数。

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(10)
])
/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/keras/src/layers/reshaping/flatten.py:37: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(**kwargs)

此网络中的第一层,tf.keras.layers.Flatten,将图像的格式从二维数组(28x28 像素)转换为一维数组(28 * 28 = 784 像素)。将此层视为将图像中的像素行展开并排成一行。此层没有要学习的参数;它只重新格式化数据。

像素展平后,网络由两个 tf.keras.layers.Dense 层组成。这些是密集连接的或全连接的神经层。第一个 Dense 层有 128 个节点(或神经元)。第二层(也是最后一层)返回一个长度为 10 的 logits 数组。每个节点包含一个分数,该分数指示当前图像属于 10 个类别中的一个。

编译模型

在模型准备好进行训练之前,它还需要一些其他设置。这些设置是在模型的 编译 阶段添加的

  • 优化器 —这是模型根据它看到的数据及其损失函数进行更新的方式。
  • 损失函数 —这衡量模型在训练期间的准确率。您需要最小化此函数以“引导”模型朝正确的方向发展。
  • 指标 —用于监控训练和测试步骤。以下示例使用准确率,即正确分类的图像的比例。
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

训练模型

训练神经网络模型需要以下步骤

  1. 将训练数据馈送到模型。在本例中,训练数据位于 train_imagestrain_labels 数组中。
  2. 模型学习将图像和标签关联起来。
  3. 您要求模型对测试集进行预测——在本例中,是 test_images 数组。
  4. 验证预测是否与 test_labels 数组中的标签匹配。

馈送模型

要开始训练,请调用 model.fit 方法——之所以这样称呼,是因为它将模型“拟合”到训练数据

model.fit(train_images, train_labels, epochs=10)
Epoch 1/10
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1720844382.080422   73401 service.cc:145] XLA service 0x7f7a68006fc0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:
I0000 00:00:1720844382.080462   73401 service.cc:153]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5
I0000 00:00:1720844382.080467   73401 service.cc:153]   StreamExecutor device (1): Tesla T4, Compute Capability 7.5
I0000 00:00:1720844382.080470   73401 service.cc:153]   StreamExecutor device (2): Tesla T4, Compute Capability 7.5
I0000 00:00:1720844382.080473   73401 service.cc:153]   StreamExecutor device (3): Tesla T4, Compute Capability 7.5
124/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.5870 - loss: 1.2303
I0000 00:00:1720844382.812309   73401 device_compiler.h:188] Compiled cluster using XLA!  This line is logged at most once for the lifetime of the process.
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 1ms/step - accuracy: 0.7832 - loss: 0.6298
Epoch 2/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.8619 - loss: 0.3838
Epoch 3/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.8715 - loss: 0.3471
Epoch 4/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.8866 - loss: 0.3164
Epoch 5/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.8936 - loss: 0.2910
Epoch 6/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.8978 - loss: 0.2773
Epoch 7/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.8998 - loss: 0.2653
Epoch 8/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.9039 - loss: 0.2578
Epoch 9/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.9079 - loss: 0.2458
Epoch 10/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - accuracy: 0.9149 - loss: 0.2300
<keras.src.callbacks.history.History at 0x7f7c3b75e7c0>

随着模型的训练,损失和准确率指标将被显示。此模型在训练数据上的准确率约为 0.91(或 91%)。

评估准确率

接下来,比较模型在测试数据集上的表现

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)

print('\nTest accuracy:', test_acc)
313/313 - 1s - 3ms/step - accuracy: 0.8888 - loss: 0.3224

Test accuracy: 0.8888000249862671

事实证明,测试数据集上的准确率略低于训练数据集上的准确率。训练准确率和测试准确率之间的差距代表了过拟合。过拟合是指机器学习模型在新的、以前从未见过的输入上的表现比在训练数据上的表现差。过拟合模型“记住”了训练数据集中的噪声和细节,以至于对新数据的模型性能产生了负面影响。有关更多信息,请参阅以下内容

进行预测

模型训练完成后,您可以使用它来预测一些图像。将 softmax 层附加到模型的线性输出——logits——以将其转换为概率,这样更容易解释。

probability_model = tf.keras.Sequential([model, 
                                         tf.keras.layers.Softmax()])
predictions = probability_model.predict(test_images)
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step

这里,模型已经预测了测试集中每张图像的标签。让我们看一下第一个预测

predictions[0]
array([1.0297459e-08, 6.0991318e-10, 4.4771734e-10, 4.6667561e-12,
       1.7007395e-08, 2.9962388e-04, 1.6731771e-07, 2.5453644e-03,
       3.6041616e-08, 9.9715471e-01], dtype=float32)

预测是一个包含 10 个数字的数组。它们代表模型对图像对应于 10 种不同服装的“置信度”。您可以看到哪个标签具有最高的置信度值

np.argmax(predictions[0])
9

因此,模型最相信这张图像是一双踝靴,即 class_names[9]。检查测试标签表明此分类是正确的

test_labels[0]
9

定义函数以绘制完整的 10 类预测集。

def plot_image(i, predictions_array, true_label, img):
  true_label, img = true_label[i], img[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])

  plt.imshow(img, cmap=plt.cm.binary)

  predicted_label = np.argmax(predictions_array)
  if predicted_label == true_label:
    color = 'blue'
  else:
    color = 'red'

  plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
                                100*np.max(predictions_array),
                                class_names[true_label]),
                                color=color)

def plot_value_array(i, predictions_array, true_label):
  true_label = true_label[i]
  plt.grid(False)
  plt.xticks(range(10))
  plt.yticks([])
  thisplot = plt.bar(range(10), predictions_array, color="#777777")
  plt.ylim([0, 1])
  predicted_label = np.argmax(predictions_array)

  thisplot[predicted_label].set_color('red')
  thisplot[true_label].set_color('blue')

验证预测

模型训练完成后,您可以使用它来预测一些图像。

让我们看一下第 0 张图像、预测和预测数组。正确的预测标签为蓝色,错误的预测标签为红色。数字表示预测标签的百分比(以 100 为单位)。

i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions[i],  test_labels)
plt.show()

png

i = 12
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions[i],  test_labels)
plt.show()

png

让我们绘制几张图像及其预测。请注意,即使模型非常自信,它也可能是错误的。

# Plot the first X test images, their predicted labels, and the true labels.
# Color correct predictions in blue and incorrect predictions in red.
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  plot_image(i, predictions[i], test_labels, test_images)
  plt.subplot(num_rows, 2*num_cols, 2*i+2)
  plot_value_array(i, predictions[i], test_labels)
plt.tight_layout()
plt.show()

png

使用训练后的模型

最后,使用训练后的模型来预测单个图像。

# Grab an image from the test dataset.
img = test_images[1]

print(img.shape)
(28, 28)

tf.keras 模型经过优化,可以一次对批次(或集合)中的示例进行预测。因此,即使您使用的是单个图像,也需要将其添加到列表中

# Add the image to a batch where it's the only member.
img = (np.expand_dims(img,0))

print(img.shape)
(1, 28, 28)

现在预测此图像的正确标签

predictions_single = probability_model.predict(img)

print(predictions_single)
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 140ms/step
[[3.1302361e-06 2.6202183e-12 9.9640650e-01 3.0387102e-13 3.1137168e-03
  5.0210312e-11 4.7672543e-04 8.7875020e-13 4.1576378e-11 2.9291714e-15]]
plot_value_array(1, predictions_single[0], test_labels)
_ = plt.xticks(range(10), class_names, rotation=45)
plt.show()

png

tf.keras.Model.predict 返回一个列表列表——每个图像在数据批次中都有一个列表。获取批次中我们(唯一)图像的预测

np.argmax(predictions_single[0])
2

模型按预期预测了一个标签。

要了解有关使用 Keras 构建模型的更多信息,请参阅 Keras 指南

# MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.