使用PyTorch进行迁移学习

概述

  • 迁移学习能够改变你创建机器学习和深度学习模型的方式
  • 了解如何使用PyTorch进行迁移学习,以及如何将其与使用预训练的模型联系起来
  • 咱们将使用真实世界的数据集,并比较使用卷积神经网络(CNNs)构建的模型和使用迁移学习构建的模型的性能

介绍

我去年在一个计算机视觉项目中工做,咱们必须创建一个健壮的人脸检测模型。python

考虑到咱们拥有的数据集的大小,从头构建一个模型是一个挑战。从头构建将是一个耗时又消耗计算资源的方案。因为时间紧迫,咱们必须尽快找出解决办法。数组

这就是迁移学习拯救咱们的时候。这是一个很是有用的工具,能够放在你的数据科学家库中,特别是当你使用有限的时间和计算能力时。网络

所以,在本文中,咱们将学习有关迁移学习的全部内容,以及如何在使用Python的实际项目中利用它。咱们还将讨论预训练模型在这个领域的做用,以及它们将如何改变构建机器学习pipeline(管道)的方式。架构

本文是面向初学者的PyTorch系列文章的一部分。我坚信PyTorch是目前最好的深度学习框架之一,在不久的未来会愈来愈强大。这是一个很好的时间来学习它是如何工做的,并参与其中。app

目录

  1. 迁移学习概论
  2. 什么是预训练模型?如何选择正确的预训练模型?
  3. 案例研究:紧急与非紧急车辆分类
  4. 使用卷积神经网络(CNNs)解决挑战
  5. 使用PyTorch的迁移学习解决挑战
  6. CNN的性能比较和迁移学习

迁移学习概论

让我用一个例子来讲明迁移学习的概念。想象一下,你想从一个你彻底陌生的领域学习一个主题。框架

你会采起什么不一样的方法来理解这个主题?你可能会:机器学习

  • 网上搜寻资源
  • 阅读文章和博客
  • 参考书籍
  • 寻找视频教程,等等

全部这些都会帮助你熟悉这个主题。在这种状况下,你是惟一一个付出全部时间来熟悉主题的人。函数

但还有另外一种方法,它可能在短期内产生更好的结果。工具

你能够咨询对你想要学习的主题具备能力的领域/主题专家。这我的会把他/她的知识传授给你。从而加快你的学习过程。性能

> 第一种方法,即你独自投入全部的努力,是从头开始学习的一个例子。第二种方法被称为迁移学习。知识转移发生在一个领域的专家到一个面对新领域的新手。

是的,迁移学习背后的思想就是这么简单!

神经网络和卷积神经网络就是从零开始学习的例子。这两个网络都从给定的一组图像中提取特征(对于与图像相关的任务),而后根据这些提取的特征将图像分类到各自的类中。

这就是迁移学习和预训练的模型很是有用的地方。让咱们在下一节中了解一下后一个概念。

什么是预训练模型?如何选择正确的预训练模型?

在你将要从事的任何深度学习项目中,预训练的模型都是很是有用的。并不是全部人都拥有顶级科技巨头的无限计算能力,相反咱们须要使用咱们本地有限的机器,因此预训练模型是一个强大的工具。

> 正如你可能已经猜到的,预训练模型是由特定人员或团队为解决特定问题而设计和训练的模型。 > 回想一下,咱们在训练神经网络和CNNs等模型时学习了权重和偏置。当这些权重和偏置与图像像素相乘时,有助于生成特征。

预训练的模型经过将它们的权重和偏置传递给一个新模型来共享它们的学习。所以,当咱们进行迁移学习时,咱们首先选择恰当的预训练模型,而后将其已学习的权值和偏置传递给新模型。

有n种预训练过的模型。咱们须要决定哪一种模式最适合咱们的问题。如今,让咱们考虑一下咱们有三个预训练好的网络——BERT、ULMFiT和VGG16。

咱们的任务是对图像进行分类(正如咱们在本系列的前几篇文章中所作的那样)。那么,你会选择哪些预训练好的模型呢?让我先给你一个快速的概述这些预训练的网络,这将帮助咱们决定正确的预训练的模型。

语言建模使用BERT和ULMFiT,图像分类任务使用VGG16。若是你看一下手头的问题,这是一个图像分类问题。因此咱们选择VGG16是理所固然的。

如今,VGG16能够有不一样的重量,即VGG16训练在ImageNet或VGG16训练在MNIST:

ImageNet与MNIST

如今,为咱们的问题肯定正确的预训练模型,咱们应该研究这些ImageNet和MNIST数据集。ImageNet数据集由1000个类和总共120万张图像组成。这些数据中的一些类别是动物、汽车、商店、狗、食物、仪器等:

另外一方面,MNIST是训练手写数字的。它包括10类从0到9:

咱们将在一个项目中工做,咱们须要将图像分为紧急和非紧急车辆(咱们将在下一节详细讨论)。这个数据集包括车辆的图像,所以在ImageNet数据集上训练的VGG16模型将更有用,由于它有车辆的图像。

简而言之,这就是咱们应该如何根据咱们的问题来决定正确的预训练模型。

案例研究:紧急与非紧急车辆分类

咱们将进行一个新的目标!这里,咱们的目标是将车辆分为紧急和非紧急。

如今让咱们开始理解这个问题并可视化一些示例。你能够经过这个连接下载图片:https://drive.google.com/file/d/1EbVifjP0FQkyB1axb7KQ26yPtWmneApJ/view

首先,导入所需的库:

接下来,咱们将读取包含图像名称和相应标签的.csv文件:

csv文件有两列:

  1. image_names:它表示数据集中全部图像的名称
  2. emergency_or_no:它指定特定的图像属于紧急类仍是非紧急类。0表示图像是非紧急车辆,1表示紧急车辆

接下来,咱们将加载全部的图像,并将它们存储为数组格式:

加载这些图像大约须要12秒。在咱们的数据集中有1646张图像,因为VGG16须要全部这种特殊形状的图像,因此咱们将它们的形状所有重设为(224,224,3)。如今让咱们从数据集中可视化一些图像:

这是一辆警车,所以有紧急车辆的标签。如今咱们将目标存储在一个单独的变量:

让咱们建立一个验证集来评估咱们的模型:

咱们在训练集中有1,481张图像,在验证集中有165张图像。如今咱们必须将数据集转换为torch格式:

相似地,咱们将转换验证集:

咱们的数据准备好了!在下一节中,咱们将构建一个卷积神经网络(CNN),而后使用预训练模型来解决这个问题。

使用卷积神经网络(CNNs)解决挑战

咱们终于到了模型制做部分!在使用迁移学习来解决这个问题以前,咱们先用一个CNN模型为本身设定一个benchmark。

咱们将构建一个很是简单的CNN架构,它有两个卷积层来提取图像的特征,最后是一个全链接层来对这些特征进行分类:

如今让咱们定义优化器,学习率和损失函数为咱们的模型,并使用GPU训练模型:

这就是模型架构的样子。最后,咱们将对模型进行15个epoch的训练。我将模型的batch_size设置为128(你能够尝试一下):

这也会打印一份训练总结。训练损失在每一个时期以后都在减小,这是一个好迹象。咱们来检查一下训练和验证的准确性:

咱们的训练正确率在82%左右,这是一个不错的分数。下面检查验证的准确性:

# 验证集预测
prediction_val = []
target_val = []
permutation = torch.randperm(val_x.size()[0])
for i in tqdm(range(0,val_x.size()[0], batch_size)):
    indices = permutation[i:i+batch_size]
    batch_x, batch_y = val_x[indices], val_y[indices]

    if torch.cuda.is_available():
        batch_x, batch_y = batch_x.cuda(), batch_y.cuda()

    with torch.no_grad():
        output = model(batch_x.cuda())

    softmax = torch.exp(output).cpu()
    prob = list(softmax.numpy())
    predictions = np.argmax(prob, axis=1)
    prediction_val.append(predictions)
    target_val.append(batch_y)
    
# 验证集精确度
accuracy_val = []
for i in range(len(prediction_val)):
    accuracy_val.append(accuracy_score(target_val[i],prediction_val[i]))
    
print('validation accuracy: \t', np.average(accuracy_val))

验证的准确性为76%。如今咱们已经有了一个基准,是时候使用迁移学习来解决紧急和非紧急车辆分类问题了!

使用PyTorch的迁移学习解决挑战

我在上面已经提到了这一点,我在这里重申一下——咱们将使用在ImageNet数据集上训练的VGG16预训练模型。让咱们看看咱们将遵循的步骤,以训练模型使用迁移学习:

  1. 首先,咱们将加载预训练模型的权重——在咱们的例子中是VGG16
  2. 而后咱们将根据手头的问题对模型进行微调
  3. 接下来,咱们将使用这些预训练的权重并提取图像的特征
  4. 最后,咱们将使用提取的特征训练精细调整的模型

那么,让咱们从加载模型的权重开始:

# 加载预训练模型
model = models.vgg16_bn(pretrained=True)

如今咱们将对模型进行微调。咱们不训练VGG16模型的层,所以让咱们固定这些层的权重:

# 固定模型权重
for param in model.parameters():
    param.requires_grad = False

因为咱们只须要预测2个类,而VGG16是在ImageNet上训练的,ImageNet有1000个类,咱们须要根据咱们的问题更新最后一层:

# 最后加一个分类器
model.classifier[6] = Sequential(
                      Linear(4096, 2))
for param in model.classifier[6].parameters():
    param.requires_grad = True

由于咱们只训练最后一层,因此我将最后一层的requires_grad设置为True。咱们将训练设置为GPU:

# 检查GPU是否可用
if torch.cuda.is_available():
    model = model.cuda()

如今,咱们将使用该模型并为训练和验证图像提取特性。我将batch_size设置为128(一样,你能够根据须要增长或减小batch_size):

# batch大小
batch_size = 128

# 从训练集提取特征
data_x = []
label_x = []

inputs,labels = train_x, train_y

for i in tqdm(range(int(train_x.shape[0]/batch_size)+1)):
    input_data = inputs[i*batch_size:(i+1)*batch_size]
    label_data = labels[i*batch_size:(i+1)*batch_size]
    input_data , label_data = Variable(input_data.cuda()),Variable(label_data.cuda())
    x = model.features(input_data)
    data_x.extend(x.data.cpu().numpy())
    label_x.extend(label_data.data.cpu().numpy())

相似地,让咱们提取验证图像的特征:

# 从验证集提取特征
data_y = []
label_y = []

inputs,labels = val_x, val_y

for i in tqdm(range(int(val_x.shape[0]/batch_size)+1)):
    input_data = inputs[i*batch_size:(i+1)*batch_size]
    label_data = labels[i*batch_size:(i+1)*batch_size]
    input_data , label_data = Variable(input_data.cuda()),Variable(label_data.cuda())
    x = model.features(input_data)
    data_y.extend(x.data.cpu().numpy())
    label_y.extend(label_data.data.cpu().numpy())

接下来,咱们将这些数据转换成torch格式:

# 转换这些数据到torch格式
x_train  = torch.from_numpy(np.array(data_x))
x_train = x_train.view(x_train.size(0), -1)
y_train  = torch.from_numpy(np.array(label_x))
x_val  = torch.from_numpy(np.array(data_y))
x_val = x_val.view(x_val.size(0), -1)
y_val  = torch.from_numpy(np.array(label_y))

咱们还必须为咱们的模型定义优化器和损失函数:

# batch大小
batch_size = 128

# 30个epochs
n_epochs = 30

for epoch in tqdm(range(1, n_epochs+1)):

    # 跟踪训练与验证集损失
    train_loss = 0.0
        
    permutation = torch.randperm(x_train.size()[0])

    training_loss = []
    for i in range(0,x_train.size()[0], batch_size):

        indices = permutation[i:i+batch_size]
        batch_x, batch_y = x_train[indices], y_train[indices]
        
        if torch.cuda.is_available():
            batch_x, batch_y = batch_x.cuda(), batch_y.cuda()
        
        optimizer.zero_grad()
        outputs = model.classifier(batch_x)
        loss = criterion(outputs,batch_y)

        training_loss.append(loss.item())
        loss.backward()
        optimizer.step()
        
    training_loss = np.average(training_loss)
    print('epoch: \t', epoch, '\t training loss: \t', training_loss)

如下是该模型的摘要。你能够看到损失减小了,所以咱们能够说模型在改进。让咱们经过观察训练和验证的准确性来验证这一点:

# 预测训练集
prediction = []
target = []
permutation = torch.randperm(x_train.size()[0])
for i in tqdm(range(0,x_train.size()[0], batch_size)):
    indices = permutation[i:i+batch_size]
    batch_x, batch_y = x_train[indices], y_train[indices]

    if torch.cuda.is_available():
        batch_x, batch_y = batch_x.cuda(), batch_y.cuda()

    with torch.no_grad():
        output = model.classifier(batch_x.cuda())

    softmax = torch.exp(output).cpu()
    prob = list(softmax.numpy())
    predictions = np.argmax(prob, axis=1)
    prediction.append(predictions)
    target.append(batch_y)
    
# 训练精度
accuracy = []
for i in range(len(prediction)):
    accuracy.append(accuracy_score(target[i],prediction[i]))
    
print('training accuracy: \t', np.average(accuracy))

咱们在训练集上得到了大约84%的准确性。如今让咱们检查验证的准确性:

# 预测验证集
prediction = []
target = []
permutation = torch.randperm(x_train.size()[0])
for i in tqdm(range(0,x_train.size()[0], batch_size)):
    indices = permutation[i:i+batch_size]
    batch_x, batch_y = x_train[indices], y_train[indices]

    if torch.cuda.is_available():
        batch_x, batch_y = batch_x.cuda(), batch_y.cuda()

    with torch.no_grad():
        output = model.classifier(batch_x.cuda())

    softmax = torch.exp(output).cpu()
    prob = list(softmax.numpy())
    predictions = np.argmax(prob, axis=1)
    prediction.append(predictions)
    target.append(batch_y)
    
# 验证精度
accuracy = []
for i in range(len(prediction)):
    accuracy.append(accuracy_score(target[i],prediction[i]))
    
print('training accuracy: \t', np.average(accuracy))

模型的验证精度也类似, 83%。训练和验证的准确性几乎是同步的,所以咱们能够说这个模型是广义的。如下是咱们的研究结果摘要:

Model Training Accuracy Validation Accuracy
CNN 81.57% 76.26%
VGG16 83.70% 83.47%

咱们能够推断,与CNN模型相比,VGG16预训练模型的准确率有所提升!

结尾

在这篇文章中,咱们学习了如何使用预训练的模型和迁移学习来解决一个图像分类问题。咱们首先了解什么是预训练模型,以及如何根据手头的问题选择正确的预训练模型。而后,咱们以汽车图像为例进行了紧急和非紧急图像的分类研究。咱们首先使用CNN模型解决了这个案例研究,而后使用VGG16预训练模型解决了一样的问题。

咱们发现使用VGG16预训练模型显著提升了模型性能,而且与CNN模型相比,咱们获得了更好的结果。我但愿你如今已经清楚地了解了如何在使用PyTorch使用迁移学习和正确的预训练模型来解决问题。

我鼓励你试着用迁移学习来解决其余的图像分类问题。这将帮助你更清楚地理解这个概念。

相关文章
相关标签/搜索