3.7 迁移学习
使用卷积来提取特征是识别图像内容的强大工具。生成的特征图可以被传入神经网络的密集层来将它们匹配到标签,并且提供了一个更准确的方法来判别图像的内容。通过使用这种方法,加上一个简单且可以快速训练的神经网络,以及一些图像增强技术,我们创建了一个模型,当在一个非常小的数据集上训练时,它在区分一匹马和一个人这项任务上达到了80%~90%的准确率。
但是我们可以使用迁移学习进一步优化模型。迁移学习背后的概念很简单:为什么不用一组已经在一个更大的数据集上学习过的滤波器呢?这些滤波器包含很多特征,让我们无须从头开始训练。我们可以把这些滤波器放入网络,然后使用这些训练好的滤波器在数据上训练模型。例如,Horses or Humans数据集只包含两个类别。我可以使用一个现有模型,它被提前训练好来分类1000个类别,但是在某些时刻我们不得不舍弃一些现有的网络,并且只添加那些让我们可以分类两个类别的层。
图3-14展示了一个分类任务的CNN架构。
图3-14:一个卷积神经网络架构
我们可以使用这个架构创建一个很好的分类器。但是对于迁移学习,如果我们可以从另一个模型中获取预先学习的层,冻结或者锁定它们以使其不可训练,然后将它们放置到我们的模型顶端,就像图3-15那样,会怎么样呢?
图3-15:通过迁移学习从另一个架构中获取一些层
一旦它们已经被训练好了,所有这些层都只是一组数字,用来表示滤波器的值、权重、偏差以及一个已知的架构(每一层滤波器的个数、滤波器的大小等),那么重用它们的想法就十分直接了。
让我们看看这在代码中看起来是什么样子的。已经有几个预先训练好的模型可以从不同的来源获得。我们将使用Google著名的Inception模型的第3版,这个模型是在超过一百万张图像上训练的,来自一个叫作ImageNet的数据库。它有十几个层,并且可以把图像分类到1000个类别。有一个保存的模型包含这些训练好的权重。为了使用它,我们只需下载这些权重,创建一个Inception V3架构的实例,并把权重加载到这个架构中,代码如下所示:
现在我们有一个完整的预训练好的Inception模型。如果你想查看它的架构,可以运行下面这行代码实现:
警告!它非常巨大!话虽如此,请看一看它的层和各层的名字。我喜欢使用叫作mixed7
的层,因为它的输出又好又小—7×7的图像。
接下来,我们会冻结整个网络不让它再训练,然后为mixed7
的输出设置一个变量,这里是我们希望截取网络的部分。我们可以运行以下代码实现:
值得注意的是,我们打印了最后一层的输出形状,你将看到在这里得到的是7×7的图像。这意味着当图像被传入mixed7
时,从滤波器输出的图像的大小是7×7,因此它们很容易管理。再次强调,你并不一定要选择mixed7
,也可以尝试其他的层。
现在让我们看一下如何添加密集层:
它与从上一层的输出创建一个展平的层集合一样简单,因为我们将把这些结果传入一个密集层。接下来添加一个包含1024个神经元的密集层,以及一个包含一个神经元的密集层作为输出。
现在我们可以简单地定义模型,通过声明它是我们预先训练好的模型的输入,后跟刚刚定义的x
。编译它:
在这个架构上训练超过40回合的模型可以达到99%+的准确率,而且验证集的准确率是96%+(如图3-16所示)。
图3-16:通过迁移学习训练horse-or-human分类器
这个结果比之前的模型好太多了,而且还可以继续优化它。你也可以探索这个模型在一个更大的数据集上表现得怎么样,例如Kaggle上知名的Dogs vs. Cats(https://www.kaggle.com/c/dogs-vs-cats)。这是一个非常丰富的数据集,它包含25 000张猫和狗的图像。
使用和之前相同的算法和模型设计,你可以在Colab上训练一个Dogs vs.Cats分类器,每个回合使用GPU大概3分钟。
当使用图3-17中非常复杂的图片进行测试时,这个分类器得到了所有正确的结果。我选了一张像猫咪耳朵一样的狗照片和一张只有背影的狗照片。两张猫的图片都不是典型的。
图3-17:不常见的狗和猫可以被正确分类
右下角的小猫眼睛是闭着的,耳朵朝下,并且舌头伸出来正在舔洗自己的爪子,当把它加载到模型时,结果如图3-18所示。你可以看到它给了一个非常低的值(4.98×10-24),这展示了模型几乎可以确定这是一只猫!
图3-18:分类一只正在洗爪子的猫
你可以在GitHub(https://github.com/lmoroney/tfbook)上找到Horses or Humans以及Dogs vs. Cats分类器的完整代码。