前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何在深度学习竞赛中获得前五名

如何在深度学习竞赛中获得前五名

作者头像
代码医生工作室
发布2020-04-20 11:31:49
7060
发布2020-04-20 11:31:49
举报
文章被收录于专栏:相约机器人相约机器人

作者 | Damian Boh

来源 | Medium

编辑 | 代码医生团队

本文中的所有代码都在GitHub Repository上。数据集已经在适当的文件夹中,并且代码可以运行(在安装PyTorch之后)。

https://github.com/bck1990/Identify-Characters-from-Product-Images

去年参加了由CrowdANALYTIX组织的深度学习竞赛,并获得第四名。通过在PyTorch中使用迁移学习来做到这一点。尽管许多读者可能已经熟悉这种技术,但将分享更多认为对模型成功至关重要的内容。不是深度学习的专家,只是依赖于反复试验(和直觉)。

排行榜(在比赛页面的“ LEADERBOARD”标签下)

有关挑战和CrowdANALYTIX的确切详细信息和规则,请访问下面的链接。

CrowdANALYTIX社区:数据专家可以协作和竞争,以构建和优化AI,ML,NLP和深度学习算法

https://www.crowdanalytix.com/contests/identify-characters-from-product-images

挑战—从产品图像中识别角色

来自CrowdANALYTIX的数据集包含与42个不同字符相关的产品图像,例如T恤,包,钥匙扣,手机套等。这些角色范围很广,从愤怒的小鸟和皮卡丘等卡通人物到鸣人和卡卡西等动漫人物,甚至还有达斯·维达和哈利·波特等电影人物。还有约翰·塞纳(大声笑)。任务是根据这些字符对图像进行分类。

训练集包含6694张图像,这些图像按类别分类,如以上文件夹中所示。

跨训练和测试数据集的数据分布如下:

  • 训练:涵盖42个类别的6694张图像(在此处获取)

https://storage.googleapis.com/cax-contests/propensity-modeling/CAX_Characters_Train.zip

  • 测试:3727张图片(在此处获取)

https://storage.googleapis.com/cax-contests/propensity-modeling/CAX_Characters_Test.zip

下面给出了训练集中的一些示例(愤怒的小鸟,火蜥蜴,达斯·维达和小黄人)。

愤怒的小鸟

特色

达斯·维德斯(Darth Vaders)

正如设计师将这些角色注入产品中所采用的许多创造性方法所示,这项任务并不是一件非常简单的事情。能否作为一个人识别出印在左边裤子上的角色?

计划概述

  • 采用PyTorch框架
  • 仔细考虑数据扩充
  • 通过应用预训练卷积神经网络(CNN)进行迁移学习
  • 模型拟合在Google Colab和我的计算机(带有GPU)上完成
  • 网络抓取更多训练图像,并删除不相关的图像

导入库

这些是使用的库。该PyTorch框架获得通过,成为最熟悉它的深度学习,觉得它允许比Keras更大的灵活性,特别是当通过试错法调整很多参数。

代码语言:javascript
复制
# Imports here
import torch
import numpy as np
from torchvision import datasets, transforms, models
import matplotlib.pyplot as plt
%matplotlib inline
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.autograd import Variable
from PIL import Image
from PIL import ImageFile

文件夹结构

随意浏览GitHub存储库以查看文件夹结构。需要train,test和有效的(ation)文件夹。在这些文件夹中的每个文件夹中,都必须使用图像标签作为文件夹名称来对图像进行进一步分类(如先前的屏幕快照所示),PyTorch将自动分配其标签。编写了一个简单的程序,从每个类别中随机选择大约20%的图像,并将其传输到验证文件夹。

注意:测试文件夹中的图像当然没有标签。但是,PyTorch需要将测试文件夹中的图像进一步放置到另一个文件夹中。否则,PyTorch会说无法在测试文件夹中找到文件夹。已经通过在测试文件夹内创建一个“ test2”文件夹并将其所有测试图像转储到那里来解决此问题。

计算机上的文件夹结构

代码语言:javascript
复制
train_dir = 'train_expanded_character'
test_dir = 'test'
valid_dir = 'valid'

不用担心“扩展”文件夹名称。将在最后详细说明。

(关键)数据增强中应考虑的因素

数据增强更多的是一门艺术,而不是一门科学。如果做错了,则会损害准确性。在训练过程中,随机裁剪,更改颜色,亮度,对比度,旋转或翻转图像,以便每次通过数据集时,神经网络都会看到同一图像的不同变化。因此这些变化会扩大数据集并向数据集“添加更多”。这有助于使模型推广到图像的不同变化。重要的是,还没有一种万能的数据增强方法。

关键点:有些人可能认为,包括的数据增强类型越多,模型越好。这是不正确的,并随上下文而变化。

因此,通过仔细查看数据集(可在CrowdANALYTIX和GitHub存储库中找到)来确定要扩充的方面和不扩充的方面至关重要。

https://storage.googleapis.com/cax-contests/propensity-modeling/CAX_Characters_Train.zip

色调

在这种情况下,生成不同色调的随机图像以增强训练数据应该会对准确性产生负面影响,这一点已得到证实。这不足为奇,并确认了颜色是重要的功能(例如,没有蓝色的装饰物等),因此这种增强方法失败了。

水平翻转

水平随机翻转图像以增强训练数据也对准确性产生负面影响。这可能是因为训练图像中的“口袋妖怪”,“汉索罗”等单词本身在不应该出现时会被翻转。

随机旋转

不管设置多少旋转度,这种增强方法都会对精度产生负面影响。查看训练和测试仪,可以看到几乎所有图像都是直立的。因此,不需要旋转训练图像,甚至会降低准确性。

皮卡丘(Pikachu)始终是黄色的(请不要随意使用色相)。图像(即使在测试集中也是如此)大多是直立的(请不要随机旋转)。图像上的文字很重要(请不要水平翻转)。

随机部分

这种增强方法也是不可取的,因为图像的主要特征在预处理期间可能不会被裁剪(例如,在许多图像(例如T恤衫中),可能会裁剪不包括角色本身的随机部分,例如袖子和衣领),使该训练图像无用)。

饱和度和对比度

通过观察图像很难直观地分辨出来,但是这种增强方法并没有提高准确性。

亮度

从图像中可以看出,产品是在不同的光照条件下拍摄的,并且具有不同的亮度阴影(尽管颜色相同,但有些图像明显比其他图像暗)。随机生成不同亮度的图像(上限为0.05)可以使模型推广到不同的光照条件,并将最佳模型的准确性提高到92%以上。

在3张原始训练图像上,小熊维尼的明暗度不同。我们希望模型能够推广到不同的亮度。

与扩充训练数据相关的代码的最后部分如下所示,它利用了PyTorch 中的transforms包。意识到只需要增加亮度!希望这对您将来的数据扩充方法有帮助。

代码语言:javascript
复制
transforms.ColorJitter(brightness=.05, saturation=0, contrast=0)

如果不确定以上内容有什么增强,请尝试一下!

应用转换并加载数据集

除了增强之外,还必须对所有图像应用固定(非随机)变换,因为PyTorch中的预训练模型期望图像的尺寸为3 x 224 x 224(3是RGB像素,224是宽度和高度)。应用调整大小和中心裁切以达到此标准尺寸。

下面是完整的转换代码。请注意不会增加(对验证数据集应用随机亮度转换)。这是为了确保在验证模型时使用完全相同的图像的公平性。

代码语言:javascript
复制
from torchvision import datasets
import torchvision.transforms as transforms
from torch.utils.data.sampler import SubsetRandomSampler
 
 
# convert data to a normalized torch.FloatTensor for validation set
# validation set is preprocessed to be of appropriate shape to fit into the ResNext model
# validation set's preprocessing does not include transfomration for augmentation
valid_transforms = transforms.Compose([transforms.Resize(255),
                                      transforms.CenterCrop(224),
                                      transforms.ToTensor(),
                                      transforms.Normalize([0.485, 0.456, 0.406],
                                      [0.229, 0.224, 0.225])])
 
 
# training set is preprocessed like validation
# but has the additional transformation for random brightness, for augmentation
augmentation_transforms = transforms.Compose([transforms.Resize(255),                                      
                                      transforms.CenterCrop(224),
                                      transforms.ColorJitter(brightness=.05, saturation=0, contrast=0),  #for AUGMENTATION                        
                                      transforms.ToTensor(),
                                      transforms.Normalize([0.485, 0.456, 0.406],
                                      [0.229, 0.224, 0.225])])

从文件夹中加载训练和验证数据集的代码(如下)几乎完全从文档中删除了,不必太担心。加载数据集后,程序将进行一些计算以分批对训练和验证数据集进行采样。常用的批量大小为50。

代码语言:javascript
复制
# load the training and test datasets with the appropriate preprocessing
train_data = datasets.ImageFolder(train_dir, transform=augmentation_transforms)
valid_data = datasets.ImageFolder(valid_dir, transform=valid_transforms)
 
# number of subprocesses to use for data loading
num_workers = 0
# how many samples per batch to load
batch_size = 50
 
# obtain indices for training and validation
num_train = len(train_data)
print("Total number of training images: " + str(num_train))
indices = list(range(num_train))
np.random.shuffle(indices)
train_size = int(np.floor(num_train))
train_idx = indices[:train_size]
 
# define samplers for obtaining training and validation batches
train_sampler = SubsetRandomSampler(train_idx)
valid_sampler = SubsetRandomSampler(list(range(len(valid_data))))
 
# prepare data loaders to be used in training model later (prepare data in batches)
trainloader = torch.utils.data.DataLoader(train_data, batch_size=batch_size,
    sampler=train_sampler, num_workers=num_workers)
 
validloader = torch.utils.data.DataLoader(valid_data, batch_size=batch_size,
    sampler=valid_sampler, num_workers=num_workers)

卷积神经网络(CNN)

CNN是深度学习网络,在图像识别任务中非常成功。如果是CNN的新手,下面将对它进行出色的介绍。

https://adeshpande3.github.io/A-Beginner%27s-Guide-To-Understanding-Convolutional-Neural-Networks/

卷积神经网络的结构(从图像上述文章)

卷积层

总而言之,卷积神经网络由首先使图像通过的卷积层(请参阅上文)组成。在经过训练的CNN模型中,前几层将拾取图像的更底层特征,例如边缘和笔触。接下来的几层将拾取更高级别的特征,例如圆形或笔触组合等形状。随着我们的进一步发展,最后几个卷积层将获得更高级别的特征,例如狗的头。最后几层中的这些功能对于网络尝试分类的内容越来越具体。卷积层的中间是池层,用于对数据进行降采样或减少过拟合的数据丢失层,以及其他层,可以在上面的文章中了解更多。

全连接层

卷积层的输出将通过完全连接的层(有时只是一层)的最终网络,该网络将其映射到与要分类的图像所需类别相对应的确切输出数量。因此该网络也称为分类器。例如,如果最后一个卷积层的输出为2048,而想训练网络以区分狗,猫和仓鼠,则来自卷积层的完全连接层的输入将为2048,输出将为3 (对应于提到的3种动物)。

训练CNN权重

经过训练的CNN可以通过调整每个图层的权重来拾取特征并分类图像。这些权重仅是负责在每个层中执行的计算的数字。每当一批图像通过模型时,模型的预测与图像的实际类别之间的误差就由损失函数计算出来。然后更新权重(通过在模型中反向传播)以最小化损失函数,从而使模型在对图像进行分类时变得更加准确-因为它将在下次图像通过时使用这些新的权重进行计算。

(关键)迁移学习和选择模型

迁移学习方法涉及使用已在ImageNet上的数百万张图像上经过预训练的权重应用卷积神经网络(CNN)。尽管只有6000幅以上的图像需要训练,但是可以利用从数百万张图像中学习到的CNN模型,然后对其进行一些修改以适合我的数据。可以在下面阅读有关迁移学习的更多信息。

https://ruder.io/transfer-learning/

PyTorch网站上的CNN模型列表及其在ImageNet数据集上的性能在此处提供。

https://pytorch.org/docs/stable/torchvision/models.html

首先对每个模型进行初步运行。经过一番尝试后,毫无疑问'resnext101_32x8d'表现最佳,并且在ImageNet上的错误率最低(在其他问题上应用深度学习时,并非总是如此,因此需要反复试验)。ResNeXt是加州大学圣地亚哥分校和Facebook AI Research(FAIR)建立在ResNet上的模型。不会详细介绍该网络的体系结构。

按照惯例,前几个通过其图像被传递模型的层也被称为底部层,而最后几个层是顶部层。

冻结和解冻层

回想一下,模型的底层包含了更多的常规和低级功能,这些功能并非特定于我们的确切任务,而是适用于所有图像识别任务。因此诀窍是在训练字符数据集上的网络时冻结(而不是训练)这些底层的权重。这是因为ImageNet中的数百万张图像已经足够好地训练了这些底层,因此已经可以拾取笔触和边缘之类的东西。

同时想解冻顶层,以便可以使用自己的数据集训练它们的权重。当顶层获取特定于任务的高级功能时,我们希望这些层适应我们的特定任务。例如,我们不希望这些功能拾取飞机的尾巴,而是希望它拾取皮卡丘的机头或火柴等东西。这些与当前的任务更相关。

关于迁移学习的许多教程将告诉您仅冻结完全连接的层。这可能是因为训练一些卷积层(更新权重)也需要大量的计算。但是通过训练一些卷积层,可以进一步提高精度,因此请注意!

看一下代码。

首先,选择模型并将pretrained设置为True。如果将其设置为False,则模型将使用随机(未经训练)的权重进行初始化。如果这是第一次选择模型,PyTorch将自动下载它。

代码语言:javascript
复制
# Select the model
model = models.resnext101_32x8d(pretrained=True)
model

“模型”命令打印出模型的完整体系结构,该体系结构太长了,无法在此处显示。可以在GitHub存储库中的此pdf文件中查看它。

https://github.com/bck1990/Identify-Characters-from-Product-Images/blob/master/model_architecture.pdf

除了尝试和错误之外,没有其他方法可以告诉应该冻结多少顶层。

已冻结的代码由层组(从CONV1到二层)以下的设置requires_grad参数假 ,因为并不需要反向传播过程中梯度更新,同时训练它们的权重。

代码语言:javascript
复制
# Freeze parameters so we don't backprop through them
for param in model.conv1.parameters():
    param.requires_grad = False
for param in model.bn1.parameters():
    param.requires_grad = False
for param in model.relu.parameters():
    param.requires_grad = False
for param in model.maxpool.parameters():
    param.requires_grad = False
for param in model.layer1.parameters():
    param.requires_grad = False
for param in model.layer2.parameters():
    param.requires_grad = False

在model.layer3中,冻结了第1到第18层,如下所示。为了澄清起见,model.layer3实际上是一组名为“ layer3” 的图层。这只是代表ResNeXt的这些层的PyTorch方法。通过反复试验,冻结这些层可提供最佳的模型精度。

代码语言:javascript
复制
layer_freeze_onwards = 18
 
# in model.layer 3, freeze all weights until the 18th layer
# layers above this are all frozen and not trained
for param in model.layer3[0:layer_freeze_onwards].parameters():
    param.requires_grad = False

model.layer3中的其余层以及所有其他剩余层(包括完全连接的层)都不会冻结,因此无法进行训练。如果不确定意思,请查看GitHub存储库中此pdf文件中PyTorch中ResNeXt 101的完整体系结构,其中突出显示的层是受过训练的层。

https://github.com/bck1990/Identify-Characters-from-Product-Images/blob/master/model_architecture.pdf

更换全连接层

滚动到模型的最后几层,将看到以下内容:

(2): Bottleneck(

(conv1): Conv2d(2048, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)

(bn1): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(conv2): Conv2d(2048, 2048, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)

(bn2): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(conv3): Conv2d(2048, 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)层。回想一下,它将卷积层的输出映射到该模型尝试分类的图像类的数量。的输入(in_features)为2048,可以解释的是,输出它上面的卷积/池层的为2048该Fc层的输出(out_features)是1000,对应于1000级的ImageNet数据集。

下面的代码将图层的输出修改为42,对应于拥有的42个不同字符。

代码语言:javascript
复制
# Replace FC layers of pretrained model
model.fc = nn.Sequential(nn.Linear(2048, 42))
print(model)

有时,简单性效果最好。

另外,还发现只需要一个单一的完全连接层为达到最佳效果,试图用一些奇特的层后辍学和RELU。

耗时的?Google Colab进行救援

无疑,这是最耗时的过程。训练了越来越多的未冻结层的每个模型,并查看了精度如何变化。有时,解冻某些层后,精度会下降,但是当解冻更多层时,精度会再次提高,有时会发生相反的情况。承认我无法凭直觉来解释为什么会这样。在这部分上花了几个星期。

在试错过程中,一次运行多个Google Colab会话。

但是,仍然可以通过同时在多个Google Colab Notebooks上运行我的代码来加快该过程。Colab 笔记本在Google的云服务器上执行代码,这意味着可以利用Google硬件(包括GPU和TPU)的功能,而与计算机的功能无关。即使有一台配备GPU的计算机,也需要进行几次Google Colab会议才能同时测试多种模型。请注意不要超出RAM限制,因为每个会话都占用一些RAM。

定义优化器

优化器是用于更新 神经网络权重的算法,以最小化损失函数并提高模型的准确性。该优化选择在其他流行的算法,“ 随机梯度下降(SGD) ”,因为SGD在训练方式过于缓慢而不准确任何显著上升。通过反复试验选择了良好的学习率(LR),也就是步长。步长太大可能导致算法缺少最小点并“越过”最小点,而步长太大可能导致算法花费很长时间训练或陷入局部最小值而不是达到真实全局最低。在这里阅读更多 。LR为0.00005被证明是一个很好的起点。

为此任务使用了最常见的损失函数,即交叉熵损失。请注意,在下面的代码中,如何针对不同神经网络的三个不同部分初始化了三个单独的优化器。

在前几个时期为完全连接(FC)层设置较高的权重。

通过为FC层设置更高的权重,训练FC层的速度比卷积层快得多。使用完全随机的权重初始化FC层,而卷积层已经具有使用ImageNet数据集训练的权重。需要快速粗略地计算出FC层权重,以便在微调卷积层之前将卷积层的输出映射到图像类别。有些人可能不会在前几个时期就完全训练卷积层,因为他们认为,当FC层仍未完成将卷积层的输出映射到图像类别的工作时,“浪费”了对卷积层的训练。注意在这两个时期之后,最终必须仍然训练卷积层,因为这样做的准确性急剧提高。

选择了前一种方法,因为发现它在这种情况下更有效。请注意如下所示在其优化器中为FC层设置了更高的学习率(LR参数)。

代码语言:javascript
复制
# No need for logsoftmax on model output when this is used
criterion = nn.CrossEntropyLoss()
 
# Only train the classifier parameters, feature parameters are frozen
optimizer1 = optim.Adam(model.layer3[layer_freeze_onwards:].parameters(), lr=0.00005)
optimizer2 = optim.Adam(model.layer4.parameters(), lr=0.00005)
optimizer3 = optim.Adam(model.fc.parameters(), lr=0.0005)

下面下一节中的代码训练模型的4个纪元,并在每个纪元中输出结果,几乎完全从文档中删除了该纪元。请注意必须在训练阶段设置model.train(),在评估阶段设置model.eval()。

训练与验证

每个时期的评估是使用前面提到的验证文件夹中的文件夹在验证数据集上完成的。验证集用于评估未经训练的数据集上的模型,以便我们知道模型何时适合训练集。防止过度拟合很重要,因为希望模型能够很好地将可见数据而不是训练集中的信息推广。训练集上模型的准确性将始终提高,但是如果看不见的验证集上的准确性开始下降,则表明模型过拟合。因此应该在验证精度开始下降的地方停止。

还请注意,在代码中,当训练每批新数据时,必须使用optimizer.zero_grad()手动将优化器的梯度设置为零,并使用optimizer.step()更新权重。评估代码相似,除了不涉及优化器(不调整模型在验证图像上的权重,因为不涉及训练)。

代码语言:javascript
复制
# number of epochs to train the model
n_epochs = 4
 
# track change in validation loss, start with maximum loss, i.e. infinity
valid_loss_min = np.Inf
 
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
 
for epoch in range(1, n_epochs+1):
 
    # keep track of training and validation loss
    train_loss = 0.0
    valid_loss = 0.0
    train_accuracy = 0.0
    valid_accuracy = 0.0
 
    ###################
    # train the model #
    ###################
 
    model.train()
    for batch_idx, (data, target) in enumerate(trainloader):
        # move tensors to GPU if CUDA is available
        if train_on_gpu:
            data, target = data.cuda(), target.cuda()
        # clear the gradients of all optimized variables
        optimizer1.zero_grad()
        optimizer2.zero_grad()
        optimizer3.zero_grad()
        # forward pass: compute predicted outputs by passing inputs to the model
        output = model(data)
        # calculate the batch loss
        loss = criterion(output, target)
        # backward pass: compute gradient of the loss with respect to model parameters
        loss.backward()
        # perform a single optimization step (parameter update)
        optimizer1.step()
        optimizer2.step()
        optimizer3.step()
        # update training loss
        train_loss += loss.item()*data.size(0)        
        top_p, top_class = output.topk(1, dim=1)
        correct_tensor = top_class.eq(target.data.view_as(top_class))
        correct = np.squeeze(correct_tensor.numpy()) if not train_on_gpu else np.squeeze(correct_tensor.cpu().numpy())
        train_accuracy += np.mean(correct)
     
          
    ######################    
    # validate the model #
    ######################
    model.eval()
    for batch_idx, (data, target) in enumerate(validloader):
        # move tensors to GPU if CUDA is available
        if train_on_gpu:
            data, target = data.cuda(), target.cuda()
        # forward pass: compute predicted outputs by passing inputs to the model
        output = model(data)
        # calculate the batch loss
        loss = criterion(output, target)
        # update average validation loss
        valid_loss += loss.item()*data.size(0)
        top_p, top_class = output.topk(1, dim=1)
        correct_tensor = top_class.eq(target.data.view_as(top_class))
        correct = np.squeeze(correct_tensor.numpy()) if not train_on_gpu else np.squeeze(correct_tensor.cpu().numpy())
        valid_accuracy += np.mean(correct)
        
        
    # calculate average losses
    train_loss = train_loss/len(trainloader.dataset)
    valid_loss = valid_loss/len(validloader.dataset)
    train_accuracy = train_accuracy/len(trainloader)
    valid_accuracy = valid_accuracy/len(validloader)
        
    # print training/validation statistics
    print('Epoch: {} \tTraining Loss: {:.6f} \t Training Acc: {:.6f} \tValidation Loss: {:.6f} \tValidation Acc: {:.6f}'.format(
        epoch, train_loss, train_accuracy, valid_loss, valid_accuracy))
    
    # check if validation loss has decreased
    # this requires
    if valid_loss <= valid_loss_min:
        print('Validation loss decreased ({:.6f} --> {:.6f}).'.format(
        valid_loss_min,
        valid_loss))
        valid_loss_min = valid_loss

几秒钟后降低学习率。

一个好的策略是在几个周期之后降低学习率(在一个周期上意味着模型遍历所有图像一次),因为接近最小值。不想“越过”并错过这个最低点。在上述4个时期之后,将学习率设置为0.00001,然后再训练另外4个时期,然后将0.000001设置为最后4个时期,直到验证准确性开始降低。请参阅GitHub存储库中的“ final code.ipynb”笔记本以查看流程。

请注意,在更改学习速度和时期数之前,花费了大量时间。必须耐心等待(并打开几个分别在不同参数上运行的Google Colab会话)!

提交前对验证集进行训练

不要忘记这个!在对模型进行最后一轮训练之前,将所有验证图像都迁移回训练文件夹,然后再提交!必须将虚拟图像放入验证文件夹,否则在训练时会遇到错误。

(关键)Webscraping扩展训练图像

该规则的挑战状态:作为一个现实世界的应用程序的问题,希望求解器使用图像数据/功能,如颜色,形状,过筛等,或深学习方法的形象造型。对硬件或GPU的使用,扩展,添加其他训练数据等没有限制。

这不足为奇,因为在现实世界中解决此类问题时,始终会建立在自己的训练数据集上以使其更加丰富。

只需Google提供更多图片即可补充训练数据!

在尝试了多种选择之后,发现在Google上使用图像类别(即皮卡丘(Pikachu),查曼德(Charmander)等名称)后跟“衬衫娃娃”进行搜索可获得最佳效果!已经尝试添加许多词,例如“产品”,“杯子”,“袋子”等,其中一些返回废话。

这对于模型的成功至关重要。在训练模型的某个时间点,意识到我应该扩展训练数据并不得不重新开始整个过程。这一步应该在模型训练之前完成,但是想在详细介绍此单独部分之前给出整个模型的总体视图。在GitHub存储库中,“ expand_train_set_character”文件夹包含从Google提取的图像,而“ train_expanded_character”文件夹包含来自CrowdANALYTIX和Google的训练数据。

在此过程中遇到了一些困难:

  • 有许多损坏的文件必须通过代码进行识别并删除(例如,未以.jpg结尾,无法打开等)。
  • 字符之一是“ ben”。当我自己用Google搜寻“ ben”时,这是一场灾难,因为许多人以“ ben”这个名字出现。因此,“ ben”的训练数据根本没有增加。

下面GiHub存储库中的“ google.ipynb的剪贴图片”文件中提供了实现此目的的脚本的完整代码。基本上,它会列出训练目录中的所有文件夹名称(即图像类别),然后对每个术语“衬衫娃娃”进行谷歌搜索并解析结果,然后将图片分类到“ expand_train_set_character”文件夹中。请访问此页面以获取有关如何使用BeautifulSoup从网络上抓取数据的教程。

代码语言:javascript
复制
from bs4 import BeautifulSoup
import requests
import re
import urllib.request
import os
import http.cookiejar
import json
 
categories = [name for name in os.listdir("./train/")]
print(categories)
 
def get_soup(url,header):
    return BeautifulSoup(urllib.request.urlopen(urllib.request.Request(url,headers=header)),'html.parser')
 
for category in categories:
 
    raw_query = category + " shirt doll" # you can change the query for the image  here
    image_type="ActiOn"
    query= raw_query.split()
    query='+'.join(query)
    url="https://www.google.com/search?q="+query+"&source=lnms&tbm=isch"
    print(url)
    #directory for image
    DIR="expand_train_set_character"
    header={'User-Agent':"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36"
    }
    soup = get_soup(url,header)
 
 
    ActualImages=[]# contains the link for Large original images, type of  image
    for a in soup.find_all("div",{"class":"rg_meta"}):
        link , Type =json.loads(a.text)["ou"]  ,json.loads(a.text)["ity"]
        ActualImages.append((link,Type))
 
    print("For " + raw_query + ", there are a total of " + str(len(ActualImages)) + " images.")
 
    if not os.path.exists(DIR):
                os.mkdir(DIR)
    DIR = os.path.join(DIR, category)
 
    if not os.path.exists(DIR):
                os.mkdir(DIR)
 
    DIR = DIR + "/"
 
    print(DIR)
 
    ###print images
    for i, (img , Type) in enumerate( ActualImages):
        try:
            r = requests.get(img, allow_redirects=True)
            if not Type:
                print("No type.")
            else:
                open(DIR + str(i) + "." + Type, 'wb').write(r.content)
                print("Saving " + img)
            #print(img,Type)
            #print()
 
 
        except Exception as e:
            print("could not load : "+img)
            print(e)

综上所述

本文介绍了以下内容:

  • 在PyTorch中选择了ResNeXt-101-32x8d模型。
  • 在训练期间,从model.layer3及其上方的所有层解冻第18层。
  • 亮度的随机变换上限为0.05,以进行图像增强,因此模型可以推广到不同光照条件下的图像。
  • 通过网络抓取更多产品图片(来自Google)以添加到数据集中。
  • 为全连接层的前几个时期设置较低的学习率。在几个时期后,仔细降低学习率(对于FC和卷积层)。
  • 反复试验!在反复试验和开发数据集的直观感觉上花费了大量时间。

这是参加深度学习竞赛的头几次尝试之一。很高兴最终能够以92.294%的准确率最终排名第四,如本文开头的排行榜所示。希望本文对您有用,并且希望掌握了一些技巧和窍门,可用于将来的深度学习项目!

推荐阅读

Google&nbsp;Colab上的YOLOv3&nbsp;PyTorch

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

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

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

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

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