第 4 章. 迁移学习和其他技巧
本作品已使用人工智能进行翻译。欢迎您提供反馈和意见:translation-feedback@oreilly.com
看过上一章的架构后,你可能会想,是否可以下载一个已经训练好的模型,然后对它进行进一步的训练。答案是肯定的!这是 Deep Learning 圈子里一种非常强大的技术,叫做迁移学习(transfer learning),即把为一项任务(如 ImageNet)训练的网络调整为另一项任务(鱼与猫)。
为什么要这么做呢?事实证明,在 ImageNet 上训练出来的架构已经对图像有了非常深入的了解,尤其是对某物是猫还是鱼(或狗或鲸鱼)有了相当深入的了解。由于你不再从一个基本空白的神经网络开始,因此使用迁移学习,你可能花费更少的时间进行训练,而且你可以使用更小的训练数据集。传统的 Deep Learning 方法需要海量数据才能产生好的结果。而使用迁移学习,只需几百张图片就能建立人类级别的分类器。
使用 ResNet 进行迁移学习
现在,显而易见的做法是创建一个 ResNet 模型,就像我们在第 3 章中所做的那样,将其放入现有的训练循环中。你可以这么做!ResNet 模型并没有什么神奇之处,它也是由你已经见过的相同构件建立起来的。不过,它是一个庞大的模型,虽然与基线 ResNet 模型相比,您的数据会有一些改进,但您需要大量的数据,以确保训练信号能够到达架构的各个部分,并对它们进行显著的训练,以完成新的分类任务。在这种方法中,我们尽量避免使用大量数据。
但问题是:我们所处理的架构并不像过去那样是用随机参数初始化的。我们预先训练好的 ResNet 模型已经包含了大量信息,可以满足图像识别和分类的需要,所以为什么还要费心重新训练它呢?相反,我们要对网络进行微调。我们稍微改变一下架构,在末端加入一个新的网络块,取代通常执行 ImageNet 分类的标准 1,000 类线性层。然后,我们冻结所有现有的 ResNet 层,在训练时,我们只更新新层的参数,但仍采用冻结层的激活值。这样,我们就能在快速训练新层的同时,保留预训练层已包含的信息。
首先,让我们创建一个预训练的 ResNet-50 模型:
fromtorchvisionimportmodelstransfer_model=models.ResNet50(pretrained=True)
接下来,我们需要冻结各层。方法很简单:使用requires_grad() 阻止它们积累梯度。我们需要对网络中的每一个参数都这么做,不过好在 PyTorch 提供了一个parameters() 方法,让这一操作变得相当简单:
forname,paramintransfer_model.named_parameters():param.requires_grad=False
提示
您可能不想冻结模型中的BatchNorm 层,因为它们将被训练成近似于模型最初训练的数据集的均值和标准偏差,而不是您想要微调的数据集的均值和标准偏差。在BatchNorm 修正输入时,数据中的一些信号可能会丢失。您可以查看模型结构,只冻结不属于BatchNorm 的层:
forname,paramintransfer_model.named_parameters():if("bn"notinname):param.requires_grad=False
然后,我们需要用一个新的分类块替换最终的分类块,我们将训练它来检测猫或鱼。在这个例子中,我们用几个Linear ...