월간 데이콘 Computer Vision 이상치 탐지 알고리즘 경진대회

[Private 5위, 0.89376] 데이터 증강, Cait/Efficientnet Ensemble

2022.05.13 21:50 5,432 조회 language

안녕하세요. Team Zoo 입니다.
좋은 대회 열어주셔서 감사합니다.
대회에 참여하신 모든 분들 수고 많으셨습니다~!

코드
로그인이 필요합니다
0 / 1000
WhiteCow
2022.05.15 11:23

좋은 코드 공유 감사합니다

정균
2022.05.16 19:47

사용하신 Lamb optimizer은 제가 잘은 모르지만 큰 크기의 batch size에서 잘 작동한다고 하던데, 배치사이즈가 16이면서 해당 옵티마이저를 사용한 특별한 이유가 있나요?? 가르쳐주신다면 감사하겠습니다!

렛서팬더
2022.05.16 21:14

네 안녕하세요. 이번에  Lamb optimizer 를 사용한 특별한 이유는 없었습니다. 다양한 optimizer를 이론적으로 공부하면서 적용해보려고 하는데, 생각보다 성능이 나오지 않는 경우도 많아서 실험적으로 적합한 optimizer를 찾아서 적용하고 있습니다. 이번 대회에서는 시간이 많이 없어서 optimizer와 같이 디테일한 튜닝 보다는 여러 모델중에서 적합한 모델을 찾는 것에만 집중하였습니다. 그래서 배치사이즈가 16일 때도 해당 옵티마이저를 적용하였을 때 학습이 충분히 잘 되어서 사용하였습니다. 기회가 되신다면 다양한 optimizer를 적용해보시면서 성능을 비교해보면 좋을 것 같습니다.

gs
2022.05.29 00:05

안녕하세요
코드를 보고 실습을 하려 했는데 
'CUDA out of memory. Tried to allocate 20.00 MiB (GPU 0; 14.76 GiB total capacity; 13.49 GiB already allocated; 13.75 MiB free; 13.50 GiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to avoid fragmentation.  See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF'
이라는 에러가 뜨면서 자꾸 안되네요
혹시 왜 이런 에러가 뜨는지 아신다면 알려주세요
감사합니다

gs
2022.05.29 00:21

그리고 anomaly detection에서 convolutional autoencoder가 많이 쓰이는 걸로 알고 있는데 굳이 데이터 증강을 하면서까지 분류를 하신 이유가 있는지 궁금합니다

렛서팬더
2022.05.29 16:07

네 안녕하세요.

첫번째 질문에서 말씀 해주신 에러는 gpu 메모리 문제로 발생하는 에러 입니다.
해결 방법이 여러가지가 있는데, 간단히 정리해드리면 다음과 같습니다.

1. batch size 줄이기
2. gpu 캐시 비워주기
import gc
gc.collect()
torch.cuda.empty_cache()
3. nvidia-smi 로 실행중인 프로세스 확인한 후 메모리가 남아있지만 사용하지 않는 프로세스 kill
4. 더 작은 모델을 사용하거나 이미지 size를 줄여서 학습

기본적으로는 batch size를 줄여주면 됩니다. 그래도 안되면 2-4번도 한 번 확인해보시는 것이 좋습니다.

렛서팬더
2022.05.29 16:11

두번째 질문의 답변입니다.

저도 말씀해주신대로 단순히 품목별로 정상과 anomaly만 구분하는 문제였다면 Autoencoder와 같은 비지도 학습 기반의 모델을 활용하려고 했습니다.
하지만, 이번 대회에서는 모든 품목에 대해서 정상과 각각의 anomaly 종류까지 모두 분류해야하는 문제여서, anomaly한 데이터만 증강시켜서 지도학습 기반의 모델링을 활용하였습니다.

답변이 충분히 도움이 되셨으면 좋겠네요.
감사합니다.

gs
2022.05.29 16:47

자세히 답변해주셔서 정말 감사합니다.
그리고 이 말씀을 처음에 드렸어야 했는데, 코드 보고 정말 많이 배웠습니다. tensorflow를 쓰다가 torch를 실습해보려 했는데, 공유해주신 코드가 공부하기에 정말 좋은 코드인 것 같습니다. 코드를 공유해주셔서 정말 감사합니다.
그리고 코드를 처음 볼때 k-fold를 하는 부분이 좀 의아했는데, 5-fold로 학습을 진행해서 5개의 모델이 나오면 그걸
'./label_results_tf_efficientnet_b7_ns_600/000', 
               './label_results_tf_efficientnet_b7_ns_600/001', 
               './label_results_tf_efficientnet_b7_ns_600/002', 
               './label_results_tf_efficientnet_b7_ns_600/003', 
               './label_results_tf_efficientnet_b7_ns_600/004'
이런 이름으로 저장하고
test를 할 때는, TTA를 해야 하기 때문에
저장된 5개의 모델을 하나씩 불러와서, TTA가 적용된 모델으로 변경해주고 test inference를 수행해주는 것으로 이해했습니다.
(tta.ClassificationTTAWrapper)
근데 왜 이렇게 하는지 모르겠더라고요. TTA를 적용하는 것이 생각보다 까다로운 건가요?
이런 식으로 모델을 저장하고, 불러와서 각각 TTA를 적용시키고 inference를 하신 이유가 궁금합니다.
그냥 이렇게 하셨을 수도 있겠지만 뭔가 이유가 있을까 싶어서요. 제 생각에는 학습시킨 모델을 바로바로 interence에 쓰면 좋을 거 같습니다
그럼 모델을 5개씩 저장할 필요도 없어지니까요.
근데 생각해보니 train과 test를 이런 식으로 분리시키는 게 코드의 수정이나 가독성의 측면에서는 더 좋을 거 같기도 하네요.
감사합니다

gs
2022.05.29 16:50

그리고 바로바로 써버리면 하이퍼파라미터 조정이 불편할 수도 있겠네요.
제가 딥러닝 관련 경험이 부족해서, 이런 식의 방법(train하고 저장하고, test하고)을 원래 많이 쓰는지 모르겠습니다.

렛서팬더
2022.05.29 16:58

네 학습 시킨 모델을 바로바로 inference에 활용해도 되지만, 하나의 모델만 실험 해보는 것이아니라 여러 모델을 실험 해보고, 그 중에서도 또 필요한 모델의 weight만 불러와서 사용할 수 있기 때문에 train과 test 코드는 분리해두는 것이 효율적이라고 생각합니다.

그리고, 실무에서도 결국 학습을 하는 프로세스가 따로 있고, 실시간으로 판별을 해야하는 경우에는 best model만 불러와서 predict만 해주면 됩니다.
단순히 지금 학습 하는 모델로 바로 결과를 제출하는 것이 아니라면, train과 test 코드를 분리해서 적절하게 활용하는 것이 좋을 것 같습니다.

감사합니다.

gs
2022.08.19 01:03

오랜만에 다시 보러 들어왔는데 제가 감사인사를 안드렸네요
상세히 답변해주셔서 감사합니다.
다시 봐도 멋진 코드인 것 같습니다. ㅎㅎㅋㅋ

Arteddy
2022.08.12 10:42

안녕하세요~! 대회는 끝났지만 코드 하나하나 리뷰해가며 공부하고있습니다.
좋은 코드 공유해주셔서 너무 감사드립니다! 다름이 아니라, kfold 사용하는 부분에서 궁금한 점이 있는데요,
1. 인덱서로 fold column에 fold를 할당해줄때 중복추출로 인해서 fold가 변하게 되면, 처음 s_fold 가 0일 때 0에 해당하는 dataset이 많이 적지 않나요??
그만큼 적은 데이터를 df_val로 사용한다는 것인지 의도를 잘 모르겠어서요,,! 
2. Trainer() class에서 kfold for 문 종료 후 val_idx를 따로 지정해주는 이유가 있을까요?? 이후로는 따로 안쓰시는 거 같은데, 혹시 따로 사용하셨나해서요,,!
3. s_fold 문이 도는 main 호출 부분에서 for문으로 Trainer class가 호출이 되는데 따로 StratifiedKFold를 사용하신 이유는 단지 class의 분포를 고려해서 학습을 진행하기 위함이라고 보면 될까요? 뭔가 for문이 두번 도는 듯한 느낌이 들어서요!
4. 또한 args.step을 지정해주셨는데, 따로 step별로 csv를 저장하는 코드가 없어서요 언제 저 코드가 실행되는지 궁금합니다!
5. get_loader에서 phase=test로 설정하는 건 test dataset loader인거같은데 다시 DataLoader로 불러주신거랑 차이점이 있을까요?? phase 설정 후 test로 쓰인게 없는것같아서요!
이렇게입니다. 대회가 종료된지 꽤 됐지만 답변해주신다면 정말 너무너무 감사하겠습니다,,!!!

제가 머신러닝만 해서 그런지, KFold를 수행 시에 dataset이 뭔가 정해져있지 않은 것에 대한 불안감이 있어서 위의 질문들을 드리게 된거같습니다..!

렛서팬더
2022.08.12 20:36

네 안녕하세요. 제가 요즘 바빠서 답변이 늦었네요.

1,3. StratifiedKFold를 사용한 이유는 본 대회와 같이 클래스가 불균형한 경우에 각 fold 별로 클래스의 균형을 맞춰주기 위해 사용하였습니다.
그리고 main에서 for문으로 도는 이유는 나눠진 fold index에 따라 한 번 씩 학습 하기 위해서 입니다. 따라서 두 번 도는 것이 아니라 예를 들어, 5개 fold라면 각각의 1,2,3,4,5 이렇게 for문이 돌면서 하나의 fold 데이터 셋을 학습시켜주는 과정이라고 이해해주시면 될 것 같습니다.
https://velog.io/@ohxhxs/파이썬-머신러닝-교차검증-KFold-StratifiedKFold-crossvalscoreGridSearchCV
kfold 및 StratifiedKFold에 대한 내용은 위와 같이 구글에 검색해보시면 많은 자료가 있으니 참고하시면 더 도움이 될 것 같습니다.

2. val_idx는 바로 아래 코드에서 각 fold 별로 나눠진 val_idx를 적용하여 df_val = df_train[df_train['fold'] == args.fold].reset_index(drop=True) 이렇게 학습 과정중에 검증에 필요한 val dataset을 만들어 주기 위해 사용하였습니다.

렛서팬더
2022.08.12 20:37

4. step 별로 저장하려고 지정하는 것은 아니고, 제가 증강하여 만든 데이터 셋 csv만을 활용하기 위해 0으로 고정해두고 사용하였습니다.
        if args.step == 0 :
            df_train = pd.read_csv(opj(args.data_path, 'train_df_add_data.csv'))
        else :
            df_train = pd.read_csv(opj(args.data_path, f'train_{args.step}step.csv'))
만약, Pseudo Labeling 방법을 사용하게 되면 추가적으로 데이터 셋이 생성되어서 그런 경우에 step별로 생긴 csv를 활용할 때 사용하는 코드입니다. 이번 대회에서는 Pseudo Labeling을 사용하지 않았기 때문에 저 부분은 크게 신경쓰지 않고 0으로 고정 시켜두고 활용하면 됩니다.
 
5. 네네 말씀해주신대로 get_loader에서 phase를 test로 해서 활용해줘도 되는데, 저는 예측할 때  test_dataset = Test_dataset(df_test, test_transform)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=0) 이렇게  phase 안에 있는 내용을 따로 활용하였습니다.
즉, get_loader에 phase=test로 설정된 내용과 아래에 DataLoader에서 test_dataset으로 만들어 준 것이 차이점이 없다고 보시면 됩니다.

답변이 도움이 되셨으면 좋겠습니다.
감사합니다.

Arteddy
2022.08.15 21:23

넵 친절한 답변 정말 정말 감사드립니다!!
다시 차근차근 읽어보면서 공부해봐야겠네요! 좋은 하루 되세요~!!

Arteddy
2022.09.19 13:36

안녕하세요! 계속 보다가 궁금한 점이 추가로 있어서 질문드립니다 ㅠ
1. Optimizer.step()은 1 batch당, Scheduler.step()은 1 epoch당이라고 알고있는데, def training()에서 model training 전부터 warmup_epoch 에는 scheduler.step()이 먼저 나왔더라고요, training 전에 step하는 것이 어떤 의미인지 궁금합니다! 혹시 batch마다 step은 cycle일 때 되어있으므로 cos scheduler일 때는 last_epoch가 default가 -1이므로 last_epoch와 1부터 시작하는 epoch를 맞춰주기 위해서 이렇게 사용된걸까요??
2. optimizer.step() 후 scheduler.step()이 나와야 user warning이 안뜬다고 알고있는데 이렇게 코드를 돌려보니 warning이 안뜨더라고요,, 1번과 더불어서 어떤 의미인지 궁금합니다!
3. scheduler.step()은 1 epoch당 하는걸로 알고 있는데, warmup, cycle scheduler는 batch마다, cos은 epoch마다 step이 됐더라고요,, 혹시 라이브러리마다 적용해줘야하는 곳이 다른걸까요??

미리 한번에 질문드리지 못하여 정말 죄송합니다.