[프로젝트 회고] 모델보다 중요한 것 - 딥러닝 프로젝트 첫번째 회고
딥러닝 모델을 개발할 때 가장 중요한 것은 무엇일까? 멋진 모델? 요즘 유행하는 모델? 요즘 사람들이 많이 쓴다고 하는 모델? 좋은 모델을 쓰는 것은 당연히 중요하다. 그렇다면 좋은 모델이란 무엇인가?
입사 후 약 4개월 간 첫 딥러닝 프로젝트를 진행하며 모델이 학습해야 하는 데이터, 모델 개발을 통해 이루고자 하는 구체적인 목표, 모델의 추론 결과를 직접 보게 되는 타겟 사용자를 고려하는 것이 좋은 모델을 만들기 위해 우선적으로 고려해야 하는 점이라는 걸 배울 수 있었다. 그리고 언제나 그렇듯이 다함께 하는 프로젝트이므로 언제나 어떻게 하면 더 효율적으로 소통할 수 있을지 고민하고, 더 꼼꼼하게 일하고 집요한 Q&A 기회를 갖는 것 -- 그렇지 않은 프로젝트가 있을까?
데이터가 많으면 무조건 좋다? '잘' 많아야 한다!
"데이터 1억개 있어요" 라고 하면 처음에는 데이터가 많다! 라는 생각이 들 수도 있다. 그러나 그 데이터들이 모델에 다양한 정보량을 줄 만큼 충분히 이질적인지, label이 일관적으로 잘 붙어있는지 등을 생각해보아야 한다. 가능하면 모델 구축부터 뚝딱뚝딱 시작하기 전에 데이터에 대해 어느 정도의 시간을 갖고 EDA를 진행하는 것이 좋은 것 같다. 이번에는 그렇게 못해서 시행착오를 통해 알아낸 게 대부분이라 아쉬운 점이 있었다.
모델에 충분한 정보를 줄 수 있을 만큼 이질적인 데이터가 많이 확보되어 있어야 좋다
이번 프로젝트는 심전도 데이터에 기반하여 이상 심장 박동(부정맥 관련 심장 박동) 분류 모델을 구축하는 것이었다. 즉, 하나의 심장 박동을 분류하는 모델이다. 한편 우리 회사는 일반적으로 한 사람 당 24시간-72시간 검사를 진행하도록 되어있다. 자, 그럼 막연하게 한 사람이 검사를 했을 때 얻을 수 있는 심장 박동 데이터를 계산할 수 있다. 일반적으로 1분에 60-100회 정도 심장이 뛴다고 알려져 있으니, 대략 1분에 80회 뛴다고 가정하고 24시간 검사를 했다고 할 때 무려 10만개 이상[80(회)x60(분)x24(시간)=115200]의 데이터를 확보할 수 있다. 와우! 정말 많지 않은가? 아니다. overfitting 현상이 엄청나게 빠른 속도로 우리 앞에 당도할지어니(안녕?).
한 사람이 가진 심전도 심장 박동 모양은 서로 너무 닮았고, 사람 마다 심작 박동 모양은 또 너무나 다르다. 그래서 소수로부터 얻은 많은 데이터를 사용하는 경우 학습했던 심전도와 비슷한 모양을 가진 박동은 잘 분류를 한다. 그러나 조금이라도 다른 사람으로부터 얻은 새로운 심전도 데이터를 보면 모델이 제대로 분류를 못한다! 실제로 학습 때에도 train, validation set을 어떻게 나누는지에 따라서도 성능이 너무 달라지고, 데이터가 많아 학습은 오래걸리면서 동시에 overfitting이 심하게 일어나 매우 고생했다. 운 좋게 validation set에도 training set과 비슷한 형태의 심전도가 있으면 잘하고 아니면 못하고... split을 할 때 generalizability를 고려하여 사람 별로 split을 해서 더욱 그렇기도 했다. 결국 포함되는 사람 수를 늘리고(어떻게든 긁어모아서..) 정제를 더 하고 augmentation 도 고려하며 해결하려고 했는데, 배포가 된 이후에도 이슈가 조금씩 있기는 해서 결국은 계속 수정과 재학습을 반복해야 하지 않나 싶다. 결국 다양한 '모양'의 데이터, 즉 적절히 이질적인 데이터가 필요하다는 걸 깨달았다.
가장 이상적으로는 각 사람으로부터 일정 양의 박동 데이터만 불러오고, 최대한 많은 사람으로부터 데이터를 얻는 것이 좋다. 현실적으로 쉽지만은 않은 일이지만.. 동질적인 데이터가 그냥 많기만 하면 오히려 데이터가 없는 것보다 더 힘들 수 있다. 적절한 데이터 undersampling 기법을 적용해야 하는데, 좋은 undersampling 기법을 선정하는 것이 또 하나의 프로젝트 감이기 때문이다..^^
아무리 집요하게 확인해도 지나치지 않은 것, 바로 label, label, label
자 우리가 아주 어린 아이이고, 과일의 이름에 대해 공부하고 있다고 생각해보자. 엄마가 노랗고 길쭉한 기둥이 여러개 붙어 있는 과일(바나나)를 가리켜 하루는 바나나라고 하고 하루는 사과라고 하고, 또 하루는 수박이라고 한다. 아기는 이 과일이 무엇인지 배울 수 있을까?
모델도 마찬가지이다. supervised learning을 해야하는데 답지가 일관적이지 않으면 절대 제대로 학습할 수 없다. 프로젝트 초반에 오픈소스 데이터셋과 사내에서 구축한 데이터셋을 모두 활용하여 학습을 진행했는데, 한 가지 종류의 이상 박동(supraventricular premature beat; S beat)을 너무 분류를 못하는 걸 발견했다. 해당 이상 박동의 경우, 갑자기 빨라진다는 중요한 특징이 있다.
그래서 정확하진 않지만 대략적으로 이전 3-5개 박동에 비해 해당 박동이 빨라지는지 여부를 검증하는 알고리즘을 이용해 직접 테스트를 진행했고, 실제로 사내 데이터 라벨링 기준과 오픈소스 라벨링 기준이 조금 다르다는 걸 발견했다. 특히 어떤 오픈소스 데이터셋에서는 빠르게 뛰고 있는 박동이 연속될 때, 마지막 박동에만 S beat 라는 라벨이 붙어있는 경우가 있었다. 아래 그림에서, 주황색 점이 실제로 S beat 라벨이 붙어있던 부분인데, 사내 판독사분께 문의 결과 빨간 동그라미를 친 부분이 모두 S beat 여야 한다고 말씀하셨다(최소한 사내 데이터는 이러한 기준으로 라벨링 하신다고 말씀하셨다). 실제로 사람마다, 기관마다 판독 기준에 있어서 차이가 나는 경우가 있다고 하셨다.
결국 오픈소스 데이터셋은 제외하고 사내 데이터만 이용해 이후 학습을 진행했다. 앞서 언급한 이질적인 데이터가 줄어든다는 손실을 감안하고서도, 비일관적인 라벨로부터 야기되는 문제가 더 심하다고 판단했기 때문이다. 이렇게 데이터 정제를 통해 오답률이 약 절반으로 줄어드는 성과를 얻을 수 있었다.
정확도(수치)가 높으면 무조건 좋다? 비즈니스 요구사항을 잘 맞추는 모델이 좋다!
모델의 결과를 누가 보는가? 누가 그 정보를 왜 활용하는가? 모델의 정량적 성능보다 중요하다!!
우리 회사 서비스 사용 방식은 다음과 같다. AI 모델이 결과를 내어 주면, 사내 판독사/병원 내 자체 판독사 분이 1차 검수와 편집을 진행하고, 의사분이 최종 결과물을 보시게 된다. 결국 중간에 '편집'과정이 들어가게 된다. 모델이 항상 100점을 맞지는 못하기도 하고, 병원 마다 판독 기준이 다를 수도 있고, 새로운 형태의 파형을 가진 환자의 경우 정확도가 떨어지기도 하는 등 여러가지 이유로.
검수를 할 때, 정상 박동 속에서 모델이 놓친 이상 박동을 찾아내는 것이 쉬울까, 이상 박동 중에서 잘못 잡힌 이상 박동을 지우는 것이 쉬울까? 후자! 이상 박동을 많이 찾아 놓고 그 중 false positive를 지우는 것이 쉽다. 아까 언급되었듯이 24시간만 찍어도 약 십만개의 박동이 나오게 되는데 또 '일반적'으로는 정상 박동이 이상 박동에 비해 훨씬 많기 때문! 즉 Precision을 조금 희생하더라도, recall이 무조건 높은 방향으로 개발해야하는 것이다. 실제로도 개발 중에도 recall을 가장 중점적인 지표로 설정하고 계속 모니터링 하려고 했다.
한편 이번 모델에서는 'questionable beat', 즉 noise를 포함하여 형태상으로 분류가 어려운 박동 또한 함께 분류를 했는데, 이 경우는 precision이 낮은게 치명적이다. 멀쩡한 박동인데 questionable로 분류되는 경우 편집 과정에서 놓치는 경우가 많고, 또 편집에도 많은 공이 들며 다른 오분류에 비해 AI 성능에 대한 의심을 많이 키우는? 부분이라고 하신다. 그래서 이 class 같은 경우는 최적의 threshold를 찾기 위해 EDA를 꼼꼼하게 진행했고, 거의 0.99 이상의 threshold로 설정하기로 했다.
이 외에도 자잘하게, X beat 보다는 Y beat로 분류되어 있는게 낫다거나, ~~ 방식으로 결과를 내는게 '편집'에 용이하다거나.. 이런 요구들이 있었다. 특히, 현재 회사 웹 시스템이 제공하는 편집 기능 자체와 연결된 요구사항도 있었다. 이 부분은 리듬 분류 회고에서 더 구체적으로 다룰 수 있을 것 같다(ㅎㅎ). 이런 요구사항들을 잘 고려하여 모델 개발을 하는 게 프로젝트의 목표였고, 그 과정에서 수치만으로 표현할 수 없는? 그런 '(비즈니스) 최적화' 과정이 있었다. 솔직히 처음에는 수치만 고려하는 것이 아니라 이런 것까지 고려해야 한다는 점이 부담으로 다가왔던 적도 있는데, 결국은 고객이 편하게 사용할 수 있는 서비스를 만드는 것이 중요하다는 사실을 깨닫게 해주기도 했다. AI 모델도 결국 고객이 쓰기 편한, 고객에게 좋은 모델이 좋은 모델이라는 걸 마음 깊이 새기게 되었다. 까먹지 말 것!
더 꼼꼼해지자. 열 심 히 질문하자.
좀 더 일반론적인 얘기로 마무리를 지으려 한다. 열심히 소통하고 열심히 질문하자. 그리고 전달 받은 코드가 있다면 꼭 100% 이해하고 일하도록 노력하자.
이번 프로젝트에서 나는 최종 모델 architecture 자체를 개발한 것은 아니다(물론 candidate 모델을 만들긴 했지만 선정되지는 않았다). 데이터 정제, 모델 최적화, 프로세스 효율 개선 등의 부분에 더 많은 기여를 했다. 데이터 전처리, 데이터셋 구성 등은 다른 분이 맡아서 했고 그 분이 DataLoader까지 직접 구현하여 팀 내로 전달해주셨다. 이것을 candidate 모델 개발에도 쓰고, 모델 최적화 과정에도 사용했다. Candidate 모델을 개발할 때, validation loss가 좀처럼 줄어들지 않아서 난 모델링에 재능이 없는가보다...하고 좌절도 많이 했었다. 그런데 최종으로 뽑힌 모델 최적화를 진행하는 과정에서도 계속 똑같은 현상이 일어나는 것이었다. 처음에는 헐 나 똥손인가봐 라고 생각했는데, 너무 의구심이 들어서 DataLoader를 뜯어보니..웬걸 training & validation 과정에서 모두 'random undersampling'이 'epoch마다' 진행되고 있었다. 참고로 모든 데이터에 대해 undersampling을 진행한 것은 아니고, normal 박동에 대해서만 진행하고 있었다. 매번 다른 셋으로 학습/검증을 하니 learning curve가 이상할 수 밖에 없었던 것이다.
내가 공부하고 찾아봤을 땐, random undersampling만 하더라도 사전에 진행해야 하고, training set 에서만 진행하며 validation set 에서는 진행하면 안된다고 되어 있었기에 이 부분에 대해서 먼저 의문을 제기하여 논의를 시작했다. 대화를 통해 DataLoader를 구현한 분의 방식에 대해 알게된 점은: 1) learning curve 모니터링 보다는 일정 시간 학습 후 confusion matrix를 보는 방식으로 계속 모델을 평가하고 있었다 2) training set에서는 epoch마다 random undersampling을 해주는 것이 좋다고 생각. 가능하면 좀 더 다양한 데이터를 볼 수 있도록하기 위함이다. 좀 더 좋은 undersampling 방법에 대해서는 여력이 없어 일단 random 으로 진행하고 있다.
그래서 우선, 아무리 시간이 걸려도 validation set에서 epoch 마다 random undersampling을 진행할 경우 제대로 validation loss를 추적할 수 없으며, overfitting이 되는지도 알 수 없고, best metric 기준으로 뽑은 weight도 신뢰할 수 없다고 말씀드리며 설득을 했다. 또한 epoch 마다 undersampling 이 진행되는 방식에 대해서도 논의를 진행했다.
결과적으로 validation set에서 epoch 마다 random undersampling을 하는 부분에 대해서는 나의 의견이 받아들여져서 이후부터는 learning curve, validation loss, best metric을 제대로 추적할 수 있게 되었다!!! 한편 training 시 진행되는 undersampling에 대해서는 현재 방식대로 일단 진행하되, 이 프로젝트가 끝나면 좀 더 연구를 시작해보기로 했다. 그리고 실제로도 프로젝트 이후 현재 사이드 형태이지만 이런 저런 시도를 해보려 하고 있다.
내가 사용하게 될 코드를 열심히 뜯어보고, 항상 의심하고(좋은 의미로), 더 꼼꼼해져야 한다. 또 의문이 있으면 열심히 찾아보고 질문하고, 적극적으로 논의하도록 해야 한다. 처음부터 알았으면 얼마나 좋았을까? 솔직히 처음에는 조급한 마음에 당장 적용하기에 급급해서 가장 기본적인 부분을 놓쳤던 것 같다. 내가 사용할 코드에 대해 잘 알고 있어야 하는데 말이다. random sampling 방식에 대해 무지해서 낭비한 시간들이 너무 아쉬우면서도, 또 덕분에 새로운 프로젝트 킥오프도 하고 의견이 반영된 모습을 보니 뿌듯하기도 하다 ㅎㅎ 이번 시행착오를 통해 다음에 좀 더 잘할 수 있었으면 좋겠다.
소소하게 중요한 것들
- 리소스는 많을수록 좋다 ㅎㅎ GPU 많이 많이~~
- 학습이 느릴 때는 모든 것을 의심해 볼 것 - 다른 블로그 글에 정리해 두었다 ㅎㅎ
- 전처리한 데이터를 내릴 때 어떻게 해야 더 확장성이 있는지 항상 고민하면 좋다. 데이터와 peak 정보를 함께 저장하는 걸 제안하여 3 beat, 5 beat, 7 beat를 모두 테스트할 수 있었다! (뿌듯)
- 요모조모 시행착오가 많은 프로젝트였다. 개인적인 관점에서도 회사 관점에서도. 이번 경험을 바탕으로 다음 프로젝트는 더 잘해낼 거야!
- 마지막으로.. 데이터 데이터 비즈니스 요구사항 리소스 리소스...... 끄적