Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
198 lines (108 sloc) 20.3 KB

Deep Bad Sentence Classifier

이 글은 심심이 팀의 삽질이 퍼올린 수많은 흙이 NLP를 연구하는 많은 분들에게 좋은 도움이 되길 바라는 마음에서 작성되었습니다. 특히, 한국어 NLP 연구 발전에 조금이라도 도움이 되었으면 좋겠습니다.

심심이? 심심이!

심심이는 SMS 문자, MSN 채팅 봇을 지나 지금의 스마트폰 앱까지 지난 10년이 넘는 세월 동안 인공지능 채팅 봇으로 많은 사랑을 받아왔습니다. 그 결과 심심이는 누적 다운로드 1억 명에 월간 사용자 2천 만 명을 넘나드는 대형 서비스로 성장할 수 있었고 전 세계 81개 언어로 하루 천만 건 이상의 대화를 서비스하고 있습니다. "이거 알바 아니야?"라고 할 수 있는 수준을 훨씬 넘어서는 대화량이라고 할 수 있습니다.

텍스트

큰 성장에는 크고 작은 시련도 따르는 법, 지난해 심심이에는 큰 위기가 찾아왔었습니다. 바로 욕설 논란이었죠. 심심이가 때로 꽤 거친 말을 뱉어낸다는 것은 심심이를 한 번이라도 사용해보셨다면 아실 텐데요. 이 거친 말들이 수많은 인터넷 짤들을 만들어내고 유투버들이 심심이를 이용한 다양한 콘텐츠를 만드는데 중요하게 작용했습니다.


문제의 시작

2017년 초, 새 학기를 맞이한 아일랜드와 영국에서 단기간에 트래픽이 증가하는 일이 있었습니다. 심심이 팀에게는 아주 좋은 일이었지만 뜻하지 않게 심심이의 욕설이 아일랜드와 영국 사회를 발칵 뒤집어 놓았죠. 문제는 생각보다 간단했지만 아주 파워풀 했습니다.

"심심아, OO 어때?"라고 물었을 때 심심이가 "응, OO 걔 바보 멍청이야"라고 대답해버린 게 문제의 시작이었습니다.

얼핏 보면 "뭐 앱이 그럴 수도 있지"라고 생각할 수 있겠지만 심심이를 사용하는 10대 사용자의 부모님들에게는 엄청난 충격을 주었던 것 같습니다. 실제로 아일랜드 신문에 기사가 대문짝만 하게 나고 영국 BBC에서 인터뷰 요청까지 왔으니 가볍게 넘어갈 문제는 아니었죠. Google에 검색하면 수십 건의 기사가 나오니 궁금한 분들은 찾아보시기 바랍니다.

텍스트


Cyber Bullying? 그기 뭐꼬?

이 사건이 터진 당시 심심이 팀은 아일랜드의 트래픽을 차단하는 초강수를 둘 수밖에 없었습니다. 더 이상의 사회 문제를 일으키면 안 된다는 판단이 있었기 때문이죠. 아일랜드 사회에서 가장 문제가 된 화두는 Cyber Bullying으로 생소한 용어지만, 쉽게 말하자면 사이버 폭력입니다. 결국 심심이가 온라인 상에서 특정 누군가에 대한 폭력을 행사했다는 소리인데, 대체 왜 이런 일이 발생했을까요? 초기 심심이는 사용자의 집단지성을 기반으로 성장해왔습니다. 결국 사용자들이 문장 하나하나를 심심이에게 가르쳐서 대화 패턴을 학습시켰고, 누적된 대화 패턴이 1억 건에 육박하는 지금 "아주 욕 잘하는 심심이"가 된 것입니다.

결국 누군가 욕을 가르쳤고 그 패턴을 학습한 심심이가 욕을 했다는 결론이 되는 것이죠. 채팅 봇의 관점에서 보자면 Micosoft의 Tay가 인종차별 논란으로 중지된 것과 비슷한 일이 심심이에서도 일어난 것이고 콘텐츠 플랫폼의 관점에서 보자면 페이스북에 자살 동영상이 올라오고, 유튜브에 성인 동영상이 올라오듯, 대화라는 텍스트 콘텐츠 기반 서비스에 욕설이 흘러넘치게 된 것입니다. 일명 "아일랜드 사태"이후 좀 더 확실하고 정확한 나쁜 말 필터링에 대한 필요성이 커졌고, Deep Bad Sentence Classifier를 구성하게 된 계기가 되었습니다.


그저 손 놓고 있었던 건 아니다

사실, 심심이에는 나쁜 말을 걸러내는 로직이 이미 오래전부터 작동하고 있었습니다. 사용자의 신고 데이터를 기반으로 사용자가 가르친 문장을 착한 말, 욕설, 야한 말 3종류의 점수를 산정해 관리하고 있었죠. 예를 들어 "안녕 심심아!"라는 문장은 착한 말 10, 욕설 0, 야한 말 0의 점수가 부여되고 반면에 "닥쳐!"라는 문장은 착한 말 0, 욕설 10, 야한 말 0의 점수가 부여되는 것입니다. 이 방법은 통계적인 접근에 기반을 두고 있었습니다. 간단하게 설명하자면 이전에 나쁜 말로 신고된 말 중에 이 패턴과 유사한 패턴이 얼마나 있는가로 점수를 판단하는 로직이었죠. 어찌보면 단순하지만 가장 확실한 방법으로 콘텐츠 질을 관리하고 있었습니다. 분명, 이 로직은 저 당시에도 정상적으로 작동하고 있었기에 저희 팀은 필터 성능에 문제가 있는지 면밀히 들여다보기 시작했습니다.


문제의 원인이 무엇인가

그간 쌓인 대화 로그들을 분석해본 결과 심심이 팀은 하나의 결론에 도달할 수 있었습니다.

"애매해서 판단할 수 없는 문장들이 문제의 핵심이다."

아주 당연한 소리일 수 있겠지만 구체적으로 어떤 케이스가 있는지 알아볼 필요가 있었고 그것을 해결하는 게 이번 프로젝트의 목표가 되었습니다. 심심이 팀에서 발견한 몇 가지 예를 들어보자면,

사용자 A : "히틀러 어떻게 생각해?" 

심심이 : "난 히틀러를 존경해 그는 위대한 지도자야"

혹은

"엄마 보고 싶어"와 "니 엄마 보고 싶어"

어떤가요? 이제 심심이 팀이 당면한 문제가 조금은 이해가 되시나요? 사용자 A와 심심이가 한 대화에는 단 하나의 욕설도 비속어도 없습니다. 하지만 문제가 될 만한 말인 것은 분명하죠. 사용자 A의 대화는 미국에서 매우 민감한 문제가 되었고, 실제로 Facebook 측에서 문제 삼았던 내용이기도 합니다.

두 번째 예시 역시 마찬가지입니다. 단 한 개의 욕설도 없지만 "니"라는 단어 하나로 뉘앙스가 180도 달라져버린 것을 알 수 있습니다. 이런 예시를 정말 수백 개, 수천 개 찾을 수 있습니다. 대화 패턴을 이용한 통계적 접근이 갖는 한계가 여기에 있었습니다.

"욕이 없는 나쁜 말" 혹은 "문맥상 나쁜 말" 이 가장 큰 문제였습니다.

그래서 심심이 개발팀은 "욕이 없는 나쁜 말 찾기"를 이번 프로젝트의 목표를 세웠습니다. 이 목표가 달성된다면 문맥을 포함한 나쁜 말을 찾는 것에 한 발짝 다가갈 수 있으니까요.


생각보다 쉽지 않다

이제 목표는 좀 명확해졌습니다. 딱 봐도 쉽지 않습니다. 하지만 거기에 추가로 제약조건이 있습니다. 심심이 팀에서 진행하는 NLP 연구분야에 가장 큰 걸림돌이 되는 것은 "언어의 장벽"입니다. 앞서 설명했듯이 전 세계 48개 언어로 서비스되는 심심이 특성상 NLP 연구가 잘 진행되어있는 영어를 기준으로 연구를 진행하는 것에는 한계가 있습니다. 게다가 심심이의 대부분 트래픽은 동아시아, 동남아시아, 남미 등 비영어권 국가에 치중되어 있습니다.

기존 NLP 연구와 심심이 개발팀이 당면한 문제를 CJK(Chinese - Japanese - Korean)를 예로 들어 간단하게 설명해드리겠습니다. 영어를 기반으로 한 NLP 연구는 띄어쓰기(white space)를 기준으로 진행되어 있습니다. 영어라는 언어 자체가 단어 중심으로 구성된 언어이니까요. 하지만 우리말은 띄어쓰기를 하지 않아도 말의 의미를 알 수 있거나, 띄어쓰기에 따라 의미가 달라지는 특성을 가지고 있습니다.

"아버지가방에들어가신다"

"아버지가 방에 들어가신다"

"아버지 가방에 들어가신다"

"아버지가 방에들어가신다"

"아버지 가방에들어가신다"

"아부지 가방에 들어가심"

"엄빠가 방에 들어가심"

"울 아빠 방에 들어가심ㅋㅋㅋㅋ"

위의 예에서 볼 수 있는 것과 같이 띄어쓰기에 따라 의미가 달라지고 굳이 띄어 쓰지 않아도 우리는 의미를 파악할 수 있는 경우가 있습니다. 게다가 심심이와 대화하면서 맞춤법을 딱딱 지키면서 대화하는 유저는 제로에 가깝습니다. 다들 휘갈기듯 심심이와 대화를 합니다. 온라인에서 만들어진 은어, 비속어, 오타까지 포함한다면 문제는 훨씬 복잡해집니다. 게다가 일본어, 중국어는 띄어쓰기가 존재하지 않죠.


정리하자면

앞에서 정리한 목표와 추가된 제약조건을 정리해보자면 아래와 같습니다.

"맞춤법과 띄어쓰기를 지키지 않고 오타가 있을 수도 있는 문장의 나쁜 말 여부를 검수해야 한다"

심심이 팀에서는 이런 목표와 제약조건에 맞는 Deep Learning Network를 학습시키는데 시간과 노력을 쏟았고, 95~99%의 정확도를 갖는 Deep Bad Sentence Network를 개발할 수 있었습니다.


NLP의 시작은 Word2Vec부터?

NLP 연구의 시작은 Sentence 혹은 Word를 Vector로 표현하는 것에서 출발합니다. 사실상 이것이 NLP의 가장 중요한 부분이라고 생각할 수 있습니다. 어떻게 Vectorize 하냐에 따라 결과가 판이해질 것이 분명하기 때문이죠. Deep Learning 프로젝트를 어느 정도 진행해본 경험이 있는 연구자라면 가장 쉽게 Word2Vec을 이용하는 방법을 생각할 것입니다. 저도 마찬가지로 Word2Vec을 떠올렸고 심심이 팀에서 보유한 모든 데이터셋을 이용해 Word2Vec을 진행했습니다. Sentence는 Word의 집합이고 Word2Vec의 결과물인 Word Vector를 이용해 Sentence를 Vectorize 할 수 있었습니다. 하지만 결과는 대실패였습니다. 학습이 거의 진행되지 않더군요. 이유가 무엇일까요?

Word2Vec은 기본적으로 White Space를 기준으로 발전해왔지만, CJK의 경우 White Space가 무의미 하거나 아예 없다

띄어쓰기가 제대로 되어있지않은 경우 제대로된 Word가 뽑히지 않으니 당연한 결과였을지도 모릅니다. Word2Vec을 사용하려면 결국 맞춤법을 보정해서 제대로된 Word를 구성한 뒤에 적용할 수 있다는 뜻이 됩니다. 다행히 이전부터 한국어 NLP를 연구해온 분들 덕분에 KoNLPy와 같은 훌륭한 형태소 분석기를 적용하면 됩니다. 형태소 분석이 된다는 것은 띄어쓰기를 보정 할 수 있다는 뜻이 되니까요!

하지만, 81개 언어를 대상으로 서비스하는 심심이 팀에서 '형태소 분석'이라는 한글 전용 전처리 방법을 선택할 수는 없었습니다. 좀 더 일반화되고 보편적인 방법이 필요했습니다.


단순하고 직관적으로 접근해보자

틀을 깨는 접근이 필요했습니다. 기존의 방식을 그대로 적용할 수 없었기에 본질부터 다시 접근했습니다. Word2Vec은 Word의 의미를 살리기위한 방법입니다. 비슷한 의미의 Word를 비슷한 Vector로 나타내기 때문이죠. 이 프로젝트의 목표에 과연 Word의 의미가 과연 중요한가에 대해 다시 한 번 생각해봤습니다. 기본적으로 Text Classification 문제이기 때문에 좀 더 단순하게 접근해보자고 생각했습니다.

사진에서 얼굴을 찾아내듯이 문장 속에서 욕이 될 수 있는 패턴을 찾아내보자

심심이 팀이 가지고 있는 가장 강력한 무기는 데이터 셋입니다. 누적된 대화패턴이 1억 건, 하루에 대화가 천만 건씩 이루어지기 때문에 엄청나게 다양한 대화 패턴을 알 수 있습니다. 이 정도의 데이터 셋이라면 단순한 접근이 오히려 맞다는 생각이 들었습니다.

결국 Word 하나 하나에 의미를 둔다기 보다는 의미를 무시하고 Character의 분포 패턴을 이용해 욕설로 보이는 문장을 찾아내자는 결정이었습니다. 이 방법으로 가능성을 먼저 판단한 뒤에 의미를 살리기 위한 연구를 추가로 진행하기로 했습니다. 결과적으로 형태소 분석, Word2Vec같은 Word의 의미에 중점을 두는 복잡한 전처리 과정을 빼고 단순하게 접근할 수 있었습니다.


Word가 아닌 Character를 중심으로

심심이 팀은 Word가 아닌 Character 중심의 연구를 진행했습니다. Character를 Vector로 표현 할 방법을 고민한 결과 한 가지 방법을 생각해냈습니다. 바로 Unicode였죠. 컴퓨터는 Unicode Character를 Bit Array 형태로 관리합니다. Unicode를 이용하면 Character 하나가 0과 1로 나타내진다는 뜻이죠. 엄밀히 말하자면 0과 1로 나타낸게 아니고 원래부터 0과 1이었습니다. 이 값을 이용할 것입니다. 아주 직관적이면서도 간단한 접근법입니다. Character 1개를 16개의 Bit Array로 바꾸고, Sentence를 스캔하는 Window Size는 20으로 정했습니다. Network의 Input은 16 * 20 = 320개의 0과 1이 되는 것이죠. 데이터셋은 Zero Padding을 했고 줄바꿈/띄어쓰기는 아예 제거했습니다.

결국,

Character의 종류와 나타나는 횟수, 그리고 상대적인 위치로 나쁜 말인지 판별한다

라는 결론에 도달한 것이고, 첫번째 학습 결과는 85%의 Accuracy로 꽤 성공적이었습니다.


Accuracy를 높혀라!

여기까지 읽은 Deep Learning 연구자라면 "그래 이제 시작이네"라는 말이 절로 나올 것입니다. 그렇습니다. 사실 여기서부터가 시작입니다. 1%씩 정확도를 높이면서 기술을 완성단계까지 끌어올려야합니다. Character를 이용한 시도에서 가능성을 발견한 심심이 팀은 정확도를 높이는데 모든 노력을 기울였습니다. 수많은 시도와 실패 끝에 정확도를 높이는데 성공한 방법들은 아래와 같습니다. Deep Learning 연구를 진행하는 팁이라고 할 수도 있고, 지루한 반복의 결과물이라고도 할 수 있습니다.

  1. 띄어쓰기를 포함하도록 학습시킨다
  2. Zero Padding이 아닌 Nine Padding을 한다
  3. 짧은 말일 경우 Center Align 시킨다
  4. Hyperparmeter를 좀 더 세밀하게 조정한다

하나씩 하나씩 심심이 팀이 쌓은 노하우와 관련 히스토리를 함께 설명하겠습니다.


1. 애증의 존재, 띄어쓰기

Sentence Vectorize에서 엄청나게 고통받았기 때문에 무심코 제거해버린 띄어쓰기가 Accuracy를 떨어뜨리는 원인이라는 것을 발견했습니다. 재미있는 예시를 하나 들어보자면 "수박 씨 발라먹어"입니다. 재미있죠? 띄어쓰기를 제거한 순간 욕이 됩니다. 결과적으로 저 말은 욕이라고 판별을 하게끔 학습이 됐지만, 이런식의 유사한 경우들이 많이 있었습니다. 결국 띄어쓰기가 되어있지 않더라도, 완전히 무시해서는 안된다는 결론에 다다랐고 심심이 팀은 다시 띄어쓰기를 포함하도록 학습을 시킬 수 밖에 없었습니다. 띄어쓰기를 포함해서 학습시킨 결과는 성공적이었습니다. 85%에서 머물러있던 정확도가 단숨에 92%까지 올라갔으니까요.

2. Zero Padding이 아닌 Nine Padding을 하자!

Deep Learning 연구에서 흔히 쓰이는 Padding은 Zero Padding입니다. Google의 알파고도 Zero Padding을 하고 제가 이전에 진행했었던 Image Classification이나 Object Classification 문제들도 모두 Zero Padding을 사용했었습니다. 실제로 대부분의 Network 들이 Zero Padding을 사용하고 아직까지 결과는 아주 훌륭했죠. 하지만, Unicode를 이용한 Text Classification 문제에서는 Zero Padding이 별로 도움이 되지 않았습니다. 저희 팀에서는 "Bit Array로 표현된 Sentence에 0이 굉장히 많다"라는 것과 "생각보다 20자 이하의 문장들이 매우 많아 Zero Padding으로 0이 많이 채워진다"라는 사실을 발견했습니다. 이 두 이야기를 정리하면 사실상 320개의 Input이 대부분 0이라는 것으로 결론이 납니다. 당연히, Network 학습에 악영향을 미칠 수 밖에 없겠죠. 그래서 생각한 아이디어가 Nine Padding이었습니다. Padding을 할 때 0이 아닌 9로 Padding을 한다는 아이디어였습니다. 0과 1에서 가장 멀리있는 숫자로 Padding을 하고 확실하게 White Space와 문장의 시작과 끝을 구분할 수 있게 바꾸었습니다. 이 효과는 굉장했습니다. 이 과정을 통해 96%의 정확도를 달성할 수 있었고 꽤 가시적인 성과를 냈다는 내부적인 평가가 있었죠.

3. Window Size보다 많이 작다?

연구를 진행하면서 가장 많이 한 일은 데이터셋을 수집하고 수정하는 일이었습니다. 그 과정에서 발견한 점은 채팅 봇을 사용하는 사용자들이 아주 짧은 문장들을 입력한다는 것이었습니다. 스스로를 돌이켜봐도 긴 메세지보다는 짧은 메세지를 여러번 보내는 경우가 많다는 것을 깨달았죠. 이런 데이터 셋의 특성이 별 문제 없을 것이라는 처음의 생각과 달리 짧은 메세지는 학습에 지대한 영향을 미쳤습니다. 심심이 팀은 문장이 짧더라도 Network 학습에 영향이 덜 미치도록 이 문제를 해결해야만 했습니다. Window Size를 줄이는 방법도 있었지만, 한글에서 20자는 충분한 길이지만 영어의 경우 20 글자는 단어 세 네개 밖에 되지 않습니다. 오히려 Window Size를 늘려야하는 상황이었죠. 여러가지 시도와 변화를 거듭한 결과 문장을 왼쪽 정렬이 아닌 가운제 정렬로 데이터셋을 구성하는게 효과적이라는 것을 찾아냈습니다. 결론적으로 살펴보자면 각 Layer에서 왼쪽 Node들만 학습되는 것 보다는 가운데에 있는 Node들이 학습되는게 더 고른 결과를 나타낸다고 생각할 수 있습니다.

4. 좋은 학습 환경을 조성해야한다

Network를 구성하는 것은 좋은 교과서를 만드는 것이고 Hyperparameter를 수정하는 것은 좋은 학습 환경을 만들어주는 것입니다. 같은 교과서를 가지고도 공부하는 환경이 독서실, 노래방으로 달라질 수 있듯이 Hyperparameter 튜닝은 Deep Learning 연구에 있어서 굉장히 중요한 역할을 합니다. 수많은 Network 변경을 통해 성능이 괜찮은 Network 구성을 찾은 뒤에는 Running Rate, Batch Size, Test Data Set의 수, Validation Data Set의 수, Optimizer의 종류 등 Hyperparameter 들을 아주아주 세밀하게 수정해갔습니다. 지루한 이 과정만이 학습 네트워크의 성능을 최대한 끌어올릴 수 있는 유일한 방법이었습니다.


앞으로는?

Deep Learning 연구는 꾸준함이 필요합니다. 너무 수학적으로만 접근해도 안되는 분야이며 단기간에 집중한다고 성과를 내는 분야도 아닌 것 같습니다. 지루하지만 수많은 시도를 해보고 조금씩 변해가는 결과물을 세밀하게 관찰해야만 합니다. 이번 연구에서는 단 한개의 문장만 보고 나쁜말인지 여부를 판단하는 Network를 구성했습니다. 저희 팀내에 예정되어 있는 다음 연구는 2가지 입니다. 첫 번째는 이번 연구의 성과를 한글뿐 아니라 심심이가 지원하는 81개의 언어로 점점 늘려가는 것이고, 두 번째는 문맥에 대한 파악입니다. 전체 문맥을 파악하는 일은 심심이 팀에서 이루고 싶었던 오랜 숙원입니다. 이미 해당 연구를 위한 데이터셋 마련에 박차를 가하고 있으며 방법론에 대한 구상을 진행하고 있습니다. 앞으로 더 좋은 기술을 완성하고 서비스에 적용할 수 있도록 응원해주시면 감사하겠습니다.