为什么使用使用transform来代替top_使用Pytorch进行多类图像分类

afb226931a11b775e89986be79203065.png

作者 | Pandeynandancse

来源 | Medium

编辑 | 代码医生团队

本教程的数据摘自Kaggle,该数据最初由Intel在analytics-vidhya上发布,以举办图像分类挑战赛。

https://www.kaggle.com/puneet6060/intel-image-classification

关于数据集

该数据包含大约65,000幅大小为150x150的25,000张图像。

{ ‘buildings’ : 0,‘forest’ : 1,‘glacier’ : 2,‘mountain’ : 3,‘sea’ : 4,‘street’ : 5 }

训练,测试和预测数据在每个zip文件中分开。训练中大约有14k图像,测试中有3k,预测中有7k。

挑战

这是一个多类图像分类问题。目的是将这些图像更准确地分类为正确的类别。

先决条件

基本了解python,pytorch和分类问题。

方法

  1. 做一些探索性数据分析(EDA)来分析和可视化数据,以便更好地理解。

  2. 定义一些实用程序功能来执行各种任务,以便可以保持代码的模块化。

  3. 加载各种预先训练的模型,并根据我们的问题对其进行微调。

  4. 为每个模型尝试各种超参数。

  5. 减轻模型的重量并记录指标。

  6. 结论

  7. 未来的工作

1.导入库

首先,导入所有重要的库。

import os
import torch
import tarfile
import torchvision
import torch.nn as nn
from PIL import Image
import matplotlib.pyplot as plt
import torch.nn.functional as F
from torchvision import transforms
from torchvision.utils import make_grid
from torch.utils.data import random_split
from torchvision.transforms import ToTensor
from torchvision.datasets import ImageFolder
from torch.utils.data import Dataset, DataLoader
from torchvision.datasets.utils import download_url

2.图像文件夹到数据集

由于我们的数据存在于文件夹中,因此让我们将其转换为数据集。

transform_train = transforms.Compose([
    transforms.Resize((150,150)), #becasue vgg takes 150*150
    transforms.RandomHorizontalFlip(),
    transforms.RandomVerticalFlip(),
    transforms.ToTensor(),
    transforms.Normalize((.5, .5, .5), (.5, .5, .5))
])#Augmentation is not done for test/validation data.
transform_test = transforms.Compose([
    transforms.Resize((150,150)), #becasue vgg takes 150*150
    transforms.ToTensor(),
    transforms.Normalize((.5, .5, .5), (.5, .5, .5))
])
train_ds = ImageFolder('../input/intel-image-classification/seg_train/seg_train', transform=transform_train)test_ds = ImageFolder('../input/intel-image-classification/seg_test/seg_test', transform=transform_test)pred_ds = ImageFolder('/kaggle/input/intel-image-classification/seg_pred/', transform=transform_test)

3.探索性数据分析(EDA)

在这里作为EDA的一部分回答一些问题,但是EDA并没有广泛涉及。

继续回答一些问题。

a)数据集中有多少张图片?

答:

b78b0364d738dfd8e63c5929491959cd.png

这意味着有14034张图像用于训练,3000张图像用于测试/验证以及7301张图像用于预测。

b)你能告诉我图像尺寸吗?

答:

f17b08038486dce46dca325fed1cd96b.png

这意味着图像大小为150 * 150,具有三个通道,其标签为0。

c)您可以打印一批训练图像吗?

答:创建数据加载器后将给出此问题的答案,因此请等待并继续下面给出的下一个标题。

4.创建一个DataLoader

为将批量加载数据的所有数据集创建一个数据加载器。

batch_size=128train_dl = DataLoader(train_ds, batch_size, shuffle=True, num_workers=4, pin_memory=True)val_dl = DataLoader(test_ds, batch_size, num_workers=4, pin_memory=True)pred_dl = DataLoader(pred_ds, batch_size, num_workers=4, pin_memory=True)

接下来,创建一个数据加载器,该数据加载器可用于打印上述问题中要求的一批图像。

batch_size=128train_dl = DataLoader(train_ds, batch_size, shuffle=True, num_workers=4, pin_memory=True)val_dl = DataLoader(test_ds, batch_size, num_workers=4, pin_memory=True)pred_dl = DataLoader(pred_ds, batch_size, num_workers=4, pin_memory=True)

5.生成类名称

尽管可以通过在此处查看文件夹名称来手动列出类名称,但是作为一种好的做法,应该为此编写代码。

a70fb12ef5455d43b8aadbac23486009.png

6.创建精度函数

定义一个可以计算模型精度的函数。

0f6b606dfdd4f49970611c0bcbd6fcff.png

7.下载预训练的模型

下载选择的任何预训练模型,可以随意选择任何模型。在这里,选择了两个模型VGG和ResNet50进行实验。移动并下载模型。

b6b40607879b9c560bbeb6f8e616228f.png

8.冻结所有图层

下载模型后,可以根据需要训练整个体系结构。一种可能的策略是,可以训练某些层的预训练模型,而有些则不能。在这里选择了这样一种策略,即在对新输入进行模型训练时,不需要对任何现有层进行训练,因此可以通过将模型的每个参数的require_grad设置为False来保持所有层冻结。

4a4663e671dad0210223b36630d76a8a.png

如果require_grad为True,则意味着更新可以计算其导数的参数。

9.添加自己的分类器层

现在,要使用下载的预训练模型作为您自己的分类器,必须对其进行一些更改,因为要预测的类别数量可能与训练模型所依据的类别数量不同。另一个原因是(几乎在每种情况下)都有可能训练模型来检测某些特定类型的事物,但是希望使用该模型来检测不同的事物。

因此模型的一些变化是可以有您自己的分类层,该层将根据要求执行分类。

因此要在预训练模型中添加哪种架构完全取决于您。在这里选择了人们最常用的策略,那就是用自己的分类层替换模型的最后一层。

其他策略是您可以从最后一个删除一些图层,例如已经删除了最后三个图层并添加了自己的分类图层。

为了更好地理解,请参见下文

预训练的VGG模型:

6eb14371b49b3a672fefb1918644fe8a.png

上图中显示了VGG模型的最后两层(avgpool和classsifer)。可以看到,该经过预训练的模型旨在用于对1000个班级进行分类。但是只需要6类分类,因此可以稍微更改此模型。

替换最后一层后的新模型:

6f37fecb3bb1be4de43ec8690051736c.png

已经用自己的分类器层替换了,因为可以看到有6个out_features表示6个输出,但是在预训练模型中还有另一个数字,因为模型经过训练可以对这些分类进行分类。

为什么分类器层内部的某些功能和out_features已更改,为什么?

因此回答这个问题。可以为它们选择任何数字,但请记住,第一个线性层内部的in_features必须为25088,因为它是输出层的数目,不能更改,该数字必须是不变的。

与ResNet50相同:

预训练模型(最后两层)

e4812bb994e928961314cbd52b4adb1c.png

更换最后一层后的新模型

f72fbc6f6ae588455d36885c7817fff8.png

请注意,第一个Linear层中的in_features与2048相同,最后一个Linear层中的out_features为6。

除上面提到的外,任何in_features和out_features均可根据选择进行更改。

10.创建基类

创建一个基类,其中将包含将来要使用的所有有用函数,并且这样做只是为了确保DRY(不要重复自己)的概念,因为这两个模型都将需要该类内部的函数,因此必须如果未在此处实现而违反DRY概念,则分别为每个函数定义这些功能。

class ImageClassificationBase(nn.Module):def training_step(self, batch):
        images, labels = batch
        out = self(images)                  # Generate predictions
        loss = F.cross_entropy(out, labels) # Calculate lossreturn lossdef validation_step(self, batch):
        images, labels = batch
        out = self(images)                    # Generate predictions
        loss = F.cross_entropy(out, labels)   # Calculate loss
        acc = accuracy(out, labels)           # Calculate accuracyreturn {'val_loss': loss.detach(), 'val_acc': acc}def validation_epoch_end(self, outputs):
        batch_losses = [x['val_loss'] for x in outputs]
        epoch_loss = torch.stack(batch_losses).mean()   # Combine losses
        batch_accs = [x['val_acc'] for x in outputs]
        epoch_acc = torch.stack(batch_accs).mean()      # Combine accuraciesreturn {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}def epoch_end(self, epoch, result):
        print("Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}, val_acc: {:.4f}".format(
            epoch, result['train_loss'], result['val_loss'], result['val_acc']))

11.继承基类

通过继承基类为每个模型创建一个类,该基类具有训练任何模型期间所需的所有有用功能。

7d10053ecac2071f20264af00a1d6d4c.png

12.创建继承类的对象

实例化课程

0eb8512e3f1560fed31adeb3f7733e28.png

13.检查装置

创建一个将检查当前设备的功能。如果存在GPU,则选择它,否则选择CPU作为工作设备。

ecc79465810b9ef825c05871061bd148.png

961b04df3180a6b70335fe5ca7cb2a82.png

在这里使用GPU,因此将设备类型显示为CUDA。

14.移至设备

创建一个可以将张量和模型移动到特定设备的函数。

acdcfc05d8cf38855edc64d3b21ceefc.png

15. DeviceDataLoader

创建一个DeviceDataLoader类,该类包装DataLoader以将数据移动到特定设备,然后可以从该设备产生一批数据。

db857c2b8a84ee3ba5c003bcb45bb386.png

2957614291b4be694f9e236880f9e9e0.png

在这里,可以看到张量和两个模型都已发送到当前存在的适当设备。该设备是GPU。

16.评估和拟合函数

定义评估函数,该函数对看不见的数据评估模型的性能,并定义可用于训练模型的拟合函数。

class ImageClassificationBase(nn.Module):def training_step(self, batch):
        images, labels = batch
        out = self(images)                  # Generate predictions
        loss = F.cross_entropy(out, labels) # Calculate lossreturn lossdef validation_step(self, batch):
        images, labels = batch
        out = self(images)                    # Generate predictions
        loss = F.cross_entropy(out, labels)   # Calculate loss
        acc = accuracy(out, labels)           # Calculate accuracyreturn {'val_loss': loss.detach(), 'val_acc': acc}def validation_epoch_end(self, outputs):
        batch_losses = [x['val_loss'] for x in outputs]
        epoch_loss = torch.stack(batch_losses).mean()   # Combine losses
        batch_accs = [x['val_acc'] for x in outputs]
        epoch_acc = torch.stack(batch_accs).mean()      # Combine accuraciesreturn {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}def epoch_end(self, epoch, result):
        print("Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}, val_acc: {:.4f}".format(
            epoch, result['train_loss'], result['val_loss'], result['val_acc']))

17.训练(第一阶段)

训练模型,即VGG的一些时期。

num_epochs = 10opt_func = torch.optim.Adamlr = 0.00001history = fit(num_epochs, lr, model, train_dl, val_dl, opt_func)
Epoch [0], train_loss: 0.8719, val_loss: 0.3769, val_acc: 0.8793Epoch [1], train_loss: 0.4265, val_loss: 0.3104, val_acc: 0.8942Epoch [2], train_loss: 0.3682, val_loss: 0.2884, val_acc: 0.9016Epoch [3], train_loss: 0.3354, val_loss: 0.2819, val_acc: 0.8988Epoch [4], train_loss: 0.3205, val_loss: 0.2704, val_acc: 0.9033Epoch [5], train_loss: 0.2977, val_loss: 0.2722, val_acc: 0.9021Epoch [6], train_loss: 0.2853, val_loss: 0.2629, val_acc: 0.9068Epoch [7], train_loss: 0.2784, val_loss: 0.2625, val_acc: 0.9045Epoch [8], train_loss: 0.2697, val_loss: 0.2623, val_acc: 0.9033Epoch [9], train_loss: 0.2530, val_loss: 0.2629, val_acc: 0.9018

18.训练(第二阶段)

训练更多的时期,并评估该模型。

6a8c475dd9cabff63eaa8edcb36ba19d.png

19.训练(第三阶段)

训练模型2,即ResNet50,以了解一些时期

num_epochs = 10opt_func = torch.optim.Adamlr = 0.00001history = fit(num_epochs, lr, model2, train_dl, val_dl, opt_func)
Epoch [0], train_loss: 1.6437, val_loss: 1.4135, val_acc: 0.7686Epoch [1], train_loss: 1.2088, val_loss: 0.9185, val_acc: 0.8582Epoch [2], train_loss: 0.8531, val_loss: 0.6467, val_acc: 0.8594Epoch [3], train_loss: 0.6709, val_loss: 0.5129, val_acc: 0.8640Epoch [4], train_loss: 0.5773, val_loss: 0.4416, val_acc: 0.8693Epoch [5], train_loss: 0.5215, val_loss: 0.4002, val_acc: 0.8739Epoch [6], train_loss: 0.4796, val_loss: 0.3725, val_acc: 0.8767Epoch [7], train_loss: 0.4582, val_loss: 0.3559, val_acc: 0.8795Epoch [8], train_loss: 0.4391, val_loss: 0.3430, val_acc: 0.8819Epoch [9], train_loss: 0.4262, val_loss: 0.3299, val_acc: 0.8823
num_epochs = 5opt_func = torch.optim.Adamlr = 0.0001history = fit(num_epochs, lr, model2, train_dl, val_dl, opt_func)
Epoch [0], train_loss: 0.4183, val_loss: 0.3225, val_acc: 0.8753Epoch [1], train_loss: 0.3696, val_loss: 0.2960, val_acc: 0.8855Epoch [2], train_loss: 0.3533, val_loss: 0.2977, val_acc: 0.8814Epoch [3], train_loss: 0.3382, val_loss: 0.2970, val_acc: 0.8891Epoch [4], train_loss: 0.3289, val_loss: 0.2849, val_acc: 0.8933

20.训练(第四阶段)

训练更多的时期,并评估该模型。

6a8c475dd9cabff63eaa8edcb36ba19d.png

21.预测单个图像

定义模型可以用来预测单个图像的函数。

def predict_single(input,label, model):
    input = to_device(input,device)
    inputs = input.unsqueeze(0)   # unsqueeze the input i.e. add an additonal dimension
    predictions = model(inputs)
    prediction = predictions[0].detach().cpu()
    print(f"Prediction is {np.argmax(prediction)} of Model whereas given label is {label}")

22.做预测

73a12e53b474f8746645bcbc751afac9.png

可以看出,尽管VGG具有良好的验证准确性(val_acc),但目前VGG给出了错误的预测,而ResNet给出了正确的预测,但不能说它会在每幅图像上正确预测。

因此训练两个模型以获取更多的时期,以便使错误最小化,即可以最大程度地减少val_loss,并且两个模型都可以更准确地执行。

现在,该轮到预测整个pred文件夹/数据集了。

提示:使用pred_dl作为数据加载器可以批量加载pred数据以进行预测。进行练习,并尝试使用集合预测的概念来获得更多正确的预测数。

23.保存模型

很好地训练模型后,保存它以便可以将其用作下一个标题中的未来工作。

34752d45f387ee124c84629c152b37ce.png

24.未来的工作

合并两个模型的预测,进行最终预测,然后使用保存的模型将此项目转换为flask / stream-lit Web应用程序。

资源资源

如果想要笔记本,可以在这里获得。

https://www.kaggle.com/awadhi123/intel-multiclass-classification?scriptVersionId=37760533

56472a5e0585f52741eec83998062a8d.png

推荐阅读

使用PyTorch对音频进行分类

a9551563aab4f08e9ce7074a6f1d46e5.gif
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值