본문 바로가기
AI, ML

[NLP/Text classification] 데이콘(Dacon) 텍스트 분류 대회 도전기

by saltyzun 2021. 10. 2.

이번년도를 시작하며 거창하게 Kaggle competition에 참가해 Expert를 달성해보겠다는 목표를 세웠다. 1월 경에 T아카데미에서 이수한 'Kaggle(캐글) 데이터분석 입문' 강의 덕분일거다. 결론부터 말하자면, 이 목표는 지키지도 못했고, 지킬 가능성도 매우 희박해졌다. 돌이켜보면 약 7달 전의 나는 이제 막 인공지능에 관심을 가지기 시작한 상태였고, 막연하게 캐글에서 좋은 성적을 내면 인공지능 전문가가 될 수 있을거라 생각했다. 그러나 막상 대학원에 진학해 인공지능을 공부하리라 마음을 먹고 보니 생각보다 좁은 영역(CV, NLP, Speech 등)에 초점을 맞추고 공부를 해야했고, 그렇게 출퇴근과 함께 Coursera 강의 듣고, 논문 아주 조금씩(초록과 결론정도?ㅎ) 읽다보니 연초에 세웠던 원대한 꿈은 자연스레 멀어져갔다. 그러던 중 7월이 되며 대학원 컨택과 함께 일이 바빠지며(꼭 한가하다가도 중요한 일은 손잡고 함께 온다) 공부를 좀 쉬게 됐고, 지금까지 공부를 잘 한건가 궁금하기도 하고 머리도 식힐겸 데이콘을 구경하다가 이 대회를 발견하게 됐다. 당시 Kaggle경진대회 솔루션을 공유한 업스테이지 영상을 재밌게 봤고, 마침 내가 관심있는 분야가 음성+자연어 분야였기 때문에 며칠 고민해보다가 신청버튼을 눌렀다. 결과는 썩 마음에 들지 않았지만, 확실히 내가 알고만 있던 것들을 실제 데이터에 적용하면서 따르는 크고 작은 어려움들을 풀어가며 스스로도 배운게 많았고, 혹여나 데이콘 대회를 도전하려 한다거나, 자연어처리가 실제 어떤 문제를 해결할 수 있는지에 관심이 있는 분들에게 조금의나마 도움이 되지 않을까하여 글을 남겨본다(왜냐면, 내가 대회 준비하면서 찾다보니 한국어 자료가 정말정말 없었다..ㅎ). +) 전체 코드는 시간이 될 때 정리해서 깃에 올리도록 하겠습니다.

 

* Kaggle(캐글) 데이터 분석 입문

https://tacademy.skplanet.com/live/player/onlineLectureDetail.action?seq=156

 

* Upstage AI 영상링크

https://youtu.be/Bl8rLKVH3Hk?t=123

 

* Git URL

: https://github.com/chanjunpark/dacon-climate-technology-taxonomy-2021


0. 대회개요


  • 대회명 : 자연어 기반 기후기술분류 AI 경진대회
  • 대회기간 : '21.6.21. ~ '21.8.16.
  • 상금 : 총 600만원
  • 주최 : 녹색기술센터(GTC)
  • 최종순위 : 상위 25%(망..ㅎ)

이번 대회는 국가 연구개발과제를 '기후기술분류체계'에 맞춰 분류하는 알고리즘을 개발하는 대회였다. 보다 자세한 설명은 대회안내(https://dacon.io/competitions/official/235744/overview/description)를 보면 알겠지만, 간략히 설명하자면 기후기술을 45개 카테고리로 분류하는 '기후기술분류체계'라는 것이 있는데 주어진 데이터의 feature들(연구명,연구목표, 연구내용 등)을 토대로 해당 연구가 어던 카테고리의 연구를 수행한 것인지 예측하는 문제였다.

 

 

1. 사전준비


개발환경

  • Colab Pro(with T4 or P100 GPU)
  • Python, Pytorch

1.1. 데이터 분석

우선 데이터셋은 아래 네가지로 구성되어 있다.

  • train.csv 
  • test.csv
  • sample_submission.csv
  • labels_mapping.csv

이름만 보아도 알 수 있듯이,  train 데이터셋으로 모델을 학습시키고, test 데이터를 모델이 집어넣어 연구과제를 분류한뒤, sample_submission에 저장해서 제출하면 된다. labels_mapping 파일은 label과 기후기술분류체계를 mapping한 메타데이터 였다.

 

① 데이터 구성

먼저, train.csv 데이터셋은 아래와 같이 13개 컬럼들로 구성되어 있었고, 174304개 데이터를 가지고 있었다.

다음으로, test.csv 데이터셋은 여기서 label을 제외한 12개 컬럼들로 구성되어 있었고, 43576개 데이터를 가지고 있었다.

결과적으로 나는 이 Column 들 중에서 [과제명, 요약문_연구목표, 요약문_기대효과, 요약문_한글키워드, 요약문_영문키워드]를 사용하여 모델을 학습시키고, 이를 토대로 예측한 분류 결과를 제출했다. 뭔가 과학적으로(?) 이 값들 중 무엇이 라벨과 연관이 높은지 파악하고 싶었지만, 급한 마음에 그냥 대충 눈으로 봤을 때 결과에 크게 영향을 줄 것 같지 않은 Column들은 제거하고 남은 데이터들을 사용했다. 그런데 막상 대회가 끝난 뒤 나보다 더 높은 순위를 기록한 코드들을 봐도 뭐 딱히 별다른 방법이 없는 것 같기도 하고... 무튼 주어진 데이터셋이 전체적으로 어떻게 생긴지 확인했다면 다음 작업은 각각의 Column이 어떤 데이터를 가지고 있는지 확인해보는 일이었다.

train.csv information
the shape of each dataset

② 데이터 길이

뒤에 자세히 적겠지만, 이번 대회에서 내가 사용한 언어모델인 BERT는 기본적으로 문장 임베딩 모델이었고, 최대 512길이의 문장까지만 임베딩을 할 수 있기 때문에, 데이터의 길이를 파악하는 작업은 아주 중요했다. 여기서 핵심은 512라는 숫자보다 BERT가 기본적으로 문장단위 임베딩 모델이라는 점이다. 다시 말해,  BERT는 최대 512길이까지의 문장을 임베딩 해줄 수 있지만, 결국 사용할 text의 최대 문장길이에 알맞은 MAX_LEN 값을 잘 정해줘야 한다.

 


1.2. 데이터 전처리

본문에 적지는 않았지만 주어진 데이터를 살펴보면 다른 대부분의 자연어 데이터들이 그렇듯 많은 특수문자가 포함되어 있고, 개행이 자주 있어 반드시 전처리를 해줘야했다.

 

가장 먼저 데이터 정제가 필요했다. 정규식을 활용해 문장을 cleansing 해줄 수 있는 함수를 만들었고, 이 함수를 모든 데이터에 적용해줌으로써 데이터를 깔끔하게 정리해줬다. 

def clean_text(sent):
  for i in range(len(sent)):
    sent = re.sub("[^가-힣ㄱ-하-ㅣa-zA-Z.]", " ", sent)
  return sent
train['과제명']  = train.과제명.apply(clean_text)
train['요약문_연구목표']  = train.요약문_연구목표.apply(clean_text)
train['요약문_연구내용']  = train.요약문_연구내용.apply(clean_text)
train['요약문_기대효과']  = train.요약문_기대효과.apply(clean_text)
train['요약문_한글키워드']  = train.요약문_한글키워드.apply(clean_text)

 

다음으로, 512 토큰이 넘는 문장을 200 토큰 내로 들어올 수 있게끔 전처리를 해줘야했다. 개발환경에서 KoBERT 모델의 MAX_LEN 값을 200을 넘겨 설정하면 그래픽카드 메모리 부족으로 모델 학습이 안 됐기 때문이다. 이 부분이 많이 아쉬웠지만, 일단 해결방법을 찾아야 했으므로 Truncation 기법을 생각했다. 정확한 출처는 기억나지 않지만 어디에선가 보기로 한 단락을 특정 길이로 잘라서 학습하고자 할 때는 단락의 앞, 뒤 문장에 중요한 정보가 대부분 포함되어 있으므로, 단락의 앞부분과 뒷부분을 잘라 그 둘을 이어 붙여서 원하는 길이만큼의 문장으로 다시 만들어주면 학습이 잘 이루어진다고 했다. 이번 대회에 주어진 데이터의 attribute 가운데 길이가 긴 것들은 일종의 단락과 유사하다고 판단했고, 길이가 200 단어를 넘어가는 데이터 들을 찾아 앞에서 120 단어, 뒤에서 80단어를 잘라내 한 문장으로 만들어줬다.

for i in range(len(train)):
  if len(train.요약문_연구목표[i])>200:
    train.요약문_연구목표[i] = train.요약문_연구목표[i][:120] + train.요약문_연구목표[i][-80:]
  if len(train.요약문_연구내용[i])>200:
    train.요약문_연구내용[i] = train.요약문_연구내용[i][:120] + train.요약문_연구내용[i][-80:]
  if len(train.요약문_기대효과[i])>200:
    train.요약문_기대효과[i] = train.요약문_기대효과[i][:120] + train.요약문_기대효과[i][-80:]
  if len(train.요약문_한글키워드[i])>200:
    train.요약문_한글키워드[i] = train.요약문_한글키워드[i][:120] + train.요약문_한글키워드[i][-80:]

 

그렇게 어느 정도의 전처리를 끝낸 뒤 마지막으로 남은 작업은 학습 데이터셋을 실제 학습에 사용할 데이터와 검증에 사용할 데이터로 나눠주는 일이었다. 검증을 위한 데이터셋을 따로 만들어야 모델의 학습 과정에서 실제 학습이 잘 이루어지고 있나 눈으로 확인할 수 있기 때문이다.

from sklearn.model_selection import train_test_split
train, validation = train_test_split(train, test_size=0.2, random_state=35)

# train set과 validation set 저장
train.to_csv(path+'clean_train_split.csv', index=False)
validation.to_csv(path+'clean_validation_split.csv', index=False)

 

2. 모델설계


2.1. 언어모델 선정 및 수정

이번 대회에 사용할 pre-trained 모델로는 BERT 기반의 KoBERT(SKTBrain)를 선정했다. NLP task 전반에 걸쳐 SOTA 성능을 내고 있는 모델들이 대부분 BERT 기반의 모델이었고, 그 중 한국어 문장들로 사전학습된 KoBERT가 이번 대회 데이터셋에 적합하다고 생각했기 때문이다. 

 

https://github.com/SKTBrain/KoBERT

 

처음 KoBERT 모델을 가져왔을 때는  BERT Dataset 생성을 위해 Label 값이 무조건 포함되어 있어야 했다. 그런데, 이를 그대로 쓰기엔 문제가 있었다. 왜냐하면 Label이 없는 test.csv를 KoBERT의 Input으로 넣을 수 있는 Dataset으로 만들 수 없었기 때문이다. 그래서 코드를 조금 수정해서 Label이 없더라도 BERT Dataset으로 만들 수 있도록 했다(물론 이게 정확히 맞는 방법인지는 모르겠고, 바꾸면서도 더 좋은 방법이 있을거라 생각했지만 마음도 급하고 일단 별문제 없이 돌아가면 쓰자는 생각으로 끝까지 썼음).


2.2. 분류모델 설계

앞서 말했듯이 단순히 MAX_LEN 값만을 늘린다고 성능이 좋아지는게 아니었다. 처음에는 이 부분을 제대로 이해하지 못해 KoBERT 모델의 MAX_LEN 값을 200에서 512까지 올려서 학습시키면 성능이 좋아질거라 생각했다. 그래서 여러개의 Attributes 들을 Sperator를 만들어 Concatenating 하는 방법을 써보려 했으나, 메모리 공간 부족으로 시도조차 못해봤다.

 

이와 같은 방법을 사용할 수 없게 되자 어떻게 하면 여러개의 비정형 Attributes로 구성된 데이터셋을 분류할 수 있을까 고민해보게 되었다. 그리고 고민 끝에 ① 각 Attribute 들을 기반으로 기후기술을 분류할 수 있는 분류기를 만들고, ② 이 분류기들이 예측한 결과 값(Class)을 Ensemble(Hard Voting) 하여, ③ 최종적으로 하나의 클래스를 예측하는 방법을 사용했다.

 

좋은 모델을 만들면 Model의 Hyperparameter 튜닝은 최종결과에 주는 영향이 작을 것이라 생각해 그냥 기본적으로 주어진 값들에 epoch만 15로 수정해서 학습을 진행했다.

 

Climate Technology classification model

 

3. 결과


Final ranking

결과는 Public에서 0.7814를 기록해 54위를 했고 Private에서 0.7588을 기록해 최종 64위로 대회를 마감했다.

 

4. 대회를 마치며


4.1. 구현하지 못 한 것

 여러 모델의 앙상블

널리 알려졌듯이 여러 모델에서 나온 결과를 종합하는 앙상블 기법은 경진대회에서 좋은 성적을 내는데 유효한 전략이다. 실제로 대회가 끝난 뒤 높은 랭킹을 기록한 참가자들이 공유해준 코드에는 여러 모델을 통해 예측한 결과를 종합하여 결과를 제출했다는 내용이 담겨있었다. 나는 대회 기간 중 많은 시간을 최대한 많은 attribute를 반영해서 결과 예측을 하고자 노력했는데, 차라리 반영하는 attribute 수를 조금 줄이되, 더 다양한 모델을 사용하는데 시간을 썼다면 어땠을까 하는 아쉬움이 남는다. 

 

② Long-text Embedding 

Long-text Embedding은 단락 혹은 단락들을 합쳐놓은 글과 같이, 문장보다 훨씬 긴 단위의 텍스트를 임베딩하기 위한 기법이다. 이번 과제에서 주어진 데이터의 경우 대부분 attributes 는 길이가 긴 편인데 반해 구현환경 상 BERT dataset의 최대 길이를 200으로 제한할 수밖에 없었기 때문에 처음부터 고민했던 부분인데 방법은 구글링을 통해서 얼추 찾아놓았음에도 시간이 부족해 구현하지 못 했다. 

 


4.2. 새롭게 알게된 것

① BERT!

우선 대충 알고만 있던 BERT 모델이용해 실제 데이터에 적용 가능한 분류기를 만들어 본 게 큰 경험이었다. 물론 여전히 BERT 모델을 완벽히 이해했다! 라고는 못 하지만 이 모델이 대표적인 NLP task인 Text Classification을 수행하는데 어떻게 사용될 수 있는지 감 정도는 잡은? 느낌이다.

 

② Text classification task를 수행하는 모델을 직접 만드는 일

Coursera 강의에서 주어진 탬플릿에 비어있는 부분만 채워서 Programming assignment를 제출하는게 아니라 Baseline 코드를 기반으로 내가 생각한 로직에 맞춰 새로 프로그래밍을 하는 경험은 그동안 내가 배워왔던 수많은 ML/NLP 지식을 하나의 프로그램 안에서 연결지어 볼 수 있는 소중한 경험이었다.

 

③ 데이터를 다루는 능력

데이터를 시각화하여 분석한 뒤, 학습에 사용할 데이터를 선정하고, 모델에 맞게 데이터를 전처리하기까지 데이터 그 자체를 다루는 능력도 키울 수 있었다.  물론 데이터를 다루며 예상대로 되지 않았던 것도 많았다. 가령, 나는 주어진 데이터 속에 있는 class 불균형(0 class가 전체 46개 class중 85% 가량이었음)이 문제가 될 것 이라 생각해서 10%만 sampling해서 사용해봤는데, 오히려 sampling을 하지 않았을 때보다 성능이 떨어지는 문제가 발생했었다. 그러나, 이런 과정이 싫지 않았다. 오히려 데이터 특성을 분석하고, 알맞게 처리하는 능력이 성능에도 크게 영향을 끼친다는 걸 알게된 뒤에는 더 공부해서 실력을 키우고 싶다는 생각이 들었다.

 


4.3. 아쉬운 점

실험엔 잘 짜여진 계획이 필요하다. 

돌이켜 보면 데이터 경진대회에서 스코어를 높이는 과정은 중고등학생 때 과학실에서 하던 실험과 비슷했다. 점수가 잘 나올 것 같은 모델을 설계하고, 설계한 모델에 투입할 수 있게 데이터를 정제하고, 모델이 예측한 스코어를 기록하고의 과정을 반복하는 일이었다. 그러니 전처리 방식과 예측모델의 베이스라인을 잡은 뒤 → 다양한 임베딩 모델을 활용한 분류기와 부스팅 방법 등을 테스트해서 가장 성능이 좋은 예측모델을 찾고 → 성능을 조금 더 높일 수 있도록 전처리 방식이나 하이퍼 파라미터를 튜닝하는 방식으로 계획을 잡고 실험을 진행했다면 시간낭비를 줄이고 더 좋은 결과를 얻었을 것 같았다. 내 경우에는 그냥 머릿속에 떠오르는 ML에서 배운 지식들(Imbalanced class, Learning rate, Optimizer, Dropout rate, Ensemble) 등과  NLP 지식을 다 때려박아가며 테스트하다 보니 모델도 지저분해지고, 중간중간 의미없는 실험하느라 시간낭비도 참 많이 했다...

 

시간은 금이다. 분업은 어떻게 해야할까.

이번 대회에서 주어진 데이터는 길이도 길고 양도 매우 많았다. 처음에는 대회 기간이 길다보니 시간이 많다고 생각했으나 제출 마감일이 다가올수록 모델을 조금 바꿔 새로운 결과를 얻는 데에만 몇일이 소요됐다. 앞서 말한대로 가설(아이디어) 검증을 위한 계획을 잘 짜고, 검증을 마친 아이디어는 빠르게 수용/기각 여부를 판단해서 시간을 최대한 효율적으로 활용해야함을 느꼈다. 또한 팀을 이뤄 참가를 했음에도 대회 경험이 처음이라, 어떤 구간을 나눠서 실험해야하는지, 과제 분배를 어떻게 해야하는지 등에 서툴러 그 이점을 잘 못살렸다는 아쉬움이 남는다.

반응형

댓글