사진은 자주 찍으면서 왜 일기는 작성하지 않을까? - 기획
어떤 블로그에서 봤던 글귀인데 다시 못찾겠다..
이미지를 통해 시각적으로 제공하는 정보가 텍스트에 비해 훨씬 많고 편리하다. 누구나 들고 다니는 핸드폰으로 터치 한 번에 고화질 사진이 찍히는 시대에서 시간을 내고 일기를 작성한다는 것은 어떻게 보면 시간 낭비라고 생각할 수도 있다.
하지만 일기는 당시의 내 생각을 구체적으로 적을 수 있다. 동시에 글쓰기 능력이 향상되고, 표현능력을 향상시킬 수 있다. 그리고, 아무에게도 말못할 사정과 심정을 표현하며 스트레스를 해소할 수 있다. (내 군대에서의 일기를 보면 간부, 선임 욕이 반이다.) 아무튼 그날의 감정과 생각을 구체적으로 회상할 수 있는 일기를 사람들이 왜 안쓸까? 책상 앞에서 작성을 하던가, 폰으로 작성을 하던가 양손이 묶여있어야 한다. 책상앞에서 종이에 작성한다고 하면 침대에서 거기까지 가기 얼마나 힘든일인지.. 또한 시간을 많이 소비해야하며, 일기와 같이 경험했던걸 다시 정리하기보단 유튜브나 넷플릭스 같이 새로운 컨텐츠를 경험하는것이 자극적이고 즉각적인 보상과 재미가 있다.
침대에 누워서, 혹은 시간에 구애받지 않고 편리하게 일기를 재미있게 작성할 수 있는 방법을 생각해봤다.
바로 음성녹음이다. 핸드폰이 내 목소리가 들리는 위치에 있다면 작성할 수 있다. 침대에 누워서도, 변기에 앉아서도, 샤워하면서도 작성할 수 있다. 그럼 음성녹음으로 일기를 작성할 때 발생하는 다른 불편한 점은 없을까? 그 날의 내용을 한 눈에 파악하기 힘들며 그날 기분이 어땠는지 음성을 들어봐야 알 수 있다는 단점이 있었다. 글로 작성된 경우 해당 내용을 문단별로 넘어가며 파악하기 쉬우나 음성은 넘겨 듣는다해도 비교적 파악하기 힘들다.
이러한 단점들을 해결하기 위해 노력했다. 이 회고글은 그 방법들에대한 내용, 아쉬운 점, 좋았던 점, github에 코드로 작성되지 않은 부분을 위주로 정리하는 글이다.
달력을 통한 그날의 대표 감정 표시
달력을 통해 녹음된 일기에서 그날의 대표 감정을 분류하여 한 눈에 보여주고자 했다.
이를 통해 어떤 감정으로 이 달을 살았는지, 굳이 음성을 다 들어보지 않아도 어떤 감정이었는지 사용자에게 보여주고자 했다. 물론 말은 쉬웠으나 기술적으로 많은 문제가 있었다.
STT(Google api 활용)를 활용하여 음성기반으로 작성된 일기를 텍스트기반으로 작성된 일기와 동일하게 보여주고자 했다.
이를 통해 꼭 음성일기를 꼭 들어봐야한다는 단점을 해소하고 그 날의 일기 내용을 조금 더 수월하게 파악할 수 있도록 했다. Google api를 활용한건 사실 무엇보다 50건정도가 무료라는 점이 가장 컸고 외국기업임에도 불구하고 높은 정확도를 보여주었다.
감정 분류 - AI
감정을 분류하는데 중요한 정보는 발화 내용과 목소리라고 생각했다.
예를 들어 "미친거 아니야??" 라는 글이 있다고 했을 때 "ㅋㅋㅋ미친거 아니야?" 와 "와..미친거 아니야?" 는 다른 감정을 표현하고 있고
이걸 분류하기 위해서는 내용뿐만아닌 목소리에서 나오는 정보가 중요하다고 생각했다.
따라서 Text기반 감정분류 모델, 음성기반 감정분류 모델을 학습시킨 뒤, 각 모델의 결과를 앙상블하여 사용하고자 했다.
이때 Text기반 감정분류는 위에서 말한 STT를 통해 만들어진 Text를 기반으로 감정을 분류했다. 음성기반 감정분류는 팀원분께서 직접 모델을 설계하고 학습시켜 활용했다. 약 5주 정도의 기간에 f1 score기준 약 0.62를 보여주었다. 인공지능을 처음 접하신 분이였음에도 불구하고 짧은 시간동안 잘해주셨다. Text는 KoBERT를 활용했는데 f1 score로 약 0.81를 보여주었다. 약 2주 동안 진행했는데 NLP모델은 처음 경험해봐서 모델을 공부해가며 직접 설계하고 학습시키기에는 내 할 일이 너무 많았다. 따라서 데이터 위주로 변경하여 정확도를 향상시키고자 했다.
데이터셋은 이곳저곳에서 많은 데이터를 가져왔다. 그리고 pandas의 stratified split을 통해 test data set을 나누어 따로 저장했다.
데이터의 불균형은 예상된 문제였고 이를 해결하기위해 augmentation 기법을 찾아봤다. Image의 경우 상하좌우 반전하고 잘라붙이고 섞고 노이즈 넣고 난리부르스를 쳤었는데 Text는 어떻게 해야할지 감이 안잡혔다. 여러 방법을 찾아봤는데 주어 동사 앞뒤를 바꾼다던가, 단어의 위치를 치환한다던가 여러 방법이 있었다...
감정의 애매함
일반적인 감정분류 데이터셋을 보면 공포, 혐오, 놀람, 분노, 중립, 행복, 슬픔 으로 7가지 클래스가 존재했다.
근데 감정이란게 너무 애매했다. 예를 들어 "직장 상사가 일을 끝도 없이 줘" 라는 데이터에 분노라고 라벨링되었으나, 내가 저런 말을 할때면 슬픔을 느꼈을것이다. 이런 애매함을 해결하기 위해 음성 분류 모델을 추가한거지만 일단 학습자체가 잘 안됐다. 직장 상사라는 말만 나오면 죄다 분노로 분류했다. 예를 들어 "직장 상사랑 밥먹었어." 라는 문장도 분노로 표시했고, 당시 test data set에 대한 f1 score는 0.6~7 정도였다. 결과에 비해 높은 f1 score를 보여주었다고 생각됐고, 데이터 자체가 애매하다는 생각이 커졌다.
감정을 줄이기로 했다.
예를 들어 공포와 놀람, 혐오와 분노는 어느정도 비슷한 감정이라 생각했다.
"엘레베이터에 갇혔어" 라는 문장은 공포라고 라벨링 되어있었으나 놀람이라해도 무방했다. 또한 "룸메이트가 술만 마시면 맨날 바닥에 토해" 라는 문장은 혐오라고 라벨링 되어있었으나 우리 팀원 모두가 분노라고 생각했다. 공포와 혐오에 대한 데이터셋을 100개씩 추출해서 읽어보았을때 십중팔구 합쳐질 수 있는 감정이었다. 심지어 분노, 놀람 등은 다른 감정들에 비해 적은 수의 데이터가 있었고 이를 합친다면 데이터셋도 비교적 균형잡힌 데이터로 바뀌었다.
다른 팀원들과 상의 끝에 감정의 갯수를 줄이기로 결정했고 최종적으로 놀람, 분노, 중립, 행복, 슬픔 다섯가지 감정으로 나누었다. 그 결과 새롭게 만든 test data set에 대해 f1 score는 0.81을 보여주었다. test data set도 변경되고 class도 줄어들어 정확한 정확도 비교는 아니였지만, 눈에 띄는 정확도 향상과 실제 새로운 텍스트를 입력했을때 보여주는 결과는 아래와 같았고, 만족스러웠다.
DevOps 환경 구축
7주 정도의 기간동안 진행된 프로젝트로 완성도 높은 서비스를 만들려면 빠른 기획과 빠른 개발이 필수적이었다.
빠른 기획과 빠른 개발은 아무리 꼼꼼하게 진행하려 노력하더라도 빈틈이 보이기 마련이고, 그때마다 다시 수정하고, 개발, 배포하는 과정이 필요했다. 따라서 폭포수보다는 애자일방식을 필요로 했다. API명세서는 초기에 꼼꼼하게 작성한다고 했지만 일정부분 변경이 이뤄졌고, 추가된 기능, 삭제된 기능도 있었다. 매일 스크럼을 진행하고 수정사항과 개선사항, 문제점등을 공유했다. 프론트엔드와 백엔드, 인공지능을 담당한 팀원들이 서로 협업하며 개발, 배포를 반복했는데 이 과정에서 프론트엔드는 빠르게 배포된 백엔드 서버에 테스트하며 개발을 진행했고, 백엔드는 프론트엔드와 소통하며 response를 수정해나가고, 분류 모델은 정확도가 올라가면 해당 모델로 변경했다.
Jenkins와 Gitlab을 연동하여 Develop 브랜치의 내용이 변경될때마다 자동으로 빌드하고 Docker를 활용하여 배포했다. 해당 develop 브랜치는 개발서버로 개발속도가 느리던 프론트엔드가 실제 서비스 환경과 같은 백엔드 서버에 직접 요청하며 테스트할 수 있도록 하기 위함이었고, 프론트엔드의 특정 부분이 완성되기까지 백엔드 서버가 미처 발견하지 못했던 에러나, 수정사항을 찾고 반영하며 업데이트가 계속 이뤄졌다. 그리고 특정 기능이 완성되면 release 브랜치에 머지하여 빌드하고 배포했다.(를 계획했으나 결국 마지막 한 번의 release 브랜치 배포로 끝났다 ㅎㅎ;; ) Spring 서버의 경우 1~2분으로 짧은 다운타임이 있었으나, FastAPI서버의 경우 음성모델은 TF, 텍스트모델은 Torch로 구현되어 두개의 라이브러리를 설치하는데 약 13분의 긴 다운타임이 있었고, 해당 다운타임은 일기를 작성중이던 사용자들에게 소중한 일기 한 편 한 편이 날아가는 치명적인 문제점이었다. 따라서 Blue-Green 방식의 무중단 배포환경을 구축하여 서비스의 다운타임을 해결하였다.
인공지능 모델의 경우 처음 생각한 방법은 MLFlow를 도입하여 실험을 추적관리하고 API를 통해 활용하고자 했다.
하지만 MLFlow를 도입하고 사용해보는 것은 순전히 내 욕심이었다. 팀원의 진행사항이나 문제점을 계속 듣고 살펴본 결과, MLflow까지 적용하면 벅찰듯했다. 무엇보다 나도 정확하게 아는것이 아니며 사용해본 경험이 없었으므로 사용하자는 말을 꺼내기 망설여졌다. 그 결과 나는 TensorBoard나 WandB를 팀원에게 추천해주었고, TensorBoard를 통해 실험내용을 공유했다. 그리고 결국에는 학습된 모델을 활용하기 위해 gpu서버에서 학습하여 저장된 가중치 파일을 구글 클라우드에 올린 뒤 EC2 혹은 로컬에서 다운받아 사용했다.
아쉬운 점 ;-;
일단 가장 아쉬운 점은 하나를 깊게 가져가지 못했다는 점이다. 정신차려보니 BE, Infra, AI를 담당했는데 그 결과 인공지능의 경우 기존에 알던 내용을 기반을 위주로 진행했다. 즉 별도로 깊게 공부하지 않고 NLP에 손을 댔다..
내 멋대로 Ensemble..
일기는 여러개의 문장으로 구성되어있다. 문장이 쌓이고 쌓일수록 긴 글이되고 이는 NLP모델이 글을 해석하기 힘들어진다고 알고있다. 이를 해결하기 위해 LSTM, Transformer 등 다양한 기법이 나왔는데 KoBERT는 Transformer 기반 모델이다. 아무튼 여기서 조금 아쉬운 점은 별도의 실험 없이 느낌으로... 전체적인 글을 한 번에 예측하기보다 문장별로 나누어 분류하고 합치는게 더 정확도가 높지 않을까 싶어서, 나는 문장별로 감정을 분류한 다음 가장 많이 나온 감정을 대표 감정으로 설정했다. 여기서 추가된 api가 감정 변화 그래프이다. 대표감정만을 그래프로 보여주기보다 더 많은 정보를 보여주기 위해 각 문장별 감정을 세고, 그래프로 보여주었다. 그리고 대표 감정을 분류할때 음성기반의 감정 분류 결과를 Weight Voting Ensemble을 진행했다. 이때 음성에 대한 weight를 고정값으로 주었는데 이는 음성과 텍스트 분류의 정확도를 비교하여 단순 감으로 때려잡은 값이었다...ㅎㅎㅎ;; 이 값을 차라리 전체 문장의 개수와 길이, 각 모델의 confidence까지 고려하여 Soft voting으로 했다면 더 좋은 정확도를 보여줄 수 있었다고 생각한다.
MLOps를 제대로 경험하지 못한 점은 여전히 아쉽다.
인공지능 모델의 경우 수동으로 서버에 저장했다. 프로젝트의 볼륨이 작아 실험이 많지 않았기에 불편하진 않았지만 나중에 실험하는 양도 많아지고 볼륨이 커지면 분명 번거로운 작업이라 생각된다. 이를 자동화하고 싶었는데 그러지 못한 점이 아쉽다. 그래도 무중단 배포 환경은 정말 만족할만한 경험이었다. 또한 사용자로부터 감정이 수정되었을때 해당 데이터를 저장하여 나중에 추가로 학습한 뒤 모델의 성능을 향상시키고자 했는데 이또한 적용하지 못했다.. 가장 아쉬운 점이다.
Backend 역할에서 새롭게 배우고 적용해본것이 적다.
거의 없다고 봐도 무방하다.. Java에서는 stream을 공부하고 구분하여 적용하였는데,,,, 아직도 언제 뭘 써야지! 라는 확신은 없다. 그리고 이전에는 동작원리도 자세히 모르고 사용했던 JPA에 대해 공부했다. 공부하고 보니 이전 프로젝트에서는 정말 개판으로 사용했다는걸 알았다. ;-;.. FastAPI에서는 인공지능 모델을 싱글톤으로 사용했다. 서버가 재실행됐을때 바로 모델을 불러온 뒤 이후에는 계속 해당 객체를 불러와 사용하였다. 이때 여러 요청을 보내도 이미 할당된 객체를 불러왔다 ! 이외에는 뭐.. 딱히 새롭게 배우고 적용해봤다 ! 개선했다 ! 라고 할만한 경험이 없어 아쉬웠다.
부캠의 회고와 싸피의 회고
부캠 AI Tech에서는 대회가 끝날때마다 회고글을 제출해야해서 작성한 경험이 있다.
형식도 정해져있지 않았고 순전히 내 멋대로 작성해서 제출해야했다. 내가 프로젝트를 하며 생각났던 짤이나 문구를 붙여넣으며 회상하기 쉽도록 작성했다.
아직도 저 EDA,,, 그게뭔데,, 하던 감정이 생생하다. 당시 팀원들이 pd, plt, seaborn 뭐 다 잘쓰고 그러니까 위축돼서 팀원들의 코드를 참고하며 열심히 데이터를 파악하려 노력했다. 아무튼 저런 회고글을 올리며 부족한점, 아쉬웠던 점들을 정리하고 기록하며 다음 대회, 프로젝트에서는 발판삼아 빠르게 발전할 수 있었다.
싸피에서는 회고를 강제하지 않는다. 때문에 많은 교육생이 프로젝트를 회고하지 않는듯하다.
블로그 포스팅을 자주하는 팀원이 아니였으면 나도 싸피가 끝날때까지 회고를 미뤘을거 같다..
싸피와 부캠 AI Tech를 비교했을때 각자 장점이 있지만 회고, 공유 문화는 부캠이 압도적으로 좋은듯 하다. 싸피의 경우 프로젝트를 엄청나게 잘만들어도 프로젝트 발표를 못하면 그만이라는 생각이 크다. 때문에 발표에 있어 사용된 기술을 공유하고 소개하며 아쉬운 점, 어려운점을 공유하기엔 청자의 관심을 끌기 힘들며 발표에서 좋은 점수를 받기 힘들다. 너무 다양한 프로젝트가 존재하고, 분야가 다른걸 개발하는데서 오는 한계라고 생각된다. 반대로 부캠의 경우 CV, NLP를 나누어 대회를 진행하고, 각 대회에서 모든 팀원들을 섞어 기술이나 문제점을 공유하고, 성능이 오른 방법을 공유하며 모두가 함께 최고 점수의 벽을 뚫기 위해 노력했다.
실제 프로젝트를 소개하고 발표하는 자리라면, 싸피만큼 좋은 문화가 또 없다. 부캠이 기술에 집중했다면, 싸피의 경우 프로젝트에 집중했다고 생각한다. 기술 말고도 발표 자체에 대한 피드백도 받을 수 있다. 개발외적으로도 신경을 많이 써주는게 싸피라고 생각된다. (돈도 많이 주고 밥도 맛있음.)
프로젝트 repo
GitHub - JODONG2/VODA
Contribute to JODONG2/VODA development by creating an account on GitHub.
github.com