상세 컨텐츠

본문 제목

형태소 분석기 Kiwi 신조어/미등재어 탐지 성능 강화!

프로그래밍/NLP

by ∫2tdt=t²+c 2026. 3. 31. 16:26

본문

최근 Kiwi의 신버전인 v0.23.0가 출시되었습니다. 이 버전에서는 신조어나 사전 미등재어처럼 Kiwi가 처음 보는 단어도 비교적 잘 분석할 수 있도록 OOV(Out-of-Vocabulary, 사전에 없는 단어) 탐지 성능이 강화되었고, 또 오타 교정시 메모리 사용량이 최적화되고 다어절에 대해서도 오타 교정이 적용되는 등 여러 가지 품질 개선이 있었습니다. 이번 포스트에서는 새 버전에서 어떻게 신조어 및 미등재어 처리가 강화되었는지 간단하게 설명하도록 하겠습니다.

Kiwi의 고질적인 약점

Kiwi는 모호성 해소 능력이 뛰어나고 방언 처리나 오타 교정 같은 까다로운 작업도 해낼 수 있지만 한편으로는 사전에 없는 단어를 지나치게 세밀하게 분해하려는 태생적인 단점도 안고 있었습니다. 이름바 '모르는 단어의 파편화' 현상은 신조어가 쏟아지는 환경에서 Kiwi의 활용도를 제한하는 고질적인 병목 구간이었죠. 왜 Kiwi는 유독 이런 약점을 가지고 있을까요? 이는 형태소 기반의 언어모델로 점수를 매기기 때문입니다. 이 언어모델 덕분에 뛰어난 모호성 해소 성능과 같은 장점뿐만 아니라 미등재어 분절이라는 단점도 동시에 얻은 셈입니다. 왜 형태소 기반의 언어 모델이 미등재어 처리에 약한 걸까요? 기존 버전의 Kiwi에서 신조어가 어떻게 처리되는지 실제 사례를 가지고 함께 살펴보시죠.

오늘의 예시문장은 바로 최근 아주 유행했던 메뉴인 '두쫀쿠'입니다.

오늘 친구가 두쫀쿠를 줬다.

위와 같은 문장을 입력 받으면 분석기는 먼저 이를 다음과 같이 가능한 형태소의 조합으로 분해합니다.

오늘
- 오늘/NNG

친구가
- 친구/NNG 가/JKS
- 친/XPN 구가/NNG

두쫀쿠를
- 두/MM 쪼/VV ㄴ/ETM 쿠/NNG 를/JKO
- 두쫀쿠/? 를/JKO

줬다
- 주/VV 었/EP 다/EF
- 주/VV 었/EP 다/EC

그리고 형태소 언어 모델을 통해 가장 확률이 높은 형태소 배열을 탐색하죠. '오늘/NNG' 다음에는 '친구/NNG + 가/JKS' 가 올 수도 있고 '친/XPN + 구가/NNG' 가 올 수도 있지만 이미 대량의 한국어 문장을 학습한 언어모델은 '친/XPN + 구가/NNG' 보다는 '친구/NNG + 가/JKS'가 올 확률이 높다는 걸 알고 있기에 '친구/NNG + 가/JKS'를 선택하는 식입니다.

이 방식은 이미 알고 있는 형태소들에 대해서는 아주 완벽하게 잘 작동합니다. 문제는 "두쫀쿠"처럼 처음 보는 단어가 나왔을때 시작됩니다. 시스템은 두쫀쿠를 자신이 알고 있는 형태소 조합으로 분해해보지만 '두/MM 쪼/VV ㄴ/ETM 쿠/NNG'처럼 형편 없는 조합만 나올 뿐입니다. 물론 그럴 때를 대비해서 입력 텍스트를 쪼개지 않고 전체를 신조어로 보는 Plan B도 준비되어 있죠. '두쫀쿠/?'처럼 원본을 쪼개지 않고 시스템이 잘 모르는 새로운 형태소일것으로 간주하고 처리하는 경로도 추가합니다. 이제 '친구/NNG + 가/JKS' 다음에 '두/MM 쪼/VV ㄴ/ETM 쿠/NNG'가 오는 게 확률이 높을 지 '두쫀쿠/?'가 오는 게 확률이 높을 지 언어모델이 계산해주면 되겠네요.

근데 언어모델 입장에서 아예 처음보는 형태소의 확률을 계산하는 건 원칙적으로 불가능합니다. 한번도 본적이 없으니 모델 입장에서는 '두쫀쿠/?'에 대해 추정하는 확률값이 0이 될수밖에 없거든요. (log를 취하면 -∞가 나와버립니다.) 확률이 0이 나오면 이 조합이 절대 선택될수 없으므로 실제로는 편법을 사용해서 확률 계산을 우회합니다. 모델이 마주치는 OOV는 주로 명사일것이므로 처음 보는 형태소에 대해서 확률을 계산하는게 아니라 임의의 명사(NNG 또는 NNP)에 대한 확률을 계산하는 것이죠. 그러면 0이 아닌 확률이 나와서 '두쫀쿠/?'가 나오는 경로를 선택하는 것도 얼마든지 가능해집니다.

자 이제 모델이 '두/MM 쪼/VV ㄴ/ETM 쿠/NNG' 대신 '두쫀쿠/?'를 선택하면 성공적으로 분석이 완료되는 것인데요, 이러려면 '두쫀쿠/?'의 확률이 '두/MM 쪼/VV ㄴ/ETM 쿠/NNG'보다 높기만 하면 되겠죠? 애초에 '두/MM 쪼/VV ㄴ/ETM 쿠/NNG'는 문법적으로 전혀 말이 안되는 형태소 배열이므로 확률값이 아주 낮게 나와서 임의의 명사가 등장할 확률을 계산한 값인 '두쫀쿠/?'가 더 높게 됩니다. 따라서 자연스레 다음과 같이 '두쫀쿠/?'가 선택되고 분석은 성공적으로 끝납니다.

오늘/NNG 친구/NNG 가/JKS 두쫀쿠/NNG 를/JKO 주/VV 었/EP 다/EF

아주 이상적인 상황이네요. 근데 이 방식은 치명적인 약점이 있습니다. '두쫀쿠'는 분해하면 안되는 사례여서 문제가 없었지만 동일한 과정으로 분해해야 하는 상황에서도 분해를 안해버리는 문제가 나올 수 있기 때문이죠. 다음과 같은 문장을 생각해볼 수 있습니다.

플라밍고사려다 유니콘샀는데

이 역시 다음과 같이 형태소 조합으로 먼저 분해가 되는데요

플라밍고사려다
- 플라밍고/NNG 사/VV 려다/EC
- 플라밍고사려다/?

유니콘샀는데
- 유니콘/NNG 사/VV 었/EP 는데/EC
- 유니콘샀/? 는데/EC

'플라밍고/NNG 사/VV 려다/EC'의 확률이 '플라밍고사려다/?'의 확률보다 높으면 전자가 선택되어 아주 깔끔한 결과가 나오겠죠? 문제는 '플라밍고 사려다'라는 문장이 한국어에서 아주 흔하게 나오는 패턴은 아니다보니 언어모델이 이 형태소 배열의 확률에 비교적 낮은 확률을 부여하게 된다는 것이죠. 반면 '플라밍고사려다/?'는 아예 처음 보는 형태소이지만 아까처럼 처음 보는 형태소에는 임의의 명사 확률을 부여하기로 했으므로 이게 더 높은 확률을 받게 됩니다. 그래서 아래처럼 분해할 것을 분해 안하는 오류를 범하게 되죠.

플라밍고사려다/NNG 유니콘/NNG 사/VV 었/EP 는데/EC

왜 이런 현상이 발생했을까요? 처음 보는 형태소에 대해 비교적 높은 확률을 부여했기 때문입니다. 이걸 막으려면 '플라밍고사려다/?' 같은 OOV 형태소에는 낮은 확률을 부여해야합니다. 그러나 모델은 주어진 텍스트가 실제로 처음보는 형태소인지 아니면 드물게 등장하지만 말이 되는 형태소들이 차례로 연결된 것인지 미리 알 수가 없습니다. 따라서 OOV 형태소에 낮은 확률을 부여하겠다고 확률값을 조정하면 이번에는 이전 사례였던 두쫀쿠가 '두/MM 쪼/VV ㄴ/ETM 쿠/NNG'로 잘못 분해되는 문제가 발생할 겁니다. 골치가 아프죠? 상황을 표로 정리해보면 다음과 같이 되겠네요.

  OOV 형태소에
높은 확률 부여
OOV 형태소에
낮은 확률 부여
신조어 / 미등재어 분해하지 않음(O)
e.g. 두쫀쿠/NNG
분해함(X)
e.g. 두/MM 쪼/VV ㄴ/ETM 쿠/NNG
알고 있는 형태소가
그저 드문 패턴으로 조합된 것
분해하지 않음(X)
e.g. 플라밍고사려다/NNG
분해함(O)
e.g. 플라밍고/NNG 사/VV 려다/EC

즉 OOV 형태소의 확률이 0이 되는것을 막고자 적당히 임의의 명사로 대체해서 확률을 계산했는데 그렇다보니 이 확률이 높은지 낮은지에 따라 신조어 / 미등재어를 바르게 분석하거나 또는 알고 있는 형태소이지만 그저 드문 패턴으로 조합된 경우를 바르게 분석할 수 있는 거죠. 둘 중 하나를 선택할 순 있어도 어떻게 해도 양쪽을 동시에 만족시킬 수 없습니다. 기존 Kiwi 버전에서는 알고 있는 경우를 제대로 분석하는게 조금 더 낫겠다는 판단하에 후자를 선택한 것이구요. 이 때문에 기존의 Kiwi에서는 미등재어를 잘게 쪼개는 경향이 두드러졌던 것입니다.

사람은 어떻게 OOV를 잘 처리할까?

형태소 분석기가 OOV를 깔끔하게 잘 처리하는게 어렵다는 건 이제 충분히 알았습니다. 근데 사람은 어떻게 OOV를 잘 처리할까요? 한 번도 본적 없는 단어가 문장 속에 섞여 있어도 사람들은 당황하지 않고 내가 모르는 새로운 단어일거라는걸 자연스럽게 어떻게 알아낼까요? 다음과 같은 예시 문장을 보면서 고민해봅시다.

두쫀쿠 먹어봤냐? 오늘 친구가 두쫀쿠를 줬는데 먹어보니 생각보다 맛있더라. 근데 그게 얼만줄 알아? 이 쪼끄만 두쫀쿠가 1만원이라는거야.

'두쫀쿠'라는 말을 아예 처음 봐서 이게 뭔지 명사인지 아닌지도 모른다고 가정하에, 사람이 두쫀쿠라는 신조어를 파악해내는 과정은 다음과 같을 겁니다.

  1. 문맥 정보: 일단 '두쫀쿠'가 정확히 뭔지는 모르겠지만 '먹어봤냐'의 목적어 위치에 등장하고 조사 '-를'과 '-가'의 앞에 등장하는 걸 봅니다. 즉 문맥 상 명사가 나타날 위치에 나왔으니 '두쫀쿠'는 아마 내가 모르는 명사일거라고 생각합니다.
  2. 형태 정보: 그리고 또 '두쫀쿠'라는 형태가 충분히 명사처럼 생겼으므로 (뭔가의 줄임말처럼 보임) 확실히 명사가 맞다고 확신합니다.
  3. 빈도 정보: 또 이 형태가 동일하게 여러 번 반복해서 등장한다는 것을 통해 이게 오타 등의 실수가 아니라 실제로 새로 등장한 무언가를 가리키는 명사일 것이라고 결론을 내립니다.

사람은 이렇게 문맥, 형태, 빈도 정보를 종합해서 유연하게 판단합니다. 그렇다면 형태소 분석기에게도 이 세 가지 '인간의 센스'를 가르쳐줄 수 있다면 어떨까요? OOV 처리 성능을 개선할 수 있지 않을까요?

Kiwi에게 형태 & 빈도 정보를 주입하자

사실 이미 Kiwi는 문맥, 형태, 빈도 정보 중 하나는 잘 알고 있습니다. 바로 문맥 정보입니다. 앞에서 설명했듯 이미 형태소 기반의 언어모델을 사용하기 때문에 국소적인 문맥 정보, 즉 특정 위치가 명사가 등장할 만한 위치인지는 비교적 정확하게 판단할 수 있습니다. 하지만 형태 정보, 즉 '이 형태가 얼마나 한국어에서 명사다운 형태인지'를 판단하는 능력과 빈도 정보를 분석 단계에서 통합하는 능력은 전무한 상태지요. 이 정보들을 주입하는 게 이번 Kiwi 업데이트의 핵심 목표였습니다. 어떻게 형태 정보와 빈도 정보를 주입했을까요?

바로 Kiwi의 약점을 설명하면서 언급했던 OOV 형태소의 등장 확률입니다. 확률 계산 방법을 변경하여 OOV가 명사다운 형태일수록 높은 확률을 받도록, 또 자주 등장할 수록 높은 확률을 받도록 한 것이죠. 이를 통해 형태 정보와 빈도 정보가 분석 과정에 자연스럽게 녹아들게 했습니다. 그럼 구체적으로 어떻게 OOV 형태소의 확률 계산을 수행했는지 이제 설명하도록 하겠습니다.

미등재어는 어떻게 언어 모델로 처리할 수 있을까

앞서 살펴보았듯 Kiwi가 OOV처리를 어려워하는 근본적인 원인은 형태소 기반의 언어 모델이 미등재어의 확률을 계산할 수 없기 때문입니다. 사실 이 문제는 Vocabulary기반으로 동작하는 모든 언어 모델이 예전부터 가지고 있던 고질적인 한계입니다. 이 때문에 OOV 처리를 위한 다양한 기법들이 연구되어 왔죠. 그런데 최근 LLM 시대부터는 이런 걱정이 싹 사라졌습니다. Subword Tokenization이 도입되었기 때문이죠. 즉 텍스트를 항상 단어(or 형태소) 단위로 쪼개지 않고 더 작은 단위(e.g. 글자 or 바이트)로 쪼개기 때문입니다. 즉, 사전(Dictionary)에 '두쫀쿠'라는 단어는 들어가 있지 않더라도 '두', '쫀', '쿠'라는 글자는 전부 들어가 있으므로 '두쫀쿠'의 확률을 구하는 대신 '두', '쫀', '쿠'가 연속해서 등장할 확률을 계산할 수 있거든요.

물론 Subword Tokenization은 모델이 충분히 커서 일반화 능력이 갖춰줘야 작동이 가능합니다. 수십 GB의 파라미터를 가진 거대 언어모델은 수조 개의 토큰을 학습하며 '두', '쫀', '쿠'라는 낯선 조합 뒤에 조사 '가'나 '를'이 붙는 맥락을 통해 이것이 명사임을 스스로 체득하기 때문에 이게 글자 단위로 쪼개져서 입력되어도 전체 의미를 대략적으로 파악할 수 있습니다.

하지만 형태소 분석기는 웹 서비스의 가장 앞단에서 밀리초(ms) 단위의 성능을 다투는 경량 라이브러리입니다. Kiwi가 형태소 기반의 언어 모델을 고집한 이유는 연산량과 메모리가 제한된 상황에서는 데이터를 기본적인 의미 단위인 형태소에 맞게 쪼개는 게 모델이 처리하기에 효율적이기 때문입니다. 따라서 미등재어를 처리하겠다고 섣부르게 형태소 분석기에 Subword Tokenization을 도입하게 되면 오히려 효율성과 품질 두 마리 토끼를 모두 놓칠 수도 있습니다. 그러면 어떻게 해야 LLM만큼 무겁지 않으면서도 미등재어의 확률을 영리하게 추론할 수 있을까요?

문자 기반 단어 모델로 명사 점수 계산하기

복잡한 시퀀스의 등장 확률을 추정하는 것은 어려운 일입니다. 예를 들어 전체 사건들 중 ABCDE가 연속해서 발생하는 사건의 확률 $P(A B C D E)$ 을 계산하는 상황을 상상해봅시다. 이 5개가 통으로 발생하는 경우는 사실 잘 없으므로 직접 확률을 구하기 보다는 이 시퀀스를 분해해서 계산하는 방식으로 접근하죠. 이 때 조건부 확률 공식을 사용합니다. 즉 $P(A B) = P(A) \cdot P(B|A)$라는 사실을 이용하는 겁니다. 이를 재귀적으로 적용하면 $P(A B C D E) = P(A) \cdot P(B | A) \cdot P(C | AB) \cdot P(D | ABC) \cdot P(E | ABCD)$처럼 ABCDE의 등장 확률을 5개의 작은 사건들 확률의 곱으로 대신 계산할 수 있게 됩니다.

그런데 이 분해는 꼭 순서대로 할 필요도 없고 하나씩 할 필요도 없습니다. $A$와 $B$가 연속해서 발생하는 사건인 $AB$를 하나의 큰 사건으로 보고, 또 $D$와 $E$가 연속해서 발생하는 사건인 $DE$를 또 다른 하나의 큰 사건으로 본다면, $P(ABCDE) = P(AB) \cdot P(C | AB) \cdot P(DE | ABC)$와 같이 분해할 수도 있습니다. 혹은 뒤에서부터 분해해서 $P(E)$부터 거꾸로 계산해오는 것도 가능하구요. 즉 분해를 어떤 단위로 하든지 어떤 순서로 하든지 상관 없이 우리는 동일한 확률을 계산할 수 있다는 것입니다.

이걸 문장에도 적용해볼 수 있습니다. "오늘 친구가 두쫀쿠를 줬다"는 문장의 확률을 계산하기 위해서 문장을 형태소 단위로 쪼개어 확률을 구해볼 수도 있습니다.

$$P_{morpheme-level} = P(오늘) \cdot P(친구 | 오늘) \cdot P(가 | 오늘\:친구) \\ \cdot P(두쫀쿠 | 오늘\:친구가) \cdot ... $$

혹은 이걸 글자 단위로 쪼개서 확률을 구할 수도 있겠죠.

$$P_{character-level} = P(오) \cdot P(늘 | 오) \cdot P(친 | 오늘) \cdot P(구 | 오늘\:친) \cdot P(가 | 오늘\:친구) \\ \cdot P(두 | 오늘\:친구가) \cdot P(쫀 | 오늘\:친구가\:두) \cdot P(쿠 | 오늘\:친구가\:두쫀) \cdot ... $$

우리가 확률을 어떻게 계산하든 두 확률은 결국 동일한 값이 됩니다. $P_{morpheme-level} = P_{character-level}$

더 나아가 둘을 섞어서 형태소 단위에서 확률을 계산하다가 글자 단위로 계산하는 것도 가능하며 이 경우에도 마찬가지로 결과값은 동일합니다.

$$P_{hybrid} = P(오늘) \cdot P(친구 | 오늘) \cdot P(가 | 오늘\:친구) \\ \cdot P(두 | 오늘\:친구가) \cdot P(쫀 | 오늘\:친구가\:두) \cdot P(쿠 | 오늘\:친구가\:두쫀) ...$$

이 하이브리드 방법이 바로 경량 모델에서 OOV를 처리하기 위한 가장 핵심적인 아이디어입니다. 즉 대부분의 텍스트에서는 형태소 기반으로 확률 계산을 수행하다가 특정한 부분에서만 글자 단위로 확률 계산을 수행하고 이를 합친다면 아주 작은 크기의 문자 모델만 추가하고 전체 계산량을 거의 그대로 유지하면서 OOV의 확률 계산을 고도화할 수 있습니다.

한국어에서 발생하는 대부분의 OOV는 명사이므로 OOV가 일단 명사일거라고 한정하면 OOV를 위한 문자 모델은 명사들의 글자 분포를 학습하는 문자 수준의 명사 모델이 됩니다. 따라서 LLM이 수많은 문장을 Subword 단위로 쪼개어 입력 받고 그 패턴을 학습하는 것처럼, 명사 모델은 다양한 종류의 명사를 글자 단위로 쪼개어 입력 받아 명사의 패턴을 학습하도록 하면 됩니다.

아주 가벼운 모델을 사용하고 싶으므로 이전에 Kiwi의 형태소 모델을 경량 신경망 기반으로 교체하면서 개발한 CoNg모델을 그대로 재사용하기로 했습니다. 다만 이번에는 vocab이 훨씬 줄어들었고(약 500여개. 한글 초+중성 음절, 종성, 알파벳, 숫자 등만 포함시켰습니다), embedding dimension이 64로 아주아주 작게 잡았습니다. 학습 데이터는 모두의 말뭉치에서 제공하는 형태분석 말뭉치 및 개체명 분석 말뭉치에서 추출된 명사들을 이용해서 구축했구요. 이를 통해 약 8MB 수준의 작은 문제 수준의 명사 모델이 구축되었습니다.

이 모델이 실제로 다음 문자열들의 확률을 어떻게 예측하는지 살짝 보여드리겠습니다.

두쫀쿠
  Log Prob. 누적 Log Prob.
$P(두)$ -5.773 -5.773
$P(쪼|두)$ -7.826 -13.599
$P(ㄴ|두쪼)$ -3.718 -17.318
$P(쿠|두쫀)$ -7.354 -24.672
$P(\$|두쫀쿠)$ -2.666 -27.339
두쫀쿠를
  Log Prob. 누적 Log Prob.
$P(두)$ -5.773 -5.773
$P(쪼|두)$ -7.826 -13.599
$P(ㄴ|두쪼)$ -3.718 -17.318
$P(쿠|두쫀)$ -7.354 -24.672
$P(르|두쫀쿠)$ -3.825 -28.498
$P(ㄹ|두쫀쿠르)$ -4.667 -33.165
$P(\$|두쫀쿠를)$ -4.594 -37.759
한국어
  Log Prob. 누적 Log Prob.
$P(하)$ -4.197 -4.197
$P(ㄴ|하)$ -2.446 -6.643
$P(구|한)$ -1.924 -8.568
$P(ㄱ|한구)$ -0.133 -8.702
$P(어|한국)$ -3.928 -12.630
$P(\$|한국어)$ -1.803 -14.434
됐습니다.
  Log Prob. 누적 Log Prob.
$P(돼)$ -7.104 -7.104
$P(ㅆ|돼)$ -4.947 -12.052
$P(스|됐)$ -5.494 -17.547
$P(ㅂ|됐스)$ -6.270 -23.817
$P(니|됐습)$ -6.294 -30.111
$P(다|됐습니)$ -4.888 -34.999
$P(.|됐습니다)$ -8.096 -43.096
$P(\$|됐습니다)$ -8.839 -51.936

(여기에서 $기호는 명사의 끝을 지칭하는 특수 토큰입니다.) 우리 모델이 '한국어'라는 형태에 대해서는 명사 확률 점수를 -14.434, '됐습니다.'에 대해서는 -51.936, '두쫀쿠'에 대해서는 -27.339, '두쫀쿠를'에 대해서는 -37.759로 계산해주고 있네요. 확실히 명사다운 명사(한국어)에는 높은 확률값(-14)을, 명사가 아닌 것(됐습니다)에 대해서는 아주 낮은 확률값(-51)을 부여하고 있습니다. 두쫀쿠는 '한국어'에 비해서는 다소 낮은 점수를 받긴했으나 그래도 명사가 아닌것에 비해서는 확실히 높은 점수를 받았네요. 그리고 '두쫀쿠' 뒤에 '를'이 붙어면 확률 점수가 10점 이상 깎이기 때문에 '두쫀쿠를'이라는 형태를 보면 이 전체를 하나의 명사로 보기보다는 '두쫀쿠' + '를/JKO'로 분해할 가능성이 충분히 높아지구요.

이렇게 형태 정보의 역할을 하는 명사 점수를 계산하는 것은 완성되었습니다. 이제 빈도 정보를 통합해볼까요?

반복되는 문자열에 더 높은 점수를 부여하기

지금까지 살펴보았듯 문맥 정보와 형태 정보는 전부 특정 패턴이 등장할 확률을 통해서 정의되었습니다. 따라서 빈도 정보 역시 확률이라는 틀에 녹여내야 자연스럽게 앞의 두 정보와 통합이 가능하겠죠? 빈도 정보를 확률의 관점에서 다시 풀어써보면 '특정 문맥에서 자주 등장했던 단어는 그 문맥에서 다시 등장할 확률이 높다' 정도가 되겠습니다. 여기서 '단어'는 그저 계산을 위해 글자의 나열로 환원할 수 있겠구요.

두쫀쿠 먹어봤냐? 두리안 말고 두쫀쿠! 오늘 친구가 두쫀쿠를 줬는데 먹어보니 생각보다 맛있더라. 근데 그게 얼만줄 알아? 이 쪼끄만 두쫀쿠가 1만원이라는거야.

위의 빈도 정보 정의를 바탕으로 이 문맥에서 두쫀쿠라는 문자열이 다시 등장할 확률을 계산해보겠습니다. 일단 형태 정보를 계산할때와 동일하게 $P(두쫀쿠)$는 조건부 확률로 분해한 뒤 이를 곱해서 계산할 수가 있습니다.

  이전 문맥의 빈도 다음 글자의 빈도 Log Prob. 누적 Log Prob.
$P(두)$ 70 5 -2.639 -2.639
$P(쫀|두)$ 5 4 -0.223 -2.862
$P(쿠|두쫀)$ 4 4 -0.000 -2.862

빈도 기반으로 확률을 추정하는건 매우 간단합니다. 조건이 발생할 빈도를 세고 해당 조건 하에서 원하는 사건이 일어날 빈도를 세서 비율만 계산하면 됩니다. 즉, '두'라는 문자가 출현할 확률 $P(두)$은 문자가 출현하는 모든 경우의 빈도(=전체 글자수) 분의 '두'라는 문자가 출현한 빈도를 계산하면 되고, '두' 다음에 '쫀'이 출현할 확률 $P(쫀|뚜)$는 '두'가 출현하는 빈도 분의 '두쫀'이 출현하는 빈도를 계산하면 되는 식이지요. 여기까지는 쉽습니다. 근데 이 빈도 정보의 확률을 형태 정보의 확률과 단순히 산술적으로 더해서 사용하게 되면 문제가 발생할 수 있습니다. 한국어에서는 특정 문맥에서 자주 등장하는 명사 말고도 그저 문법적인 이유로 반복적으로 쓰이는 조사, 어미, 동사 등이 많습니다. 빈도 정보를 그냥 가져다 사용하게 되면 그저 자주 반복된다는 이유로 OOV 명사일 확률을 높게 받을 수 있기 때문이죠. 우리는 한국어 특성상 자주 반복적으로 등장하는 문자열과 그렇지 않은 문자열을 구분해서 보는게 필요합니다.

  국소적으로 자주 출현 국소적으로 드물게 출현
전역적으로 자주 출현 일반적인 명사 / 동사 or 조사, 어미일 가능성이 높다 -
전역적으로 드물게 출현 OOV일 가능성이 높다 -

지금 분석 대상이 되는 문맥 내에서의 문자열 빈도를 국소적 빈도(Local Frequency), 일반적인 한국어 말뭉치 전체 내에서의 문자열 빈도를 전역적 빈도(Global Frequency)라고 구분해 보겠습니다. (빈도 정보는 국소적 빈도와, 형태 정보는 전역적 빈도와 연관되어 있다고 볼수도 있겠네요) 우리의 관심 대상인 미등재어는 전역적 빈도는 낮지만 국소적 빈도는 높은 상황에 속합니다. 이 경우에만 빈도 정보가 강하게 반영되고, 나머지 경우에는 상대적으로 덜 반영되게 해야 일반 명사, 동사 등에 대한 분석 오류를 막을 수 있겠죠?

그래서 여기에서 베이지안 이론이 등장합니다. 전역적 빈도에서 추정되는 확률을 사전 확률(Prior)로 두고, 현재 국소적인 문맥에서 관찰되는 빈도(Observation)를 반영했을때의 사후 확률(Posterior)를 실제 특정 문자열 패턴이 OOV 명사일 점수로 사용하자는 아이디어입니다. 그리고 여기에서 사전 확률은 앞에서 만든 형태 정보를 추정하기 위한 문자 수준의 명사 모델에서 가져오는 것이죠. 단 이 모델은 그저 이전 문맥에서 다음 문자가 등장할 확률만을 계산해줄 수 있으므로 전역 빈도 정보까지는 제공하지 못합니다. 그렇다고 말뭉치 내의 모든 문자열 패턴에 대해서 미리 빈도수를 구해서 저장해놓을수도 없으므로 CoNg모델의 문맥 데이터에 빈도수 근사치를 함께 저장해두는 방식을 사용했습니다. 즉 $P(쪼|두)$가 뭔지 알수 있고, 또 '두'의 빈도수 근사치를 알고 있으므로 이를 바탕으로 '두쪼'의 빈도수도 추정해보는 것이죠.

두쫀쿠
  사전 Log Prob. 전역 빈도(추정) 국소 빈도 사후 분포 사후 Log Prob.
$P(두)$ -5.773 - / - 5 / - - / - -5.773
$P(쪼|두)$ -7.826 0.647 / 1623 4 / 5 3.647 / 1627 -6.100
$P(ㄴ|두쪼)$ -3.718 0.194 / 8 4 / 4 3.194 / 11 -1.236
$P(쿠|두쫀)$ -7.354 $\epsilon$ / $\epsilon$ 4 / 4 3 / 3 -0.000
$P(\$|두쫀쿠)$ -2.666 $\epsilon$ / $\epsilon$ - - -2.666
됐습니다
  사전 Log Prob. 전역 빈도(추정) 국소 빈도 사후 분포 사후 Log Prob.
$P(돼)$ -7.104 - / - 5 / - - / - -7.104
$P(ㅆ|돼)$ -4.947 6.117 / 861 5 / 5 10.117 / 865 -4.448
$P(스|됐)$ -5.494 1.767 / 430 5 / 5 5.767 / 434 -4.320
$P(ㅂ|됐스)$ -6.270 1.644 / 400 5 / 5 5.644 / 404 -4.270
$P(니|됐습)$ -6.292 0.731 / 395 5 / 5 4.731 / 399 -4.434
$P(다|됐습니)$ -4.888 2.954 / 392 5 / 5 6.954 / 396 -4.042
$P(.|됐습니다)$ -8.096 $\epsilon$ / $\epsilon$ - - -8.096
$P(\$|됐습니다.)$ -8.839 $\epsilon$ / $\epsilon$ - - -8.839

실제 계산 결과를 보면서 설명해보겠습니다. $P(쪼|두)$, 즉 명사 환경에서 '두' 다음에 '쪼'가 등장할 로그 확률은 문자 기반의 명사 모델에서 약 -7.826로 추정해줍니다. 퍼센트로 나타내면 0.39% 정도인데요, 말뭉치 내에서 '두'가 등장한 빈도가 대략 1623이라는 것을 알고 있으면 '두쪼'은 약 0.647 ($1623 \times 0.39\%$)라고 추정해볼 수 있습니다(여기까지가 사전확률입니다). 그런데 주어진 문맥에서 실제로 '두'는 총 5번 등장했고 '두쪼'는 4번 등장했다고 합시다(관측). 물론 이 중 1번은 당연히 현재 분석하는 글자 자기자신이므로 이 값을 제외한 나머지 빈도 (3 / 4)를 전연 빈도에 더해서 더 정교한 사후 분포를 구할 수 있습니다. 즉, '두'는 전역과 국소 빈도 전체를 합쳐서 총 1627번, '두쪼'는 약 3.647번 등장한 셈이죠. 이걸로 새롭게 로그 확률을 계산해보면 약 -6.100이 나옵니다. 현재 문맥의 빈도 정보를 고려한 결과 '두' 다음에 '쪼'가 등장할 확률이 전역 문맥 대비 조금 더 증가(-7.826 → -6.100)했다고 볼 수 있습니다.

마찬가지 계산을 $P(ㄴ|두쪼)$, $P(쿠|두쫀)$ 등에 대해서도 수행해볼 수 있습니다. 이 경우 '두'와는 다르게 '두쪼'의 추정 빈도는 약 8회, 심지어 '두쫀'은 말뭉치 내에서 출현빈도가 아예 없는데요, 이런 경우는 아주 작은 양수인 $\epsilon$으로 전역 빈도를 대신합니다. 똑같이 국소빈도를 더해주더라도 이번에는 '두쪼'와 '두쫀'의 전역 빈도가 워낙 작기 때문에 똑같이 국소 빈도를 증가시켰음에도 사후 로그 확률은 더 크게 증가($P(ㄴ|두쪼)$는 -3.718 → -1.236, $P(쿠|두쫀)$는 -7.354 → -0.000)합니다. 따라서 이 사후 확률을 OOV의 명사 점수로 사용하게 되면 여러 번 반복된 문자열인 '두쫀쿠'는 한 번 쓰인 '두쫀쿠'보다는 더 높은 점수를 받게 될 겁니다.

반면 "됐습니다." 와 같이 워낙 자주 반복되는 문자열이지만 실제로 OOV는 아닌 일반적인 형태는 어떨까요? '됐습니다'의 주어진 문맥에서의 국소빈도가 5라고 가정해보고 마찬가지로 사후 확률을 계산해보면 이번에는 '됐습니다' 자체의 전역 빈도가 높기 때문에 국소빈도를 더해도 사후 분포가 크게 변화하지 않고 그 결과 사후 로그 확률도 처음과 거의 유사하게 유지됩니다. 따라서 '됐습니다'는 현재 문맥에서 많이 쓰였다해도 여전히 OOV 명사 점수는 낮게 추정되고 OOV로 묶이는게 아니라 정상적으로 형태소 분석을 거치게 될 겁니다.

그런데 만약 '됐습니다'가 수 천번 등장하는 문맥이 있다면 어떻게 될까요? 비현실적인 가정이긴 하지만, 국소빈도가 워낙 크기 때문에 사후 로그 확률이 크게 증가하게 되어서 '됐습니다'가 OOV 명사로 잘못 분석될 가능성이 생기게 되겠죠. 그래서 전역 빈도와 국소 빈도를 그대로 사용하지 않고 다음과 같이 상한을 가지도록 약간 변형했습니다. 여기에서 $C_g$는 전역 빈도, $C_l$는 국소 빈도, 그리고 $w_g$는 전역 빈도의 상한, $w_l$는 지역 빈도의 상한입니다. $\tanh(x)$ 함수는 $x$가 커짐에 따라 꾸준히 우상향하지만 아무리 커져도 1을 넘지는 못한다는 특성이 있습니다. 이를 활용해 $C'_g$는 아무리 커도 $w_g$를 넘지 않도록, $C'_l$는 $w_l$를 넘지 못하도록 조정해주었습니다. 여러 실험을 통해 $w_g$는 35로, $w_l$는 3으로 결정했습니다.

$$C'_g = \tanh\left ( C_g / w_g \right ) \cdot w_g$$

$$C'_l = \tanh\left ( C_l / w_l \right ) \cdot w_l$$

성능 비교 & 벤치마크 결과

좋습니다. 이론 상으로는 완벽한것 같은데 실제로도 잘 작동할까요? 이를 위해선 미등재어들이 태깅된 평가 데이터셋이 필요합니다. Kiwi가 이미 잘 알고 있는, 사전에 등재된 명사들만 포함된 문장을 분석하게 되면 OOV 탐지 성능 평가가 아니라 그냥 명사 찾기 평가가 되어버리니깐요. 그렇다면 Kiwi가 한번도 못 본 명사들이 포함된 문장은 어디서 구할 수 있을까요? 개체명인식(Named Entity Recognition, NER)이라는 자연어처리와 정보 검색 분야에서 오랫동안 연구된 과제가 있습니다. 말 그대로 문장 내에서 사람, 장소, 단체 등의 개체명을 인식해내는 작업인데요, 대체로 이런 이름들은 고유 명사이고 끊임없이 새로 만들어지기 때문에 대부분이 미등재어라고 봐도 무방합니다. 따라서 개체명 인식 평가에서 쓰이는 개체명들을 OOV라고 간주하고 Kiwi가 이를 잘못 분해하지 않고 제대로 분석하는지를 살펴본다면 Kiwi의 OOV 탐지 성능을 확인할 수 있습니다. 다행히도 모두의 말뭉치에서 개체명 분석 말뭉치를 제공하고 있으므로 이걸 그대로 가져다가 약간만 수정하여 평가 데이터를 구축했습니다.

다음과 같이 개체명 분석 말뭉치에서 임의로 문장 하나를 뽑고 태깅된 개체명 전체를 그대로 <n> 태그로 감싸고 개체명 타입을 추가했습니다. 추가로 원문을 형태소 분석하여 일반 명사도 찾아 <n>으로 감쌌습니다.

원문: CNP차앤박(OG, 기관)의 화장품 클렌징퍼펙타(AF, 인공물)는 25000원이다.
레이블: <n e="OG">CNP차앤박</n><n>화장품</n> <n e="AF">클렌징퍼펙타</n>는 25000원이다.

빈도 정보의 효과도 검증하기 위해 동일한 OOV가 여러번 반복되는 데이터도 필요한데요, 불행히도 개체명 분석 말뭉치에서 동일한 OOV가 여러 번 등장하는 사례가 너무 적었습니다. 그래서 LLM을 이용해 원문 데이터를 증강하는 방식을 사용했습니다. 크기에 비해 한국어 성능이 좋다고 잘 알려진 kanana-2 instruct 모델을 활용했어요. (덕분에 놀던 제 GPU가 오랜만에 열심히 일했습니다~)

증강 1차: CNP차앤박의 화장품 클렌징퍼펙타는 25000원이다. 이 제품은 뛰어난 클렌징 효과로 유명한데, 얼굴의 메이크업과 불순물을 효과적으로 제거해주는 제품입니다. CNP차앤박은 피부과와의 협력을 통해 개발된 다양한 스킨케어 제품들로 잘 알려져 있죠. 특히 클렌징퍼펙타는 자극이 적고 피부에 부드럽게 작용하여 민감한 피부를 가진 사람들도 안심하고 사용할 수 있습니다.
(각 개체명이 2번 반복됨)

증강 2차: CNP차앤박의 화장품 클렌징퍼펙타는 25000원이다. 이 제품은 뛰어난 클렌징 효과로 유명한데, 얼굴의 메이크업과 불순물을 효과적으로 제거해주는 제품입니다. CNP차앤박은 피부과와의 협력을 통해 개발된 다양한 스킨케어 제품들로 잘 알려져 있죠. 특히 클렌징퍼펙타는 자극이 적고 피부에 부드럽게 작용하여 민감한 피부를 가진 사람들도 안심하고 사용할 수 있습니다. CNP차앤박 클렌징퍼펙타를 사용하면 하루의 피로를 말끔히 씻어내고, 깨끗하고 맑은 피부를 유지할 수 있을 거예요. 클렌징퍼펙타를 통해 CNP차앤박의 뛰어난 품질을 경험해보세요!
(각 개체명이 4번 반복됨)

이렇게 각 개체명이 1번만 등장하는 데이터셋(ne_1), 2번 등장하는 데이터셋(ne_2), 4번 등장하는 데이터셋(ne_4)을 구축했습니다. (평가데이터셋 전체는 kiwipiepy의 Github 저장소에서 확인하실 수 있습니다.)

평가 척도는 다음과 같이 세 가지로 구성했습니다.

  • NE Recall: 태깅된 개체명들 중 분석기가 올바르게 분석해낸 개체명의 비율
  • 명사 F1: 태깅된 모든 명사와 분석기가 찾아낸 명사가 얼마나 일치하는지 F1으로 계산한 것.
  • 명사 ChrF1: 명사 F1과 동일한데 F1 계산시에 단어 단위가 아니라 글자 단위로 점수를 계산한 것.

NE Recall은 미등재어를 잘못 분해하지 않고 얼마나 제대로 쪼개는지를 보기 위한 것입니다. 이번 평가에서 제일 눈여겨봐야 할 지표라고 볼 수 있죠. 그런데 미등재어를 분해하지 않으려고 하다 보면 멀쩡한 일반 명사들도 쪼개지 않고 묶어서 잘못 분석할 수도 있습니다. 그래서 일반 명사들을 분석하는 성능이 떨어지지 않는지를 보기 위해 명사 F1, ChrF1를 보조 지표로 두었습니다. 즉 이번 Kiwi 신기능 평가의 관전 포인트는 과연 명사 F1, ChrF1은 이전 버전보다 떨어지지 않으면서 NE Recall이 올라가는지가 되겠습니다.

형태 정보, 빈도 정보를 반영하는 방법을 바꿔가며 총 4종의 Kiwi를 실험해보았습니다.

  • Kiwi (v0.22.2): 형태, 빈도 정보가 전혀 반영되지 않는 베이스라인 시스템
  • Kiwi (v0.23.0, rule): 규칙 기반으로 형태 정보만 반영합니다. 구체적으로 OOV 점수를 길이에 비례하여 부여합니다.
  • Kiwi (v0.23.0, chr): 형태 정보를 문자 기반의 명사 모델을 통해 반영합니다.
  • Kiwi (v0.23.0, chr_freq): 형태 정보에 빈도 정보까지 반영한 사후 확률을 사용합니다.

결과부터 보면서 이야기해볼까요? Kiwi 이전 버전(v0.22.2)와 최신 버전의 3가지 OOV 처리 모드(rule, chr, chr_freq), 그리고 다른 형태소 분석기들의 평가 결과를 비교한 것입니다. rule, chr, chr_freq 모든 경우에서 이전 버전보다는 개선되었습니다. 그리고 특히 rule과 chr을 비교해보면 chr을 사용시 NE Recall이 50% 이상으로 크게 올라가는 걸 볼 수 있습니다. 즉 문자 기반 모델로 점수를 매기는게 OOV 탐지에 크게 도움이 된다는 걸 알 수 있습니다. 그리고 chr과 chr_freq를 비교해보면, OOV가 한번씩만 등장한 ne_1 데이터셋에서는 chr, chr_freq 간의 점수 차이가 거의 없지만, 각각 2번, 4번씩 등장한 ne_2, ne_4 데이터셋에서는 chr_freq의 점수가 크게 올라가는 것을 확인할 수 있습니다. 즉 빈도 정보까지 활용하는 경우 확실히 OOV 탐지 성능이 올라간다는게 입증된 셈입니다. 보조 지표인 명사 F1와 ChrF1도 Kiwi v0.23.0에서 하락한 경우는 없고 일관되게 개선된 것으로 측정되었습니다. 즉 일반 명사 분석 성능에는 해를 끼치지 않으면서 OOV만 더 잘 잡게 되었다고 주장할 수 있겠네요. 

다른 형태소 분석기들의 결과도 살펴볼까요? 먼저 주목할 만한 것은 Hannanum입니다. Hannanum의 경우 Kiwi보다 NE Recall이 아주 높게 나오는데요, 사실 이건 Hannanum이 잘 모르는 글자 배열에 대해서는 임의로 분할하는 대신 통으로 묶어 버리는 전략을 취하기 때문에 그렇습니다. 그래서 명사 F1, ChrF1에서는 반대로 Hannanum이 Kiwi보다 낮게 나옵니다. 즉 애매한 것들을 다 OOV 취급하다보니 잘 아는 단어들까지도 분석하지 않고 OOV로 묶어 버리는 실수를 한다고 볼 수 있겠네요. Khaiii와 Bareun은 비교적 NE Recall과 명사 F1, ChrF1이 균형있게 높은 점수를 기록하고 있습니다. 특히 Bareun은 ne_1에서 NE Recall이 제일 잘 나오고 있어서 확실히 Transformer 모델의 강력함을 잘 보여줍니다. 다만 빈도 정보까지는 활용하지 못하는지 ne_2, ne_4에서는 chr_freq 기반의 Kiwi보다는 약간 성능이 낮네요.

다음은 최적의 문자 모델을 고르기 위해 하이퍼파라미터를 탐색했던 과정입니다. 먼저 문자 모델의 문맥 길이별로 OOV 탐지 성능이 어떻게 바뀌는지 살펴봤습니다.

문맥이 길수록 좋을거라는 예상과는 다르게 3~4일때 가장 높은 성능을 찍고 서서히 우하향하는 모습을 보였습니다. CoNg이 신경망 구조이긴 해도 문맥 처리는 근본적으로 n-gram과 유사하기 때문에 이런 부분에서는 n-gram과 유사한 모습을 보이는 것 같네요. OOV가 여러번 등장하는 경우보다 1번만 등장할 경우가 더 잦을 것이라고 생각했기에 ne_1에서 제일 높은 성능을 보일 수 있도록 문맥 길이는 4로 잡았습니다.

그리고 전역 빈도의 상한을 결정하는 하이퍼파라미터 $w_g$에 대한 실험도 진행했습니다. $w_g$가 작으면 작은 국소 빈도도 상대적으로 많이 반영되기 때문에 ne_2, ne_4 데이터셋의 정확도는 높아집니다. 반면 실제로 OOV 명사가 아님에도 우연히 여러번 등장한 문자열이 OOV로 오분석될 가능성이 있기 때문에 ne_1에서는 정확도가 살짝 떨어집니다. 반대로 $w_g$가 커질수록 ne_1에서의 정확도는 상승하지만 ne_2, ne_4의 정확도는 떨어지죠. 최적의 문맥 길이를 고를때와 마찬가지 이유로 ne_1의 점수가 안정적인게 가장 중요하다는 판단 하에 ne_1 정확도가 더이상 오르지 않는 $w_g$ = 35 지점을 최적값으로 선택하게 되었습니다.

속도 비교

OOV 분석 성능이 개선된 건 확실해 보이는데요, 문자 기반의 명사 모델을 도입하고 또 국소 빈도를 세고 사후 확률을 계산하는 등의 작업 때문에 형태소 분석 과정이 느려지지 않을까요? 다행히도 그렇지는 않습니다. 분석 과정에서 OOV의 발생빈도가 높지 않고 또 OOV의 명사 점수의 경우 한 번 계산해두면 동일한 형태가 다시 등장했을 때 재사용이 가능하기 때문에 연산량을 추가로 줄일 수 있는 방법이 얼마든지 있기 때문이죠.

  분석 속도(KB/s) 속도 증감(%)
Kiwi 0.22.2 - 335.7681 0.0%
Kiwi 0.23.0 rule 348.8715 +3.9%
chr 337.2581 +0.4%
chr_freq 330.7070 -1.5%

실제로 이전 버전의 분석 속도를 베이스라인으로 삼고, 0.23.0에서 도입된 3가지 기법의 분석 속도를 측정해본 결과는 위와 같습니다. oov_handling = chr를 사용한 경우 rule보다 속도가 3% 가량 감소하는 것을 확인할 수 있습니다. 다만 0.23.0 업데이트에서 적용한 다양한 최적화 덕분에 oov_handling = chr일때의 속도를 이전 버전과 유사하게 맞출 수 있었습니다. chr_freq의 경우 국소 문맥의 빈도를 조사하고 이를 사후 확률에 통합하는 과정이 포함되어 속도 하락이 조금 더 발생하지만 여전히 이전 버전과 비교해서 크게 느린 수준은 아닙니다. 즉 사실 상 이전 버전과 유사한 속도를 유지하면서 OOV 처리를 개선하는데에 성공했다고 볼 수 있겠습니다.

실제 사례 분석

수치로만 보면 감이 잘 안 오죠? 실제 형태소 분석 사례들을 살펴보면서 chr, chr_freq 기법이 어떤 효과가 있는지 확인해봅시다. (참고로 Kiwi 데모 페이지kiwi-gui 저장소에서 다음의 분석 결과를 직접 테스트해보실 수 있습니다.)

원문 oov_handling = rule oov_handling = chr
배그또하고싶다 진짜재미있었는데 배그또/NNG/XSV/EC/VX/EF 진짜/MAG 재미있/VA/EP 는데/EC 배그/NNG/MAG/VV/EC/VX/EF 진짜/MAG 재미있/VA/EP 는데/EC
김병근 국립해양문화재연구소 학예연구관은 27일 논문을 발표했다. /NNP/NNG/NNG 국립해양문화재연구소/NNP 학예/NNG 연구관/NNG/JX 27/SN/NNB 논문/NNG/JKO 발표/NNG/XSV/EP/EF ./SF 김병근/NNP 국립해양문화재연구소/NNP 학예/NNG 연구관/NNG/JX 27/SN/NNB 논문/NNG/JKO 발표/NNG/XSV/EP/EF ./SF
아니 이 맹고스틴들 뭐야 진짜 맛있잖아 아니/IC/MM 맹고스틴들/NNG/NP/VCP/EF 진짜/MAG 맛있/VA 잖아/EF 아니/IC/MM 맹고스틴/NNG/XSN/NP/VCP/EF 진짜/MAG 맛있/VA 잖아/EF
오늘 저녁 롯데리아 모짜새우버거는 어때? 오늘/NNG 저녁/NNG 롯데리아/NNP 모짜새우버거/NNG/JX 어떻/VA-I/EF ?/SF 오늘/NNG 저녁/NNG 롯데리아/NNP 모짜/NNG 새우/NNG 버거/NNG/JX 어떻/VA-I/EF ?/SF
마켓컬리에있어요! 금미옥떡볶이라구 되게맛나요~ 마켓컬리/NNP/JKB/VA 어요/EF !/SF 금미옥떡볶이라구/NNG 되게/MAG 맛나/VA 어요/EF ~/SO 마켓컬리/NNP/JKB/VA 어요/EF !/SF 금미옥떡볶이/NNG/VCP 라구/EC 되게/MAG 맛나/VA 어요/EF ~/SO

OOV의 길이만 가지고 추정하는 기법인 oov_handling=rule에서는 미등재어에 뒤에 붙은 조사나 접미사들을 대체로 분해하지 않고 통으로 분석하는 경향이 있습니다. 사람 이름 같은 경우는 아예 쪼개버리기도 하구요. 반면 문자 모델로 OOV의 확률을 추정하면 oov_handling=chr에서 보이는것처럼 조사나 접미사들이 붙은 경우에 비교적 경계를 잘 잡아줍니다. "배그또"는 명사답지 못하지만 "배그"는 명사답다는 것, "금미옥떡볶이라구"는 명사답지 못하지만 "금미옥떡볶이"는 명사답다는 것을 알려주기 때문이죠. 물론 "모짜새우버거"처럼 통으로 분석하는게 맞는 상황에서도 쪼개는 경우도 종종 있긴 합니다.

원문 oov_handling = chr oov_handling = chr_freq
삼성증권의 '슈퍼스텝다운주가연계증권'은 지난달 출시 후 한달여 만에 900억원이 넘는 자금을 끌어 모았다. 슈퍼스텝다운주가연계증권은 주가의 변동에 따라 수익률이 결정되는 상품으로 삼성증권/NNP/JKG '/SSO 슈퍼/NNG 스텝/NNG/XSA-I/ETM 주가/NNG 연계/NNG 증권/NNG '/SSC/JX 지난달/NNG 출시/NNG/NNG/MM/NNB/XSN/NNB/JKB 900/SN/NR/NNB/JKS/VV/ETM 자금/NNG/JKO/VV/EC 모으/VV/EP/EF ./SF 슈퍼스텝/NNG/XSA-I/ETM 주가/NNG 연계/NNG 증권/NNG/JX 주가/NNG/JKG 변동/NNG/JKB 따르/VV/EC 수익/NNG/XSN/JKS 결정/NNG/XSV/ETM 상품/NNG 으로/JKB 삼성증권/NNP/JKG '/SSO 슈퍼스텝다운주가연계증권/NNG '/SSC/JX 지난달/NNG 출시/NNG/NNG/MM/NNB/XSN/NNB/JKB 900/SN/NR/NNB/JKS/VV/ETM 자금/NNG/JKO/VV/EC 모으/VV/EP/EF ./SF 슈퍼스텝다운주가연계증권/NNP/JX 주가/NNG/JKG 변동/NNG/JKB 따르/VV/EC 수익/NNG/XSN/JKS 결정/NNG/XSV/ETM 상품/NNG 으로/JKB
수능문제가 한능검보다 더 어렵습니다. 한능검 시험은 한국사에 대한 이해를 평가하는 시험이죠. 수능/NNG 문제/NNG/JKS /MM/NNG/NNG 보다/JKB/MAG 어렵/VA-I 습니다/EF ./SF /MM/NNG/NNG 시험/NNG/JX 한국사/NNG/JKB 대하/VV/ETM 이해/NNG/JKO 평가/NNG/XSV/ETM 시험/NNG/VCP/EF ./SF 수능/NNG 문제/NNG/JKS 한능검/NNP 보다/JKB/MAG 어렵/VA-I 습니다/EF ./SF 한능검/NNG 시험/NNG/JX 한국사/NNG/JKB 대하/VV/ETM 이해/NNG/JKO 평가/NNG/XSV/ETM 시험/NNG/VCP/EF ./SF
IFC몰 지하 올리브영에 갔지만, 식품판매 제외 매장이여서 결국 못사고, 다른 지점 올리브영을 찾아가서 통밀당단백칩을 사왔어요. IFC/SL/NNG 지하/NNG 올리브/NNG/NNG/JKB/VV/EP 지만/EC ,/SP 식품/NNG 판매/NNG 제외/NNG 매장/NNG/VCP 여서/EC 결국/MAG/MAG/VV/EC ,/SP 다른/MM 지점/NNG 올리브/NNG/NNG/JKO 찾아가/VV 어서/EC 통밀당단백칩/NNG/JKO/VV/EC/VV/EP 어요/EF ./SF IFC/SL/NNG 지하/NNG 올리브영/NNG/JKB/VV/EP 지만/EC ,/SP 식품/NNG 판매/NNG 제외/NNG 매장/NNG/VCP 여서/EC 결국/MAG/MAG/VV/EC ,/SP 다른/MM 지점/NNG 올리브영/NNP/JKO 찾아가/VV 어서/EC 통밀당단백칩/NNG/JKO/VV/EC/VV/EP 어요/EF ./SF
앤디워홀의 대표적인 실크스크린 작품 ‘메릴린먼로’ 시리즈 10점 등 대중과 친숙한 작품들이 수두룩하다. 특히 앤디워홀의 실크스크린 기법은 현대 미술사에서 매우 중요한 위치를 차지하고 있는데 앤디/NNP 워홀/NNP/JKG 대표/NNG/XSN/VCP/ETM 실크/NNG 스크린/NNG 작품/NNG/SSO 메릴린먼로/NNP/SSC 시리즈/NNG 10/SN/NNB/NNB 대중/NNG/JKB 친숙/NNG/XSA/ETM 작품/NNG/XSN/JKS 수두룩하/VA/EF ./SF 특히/MAG 앤디/NNP 워홀/NNP/JKG 실크/NNG 스크린/NNG 기법/NNG/JX 현대/NNG 미술사/NNG 에서/JKB 매우/MAG 중요/NNG/XSA/ETM 위치/NNG/JKO 차지/NNG/XSV/EC/VX 는데/EC 앤디/NNP 워홀/NNP/JKG 대표/NNG/XSN/VCP/ETM 실크/NNG 스크린/NNG 작품/NNG/SSO 메릴린먼로/NNP/SSC 시리즈/NNG 10/SN/NNB/NNB 대중/NNG/JKB 친숙/NNG/XSA/ETM 작품/NNG/XSN/JKS 수두룩하/VA/EF ./SF 특히/MAG 앤디워홀/NNP/JKG 실크/NNG 스크린/NNG 기법/NNG/JX 현대/NNG 미술사/NNG 에서/JKB 매우/MAG 중요/NNG/XSA/ETM 위치/NNG/JKO 차지/NNG/XSV/EC/VX 는데/EC
MIT테크놀로지리뷰는 올해 주목해야 할 기술로 폴더블 디스플레이와 스마트폰을 꼽았다. MIT테크놀로지리뷰는 기술의 발전이 앞으로의 전자기기 시장에 어떤 변화를 가져올지에 대해 깊이 있게 다루고 있다. MIT/SL 테크놀로지/NNG 리뷰/NNG/JX 올해/NNG 주목/NNG/XSV 어야/EC/VX/ETM 기술/NNG/JKB/NNG 더블/NNG 디스플레이/NNG/JC 스마트폰/NNG/JKO/VV-R/EP/EF ./SF MIT/SL 테크놀로지/NNG 리뷰/NNG/JX 기술/NNG/JKG 발전/NNG/JKS/NNG 으로/JKB/JKG 전자/NNG 기기/NNG 시장/NNG/JKB 어떤/MM 변화/NNG/JKO 가져오/VV ᆯ지/EC/JKB 대하/VV/EC 깊이/MAG/VV/EC 다루/VV/EC/VX/EF ./SF MIT테크놀로지리뷰/NNP/JX 올해/NNG 주목/NNG/XSV 어야/EC/VX/ETM 기술/NNG/JKB/NNG 더블/NNG 디스플레이/NNG/JC 스마트폰/NNG/JKO/VV-R/EP/EF ./SF MIT/SL 테크놀로지리뷰/NNG/JX 기술/NNG/JKG 발전/NNG/JKS/NNG 으로/JKB/JKG 전자/NNG 기기/NNG 시장/NNG/JKB 어떤/MM 변화/NNG/JKO 가져오/VV ᆯ지/EC/JKB 대하/VV/EC 깊이/MAG/VV/EC 다루/VV/EC/VX/EF ./SF

이번에는 빈도정보는 고려하지 않는 chr와 빈도정보까지 고려한 chr_freq 간의 형태소 분석 결과 비교입니다. 동일한 OOV가 두 번 반복될때 어떻게 분석 결과가 달라지는지를 볼 수 있네요. chr에서는 잘개 쪼개는 OOV를 chr_freq에서는 통으로 묶어내는 비율이 늘어났습니다. 물론 chr_freq도 완벽하지는 않아서 앤디워홀이나 MIT테크놀로지리뷰의 사례처럼 첫번째는 '앤디 + 워홀'로 분할하고 두번째는 '앤디워홀'로 묶어서 분석하는 경우가 보입니다. 이는 각 단어가 위치한 문맥에 따라 '앤디 + 워홀'로 분석하는 게 언어 모델 상 확률이 높기도 하고 또 다른 경우에는 '앤디워홀'로 묶어서 분석하는게 확률이 높기도 해서 발생하는 현상입니다. 즉, 두 확률이 매우 근접한 값에서 서로 경쟁하고 있는 상황인데요 이 때문에 같은 형태의 단어를 입력해도 주변 문맥에 따라 분석 결과가 전혀 다르게 나타날 수 있어 아쉽게도 결과의 예측이 조금 떨어지고 있습니다.

이 때문에 chr_freq는 현재 선택 가능한 옵션으로만 두고 Kiwi v0.23.0에서는 빈도 정보는 고려하지 않더라도 결과가 일정하게 나오는 chr를 oov_handling의 기본값으로 채택했습니다. 빈도 정보를 활용하는 chr_freq은 텍스트가 바뀔 때마다 분석 결과가 요동칠 수 있는 반면 chr은 빈도 정보를 활용 못하더라도 예측성 있는 결과가 나오기 때문입니다. 확률값 경쟁 때문에 동일한 형태가 문맥에 따라 서로 다르게 분석되는 문제는 어떻게 개선할지 추가적으로 고민해볼 필요가 있습니다.

또 눈치 채셨을지도 모르지만, OOV에 일반명사 NNG를 태깅하는지 고유명사 NNP를 태깅하는지도 입력마다 제각각입니다. 사실 일반명사와 고유명사의 경계는 굉장히 모호하기도 하고 모델이 이를 정확히 예측하는게 불가능하기 때문에 다소 무작위적인 태깅 결과가 나오게 되는데 이를 하나로 일관성 있게 통일하는 작업도 필요한 상황입니다. 아니면 아예 OOV로 분석된 명사에는 별도의 태그를 할당하는게 나을지도 모르겠습니다. 추후 업데이트에서는 이 부분을 좀 더 고도화해봐야겠네요. 긴 글 읽어주셔서 감사드리며 다음에도 재미난 업데이트 들고 돌아오도록 하겠습니다.

관련글 더보기

댓글 영역