Skip to content

Commit

Permalink
Merge pull request #2 from bcaitech1/Hyeonjin
Browse files Browse the repository at this point in the history
Hyeonjin
  • Loading branch information
HyeonJin-A authored Apr 28, 2021
2 parents eebfcd5 + 8e21d8b commit 5bdd1a4
Show file tree
Hide file tree
Showing 8 changed files with 512 additions and 4 deletions.
23 changes: 23 additions & 0 deletions 4.27 backbone 실험.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## 제출 목록
|모델|배치|시드|time/step|epoch|loss|val_loss|val_mIoU|LB score|
|------|---|---|---|---|---|---|---|---|
|DeepLabV3+, resnext50|16|77|4.8s|10|0.108|0.283|0.463|0.5754|
|DeepLabV3+, seresnext50|16|77|6.8s|16|0.057|0.280|0.476|0.5670|
|DeepLabV3+, resnext50|8|77|2.4s|9|0.098|0.294|0.451|0.5795|

나머지는 너무 오래 걸려서 1~2에폭에서 관둠
<br>
<br>

### 비고
- scheduler: CosineAnnealingWarmup(LR_start=2e-6, LR_max=1e-4, T_0=20, T_up=2)
- batch_size를 8로 주면 마지막(327번째) 배치에서 size관련 에러 발생<br>
ㄴ train_loader에서 drop_last=True로 해결했으나 원인파악을 못함<br>
- V3에 비해 V3+가 파라미터 수도 작고, 러닝타임도 빠름
- smp 모델에서 인코더 부분만 timm 모델로 교체하려 했으나, 동일 모델이라도 weight값이 아예 달랐음 (일단 시도 예정)

<br>

### 배치16 기준 러닝타임(per step)
- PSPNet : eff-b0(3.2s), eff-b3(3.5s), resnet34(6.0s)
- DeepLabV3+ : resnext50(4.8s), eff-b0(6.7s), seresnext50(6.8s), eff-b3(9.5s), effi-b5(15.5s)
31 changes: 31 additions & 0 deletions 4.28 실험 목록.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Overfitting?
|모델|배치|시드|time/step|epoch|loss|val_loss|val_mIoU|LB score|
|------|---|---|---|---|---|---|---|---|
|DeepLabV3+, resnext50|8|77|2.4s|9|0.098|0.294|0.451|0.5795|
|"|"|"|"|12|0.065|0.305|0.451|0.5696|

CrossEntropyLoss 기준 대체적으로 train_loss < 0.1 부터 오버피팅되는 듯함<br>
위 결과는 증명을 위해 제출해본 내역

## Resize(256)
|모델|input_size|배치|시드|time/step|epoch|loss|val_loss|val_mIoU|LB score|
|------|---|---|---|---|---|---|---|---|---|
|DLV3+, resnext50|512|16|77|4.8s|10|0.108|0.283|0.463|0.5754|
|"|256|16|77|0.8s|14|0.097|0.339|0.416|0.5609|
|"|512|8|77|2.4s|14|0.098|0.294|0.451|0.5795|
|"|256|8|77|0.45s|12|0.103|0.343|0.434|0.5640|

다른 전처리 없이 train,test 모두 resize(256)<br>
러닝타임 대폭 감소(1epoch 13분->2분), 다양한 실험 진행하기에 좋을듯

## (1-mIoU)+CE
|모델|loss|배치|시드|time|epoch|loss|val_loss|val_mIoU|LB score|
|------|---|---|---|---|---|---|---|---|---|
|DLV3+, resnext50|CE|8|77|0.45s|12|0.103|0.343|0.434|0.5640|
|"|(1-mIoU)*0.4|"|"|"|11|0.204|0.428|0.431|0.5653|
|"|(1-mIoU)*0.7|"|"|"|14||0.510|0.417|-|
|"|(1-mIoU)*0.2|"|"|"|13||0.394|0.431|-|


## 기타 loss Function
Focal, (1-mIoU)+Focal, weighted(CE) 모두 학습 안됨
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
# Team.분리수거잘하조
# 안현진

***

*made by* [김동우]([email protected]), [김익재]([email protected]), [송민기]([email protected]), [안현진]([email protected]), [최재하]([email protected]), [황정훈](https://github.com/wjdgns7712)
### train_wandb.py
wandb autoML 실행 코드

blah blah~
### dataloader.py
CustomDataset 클래스만 있음

### scheduler.py
custom scheduler : CosineAnnealingWarmUpRestarts() <br>
출처: https://gaussian37.github.io/dl-pytorch-lr_scheduler/

### evaluate.py
metric 및 validation 함수

### utils.py
save, load, submit, calculate_parameter
70 changes: 70 additions & 0 deletions dataloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import os
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import numpy as np
import pandas as pd

# 전처리를 위한 라이브러리
import cv2
from pycocotools.coco import COCO
import torchvision
import torchvision.transforms as transforms
import albumentations as A
from albumentations.pytorch import ToTensorV2


def get_classname(classID, cats):
for i in range(len(cats)):
if cats[i]['id']==classID:
return cats[i]['name']
return "None"

class CustomDataLoader(Dataset):
"""COCO format"""
def __init__(self, data_dir, mode = 'train', transform = None):
super().__init__()
self.mode = mode
self.transform = transform
self.coco = COCO(data_dir)
self.dataset_path = 'input/data/'
self.category_names = ['Backgroud', 'UNKNOWN', 'General trash', 'Paper', 'Paper pack', 'Metal', 'Glass', 'Plastic', 'Styrofoam', 'Plastic bag', 'Battery', 'Clothing']

def __getitem__(self, index: int):
image_id = self.coco.getImgIds(imgIds=index)
image_infos = self.coco.loadImgs(image_id)[0]

images = cv2.imread(self.dataset_path+image_infos['file_name'])
images = cv2.cvtColor(images, cv2.COLOR_BGR2RGB).astype(np.float32)
images /= 255.0

if (self.mode in ('train', 'val')):
ann_ids = self.coco.getAnnIds(imgIds=image_infos['id'])
anns = self.coco.loadAnns(ann_ids)
cat_ids = self.coco.getCatIds()
cats = self.coco.loadCats(cat_ids)

masks = np.zeros((image_infos["height"], image_infos["width"]))
for i in range(len(anns)):
className = get_classname(anns[i]['category_id'], cats)
pixel_value = self.category_names.index(className)
masks = np.maximum(self.coco.annToMask(anns[i])*pixel_value, masks)
masks = masks.astype(np.float32)

if self.transform is not None:
transformed = self.transform(image=images, mask=masks)
images = transformed["image"]
masks = transformed["mask"]

return images, masks #, image_infos

if self.mode == 'test':
if self.transform is not None:
transformed = self.transform(image=images)
images = transformed["image"]

return images #, image_infos


def __len__(self):
return len(self.coco.getImgIds())
83 changes: 83 additions & 0 deletions evaluate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import torch
import numpy as np
from utils import *


def validation(model, data_loader, criterion, device):
model.eval()
with torch.no_grad():
total_loss = 0
cnt = 0
mIoU_list = []
for step, (images, masks) in enumerate(data_loader):
images, masks = images.to(device), masks.long().to(device)

outputs = model(images)
loss = criterion(outputs, masks)
total_loss += loss
cnt += 1

outputs = torch.argmax(outputs.squeeze(), dim=1).detach().cpu().numpy()

mIoU = label_accuracy_score(masks.detach().cpu().numpy(), outputs, n_class=12)
mIoU_list.append(mIoU)

avrg_loss = total_loss / cnt
model.train()
return avrg_loss, np.mean(mIoU_list)


def validation2(model, data_loader, criterion, device, n_class=12):
model.eval()
with torch.no_grad():
total_loss = 0
cnt = 0
hist = np.zeros((n_class, n_class))
for step, (images, masks) in enumerate(data_loader):
images, masks = images.to(device), masks.long().to(device)

outputs = model(images)
loss = criterion(outputs, masks)
total_loss += loss
cnt += 1

outputs = torch.argmax(outputs.squeeze(), dim=1).detach().cpu().numpy()

hist = add_hist(hist, masks.detach().cpu().numpy(), outputs, n_class=n_class)

avrg_loss = total_loss / cnt
mIoU = mIoU_score(hist)
model.train()
return avrg_loss, mIoU


def mIoU_score(hist):
with np.errstate(divide='ignore', invalid='ignore'):
iu = np.diag(hist) / (hist.sum(axis=1) + hist.sum(axis=0) - np.diag(hist))
mean_iu = np.nanmean(iu)
return mean_iu


def add_hist(hist, label_trues, label_preds, n_class):
for lt, lp in zip(label_trues, label_preds):
hist += _fast_hist(lt.flatten(), lp.flatten(), n_class)
return hist

def _fast_hist(label_true, label_pred, n_class):
mask = (label_true >= 0) & (label_true < n_class)
hist = np.bincount(
n_class * label_true[mask].astype(int) +
label_pred[mask], minlength=n_class ** 2).reshape(n_class, n_class)
return hist


def label_accuracy_score(label_trues, label_preds, n_class=12):
hist = np.zeros((n_class, n_class))
for lt, lp in zip(label_trues, label_preds):
hist += _fast_hist(lt.flatten(), lp.flatten(), n_class)
with np.errstate(divide='ignore', invalid='ignore'):
iu = np.diag(hist) / (
hist.sum(axis=1) + hist.sum(axis=0) - np.diag(hist)
)
mean_iu = np.nanmean(iu)
return mean_iu
58 changes: 58 additions & 0 deletions scheduler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import math
from torch.optim.lr_scheduler import _LRScheduler

class CosineAnnealingWarmUpRestarts(_LRScheduler):
def __init__(self, optimizer, T_0, T_mult=1, eta_max=0.1, T_up=0, gamma=1., last_epoch=-1):
if T_0 <= 0 or not isinstance(T_0, int):
raise ValueError("Expected positive integer T_0, but got {}".format(T_0))
if T_mult < 1 or not isinstance(T_mult, int):
raise ValueError("Expected integer T_mult >= 1, but got {}".format(T_mult))
if T_up < 0 or not isinstance(T_up, int):
raise ValueError("Expected positive integer T_up, but got {}".format(T_up))
self.T_0 = T_0
self.T_mult = T_mult
self.base_eta_max = eta_max
self.eta_max = eta_max
self.T_up = T_up
self.T_i = T_0
self.gamma = gamma
self.cycle = 0
self.T_cur = last_epoch
super(CosineAnnealingWarmUpRestarts, self).__init__(optimizer, last_epoch)


def get_lr(self):
if self.T_cur == -1:
return self.base_lrs
elif self.T_cur < self.T_up:
return [(self.eta_max - base_lr)*self.T_cur / self.T_up + base_lr for base_lr in self.base_lrs]
else:
return [base_lr + (self.eta_max - base_lr) * (1 + math.cos(math.pi * (self.T_cur-self.T_up) / (self.T_i - self.T_up))) / 2
for base_lr in self.base_lrs]

def step(self, epoch=None):
if epoch is None:
epoch = self.last_epoch + 1
self.T_cur = self.T_cur + 1
if self.T_cur >= self.T_i:
self.cycle += 1
self.T_cur = self.T_cur - self.T_i
self.T_i = (self.T_i - self.T_up) * self.T_mult + self.T_up
else:
if epoch >= self.T_0:
if self.T_mult == 1:
self.T_cur = epoch % self.T_0
self.cycle = epoch // self.T_0
else:
n = int(math.log((epoch / self.T_0 * (self.T_mult - 1) + 1), self.T_mult))
self.cycle = n
self.T_cur = epoch - self.T_0 * (self.T_mult ** n - 1) / (self.T_mult - 1)
self.T_i = self.T_0 * self.T_mult ** (n)
else:
self.T_i = self.T_0
self.T_cur = epoch

self.eta_max = self.base_eta_max * (self.gamma**self.cycle)
self.last_epoch = math.floor(epoch)
for param_group, lr in zip(self.optimizer.param_groups, self.get_lr()):
param_group['lr'] = lr
Loading

0 comments on commit 5bdd1a4

Please sign in to comment.