amos • Jaeyong Jeong, ML Engineer
안녕하세요 채널톡 AI팀 아모스입니다! 이번 포스팅에서는 사용자의 입력의 일부만 주어졌을 때, 사용자가 어떤 말을 하고 싶은지 예측하는 문장 자동완성을 만드는 방법과 채널톡에 적용하는 과정에서의 성능을 개선한 방법을 공유하고자 합니다.
자동완성이라는 단어를 다들 한 번쯤은 들어 보셨을 거라고 생각합니다. 우리는 이미 자동완성을 사용해왔습니다(아래 예시 그림을 보면 아마도 한 번에 이해할 수 있습니다). 일상에서 사용되고 있는 자동완성의 활용 사례들을 생각해 본다면, 검색을 할 때 단어의 일부만 입력했는데 내가 말하고 싶은 단어가 등장하거나 메일을 보낼 때도 몇 단어만 입력을 했는데 내가 말하고 싶은 문장이 등장한 경험을 한 번쯤 해봤을 것입니다. 이처럼, 자동완성은 몇 개의 글자로 나머지 부분을 예측하는 기능을 말합니다. 이번 포스팅에서는 몇 개의 단어가 주어지면 다음에 나타날 단어들을 예측하는 문장의 자동완성에 대해서 공유를 하고자 합니다.
구글의 2021년도의 리포트의 내용 중에서 문장의 자동완성의 필요성을 잘 보여주는 내용이 있었습니다. 구글의 smart compose라는 메일 자동완성 기능을 통해서 매주 10억 개의 문자를 입력하지 않도록 하였다고 합니다. 또한, 2017년도의 보낸 답장의 12%는 자동완성을 사용했다고 합니다. 이러한 효과가 상담에서도 적용된다면, 상담사가 짧은 시간에 더 많은 상담을 수행할 수 있도록 도울 수 있을 것입니다.
자동완성을 만들기 위해서 아래의 세 가지 내용에 대해서 정리되어야 합니다.
다음 단어를 예측하는 방법?
문장을 나누는 단위를 어떻게 정할지?
어떤 구조로 동작할 것인가?
각 과정을 어떻게 정리 했는지 설명드리겠습니다.
다음 단어를 예측하기 위해서, N-gram이라는 방식을 사용합니다. 이전의 N-1개의 단어들을 보고 다음의 N 번째 단어를 예측하는 방법을 말합니다. 아래는 그림은 4-gram에 대한 예시입니다. I want to라는 3개의 단어를 보고, 4번째 단어인 read를 예측하는 그림입니다.
N-gram이 다음 단어를 예측하는 방법은 기본적으로 단어의 빈도수를 기반으로 합니다. 수식으로 표현하면 다음과 같이 표현이 가능합니다.
만약, “안녕하세요”라는 단어가 주어질 때 다음에 “저는”이라는 단어가 등장할 확률을 구한다면 아래와 같은 방식을 사용해서 구할 수 있습니다.
“안녕하세요”라는 단어 뒤에 다양한 단어들이 등장할 수 있는데, 그중에서 가장 확률이 높은 단어를 선택하는 방법으로 다음 단어를 예측하게 됩니다(아래 그림 참고).
다음 단어를 예측한다고 했을 때, 아마도 GPT와 같은 신경망을 기반의 생성 모델을 대부분 떠올릴 것입니다. GPT를 대신하여 N-gram을 사용한 이유로는 신경망 기반의 방법 다음과 같은 문제점들이 있기 때문입니다.
모델의 크기가 커지면 연산량이 많아져서, 요청이 많거나 실시간 응답이 중요한 상황에서 적절하지 않을 수 있습니다.
환각(Hallucination)이라고 하는 실제로는 없거나 사실이 아닌 정보를 사실인 것처럼 말하는 문제를 가지고 있습니다.
자동완성을 수행하기 위해서는 주어진 문장을 작은 단위로 나누는 작업을 먼저 수행해야 합니다. 이때 사용되는 작은 단위를 ‘토큰’이라고 부릅니다. 가장 직관적으로 문장을 나누는 단위를 생각한다면 띄어쓰기를 생각할 수 있습니다. 하지만 띄어쓰기를 기준으로 단어를 구분할 경우 생기는 문제점이 있는데, 유사한 의미를 가진 언어가 전혀 다른 단어로 구분되는 문제가 발생합니다. 하나의 예시를 아래 그림으로 살펴보면, 유사한 두개의 단어가 전혀 다른 문자로 인식이 되는 문제가 발생합니다.
이를 해결하기 위해서, BPE(바이트 페어 인코딩)라고 불리는 방법을 적용하여 토큰화를 수행합니다. BPE 방식은 가장 작은 글자 단위로 글자를 나누고, 빈도수를 기반으로 토큰을 만들어 주는 방식입니다. 그래서 [“담당자”, “는", “접니다"]와 같은 형태의 토큰화가 가능합니다.
위에서는 다음 토큰을 예측하기 위한 방법과 토큰화의 기준을 이야기하였고, 이를 실제 동작하기 위해서 Trie라는 자료구조를 사용합니다. 아래 그림과 같이 사용자가 입력한 문장들이 Trie 형태의 자료구조로 구성이 됩니다. 아래 그림은 사용자가 “채널톡의”라는 입력이 들어오면 입력한 토큰을 Trie에서 순회하고(그림의 검은색 화살표), 뒤에 오게 될 토큰을 추론할 때는 Trie의 확률이 가장 높은 다음 토큰을 순회하여서 “아모스 입니다”를 예측하는 과정입니다.
빈도수를 기반으로 다음 단어를 예측하는 모델을 만들고 테스트하는 과정에서, 다음과 같은 문제들이 발생하였습니다.
상담을 많이 한 사용자의 패턴을 따라가는 문제가 발생
→ 예를 들어, 사용자 A, 사용자 B, 사용자 C가 있다고 가정하겠습니다. 사용자들은 전부 상담의 시작 부분에 “안녕하세요 저는 상담사 사용자 ‘각자의 이름’입니다.”라는 멘트를 사용한다면, N-gram은 이 중에서 가장 상담을 많이 한 상담사 1명의 이름만 만들 수 있습니다. 그렇다면, 나머지 사용자들은 자신의 이름이 아닌 다른 상담사의 이름이 자동완성되는 문제가 발생합니다.
다른 사용자의 개인 정보가 출력되는 문제가 발생
→ 전화번호나 주소와 같이 사용자가 스스로는 사용하지만, 다른 사람들에게 공개가 되는 것은 원하지 않는 정보들이 존재할 것입니다. 하지만, 기존의 N-gram은 빈도수를 기반으로 동작하기 때문에 사용자가 자주 입력한 정보라면 민감한 개인정보가 포함될지라도 출력 되는 문제가 존재합니다.
그래서 개인 맞춤형 출력이 필요하게 되었습니다.
N-gram을 개선하기 위해서 Node에 있는 사용자 빈도수를 이용해서 N-gram의 확률을 사용자에 맞게 조절을 하는 방법을 생각하였습니다. 기존에는 이전 단어들만 보고 다음 단어를 예측을 했었는데, 사용자가 해당 단어에 대한 사용 확률을 추가로 부여해 주었습니다. 수식으로 표현을 해보자면, 기존 N-gram을 아래와 같은 수식으로 표현할 수 있습니다. (이제부터 기존의 N-gram에서 다음 단어를 예측하기 위해서 사용된 이전 단어들을 “context”라고 부르겠습니다.)
우리가 구하고자 하는 것은 유저의 개인화된 출력으로, 아래 형태의 수식을 구하고자 합니다.
이를 계산하기 위해서, 수식을 전개를 해보면
형태로 표현이 가능합니다. 이때, context와 유저는 이미 고정된 값이기 때문에 분모 부분을 무시할 수 있습니다.
최종적으로 아래 수식을 계산하여서, 유저의 개인화된 출력을 얻을 수 있습니다.
이때, P(W|context) 부분은 기존의 N-gram을 사용하여 계산할 수 있습니다.
그리고 P(user|W,context)는 사용자가 문장을 사용한 빈도수를 기반으로 계산할 수 있습니다. 아래의 동작 과정을 통해서 더 자세히 설명드리겠습니다.
만약, “좋은 아침입니다. 저는”이라는 입력이 주어지고 다음에 오게 될 토큰을 찾는다고 가정을 하겠습니다. 기존의 N-gram의 경우에는 확률이 가장 높은 “아모스”만을 출력될 것입니다. 하지만, 아래의 그림과 같이 사용한 문장의 유저의 빈도수를 같이 고려해 준다면 사용자 별로 다른 출력을 얻을 수 있을 것입니다.
만약, 사용자가 제이브라면 아래와 같이 확률이 계산됩니다.
기존 N-gram은 예측하지 못하는 개인에게 더 적절한 다음 토큰을 예측할 수 있게 됩니다.
개인화 N-gram을 통해 개인에게 맞는 출력이 나타나는 것을 확인하였습니다. 하지만, 만약 새로운 사용자가 “오늘도 좋은”이라는 문장을 입력한다면, 새로운 사용자의 정보가 없기 때문에 다음 토큰을 예측할 수 없는 문제가 발생합니다. 이러한 부분을 개선하기 위해서, 여러 사용가 공통적으로 사용한 문장을 따로 구분하였습니다.
여러 사용자가 공통적으로 사용한 문장에는 개인 정보를 포함될 가능성이 낮고 자주 사용하는 일반적인 표현들이 많이 등장하기 때문에, 이러한 문장들은 개인화 N-gram에 추가로 적용하였습니다. 이를 구현하기 위해, Trie의 노드 종류를 공용 노드와 일반 노드로 구분하여 탐색하는 방법을 사용하였습니다. 그리고 공용 노드에 대해서는 사용자가 사용하지 않더라도 확률을 부여하는 방식을 사용했습니다.
공용 노드 : 여러 명의 사용자가 문장을 공통적으로 사용 한 경우
일반 노드 : 소수의 사용자만 문장을 사용 한 경우
아래 그림은 아래 두 가지 조건에 대해서 다음 노드를 예측하는 과정을 설명합니다.
3명 이상의 사용자가 노드에 접근할 경우 공용 노드로 간주
새로운 사용자가 “좋은”이라는 문장을 입력으로 사용
새로운 사용자에 대해서는 기존의 유저 정보가 존재하지 않습니다. 따라서 N-gram의 확률은 존재하지만, 사용자의 빈도수에 대한 확률은 모두 0이기 때문에 다음 토큰을 예측하지 못합니다. 이때, 3명의 사용자가 사용한 문장인 “하루”에 대해서는 공용 노드로 간주하여 확률을 계산합니다. 공용 노드에서 사용자가 사용한 문장이 없어서 빈도수를 계산하지 못하는 경우는 다른 사용자들의 빈도수의 중앙값에서 전체 사용자 수를 나누는 방법으로 빈도수를 사용합니다.
임의의 입력 문장을 예시로, 실제 채널톡의 매니저님들의 자동완성 결과를 아래 표로 정리를 해봤습니다. 같은 입력 문장에 대해서 사용자 별로 적합한 문장이 나오고, 같은 의미의 내용이더라도 평소 말하는 스타일을 반영한 자동완성 결과를 볼 수 있습니다.
처음 N-gram으로 자동완성을 적용하려고 계획을 했을 때, 쉽게 적용할 수 있겠다고 생각을 하였습니다. 하지만, 적용하는 과정에서 여러 문제를 만났고 하나씩 해결하는 과정이 생각처럼 쉽지는 않았습니다. 그래도 문제를 하나씩 해결하는 과정에서 많은 것을 배울 수 있었습니다. 아직 데모 단계로 테스트를 진행하고 있는데, 빨리 적용되어서 상담 효율성을 높여주면 좋겠습니다.
긴 글 읽어주셔서 감사합니다.
We Make a Future Classic Product
채널팀과 함께 성장하고 싶은 분을 기다립니다