최신 모델의 학습 방법

최신 모델의 학습방법

-이미지 분류 모델 학습에 최신 기법을 적용하고 높은 수준의 결과를 얻는 방법

이미지네트

이미지넷 - 범주 1,000개, 픽셀 500개
MNIST - 2828 5만장으로 구성된 흑백 손글씨 이미지
CIFAR10 - 32
32 6만장으로 구서오딘 컬러 이지미, 10개 범주

-알고리즘을 빠르게 검증하는 동시에 완전한 이미지넷 데이터셋 수준의 통찰을 얻을 수 있는 최소한 양의 이미지 데이터셋
-범주는 10개로 구성되어 있다.

->데이터셋의 크기를 줄이거나 모델을 단순화하여 새로운 아이디어가 생겼을 때 모델을 학습시키고, 몇 분 안에 어느정도 확인할 수 있어야한다.

from fastai.vision.all import *
path = untar_data(URLs.IMAGENETTE)

이미지네트 데이터셋 다운로드

dblock = DataBlock(blocks=(ImageBlock(), CategoryBlock()),
                   get_items=get_image_files,
                   get_y=parent_label,
                   item_tfms=Resize(460),
                   batch_tfms=aug_transforms(size=224, min_scale=0.75))
dls = dblock.dataloaders(path, bs=64)

Datablock과 사전 크기 조절 기법(item_tfms,batch_tfms)을 활용하여 데이터셋을 DataLoaders 객체로 만든다.

bs = batch size 의 줄임말로서 배치사이즈를 의미한다.

model = xresnet50(n_out=dls.c)
learn = Learner(dls, model, loss_func=CrossEntropyLossFlat(), metrics=accuracy)
learn.fit_one_cycle(5, 3e-3)

epoch	train_loss	valid_loss	accuracy	time
0	1.558659	1.976960	0.487304	02:25
1	1.220726	1.293779	0.589246	02:24
2	0.960535	1.002095	0.679985	02:23
3	0.720760	0.696808	0.779313	02:26
4	0.598845	0.594559	0.807319	02:20

xresnet50 사전 학습 모델을 사용하여 학습한다.
n_out은 출력층의 구조로서 데이터의 종속변수(카테고리)수로 맞춰준다(10)

CrossEntropyLossFlat 은 CrossEntropyLoss와 동일하지만 입력과 타겟(레이블)에 flatten이 적용된 손실 함수이다.

model = xresnet50(n_out=dls.c)
model

XResNet(
  (0): ConvLayer(
    (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
    (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
  )
  (1): ConvLayer(
    (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
    (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
  )
  (2): ConvLayer(
    (0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
  )
  (3): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
  ...
  

xresnet50 사전 학습 모델은 대략적으로 이러한 구조로 구성되어 있다.

데이터 정규화 (Normalization)

정규화 - 평균이 0이고, 표준편차가 1인 분포

x,y = dls.one_batch()
x.mean(dim=[0,2,3]),x.std(dim=[0,2,3])

(TensorImage([0.4661, 0.4588, 0.4321], device='cuda:0'),
 TensorImage([0.2800, 0.2725, 0.3008], device='cuda:0'))

현재 샘플 배치 데이터의 채널을 제외한 축값의 평균과 표준편차를 살펴보자.

x.shape

torch.Size([64, 3, 224, 224])
def get_dls(bs, size):
    dblock = DataBlock(blocks=(ImageBlock, CategoryBlock),
                   get_items=get_image_files,
                   get_y=parent_label,
                   item_tfms=Resize(460),
                   batch_tfms=[*aug_transforms(size=size, min_scale=0.75),
                               Normalize.from_stats(*imagenet_stats)])
    return dblock.dataloaders(path, bs=bs)


fastai에서는 데이터블록의 batch_tfms 부분에 Normalize 변형만 추가하면 데이터를 쉽게 정규화할 수 있다.

dls = get_dls(64, 224)

x,y = dls.one_batch()
x.mean(dim=[0,2,3]),x.std(dim=[0,2,3])

(TensorImage([-0.1921, -0.1583, -0.0353], device='cuda:0'),
 TensorImage([1.2585, 1.2572, 1.3039], device='cuda:0'))

변형(정규화)이 적용된 데이터 배치 하나를 살펴본다.

model = xresnet50(n_out=dls.c)
learn = Learner(dls, model, loss_func=CrossEntropyLossFlat(), metrics=accuracy)
learn.fit_one_cycle(5, 3e-3)

epoch	train_loss	valid_loss	accuracy	time
0	1.650448	4.502547	0.322629	02:22
1	1.254096	2.171517	0.450709	02:26
2	0.958310	1.197181	0.623973	02:26
3	0.746321	0.657616	0.794623	02:26
4	0.603508	0.557986	0.828977	02:26

이를 적용한 모델 학습 결과
정확도가 전체적으로 5~10%정도 개선된 모습

사전 학습된 모델을 사용할 때 특히나 효과가 크다.
fastai에서는 cnn_leanrer 함수로 사전 학습된 모델을 사용하면 해당 모델에 적절한 정규화 변환을 자동으로 추가해줌

점진적 크기 조절 (Progressive Resizing)

점진적 크기 조절 - 작은 이미지로 학습을 시작해서 큰 이미지로 학습을 끝내자는 아이디어
-> 학습의 대부분을 작은 이미지로 진행하면 학습 시간이 많이 단축되며, 큰 이미지로 학습을 마무리하면 최종 정확도가 훨씬 높아진다.

  • 데이터 증강 기법의 일종으로 바라볼 수도 있다.(일반화가 잘 됨)
dls = get_dls(128, 128)
learn = Learner(dls, xresnet50(n_out=dls.c), loss_func=CrossEntropyLossFlat(), 
                metrics=accuracy)
learn.fit_one_cycle(4, 3e-3)

epoch	train_loss	valid_loss	accuracy	time
0	1.639175	2.925121	0.387603	02:09
1	1.256326	1.015483	0.665422	02:09
2	0.941067	1.028242	0.696042	02:12
3	0.745236	0.635862	0.800597	02:13

작은 크기의 이미지에 대한 DataLoaders를 생성하고, fit_one_cycle 메소드로 적은 에포크 동안 학습을 한다.

learn.dls = get_dls(64, 224)
learn.fine_tune(5, 1e-3)

epoch	train_loss	valid_loss	accuracy	time
0	0.813107	1.650429	0.556385	02:29
epoch	train_loss	valid_loss	accuracy	time
0	0.637350	0.737521	0.776326	02:25
1	0.637559	0.715844	0.771471	02:25
2	0.562748	0.520876	0.830471	02:27
3	0.489363	0.476661	0.850261	02:30
4	0.424444	0.452161	0.851382	02:29

learner 내부의 DataLoaders를 큰 이미지로 교체한 뒤 다시 미세 조정을 수행한다.

정확도가 10~20%정도 상승했다.

다만 점진적 크기 조절은 데이터셋이 사전 학습된 데이터와는 다른 크기와 스타일로 구성되어 있을때 효과가 큰 편이다.

테스트 시 증강 (Test Time Augmentation)

지금까지는 임의로 자르는 데이터 증강 방식을 사용해 일반화가 더 잘 된 결과를 얻었다.

하지만 이 방법의 경우 어떤식으로 자르느냐에 따라서 매우 중요한 특징이 의도치 않게 잘려 사라질 수 있다.

그래서 테스트 시 증강은 추론 및 검증 시 데이터 증강을 적용하여 이미지의 다양한 버전을 생성하고 이에 대한 예측의 최대값 혹은 평균을 구하는 기법이다.

fastai는 기본적으로 증강되지 않은 중심부 잘라내기와 임의로 증강된 이미지 네 장을 사용한다.

preds,targs = learn.tta()
accuracy(preds, targs).item()

0.8737863898277283

TTA는 추가 학습 없이도 성능을 끌어올린다.
다만 추론 시간이 늘어난 데이터만큼 증가한다.

믹스업 (Mixup)

믹스업은 데이터 유형에 따라 서로 다른 데이터 증강 기법이 적용되어야 할 때 유용한 기법이다.
좌우로 뒤집어야할지, 상하로 적용해야 할지, 뒤틀어야 할지.. 모르는 경우에 유용하다.

image2,target2 = dataset[randint(0,len(dataset)]
t = random_float(0.5,1.0)
new_image = t * image1 + (1-t) * image2
new_target = t * target1 + (1-t) * target2

순서
1. 데이터셋에서 임의로 다른 이미지 선택
2. 임의로 가중치 고른다.
3. 기존 이미지와 선택 이미지의 가중 평균을 구하고 독립변수로 사용한다.

[0, 0, 1, 0, 0, 0, 0, 0, 0, 0] and [0, 0, 0, 0, 0, 0, 0, 1, 0, 0]

[0, 0, 0.3, 0, 0, 0, 0, 0.7, 0, 0]

세번째 이미지는 1번째 이미지의 70% 2번째 이미지의 30%가 결합된 이미지이다.

model = xresnet50(n_out=dls.c)
learn = Learner(dls, model, loss_func=CrossEntropyLossFlat(), 
                metrics=accuracy, cbs=MixUp())
learn.fit_one_cycle(5, 3e-3)

믹스업은 Learner에 콜백(callback)을 추가하기만 하면 fastai가 내부적으로 처리한다.
다만 믹스업으로 정확도를 개선하려면 앞에 데이터 증강 방식보다 더 많은 에포크가 소요된다.

믹스업은 이미지 외의 자료형에도 사용가능하고, 모델 내부의 활성에도 사용 가능하다.

레이블 평활화 (Label Smoothing)

데이터를 완벽하게 레이블하는 것은 매우 어렵다.
따라서 부분적인 실수가 필연적으로 생기는데 이 부분에 대한 영향을 상쇄하는 기법이다.

0와 1이라는 정확한 확률 대신에 오차(노이즈)를 살짝 넣는 기법

[0.01, 0.01, 0.01, 0.91, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01]


model = xresnet50(n_out=dls.c)
learn = Learner(dls, model, loss_func=LabelSmoothingCrossEntropy(), 
                metrics=accuracy)
learn.fit_one_cycle(5, 3e-3)

fastai에서는 손실 함수를 통해(LabelSmoothingCrossEntropy 이런식으로) 사용이 가능하다.

좋은 웹페이지 즐겨찾기