상세 컨텐츠

본문 제목

형태소 분석기 Kiwi CoNg (1/4): 신경망 모델 도입기

프로그래밍/NLP

by ∫2tdt=t²+c 2025. 5. 4. 19:30

본문

오랜만에 Kiwi 이야기를 들고 블로그로 돌아왔습니다. 최근에 Kiwi v0.21.0에 신경망 모델을 도입하면서 분석 정확도를 크게 향상시켰는데요, 그 시작부터 진행 과정, 결과까지를 함께 나눠보고자 이렇게 시리즈 포스팅을 작성하게 되었습니다. 길고 지루한 이야기가 될 수도 있지만 최대한 재미나게 구성해볼테니 마지막 편까지 "채널 고정" 해주시면 감사드리겠습니다~

Kiwi CoNg 포스팅 시리즈

  1. 형태소 분석기 Kiwi CoNg (1/4): 신경망 모델 도입기
  2. 형태소 분석기 Kiwi CoNg (2/4): CoNg 모델 소개
  3. 형태소 분석기 Kiwi CoNg (3/4): 모델 성능 비교 (조만간 공개됩니다)
  4. 형태소 분석기 Kiwi CoNg (4/4): 모델 최적화 (조만간 공개됩니다)

왜 신경망 모델인가?

잘 돌아가고 있는 Kiwi에 갑자기 왜 신경망 언어 모델(Neural Language Model)을 도입하게 되었을까요? 내부 언어 모델 엔진은 사실 상 Kiwi의 본체나 다름없기 때문에 이걸 갈아치운다는 결정은 즉흥적으로 내릴만한게 아닙니다. 세상이 맨날 AI를 외치고 거대 언어 모델을 하니깐 우리도 해야한다는 그런 안일한 마음가짐으로는 Kiwi는 살아남을 수 없어요. 정부 지원 과제를 타내기 위해서 윗분들이 시켜서 강제로 하게 된 것도 당연히 아니구요. 신경망 모델을 고민하게 된 가장 근본적인 원인은 기존 방식으로는 Kiwi 성능 개선이 한계에 봉착했기 때문이었습니다. 크게 정리해보면 기존 Kiwi가 가지고 있던 한계들은 다음과 같습니다.

  • 맥락이 모호한 경우 가능한 분석 후보 여럿 중 엉뚱한 것을 선택하는 경우가 자주 발생함
  • 신조어 등 사전에 등재되지 않은 단어들에 대한 분석 정확도 부족
  • 성능 개선을 위한 지도학습 데이터를 더 이상 확보하기 어려움
  • 방언, 은어, 비속어 등 비표준어에 대한 분석 능력 부족
  • 모델 구조 상 더 이상 속도를 높이기 어려움

마지막 하나는 속도에 관한 문제고, 나머지 넷은 정확도에 대한 문제입니다. 정확도 부분에 있어서 다양한 이슈가 있는데 이를 하나로 묶어보자면 전반적으로 모델이 융통성이 없어서 배운 것에 대해서만 잘하고 그 외의 입력에 대해서는 잘 못한다는 말이 되겠습니다. 그럴수 밖에 없는게 기존 Kiwi가 채용하고 있던 언어 모델은 KnLM(Kneser-ney smoothing을 적용한 통계 기반의 언어 모델)인데, 이는 간단히 말하면 학습데이터에서 패턴을 찾아서 기억해두고, 모델이 추론할때 입력이 들어오면 기억해둔 패턴에서 찾아서 해당 패턴 뒤에 어떤 형태소가 주로 나왔는지 확률을 계산하는 방식입니다. 즉, 학습 데이터에서 보지 못한 패턴이 들어오면 정확도가 크게 떨어질 수 밖에 없는 구조라는 것입니다.

좀 더 구체적인 사례를 볼까요? 다음 두 문장에는 동일하게 "논의"라는 형태가 들어 있지만 한국인이라면 누구나 이 두 "논의"가 서로 다르게 분석되어야한다는 것을 압니다.

(가) 이 글의 논의 전개는 다음과 같다.
(나) 가을철 추수 전에 양쪽 논의 미꾸라지를 잡아 보았다.

(가)의 "논의"는 무언가를 토론하고 의견을 나눈다는 의미의 명사인 반면, (나)의 "논의"는 벼를 기르기 위한 장소인 ""에 조사 ""가 붙은 형태입니다. Kiwi에서 언어 모델을 이용해 이 문장을 형태소 분석하는 과정을 살펴보면 다음과 같습니다. 여기서 $P(x|y)$는 $y$ 문맥 다음에 $x$라는 형태소가 등장할 확률을 뜻합니다.

$$P_{a1} = P(\mathbf{논의}\: | \:\mathbf{이\: 글의}) ,\\
P_{a2} = P(\mathbf{논}\: | \:\mathbf{이\: 글의}) \cdot P(\mathbf{의}\: | \:\mathbf{이\: 글의\: 논}) ,\\
P_{b1} = P(\mathbf{논의}\: | \:\mathbf{가을철\: 추수\: 전에\: 양쪽}) ,\\
P_{b2} = P(\mathbf{논}\: | \:\mathbf{ 가을철\: 추수\: 전에\: 양쪽 }) ~~~~~~~ \\ ~~~~~~~ \cdot P(\mathbf{의}\: | \:\mathbf{ 가을철\: 추수\: 전에\: 양쪽\: 논}) $$

(가) 문장에서 $P_{a1} > P_{a2}$라면 "논의"는 "논의"로 분석되고, 반대로 $P_{a1} < P_{a2}$라면 "논 + 의"로 분석됩니다. (나) 문장에서도 마찬가지로 $P_{b1} > P_{b2}$라면 "논의"는 "논의"로 분석되고, 반대로 $P_{b1} < P_{b2}$라면 "논 + 의"로 분석됩니다. 확률 모델 $P$가 위의 확률들을 실제 의미에 맞게 잘 계산해준다면 가)에서는 $P_{a1} > P_{a2}$, 나)에서는 $P_{b1} < P_{b2}$가 나와야겠죠? 그러나 Kiwi 0.20.4의 분석 결과는 다음과 같습니다.

(가) 이 글의 논의 전개는 다음과 같다.
이/MM 글/NNG 의/JKG 논/NNG 의/JKG 전개/NNG 는/JX 다음/NNG 과/JKB 같/VA 다/EF ./SF

(나) 가을철 추수 전에 양쪽 논의 미꾸라지를 잡아 보았다.
가을철/NNG 추수/NNG 전/NNG 에/JKB 양쪽/NNG 논의/NNG 미꾸라지/NNG 를/JKO 잡/VV-R 어/EC 보/VX 었/EP 다/EF ./SF

저런, 정반대의 결과가 나왔네요. 이는 Kiwi의 언어 모델이 판단하기로는 $P_{a1} < P_{a2}$ ,   $P_{b1} > P_{b2}$이라는 것입니다. 왜 그랬을까요? 바로 통계 기반 언어 모델의 한계 때문입니다. $ P(\mathbf{논의}\: | \:\mathbf{이\: 글의}) $를 계산하고 싶은데 모델이 학습할때 "이 글의" 다음에 "논의"가 등장하는 사례를 많이 보지 못했다면 이 확률을 제대로 계산할 수가 없습니다. 그래서 문맥을 줄여서 그 대신 $ P(\mathbf{논의}\: | \:\mathbf{글의}) $를 계산하게 되고, 이 마저도 자주 본적이 없다면 $ P(\mathbf{논의}\: | \:\mathbf{의}) $를 계산하게 됩니다. 마찬가지로 (나) 문장에서도 $ P(\mathbf{논}\: | \:\mathbf{ 가을철\: 추수\: 전에\: 양쪽 }) $를 계산할때 "가을철 추수 전에 양쪽"이라는 패턴을 보지 못했을 가능성이 크므로, 차선책으로 $ P(\mathbf{논}\: | \:\mathbf{ 추수\: 전에\: 양쪽 }) $을 시도하고, 그 다음에는 $P(\mathbf{논}\: | \:\mathbf{ 전에\: 양쪽 })$, 또 차차선책으로 $P(\mathbf{논}\: | \:\mathbf{ 에\: 양쪽 })$, 이마저도 본적이 없다면 마지막으로 $ P(\mathbf{논}\: | \:\mathbf{ 양쪽 }) $을 대신 계산하게 됩니다. Kiwi가 알고 있는 형태소의 개수를 대략 5만개라고 가정할 경우, 문맥의 길이가 3 정도만 되어도 $50000^3 = 125000000000000$가지의 모든 경우를 모델이 학습할때 미리 살펴보고 기억해둬야합니다. 하물며 더 긴 문맥을 모델이 미리 학습해두려면 어마어마한 메모리와 학습 데이터가 필요하게 됩니다. 따라서 통계 기반의 언어 모델은 자주 쓰이는 조사, 어미 형태소들을 제외하면 모델이 긴 문맥이 들어가는 확률을 정확하게 계산하는 것이 사실상 불가능합니다.

그런데 왜 Kiwi에서는 이렇게 한계가 명확한 KnLM을 내장 언어 모델로 채택한걸까요? 가능한 형태소 조합을 빠르게 탐색하기 위해서 모델이 가볍고 빨라야했기 때문입니다. KnLM은 통계기반 모델이기 때문에 추론 과정에서 별도의 복잡한 연산은 필요없고 그저 들어온 패턴을 검색해서 그에 대응하는 확률만 반환하면 되기에 구식의 CPU에서도 아주 빠르게 동작할 수 있습니다. 그리고 위와 같이 모호성이 없는 경우에는 긴 문맥을 보지 않고 바로 근처의 형태소 1~2개만 봐도 충분히 괜찮은 분석 결과를 내기도 하기에 주어진 제약 조건에서는 나쁘지 않은 선택이었다 볼 수 있습니다. (이민철. (2024). Kiwi: 통계적 언어 모델과 Skip-Bigram을 이용한 한국어 형태소 분석기 구현. , 1(1), 109-136, https://doi.org/10.23287/KJDH.2024.1.1.6 참조)

신경망 모델의 장점

KnLM와 같은 통계 기반 모델이 가지고 있던 한계를 한 마디로 표현하자면 "융통성 부족"입니다. 그리고 신경망 모델이 가지고 있는 장점은 한 마디로 "융통성"입니다. 신경망 모델은 학습 과정을 통해 파라미터를 스스로 적절한 값으로 조절하고, 이렇게 학습한 파라미터는 처음 보는 패턴에 대해서도 어느 정도 통합니다. 또 신경망 구조는 현재 AI연구의 대세이기 때문에 관련된 다양한 최신 연구들을 그대로 적용해볼 수 있어서 확장 가능성도 있지요.

학습 가능한 양질의 데이터를 추가로 확보하기 어렵다는 문제도 (노이즈가 섞여있지만) 대량의 웹 텍스트를 확보함으로써 해결할 수 있습니다. 통계 기반 모델에서는 노이즈가 섞인 데이터가 학습과정에 들어가는 순간 모델 자체가 오염되기 때문에 그만큼 깨끗한 데이터를 더 많이 넣어서 일명 "물타기"를 해줘야합니다. 반면 신경망 모델은 학습 과정에서 노이즈 데이터가 대량으로 섞였다고 하더라도 마지막에만 양질의 데이터를 조금만 학습하면 금세 양질의 데이터에 적응합니다. 그래서 데이터 문제에서도 비교적 자유롭죠.

정확도를 올리는 것도 비교적 용이합니다. 신경망 모델은 그저 모델의 크기를 키우고 더 많은 데이터를 투입하는 것만으로도 성능이 향상된다(Kaplan, Jared, et al. "Scaling laws for neural language models." arXiv preprint arXiv:2001.08361 (2020). )고 잘 알려져 있기 때문에 정확도를 올리기가 쉽습니다. 반면 통계 기반 모델은 모델 크기를 키우는 것(기하급수적)에 비해 성능 향상폭이 제한적(산술급수적)이라서 모델 크기를 키우는 방식을 취하기 어렵습니다.

이렇게 좋은 데 진작 신경망 모델을 쓰지~ 왜 굳이 다른 걸 쓰는걸까요? 당연히도 신경망 모델의 연산 비용 때문입니다. 신경망 모델은 그 특성 상 통계 기반 모델과는 다르게 큰 행렬곱셈을 자주 수행해야해서 연산 비용이 비싼 편입니다. 그래서 쓸만한 정확도가 나오는 큰 모델은 연산량이 너무 많아서 CPU로 빠르게 돌리기 버겁고, 그렇다고 모델 크기를 줄이면 속도는 충분히 빨라지지만 정확도도 급락하는 한계가 있습니다. 그나마 다행인것은 요즘 로컬 AI가 보편화되면서 개인용 PC에서도 AI 모델들을 구동할 수 있게 CPU에도 AI용 다양한 전용 명령어(AVX, AMX)들이 추가되고 있다는 것입니다. 그래서 이 명령어들을 잘 활용할 수 있도록 신경망 모델을 설계한다면 최신 CPU에서 빠르면서도 높은 정확도를 달성하는 모델을 만들 수 있습니다. (반면 통계 기반 모델 구조를 위한 전용 명령어 같은 건 존재하지 않습니다. 앞으로 추가될걸로 보이지도 않구요. 그래서 장기적으로 보면 통계 기반 모델의 빠르다는 장점은 퇴색될 가능성이 높습니다.)

어떤 신경망 모델을 써야할까?

좋습니다~ 신경망 모델의 장점을 훑어보니 Kiwi의 코어 엔진을 갈아엎더라도 도입하는게 장기적으로 이득이 될 것 같습니다. 그런데 어떤 모델을 써야할까요? Kiwi는 범용적으로 사용할 수 있는 가벼운 분석기를 지향하기 때문에 가능하면 너무 무겁고 느린 모델을 도입하고 싶지는 않습니다. 그래서 품질이 좋으면서도 다음 두 가지 기준을 만족하는 모델을 찾아보기로 결정했습니다.

  • 모델 크기는 100MB이내. (Python으로 용이하게 배포하려면 pypi에 패키지를 올려야 하는데 이 때 일반적인 크기 제한이 100MB입니다. 그래서 이 수치를 잡았습니다.)
  • 속도는 현재 KnLM 기반의 분석기와 유사할 것. 혹시나 느려지더라도 1.5배 이상 느려지면 안됨.

일단 베이스라인으로 잡아볼 수 있는 것은 요즘 신경망 기반 언어 모델의 사실상 표준이라고 할 수 있는 GPT입니다. GPT3이후로는 모델 크기도 너무 커졌고 그 구조도 알려져 있지 않으니 현실적으로는 GPT-2정도가 시도해볼 수 있는 선택지가 되겠습니다. 실제로 GPT-2도 충분히 좋은 생성능력을 가졌으니깐요. 성능과 크기면에서 GPT가 상한이라면 하한으로는 Word2Vec 모델을 꼽아볼 수 있겠네요. 신경망 언어 모델 중 가장 단순한 구조를 가지고 있어서 속도도 아주 빠르겠지만 성능도 많이 낮을 것입니다. 그리고 GPT-2와 Word2Vec 사이에 존재하는 다양한 신경망 구조들도 있을텐데요 이 중에서 품질도 괜찮으면서 위의 두 조건을 만족하는 것을 찾을 수 있느냐가 관건이 되겠습니다!

GPT-2 모델의 구조. Self-attention과 Feed forward network라는 레이어로 이뤄지는 Transformer block을 여러 개 쌓은 형태이다. 구조는 복잡하지만 그 덕에 이전의 문맥을 고려해서 다음 단어를 정확하게 예측해낸다.
Word2Vec CBOW 모델의 구조. Input과 Output 2개의 임베딩 레이어로만 구성된다. 주변 단어를 이용해 중심 단어를 예측하는 구조로 어순을 고려하지 못하지만 굉장히 빠르게 연산할 수 있다.

다양한 탐색 끝에 제가 최종적으로 만들어낸 구조는 GPT의 성격을 지녔지만 Word2Vec CBOW와 닮은 CoNg(Contextual N-gram embedding)이라는 모델입니다. 이 모델은 실제로 KnLM보다 모호성 해소 성능은 크게 높으면서도 속도는 더 빠르고, 모델 크기도 50~70MB로 줄이는데에 성공하여 위의 모든 조건을 만족시켰고 결국 Kiwi에 포함되게 되었습니다. 신경망 모델의 도입이 실제로 얼마나 효과가 있었을까요?

CoNg 모델의 성능은?

모델 성능에 대해서는 이후 포스팅들에서 좀 더 구체적으로 다룰 예정이니 이 자리에서는 대략적인 결과만 공유하도록 하겠습니다.

더 어려워진 모호성 해소 v2025.05 데이터셋에 대해서 기존 KnLM, SBG 모델들보다 10%p 이상 정확도를 개선하는 데에 성공했고,

문장 분리에 있어서도 기존 모델보다 3%p이상 성능을 개선하여 타 분리기 대비 가장 높은 정확도를 달성했습니다.

신경망 모델을 도입했음에도 분석 속도는 오히려 빨라져서 인접한 문맥만 다루는 cong 모델은 유사하게 인접한 문맥만 다루던 이전 모델인 knlm보다 20%이상 빨라졌으며, 먼 거리의 관계를 분석하는 cong-global 모델은 마찬가지로 먼 거리를 다루던 이전 모델인 sbg보다 30~40%가량 빨라졌습니다.

종합해보면 속도는 빨라졌고 분석 정확도는 개선되었습니다. 도대체 CoNg모델이 어떤 구조이길래 이렇게 속도와 정확도를 동시에 올릴 수 있었을까요? CoNg 모델의 구조와 학습 방법에 대해서는 다음 포스팅에서 계속 설명하도록 하겠습니다.

관련글 더보기

댓글 영역