Test Driven Development in Machine Learning Project

머신러닝 분야에서 일한 지 3개월, 소규모 프로젝트를 맡게 됐다. 대학 산학협력과제로서 소셜미디어 데이터 수집 및 자연어 분석을 하는 프로젝트다. 처음으로 맡게 된 PL에 신나 이것저것 시도해봤다. 때로는 지나치게 기술에 집착하여 프로젝트 진행을 스스로 어렵게 만들기도 했다. 그럼에도 시행착오에서 배운 것이 많고 선임님들의 도움으로 프로젝트를 무사히 마무리 할 수 있었다. 프로젝트를 회고하며 앞으로 어떻게 시행착오를 줄일 수 있을지 탐색해보는 글을 적는다.


프로젝트 개요

  • 이름: 대학교 산학협력과제 데이터 수집 및 분석
  • 기간: 2021. 07. 05 - 2021. 10. 15
  • 목표: 소셜미디어 데이터 수집 및 자연어 분석
  • 세부목표
    1. SNS 크롤링
      1. 키워드: 3개 키워드
      2. 사이트: 네이버 블로그, 디씨인사이드, 트위터
    2. 자연어 분석
      1. 군집화
      2. 토픽모델링
      3. 감성분석

잘한 점

비동기 크롤러

비동기에 대한 이해가 없는 상황이었음에도 리서치를 통해 크롤러를 비동기로 빌드. 지금까지 작성한 코드 중에 가장 긴 코드를 작성. 여러가지 시행착오를 해가며 코딩에 대한 이해도 상승. 내가 이렇게 긴 코드를 썼다는 것에 대한 자신감 획득. 알고리즘, 프로세스 등 CS 지식이 코드 작성에 base가 된다는 것을 깨달음

코드 리팩토링&패키징

중복되는 코드를 리팩토링하며 코딩 경험 상승. 각 scraper의 base가 되는 crawler파일을 만들고 utils, proxy 등의 파일로 나누면서 패키징에 대한 이해도 상승. argument parser를 작성하여 완성된 패키지를 만들어 봄. 코드의 설계도를 먼저 만들고 써나가는 TDD가 필요하다는 것을 깨달음

못한 점

기술 스택 집착

형태소 분석기를 빌드하는 데 엄청난 시간을 쏟음. khaiii, kiwi, MeCab 등 여러 개의 형태소 분석기를 빌드하여 적용. 더 좋은 기술에 대한 집착이 코드 개발에 시간을 너무 많이 쏟게 만듬. 쏟은 시간 대비 결과가 가성비가 매우 나쁨

비동기 크롤러를 빌드하는 데 거의 2달의 시간을 씀. 새로운 기술을 적용하는 데 신나서 이해를 하지도 못하는 코드를 마구잡이로 적용. 유지보수가 매우 힘든 코드 작성. 실제로 - 선임님 로컬에서 안돌아갔음

요구사항 정의

커뮤니케이션의 부재. 클라이언트가 원하는 결과와 내가 제출한 결과가 일치하는 게 가장 중요함. 그러나 나는 내가 원하는 결과를 목표로 잡고 프로젝트를 진행. 클라이언트가 먼저 나서지 않더라도 프로젝트의 완성을 위해 내가 주도적으로 클라이언트의 요구와 내가 만들고 있는 결과물을 계속해서 비교하며 방향을 수정했어야 했음. 결론적으로 - 팀장님 등의 윗선에서 클라이언트 대응에 골머리를 앓게 되는 결과를 초래함

결과 청사진의 부재. 결과물이 무엇이 될 지 정확히 모르는 채로 프로젝트를 진행. 당연히 장애물이 생기고 수시로 결과물 형태가 바뀌면서 불안감 상승. 결과물을 예상하기 위해 점진적으로 결과물을 산출했어야 함. 적어도 1차 보고 단계에서 3개 분석의 결과물을 아주 러프하고 작은 크기로 제출했어야 함

내부 기한

이건 진짜 고쳐야 함. 내 능력을 과대평가하지 말아야 함. 이건 진짜 진짜 지켜야 함. git과 redmine을 이용하여 프로젝트를 문서화함으로써 진행상황을 파악하고 일정계획을 짤 수 있음

앞으로 적용해 볼 것

Test Driven Development

TDD란 Test Driven Development의 약자로 테스트 주도 개발이라고 한다. 반복 테스트를 이용한 소프트웨어 방법론으로, 작은 단위의 테스트 케이스를 작성하고 이를 통과하는 코드를 추가하는 단계를 반복하여 구현한다.

이번 프로젝트의 가장 큰 패착은 전체 프로세스를 linear하게 차근차근히 진행했다는 것. 따라서 Data 수집/Wrangling -> 분석 -> Deployment(결과물 제작)을 하나의 Unit(단위)로 설정하고 이를 반복하여 develop하는 테스트 주도 개발 방법론을 적용해보자.

프로젝트 관리

git을 이용한 프로젝트 관리. Event가 발생할 때마다 commit. commit이 많아서 문제될 일은 절대 없다. 버그 발생/FIX, TODO 해결, 프로젝트 phase 종료 등 모든 상황에서 commit을 작성. 정확한 commit message를 작성하는 것은 중요하다. 그러나 프로젝트 경험이 적은 나는 commit message 적는 짬바가 부족하다. 따라서, too many but complex messages > precise but few messages

redmine을 이용한 프로젝트 관리. git이 메모라면 redmine은 스케쥴러라고 할 수 있다. weekly 또는 단계 별로 내부적으로 발생한 일들을 요약하여 작성한다. 외부인이 봐도 이해할 수 있도록 작성한다

사업 관리부를 이용한 프로젝트 관리. 프로젝트는 나 혼자 하는 게 아니다. 사업부에 진행 상황을 보고하여 개발 상황을 톺아보고 이를 바탕으로 클라이언트의 요구사항을 맞출 수 있도록 해야 한다


타임라인

  • 2021.02.16
    • 프로젝트 제안
  • 07.05
    • 네이버 블로그 크롤러 프로토타입 작성
  • 07.07
    • 1차 보고서 작성
      • 분석 기법 확정
        • 군집화: k-means
        • 토픽모델링: LDA
        • 감성분석: Logistic Regression
  • 07.09
    • 네이버 블로그 크롤러 추가 개발
      • arg parser
      • logger
  • 07.21
    • 2차 보고서 작성
      • 네이버 블로그 1000건 수집
      • Mecab 형태소 분석기로 parse
      • LDA - pyLDAVis 결과 예시
  • 07.22
    • 네이버 블로그 크롤러 디벨롭
      • async 크롤링 도입
      • proxy 사용 코드 작성
      • csv writer 추가
  • 07.27
    • 디씨인사이드 크롤러 작성
  • 08.09
    • 네이버 블로그/디씨 인사이드 크롤러 버그 수정
      • date argument 오류
      • pagination 오류
    • Proxy
      • Ip rotation 추가
  • 08.18
    • 트위터 크롤러 작성
      • selnium-wire 이용
  • 09.09
    • 과업 지시서 작성
  • 09.03
    • 크롤러 코드 리팩토링
  • 09.07 ~ 09.17
    • 네이버 블로그 크롤링
      • 날짜 범위: 2021.01.01-2021.06.30
    • 텍스트 클렌징 코드 작성
      • regex 적용 데이터 클렌징
        • one word
        • html tag
        • url
        • special character
      • khaiii 형태소 분석기 설치
        • 명사 추출
    • kmeans 적용
      • doc2vec으로 데이터셋을 vector로 만듬
    • khaiii parser의 결과물이 좋지 않아 사용자 사전 추가
      • 관련 용어 등 직접 추가
    • LDA 적용
      • pyLDAVis로 결과물 도출
    • Naver Sentiment Movie Corpus로 Logistic Regression 모델 학습
    • 디씨인사이드/트위터 추가 수집
      • 텍스트의 길이가 짧아 kmeans에 적합하지 않단 걸 알게 됨
    • kmeans/LDA에는 네이버 블로그 텍스트만, 감성분석에만 디씨인사이드/트위터 텍스트 사용
  • 09.20 ~ 10.01
    • 데이터셋 완성
      • 적은 데이터셋으로 분석 결과가 좋지 않아 날짜 범위 확장
        • 2020.07.01 ~ 2021.06.30
      • 네이버 블로그: 13,251 건
      • 디씨인사이드: 5,341 건
      • 트위터: 15,105 건
    • Kmeans
      • silhouette score 계산 -> 최적 군집개수 도출
    • LDA
      • coherence value 계산 -> 최적 토픽개수 도출
    • 감성분석
      • C value 계산 -> Logistic Regression에서 최적 L2 Normalizaition 계수 계산
      • 모델 성능 0.8514
      • LDA 토픽 군집 상위 키워드 긍부정 점수 계산
    • 09.24
      • 논문의 결과 부분과 연구방법 작성시작
    • 10.01
      • 내부 제출 기한이었던 본 날짜에 제출 못함
      • 논문 작성에 시간이 걸림
    • 10.06
      • 최종 결과물 제출
    • 10.08
      • 클라이언트: 군집분석에서 중앙성이 높은 키워드 현황을 종목별로 제시해주세요
      • kmeans에서 doc2vec 대신 word2vec을 적용하여 키워드 군집 생성
    • 10.11
      • 클라이언트: 데이터 분석 보완 및 추가 데이터 수집 요청

Review Project with TDD

다음은 미래의 시점에서 지난 프로젝트를 어떻게 TDD 방식으로 진행할 수 있었을까를 시뮬레이션 해본 시나리오다.

물론 실세계는 시나리오보다 훨씬 더 복잡하여 Big-O가 급격하게 늘어난다. 클라이언트가 생떼를 부릴 수 도 있고 예기치 못한 이벤트가 발생할 수도 있다. 그럼에도 지난 프로젝트를 회고하며 어떤 행동을 선택할 수 있었을까를 되짚어보는 것은 의미있는 작업이라고 생각한다.

프로젝트 기획

  • 목표: 특정 키워드로 SNS 크롤링 후 자연어 분석 프로젝트 계약
  • 상황:
    • 개발/결과물 레퍼런스 없음 -> 무엇부터 시작해야할 지 모름
    • 나 자신의 능력/프로세스가 없음 -> 프로젝트 기획을 어떻게해야할 지 모름
  • TDD:
    • Unit 설정 후 실행
      • 가장 쉬운 난이도의 크롤러 작성: 네이버 블로그
      • 아주 적은 양의 데이터 수집: 100건~500건 수집
      • 가장 쉬운 난이도의 자연어 전처리: Kkma
      • 가장 쉬운 난이도의 자연어 분석: TF-IDF, 워드 클라우드
    • 위 프로세스를 하나의 Unit으로 설정
      • 소요된 시간을 앞으로 프로젝트를 진행하는 데 걸릴 시간의 단위로 설정한다

요구사항 정의

  • 목표: 프로젝트 일정 확립 및 결과물 논의
  • 상황:
    • 프로젝트 기획 단계의 Unit 로그 및 결과물을 갖고 회의 참가
    • 클라이언트의 요구사항: 군집화, 토픽모델링, 감성분석 등
      • 그러나 나는 이에 대해 아는 게 없음
  • TDD:
    • 클라이언트에게 Unit 로그 및 결과를 보여주며 더 정확한 요구사항 논의 -> 이를 바탕으로 Unit 실행
    • 클라이언트의 요구 사항을 그대로 실행하여 전달하는 Unit 실행
      • 크롤러 작성: 필요 없음
      • 데이터 수집: 필요 없음
      • 자연어 전처리: 필요 없음
      • 자연어 분석: 있는 코드를 그대로 긁어와서 보유 데이터셋에 적용
        • KMeans:
          • How to make sentence to vector 검색 -> word2vec
          • 이해하지 말고 구현된 kmeans 코드 적용
        • LDA: pyLDAVis를 이용한 시각화 적용
        • 감성분석: 네이버 뮤비 리뷰 감성 데이터셋 Linear Regression
        • 이는 모두 당시 능력으로 짤 수 있는 코드임
        • 결과물의 수준은 고려할 필요가 없음
    • 클라이언트 전달
      • 결과물과 분석 과정에 대한 피드백 수용
        • 클라이언트는 군집 중심성이 아니라 네트워크 중심성 분석을 원한 것이었음
        • Kmeans 대신 NetworkX 적용 필요
      • 예상되는 어려움에 따른 결과물 수준 논의
  • 목표: 결과물 수정
  • 상황: 1차 결과물 제출 후 Kmeans 대신 NetworkX 적용 필요
  • TDD:
    • 크롤러 작성: 필요 없음
    • 데이터 수집: 필요 없음
    • 자연어 전처리: 필요 없음
    • 자연어 분석: 있는 코드를 그대로 긁어와서 보유 데이터셋에 적용
    • 클라이언트와 수정된 결과물 협의

개발

  • 목표: 각 분석의 정확도를 높이기 위한 데이터셋 확장 필요
  • 상황:
    • 크롤러 코드 작성 필요
    • 협의된 데이터셋 규모: 각 키워드 당 2000건
  • TDD:
    • 크롤러 작성:
      • 디씨 인사이드 크롤러 작성
        • selenium을 써야하는 트위터는 나중에
      • 디씨인사이드와 네이버 블로그 synchronous 크롤러는 동일한 방식으로 작성할 수 있음
        • 다만 반복작업이기 때문에 매우 지루하고 시간도 꽤나 걸림
        • 그래도 그냥 함
    • 데이터 수집:
      • 키워드 3개
      • 네이버 블로그/디씨인사이드
      • 수집 설정 기간: 6개월
    • 자연어 전처리:
      • 디씨인사이드 데이터의 욕설 또는 ㅋ과 같은 single character 제거
    • 자연어 분석: 개발 필요 없음
      • 기존 수립 모듈 그대로 적용
    • 내부 회의를 통해 결과물의 수준 논의
      • 클라이언트에게 2차 보고
  • 목표: 분석 결과물 정확도 개선 필요
  • 상황: 분석 결과는 맞으나 정확도의 개선이 필요
  • TDD:
    • 크롤러 작성: selenium을 이용한 트위터 크롤러 작성
    • 데이터 수집: 동일 기간 트위터 수집
    • 자연어 전처리: 트위터의 해쉬태그와 같은 special character 제거
    • 자연어 분석: 개발 필요 없음
      • 기존 수립 모듈 그대로 적용
      • 각 분석 결과의 정확도를 측정할 수 있는 score 리서치
      • score 계산
    • 클라이언트에게 진행상황 보고
      • 소규모 데이터셋과 customized 사전 빌드의 부재로 인한 정확도 한계가 존재함을 인지시킴
    • 자연어 전처리:
      • 사용자 텍스트 데이터셋에서 더 좋은 성능을 보이는 MeCab 적용
      • 결과물 정확도 부족 -> 개선 필요
    • 자연어 분석: 코드 수정 필요 없음
    • 자연어 전처리:
      • CNN을 이용한 카카오의 형태소 분석기 Khaiii 빌드
      • 각 키워드 당 사용자 사전 빌드
      • 결과물 정확도 부족 -> 개선 필요
    • 자연어 분석:
      • 각 분석 모듈의 공식 doc 및 활용 code 검색
      • 개선할 수 있는 parameter 테스트

결과물 보완 및 제출

  • 목표: 프로젝트 마무리
  • 상황: 결과물이 클라이언트 요구사항을 맞춤
  • TDD:
    • 클라이언트 결과물 전달

우리라고… 했다

금요일 밤 10시, 프로젝트 결과를 클라이언트에게 전달했다. 스스로 뿌듯해하며 이제 3일간 연휴니까 푹 쉴 수 있겠다 했다. 그러나 월요일 밤에 클라이언트에게서 메일을 받았다. 분석결과가 부족하여 논문을 작성하기가 어렵다는 것이다. 다음 날 아침부터 지끈거리는 머리를 붙잡고 출근했다. 보고를 받은 팀장님의 반응은 의외였다.

“그걸 왜 니가 머리아파해? 너는 계약서대로 이행했고 그 이상의 것은 상부에서 결정하서 대응할 일이야.” 이것만으로 꽤나 안심이 됐다. 그리고 - 팀장님이 상황을 파악하고 대응을 시작했다. 계약서 대로 이행했는 지를 체크했고 위에 보고했다. 이후로 몇 번의 메일이 오갔다. - 팀장님이 잠깐 담배 한 대피자고 불렀다. 수고했다는 말과 함께 이번 일은 클라이언트가 생떼를 부리고 있는 상황이며 내가 직접적으로 대응할 필요가 없다고 정리했다. 나는 모르는 새 위에서 클라이언트를 직접 대하며 프로젝트의 마무리가 결정됐다. 대표님은 나를 불러 수고했다며 클라이언트가 잘 몰라서 그런 거니 내가 이해하라고 했고 이사님도 개인적으로 수고했다는 말을 전했다.

계약서대로 이행헀다고 해도 내 잘못이 없는 건 아니다. 결과물 제출 내부 일정을 맞추지 못했다. 중간 보고를 했다고 해도 완성된 결과물이 아니라 예시 수준의 결과를 보냈다. 중간 보고 때 적극적으로 협의 과정을 거쳐 완성물에 대한 피드백을 받아야 했다. 그러나 나는 프로젝트가 어떻게 흘러갈 지 전혀 예상하지 못했기 때문에, 내 할 일만 하면 된다는 안이한 생각으로 프로젝트에 임했다.

그럼에도 불구하고 회사 차원에서 케어를 받는다는 느낌은 굉장한 소속감을 불러 일으켰다. 프로젝트는 나만 잘해서 절대 진행되지 않으며 회사의 일이기 때문에 전체적인 과정을 머릿속에 담고 있어야한다는 것을 배웠다. 또한 결과물에 대해 작게 시작해서 작게 보고하고 점점 디벨롭하는 과정이 부재했다는 것에 큰 아쉬움을 느낀다. 나는 프로젝트를 크롤링 수집 코드 작성과 분석 두 부분으로 나눴고, 수집을 완성하고 나서야 분석을 시작했다. 만약 적은 데이터셋으로 분석을 먼저 실행하고 부족하더라도 이를 클라이언트에게 보고하고 보완점을 모색했다면 양쪽에게 윈윈이 되는 결과로 이어졌을 것이다.

도움이 될 링크