언어 모델(Language Model)은 특정 문장(=단어의 나열)이 등장할 확률을 계산해주는 모델입니다. 문장에 대해 확률을 계산할 수 있다는 건, 단어(혹은 문장)를 적절하게 선택하거나 생성해야하는 경우 여러 후보 중에서 더 적절한(확률이 높은) 후보를 선택하는 데에 쓰일 수 있다는 뜻이지요. 이 때문에 기계번역, 음성 인식, 철자 교정, OCR 등 언어와 관련된 여러 과제에서 널리 쓰이고 있습니다(이에 대해서는 예전에 몇몇 포스팅에서 간략하게 다룬 적이 있습니다). 게다가 최근 GPT-3와 같은 강력한 언어 모델이 등장하면서 언어모델이 위와 같이 비교적 단순한 과제뿐만 아니라 대화나 긴 글을 통채로 생성하거나, 주어진 예문이나 설명을 바탕으로 새로운 문제를 해결하는 것까지도 가능하다는 것이 밝혀져서 크게 관심을 받고 있죠.
특정 단어열이 주어졌을때 다음 단어가 등장할 확률을 계산할 수만 있으면 모두 언어 모델로 쓰일 수 있기 때문에 전산언어학이 태동한 이래로 다양한 언어 모델이 등장했었는데요, 이번 포스팅에서는 여러 언어 모델을 살펴보면서 각각의 특징을 분석해보고 어떤 모델이 어떤 상황에서 쓰이는지 살펴보았습니다.
반 세기가 넘는 시간 동안 다양한 언어 모델이 개발되고 발전되어 왔는데요, 모든 기술이 그러하듯 처음에는 굉장히 단순한 구조에서 출발했다가, 이전 모델의 한계를 극복하기 위해 하나씩 아이디어를 덧붙이면서 점차 발전하는 모습을 보입니다. 이 흐름을 기억하며 다양한 언어 모델들을 차례대로 살펴봅시다.
Unigram은 제일 단순한 언어모델입니다. 사실 언어모델이라고 하기에도 민망한데요, 각각의 단어가 출현할 확률이 독립이라고 가정하고 모두 따로따로 계산하는 식입니다. 수식으로 나타내면 아래와 같습니다.
$$P_{uni}(w_1, w_2, w_3, \cdots) = P(w_1) P(w_2) P(w_3) \cdots$$
각각의 단어를 독립적으로 보기 때문에 단어의 순서를 전혀 고려하지 않게 되구요 이 때문에 단어주머니(bag of words) 모델이라고도 부릅니다. 이 모델에서는 전체 문장에서 각 어휘가 몇 번씩 등장했는지만 고려합니다. 또한 이 언어 모델을 학습하는 건 역시 단순합니다. 학습용 말뭉치에서 각 어휘의 빈도를 조사해서 등장 확률을 계산하기만 하면 됩니다.
너무 단순해서 잘 안 쓰일것 같지만, 이 단순한 매력이 유용한 경우도 있습니다. 확률을 추정하는 것과 계산하는 것이 매우 빠르기 때문에 노이즈 데이터를 1차로 필터링하는데 종종 쓰이구요, 또 SentencePiece라는 서브워드 토크나이저에서 토큰의 유용성을 판단하는 데에도 쓰입니다. (sentencepiece가 설명된 wikidocs)
n-gram 언어 모델부터는 진짜 언어 모델이라고 부를 만합니다. Unigram과 달리 이전 단어를 고려하여 다음 단어의 확률을 계산하기 때문이지요. $n$-gram 모델에서는 다음 단어를 예측하기 위해 이전의 $n-1$개의 단어를 활용합니다. Unigram 모델은 n-gram 모델에서 $n=1$로 단순화시킨 모델인 셈이지요. n=2인 경우를 수식으로 나타내면 다음과 같습니다.
$$P_{2}(w_1, w_2, w_3, \cdots) = P(w_1) P(w_2 | w_1) P(w_3 | w_2) \cdots$$
2-gram 모델에서 $a$ 다음에 $b$가 나올 확률을 계산하기 위해서는 학습용 말뭉치에서 $a?$가 등장하는 빈도를 세고, 그 중 $ab$의 빈도를 세서 그 비율을 계산하면 됩니다. Unigram보다는 조금 복잡하지만 그래도 간단하죠? 마찬가지로 3-gram 모델에서는 $ab?$의 빈도를 세고, 그 중 $abc$의 빈도를 셈으로써 $ab$ 다음에 $c$가 등장할 확률을 계산할 수 있구요.
$n$이 커질수록 더 많은 문맥을 고려하게 되므로 더 정확할 확률 추정이 가능해지지만 그만큼 학습시에 조사해야하는 단어쌍의 개수도 많아집니다. 어휘가 총 $V$종류가 있다고 가정하면, 최악의 경우 $V^n$ 조합의 빈도를 전부 세어야할 지도 모릅니다. 따라서 모델 크기와 빈도 조회 시간의 문제로 보통 $n$은 2~6정도로 선택합니다.
n-gram 모델은 모델 크기 외에 또 다른 치명적인 문제가 있는데, 바로 학습 말뭉치에서 등장하지 않은 단어열에 대해서는 확률 계산이 불가능하다는 것입니다. $ab$ 다음에 $c$가 등장할 확률을 계산하고 싶어서, 학습 말뭉치 내에서 $ab?$와 $abc$가 등장한 빈도를 세어봤는데, $abc$가 등장한 횟수가 0건이라고 가정해봅시다. 그럼 확률은 0이 되어버리겠죠. 이 경우 $abc$가 포함된 전체 문장은 항상 0인 확률값을 내보내게 될 것이므로 언어 모델이 제역할을 할 수 없습니다. 더 최악인 경우는 $ab?$가 등장한 빈도 자체가 0인 경우입니다. 이 경우 0 / 0이 되어 아예 $ab$ 다음에 $c$가 등장할 확률을 아예 계산할 수가 없습니다.
학습용 말뭉치를 더 크게 키워서 최대한 다양한 패턴이 들어가도록 한다해도 이 문제는 해결되지 않습니다. 인간 언어의 창의성 때문에 얼마든지 이전에 학습해보지 못한 조합의 문장을 사용할 수 있기 때문이죠. 이를 해결하기 위해서 학습 말뭉치에 등장하지 않는 패턴에 대해 적당히 추정하는 Smoothing 기법이 등장하게 됩니다.
Kneser-ney smoothing은 n-gram 모델의 여러 smoothing 기법 중에서 가장 높은 성능을 내는 것으로 알려져 있습니다. 예전에 초성체 해석기를 개발하면서 이에 대해 한 번 포스팅한 적이 있기에 여기에서는 간략하게 설명하고 넘어가도록 하겠습니다.
핵심 아이디어를 간략하게 풀어 설명하자면, $ab$ 다음에 $c$가 등장할 확률을 계산할 때, $b$ 다음에 $c$가 등장할 확률도 함께 고려하는 겁니다. 마찬가지로 $b$ 다음 $c$가 등장할 확률을 계산할 때도 그냥 $c$가 등장할 확률도 함께 고려하는 거구요. 따라서 만약 $abc$가 등장한 빈도가 0일지라도, $bc$가 등장한 빈도와 그냥 $c$가 등장한 빈도가 모두 0일리는 없으므로, 최종적으로 항상 0보다 큰 확률값을 계산해낼 수 있게 됩니다.
Kneser-ney 모델은 그 강력함 덕분에 다양한 분야에서 널리 쓰이고 있습니다. 특히 일단 학습 말뭉치에서 빈도 통계를 계산해내는 작업만 끝내놓으면, 확률 추정시에는 별도의 복잡한 계산 없이 각 패턴별 확률값을 조회하는 것만으로 문장의 확률을 쉽게 계산할 수 있다는 장점이 있습니다. 즉 매우 빠릅니다. 다만 단점으로는 $n$이 커질수록 기하급수적으로 증가하는 패턴에 대한 통계 정보를 저장해두어야 하므로 모델의 크기가 매우 커지고, 이 때문에 디스크 및 메모리 사용량이 (매우) 많습니다.
대표적인 구현체로는 kenlm가 있고, github 레포의 dependents를 보면 KenLM이 어떤 패키지에서 쓰이고 있는지도 살펴볼 수 있습니다.
여기까지 빈도 기반의 언어 모델을 살펴보았습니다. 사실 요즘 빈도 기반의 언어 모델을 고민한다면 무조건 Kneser-ney 모델을 선택하시면 됩니다. 고민해야할 건 메모리와 성능 사이에서 줄타기하며 $n$을 잘 선택하는건데 이건 학습용 말뭉치의 크기와 어휘 집합의 크기도 함께 고려해야하기 때문에 좋은 팁이 있다기보다는 다양한 값을 선택해가며 실험하는게 필요하겠습니다.
2010년에 Mikolov 1가 RNN의 일종인 Elman network를 사용하여 신경망 기반 언어 모델을 소개한 이후, 신경망을 이용한 언어 모델 연구가 본격적으로 시작되었습니다. 2
RNN은 이전 상태값과 현재 입력을 조합하여 새로운 상태값을 계산해 내는 신경망입니다. 마찬가지로 현재 상태값에 다음 입력을 조합하면 그 다음 상태값을 계산하게 되구요. 이 때문에 입력이 순서대로 들어오며 그 길이가 정해져 있지 않은 데이터를 고정된 크기의 가중치로 처리해낼 수 있습니다. RNN을 구성하는 신경망을 $f$라고 한다면 다음과 같이 수식으로 표현할 수 있겠죠. 보통 $h_0$은 모든 값이 0인 영벡터로 초기화하고, 차례로 $x_1, x_2, \cdots x_n$을 입력해나가면서 $h_1, h_2, h_3 \cdots$을 계산해나갑니다.
$$ h_t = f(x_t ; h_{t-1}) $$
이 때 $f$에 차례로 입력할 단어 $x_t$는 계산이 가능하도록 실수의 벡터의 형태여야 합니다. 그런데 우리가 갖고 있는건 그냥 단어 $a, b, c$ 등이라서 $x_t$에 넣기 적합하지 않죠. 그래서 각 단어마다 그 단어를 대표하는 벡터 $\mathbf E_x$를 부여해서 $x_t$에 넣게 됩니다. 이렇게 각각의 어휘에 임의의 벡터를 대응시키는 걸 임베딩(embedding)이라고 합니다. 여기서는 단어를 임베딩시켰으니 단어 임베딩(word embedding)이라고 부릅니다. 임베딩할 벡터의 크기는 임의의 값인 $D$로 지정해도 되지만, 전체 개수는 총 어휘의 종류인 $V$개와 일치해야합니다. 따라서 전체 단어 임베딩은 $(D, V)$ 형태의 행렬이 되겠죠.
RNN의 출력 또한 마찬가지로 다음에 등장할 단어의 확률이 아니라 $h_t$라는 $H$크기의 임의의 벡터입니다. 이 값을 단어들에 대한 확률의 형태로 바꿔주기 위해 $(H, V)$형태의 행렬 $\mathbf P$를 곱합니다. 그러면 $\mathbf P \cdot h_t$는 $V$ 형태의 벡터가 되며 여기서 Softmax를 씌우면 최종적으로 각각의 어휘에 대한 확률을 계산할 수 있게 됩니다. 따라서 RNN 언어 모델의 식을 완벽하게 쓰자면 다음과 같겠네요.
$$h_t = f(\mathbf E_{w_t} ; h_{t-1})$$
$$P(w_{t+1} | w_1 \cdots w_t) = Softmax(\mathbf P \cdot h_t)_{w_{t+1}}$$
RNN 언어 모델은 언어의 생성의 패러다임을 암기에서 계산으로 변화시켰습니다. n-gram 모델과 같이 빈도 기반 모델은 학습용 말뭉치에서 등장하는 패턴을 외워서 이를 그저 적용하는 것에 불과했다면, RNN은 암기하는 대신에 그저 $f$를 반복 계산하는 것을 통해 특정 단어나 문장이 등장할 확률을 계산할 수 있으니깐요. 물론 그대신 학습을 통해 $f$와 $\mathbf E$, $\mathbf P$에 들어가는 파라미터를 추정해야하는 어려운 일이 추가되긴 했지만요.
RNN의 핵심은 이전 상태와 현재 입력으로부터 다음 상태를 계산하는 방법인 $f$인데요, 이 $f$를 구성하는 방법에 따라 고전적 RNN, 혹은 LSTM, GRU처럼 부르는 명칭이 달라집니다. 고전적인 RNN의 경우 입력 데이터의 길이가 길어질수록 학습이 어려워지는 문제가 있어 이를 개선하기 위해 LSTM가 등장했고, LSTM의 복잡한 연산을 단순화시킨 것이 GRU라고 생각하면 쉽습니다. (RNN에 대해 잘 설명된 wikidocs)
적당한 크기의 RNN 모델에 충분한 양의 데이터를 넣어서 학습을 시키면 모델이 작은 파라미터 안에 최대한 많은 정보를 담기 위해 알아서 적절하게 일반화를 시키게 됩니다. 즉, 다시 말하자면 모델은 학습 말뭉치에 등장한 패턴을 그대로 기억하는게 아니라 그 중에서 규칙만을 찾아내 이를 외우게 되는 셈이죠. 이 때문에 RNN을 비롯한 신경망 계열의 언어 모델은 학습 말뭉치에 전혀 등장하지 않은 문장에 대해서도 나름대로 적절하게 확률을 잘 추정할 수 있습니다. (물론 반대로 과도한 일반화 때문에 정확도가 낮아지는 부분도 있을테구요.)
Attention이라는 개념은 기계번역 연구에서 비롯되었습니다. 2010년대 초 RNN을 이용해 임의 길이의 문장에 포함된 정보를 고정된 $h$ 벡터로 압축할 수 있다는 것이 밝혀졌고, 당연히 반대로 고정된 벡터 $h$에서 임의 길이의 문장을 생성해 낼수도 있게 됐습니다. 그럼 자연히 특정 문장을 입력받아 벡터로 인코딩하고, 이 벡터를 다시 디코딩하여 다른 문장을 생성해내는 것도 가능하겠죠? 이렇게 RNN 인코더와 디코더를 붙여서 입력 문장을 출력 문장으로 변환하는 모델을 Seq2Seq이라고 합니다. 번역에 적용하면 기계번역을 할 수 있고, 질문과 답변에 적용하면 자동 답변 모델을 만들 수 있는 아주 유연한 모델이죠.
이 중 특히 기계 번역 모델이 많은 관심을 받았는데요, 짧은 문장에 대해서는 번역을 성공적으로 수행할 수 있었으나 문장이 길어질 수록 정확도가 크게 떨어지는 문제가 발생했습니다. 당연하게도 고정된 크기의 $h$ 벡터에 담을 수 있는 정보량에는 한계가 있는데, 입력 문장이 길어지면 그 안에 모든 정보가 담기지 못하니 문제가 발생하는 것이었습니다. 이는 벡터 $h$의 크기를 키운다고 해도 해결될 수가 없는데요, 왜냐면 입력 문장의 길이는 인간 언어의 창의성 덕분에 얼마든지 더 길어질 수 있기 때문입니다. 그래서 Bahdanau (2014)는 디코더에서 문장을 생성할 때 인코더의 최종 벡터 $h$ 말고도 중간 벡터들도 모두 살펴볼 수 있는 구조를 제안했습니다. 즉 모델이 번역을 수행하면서 중간중간 기억이 안나는 부분은 다시 앞부분을 들춰볼 수 있도록 한 것이죠. 이렇게 모델이 필요한 부분을 다시 주목(attend)해서 그 부분에서 정보를 꺼내오는 메커니즘을 Attention이라고 부릅니다. 3
Attention은 꼭 인코더-디코더 구조가 아니더라도 일반 RNN 모델에도 적용할 수 있습니다. 이 경우 Attetion이 적용된 RNN 모델을 수식으로 표현하자면 아래와 같이 되겠습니다.
$$h_t = f(\mathbf E_{w_t} ; h_{t-1}; h_0 \cdots h_{t-1})$$
다음 상태 $h_t$를 계산할 때 현재 입력과 이전 상태 뿐만아니라, 처음부터 그전까지의 모든 상태를 다 고려하는 것이죠.
Transformer (2017) 는 RNN + Attention 모델에서 RNN을 빼버린 모델입니다. 그전까지의 연구자들은 각 단어를 차례로 생성해나가는 과정은 재귀적이므로, 그 모델 역시 반복적인 구조를 가져야한다는 편견에 사로잡혀 있었습니다. 그래서 이전 상태 $h_0$에서 단어를 입력받으면 다음 상태 $h_1$로 모델이 전이하고, 여기서 또 새로운 단어를 입력받으면 또 다음 상태 $h_2$로 전이하는 식으로 모델을 구성할 수 밖에 없었습니다. Transformer를 제안한 구글의 연구자들은 이렇게 상태 $h$를 사용하는게 오히려 독이 된다고 생각했습니다. 4
RNN + Attention에서는 각 단어를 RNN에 통과시켜서 $h$를 얻은 다음, 현재의 $h_n$값을 이용해 주목해야할 과거의 $h$ 값들을 찾아내고, 최종적으로 이 $h$들을 조합하여 다음 단어를 예측해냅니다. 여기서 다음 단어를 예측하기 위해 과거의 $h$ 중 어떤 것에 주목할 지가 중요한 것이지, RNN을 통과시키는 게 중요한게 아니라는 거죠. 그래서 아예 RNN구조를 빼버리고, $h_n$이 스스로 $h$ 중 주목해야할 대상들을 찾아내는 것에만 집중하도록 했습니다. 이를 Self-attention이라고 합니다. 즉, 총 N개의 단어가 있다면, N개가 각각 자기자신을 포함한 총 N개의 단어를 살펴보면서 주목해야할 대상을 고르도록 한 셈입니다. (물론, 엄밀하게는 약간 다릅니다. 인코더 단계에서는 모든 입력을 다 살펴볼 수 있으므로 $h_i$는 $h_1 \cdots h_n$까지를 자유롭게 살펴볼 수 있습니다만, 실제 다음 단어를 예측해야하는 디코더 단계에서는 다음 단어를 생성하기 위해 미래의 단어를 내다보면 안되기 때문에 $h_i$는 $h_1 \cdots h_{i-1}$까지만 살펴보도록 Causal Masking이라는 제한을 가합니다.) 이미 Transformer에 대해서 잘 설명된 자료들(wikidocs)이 많으니 여기서는 이에 대해서 더 다루지는 않도록 하겠습니다.
이 Transformer 구조에서 인코더 부분만 따로 떼어서 사용하는게 요즘 널리 쓰이는 BERT모델이고, 디코더 부분만 따로 떼어서 사용하는 게 요즘 아주 뜨거운 GPT 모델이죠. BERT 모델은 입력된 문장 전체를 자유롭게 살펴보며 그 의미를 파악하는 역할을 하기에 언어 모델이라고 하기에는 조금 어렵습니다. 반면 GPT 모델은 이전 단어들로부터 다음 단어를 예측하는 역할을 수행하므로 앞서 논의한 언어모델이라는 개념에 딱 들어맞습니다. GPT 모델의 구조를 아주 간략하게 수식화하면 다음과 같겠네요.
$$h_t = f(\mathbf E_{w_t} ; \mathbf E_{w_0} \cdots \mathbf E_{w_{t-1}})$$
그리고 $h_t$는 RNN에서와 마찬가지로 행렬 $\mathbf P$와 곱해진 뒤 Softmax가 적용되어 $V$개의 단어에 대한 확률로 변환됩니다. 이것으로 다음 단어 $w_{t+1}$를 예측할 수가 있지요.
현재 시점에서 특정 문장이 등장할 확률을 계산하거나, 주어진 단어들 다음에 등장할 단어를 예측하는 작업을 아주 뛰어나게 수행하고 싶다고 하시면, GPT 모델을 선택해야합니다. GPT-1에서부터 시작하여 현재는 GPT-3까지 등장했는데, 버전이 올라갈 수록 모델의 크기가 어마어마하게 커져서 일반 컴퓨터에서는 돌리기도 어려워졌다는 문제가 있긴 하지만 매우 강력한 성능을 보이고 있죠. 특히 GPT-3의 경우 모델의 크기가 매우 커지면 단순히 이전 단어들로부터 다음 단어를 예측하는 식으로 학습을 시켰음에도 그 안에서 놀라운 능력들이 생겨난다는 점을 보여주어 아주 흥미롭습니다. 가르쳐준적이 없는데도 모델이 스스로 번역을 한다든지, 몇개의 예제와 함께 문제를 던져주면 예제를 파악해서 문제를 풀어준다든지, 수학 계산을 해주기도 합니다. 이 모든게 그저 이전 단어들이 주어졌을 때 다음 단어를 예측하는 과정에서 옵니다. n-gram 기반의 통계 모델에서는 없던 능력이 RNN을 거쳐 Transformer로 오면서 생겨났다는 점에 아주 놀랍습니다.
지금까지 언어 모델의 역사를 간략하게 훑으며 여러 종류의 모델의 특징에 대해 살펴봤는데요, 실제로 각 언어 모델이 얼마나 인간의 언어를 잘 반영하는지를 평가해봐야겠지요. 이에 대해서는 글이 길어지는 관계로 다음 포스팅에서 이어나가도록 하겠습니다~
다음 포스팅: 언어 모델을 평가해보자
어떤 언어 모델이 좋을까 - 언어 모델을 평가해보자 (1) | 2021.06.29 |
---|---|
[토픽 모델링] Generalized DMR 토픽 모델 (34) | 2020.06.06 |
[토픽 모델링] Dynamic Topic Model (13) | 2020.05.10 |
[토픽 모델링] 토픽에 자동으로 이름 붙이기 (8) | 2020.03.19 |
그림으로 깁스샘플링 이해하기 (7) | 2020.01.03 |
[기계 학습] Mean Shift 클러스터링 (5) | 2019.09.04 |
댓글 영역