关于此 Codelab
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. 编译和训练模型
现在已定义了模型,接下来要做的是构建模型。我们来创建模型,先使用 optimizer
和 loss
函数编译模型,然后使用训练数据和标签训练模型。目标是让模型确定训练数据与其训练标签之间的关系。稍后,您希望模型看到与训练数据类似的数据,然后预测该数据应是什么样的。
请注意,我们使用了 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])