카톡 데이터는 어떻게 정제할 수 있을까? - Dialog-BERT 만들기 1편

Preparation for Korean Dialog Data (How to build Dialog-BERT: Part #1)

구상준 | 2019년 07월 04일

한국어 모델을 만드는 것은 가슴 떨리는 일입니다. 최근 저희는 야심차게 BERT를 활용한 한국어 모델을 만들어보고자 하였습니다. 그러나 첫 술에 배부를 수는 없는 법. 도도한 BERT의 학습을 위해서는 데이터를 미리 예쁘게 준비하는 작업이 필요하였습니다. 이 포스트에서는 어떻게 데이터를 전처리하였는지에 대해서 다뤄보고자 합니다.

이 글은 한국어 NLP에 관심이 있는 초심자를 대상으로 작성하였으니 편하게 보시면 되겠습니다.

Table of Contents

  1. 들어가며
    1. 왜 전처리를 해야할까요?
    2. 전처리를 보는 관점: 근원과 데이터
    3. 예시: 대화 데이터
  2. 사용자 단위 전처리
    1. 왜 해야할까요: 사람들 개개인의 특성 - 개성과 일탈 사이
    2. 얼마나 어떻게 해야할까요: 분포 조사
  3. 대화 세션 단위 전처리
    1. 왜 해야할까요: 사람들의 메신저 패턴에 대해서
    2. 얼마나 어떻게 해야할까요: 분석을 위한 기준 설정
      1. 기준 모델의 구성과 훈련
      2. 실제 적용 예시: 최소 시간 간격 정하기
      3. 기준 모델의 피드백
  4. 문장 단위 전처리
    1. 왜 해야할까요: 한글을 ‘똑바로’ 쓰는 것의 어려움
    2. 얼마까지 어떻게 해야할까요: 정규화하기
      1. 걸러내기
      2. 바꾸기
      3. 정규화의 피드백
  5. 마무리하며

1. 들어가며

왜 전처리를 해야할까요?

왜 전처리를 해야할까요? 라는 질문은 물어보는게 이상합니다. 데이터를 조금이라도 만지작 해본 사람들한테는 마치 ‘왜 우리는 쌀을 씹어먹지 않고 밥을 지어서 먹을까요?’를 물어보는 것과 같습니다. 그렇지만 생소하신 분들도 있으실테니 짤막하게나마 이야기해보도록 하겠습니다.

우리에게 모이는 소위 데이터라는 것들은 실제 세계에서 나오는 무언가입니다. 창문 밖의 바로 그 광경, 그 곳에서 우리가 친구에게 말하는 바로 그 목소리, 그 목소리가 담은 그 이야기, 이런 것들이 실제 데이터 (real data)를 이룹니다. 당연히 이런 실제 데이터는 어떤 실험이나 수학적 모델에서 만들어진 것이 아니라서 그 형태가 제멋대로입니다. 제멋대로 생긴 데이터들을 바로 다루게 되면 데이터 하나하나의 개성 때문에 데이터들 전체가 가지고 있는 특성이나 경향 등등을 파악하기 어렵습니다. 따라서 데이터를 다루기 이전에 이런 제멋대로 생긴 데이터들을 ‘정리’해줄 필요가 있습니다. 이것이 전처리 작업입니다. 사진 같은 경우는 그 크기를 똑같게 맞춰준다거나, 소리 같은 경우는 소리의 길이와 크기를 같게 맞춰준다거나.

실제 대화 데이터를 다루는 경우에는 이런 전처리 과정이 정말 정말 중요합니다. 가지각색의 사람들이 제각각으로 말하기 때문입니다. 원래 데이터들은 ‘더럽다’는 말로도 표현하기 어려울 정도입니다. 이런 ‘더러운’ 데이터들을 가지런히 정리해야만 그 사람들이 정말로 무슨 말을 하려고 했었고, 그런 말에 대해서 또 어떤 말을 했는지를 판단할 수가 있습니다.

전처리를 보는 관점: 근원과 데이터

전처리의 대상으로는 크게 2가지를 들어볼 수 있습니다. 데이터의 근원(Source-level)과 데이터 자체(Data-level), 이 두 가지입니다. 이해를 돕기 위해서 사진의 예시를 들어보겠습니다. 여러분이 갤러리의 큐레이터가 되었다고 가정해봅시다. 갤러리의 기획회의에서 “이번 달 갤러리의 메인 테마는 ‘노을’입니다.”라는 결론이 나왔다고도 가정해봅시다. 이제 여러분은 사진사들을 고용해서 전국 각지의 아름다운 노을 사진들을 수집해야합니다. 수집하다보니 어떤 사진은 아주 괜찮았지만 또 어떤 사진들은 아주 엉터리였습니다. 여러분이 이 상황에서 할 수 있는 방법은 크게 두 가지가 있습니다.

  1. 각 사진사들이 찍은 사진을 비교하여 엉터리 사진을 많이 찍은 사진사의 사진들을 제쳐둔다: ‘사진도 잘 찍는 사람이 잘 찍지’라고 생각하면서. 이 방법으로 사진들을 걸러내었다면 여러분은 근원 단위의 전처리를 한 것입니다. 이 방법은 데이터의 근원들에 대한 격차가 크고 (그래서 데이터의 품질 차이가 확연하게 드러나는 경우), 데이터의 양이 방대할 때 사용할 수 있는 방법입니다. 실제로 크라우드 태스크 플랫폼 (Amazon Mechanical Turk크라우드웍스) 등에서는 작업을 서투르게 하는 분들을 걸러내는 장치가 있습니다. ‘일도 잘하는 사람이 잘하지’ 전략입니다. 단점으로는 상대적으로 정상적인 데이터도 근원의 상태가 메롱하다는 이유로 무고하게 배제되는 경우가 있을 수 있다는 것이겠죠.

  2. 사진사들이 찍은 사진을 사진사의 구별없이 통일된 기준으로 갈무리한다: 어쨌든 사진이 중요하지, 사진사가 중요한 것은 아니니까요? 이 방법으로 불량한 사진들을 걸러내었다면 여러분은 데이터 단위의 전처리를 한 것입니다. 이 방법은 위와 반대로 데이터의 근원들의 격차가 작은 경우에 사용될 수 있습니다. 데이터의 무결성을 담보하는 단계는 이 단계이기 때문에 대부분의 전처리 과정은 데이터 단위의 전처리를 포함합니다. 그러나 처음부터 대용량의 데이터를 다루게 되면 이 과정은 매우 많은 자원을 요구할 수 있기 때문에 기준을 어떻게 잡을지가 중요합니다.

Our Target: Source or Data?

예시: 대화 데이터

이제 저희가 모은 대화 데이터에 대해서 살펴보도록 하겠습니다. 저희가 다루는 대화 데이터는 연애의 과학 어플리케이션을 통하여 수집되었습니다. 연애의 과학 사용자들께서는 분석을 위해서 친구, 연인간의 메신저 대화 내용을 업로드하여 주셨습니다. 이렇게 해서 모인 데이터는 대략 128억여 문장이 되었습니다(!). 이 문장을 저희가 개략적으로 관찰한 바는 다음과 같습니다.

따라서 여기서 얻을 수 있는 결론은 대화 데이터의 경우는 위의 두 관점 모두 충분히 고려되어야 한다는 점입니다. 기상 관측 데이터과 같이 실제 환경이나 상황 데이터와는 다르게 대화 데이터의 경우는 ‘누구’한테서 나왔냐가 ‘어떻게’ 생겼냐에 밀접하게 연결되어 있고, 이를 감안해도 데이터가 불규칙적입니다: ‘더러운/이상한’ 말을 쓰는 사람들은 상대적으로 그 사람이 말하는 모든 말이 ‘더러울/이상할’ 가능성이 높습니다. 그렇다고 (당연하겠지만) 다른 사람들이 언제나 맞춤법에 맞춰서 규칙적으로 말하지는 않습니다. 그래서 데이터 수준에서의 전처리 역시 간과되어서는 안됩니다.

다음 세션들에서는 사용자 단위에서의 전처리와 데이터 단위에서의 전처리하는 법에 대해서 이야기해보도록 하겠습니다. 아울러 데이터 수준에서의 전처리는 또 세션 단위에서의 전처리와 문장 단위에서의 전처리로 살펴보도록 하겠습니다.

2. 사용자 단위 전처리

왜 해야할까요: 사람들 개개인의 특성 - 개성과 일탈 사이

대화 데이터는 실제 사람들의 개개인의 특성이 크게 두드러지는 데이터입니다. 우리가 어떤 개성이 있는 사람을 잘못되었다고 말하는 것은 참 옳지 못한 일입니다만 그런 개성이 너무 심해서 일탈처럼 나타나는 경우는 문제가 됩니다. 이러한 일탈중인 사용자들을 모델에 훈련하기 전에 배제함으로써 모델을 효과적으로 훈련시킬 수 있다고 생각했습니다. 아울러 사용자 단위에서의 전처리가 선행된다면, 실제로 각 사용자의 문장 하나하나를 걸러내는 작업의 양을 미리 줄이는 효과도 기대하였습니다. 위의 큐레이터가 서투른 사진사들을 미리미리 정리하듯이 말입니다.

얼마나 어떻게 해야할까요: 분포 조사

이상한 사용자들을 걸러내기 위해서 저희는 다음과 같은 기준을 내부적으로 마련하였습니다

총 105만 사용자들에 대해서, 각 사용자들의 발화 문장 1000개에 대해 위의 기준을 백분위에 대해서 측정하였습니다.

사용자 필터링 백분위

저희들은 각 기준들에 대해서 변곡점(그림의 화살표)들을 확인하였습니다. 이 변곡점에 따라서 필터링 기준을 마련하는 것이 합당하다고 생각하였습니다.

일련의 제거 과정을 통하여 105만명의 사용자 중에서 66만여명만을 남길 수 있었습니다.

3. 대화 세션 단위 전처리

왜 해야할까요: 사람들의 메신저 패턴에 대해서

여러분들이 친구들과 카카오톡을 하는 경우를 생각해봅시다. 이 경우, 소셜 메신저 앱을 쓰는 사람들은 실제 대화를 하는 사람들과 다른 대화 양상을 보입니다.

얼마나 어떻게 해야할까요: 분석을 위한 기준 설정

위의 문장 단위 전처리와 마찬가지로 세션 단위에서도 전처리의 수준을 결정하는 것은 매우 중요한 일입니다. 아니 오히려 더 어려운 일이라고 볼 수 있습니다. 문장 단위의 전처리의 경우는 일단 어떤 전처리 루틴을 실행할 지를 결정하게 되면, 정답이 정해져있기 때문에 크게 골치 아플 일이 없었지만 세션 단위의 경우는 정해진 정답이 없다는 것이 문제입니다. 1분 안에 발화한 문장 중 몇 문장이 섞인 것까지를 받아들일지? 대화 사이의 최대 시간 간격은 몇 분으로 잡아야할지?

가장 확실한 방법은 정한 설정에 대해서 정제한 데이터로 원하는 모델에 훈련시켜서 그 모델의 성능을 측정하는 것입니다. 하지만 전체 모델을 훈련시켜서 모델 훈련을 위한 정제 설정을 확인하는 것은 본말이 전도된 것이고, 상식적으로도 며칠, 몇 달이 걸리는 전체 모델을 미리 훈련시킨다는 것은 말이 되지 않습니다. 좋은 차선책은 전체 모델보다는 한참 작지만 정제가 잘 되었는지를 보여줄 수 있는 기준 모델을 만드는 것입니다. 이 경우에는 앞 문장과 뒷 문장이 자연스럽게 이어지도록 정제하는 것이 목표이기 때문에 앞 문장과 뒷 문장의 자연스러움도/이어짐도를 수치화할 수 있는 모델이면 되겠습니다.

기준 모델의 구성과 훈련

Dual LSTM (Ryan Lowe et al., 2015)

위에서도 말했듯이 앞 문장과 뒷 문장의 이어짐도를 수치화할 수 있는 모델이 필요합니다. 많은 대화 모델 구조가 있지만 저희는 정제를 위해서 Dual LSTM Dense Model(그림 참조)을 사용하였습니다. 이 모델은 원래는 응답을 찾아주는 모델(Retrieval Model)을 구현하기 위해 훈련되었습니다. 이 구조에서 각 대화 턴에 대한 발화와 대응되는 응답은 각각 LSTM으로 인코딩됩니다. 각 문장의 마지막 LSTM 셀에서의 결과는 이진 매칭 모델로 넘어갑니다. 이 모델은 두 문장이 실제로 대응되는 문장이면 1, 그렇지 않으면 0이 나오도록 훈련이 됩니다.

이 모델에서의 설정이나 모델을 훈련시키기 위한 데이터 필터링에 대한 이슈가 많이 있었지만 간략하게 키 포인트만 뽑아보면 다음과 같습니다.

훈련은 총 4억 5천만 대화 쌍을 이용하여 진행되었으며, 훈련 루틴은 Tensor2Tensor Framework를 이용하여 구현되었습니다. 저희는 본 모델을 Reply Matching Model (RMM)이라고 붙였습니다. 멋있는 대화 처리 프레임워크 핑퐁에도 들어가 있죠.

실제 적용 예시: 최소 시간 간격 정하기

위에서 대화 사이의 시간 간격이 문제가 된다는 말씀을 드렸습니다. 저희는 위의 RMM 모델을 통하여 대화 사이의 시간 간격에 대한 정제 조건을 정하고자 하였습니다. 전체 사용자 중 5000명을 무작위로 선정하여 5000명의 발화 파일에서 연속된 문장에 대하여 각각 적용하였을 때, 총 619만여 쌍을 얻을 수 있었습니다. 쿼리(이 경우는 대화 쌍의 첫 문장)의 특정 길이 (10글자, 16글자)에 대해서 해당 쌍들의 RMM 수치 평균과, 누적 쌍수는 다음과 같습니다 (보여드리기 위해 임의로 특정 길이 2가지만 선정하였지만 실제로 많은 종류의 길이에 대해서 성능을 뽑았습니다.)

시간 간격에 따른 정량적 수치

표를 보면 시간 간격 0분과 1분 사이에 확연한 RMM 성능의 변화가 있는 것을 알 수 있습니다. 위에서도 말했지만 시간 간격이 0분(1분 미만)인 경우에는 서로 말이 맞물리지 않고 충돌되는 경우가 많기 때문입니다. 실제로 10분이 될 때까지는 시간 간격이 길수록 RMM 성능이 올라가는 것을 관찰할 수 있습니다. 하지만 10분이 넘어가면 RMM 값이 줄어드는 것을 볼 수 있는데, 이것은 반대로 시간이 너무 오래 걸려서 대화가 단절된 것을 뜻합니다.

그러면 최소 시간 간격을 10분으로 하면 안될까요? 누적 쌍 수를 보면 0분이 무려 43%-49%, 1분은 약 23-30%를 차지하는 것을 볼 수 있습니다. 2분 미만인 것들만 날려도 2/3은 날아간다는 뜻입니다. 모델을 학습시키기 위해서는 데이터가 많이 남아있을수록 유리하기 때문에 무턱대고 RMM 수치로 끊을 수 없는 노릇입니다.

이와 같이 정제 작업에는 늘 기회비용(trade-off)가 있기 마련입니다. 개발자의 입장에서 그 균형점을 찾는 것은 중요하죠. 고심 끝에 저희는 0분, 즉 1분 미만인 것들만 날리기로 결정하였습니다. 1분 미만인 것들은 확연하게 RMM 수치를 떨어뜨리는 것을 볼 수 있었으며 반대로 1분-2분인것들은 그것을 정제한다고 했을 때 얻는 이득이 잃는 손해에 비해 매우 작게 여겨졌습니다. 또한 상식적으로 1분 정도의 휴지기가 있으면 서로 말이 완전히 어긋나게 충돌되는 경우는 없다고 판단하였기 때문입니다.

기준 모델의 피드백

원 모델 대신 기준 모델을 활용하는 경우 부작용이 없을까요? 가장 큰 부작용은 훈련된 모델에 대한 편향(Bias)이 생긴다는 것이고, 그 편향은 기준 모델의 크기가 작으면 작을수록, 그리고 모델 훈련이 진행되면 될수록 심해집니다.

저희 RMM 모델에도 편향이 있었습니다. 모델을 훈련할 때 길이가 3 이하인 문장을 배제하였기 때문에 짧은 문장에 대해서는 제대로 훈련이 되지 않았으며, 아울러 정답과 오답의 비율이 1:4 인데 대부분의 대화는 길이가 짧기 때문에 길이가 긴 문장인 경우 정답일 가능성이 비약적으로 상승했습니다. 이것은 대상 문장의 길이가 길면 길수록 RMM 점수가 높아지는 현상을 야기했습니다.

이런 편향이 반드시 나쁜 것은 아닙니다. 어쨌든 모델을 잘 만들었다면 우리가 원하는 답은 대체적으로 기준 모델도 호의적으로 평가하도록 편향될 가능성이 높기 때문입니다. 다만 개발자는 그 편향성이 일반적인 데이터의 경향성이나 상식에 반하는 결과를 야기하지 않았는지 조심해야합니다.

4. 문장 단위 전처리

왜 해야할까요: 한글을 ‘똑바로’ 쓰는 것의 어려움

여러분들은 아마 어렸을 때부터 한글이 얼마나 과학적인 글자인지, 그리고 얼마나 우수한지에 대해서 못이 박히도록 들었을 것입니다. 어떤 분들은 찌야찌야족 이야기를 덧붙이시기도 하실 것입니다. 맞습니다. 한글은 (적어도 사람이 쓰기에는) 과학적이고 우수한 문자체계입니다.

그러나 문제가 있습니다. 한글은 그냥 쓰기는 쉽지만, ‘똑바로’ 쓰기가 어렵다는 점입니다. 맞춤법에 맞춰서 쓴다고는 하지만 사실 100점짜리 맞춤법에 맞는 문장을 쓰는 것은 거의 불가능에 가깝습니다. 그 이유는 아주 다양합니다.

예를 하나 들어볼까요? 자기야 나 오늘 내일 휴가 썼어 그 다음날에는 시험 봐 라는 문장을 생각해봅시다. 예시로 든 문장들 모두 충분히 나올 수 있는 말입니다. 우리는 이 세 문장 사이에 미묘한 뉘앙스를 빼면 거의 뜻에서 차이가 없다 받아들이고 또 비슷하게 답합니다.

그러나 컴퓨터의 입장에서는 세 문장은 서로 다른 단어들로 구성된 문장들이기 때문에 다른 문장입니다. 물론 모델을 잘 훈련시키면 세 문장이 비슷한 뜻을 가지고 있다는 것은 파악할 수 있게 되겠죠. 그러나 그 훈련과정은 상당히 고될 것입니다 (= 수렴이 잘 되지 않을 것입니다). 우리가 이런 문장들을 같게 만들어줄 수 있다면 컴퓨터님의 고생을 덜어주고 더 멋있는 모델을 만들어낼 수 있을 것입니다.

얼마까지 어떻게 해야할까요: 정규화하기

한글 데이터 전처리를 하는 것에 있어서 가장 중요한 질문은 전처리를 ‘어떻게’ 할지가 아니라 ‘얼마나’ 할지입니다. 왜냐하면 위에서도 설명했지만 한글을 쓰는 사람들은 정말로 제각각으로 쓰기 때문에 처음부터 모두를 만족시키는 전처리 모듈을 만들어내는 것이 어렵기 때문입니다. 아니, 사실 불가능하다는 표현이 더 맞겠습니다. 그러므로 어느 정도 만족할만한 지점을 찾아서 ‘타협’하는 지혜가 필요합니다.

물론 말은 쉬운 법이라 실제로 그 지점을 찾는 것은 정말 어려운 일입니다. 너무 전처리를 적게 하면 나중에 모델을 만들 때 별로 도움이 되지 않을 것입니다. 그렇다고 전처리를 많이 하면 자원이 많이 들어갈 뿐더러 완벽하게 처리하기가 어렵고 아울러 모델의 표현력이 약해집니다: 전처리가 많이 된 데이터로 학습시킨 모델은 실제 데이터를 다룰 때 성능이 떨어집니다. 전처리 기준을 애매하게 잡으면 일관성이 떨어질 것이고 반대로 너무 빡빡하게 잡으면 소중한 데이터들을 많이 소실할 가능성이 있습니다.

그 지점을 찾는 노하우를 조금 알려드리도록 하겠습니다.

전처리 과정은 여러가지 기능으로 구성됩니다. 그 중에서 토큰화(Tokenization)과 띄어쓰기(Spacing)에 대해서는 다른 포스트에서 다루고, 이 포스트에서는 정규화(Normalization)에 대해서만 다룹니다.

걸러내기

걸러내기(Filtering)은 말 그대로 데이터를 걸러내는 것, 불량한 데이터를 지워버리는 것입니다. 이러한 데이터로는 다음과 같은 것들이 있습니다.

바꾸기

사실 바꾸기(Replacing)라는 말보다는 ‘‘풀어쓰기 (Paraphrasing)’‘가 더 적합한 표현일 수 있습니다. 사람들은 서로 같은 대상에 대해서 다른 표현을 사용합니다. 이런 표현들을 하나로 통일시켜 주는 작업을 의미합니다. 뜻을 분명히 가지고 있지만 표현의 형태가 제각각이라 통일시키는 것이 모델의 훈련에 더 좋은 경우로 이러한 데이터로는 다음과 같은 것들이 있습니다.

이런 바꾸기의 특수한 경우가 교정입니다. 교정(Correcting)은 전처리의 의도가 담긴 바꾸기라고 보면 되겠습니다. 즉, 오인될 수 있는 형태의 문장을 의도가 명확한 표현의 문장으로 바꾸는 것입니다. 예를 들어, “나 거기 나갓어”라고 쓴 사람의 의도한 바는 “나 거기 나갔어”일 것입니다. 원치 않는 표현이 데이터에 많이 남아있으면 비일관적이기 때문에 수정해주는 것이 바람직합니다. 다만 항상 과수정이 될 가능성이 있으므로 조심하여야 합니다.

정규화의 피드백

정규화를 할 때 주의해야할 점 중의 하나는 지나치게 정규화를 하면 안된다는 것입니다. 문장 하나 하나는 뜻을 가지고 있는데 지나친 정규화는 화자가 의도한 바를 왜곡합니다. 또 주의해야할 점은 (이쯤되면 다들 눈치채셨겠지만) 부족하게 정규화를 하면 곤란하다는 것입니다. 물론 지나친 정규화보다야 낫겠습니다만, 어설픈 정규화를 거치게 되면 교정되어야할 문장이 교정되지 않으니 궁극적으로 모델 훈련에 악영향을 미치게 됩니다. 이를 막기 위에서는 필수적으로 정규화되어야 할 패턴들의 리스팅이 필요합니다.

5. 마무리하며

지금까지 저희가 한국어 모델을 학습하기 위해서 준비했던 과정을 간단하게 소개하였습니다. 요즘에는 계산 자원을 확보하는 것이 어려운 일이 아니기 때문에, “그냥 데이터가 많으면 장땡이지!” 라는 생각을 가지기 쉽습니다. 그렇지만 저희는 그게 좋은 태도는 아닌 것 같습니다.

첫째로 계산 자원은 어쨌든 한정적입니다. 여러분이 어느 곳에 계산 자원을 투입하면 다른 모델에는 그 계산 자원을 투입하지 못합니다. 여러분들은 다 멋있는 모델을 만들 수 있는데, 모델 한 개 돌리는데만 몇 주일이 걸린다면 얼마나 슬픈 일일까요? 두번째로 아웃라이어가 많은 데이터는 결코 효과적으로 학습되지 못합니다. 이것은 기계학습이 처음 나왔던 70-80년대부터 제기되었던 문제입니다. 기껏 코드를 돌렸는데 수렴이 안 되면 또 얼마나 슬플까요?

그렇기 때문에 전처리는 필요합니다. 가장 중요한 것은 균형을 찾는 것이고요. 이러한 균형은 탄탄한 분석으로부터 시작하는 것이겠지요.