深度学习笔记(九)---迁移学习

1.概念

1.1 什么是迁移学习?

深度学习算法应用过程中,数据规模大,训练时间长是我们遇到到一大问题,那么在搭建好深度神经网络模型后,我们还需要大量的算力和时间训练和参数的优化,使得性价比非常低。我们就考虑同类问题的模型迁移,这样提高模型的性价比。考虑刀片没有免费午餐原理,我们尽可能的提高模型的泛化能力以及鲁棒性。需要注意的是,我们在使用迁移学习的过程中有事会导致迁移模型的负迁移,我们可以理解为模型的泛化能力的恶化。当利用同一模型处理两个相关性很小的问题就很容易出现这种情况。

2.数据读取

我们将使用kaggle dogs vs.cats数据集进行模型的迁移,首先我们需要对数据集进行处理。

import torch
import torchvision
from torchvision import datasets,models,transforms
import os
import matplotlib.pyplot as plt
from torch.autograd import Variable
import time
#torchvision包主要实现数据的处理,导入和预览,以下数据的导入我们将使用这个包

2.1数据集处理

关于kaggle dogs vs.cats数据集,官网提供的train和test,其中train data包含25000张猫狗的照片,且数量相同。但test data中的猫狗是无序混乱的,且没有label。为了增加模型的泛化能力,我们从训练集中选出一部分做验证集。关于train data ,test data ,valid data的区别下面详细说明一下。

1. 训练数据(Training Set)

     用于调整网络的权重(weights)和偏差(biases)。 

2. 验证数据(Validation Set)

    验证数据用于最小化过拟合(overfitting)。训练完成之后,使用测试数据验证其准确度是否满足要求,即验证其推广/泛化能力。

    这数据不调整权重和偏差。在基于训练数据调整权重之后,如果基于训练数据的准确度增加了,而基于验证数据的准确度没有增加或反而下降了,则表明过拟合(overfitting)了,需要立即停止训练。

3. 测试数据(Testing Set)

    在训练完成之后,使用测试数据确认网络真正的预测和分类能力。

4. Ground Truth

    在有监督学习中,数据是有标注的,以(x, t)的形式出现,其中x是输入数据,t是标注。正确的t标注是Ground Truth, 错误的标记则不是。(也有人将所有标注数据都叫做Ground Truth)

所以为了使得模型具有很高的准确率和低损值,我们将验证集看作模拟的训练集,将测试集看作最终的测试,从而提高泛化能力。

2.2数据集预览

获取全部的数据集之后,我们就可以对这些数据进行简单分类了。新建一个名为DogsVSCats的文件夹,在该文件夹下面新建一个名为train和一个名为valid的子文件夹,在子文件夹下面再分别新建一个名为cat的文件夹和一个名为dog的文件夹,最后将数据集中对应部分的数据放到对应名字的文件夹中,之后就可以进行数据的载入了。

data_dir = "DogsVSCats"
data_transform = {x:transforms.Compose([transforms.Scale([224,224]),
                                        transforms.ToTensor(),
                                        transforms.Normalize(mean=[0.5,0.5,0.5], std=[0.5,0.5,0.5])])
                  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"]}

判断是否支持GPU

Use_gpu = torch.cuda.is_available()

3.模型的迁移

以下代码中我们将进行下载的模型是VGG16,并通过设置prepaer=True中的值为Ture,来实现下载的模型附带了已经优化好的

模型参数。

model = models.vgg16(pretrained=True)

第二步需要对迁移的模型进行调整。在迁移学习中全连接层是我们经常调整的部分。思想是冻结卷积神经网络中全连接层之前的所有网络,让这些被冻结的网络层次中的参数不进行梯度更新,能够被优化的参数仅仅是没有被冻结的全连接层的全部参数。

代码调整如下:

or parma in model.parameters():
    parma.requires_grad = False
    model.classifier = torch.nn.Sequential(torch.nn.Linear(25088, 4096),
                                           torch.nn.ReLU(),
                                           torch.nn.Dropout(p=0.5),
                                           torch.nn.Linear(4096, 4096),
                                           torch.nn.ReLU(),
                                           torch.nn.Dropout(p=0.5),
                                           torch.nn.Linear(4096, 2))
if Use_gpu:
    model = model.cuda()

cost = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.classifier.parameters())
loss_f = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.classifier.parameters(), lr = 0.00001)
epoch_n = 5

首先,对原模型中的参数进行遍历操作,将参数中的parma.requires_grad全部设置为False,这样对应的参数将不计算梯度,
当然也不会进行梯度更新了,这就是之前说到的冻结操作;然后,定义新的全连接层结构并重新赋值给model.classifier。在完成了新的全连接层定义后,全连接层中的parma.requires_grad参数会被默认重置为True,所以不需要再次遍历参数来进行解冻操作。损失函数的loss值依然使用交叉熵进行计算,但是在优化函数中负责优化的参数变成了全连接层中的所有参数,即对  model.classifier.parameters这部分参数进行优化。

time_open = time.time()
for epoch in range(epoch_n):
    print("Epoch {}/{}".format(epoch, epoch_n - 1))
    print("-"*10)
    for phase in ["train", "valid"]:
        if phase == "train":
            print("Training...")
            model.train(True)
        else:
            print("Validing...")
            model.train(False)
            
    running_loss = 0.0
    running_corrects = 0
    for batch, data in enumerate(dataloader[phase], 1):

        X, y = data
        if Use_gpu:
            X, y = Variable(X.cuda()), Variable(y.cuda())
        else:
            X, y = Variable(X), Variable(y)
            
        y_pred = model(X)
        _, pred = torch.max(y_pred.data, 1)
        optimizer.zero_grad()
        loss = loss_f(y_pred, y)
        
        if phase == "train":
            loss.backward()
            optimizer.step()
            running_loss += loss.data[0]
            running_corrects += torch.sum(pred == y.data)
            
            if batch%500 == 0 and phase =="train":
                print("Batch {}, Train Loss:{:.4f}, Train ACC:{:.4f}".format(batch, running_loss/batch, 100*running_corrects(16*batch)))

    epoch_loss = running_loss*16/len(image_datasets[phase])
    epoch_acc = 100*running_corrects/len(image_datasets[phase])
    print("{} Loss:{:.4f} Acc:{:.4f}%".format(phase, epoch_loss,epoch_acc))
            
time_end = time.time() - time_open
print(time_end)

猜你喜欢

转载自blog.csdn.net/qq_41997920/article/details/88839129