使用 TensorFlow 构建计算机视觉模型

1. 准备工作

在此 Codelab 中,您将使用 TensorFlow 创建一个可以识别衣物的计算机视觉模型。

前提条件

  • 扎实的 Python 知识
  • 基本编程技能

学习内容

在此 Codelab 中,您将:

  • 训练神经网络识别衣物
  • 完成一系列练习,这些练习会引导您对不同的神经网络层进行实验

您将构建的内容

  • 一个可识别衣物的神经网络

所需条件

如果您从未使用 TensorFlow 创建计算机视觉神经网络,则可以使用 Colaboratory,这是一个基于浏览器的环境,包含所有必需的依赖项。您可以找到在 Colab 中运行其余 Codelab 的代码。

但是,您将用于训练模型的主要语言是 Python,因此您需要安装 Python。除此之外,您还需要 TensorFlow 和 NumPy 库。您可以在此处详细了解并安装 TensorFlow点击此处安装 NumPy

2. 开始编码

首先,浏览可执行的 Colab 笔记本

首先导入 TensorFlow。

import tensorflow as tf
print(tf.__version__)

您将训练一个神经网络,从名为 Fashion MNIST 的常见数据集中识别衣物。该数据集包含 70000 种衣物,分为 10 个不同的类别。每件衣物都采用 28x28 的灰度图像。下面是一些示例:

与数据集关联的标签如下:

标签

说明

0

T 恤衫/上衣

1

裤子

2

套衫

3

裙子

4

外套

5

凉鞋

6

衬衫

7

运动鞋

8

9

踝靴

Fashion MNIST 数据可在 tf.keras.datasets API 中获取。加载方式如下所示:

mnist = tf.keras.datasets.fashion_mnist

在该对象上调用 load_data 会生成两组列表,分别是训练值和测试值,它们分别表示衣物及其标签。

(training_images, training_labels), (test_images, test_labels) = mnist.load_data()

这些值看起来如何?输出训练图像和训练标签即可查看。您可以在数组中尝试不同的索引。

import matplotlib.pyplot as plt
plt.imshow(training_images[0])
print(training_labels[0])
print(training_images[0])

物品 0 的输出数据如下所示:

您会发现,所有值都是 0 到 255 之间的整数。在训练神经网络时,所有值在 0 到 1 之间处理起来更轻松,此过程称为归一化。幸运的是,Python 提供了一种无需循环便可归一化列表的简单方法。

training_images  = training_images / 255.0
test_images = test_images / 255.0

您可能还想查看物品 42,它是一只靴子,与索引 0 对应的靴子并不相同。

现在,您可能想知道为什么会有两个数据集:训练集和测试集。

原理是有一组用于训练的数据和另一组模型尚未遇到的数据,从而可以了解模型对值的分类效果。毕竟,创建完模型后,您需要将模型用到它之前未见过的数据上!此外,如果没有单独的测试数据,则网络将只会记住其训练数据,而不会泛化所学的知识。

3. 设计模型

现在设计模型。您将有三个层。逐一浏览每个层,并探索不同类型的层以及每个层使用的参数。

model = tf.keras.models.Sequential([tf.keras.layers.Flatten(),
                                    tf.keras.layers.Dense(128, activation=tf.nn.relu),
                                    tf.keras.layers.Dense(10, activation=tf.nn.softmax)])
  • Sequential 定义了神经网络中的层序列。
  • Flatten 会接受一个正方形并将其转换为一维矢量。
  • Dense 会添加一层神经元。
  • Activation 函数会告知各层神经元要执行的操作。选项有很多,但目前只采用以下选项:
  • Relu 实际上意味着,如果 X 大于 0,则返回 X,否则返回 0。它只会将 0 或更大的值传递到网络中的下一层。
  • Softmax 会接受一组值,并能有效地选择最大的值。例如,如果最后一层的输出是 [0.1, 0.1, 0.05, 0.1, 9.5, 0.1, 0.05, 0.05, 0.05],那么您无需通过排序来获取最大值,它会返回 [0,0,0,0,1,0,0,0,0]。

4. 编译和训练模型

现在已定义了模型,接下来要做的是构建模型。我们来创建模型,先使用 optimizerloss 函数编译模型,然后使用训练数据和标签训练模型。目标是让模型确定训练数据与其训练标签之间的关系。稍后,您希望模型看到与训练数据类似的数据,然后预测该数据应是什么样的。

请注意,我们使用了 metrics= 参数,这样的话,TensorFlow 可根据已知答案(标签)检查预测结果,从而报告训练准确率。

model.compile(optimizer = tf.keras.optimizers.Adam(),
              loss = 'sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(training_images, training_labels, epochs=5)

model.fit 执行时,您会看到损失和准确率:

Epoch 1/5
60000/60000 [=======] - 6s 101us/sample - loss: 0.4964 - acc: 0.8247
Epoch 2/5
60000/60000 [=======] - 5s 86us/sample - loss: 0.3720 - acc: 0.8656
Epoch 3/5
60000/60000 [=======] - 5s 85us/sample - loss: 0.3335 - acc: 0.8780
Epoch 4/5
60000/60000 [=======] - 6s 103us/sample - loss: 0.3134 - acc: 0.8844
Epoch 5/5
60000/60000 [=======] - 6s 94us/sample - loss: 0.2931 - acc: 0.8926

模型完成训练后,您会在最后一个周期结束时看到准确率值。类似如上所示的 0.8926。这表明您的神经网络对训练数据进行分类时的准确率约为 89%。换言之,它可以找出图像与标签之间的模式匹配,成功率达到 89%。这一结果并不好,但考虑到它只训练了 5 个周期,并且速度很快,所以也不算坏。

5. 测试模型

模型如何处理它未看到的数据?因此,您应设置测试集。您调用 model.evaluate 并传入两组数据,并报告每组的损失。你也试试吧:

model.evaluate(test_images, test_labels)

输出内容如下所示:

10000/10000 [=====] - 1s 56us/sample - loss: 0.3365 - acc: 0.8789
[0.33648381242752073, 0.8789]

该示例返回了 0.8789 的准确率,表示其准确率约为 88%。(您的值可能略有不同。)

正如预期的那样,模型使用的未知数据的准确率不如训练时所用数据的准确率高!随着您对 TensorFlow 了解的更多,您会找到一些改进的方法。

如需进一步了解相关信息,请尝试执行下一步中的练习。

6. 探索练习

练习 1

对于第一个练习,请运行以下代码:

classifications = model.predict(test_images)
print(classifications[0])

它会为每个测试图像创建一组分类,然后输出分类中的第一个条目。运行后,输出的是数字列表。为何您会认为输出的是数字列表?这些数字代表什么?

请尝试运行 print(test_labels[0]),您将获得一个 9。这是否有助于您了解为什么该列表看起来是这样的?

模型的输出是包含 10 个数字的列表。这些数字是一个概率,即被分类的值是相应的标签。例如,列表中的第一个值是一个概率,即衣物属于类 0,下一个是 1。请注意,这些概率都非常低(其中一个除外)。此外,由于使用了 Softmax,列表中的所有概率总和为 1.0。

该列表和标签均基于 0,因此标签 9 的踝靴表示它是 10 类中的第 10 个。列表中第 10 个元素的值最高,表示神经网络预测其正在分类的物品很可能是踝靴。

练习 2

查看模型中的层。针对包含 512 个神经元的密集层,使用不同的值进行实验。

损失和训练时间有什么不同的结果?为什么您认为是这样?

例如,如果增加到 1024 个神经元,您就必须进行更多计算,从而导致进程变慢。但在这种情况下,因为模型更加准确,所以能够产生更好的效果。这并不意味着越多越好。您很快就能实现收益递减法。

练习 3

如果移除 Flatten() 层,会发生什么情况。为什么您认为是这样?

您会收到有关数据形状的错误。虽然错误的详细信息目前看起来并不明确,但这强化了经验法则,即网络的第一层应与数据的形状保持一致。现在您的数据是 28x28 图像,28 个包含 28 个神经元的层将不可行,因此将 28,28 平化为 784x1 更有意义。

您可以在开头添加 Flatten() 层,而无需编写所有代码。当稍后将数组加载到模型中时,系统会自动为您扁平化这些数组。

练习 4

考虑最终(输出)层。为什么会有 10 层?如果层数少于 10,会出现什么情况?

尝试使用 5 层训练网络。一旦发现意外值,您就会收到错误。另一个经验法则:最后一层中的神经元数量应该与您要分类的类的数量相匹配。在本例中,数字从 0 到 9,因此有 10 个,即最后一层中应有 10 个神经元。

练习 5

考虑网络中其他层的效果。如果您在包含 512 个神经元的层与包含 10 个神经元的最终层之间再添加一个层,会发生什么情况?

因为这些数据相对简单,是以不会产生明显的影响。对于更复杂的数据,通常需要额外的层。

练习 6

在训练之前,您对数据进行了归一化(从 0 到 255 的值转换为 0 到 1 的值)。移除它会产生什么影响?以下是供您试用的完整代码(请注意,将数据归一化的两行注释掉)。

为什么您会得到不同的结果?查看 Stack Overflow 上的优秀答案。

import tensorflow as tf
print(tf.__version__)
mnist = tf.keras.datasets.fashion_mnist
(training_images, training_labels), (test_images, test_labels) = mnist.load_data()
#training_images=training_images/255.0
#test_images=test_images/255.0
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy')
model.fit(training_images, training_labels, epochs=5)
model.evaluate(test_images, test_labels)
classifications = model.predict(test_images)
print(classifications[0])
print(test_labels[0])

7. 探索回调

之前,在训练额外的周期时,您遇到的损失可能会发生变化。您可能要花一些时间来等待训练完成,您可能认为在达到所需的值(例如 95% 的准确率)后停止训练会更好。如果您在 3 个周期后达到此值,为什么要苦苦等待完成更多周期结束呢?

与任何其他程序一样,您可以使用回调!了解其实际运用情况:

import tensorflow as tf

class myCallback(tf.keras.callbacks.Callback):
  def on_epoch_end(self, epoch, logs={}):
    if(logs.get('accuracy')>0.95):
      print("\nReached 95% accuracy so cancelling training!")
      self.model.stop_training = True

callbacks = myCallback()
mnist = tf.keras.datasets.fashion_mnist
(training_images, training_labels), (test_images, test_labels) = mnist.load_data()
training_images=training_images/255.0
test_images=test_images/255.0
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(training_images, training_labels, epochs=5, callbacks=[callbacks])

8. 恭喜

您已构建了自己的第一个计算机视觉模型!如需了解如何增强计算机视觉模型,请转到构建卷积并执行池化