728x90

2020.05.06 ~ 2020.05.22 16일간 진행되었던 NAVER HACKDAY가 끝이 났습니다. 얻고 느낀 것이 너무 많았던 2주간의 기록을 남기러 왔습니다. 글이 길기 때문에 편한 어투로 기록합니다.

콘텐츠

  • 지원, 합격
  • 머신 러닝 첫 경험(수박 겉핥기) / Keras 사용후기와 시행착오😈
  • 얻은 것😁
  • 얻지 못한 것😓
  • 가장 감명 깊었던 것😌
  • 후기💪

🙋지원, 합격

대학교 4학년까지 한번도 쉬지 않고(군대 기간을 제외하고) 수료한 뒤, 코딩테스트를 보는 곳은 있는 대로 다 지원하고 있었다. 떨어지더라도 경험은 얻을 수 있으니 할 수 있는 건 다 해보자는 생각이었다.
자소설을 집필하며 깃허브 잔디를 가꾸고, 알고리즘 스터디를 진행하고 있었다. 개의치 말자고 생각했지만 연이은 낙방 소식에 은근히 풀이 죽어 갈 때쯤, 네이버에서 메일 하나가 왔다.

핵데이합격

이번 년도 두 번째 합격 소식이었다. 받고 나서 진짜로 소리 질렀다. 😱

해커톤 주제

해커톤주제

네이버 해커톤은 다른 해커톤과 달리 주제가 정해져 있고, 지원자가 원하는 주제에 지원하는 구조였다.


일 관련해서는 빈틈없이 관리하는 걸 좋아해서(파워 J) 캘린더 어플리케이션에 자연스럽게 눈이 갔고, 음성 인터페이스를 활용한 캘린더 일정 관리 앱 개발을 주제로 선택했다. 1지망만 넣고 2지망부터는 선택이라 그냥 비워 두었다.

 

네이버 현직자 멘토 한 분과 네 명의 팀원으로 프로젝트가 시작되었다. 자연스럽게 안드로이드 2명, 머신러닝 2명으로 나뉘어졌고, 안드로이드는 모르고 파이썬만 조금 알던 나는 머신러닝을 담당하게 되었다.

머신 러닝 첫 경험(수박 겉핥기...) / Keras 사용후기와 시행착오😈

사실상 이번 회고를 쓰는 가장 큰 이유이다. 머신 러닝이라기엔 머신러닝 라이브러리를 사용한 것 뿐이라 민망하지만😅

머신러닝을 맡은 두 명 다 관련지식이 아예 없고 TensorFlow, Keras를 처음 써 보는 쌩 초짜였다.
작업 과정이 얼마나 험난했는지는 예상에 맡긴다...🙀

순수 Tensorflow 코드를 익히고, 직접 학습시키며 사용하기에는 시간이 부족했기 때문에 Keras를 사용하기로 결정했다.

 

머신러닝 팀의 목표는 학습한 모델이 사용자의 발화를 받고, 그 발화의 역할(개체명)을 출력하도록 하는 것이었다. 멘토님의 조언에 따라 먼저 할 일을 잘게 쪼갰다.

  • 프로젝트에 적합한 모델 학습 방법 찾기
  • 데이터 전처리
  • 모델 학습
  • 어플리케이션 적용

이렇게 크게 네 단계로 나누어 진행했다.

프로젝트에 적합한 모델 학습 방법 찾기

컴퓨터가 아무리 좋아도 단어만 모델에 집어넣는다고 척척 알아들어 주지는 않는다. 단어가 문장에서 어떤 역할을 하고 있는지 학습을 시켜줘야 Input을 받았을 때 적절한 Output을 받을 수 있다.

 

하지만 AI, ML 관련해서는 난 아이언맨의 자비스밖에 모르는 사람이었다. 그래서 어떻게든 감이라도 잡기 위해 텐서플로의 온갖 예제를 풀었는데, 수많은 예제들을 풀었음에도 나의 경우에 어떻게 적용해야 할지 감을 잡지 못했다. 😱

 

예제의 경우, 결과가 참 혹은 거짓이다.

  • 인풋으로 들어온 영화 리뷰가 긍정적인지 부정적인지. [긍정/부정 관련 영화 리뷰 데이터로 학습]
  • 타이타닉 호에 승선한 승객이 죽었는지 살았는지. [타이타닉 호의 생존/사망 관련 데이터로 학습]

하지만, 나는 오늘 오후 5시 강남역이라는 발화를 받았을 때, '오늘'은 날짜, '오후 5시'는 시간, '강남역'은 장소 이렇게 다양한 경우의 결과를 도출해 내야 했다. 때문에 긍정인지 부정인지 이분법적으로 나누는 방식으로는 안된다고 생각했다. 우리는 한참동안 논의하면서 적합한 방법을 찾으려 했고, 결국 해결책을 떠올릴 수 있었다.

 

입력받은 단어를 학습된 개체명 각각에 일치하는 %를 구한 뒤, 가장 확률이 높은 것을 개체명으로 인식하면 되는 것이 아닐까?

 

예를 들어 '오늘'을 데이터로 넣었을 때, 날짜일 확률 - 95%, 시간일 확률 - 60%, 내용일 확률 - 20% 이런 식으로 나오게 하는 것이다.

우선순위 큐를 만들어서 각 label에 대한 확률을 저장한 뒤, 가장 확률이 높은 peek 값을 리턴하는 방식을 선택했다.

개체명 인식을 위한 데이터 전처리📈

우선 Mecab, Konlpy 등을 이용해 단어의 품사, 형태소로 나눈 결과를 출력해 보았다.

Mecab

처음에는 이렇게 작업을 계속 하다가, 문득 "모든 형태소와 개체명을 따로따로 취급해 줘야 하나?" 하는 생각이 들었다. 넉넉하지 않은 시간이 주어졌으니 최소 기능(MVP)을 구현하는 것이 우선인데 불필요한 자원낭비를 할 수 없었다.

 

먼저 단어의 역할을 구분해 주는 개체명 인식(NER - Named-entity recognition) 이후, 프로젝트에 필요한 개체명들만 추려서 Labels array를 만들었다. 나중에서야 알았지만, 이 작업을 Stopwords(불용어처리)라고 한다.

*갖고 있는 데이터에서 유의미한 단어 토큰만을 선별하기 위해서 큰 의미가 없는 단어 토큰을 제거하는 작업

 

이제 우리의 프로젝트는 아래의 array에 있는 개체명에 대한 정확도를 비교하게 될 것이다.

DT(숫자), LC(지명), PS(인명), OG(기관명), TI(시간), TODAY, TOMM(내일)

<!-- 개체명이 표시된 문장 예 -->
'<부천 영화제 사무국:OG>'은  <내달 3일:DT> 
<오후 3시 30분~5시 30분:TI> <부천:LC>
<중동:LC>신도시<중앙공원:LC>'PiFan 에서 사랑걷기 대회'를 개최한다.

그리고 학습된 24만 Line의 기존 NER 데이터를 프로젝트에 적합한 개체명으로 변환시켰으며, 정확도를 높이기 위해 필요한 개체명 (오늘, 내일, 모레, 1시, 월요일...)들을 추가했다.

 

또한 한국어 데이터를 Mecab으로 형태소 분석한 뒤, 적합한 Label에 추가했다.

💻모델 학습

자, 이제 문제는 적절한 모델이다. RNN, CNN... 어떤 모델을 써야 하는 거지? 산 넘어 산 🌄

수많은 뻘짓 끝에 양방향(Bidirectional) LSTM 모델을 선택했다.


LSTM은 RNN에서 발전한 모델으로, RNN이 학습을 거듭하다 관련 정보와 그 정보를 사용하는 지점 사이의 거리가 멀 경우 학습 능력이 크게 저하되는 문제를 극복하기 위해 고안된 모델이다. RNN과 LSTM의 차이를 그림으로 볼 수 있다.

LSTM

간단하게 설명하면 LSTM은 이전에 학습한 정보를 기억하여 학습 능력을 늘렸다는 말이다.
그리고 단방향이 아닌 양방향을 선택했는데, 단방향 LSTM이 아닌 양방향의 이점을 쉽게 설명하면 이렇다.

소년이 집에 간다.

이 한 문장만 가지고는 앞뒤 문맥의 파악이 힘들 것이다.

학교를 마친 소년은 교문을 나섰다.

하지만 이 정보를 기억하고 있다가 더하면 이해가 더 쉽다. 긴 문장을 학습한다고 할 때, 앞에서부터 읽은 것과 앞에서 한번, 뒤에서 한번 읽는 것의 정확도는 단순히 생각해도 후자가 높을 것이다.

 

물론 모델 학습 과정에 문제는 계속 터졌다. 실행을 시킬 때마다 AttributeError, ValueError, TypeError _get_save_@!@!@!@... 나중에는 컴퓨터한테 제발...제발 한번만 배워달라고 애원했지만 새침한 컴퓨터는 제대로 된 모델을 구축하기 전까지는 콧방귀도 뀌지 않았다. 테스트 중 삽질 과정을 기록한 사진만 간추려도 100장이 넘는다.

 

하지만 열번 찍어 안 넘어가는 나무 없다고, 마침내 학습에 성공했다.

성공한 모델학습 과정은 아래와 같다.

  • 단어와 개체명이 담긴 데이터를 list로 추출
  • 단어와 개체명 리스트 각각의 Tokenizer를 만들고, 각각 정수 인코딩을 수행
  • 정수 인코딩이 완료된 '단어 train', '개체명 train' 데이터를 pad_sequences 메서드를 사용해 test 데이터와 비교할 수 있도록 padding
  • train data를 test_size 옵션으로 분리하여 훈련 데이터와 테스트 데이터 생성
  • 모델에 필요한 모듈을 추가하고 컴파일 한 뒤, 훈련 데이터와 테스트 데이터를 활용해 학습과 검증

내가 의도한 바를 팀원이 이해하기 쉽도록, 주석을 굉장히 꼼꼼하게 달면서 작업했다.아래는 모델학습 코드의 일부이다.

# 문장 데이터에는 src_token, 개체명 데이터에는 tar_token 사용
src_tokenizer = Tokenizer(num_words=max_words, oov_token='OOV')
src_tokenizer.fit_on_texts(sentences)

tar_tokenizer = Tokenizer()
tar_tokenizer.fit_on_texts(ner_tags)

vocab_size = max_words
# 개체명 태깅 정보 집합의 크기
tag_size = len(tar_tokenizer.word_index) + 1

# 텍스트와 개체명 각각 정수 인코딩 수행
x_train = src_tokenizer.texts_to_sequences(sentences)
y_train = tar_tokenizer.texts_to_sequences(ner_tags)

그리고...!

1차_80대20

 

차근차근 비율을 바꿔 나가고, 달력 관련 개체명을 추가하며 데이터를 깎은 결과, 60%의 정확도에서 80%까지 끌어오릴 수 있었다. Kaggle의 이미 정제된 데이터를 사용했기에 가능한 결과이긴 했지만 고무적인 상승이었다.

나중에는 결과의 실제값과 예측값을 비교하는 코드를 추가하기도 했다. 

 

8차_예측결과

해치웠나...?

📱어플리케이션 적용

플래그를 성공적으로 세워서 그런지 이번엔 tflite로의 파일 변환이 말썽을 부리기 시작했다. 모델 학습은 멀쩡히 되는데 어플리케이션에 올리기 위해 모델을 tflite 파일로 바꾸려고 할 때마다 의미를 알 수 없는 오류가 끊임없이 반복됐다. 진짜 키보드 부술 뻔했다.💥
키보드가 박살나기 직전 팀원분이

from keras import~ 를
from tensorflow.keras import~ 로 바꾸면 변환이 된다

고 하셔서 신나서 학습을 시켰더니, 정말 tflite 파일로 변환이 됐다!

 

하지만 환호하는 것도 잠시, 80%의 정확도를 보여주던 모델이 3%(...)의 정확도를 보여주었다. 다시 멘탈 붕괴. 길어서 다 쓰진 못하지만 또다시 온갖 방법을 동원했다.
정확도를 포기하면 머신러닝의 의미가 있나 싶어 포기하기 싫지만 핵데이 마감은 이틀 남았고... 속이 타들어갔다.
하지만 모델 학습이 잘 되는데 파일 변환이 안된다는게 말이 돼? 라는 생각으로 계속 매달렸고, 결국 모델을 h5 파일으로 저장한 뒤 h5 파일을 tflite로 변환하는 방식으로 성공했다.

모델 변환 해결

from tensorflow.keras.layers에서 tensorflow 다시 제거!
from keras.layers import Bidirectional, LSTM...
model = Sequential() 모델을 tflite  파일로 변환하는 데 성공했다.

정확도를 포기하지 않은 변환에 성공하면서 의미있는 결과를 만들게 되었다.

model.save_weight() 를 써서 weight만 저장한 것이 문제였던 것 같다.
model.save() 로 저장한 뒤 변환하니 바로 되었다.

빙빙 돌고 돌았는데 단순하게 해결되어 순간 허탈했지만, 빙빙 도는 과정이 없었으면 결국 해결하지 못했을 것이다.
팀원과 골머리를 앓으며 계속 부딪치고 결국 성공했기에 정말 뿌듯했다.

얻은 것😁

  • Git-Flow를 지키며 프로젝트를 수행한 경험
    Git Flow는 그림으로만 봤지 전혀! 정말 전혀 할 줄 몰랐다. 사실 나는 그동안 Github를 썼지만 Git을 잘 알고 쓴 것은 아니다.
    (github와 git은 다르다!)당황해서 실수가 더 있기도 했지만 checkout 같은 기본적인 것도 몰랐던 것이 부끄러웠다. (밤마다 이불킥)
    그동안 혼자 git push/pull 만 썼는데 이건 사실 개인 클라우드 쓴 거나 다름없다. 제대로 써 보면서 알고 쓰는 것의 중요함을 또 느꼈다.
    이번 프로젝트를 하며 Pull Request도 날리고, 다른 branch에서 Merge도 해 보고, Release 할 때 그동안의 커밋들이 쫘르륵 펼쳐지는 걸 보면서 감동도 느끼며 많은 것을 배웠다.

멘토님이 Git-Flow는 한 번 써보면 이해가 쉽다고 하시면서 실습 시간을 가졌을 때 나 혼자만 모르는 것이 너무 많아 쥐구멍으로 기어 들어가고 싶었다.🐭(찍!)

  • Slack 사용 경험
  • 칸반보드 사용 경험
  • 크게 To Do, In Progress, Done으로 구분해 일을 진행하는 칸반보드는 정말 효율적이다. 핵데이 초반에는 각자 branch를 파서 진행하는데 서로 어떤 부분을 진행하는지 어떤 부분을 도와줄 수 있는지 몰라서 몇 명은 일하고 몇 명은 뭐 할지 몰라 가만히 있게 되어 작업이 더뎠다.
    칸반보드의 도입과 일을 잘게 쪼개어 하나씩 가져가면서 능률이 자연스레 올라갔다.
    코드를 짤 때도 효율성을 생각하면서 짜는 것을 좋아하는데 왜 일 자체를 이렇게 할 생각은 못했을까?
  • 👑문제를 쪼개어서 해결하는 능력
  • 이번 핵데이에서 얻은 것 중 가장 값진 부분이다. 어떤 일을 맡았을 때 그 일을 잘 해내려면 무엇보다 일을 나눠야 한다. 잘게 쪼갠 일을 전부 다 하면 맡은 일이 끝날 수 있게!
    이 프로세스가 몸에 박히면 무슨 부분에서 막혔는지 스스로도 잘 알 수 있고, 질문도 잘 할 수 있게 된다.
  • 여지를 남기는 말버릇(~할 것 같습니다. ~일 것 같습니다)을 덜 쓰게 된 것

얻지 못한 것😓

  • 잘 질문하는 법
    그동안 개발하면서 모르는 부분은 Google에서 찾아 왔다. 혼자 고민하면서 답을 찾으려 낑낑대는 것은 물론 좋은 자세이지만, 네이버 현직자가 멘토로 계시는데도 구글에 질문한 것은 조금 바보같은 짓이었다.
    고민 시간에 제한을 두고 안되면 질문하는 것이 훨씬 효율적이었을 것이다. 멘토님은 제한 시간으로 1시간을 주셨지만, 나는 과연 1시간만 고민하고 말았는가... 이제는 그냥 질문을 하자.
  • 일정을 지키지 못한 것
    내가 맡은 부분의 일을 제 때에 끝내지 않으면 전체에 영향이 간다. 이론적으로는 아는 내용이지만 직접 겪으니 더 실감이 되었다.
  • Slack, Kanban board를 썼지만 팀원들끼리 소통이 원활하지는 못했던 것
    말 그대로다.

+네이버 핵데이 후드티 못 받은 것😢

가장 감명 깊었던 것😌

  • 준비되어 있지 않으면 얻지 못한다는 것
    내가 머신러닝을 맡은 이유는 다른 팀원분들이 안드로이드를 맡는 것이 프로젝트 퀄리티에 더 좋다고 생각했기 때문이다.
    다른 분들은 준비되어 있었고, 나는 그렇지 않았다.앞으로는 내가 할 수 있는 최대한의 준비를 꾸준히 할 것이다.
    상투적인 말이지만 준비된 자에게 기회는 온다.
  • 받으려면 돈을 내야 하는 코드 리뷰를 무려 네이버 현직자가 Line by line으로 봐주시는데 나는 안드로이드를 할 줄 몰라 python 코드만 치고 있었다. 계속해서 올라오는 commit과 코드 리뷰, 그에 답하며 많은 것을 얻어 가는 팀원들을 보면서 얼마나 부러웠는지 모른다. 한편으로는 내가 얼마나 부족한지 체감이 되어 가슴이 철렁했다.
    무슨 일을 함에 있어 준비된 자세가 얼마나 중요한지 체험하고, 핵데이 내내 악착같이 집중하면서 최대한 많은 것을 습득하려 했다. 바보 같은 질문이어도 그냥 했다.
  • 기능 구현만 되면 장땡이라는 사고방식에서 벗어나 코드가 쓰이는 이유와 고려해야 할 점에 대해 배운 것
    남의 것을 보고 베끼는 것에서 끝나면 코딩이 아니다. 이 코드가 어떤 역할을 하는지, 왜 썼는지 고민하면서 개발해야 한다.

후기💪

프로젝트 초반, Github에 안드로이드 기본 세팅도 할 줄 몰랐던 내가 많은 것을 배우고 성장할 수 있었던 정말(x99999) 좋은 시간이었다. 코로나 이슈로 비대면으로 진행되었지만, 내가 깨어있는 시간에는 거의 항상 슬랙에 초록불이 들어와 있던 멘토님, 팀원분들 덕분에 오히려 더 많은 시간에 많은 것을 배우게 되었다고 생각한다.

나는 국비지원 강의와 병행해서 매일 새벽 3시에 자고 6시 반에 일어났는데 멘토님은 거의, 항상 계셨다. 언제나 칼답장🍴

멘토님은 방향을 못 잡고 허우적거릴 때마다 적절한 조언과 채찍으로 우리가 성장할 계기를 주셨다. 시간을 많이 못 썼다고 하시지만 그것보다는 멘토님에게 질문을 제대로 하지 못한 내 부족함이 더 크지 않았나 싶다.


맨 처음부터 질문하는 게 미덕이라고 그렇게 강조를 하셨는데 프로젝트가 거의 끝나기 전까지는 질문을 망설였던 것이 아쉽다. 멘토가 있는데 혼자 끙끙대고 있었던 건 순전히 내 실수고, 아쉬운 점이다. 이 경험이 다음에는 좋은 질문을 할 수 있게 해 줄 것이다.

 

프로젝트도 좋았지만 좋은 인연들👪을 만난 것도 큰 복이었다. 뛰어난 실력과 이성적인 사고, 따뜻한 속내를 가진 멘토님, 묵묵히 자신의 일을 수행하는 팀원분들과 만나 첫 프로젝트를 하게 되어 좋았다. 서로 존중하며 책임감 있게 본인의 일을 척척 하는 모습에 큰 자극을 받을 수 있었다. 혼자서는 할 수 없었던 일을, 계속 같이 얘기하다가 실마리를 잡고 해결하는 경험은 처음이었고 너무 즐거웠다.

애자일

비록 코로나 때문에 프로젝트를 마치고 난 뒤 Hackday 회고 시간에야 처음으로 얼굴을 볼 수 있었지만, 함께 새벽을 하얗게 지샌 전우들이라 어색함보다는 반가움이 컸다. 각자의 자리로 돌아가면 지금처럼 리얼타임으로 연락하기는 어렵겠지만, 끊기지 않게 계속 이어나가고 싶다는 작은 욕심을 가져 본다.

 

나 혼자서 공부하며 개발한 3개월보다 네이버 핵데이에서 팀 프로젝트를 3주간 진행하며 얻은 것이 훨씬 더 많았다.

 

마지막으로, 좋은 기회를 준 네이버에게 : 감사합니다.
실력있는 개발자가 되기 위해
개발을

열심히

하자.

 

팀원분들의 회고.

https://jaeryo2357.tistory.com/35

 

2020 Naver HackDay 참여 후기

들어가며 어느덧 4학년 안드로이드와 친해진지 3년 정도 돼가던 해, 졸업이 눈 앞에 보여 마음이 급한 대학 동기들끼리 여러 취업 공모전과 행사 소식을 소개해주고 있었고 그중 네이버 핵 데이�

jaeryo2357.tistory.com

https://gyugyu96.tistory.com/17

 

NaverHackday를 마치며

2020년 5월 6일. 네이버 핵데이가 본격적으로 시작되었다. 네이버 핵데이는 후배의 추천으로 지원하게 되었다. 안드로이드 클라이언트 개발자로 진로를 정한 후에 손에 잡히는 대로 공부하고 지��

gyugyu96.tistory.com

https://euzl.github.io/hackday_1

 

  • 네이버 블로그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기