从0梳理1场CV大赛(Top 3%)!

共 4504字,需浏览 10分钟

 ·

2021-02-02 20:14

↑↑↑关注后"星标"Datawhale
每日干货 & 每月组队学习,不错过
 Datawhale干货 
作者:阿水,北京航空航天大学 ,Datawhale成员

摘要:数据竞赛对于大家理论实践和增加履历帮助比较大,但许多读者反馈不知道如何入门,本文以全国数字生态大赛为背景,梳理了cv数据竞赛的整个流程和改进思路。



继上一篇从0梳理1场数据挖掘赛事后。今天以天池正在进行的数字生态大赛为背景,对CV赛事的解题思路和上分技巧做了详细讲解。
这份Baseline不算复杂,用了1个小时,提交结果后比赛排名100/3171(Top 3%

0.赛事背景

大赛名称:全国数字生态创新大赛-智能算法赛

大赛地址:
https://tianchi.aliyun.com/s/92ccdd364891b9e559a10e1df10079de(或文末
阅读原文

大赛类型:计算机视觉、语义分割

1. 赛题分析

赛题基于不同地形地貌的高分辨率遥感影像资料,要求参赛队伍利用遥感影像智能解译技术识别提取土地覆盖和利用类型,实现生态资产盘点、土地利用动态监测、水环境监测与评估、耕地数量与监测等应用。

地表类型(初赛)包括:

{
  1: "耕地",
  2: "林地",
  3: "草地",
  4: "道路",
  5: "城镇建设用地",
  6: "农村建设用地",
  7: "工业用地",
  8: "构筑物"
  9: "水域"
  10: "裸地"
 }


2. 赛题数据

赛题设置

  • 初赛:利用算法对遥感影像进行10大类地物要素分类,主要考察算法地物分类的准确性;
  • 复赛:利用AI算法对遥感影像进行10大类地物要素地物分类。同时考察算法地物分类的准确性及模型推理效率。

数据介绍

  • 数据简介:数据为覆盖0.8m-2m分辨率的高分系列遥感多光谱影像,成像波段包括R、G、B、Nir波段,数据覆盖地貌包括:山地、丘陵地区、河湖(水库)、平原、城镇等等。感谢浙江大学环境与资源学院为本赛题提供数据支持。
  • 数据规格:4万+张遥感影像及对应地物分类标记样本,影像大小为256*256像素。
    • 初赛:16017张高分遥感影像和标注文件训练集,A榜测试集3000张测试数据,B榜测试集4366张测试数据。
    • 复赛:15904张高分遥感影像和标注文件,6000张测试数据。

训练测试数据说明:影像保存格式为tif文件,包括R、G、B、Nir四个波段,训练测试集影像尺寸均为256 * 256像素。标签数据格式为单通道的png。

评价标准

初赛指标

使用通用指标平均交并比mIoU,计算每个类别的交并比的平均值,仅对算法效果进行评价,具体计算公式为:

其中,是目标类别数,是真值类别为i的像素被分为第j类的个数。

复赛指标

在初赛指标基础上,综合考虑算法效果、模型推理效率。具体如下:在mIoU的技术上引入模型效率评价指标,模型效率得分Fe,衡量模型推理时间得分,Fe的计算方式待复赛阶段公布。

最终成绩计算公式为:


3. 建模方法

首先可以对赛题的数据进行分析,下图所示左边为对应的标注,右边为原图。

从中大致看出,原始图片是比较模糊的,也表明赛题难度比较大。

数据集案例

根据对赛题任务的理解,可以将赛题任务视为语义分割任务,则可以使用语义分割的流程来完成。

输入四通道图片,完成10通道输出。

语义分割FCN模型

具体的流程可以分为:

  1. 构建赛题数据集,数据&标签读取;
  2. 构建分割模型;
  3. 完成模型训练并提交;

赛题数据读取

首先完成赛题读取,这里我们先只用到TIF默认三通道,然后对应的标注映射到0-9范围内。

class TianChiDataset(D.Dataset):
    def __init__(self, paths, transform, test_mode=False):
        self.paths = paths
        self.transform = transform
        self.test_mode = test_mode
        
        self.len = len(paths)
        self.as_tensor = T.Compose([
            T.ToPILImage(),
            T.Resize(IMAGE_SIZE),
            T.ToTensor(),
            T.Normalize([0.625, 0.448, 0.688],                         [0.131, 0.177, 0.101]),
        ])
        
    # get data operation
    def __getitem__(self, index):
        img = cv2.imread(self.paths[index])
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        if not self.test_mode:
            mask = cv2.imread(self.paths[index].replace('.tif''.png')) - 1
            mask = cv2.resize(mask, (IMAGE_SIZE, IMAGE_SIZE))
            augments = self.transform(image=img, mask=mask)
            return self.as_tensor(augments['image']), augments['mask'][:, :, 0].astype(np.int64)
        else:
            return self.as_tensor(img), ''        
    
    def __len__(self):
        return self.len

模型构建

使用FCN或者Unet都是可以的:

# Unet
import segmentation_models_pytorch as smp
model = smp.Unet(
        encoder_name="resnet50",        # choose encoder, e.g. mobilenet_v2 or efficientnet-b7
        encoder_weights="imagenet",     # use `imagenet` pretreined weights for encoder initialization
        in_channels=3,                  # model input channels (1 for grayscale images, 3 for RGB, etc.)
        classes=10,                      # model output channels (number of classes in your dataset)
)

# FCN
model = torchvision.models.segmentation.fcn_resnet50(True)
model.classifier[4] = nn.Conv2d(512, 10, kernel_size=(1, 1), stride=(1, 1))

损失函数&训练

这里由于图片像素有可能属于多个类别,因此我们直接选用CrossEntropy对像素类别进行分类即可。

需注意,这里我们本质是对256*256尺寸每个像素进行类别划分,所以此时的类别10体现在具体的通道上。

optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-3)
loss_fn = nn.CrossEntropyLoss().to(DEVICE);

best_iou = 0
for epoch in range(1, EPOCHES+1):
    losses = []
    start_time = time.time()
    model.train()
    model.to(DEVICE);
    for image, target in tqdm_notebook(loader):
        optimizer.zero_grad()
        output = model(image)
        loss = loss_fn(output, target)
        loss.backward()
        optimizer.step()

预测提交

model.eval()
for idx, name in enumerate(tqdm_notebook(glob.glob('./suichang_round1_test_partA_210120/*.tif')[:])):
    image = cv2.imread(name)
    image = trfm(image)
    with torch.no_grad():
        image = image.to(DEVICE)[None]
        score1 = model(image).cpu().numpy().argmax(0) + 1
        cv2.imwrite('results/' + name.split('/')[-1].replace('.tif''.png'), score_sigmoid)

使用单折模型完成训练,可以到线上34.5的分数,在排行榜100名以内。


4. 改进思路

类别不均衡的情况

由于赛题使用mIOU进行评价,而赛题中不同类别存在不均衡的情况。

  • 可以从损失函数进行尝试,如Lovasz-Softmax Loss、 Focal Loss等;
  • 可以在数据采样角度考虑,对像素占比少的类别进行上采样;
排名榜上的类别IOU

多折模型

可以对赛题数据进行多折划分,然后训练多个模型,然后集成多个模型的结果。

数据通道

R、G、B、Nir四个波段加入训练,带来的精度肯定会比三通道高。这部分操作可能需要修改分割模型输入层的通道个数。

分类损失&分类模型

赛题类别存在不均衡的情况,因此也可以考虑在分割的同时加加入类别损失。

完整代码的notebook可在后台回复阿水下载

希望能帮助你完整实践一场cv赛事。

“整理不易,三连
浏览 71
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报