성능 개선을 위해서 계속 노력하다가 어느 순간 한계에 봉착했다..
도저히 뭘 해도 성능이 개선이 안됐는데 그때 조장분이 한 가지 인사이트를 얻어왔다.
지난 기수에서 진행한 경진대회 블로그 후기들을 보면서 다들 데이터 증강 기법을 통해 성능개선을 한 사례들이 많다는 걸 알아챘다.
그래서 기본 베이스라인 코드에서 모델을 efficient b3으로 수정하고 데이터 증강기법을 사용했는데 성능이 0.8957까지 올랐다.
근데 이때 증강기법을 offline으로 아예 실제 이미지 파일을 생성하는 방법을 사용했다.
offline 증강
이게 무슨말이냐면, 실제로 우리가 지금까지 작성한 데이터 증강기법은 online 증강으로, 학습 시에만 변형을 주어 실시간으로 증강 데이터를 생성하는 방식이었다.
데이터가 매 epoch마다 새롭게 변형되어서 모델이 다양한 변형된 이미지를 학습할 수 있었는데 결국엔 학습 데이터의 양은 정해져 있다.
즉, 실제 학습 데이터양(1570장)은 변하지 않고 매번 학습할때마다 다른 이미지가 적용된다는 뜻이다.
그래서 아예 코드에서 적용한 데이터 증강 기법을 적용한 새 이미지를 생성해서 실질적으로 학습 데이터셋 양을 늘려버리는 작업을 진행해 줬다.
이렇게 하니 별다른 방법 없이 0.8957까지 올랐다.
이 방법을 베이스로
- ViT 모델 적용
- 더 다양한 argumentation 적용
- 더 높은 epoch 수
- offline으로 더 많은 학습 데이터셋 이미지 생성
- mixup, cutmix 적용
등등으로 현재 정확도를 0.9221까지 올려놨다.
1,2 등 모두 F1 스코어가 0.93대로 조금만 더 성능을 높이면 될 것 같다.
(궁극적으로 우리 팀의 목표는 0.97까지 다다르는게 목표였다.)
현재 시점에서 우리가 더 분석해 볼 방법은 다음과 같다.
- 증강기법 탐색
- Albumentation에서 추가로 사용해 볼 만한 방법과 기존 방법의 parameter 체크
- Augraphy에서 사용해 볼 만한 증강 방법
- Mixup 기법 학습(모델 학습 말고 사람 학습)
- 학습과정 Augmentation과 Test Time Augmentation (TTA) 사용방법
- 추론 결과와 인간지능 답안 비교 및 다른 이미지에 대해 분석
- 모델 별 학습 결과 비교 및 결정
- 모델
- 하이퍼 파라미터 (Lerning Rate, epoch 등등)
- 학습과정 중 평가 방법
위에서 말하는 인간지능은 일단 우리가 테스트 데이터셋을 알고 있기 때문에 실제로 눈으로 하나하나 보면서 target을 예측한 실제 정답 데이터를 말한다. (물론 실제로 눈으로 보고 정답을 예측한 거니 이걸 직접 서버에 제출하는 건 치팅이 된다. 그래서 이 데이터를 기반으로 로컬에서도 조금 더 성능을 개선하고 서버에 제출하기 위함으로 사용했다.)
틀린 예측 분석
일단 지금 우리가 제출한 모델 중에서 가장 성능이 좋은 예측 output과 인간지능 데이터를 비교했을 때 우리 모델이 틀리게 예측한 분포는 다음과 같이 나왔다.
class 3과 7을 서로 틀리게 예측한 게 굉장히 많았다.
이제 이 두 클래스에 초점을 맞춰서 모델 성능 개선을 해보려고 한다.
일단 Offline으로 class 3과 7에 대해서 데이터 증강을 더 해보려고 한다.
3과 7 클래스에 대해서 데이터 증강을 진행해 줬더니 약 500개 정도의 데이터가 추가되었다.
이걸 기반으로 최고성능(0.9221)을 보인 코드에 넣고 진행해 보니 0.7503 결과가 나왔다... 띠용?
그리고 모델을 바꿔 k fold 5, epoch 20으로 주고 돌려봤다.
그런데 fold를 돌면서 각 fold가 끝날 때마다 인간지능과 비교하는 코드를 넣었는데 어느 순간 val f1 스코어는 거의 0.99를 찍어도 실제로 인간지능과의 정확도는 어느순간 멈춰서 더 이상 오르지 않는 모습을 확인했다.
그리고 실제 성능에 가까운 인간지능과의 정확도도 0.8처럼 꽤 낮게 나왔다...
진짜 새로운 마음,,,
아예 진짜 새로운 마음으로(이미 몇 번째 새로운 마음이긴 하지만ㅋㅋㅋㅋ)
지금까지의 모든 실패를 합쳐서 이런 시도를 해봐야겠다.
현재 아무리 뭘 해도 모델의 성능이 개선되지 않는 시점이다.
- fold 수를 늘리고 epoch 줄이기 -> 정확도 0.66 0.7 수준,,
- epoch 수를 늘리고 cross validation 없애기 -> 정확도 0.75 수준,,
- seed ensemble
- model ensemble
- 더 다양한 데이터 증강기법 알아보기
- 틀리게 예측한 클래스 가중치 주기
- class 3,7, 14를 하나의 클래스로 분류하고 먼저 모델 학습하고 그 뒤에 class 3,7,14만 따로 한번 더 학습 돌려서 총 두 개의 모델을 사용해서 분류
하이퍼파라미터 수정
기존 코드와 데이터셋은 그대로 두고 여러 하이퍼파라미터들을 수정해 봤다.
모델은 efficientnet b3로 고정하고
img_size = 300
LR = 1e-4
EPOCHS = 30
BATCH_SIZE = 32
num_workers = 4
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4)
scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2, eta_min=1e-6)
loss_fn = nn.CrossEntropyLoss()
early_stopping = EarlyStopping(patience=7, min_delta=0.005)
- optimizer
- AdamW와 SGD 중에서 고민했는데 일단 빠른 학습과 노이즈가 많은 학습 데이터셋(데이터 증강 때 노이즈를 많이 심고 이미지를 새로 저장했다.) 때문에 AdamW를 선택했다.
- AdamW는 학습률을 자동으로 조정하므로 데이터의 잡음에 강건하게 대응할 수 있다.
- AdamW는 다양한 데이터 상황에서도 빠르게 최적화되며 학습 속도 또한 빠르다.
- CosineAnnealingWarmRestarts
- 스케줄러를 사용하면 학습률을 초기 값으로 반복적으로 리셋하면서 학습 속도를 조정할 수 있다. 이건 주기적인 재시작을 통해 모델이 지역 최적값에 갇히지 않도록 도움을 줄 수 있다.
- T_0=10: 첫 번째 주기는 10 에폭 동안 지속된다.
- T_mult=2: 각 주기가 끝날 때마다 주기 길이가 두 배로 늘어난다.
- eta_min=1e-6: 최소 학습률
- 이 설정은 학습률을 반복적으로 낮추면서 초기 값으로 리셋하여 안정적인 학습을 지원한다.
- Early Stopping
- 초기 학습에서 모델이 수렴하는 속도를 고려하여 patience 값을 5로 늘렸다.
- 초기 몇 에폭 동안은 학습이 안정되지 않을 수 있기 때문에 조금 더 느긋한 조기 종료 설정이 유리할 수 있다.
이렇게 적용하고 보니 정확도가 0.9161가 나왔다.
여기서 아예 EPOCH를 30으로 늘려주고 early stop도 더 천천히 주도록 바꿔줬다. 그리고 early stop을 거는걸 f1 score로 적용해 줬다.
그러고 돌려보니 정확도가 0.86528가 나왔다.
혹시 몰라서 SGD로 적용해 봤다.
그래도 정확도가 0.8621가 나온다..
틀리게 예측한 클래스 학습 데이터셋 추가 생성
기존에 class 3, 7에 대해서 틀리게 예측한 게 많아서 해당 데이터를 더 만들어주었는데 그게 500장 정도밖에 더 추가가 안 돼서 그 부분에 대해서 데이터를 더 늘려봤다.
그리고 아예 데이터 증강을 더해서 총 데이터 개수를 약 115702개로 만들었다.
기존 데이터셋은
이런 분포를 가졌는데 데이터 증강으로 다음과 같은 분포를 갖게 되었다.
그 뒤에 우리 기존에 있던 코드를 돌려봤을 때 정확도가 0.8636만큼 나왔다.
밸런스 샘플링
현재 데이터가 너무 많고 fold까지 적용하니 학습 시간이 너무 길어졌다. 이제 각각의 클래스에 최소 4000장은 있으니 여기서 학습데이터셋을 샘플링해서 학습시간을 단축시켜 보자 라는 생각도 해봤다.
Balanced sampling을 적용한 데이터셋을 만들려면 SubsetRandomSampler나 WeightedRandomSampler를 사용해서 DataLoader에 균형 잡힌 샘플링을 적용할 수 있다.
samples_per_class = 2000
class_indices = defaultdict(list)
for idx, target in enumerate(csv_df['target']):
class_indices[target].append(idx)
balanced_indices = []
for indices in class_indices.values():
if len(indices) > samples_per_class:
indices = np.random.choice(indices, samples_per_class, replace=False)
balanced_indices.extend(indices)
balanced_sampler = SubsetRandomSampler(balanced_indices)
trn_loader = DataLoader(
trn_dataset,
batch_size=BATCH_SIZE,
sampler=balanced_sampler,
num_workers=num_workers,
pin_memory=True,
drop_last=False
)
print("Balanced dataset sample count:", len(balanced_indices))
이런 식으로 총 34000개를 뽑아서 돌려봤다. (사실 34000개도 그렇게 적은 데이터는 아니라 여전히 학습 시간은 엄청 오래 걸렸다..)
이렇게 하고 있는데도 실제 로그를 보면 0.6~0.7 언저리라 실험 중지,,,
인사이트 이용
이것저것 검색한 결과 여러 가지 인사이트를 얻었다.
- vit tiny 모델 적용
- optimizer Ranger 21 적용
- SmoothFocalLoss 손실함수 적용
- 혼합 정밀도 GradScaler 적용
이렇게 하고 돌려보니 엄청 오래 걸렸지만 정확도는 0.78 정도로 나왔다.
그리고 여러 블로그 후기들을 보니까 WanDB를 적용한 프로젝트가 많이 있고 이걸 이용하면 각 모델의 파라미터와 성능을 한눈에 볼 수 있어서 우리도 이때 적용해 봤다. (조금 더 일찍 적용할걸 ㅠㅠ)
fold = 5로 주고 EPOCH를 30으로 줘서 val f1 값으로 early stop을 걸어줬다.
이렇게 해도 진짜 성능이 최고점을 넘지 않아서 멘붕이다,,,,
모델 2개 적용
이건 위에서 실제로 모델이 틀리게 예측한 데이터를 봤을 때 3,7 그리고 14 클래스에 대해서 모델이 잘 예측을 못하는 걸 확인했다.
그래서 3, 7, 14 클래스를 하나의 클래스로 분류를 하고, 그 뒤에 이렇게 분리된 데이터 안에서 다시 3, 7, 14 클래스로 세 개로만 분류를 하는 코드를 적용해 봤다.
이렇게 했는데도 정확도가 좋아지진 않았다.
아마 다음글은 이번 경진대회를 마무리하면서 후기글로 작성할 거 같다..
'Upstage AI Lab 4기' 카테고리의 다른 글
Dialogue Summarization (일상 대화 요약) 경진대회 1주차 (1) | 2024.11.28 |
---|---|
[CV 경진대회] 최종 제출, 자체 평가 및 회고 (1) | 2024.11.08 |
[CV 경진대회] TTA 실패기,,, (0) | 2024.11.04 |
[CV 경진대회] K-fold 적용 (0) | 2024.11.04 |
[CV 경진대회] 데이터 증강 기법, ViT 모델 사용 (0) | 2024.11.04 |