专栏首页相约机器人使用Pytorch和转移学习进行端到端多类图像分类

使用Pytorch和转移学习进行端到端多类图像分类

数据探索

将从Kaggle 的Boat数据集开始,以了解多类图像分类问题。该数据集包含约1,500种不同类型的船的图片:浮标,游轮,渡船,货船,吊船,充气船,皮划艇,纸船和帆船。目标是创建一个模型,以查看船只图像并将其分类为正确的类别。

这是来自数据集的图像样本:

以下是类别计数:

由于货船,充气船和船只类别没有很多图像,因此在训练模型时将删除这些类别。

创建所需的目录结构

在训练深度学习模型之前,需要为图像创建所需的目录结构。现在数据目录结构如下所示:

images    sailboat    kayak    .    .

需要图像是在三个文件夹:train,val和test。然后将在train数据集中的图像上训练模型,在数据集中进行验证val,最后对test数据集进行测试。

data    train        sailboat        kayak        .        .    val        sailboat        kayak        .        .    test        sailboat        kayak        .        .

数据可能采用不同的格式,除了常用的库之外,glob.globand os.system函数也非常有用。在这里,可以找到完整的数据准备代码。现在快速看一下一些在进行数据准备时发现有用的未使用的库。

什么是glob.glob?

简而言之使用glob,可以使用正则表达式获取目录中文件或文件夹的名称。例如可以执行以下操作:

from glob import globcategories = glob(“images/*”)print(categories)------------------------------------------------------------------['images/kayak', 'images/boats', 'images/gondola', 'images/sailboat', 'images/inflatable boat', 'images/paper boat', 'images/buoy', 'images/cruise ship', 'images/freight boat', 'images/ferry boat']

什么是os.system?

os.system是os库中的一个函数,可让在python本身中运行任何命令行函数。通常使用它来运行Linux函数,但也可以用来在python中运行R脚本,如下所示。例如,在从pandas数据框中获取信息后,在数据准备中使用它将文件从一个目录复制到另一个目录。也使用f字符串格式。

import osfor i,row in fulldf.iterrows():    # Boat category    cat = row['category']    # section is train,val or test    section = row['type']    # input filepath to copy    ipath = row['filepath']    # output filepath to paste    opath = ipath.replace(f"images/",f"data/{section}/")    # running the cp command    os.system(f"cp '{ipath}' '{opath}'")

现在,已将数据存储在所需的文件夹结构中,可以继续进行更令人兴奋的部分。

数据预处理

变身

1. Imagenet预处理

为了将图像与在Imagenet数据集上训练的网络一起使用,需要以与Imagenet网络相同的方式预处理图像。为此需要将图像重新缩放为224×224,并按照Imagenet标准对其进行归一化。可以使用torchvision transforms库来做到这一点。在这里,采用CenterCrop224×224的a并根据Imagenet标准进行归一化。以下定义的操作顺序发生。

transforms.Compose([        transforms.CenterCrop(size=224),        transforms.ToTensor(),        transforms.Normalize([0.485, 0.456, 0.406],                             [0.229, 0.224, 0.225])    ])

2.数据扩充

可以为数据扩充做更多的预处理。神经网络可以更好地处理大量数据。数据扩充是在训练时使用的一种策略,用于增加拥有的数据量。

例如,可以水平翻转船的图像,但它仍然是船。或者可以随机裁剪图像或添加颜色抖动。这是使用过的图像变换字典,它既适用于Imagenet预处理也适用于增强。不对测试数据和验证数据应用水平翻转或其他数据增强转换,因为不想对增强图像进行预测。

# Image transformationsimage_transforms = {    # Train uses data augmentation    'train':    transforms.Compose([        transforms.RandomResizedCrop(size=256, scale=(0.8, 1.0)),        transforms.RandomRotation(degrees=15),        transforms.ColorJitter(),        transforms.RandomHorizontalFlip(),        transforms.CenterCrop(size=224), # Image net standards        transforms.ToTensor(),        transforms.Normalize([0.485, 0.456, 0.406],                             [0.229, 0.224, 0.225]) # Imagenet standards    ]),    # Validation does not use augmentation    'valid':    transforms.Compose([        transforms.Resize(size=256),        transforms.CenterCrop(size=224),        transforms.ToTensor(),        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])    ]),         # Test does not use augmentation    'test':    transforms.Compose([        transforms.Resize(size=256),        transforms.CenterCrop(size=224),        transforms.ToTensor(),        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])    ]),}

这是应用于训练数据集中图像的训练变换的示例。不仅可以从单个图像中获得大量不同的图像,而且还可以帮助网络针对对象的方向保持不变。

ex_img = Image.open('/home/rahul/projects/compvisblog/data/train/cruise ship/cruise-ship-oasis-of-the-seas-boat-water-482183.jpg')t = image_transforms['train']plt.figure(figsize=(24, 24))for i in range(16):    ax = plt.subplot(4, 4, i + 1)    _ = imshow_tensor(t(ex_img), ax=ax)plt.tight_layout()

数据加载器

下一步是向PyTorch提供训练,验证和测试数据集位置。可以通过使用PyTorch数据集和DataLoader类来做到这一点。如果数据位于所需的目录结构中,则这部分代码将基本保持不变。

# Datasets from folderstraindir = "data/train"validdir = "data/val"testdir = "data/test"data = {    'train':    datasets.ImageFolder(root=traindir, transform=image_transforms['train']),    'valid':    datasets.ImageFolder(root=validdir, transform=image_transforms['valid']),    'test':    datasets.ImageFolder(root=testdir, transform=image_transforms['test'])}# Dataloader iterators, make sure to shuffledataloaders = {    'train': DataLoader(data['train'], batch_size=batch_size, shuffle=True,num_workers=10),    'val': DataLoader(data['valid'], batch_size=batch_size, shuffle=True,num_workers=10),    'test': DataLoader(data['test'], batch_size=batch_size, shuffle=True,num_workers=10)}

这些数据加载器帮助遍历数据集。例如将在模型训练中使用以下数据加载器。(batch_size, color_channels, height, width)当目标为形状时(batch_size),data变量将包含形式的数据,并保存标签信息。

train_loader = dataloaders['train']for ii, (data, target) in enumerate(train_loader):

创建模型

1.使用预先训练的模型创建模型

将在数据集上使用resnet50,可以根据自己的喜好有效使用其他模型。

from torchvision import modelsmodel = models.resnet50(pretrained=True)

先冻结模型权重,因为不想更改resnet50模型的权重。

# Freeze model weightsfor param in model.parameters():    param.requires_grad = False

接下来需要做的是用自定义分类器替换模型中的线性分类层。发现要这样做,最好先查看模型结构以确定最终的线性层。可以简单地通过打印模型对象来做到这一点:

print(model)------------------------------------------------------------------ResNet(  (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)  (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)  (relu): ReLU(inplace=True)  .  .  .  .(conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)      (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)      (relu): ReLU(inplace=True)    )  )(avgpool): AdaptiveAvgPool2d(output_size=(1, 1))  (fc): Linear(in_features=2048, out_features=1000, bias=True))

在这里,发现从卷积层获取输入的最终线性层被命名为fc。

现在可以fc使用自定义神经网络简单地替换该层。该神经网络从上一层获取输入,fc并给出shape的对数softmax输出(batch_size x n_classes)。

n_inputs = model.fc.in_featuresmodel.fc = nn.Sequential(                      nn.Linear(n_inputs, 256),                      nn.ReLU(),                      nn.Dropout(0.4),                      nn.Linear(256, n_classes),                      nn.LogSoftmax(dim=1))

请注意,默认情况下,现在添加的新图层是完全可训练的。

2.在GPU上加载模型

可以使用PyTorch的DataParallel使用单个GPU或多个GPU(如果有)。这是可以用来检测GPU以及将GPU加载模型的GPU数量。现在正在双Titan RTX GPU上训练模型。

# Whether to train on a gputrain_on_gpu = cuda.is_available()print(f'Train on gpu: {train_on_gpu}')# Number of gpusif train_on_gpu:    gpu_count = cuda.device_count()    print(f'{gpu_count} gpus detected.')    if gpu_count > 1:        multi_gpu = True    else:        multi_gpu = Falseif train_on_gpu:    model = model.to('cuda')if multi_gpu:    model = nn.DataParallel(model)

3.定义标准和优化器

训练任何模型时要注意的最重要的事情之一是损失函数的选择和所使用的优化器。这里要使用分类交叉熵,因为有一个多类分类问题,而Adam最优化器是最常用的优化器。但是由于在模型的输出上应用了LogSoftmax操作,因此将使用NLL损失。

from torch import optimcriteration = nn.NLLLoss()optimizer = optim.Adam(model.parameters())

4.训练模型

在下面,将找到用于训练模型的完整代码。它本身看起来可能很大,但实际上正在做的事情如下:

  • 开始运行纪元。在每个时代
  • 将模型模式设置为使用训练model.train()。
  • 使用训练数据加载器循环遍历数据。
  • 使用以下data, target = data.cuda(), target.cuda()命令将数据加载到GPU
  • 使用以下命令将优化器中的现有渐变设置为零 optimizer.zero_grad()
  • 使用以下命令运行批处理的前向传递 output = model(data)
  • 使用计算损失 loss = criterion(output, target)
  • 使用网络反向传播损失 loss.backward()
  • 采取优化程序步骤,使用以下方法更改整个网络的权重 optimizer.step()
  • 训练循环中的所有其他步骤只是为了保持历史记录并计算准确性。
  • 使用将模型模式设置为eval model.eval()。
  • 使用valid_loader并计算valid_loss和获得验证数据的预测valid_acc
  • 每次打印验证损失和验证准确性结果print_every。
  • 根据验证损失保存最佳模型。
  • 提前停止:如果交叉验证损失没有因max_epochs_stop停止训练而改善,并以最小的验证损失加载最佳可用模型。

这是运行上述代码的输出。仅显示最后几个时期。验证准确性在第一个时期开始于〜55%,最终验证准确性为〜90%。

这是显示损耗和准确性指标的训练曲线:

训练曲线

推论和模型结果

在使用模型时,希望以各种不同的方式获得结果。首先需要测试精度和混淆矩阵。用于创建这些结果的所有代码都在代码笔记本中。

1.测试结果

测试模型的整体准确性为:

Overall Accuracy: 88.65 %

这是测试数据集结果的混淆矩阵:

还可以查看类别精度。还添加了训练数量以从新的角度查看结果。

2.可视化单个图像的预测

出于部署目的,它有助于获得单个图像的预测。可以从笔记本中获取代码。

3.可视化类别的预测

还可以看到按类别的结果,以进行调试和演示。

4.测试时间增加的测试结果

还可以增加测试时间来提高测试准确性。在这里正在使用新的测试数据加载器和转换:

# Image transformationstta_random_image_transforms = transforms.Compose([        transforms.RandomResizedCrop(size=256, scale=(0.8, 1.0)),        transforms.RandomRotation(degrees=15),        transforms.ColorJitter(),        transforms.RandomHorizontalFlip(),        transforms.CenterCrop(size=224), # Image net standards        transforms.ToTensor(),        transforms.Normalize([0.485, 0.456, 0.406],                             [0.229, 0.224, 0.225]) # Imagenet standards    ])# Datasets from foldersttadata = {    'test':    datasets.ImageFolder(root=testdir, transform=tta_random_image_transforms)}# Dataloader iteratorsttadataloader = {    'test': DataLoader(ttadata['test'], batch_size=512, shuffle=False,num_workers=10)}

然后,可以使用以下函数获得测试集上的预测:

在上面的函数中,将tta_random_image_transforms5次应用于每个图像,然后再进行预测。最终预测是所有五个预测的平均值。当在整个测试数据集上使用TTA时,注意到准确性提高了大约1%。

TTA Accuracy: 89.71%

此外,以下是与正常结果类别相比的TTA结果:

在这个小的数据集中,TTA似乎并没有增加太多价值,但是注意到它为大型数据集增加了价值。

结论

在本文中,讨论了使用PyTorch进行多类图像分类项目的端到端管道。致力于创建一些现成的代码,以使用迁移学习来训练模型,可视化结果,使用测试时间增加,并获得单个图像的预测,以便在需要时使用Streamlit之类的任何工具部署模型。

本文分享自微信公众号 - 相约机器人(xiangyuejiqiren),作者:代码医生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 摸不到视频里的犀牛,却能在它身上画画,特效紧贴凹凸表面,动也不掉:全靠免费AE插件

    毕竟,三点 (不共线) 可以确定平面,曲面就需要标记更多点。标出的点越多,近似的曲面就越精确。

    代码医生工作室
  • 基于声音的鸟类物种检测

    拥有Python经验的女性数据科学家,博士候选人,鸟类学家,数据分析师和软件工程师共同参与了一系列为期两周的冲刺,共同致力于该项目。

    代码医生工作室
  • 深度学习检测疟疾

    欢迎来到AI for Social Good系列,将重点关注人工智能(AI)与流行的开源工具,技术和框架如何用于发展和改善社会的不同方面。“健康就是财富”也许是...

    代码医生工作室
  • Pytorch小技巧-数据增强(下)

    局部增强,一般裁剪操作是配合图片旋转操作共同进行,先裁减掉一部分,再进行旋转即可。

    用户6719124
  • 在Windows 2008 R2环境安装Flowportal.Net BPM的注意事项

    由于Windows 2008 R2是64位的操作系统,我们的Flowportal.Net BPM是32位的应用程序,所以安装的时候有一些注意事项。当然了这里我选...

    崔文远TroyCui
  • 浅析JDK中ServiceLoader的源码

    紧接着上一篇《通过源码浅析JDK中的资源加载》,ServiceLoader是SPI(Service Provider Interface)中的服务类加载的核心类...

    Throwable
  • TensorFlow深度学习笔记 循环神经网络实践

    加载数据 使用text8作为训练的文本数据集 text8中只包含27种字符:小写的从a到z,以及空格符。如果把它打出来,读起来就像是去掉了所有标点的wikip...

    梦里茶
  • DevOps研发模式下「产品质量度量」方案实践

    这些问题不能靠感觉、拍大脑,而是需要客观的数据来反映。质量度量指标就是用一组数据来客观衡量产品研发环节的各方面情况,作为评审和决策的依据。

    测试开发技术
  • DevOps研发模式下「产品质量度量」方案实践

    这些问题不能靠感觉、拍大脑,而是需要客观的数据来反映。质量度量指标就是用一组数据来客观衡量产品研发环节的各方面情况,作为评审和决策的依据。

    测试开发技术
  • oracle12c rac搭建时主机名无效问题的解决

    在windows 2012 64位企业版上搭建oracle 12c  rac集群,hosts文件如下: #add for rac config 11.14.72...

    孙杰

扫码关注云+社区

领取腾讯云代金券