2.1.4 编写Fashion MNIST模型
前面描述的模型架构如下所示:
真的就是这么简单!这里有一些新概念,让我们来探索一下。
首先,我们可以看到我们正在使用Sequential。回想一下,这允许我们使用列表定义网络中的层。该列表中的每个元素都定义了一个层类型(在这个例子中,一个Flatten后跟两个Dense层),以及有关该层的详细信息,例如神经元的数量和激活函数。
第一层是:
这展示了层的部分功能——你不仅可以使用它们定义模型架构,还可以将功能封装在层中。所以在这里你的输入形状28×28被“展平”为你需要输入神经网络的784×1。
之后,你拥有我们在图2-12中显示的两个层,即一个具有20个神经元的Dense层,以及一个具有10个神经元的Dense层。
但是,这里也有一些新东西——激活(activation)参数。这定义了一个激活函数,该函数在该层处理结束时于该层上执行。激活函数可以帮助网络识别更复杂的模式,并在信息从一层到另一层流动时改变信息的行为,帮助网络更好、更快地学习。
它们是可选的,但它们非常有用并且经常被推荐使用。
在这20个神经元的层上,激活函数是tf.nn.relu,其中relu代表rectified linear unit(修正线性单元)。这是一个非常新奇的术语,有效地等同于:如果该值小于零,则设置它归零;否则,保持原样。就像:
这有什么帮助呢?如果该层中的任何神经元返回负值,则可能会抵消另一个神经元的正值,从而有效地忽略了它学到的东西。因此,与其在每次迭代时对每个神经元进行大量检查,我们不如在层上设置一个激活函数来为我们做这件事。
类似地,输出层有一个称为softmax的激活函数。这里的想法是我们的输出层有10个神经元。理想情况下,它们都包含零,除了其中一个的值是1。那个就是我们要的类别。在现实中,这种情况很少发生,每个神经元都有一个值。最大的将是我们对输入图像进行分类的最佳候选值。然而,为了报告一个概率值,我们希望每个神经元的值加起来为1,并且它们的值能适当地缩放。我们可以简单地将softmax激活函数应用于该层,而不是编写代码来处理这个问题,它会为我们做到这一点!
这只是模型架构。现在让我们探索完整的代码,包括获取数据、编译模型,然后执行训练:
还记得之前我提到解析图像内容的传统编码时,即使是像Fashion MNIST这样简单的图像,也可能需要数千行代码来处理它,而机器学习只需几行代码就可以完成吗?现在让我们来看一下。
首先是获取数据。Fashion MNIST数据集内置于TensorFlow,因此我们可以像这样轻松获取它:
执行这行代码后,training_images将有60000个训练图像,training_labels将有它们的关联标签。此外val_images和val_labels将有10000个图像及其相关标签。好在训练时不使用它们,因此我们可以在探索神经网络的效能时拥有一组神经网络以前“没有见过”的数据。
接下来是这些行:
使用Python中的NumPy的强大之处在于,如果将数组除以一个值,那么你会将该数组中的每个元素都除以该值。但是为什么我们要除以255?
这个过程称为归一化,这又是一个相当新奇的术语,意思是将一个值设置为0~1之间的某个值。我们的像素在0~255之间,因此通过除以255,我们将对其进行归一化。为什么要归一化?当值在0和1之间时,Dense中的数学运算效果最佳,当误差较大时,它们不会大量增加。你可能还记得第1章中的y=2x-1示例,我们没有进行归一化。这是一个平凡示例,不需要这样做,但在大多数情况下,你需要在将数据输入神经网络之前对其进行归一化!
然后,在定义模型架构后,编译模型,指定损失函数和优化器:
它们与你在第1章中使用的sgd和mean_squared_error不同。TensorFlow有一个包含这些函数的库,你通常可以从中挑选出最适合你的模型的方法进行试验。这里有一些限制,最明显的是损失函数。鉴于该模型有多个输出神经元,并且这些神经元为我们提供输出的类别或类型,我们希望使用分类损失函数来有效地测量它们,为此我选择了sparse_categorical_crossentropy。了解这些的工作方式超出了本书的范围,但最好尝试一下你可以在TensorFlow.org上找到的不同损失函数和优化器。对于优化器,我选择了adam,它是sgd的增强版本,可以在内部进行自我调整以获得更好的性能。
还请注意,我使用了另一个参数metrics=['accuracy'],它要求TensorFlow在训练时报告准确度。当进行分类模型训练时,我们希望分类器告诉我们它看到了什么,我们可以使用基本准确度,即其猜测“正确”的训练图像有多少,并报告损失值。通过在编译时指定指标,TensorFlow会将此报告给我们。
最后,我们可以用训练值拟合训练数据:
通过将epochs设置为20,可执行整个训练循环(进行猜测、评估和测量损失、优化、重复)20次,并要求它用训练图像拟合训练标签。
在训练时,你会看到如下输出:
注意准确度:仅经过三次循环,它在训练集上的准确度就达到了94.5%!我使用Google Colab进行了训练,可以看到,尽管处理了60000张图像,但每次循环只用了两秒钟。最后,你会看到值1875/1875,你可能想知道它们是什么?训练时你不必一次处理一张图像,TensorFlow支持批处理以加快处理速度。Fashion MNIST默认每批次有32张图像,因此它一次训练一批图像。这为你提供了1875批图像来组成60000张图像(即60000除以32可得1875)。
当你到达第20个轮次时,你会看到准确度现在超过97%:
因此,只需几行代码和不到一分钟的训练,你现在就拥有一个模型,可以以超过97%的准确度识别Fashion MNIST图像。
还记得之前你保留了10000张图像作为验证数据集吗?你现在可以将它们传递给模型来查看模型如何解析它们。请注意,它以前从未见过这些图像,因此这是测试你的模型是否真正准确的好方法——它是否可以对以前从未见过的图像进行高度准确的分类。你可以通过调用model.evaluate将图像和标签集传递给它来完成此操作:
由此可以看到,你的模型在之前从未见过的数据上的准确度为96%,这表明你有一个非常好的模型用以预测时尚数据。机器学习中称为过拟合的概念是你在这里要避免的。当你的模型非常擅长理解其训练数据,但却不太擅长理解其他数据时,就会发生过拟合。这表明训练准确度和验证准确度之间存在巨大差异。把它想象成你在教一个聪明的人什么是鞋子,但只给他展示过高跟鞋。那么他会“认为”所有的鞋子都是高跟鞋,如果你随后给它展示了一双运动鞋,它便会过拟合到高跟鞋。你也想在神经网络中避免这种做法,但可以看到我们在这里做得很好,在训练和验证准确度之间只有很小的差异!
这展示了如何创建一个简单的模型来学习如何“查看”图像的内容,但它依赖于非常简单的单色图像,其中数据是图片中唯一的内容,并在图像框内居中。识别真实世界图像的模型要比这复杂得多,但可以使用卷积神经网络来构建它们。深入了解它们的工作原理超出了本书的范围,但请查看我在本章前面提到的其他书籍来获得更深入的介绍。
不过,你可以做的一件事是迁移学习,无须深入研究模型架构类型,我们接下来将对此进行探讨。