일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- dtype
- Numpy data I/O
- Python
- groupby
- linalg
- 부스트캠프 AI테크
- Array operations
- 카테고리분포 MLE
- unstack
- BOXPLOT
- ndarray
- scatter
- 딥러닝
- namedtuple
- Operation function
- type hints
- seaborn
- Python 특징
- 최대가능도 추정법
- Numpy
- Comparisons
- 정규분포 MLE
- pivot table
- python 문법
- VSCode
- 가능도
- subplot
- boolean & fancy index
- 표집분포
- Python 유래
- Today
- Total
또르르's 개발 Story
[Stage 1 - 07] Ensemble 하기 본문
1️⃣ Goal
[BaseLine 작성] (추가 : 3/29, 기간 : 3/29 ~ 3/29)- [Data Processing]
-Face Recognition (추가 : 3/29, 기간 : 3/30 ~ 3/31)
- Cross-validation 사용 (추가 : 3/29)
-데이터 불균형 해소 /imbalanced Sampler,Focal Loss, OverSampling, Weighted loss (추가 : 3/30, 기간 : 4/1 ~ )
-Data Augumentation (Affine, Gaussian 등) (추가 : 3/30, 추가 : 4/5 ~ 4/8)
-Generator의 초당 Batch 처리량 측정 및 향상 (추가 : 3/30, 기간 : 3/31 ~ 3/31)- Cutmix 시도 (추가 : 4/1)
- Repeated Agumentation (추가 : 4/1)
-validation data 사용 (추가 : 4/1, 기간 : 4/1 ~ 4/1) - [Model]
- ResNet 152층 시도 (추가 : 3/29)
-Efficient Net 시도 (추가 : 3/29, 기간 : 4/1 ~ 4/2)
- YOLO 시도 (추가 : 3/31)
-Pre-trained 모델에 Fine-tuning 하기 (추가 : 3/29, 기간 : 4/1 ~ 4/1)
- Model의 초당 Batch 처리량 측정 및 향상 (추가 : 3/30)
- dm_nfnet 시도 (추가 : 4/1, 기간 : 4/7 ~ ) - [Training]
- 앙상블 시도 (추가 : 3/29, 기간 : 4/7 ~ )
- Hyperparameter 변경 (추가 : 3/29, 기간 : 3/29 ~)
-Learning Schedular 사용 (추가 : 3/29, 기간 : 4/1 ~ 4/7 )
- Model의 초당 Batch 처리량 측정 (추가 : 3/30)- 좋은 위치에서 Checkpoint 만들기 (Adam으로 모든 minimum 찾고, SGD로 극소점 찾기) (추가 : 4/1, 새로운 baseline code)
- Sex 분류 (1번 모델) -> Age 분류 (2번 모델) -> Mask 분류 (3번 모델) // 모델 나누기 (추가 : 4/1, 기간 : 4/5 ~ )
-Crop image (mask 분류)와 일반 image(age, sex 등 분류) 둘 다 사용 (추가 : 4/1, 기간 : 4/5 ~4/5 )
- batch size 작게 쓰면서, SGD 사용 (추가 : 4/2)
-NNI (Auto ML) 사용 (추가 : 4/2, 기간 : 4/2 ~ 4/4) - [Deploy]
-Python 모듈화 (추가 : 3/30, 새로운 baseline code)
2️⃣ Learning
- Adam보다 SGD (momentum/Nesterov momentum)이 좋을 떄도 있음.
Adam의 경우 training accuracy 측면에서 다른 optimzier보다 빠르게 도달합니다.
하지만 SGD는 수렴속도가 느리긴 하지만 Generalization 측면에서 더 우월하고, validation accuracy가 더 높게 나옵니다.
따라서 처음엔 Adam을 사용하다, 이후 일정 조건 충족 시 SGD로 optimizer를 중간에 변경하는 방법도 있습니다.

3️⃣ Main Task
1) dm_nfnet 시도
Model의 다양성을 주기 위해서 timm에 pre-trained 되어있는 dm_nfnet_0을 사용했습니다.
# model.py
class nfnet_f0(nn.Module):
def __init__(self, num_classes, pretrained=True):
super().__init__()
if pretrained == True:
self.model = timm.create_model('dm_nfnet_f0', pretrained=True)
else:
self.model = timm.create_model('dm_nfnet_f0', pretrained=False)
self.model.head.fc = nn.Linear(in_features=3072, out_features=num_classes, bias=True)
def forward(self, x):
return self.model(x)
nf_net의 params가 많아서 가장 적은 F0으로 선택했습니다.
실제로 F2까지 선택할 수는 있지만, 수렴이 너무 너무 느렸습니다.

문제는 f0도 성능이 거의 나오지 않아서 (빨간 박스) nni의 대기열에서 취소하게 되었습니다.

2) 다양한 pre-trained 모델 시도
- Efficientnet_b6
43M parameter로 확실히 적은 parameter입니다.
train도 확실히 빨라서 pretrained 사용할 때 기준 1 epoch에 78% Accuracy를 보여주었습니다.

class Efficientnet_b6(nn.Module):
def __init__(self, num_classes, pretrained=True):
super().__init__()
if pretrained == True:
self.model = EfficientNet.from_pretrained('efficientnet-b6', num_classes=num_classes)
else:
self.model = EfficientNet.from_name('efficientnet-b6', num_classes=num_classes)
def forward(self, x):
return self.model(x)
- Efficientnet_b4
b6보다 가벼운 모델
class Efficientnet_b4(nn.Module):
def __init__(self, num_classes, pretrained=True):
super().__init__()
if pretrained == True:
self.model = EfficientNet.from_pretrained('efficientnet-b4', num_classes=num_classes)
else:
self.model = EfficientNet.from_name('efficientnet-b4', num_classes=num_classes)
def forward(self, x):
return self.model(x)
- ECAresnet50t
82.35 top-1 @ 320x320, 81.52 @ 256x256 (Feb 8, 2021)
사용해보니 확실히 빠르게 Accuracy에 도달했고, 모델도 가벼웠습니다.
ECA Attention과 함께 여러 ResNet 가중치를 추가한 모델입니다.
ref) github.com/rwightman/pytorch-image-models
class ecaresnet50t(nn.Module):
def __init__(self, num_classes, pretrained=True):
super().__init__()
if pretrained == True:
self.model = timm.create_model('ecaresnet50t', pretrained=True)
else:
self.model = timm.create_model('ecaresnet50t', pretrained=False)
self.model.fc = nn.Linear(in_features=2048, out_features=num_classes, bias=True)
def forward(self, x):
return self.model(x)
- SE - Resnet152d
256x256 val, 0.94 crop top-1 - 83.75, 320x320 val, 1.0 crop - 84.36 (Jan 3, 2021)
Squeeze-and-Excitation Networks
ref) arxiv.org/abs/1709.01507
class seresnet152d(nn.Module):
def __init__(self, num_classes, pretrained=True):
super().__init__()
if pretrained == True:
self.model = timm.create_model('seresnet152d', pretrained=True)
else:
self.model = timm.create_model('seresnet152d', pretrained=False)
self.model.fc = nn.Linear(in_features=2048, out_features=num_classes, bias=True)
def forward(self, x):
return self.model(x)
3) 모델 나누기 ( Mask -> Gender -> Age )
nni에서 한 번에 돌리기 위해 Dataset의 num_classes와 model의 num_classes 부분들을 수정해서 search_space.json에 변수로 choice하게 만들었습니다.
// search_space.json
{
"batch_size": {"_type":"choice", "_value": [8, 16, 32]},
"model": {"_type" : "choice", "_value" : ["Efficientnet_b6", "Efficientnet_b4", "ecaresnet50t", "seresnet152d"]},
"criterion": {"_type": "choice", "_value" : ["focal", "label_smoothing", "cross_entropy", "f1"]},
"classification": {"_type": "choice", "_value": ["mask", "gender", "age", "multi"]}
}
train.py에는 parser를 추가했습니다.
# train.py
parser.add_argument('--classification', type=str, default='multi', help='classification type (default: multi)')
nni를 통해 mask, gender, age의 classification에 대한 여러 hyperparameter를 실험할 수 있습니다.

4) 앙상블
앙상블은 Soft predictions votes를 사용했습니다.
따라서 NNI를 통해 "train_accuracy가 0.9 이상이면서 loss가 줄어든" epoch에서 soft prediction csv를 출력했습니다.

아래 사진은 csv로 출력된 soft predictions 입니다.
차례대로 0, 1, 2번 label이며, 이 중 ans는 argmax로 구합니다.

따라서 mask, age, gender에 관한 각각의 csv들을 모아서 평균을 내줍니다.
아래는 age ensemble에 대한 예시입니다.
ensemble하고 싶은 soft predictions csv path를 list로 가지고 옵니다.
_age_list = [
'/content/csv_file/age_ecaresnet50t_140_focal_0.01/exp/submission29_0.9947_0.0061_soft.csv',
'/content/csv_file/age_seresnet152d_64_cross_entropy_0.01/exp/submission34_0.9955_0.0188_soft.csv',
]
age = []
age에 pd.read_csv로 넣어줍니다.
for idx, path in enumerate(_age_list):
age.append(pd.read_csv(path))
_age_list의 각각의 column '0', '1', '2' (label)들을 각 행에 맞춰서 더해줍니다.
_0 = 0
_1 = 0
_2 = 0
for idx in range(len(_age_list)):
_0 += age[idx]['0']
_1 += age[idx]['1']
_2 += age[idx]['2']
ensemble_age라는 새로운 dataframe을 만들고, 각 label (column)의 평균을 넣어줍니다.
ensemble_age = pd.DataFrame({'ImageID':age[0]['ImageID'],
'0':_0/len(_age_list),
'1':_1/len(_age_list),
'2':_2/len(_age_list),
})
ensemble _age의 각 label들의 argmax (pandas에서는 idxmax)를 구해서 ensemble_age의 'ans' 열에 넣어줍니다.
ensemble_age['ans'] = ensemble_age[['0', '1', '2']].idxmax(axis=1)
>>> ensemble_age

나머지 mask와 gender도 같은 방식으로 출력합니다.
이후, submission 행에 넣어서 'Image ID'와 'ans'를 출력합니다.
submission['ans'] = pd.to_numeric(ensemble_mask['ans']) *6 + pd.to_numeric(ensemble_gender['ans'])*3 + pd.to_numeric(ensemble_age['ans'])
>>> submission

4️⃣ Sub Task
없음.
5️⃣ Evaluation
날짜 | Data processing | Model | Training | Time | Accuracy | F1 |
3/29 | - ResNet-50 - |
- Hyperparmeter 설정 - |
7h 32m | 61.87% | 0.52% | |
3/31 | - Face Recognition - |
36m | 65.05% | 0.56% | ||
4/1 | - (pre-trained) Efficient-Net-b6 - |
- epoch : 20, batch size : 128 - |
1h 35m | 73.67% | 0.66% | |
4/1 | - (pre-trained) Efficient-Net-b7 - |
- epoch : 20, batch size : 100 - |
1h 31m | 73.25% | 0.66% | |
4/1 | - learning scheduler : CosineAnnealingLR - epoch : 21 - |
1h 41m | 68.49% | 0.60% | ||
4/2 | - imbalanced-dataset-sampler - |
- learning scheduler X - epoch : 26 - |
1h 39m | 72.38% | 0.64% | |
4/2 | - loss : Focal loss (γ = 2) - epoch : 16 - |
1h 29m | 74.43% | 0.68% | ||
4/8 | - (pre-trained) Efficient-net b6 Efficient-net b4 ecaresnet50t seresnet152d - |
- Ensemble - |
17h 24m | 74.46% | 0.68% |
* F1 Score

1) NNI를 사용한 모델 나누기 ( Mask -> Gender -> Age )
- 17시간을 돌려서 Mask, Gender, Age에 대한 각각의 모델을 찾고, hyperparameter들을 파악했습니다.
- 대부분의 Train Accuracy는 높은 점수를 보였습니다.
- nf_net은 pre-trained 모델을 사용했지만 훈련시간이 어마어마하게 걸렸고, 그에 비해 좋지 못한 성능을 보였습니다.
아마 Fine-tuning을 하기에 적합하지 않은 모델일 수도 있습니다.

아래 그림은 Age Classification을 수행한 모델들입니다.

아래 그림은 Mask Classification을 수행한 모델들입니다.

아래 그림은 Gender Classification을 수행한 모델들입니다.

Mask, Gender, Age 모든 모델에 대한 Train Accuracy를 나타낸 그래프입니다.

2) 앙상블
- 앙상블은 Mask, Gender, Age classification에 대한 여러 model들에 대해 진행했습니다.
- 현재 Mask는 6개, Gender는 1개, Age는 2개의 model을 가지고 ensemble했습니다.
--- AGE ---
- model : ecaresnet50t / batch size : 140 / loss : focal / lr : 0.01 (10 epoch 당 1/10)
- model : seresnet152d / batch size : 64 / loss : cross_entropy lr : 0.01 (10 epoch 당 1/10)
--- GENDER ---
- model : seresnet152d / batch size : 64 / loss : cross_entropy / lr : 0.01 (10 epoch 당 1/10)
--- MASK ---
- model : Efficientnet_b4 / batch size : 128 / loss : cross_entropy / lr : 0.01 (10 epoch 당 1/10)
- model : Efficientnet_b4 / batch size : 140 / loss : f1 / lr : 0.01 (10 epoch 당 1/10)
- model : Efficientnet_b4 / batch size : 140 / loss : focal / lr : 0.01 (10 epoch 당 1/10)
- model : Efficientnet_b6 / batch size : 64 / loss : f1 / lr : 0.01 (10 epoch 당 1/10)
- model : ecaresnet50t / batch size : 128 / loss : label_smoothing / lr : 0.01 (10 epoch 당 1/10)
- model : seresnet152d / batch size : 16 / loss : label_smoothing / lr : 0.01 (10 epoch 당 1/10)
3) 추가 목표
- FaceNET crop image가 아닌 일반 image를 가지고 train
- crop image와 일반 image 앙상블
- Mask, Age, Gender 앙상블 + Multi 앙상블
'[P Stage 1] Image Classification > 프로젝트' 카테고리의 다른 글
[Stage 1 - 08] No Augmentation (0) | 2021.04.08 |
---|---|
[Stage 1 - 06] 최적의 Hyperparameter 찾기 (0) | 2021.04.05 |
[Stage 1 - 05] Remote Server에서 NNI (Auto ML) 사용하기 (0) | 2021.04.04 |
[Stage 1 - 04] Focal Loss (0) | 2021.04.02 |
[Stage 1 - 03] Model & Optimizer (0) | 2021.04.01 |