【学习笔记】pytorch迁移学习-猫狗分类实战

1.迁移学习入门
什么是迁移学习:在深度神经网络算法的引用过程中,如果我们面对的是数据规模较大的问题,那么在搭建好深度神经网络模型后,我们势必要花费大量的算力和时间去训练模型和优化参数,最后耗费了这么多资源得到的模型却只能解决这一个问题,性价比较低。如果训练得到的模型能够解决同一类问题,那么模型的性价比就会提高很多,这就促使使用迁移模型解决同一类问题的方法出现。我们通过对一个训练好的模型进行细微调整,就能将其应用到相似的问题中,最后还能取得很好的效果;另外,对于原始数据较少的问题,我们也能够通过采用迁移模型进行有效的解决,所以,如果能够选取合适的迁移学习方法,则会对解决我们所面临的的问题有很大的帮助。
如果我们选择使用卷积神经网络模型解决猫狗分类问题,需要首先搭建模型,然后不断对模型进行训练,使其预测猫和狗的图片的准确性达到要求的阈值,在此过程中会消耗大量的时间在参数优化和模型训练上。之后面临类似分类问题时,可以直接使用以前已经得到的模型和模型的参数并稍加改动来满足新的需求。不过,对迁移模型需要进行重新训练,这是因为最后分类的对象发生了变化,但是重新训练的时间和搭建全新的模型进行训练的时间相比很少。通过迁移学习可以节省大量的时间和精力,而且最终得到的结果不会太差,这就是迁移学习的优势和特点。
需要注意的是,迁移学习过程中有时会导致迁移模型出现负迁移,可以将其理解为模型泛化能力恶化。加入我们将迁移学习用于解决两个毫不相关的问题,则极有可能使最后迁移得到的模型出现负迁移。
2.数据集处理
使用的数据集来自Kaggle网站上的“Dogs vs. Cats”竞赛项目,可以通过网络免费下载这些数据集。在这个数据集的训练数据集中一共有25000张猫和狗的图片,测试集中有12500张图片。
2.1 验证数据集和测试数据集
在实践中,我们不会直接使用测试数据集对搭建的模型进行训练和优化,而是在训练数据集中划出一部分作为验证集,来评估在每个批次的训练后模型的泛化能力。这样做的原因是如果我们使用测试数据集进行模型的训练和优化,那么模型最终会对测试数据集产生拟合倾向,换言之,我们的模型只有在对测试数据集中图片的类别进行预测时才有极强的准确率,而在对测试数据集以外的图片类别进行预测时会出现非常多的错误,这样的模型缺少泛化能力。所以,为了防止这种情况的出现,我们会把测试数据集从模型的训练和优化过程中隔离出来,只在每轮训练结束后使用。如果模型对验证数据集和测试数据集的预测同时具备高准确率和低损失值,就基本说明模型的参数优化是成功的,模型将具备极强的泛化能力,在本章的实践中我们分别从训练数据集的猫狗图片中各抽出2500张图片构成一个具有5000张图片的验证数据集。
2.2 数据预览
在划分好数据集后,可以进行数据预览。

import torch
import torchvision
from torchvision import datasets, transforms
import os
import matplotlib.pyplot as plt
import time

%matplotlib inline

以上代码中先导入必要的包,新增加os包和time包,os包集成了一些对文件路径和目录进行操作的类,time包主要是一些和时间相关的方法。
在获取全部数据集后,我们可以对数据进行简单分类。新建一个名为DogsVSCats的文件夹,在该文件夹下新建一个名为train和valid的子文件夹,在子文件夹下面再分别新建一个名为cat和dog的文件夹,最后将数据集中对应部分的数据放到对应名字的文件夹中,之后就可以进行数据的载入了。

data_dir = "DogsVSCats"
data_transform = {
   x:transforms.Compose([transforms.Scale([64,64]),
					transforms.ToTensor()]) for x in ["train","valid"]}
image_datasets = {
   x:datasets.ImageFolder(root = os.path.join(data_dir,x)),
					transform = data_transform[x] for x in ["train","valid"]}
dataloader = {
   x:torch.utils.data.DataLoader(dataset = image_datasets[x],
				batch_size = 16,
				shuffle = True)
				for x in ["train","valid"]}

在进行数据的载入时我们使用torch.transforms中的Scale类将原始图片的大小统一缩放至64*64。在以上代码中对数据的变换和导入都使用了字典的形式,因为我们需要分别对训练数据集和验证数据集的数据载入方法进行简单定义,所以使用字典可以简化代码,也方便之后进行相应的调用和操作。
os.path.join来自os包,它的作用是将输入参数中的两个名字拼接成一个完整的文件路径。其他常用os.path类方法如下:
**(1)os.path.dirname:**用于返回一个目录的目录名,输入参数为文件的目录。
**(2)os.path.exists:**用于测试输入参数指定的文件是否存在
**(3)os.path.isdir:**用于测试输入参数是否是目录名
**(4)os.path.isfile:**用于测试输入参数是否是一个文件
**(5)os.path.samefile:**用于测试两个输入的路径参数是否指向同一个文件
**(6)os.path.split:**用于对输入参数中的目录名进行分割,返回一个元组,该元组由目录名和文件名组成
接下来获取一个批次的数据并进行数据预览和分析:

X_example, y_example - next(iter(dataloader["train"]))

以上代码通过next和iter迭代操作获取一个批次的装载数据,不过因为受到我们之前定义的batch_size值得影响,这一批次数据只有16张图片,所以X_example的长度也全部是16.可以通过打印这两个变量来确认。打印代码如下:

print(u"X_example 个数{}".format(len(X_example)))
print(u"y_example 个数{}".format(len(y_example)))

其中,X_example是Tensor数据类型的变量,因为做了图片大小的缩放变换,所以现在图片的大小全是64*64了,X_example的维度就是(16,3,64,64),16代表批次中有16张图片;3代表色彩通道数;64代表图片宽度和高度。
y_example也是Tensor数据类型的变量,不过其中元素全是0和1。这是因为在进行数据装载时已经对dog文件夹和cat文件夹下的内容进行了独热编码,所以这时的0和1不仅是每张图片的标签,还分别对应猫的图片和狗的图片。我们可以简单做一个打印输出,来验证这个独热编码的对应关系:

index_classes = image_datasets["train"].class_to_idx
print(index_classes)

输出结果如下:

{
   'cat':0,'dog':1}

这样就很明显了,猫的图片标签和狗的图片标签被独热编码后分别被数字化了,相较于使用文字作为图片的标签而言,使用0和1也可以让之后的计算方便很多。不过,为了增加之后绘制的图像标签的可识别性,我们还需要通过image_datasets[“train”].classes将原始标签的结果存储在名为example_classes的变量中。代码如下:

example_classes = image_datasets["train"].classes
print(example_classes)

example_classes其实是一个列表,而且在这个列表中只有两个元素,分别是dog和cat。我们是用Matplotlib对一个批次的图片进行绘制,具体代码如下:

img = torchvision.utils.make_grid(X_example)
img = img.numpy().transpose([1,2,0])
print([example_classes[i] for i in y_example])
plt.imshow(img)
plt.show()

3.模型搭建和参数优化
本章会先基于一个简化的VGGNet架构搭建卷积神经网络模型并进行模型训练和参数优化,然后迁移一个完整的VGG16架构的卷积神经网络模型,最后迁移一个ResNet50架构的卷积神经网络模型,并对比这三个模型在预测结果上的准确性和在泛化能力上的差异。
3.1 自定义VGGNet
首先基于VGG16架构来搭建一个简化版的VGGNet模型,要求输入图片大小为6464,而在标准的VGG16架构模型中输入图片大小为224224的;同时简化版模型删除了VGG16最后的三个卷积层和池化层,也改变了全连接层中的连接参数,这一系列的改变都是为了减少整个模型参与训练的参数数量。简化版模型的搭建代码如下:

class Models(torch.nn.Module):
	def __init__(self):
		super(Models, self).__init__()
		self.Conv = torch.nn.Sequential(
			torch.nn.Conv2d(3,64,kernel_size = 3,stride = 1,padding = 1),
			torch.nn.ReLU(),
			torch.nn.Conv2d(64,64,kernel_size = 3,stride = 1,padding = 1),
			torch.nn.ReLU(),
			torch.nn.MaxPool2d(kernel_size = 2, stride =2),

			torch.nn.Conv2d(64,128,kernel_size = 3,stride = 1,padding = 1),
			torch.nn.ReLU(),
			torch.nn.Conv2d(128,128,kernel_size = 3,stride = 1,padding = 1),
			torch.nn.ReLU(),
			torch.nn.MaxPool2d(kernel_size &#
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值