华为美食识别入门比赛-第三名方案分享

比赛简介

    共10类食物,数据集共5000个图片,尺寸大小不一,类别分别均衡。需要自己划分训练集和验证集,用于判分的测试集不可见。
    比赛难度不大,主要难点在于如何减小过拟合,提高模型的泛化能力。代码已在我的github上开源,欢迎star。

源码的github地址

    涨点核心思想:

  • cbam注意力
  • auto_augment
  • cutmix
  • snapshot
  • LabelSmooth

数据探索

    由于数据集比较小,也比较简单,因此主要是统计了一下所有图像的尺寸分布,发现大多数图像的宽高在400~500,因此可以选择将图像输入尺寸调整到这个区间。
    大尺寸的另一个好处是可以显著提高分类准确率。

训练策略

数据集划分

    采用9:1的比例将数据集划分为训练集和验证集,时间和资源允许的情况下,可以进行10折交叉验证。

数据增强

    数据集比较小,因此数据增强是提分的重点方向。除了torch自带的随机裁切和随机擦除以外,涨分的一大技巧是采用auto_augment。数据增强的代码如下:

train_transform = transforms.Compose([
                    transforms.Resize((size+32, size+32)),
                    transforms.RandomChoice([transforms.RandomCrop(size, padding=1, pad_if_needed=True, padding_mode='edge'),
                                                transforms.RandomResizedCrop(size, scale=(resize_scale, 1.0), ratio=(0.8, 1.2))]),
                    transforms.RandomHorizontalFlip(),
                    auto_augment.AutoAugment(dataset='CIFAR'),
                    transforms.ToTensor(),
                    transforms.RandomErasing(p=erasing_prob),
                    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
                ])
模型选择

    采用efficientnet-b5,同时增加了cbam注意力模块,代码如下:

class ChannelAttention(nn.Module):
    def __init__(self, in_planes, ratio=16):
        super(ChannelAttention, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.max_pool = nn.AdaptiveMaxPool2d(1)

        self.fc1   = nn.Conv2d(in_planes, in_planes // 16, 1, bias=False)
        self.relu1 = nn.ReLU()
        self.fc2   = nn.Conv2d(in_planes // 16, in_planes, 1, bias=False)

        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avg_out = self.fc2(self.relu1(self.fc1(self.avg_pool(x))))
        max_out = self.fc2(self.relu1(self.fc1(self.max_pool(x))))
        out = avg_out + max_out
        return self.sigmoid(out)
        
class SpatialAttention(nn.Module):
    def __init__(self, kernel_size=7):
        super(SpatialAttention, self).__init__()

        assert kernel_size in (3, 7), 'kernel size must be 3 or 7'
        padding = 3 if kernel_size == 7 else 1

        self.conv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avg_out = torch.mean(x, dim=1, keepdim=True)
        max_out, _ = torch.max(x, dim=1, keepdim=True)
        x = torch.cat([avg_out, max_out], dim=1)
        x = self.conv1(x)
        return self.sigmoid(x)
优化器和学习率衰减

    优化器采用RAdam,学习率衰减策略采用torch1.4自带的学习率自动重启的余弦衰减。

optimizer = newoptim.RAdam(model.parameters(), lr=lr, weight_decay=weight_decay)
scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=epochs//snap_num)
训练方法

    采用三阶段训练法,每一阶段基于快照集成思想获得5个模型快照,选择acc最高的模型作为当前阶段的训练结果。具体流程如下:

  • Stage 1:图像输入尺寸为400,使用LabelSmooth和cutmix,采用带学习率自动重启的CosineAnnealingWarmRestarts方法,获得5个模型快照,选择val_acc最高的模型,作为Stage 1的训练结果。
  • Stage 2:图像输入尺寸为500,适当调整随机裁切和随机擦除的参数,增加weight_decay,在Stage 1模型的基础上训练获得5个模型快照,选择val_acc最高的模型,作为Stage 2的训练结果。
  • Stage 3:图像输入尺寸为500,关闭cutmix,损失函数采用CrossEntropyLoss,在Stage 2模型的基础上训练获得5个模型快照,选择val_acc最高的模型,作为最终的训练结果。
模型性能

    单模型,验证集上acc为99.4%,提交到modelarts上,测试集的acc为99.2%。
    由于是采用的单模型,因此如果加入模型融合的trick,应该还能够更进一步提高acc。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值