Naver Ai Boostcamp

[03/30] Data Feeding

잡담연구소 2021. 3. 31. 01:39
목표

 

1. 거만하게 공부하지 않기 : 교수님이 시키는 거 해보자

2. 베이스 코드를 짠 후, 교수님께 배운 기법들을 적용해보고 성능이 좋아졌는지 확인해보자

3. augmentation 이것 저것 가정하고 시도해보기 

 

 


 

강의 정리

- Dataset

주어진 vanilla data를 모델이 좋아하는 형태의 Dataset으로 만들어주는 법

 

- Pre-processing 

현업에서는 null, noise 등 제대로 되지 않은 데이터가 과반수 -> 전처리 시간 오래 걸리고 중요함 

이미지 데이터의 경우 , 시계열이나 정형 데이터에 비해서 전처리에 손이 많이 가는 편은 아니다.

하지만 이미지의 크기 때문에 메모리 문제가 발생

  

competition data는 품질이 매우 양호한 편 속지마라!

 

1. bounding box 

: 이미지 내 내가 필요한 정보에 대해서만 bbox를 쳐서 그 부분만 사용하기 

 

2. Resize 

: 계산의 효율(Memory)을 위해 적당한 크기로 사이즈 변경

 

3. 도메인, 데이터 형식에 따라 정말 다양한 전처리 방법이 존재 

-> medical data의 경우 밝기조절 등  

 

전처리는 마스터키가 아니다! 이것저것 가정하고 실험해보자

 

- Data augmentation

: 주어진 데이터가 가질 수 있는 Case , State 의 다양성

문제가 만들어진 배경과 모델의 쓰임새를 살펴보면 힌트를 얻을 수 있다.

좋다고 막 쓰면 안됨 -> 이게 실제로 가능한지? 고려해보기 

 

'무조건'이 아니다! 주제를 깊이 관찰해 어떤 기법을 적용해 효과를 얻을 수 있는 지 실험으로 증명해야 된다.!

 

- Data feedinng 

대상의 상태를 고려해 적정한 양을 준다

generator의 속도 측정 추천! 벤치마킹하면서 적당한 batch)size , num_worker 등 찾기 

 

compose안의 순서도 중요하다. 이거에 따라 속도가 달라진다.

이 부분은 생각도 안해봤는데 엄청난 거 같다. 메모리를 줄일 수 있는 Resize를 먼저 하는 게 맞는 거 같다.

이것도 순서를 실험해보면서 튜닝하기 

 

 

- Dataset , Dataloader 

Dataset :  torch.utils.data.Data 상속받으므로 init, getitem, len 필수  

Dataloader : Dataset을 batch size만큼 불러온다.

-> shuffle & sampler ,& collate_fn 조합해서 직접 해보기 추천 

 

Dataset과 Dataloader는 엄연히 다르다 !

데이터를 원하는 형태로 출력하는 클래스  / Dataset을 더 효율적으로 사용하기 위함

 


이 중에서 교수님이 dataloader에서 언급하신 sampler와 catalyst에서 제공하는 sampler기능에 대해서 좀 공부해봤다.

 

[Sampler]

Sampler는 Dataset을 인자로 받아 호출 시 data의 index를 반환해준다. 

즉 dataloader 내 에서 index를 컨트롤 하는 역할을 한다. 

shuffle = True를 실행했을 때, RandomSampler가 실행되는 거라고 한다. 

catalyst-team.github.io/catalyst/api/data.html#balanceclasssampler 적용이 힘들다;; ㅜㅜ

 


 

목표를 위한 행동 

 

1 . Baseline 짜보기 

baseline 완성! 올리고 싶지만 그러면 안되니까 꾹 참기,,, 

 

2. 겸손히 공부하기 : 교수님이 하라는 대로 해보기 

-1번 실험 : transform vs albumentation  누가 더 빠를까?

# torchvision tramsforms 사용 
class CustomDataset(torch.utils.data.Dataset):
    def __init__(self, path , df ,transform = None):
        
        self.path = path
        self.df = df
        self.transform = transform
        
    def __getitem__(self,idx):
        image = Image.open(self.df['path'].iloc[idx]).convert("RGB")
        if self.transform:
            image = self.transform(image)
        label = self.df['label'].iloc[idx]
        return image, label
    
    def __len__(self):
        return len(self.df)
        
        
# torchvision tramsforms 사용 
train_transforms = transforms.Compose([
                                    transforms.Resize((img_size, img_size)),
                                    transforms.ToTensor(),
                                    transforms.Normalize([0.485, 0.456, 0.406],
                                                         [0.229, 0.224, 0.225])])

val_transforms = transforms.Compose([
                                    transforms.Resize((img_size, img_size)),
                                    transforms.ToTensor(),
                                    transforms.Normalize([0.485, 0.456, 0.406],
                                                         [0.229, 0.224, 0.225])])

 

albumentation을 사용할 때, torch vision 과 거의 똑같지만 다른 점이 있다.

 transform을 함수처럼 받아와서 그 중 'image'에 해당하는 이미지를 가져온다. 

# albumentation 사용
class CustomDataset(torch.utils.data.Dataset):
    def __init__(self, path , df ,transform = None):
        
        self.path = path
        self.df = df
        self.transform = transform
        
    def __getitem__(self,idx):
        image = cv2.imread(self.df['path'].iloc[idx])
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        if self.transform:
            augmented = self.transform(image=image) 
            image = augmented['image']
        label = self.df['label'].iloc[idx]
        return image, label

    def __len__(self):
        return len(self.df)
        
# albumentation 사용 
train_transforms = A.Compose([
    A.Resize(256,256),
    A.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225]),
    A.pytorch.transforms.ToTensor()
])

val_transforms = A.Compose([
    A.Resize(256,256),
    A.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225]),
    A.pytorch.transforms.ToTensor()
])

 

시간을 재보자! 

# torch transform 사용
start = time.time()
for i , (img , tgt) in enumerate(train_dataset):
    if i == 100 :
        end = time.time()
        print(f'소요시간 : {end - start}')
        break

 

 

iteration 100번에 대해서 albumentation을 사용했을 떄가 0.25초 정도 더 빨랐다. 

그러면 학습을 시켜보면 어떨까?

trasform 사용
albumentation 사용

예상 외로 똑같이 1분 38초가 나온다. 

내 예상으로는 에폭1번당 iter100번 정도인데 그럼 둘이 0.25초 차이로 tqdm은 초까지만 표시해주므로 아마 차이는 나지만 반올림 돼서 둘이 똑같아 보이는 게 아닌가 싶다.

 

💚하지만 제 사랑은 albumentation입니다💚

 

 


- 2번 실험 : 어떤 augmentation이 좋을까? 

 

실험은 아래와 같은 조건으로 모두 동일하게 진행했다.

Epoch 10번이 너무 적을 수도 있지만 빠르게 비교해보기 위해서 10번만 했다. 

❗❗ 저작권의 이유로 다른 사진으로 대체 ❗❗ 

batch_size = 128
num_workers = 2
learning_rate = 0.001
epochs = 10
img_size = 256

criterion = CEloss
optimizer = Adam
lr_scheduler = None 

Model = EfficientNet B0

 


1. Base

# albumentation 사용 
train_transforms = A.Compose([
    A.Resize(256,256),
    A.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225]),
    A.pytorch.transforms.ToTensor()
])

결과 : 10epoch 기준 73.84%  0.67%

➕ 번외로 사람들이 lr_scheduler는 잘 안 쓰는 거 같던데,,, 이거만 잘 써도 4% 이상 향상된다.

 

 


2. Rotate 

왜? train / test 이미지가 모두 정면을 바라보고 있어서 Rotate는 필요가 없나 싶지만 갸웃거리는 수준의 이미지도 있을 수 있으니 10도 정도 rotate 시켜보자.

# rotate_albumentation 사용 
rotate_train_transforms = A.Compose([
    A.Resize(256,256),
    A.Rotate(limit = 10 , border_mode =  1),                     # 'cv2.BORDER_REPLICATE' = 1
    A.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225]),
    A.pytorch.transforms.ToTensor()
])

그려보자 : 정말 티도 안나게 조금씩 돌아간 걸 볼 수 있다.

결과 : 10 epoch 기준 : 72.35% 0.62%

 

 


3. RandomCrop

왜? 개요를 보면 공공장소에서 사람들의 착용 상태를 검사한다고 한다. 그렇다면 항상 정직하게 찍히진 않을 것이다.

그런 점을 고려해 RandomCrop을 사용했다.

scale과 ratio가 디폴트값인 ver1 과 scale 과 ratio를 내가 조절해준 ver2 두 가지 버전으로 시도해보았다. 

# randomcrop_albumentation 사용 ver1
randomcrop_train_transforms = A.Compose([
    A.Resize(256,256),
    A.RandomResizedCrop(256,256) ,                     
    A.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225]),
    A.pytorch.transforms.ToTensor()
])


# randomcrop_albumentation 사용 ver2
randomcrop_train_transforms = A.Compose([
    A.Resize(256,256),
    A.RandomResizedCrop(256,256, scale=(0.4, 1.0) , ratio = (0.75 , 1)) ,          
    A.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225]),
    A.pytorch.transforms.ToTensor()
])

 

그려보자 ver1 scale = (0.08 , 1.0) & ratio = (0.75 , 1.3333)

그려보자 ver2 scale = (0.4 , 1.0) & ratio = (0.75 , 1.0) 

scale과 ratio를 이렇게 준 이유는 원본 이미지의 ratio를 따라했고, scale은 이미지를 그려가면서 확인했을 때 마스크를 가리지 않는 선에서 가장 적당하다고 생각했기 때문이다.

ver1 결과 : 78.83% 0.74%

ver2 결과75.57% 0.70%

 

 


4. Horizontal Flip

왜? 성능의 큰 향상이 목적보다는 단순히 데이터의 다양성을 위해 추가함 

vertical flip의 경우 실제에서 그럴 일이 없기때문에 사용하지 않았고, 마스크를 눈에 낀 경우와 혼동할 수 있어 추가하지 않았다. 

그려보자

결과 : 70.63% 0.62%

 


5.  JpegCompression

왜? 보현님의 의견! 완전 압축돼서 굵직한 이미지가 남으면 그게 마스크를 의미하지 않을까? 라는 아이디어 

# JpegCompression_albumentation 사용 
Jpeg_train_transforms = A.Compose([
    A.Resize(256,256),
    A.JpegCompression(quality_lower=0, quality_upper=50),                  
    A.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225]),
    A.pytorch.transforms.ToTensor()
])

그려보자

결과 : 67.71%  0.57%

-> 노이즈를 추가하는 방식은,,, epoch을 좀 더 늘려봐야하나?

 


오늘의 달성 및 깨달음  
  • Random Crop을 사용했더니 성능이 갑자기 훅! 올라서 순간 2등을 했다 하하.
    역시 이것저것 해보라는 마스터님의 말씀을 곧이곧대로 해서 그런건가,,,! 싶었다.
  • 다른 조원들은 안된다고 로터리티켓 아니냐고 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 그럴 수도 있을 거 같다.
  • augmentation을 추가했을 때가 오히려 base에 비해 성능이 안나오는 것이,,,,,좀 놀라웠다. 이게 진짜로 base보다 안좋은 건지, 아니면 epoch 수가 너무 적어서 그런 건지 또 실험을 좀 해봐야 할 거 같다.
  • Sampler를 이용해보려고했는데,, 너무 어렵다. 내 코드에 적용이 안 된다. ㅠㅠ
  • Further Reading 중 흥미롭게 읽은 것
    • 여러가지상황에 맞는 custom dataset을 제공하는 github
    •  catalyst 라는 만능 라이브러리

목표 & 내일의 시도 

시간 상의 문제로 오늘의 실험은 여기까지 ㅠㅠ 

  • 내일은 augmentation 조합을 좀 도전 해보자!
    • RandomResizedCrop
    • One of [ gaussian noise , MotionBlur ,  jpegcompression]
    • horizontal flip
    • rotate
  • Fc layer를 한 단 더 추가해보기
  • Model 바꿔보기 B3 , B4 등

남들이 쓰니까 augmentation을 하는 것도 좋지만 ,직접 어떤 게 필요할까? 생각하면서 시도해보니 데이터나 문제에 대해 더 잘 이해할 수 있던 거 같다. 당장 리더보드에서 점수 내는 거에 급급하지 말고 내일도 교수님 말에 좀 충실해보자. 그럼 내일을 위해 빨리 자러 가보자 💨

 

 

 

 

 

 

 

'Naver Ai Boostcamp' 카테고리의 다른 글

[04/02] More...  (0) 2021.04.03
[03/31] Model  (0) 2021.03.31
[03/29] P Stage Start !  (0) 2021.03.30
[Day 38] 빠르게 & 가지치기  (0) 2021.03.28
[Day 37] 모델의 시공간 & 알뜰히  (0) 2021.03.27