【天池赛事】零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline
【天池赛事】零基础入门语义分割-地表建筑物识别
Task1:赛题理解与 baseline(3 天)
– 学习主题:理解赛题内容解题流程
– 学习内容:赛题理解、数据读取、比赛 baseline 构建
– 学习成果:比赛 baseline 提交Task2:数据扩增方法(3 天)
– 学习主题:语义分割任务中数据扩增方法
– 学习内容:掌握语义分割任务中数据扩增方法的细节和使用
– 学习成果:数据扩增方法的实践Task3:网络模型结构发展(3 天)
– 学习主题:掌握语义分割模型的发展脉络
– 学习内容: FCN、 Unet、 DeepLab、 SegNet、 PSPNet
– 学习成果:多种网络模型的搭建Task4:评价函数与损失函数(3 天)
– 学习主题:语义分割模型各种评价函数与损失函数
– 学习内容: Dice、 IoU、 BCE、 Focal Loss、 Lovász-Softmax
– 学习成果:评价/损失函数的实践Task5:模型训练与验证(3 天)
– 学习主题:数据划分方法
– 学习内容:三种数据划分方法、模型调参过程
– 学习成果:数据划分具体操作Task6:分割模型模型集成(3 天)
– 学习主题:语义分割模型集成方法
– 学习内容: LookaHead、 SnapShot、 SWA、 TTA
– 学习成果:模型集成思路
Task1:赛题理解与 baseline
- 1 学习目标
- 2 赛题数据
- 3 数据标签
- 4 评价指标
- 5 读取数据
- 6 解题思路
- 7 本章小结
- 8 课后作业
- 9 Baseline
本章将对语义分割赛题进行赛题背景讲解,对赛题数据读取进行说明,并给出解题思路。
- 赛题名称:零基础入门语义分割-地表建筑物识别
- 赛题目标:通过本次赛题可以引导大家熟练掌握语义分割任务的定义,具体的解题流程和相应的模型,并掌握语义分割任务的发展。
- 赛题任务:赛题以计算机视觉为背景,要求选手使用给定的航拍图像训练模型并完成地表建筑物识
别任务。
1 学习目标
• 理解赛题背景和赛题数据
• 完成赛题报名和数据下载,理解赛题的解题思路
2 赛题数据
遥感技术已成为获取地表覆盖信息最为行之有效的手段,遥感技术已经成功应用于地表覆盖检测、植被面积检测和建筑物检测任务。本赛题使用航拍数据,需要参赛选手完成地表建筑物识别,将地表航拍图像素划分为有建筑物和无建筑物两类。
如下图,左边为原始航拍图,右边为对应的建筑物标注。
赛题数据来源(Inria Aerial Image Labeling),并进行拆分处理。数据集报名后可见并可下载。赛题数据为航拍图,需要参赛选手识别图片中的地表建筑具体像素位置。
3 数据标签
赛题为语义分割任务,因此具体的标签为图像像素类别。在赛题数据中像素属于 2 类(无建筑物和有建筑物),因此标签为有建筑物的像素。赛题原始图片为 jpg 格式,标签为 RLE 编码的字符串。
RLE 全称(run-length encoding),翻译为游程编码或行程长度编码,对连续的黑、白像素数以不同的码字进行编码。 RLE 是一种简单的非破坏性资料压缩法,经常用在在语义分割比赛中对标签进行编码。
RLE 与图片之间的转换如下:
# rle编码的具体的读取代码如下:
import numpy as np
import pandas as pd
import cv2# 将图片编码为rle格式
def rle_encode(im):'''im: numpy array, 1 - mask, 0 - backgroundReturns run length as string formated'''pixels = im.flatten(order = 'F')pixels = np.concatenate([[0], pixels, [0]])runs = np.where(pixels[1:] != pixels[:-1])[0] + 1runs[1::2] -= runs[::2]return ' '.join(str(x) for x in runs)# 将rle格式进行解码为图片
def rle_decode(mask_rle, shape=(512, 512)):'''mask_rle: run-length as string formated (start length)shape: (height,width) of array to return Returns numpy array, 1 - mask, 0 - background'''s = mask_rle.split()starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]starts -= 1ends = starts + lengthsimg = np.zeros(shape[0]*shape[1], dtype=np.uint8)for lo, hi in zip(starts, ends):img[lo:hi] = 1return img.reshape(shape, order='F')
4 评价指标
赛题使用 Dice coefficient 来衡量选手结果与真实标签的差异性, Dice coefficient 可以按像素差异性来比较结果的差异性。 Dice coefficient 的具体计算方式如下:
2∗∣X∩Y∣∣X∣+∣Y∣{{2*|X\cap Y|}\over {|X|+|Y|}} ∣X∣+∣Y∣2∗∣X∩Y∣
其中X是预测结果,Y为真实标签的结果。当X与Y完全相同时Dice coefficient为1,排行榜使用所有测试集图片的平均Dice coefficient来衡量,分数值越大越好。
5 读取数据
6 解题思路
由于本次赛题是一个典型的语义分割任务,因此可以直接使用语义分割的模型来完成:
• 步骤 1:使用 FCN 模型模型跑通具体模型训练过程,并对结果进行预测提交;
• 步骤 2:在现有基础上加入数据扩增方法,并划分验证集以监督模型精度;
• 步骤 3:使用更加强大模型结构(如 Unet 和 PSPNet)或尺寸更大的输入完成训练;
• 步骤 4:训练多个模型完成模型集成操作;
7 本章小结
本章主要对赛题背景和主要任务进行讲解,并多对赛题数据和标注读取方式进行介绍,最后列举了赛题解题思路。
8 课后作业
- 理解 RLE 编码过程,并完成赛题数据读取并可视化;
- 统计所有图片整图中没有任何建筑物像素占所有训练集图片的比例;
- 统计所有图片中建筑物像素占所有相似度的比例;
- 统计所有图片中建筑物区域平均区域大小;
9 Baseline
# -*- coding: utf-8 -*-
from google.colab import drive
drive.mount('/content/drive')# !unzip -n /content/drive/MyDrive/SemanticSegmentation/train.zip -d /content/data
# !unzip -n /content/drive/MyDrive/SemanticSegmentation/test_a.zip -d /content/data
# !unzip -n /content/drive/MyDrive/SemanticSegmentation/train_mask.csv.zip -d /content/data
# !cp /content/drive/MyDrive/SemanticSegmentation/test_a_samplesubmit.csv /content/data
# !pip install rasterio# Commented out IPython magic to ensure Python compatibility.
import numpy as np
import pandas as pd
import pathlib, sys, os, random, time
import numba, cv2, gc
from tqdm import tqdm_notebookimport matplotlib.pyplot as plt
# %matplotlib inlineimport warnings
warnings.filterwarnings('ignore')from tqdm.notebook import tqdmimport albumentations as Aimport rasterio
from rasterio.windows import Windowdef rle_encode(im):'''im: numpy array, 1 - mask, 0 - backgroundReturns run length as string formated'''pixels = im.flatten(order = 'F')pixels = np.concatenate([[0], pixels, [0]])runs = np.where(pixels[1:] != pixels[:-1])[0] + 1runs[1::2] -= runs[::2]return ' '.join(str(x) for x in runs)def rle_decode(mask_rle, shape=(512, 512)):'''mask_rle: run-length as string formated (start length)shape: (height,width) of array to return Returns numpy array, 1 - mask, 0 - background'''s = mask_rle.split()starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]starts -= 1ends = starts + lengthsimg = np.zeros(shape[0]*shape[1], dtype=np.uint8)for lo, hi in zip(starts, ends):img[lo:hi] = 1return img.reshape(shape, order='F')import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as Dimport torchvision
from torchvision import transforms as TEPOCHES = 20
BATCH_SIZE = 32
IMAGE_SIZE = 256
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu' trfm = A.Compose([A.Resize(IMAGE_SIZE, IMAGE_SIZE),A.HorizontalFlip(p=0.5),A.VerticalFlip(p=0.5),A.RandomRotate90(),
])class TianChiDataset(D.Dataset):def __init__(self, paths, rles, transform, test_mode=False):self.paths = pathsself.rles = rlesself.transform = transformself.test_mode = test_modeself.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 operationdef __getitem__(self, index):img = cv2.imread(self.paths[index])if not self.test_mode:mask = rle_decode(self.rles[index])augments = self.transform(image=img, mask=mask)return self.as_tensor(augments['image']), augments['mask'][None]else:return self.as_tensor(img), '' def __len__(self):"""Total number of samples in the dataset"""return self.lentrain_mask = pd.read_csv('data/train_mask.csv', sep='\t', names=['name', 'mask'])
train_mask['name'] = train_mask['name'].apply(lambda x: 'data/train/' + x)img = cv2.imread(train_mask['name'].iloc[0])
mask = rle_decode(train_mask['mask'].iloc[0])print(rle_encode(mask) == train_mask['mask'].iloc[0])dataset = TianChiDataset(train_mask['name'].values,train_mask['mask'].fillna('').values,trfm, False
)image, mask = dataset[0]
plt.figure(figsize=(16,8))
plt.subplot(121)
plt.imshow(mask[0], cmap='gray')
plt.subplot(122)
plt.imshow(image[0]);valid_idx, train_idx = [], []
for i in range(len(dataset)):if i % 7 == 0:valid_idx.append(i)
# else:elif i % 7 == 1:train_idx.append(i)train_ds = D.Subset(dataset, train_idx)
valid_ds = D.Subset(dataset, valid_idx)# define training and validation data loaders
loader = D.DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=0)vloader = D.DataLoader(valid_ds, batch_size=BATCH_SIZE, shuffle=False, num_workers=0)def get_model():# model = torchvision.models.segmentation.fcn_resnet50(True)# pth = torch.load("../input/pretrain-coco-weights-pytorch/fcn_resnet50_coco-1167a1af.pth")
# for key in ["aux_classifier.0.weight", "aux_classifier.1.weight", "aux_classifier.1.bias", "aux_classifier.1.running_mean", "aux_classifier.1.running_var", "aux_classifier.1.num_batches_tracked", "aux_classifier.4.weight", "aux_classifier.4.bias"]:
# del pth[key]# model.classifier[4] = nn.Conv2d(512, 1, kernel_size=(1, 1), stride=(1, 1))model = torchvision.models.segmentation.deeplabv3_resnet50(True)model.classifier[4] = nn.Conv2d(256, 1, kernel_size=(1, 1), stride=(1, 1))return model@torch.no_grad()
def validation(model, loader, loss_fn):losses = []model.eval()for image, target in loader:image, target = image.to(DEVICE), target.float().to(DEVICE)output = model(image)['out']loss = loss_fn(output, target)losses.append(loss.item())return np.array(losses).mean()model = get_model()
model.to(DEVICE);optimizer = torch.optim.AdamW(model.parameters(),lr=1e-4, weight_decay=1e-3)class SoftDiceLoss(nn.Module):def __init__(self, smooth=1., dims=(-2,-1)):super(SoftDiceLoss, self).__init__()self.smooth = smoothself.dims = dimsdef forward(self, x, y):tp = (x * y).sum(self.dims)fp = (x * (1 - y)).sum(self.dims)fn = ((1 - x) * y).sum(self.dims)dc = (2 * tp + self.smooth) / (2 * tp + fp + fn + self.smooth)dc = dc.mean()return 1 - dcbce_fn = nn.BCEWithLogitsLoss()
dice_fn = SoftDiceLoss()def loss_fn(y_pred, y_true):bce = bce_fn(y_pred, y_true)dice = dice_fn(y_pred.sigmoid(), y_true)return 0.8*bce+ 0.2*diceheader = r'''Train | Valid
Epoch | Loss | Loss | Time, m
'''
# Epoch metrics time
raw_line = '{:6d}' + '\u2502{:7.3f}'*2 + '\u2502{:6.2f}'
print(header)EPOCHES = 60
best_loss = 10
for epoch in range(1, EPOCHES+1):losses = []start_time = time.time()model.train()for image, target in tqdm_notebook(loader):image, target = image.to(DEVICE), target.float().to(DEVICE)optimizer.zero_grad()output = model(image)['out']loss = loss_fn(output, target)loss.backward()optimizer.step()losses.append(loss.item())# print(loss.item())vloss = validation(model, vloader, loss_fn)print(raw_line.format(epoch, np.array(losses).mean(), vloss,(time.time()-start_time)/60**1))losses = []if vloss < best_loss:best_loss = vlosstorch.save(model.state_dict(), 'model_best.pth')torch.save(model.state_dict(), '/content/drive/MyDrive/SemanticSegmentation/model_best.pth')trfm = T.Compose([T.ToPILImage(),T.Resize(IMAGE_SIZE),T.ToTensor(),T.Normalize([0.625, 0.448, 0.688],[0.131, 0.177, 0.101]),
])subm = []# model.load_state_dict(torch.load("/content/drive/MyDrive/SemanticSegmentation/model_best.pth"))
model.load_state_dict(torch.load("./model_best.pth"))
model.eval()test_mask = pd.read_csv('data/test_a_samplesubmit.csv', sep='\t', names=['name', 'mask'])
test_mask['name'] = test_mask['name'].apply(lambda x: 'data/test_a/' + x)for idx, name in enumerate(tqdm_notebook(test_mask['name'].iloc[:])):image = cv2.imread(name)image = trfm(image)with torch.no_grad():image = image.to(DEVICE)[None]score = model(image)['out'][0][0]score_sigmoid = score.sigmoid().cpu().numpy()score_sigmoid = (score_sigmoid > 0.5).astype(np.uint8)score_sigmoid = cv2.resize(score_sigmoid, (512, 512))# breaksubm.append([name.split('/')[-1], rle_encode(score_sigmoid)])subm = pd.DataFrame(subm)
subm.to_csv('./tmp.csv', index=None, header=None, sep='\t')
subm.to_csv('/content/drive/MyDrive/SemanticSegmentation/tmp.csv', index=None, header=None, sep='\t')plt.figure(figsize=(16,8))
plt.subplot(121)
plt.imshow(rle_decode(subm[1].fillna('').iloc[0]), cmap='gray')
plt.subplot(122)
plt.imshow(cv2.imread('data/test_a/' + subm[0].iloc[0]));
【天池赛事】零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline相关推荐
- 【天池赛事】零基础入门语义分割-地表建筑物识别 Task6:分割模型模型集成
[天池赛事]零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline(3 天) – 学习主题:理解赛题内容解题流程 – 学习内容:赛题理解.数据读取.比赛 baseline 构建 ...
- 【天池赛事】零基础入门语义分割-地表建筑物识别 Task5:模型训练与验证
[天池赛事]零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline(3 天) – 学习主题:理解赛题内容解题流程 – 学习内容:赛题理解.数据读取.比赛 baseline 构建 ...
- 【天池赛事】零基础入门语义分割-地表建筑物识别 Task4:评价函数与损失函数
[天池赛事]零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline(3 天) – 学习主题:理解赛题内容解题流程 – 学习内容:赛题理解.数据读取.比赛 baseline 构建 ...
- 【天池赛事】零基础入门语义分割-地表建筑物识别 Task3:网络模型结构发展
[天池赛事]零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline(3 天) – 学习主题:理解赛题内容解题流程 – 学习内容:赛题理解.数据读取.比赛 baseline 构建 ...
- 【天池赛事】零基础入门语义分割-地表建筑物识别 Task2:数据扩增方法
[天池赛事]零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline(3 天) – 学习主题:理解赛题内容解题流程 – 学习内容:赛题理解.数据读取.比赛 baseline 构建 ...
- 【天池赛事】零基础入门语义分割-地表建筑物识别
https://tianchi.aliyun.com/competition/entrance/531872/introduction [天池赛事]零基础入门语义分割-地表建筑物识别:第一章 赛题及b ...
- 天池赛题解析:零基础入门语义分割-地表建筑物识别-CV语义分割实战(附部分代码)
赛题内容 赛题背景 赛题以计算机视觉为背景,要求选手使用给定的航拍图像训练模型并完成地表建筑物识别任务.为更好的引导大家入门,我们为本赛题定制了学习方案和学习任务,具体包括语义分割的模型和具体的应用案 ...
- 零基础入门语义分割-地表建筑物识别 Task2 数据扩增 -学习笔记
先给出task1的链接:task1-赛题理解 这一节进行数据扩增的试验 读取图片: train_mask = pd.read_csv('./data/train_mask.csv', sep='\t' ...
- 地表建筑物识别——Task01赛题理解
前言:这次参加Datawhale与天池联合发起的零基础入门语义分割之地表建筑物识别挑战赛.自己在大四上(2018下半年)也简单接触过语义分割,当时是基于FCN做CT图像乳腺肿瘤的分割.现在参加这个入门 ...
最新文章
- 数据库中范式的理解1NF、2NF、3NF
- 前端三十二:超链接(a标签)
- 辽师大计算机科学与技术专业怎么样,性价比很高的大学,辽师大的优势专业分析!家长请收藏...
- linux 截取后缀名,Shell 截取文件名和后缀
- 以太坊solidity编程常见错误(不定期更新)
- 6174问题 --ACM解决方法
- IDEA创建SpringBoot
- 启动不起来_电脑启动不起来该怎么办
- Qt-Qt Creator的下载、安装与配置(Windows)
- DPDK AF_XDP
- Qt优秀开源项目之十四:SortFilterProxyModel
- 如何判断本地(路由器)分配的IP是否是公网IP?
- Jasper实现报表(Java)
- img取消无图片时的默认边框
- 数据结构——图的邻接表实现
- Vue项目antdv中scopedSlots的customRender和customRender函数冲突
- ASPICE SWE3之——C代码生成软件详细设计2 注释格式
- 【JZOJ 5498】 大佬的难题
- element ui input限制输入6位数字(短信验证码)
- android+360+手表,360智能手表