前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Pytorch进行多类图像分类

使用Pytorch进行多类图像分类

作者头像
代码医生工作室
发布2020-07-09 17:17:25
4.4K0
发布2020-07-09 17:17:25
举报
文章被收录于专栏:相约机器人

作者 | 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.导入库

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

代码语言:javascript
复制
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.图像文件夹到数据集

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

代码语言:javascript
复制
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))



])
代码语言:javascript
复制
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)数据集中有多少张图片?

答:

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

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

答:

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

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

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

4.创建一个DataLoader

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

代码语言:javascript
复制
batch_size=128

train_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)

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

代码语言:javascript
复制
batch_size=128

train_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.生成类名称

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

6.创建精度函数

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

7.下载预训练的模型

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

8.冻结所有图层

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

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

9.添加自己的分类器层

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

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

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

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

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

预训练的VGG模型:

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

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

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

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

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

与ResNet50相同:

预训练模型(最后两层)

更换最后一层后的新模型

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

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

10.创建基类

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

代码语言:javascript
复制
class ImageClassificationBase(nn.Module):

    def training_step(self, batch):

        images, labels = batch

        out = self(images)                  # Generate predictions

        loss = F.cross_entropy(out, labels) # Calculate loss

        return loss



    def 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 accuracy

        return {'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 accuracies

        return {'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.继承基类

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

12.创建继承类的对象

实例化课程

13.检查装置

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

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

14.移至设备

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

15. DeviceDataLoader

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

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

16.评估和拟合函数

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

代码语言:javascript
复制
class ImageClassificationBase(nn.Module):

    def training_step(self, batch):

        images, labels = batch

        out = self(images)                  # Generate predictions

        loss = F.cross_entropy(out, labels) # Calculate loss

        return loss



    def 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 accuracy

        return {'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 accuracies

        return {'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的一些时期。

代码语言:javascript
复制
num_epochs = 10

opt_func = torch.optim.Adam

lr = 0.00001

history = fit(num_epochs, lr, model, train_dl, val_dl, opt_func)
代码语言:javascript
复制
Epoch [0], train_loss: 0.8719, val_loss: 0.3769, val_acc: 0.8793

Epoch [1], train_loss: 0.4265, val_loss: 0.3104, val_acc: 0.8942

Epoch [2], train_loss: 0.3682, val_loss: 0.2884, val_acc: 0.9016

Epoch [3], train_loss: 0.3354, val_loss: 0.2819, val_acc: 0.8988

Epoch [4], train_loss: 0.3205, val_loss: 0.2704, val_acc: 0.9033

Epoch [5], train_loss: 0.2977, val_loss: 0.2722, val_acc: 0.9021

Epoch [6], train_loss: 0.2853, val_loss: 0.2629, val_acc: 0.9068

Epoch [7], train_loss: 0.2784, val_loss: 0.2625, val_acc: 0.9045

Epoch [8], train_loss: 0.2697, val_loss: 0.2623, val_acc: 0.9033

Epoch [9], train_loss: 0.2530, val_loss: 0.2629, val_acc: 0.9018

18.训练(第二阶段)

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

19.训练(第三阶段)

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

代码语言:javascript
复制
num_epochs = 10

opt_func = torch.optim.Adam

lr = 0.00001

history = fit(num_epochs, lr, model2, train_dl, val_dl, opt_func)
代码语言:javascript
复制
Epoch [0], train_loss: 1.6437, val_loss: 1.4135, val_acc: 0.7686

Epoch [1], train_loss: 1.2088, val_loss: 0.9185, val_acc: 0.8582

Epoch [2], train_loss: 0.8531, val_loss: 0.6467, val_acc: 0.8594

Epoch [3], train_loss: 0.6709, val_loss: 0.5129, val_acc: 0.8640

Epoch [4], train_loss: 0.5773, val_loss: 0.4416, val_acc: 0.8693

Epoch [5], train_loss: 0.5215, val_loss: 0.4002, val_acc: 0.8739

Epoch [6], train_loss: 0.4796, val_loss: 0.3725, val_acc: 0.8767

Epoch [7], train_loss: 0.4582, val_loss: 0.3559, val_acc: 0.8795

Epoch [8], train_loss: 0.4391, val_loss: 0.3430, val_acc: 0.8819

Epoch [9], train_loss: 0.4262, val_loss: 0.3299, val_acc: 0.8823
代码语言:javascript
复制
num_epochs = 5

opt_func = torch.optim.Adam

lr = 0.0001

history = fit(num_epochs, lr, model2, train_dl, val_dl, opt_func)
代码语言:javascript
复制
Epoch [0], train_loss: 0.4183, val_loss: 0.3225, val_acc: 0.8753

Epoch [1], train_loss: 0.3696, val_loss: 0.2960, val_acc: 0.8855

Epoch [2], train_loss: 0.3533, val_loss: 0.2977, val_acc: 0.8814

Epoch [3], train_loss: 0.3382, val_loss: 0.2970, val_acc: 0.8891

Epoch [4], train_loss: 0.3289, val_loss: 0.2849, val_acc: 0.8933

20.训练(第四阶段)

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

21.预测单个图像

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

代码语言:javascript
复制
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.做预测

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

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

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

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

23.保存模型

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

24.未来的工作

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

资源资源

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档