欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

【天池赛事】零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline

发布时间:2024/10/8 编程问答 45 豆豆
生活随笔 收集整理的这篇文章主要介绍了 【天池赛事】零基础入门语义分割-地表建筑物识别 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+Y2XY

其中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的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。