Symspell을 이용한 한글 맞춤법 교정 2 — 복합어와 띄어쓰기 교정

김희규
6 min readNov 18, 2021

--

Photo by Kristin Wilson on Unsplash

이전 글에서는 한 어절에 대해 오타교정을 하는 방법에 대해서 알아봤습니다. 하지만 띄어쓰기 오류는 해결할 수 없었는데요, 이를 위해 이번 글에서는 SymSpell에서 제공하는 또다른 기능인 복합어 교정과 단어 분할에 대해서 알아보겠습니다.

Compound aware multi-word spelling correction

영어로 좀 긴데 줄여서 복합어 교정이라고 부르겠습니다. 복합어 교정은 아래 3가지 경우를 찾아서 교정하는 과정입니다.

  1. 한 어절에 띄어쓰기를 넣어서 실수로 두 어절로 분리된 경우
    예: 배고파 → 배 고파
  2. 두 어절 사이에 띄어쓰기 해야하는데 실수로 빼서 하나로 붙여쓴 경우
    예: 같이먹자 → 같이 먹자
  3. 1 혹은 2가 발생했는데 거기에다가 오타까지 발생한 경우
    예: 괜찮지앟ㄴ을까 → 괜찮지 않을까?

Word Segmentation

단어 분할은 마찬가지로 띄어쓰기와 오타 교정을 동시해 해줍니다. 차이점은 복합어 교정은 한 어절에 하나의 띄어쓰기만 넣어서 분리할 수 있지만 단어 분할은 여러 띄어쓰기를 넣을 수 있습니다. 따라서 아래처럼 띄어쓰기가 전혀 없는 문장을 줘도 상관없습니다. 단점으로는 복합어 교정보다 속도가 느립니다.

아버지가방에들어가신다

이 기능은 띄어쓰기 교정 외에도, OCR이나 음성인식 결과물에 적용해서 정확한 문장을 만드는 데에도 사용하기 좋다고합니다. SymSpell은 단어 분할에 Dynamic Programming을 사용하지 않고 Triangular Matrix를 이용해서 메모리 효율을 높였습니다. 보다 자세한 설명은 아래 글에 나와있습니다.

Bigram Dataset

한 어절을 두 어절로 분할하기 위해서는, 둘로 나뉜 어절이 올바른 어절인지도 확인해야 하지만, 두 어절로 나뉘는 게 맞다는 걸 알려줄 사전도 필요합니다. 이를 위해서 필요한 건 이전 글에서 사용한 Term Frequency 데이터 외에도 Bigram 데이터가 필요합니다. Bigram이란 “밥을 먹었다” 처럼 두개의 어절이 이어진 문장을 말합니다. 한 어절이라면 Unigram, 세 어절이라면 Trigram이라고 부릅니다.

symspellpy에는 영어 bigram 사전이 내장되어있습니다. 이 사전 파일을 보면 아래처럼 되어있습니다.

abcs of 10956800
aaron and 10721728
abbott and 7861376
abbreviations and 13518272
aberdeen and 7347776
ability to 2199042048
... 총24만개

순서대로 Bigram의 첫번째 어절, 두번째 어절이고 세번째 숫자는 이 Bigram이 Bigram을 구축하는데 사용한 문서 내에서 출현한 횟수입니다. 이제 복합어 교정과 단어분할 두가지는 이 사전과 symspellpy를 이용하면 손쉽게 할 수 있습니다.

예제원문: https://symspellpy.readthedocs.io/en/latest/examples/index.html

한국어 띄어쓰기 교정

한국어도 마찬가지로 사전을 구해서 symspellpy에 주기만 한다면 그대로 사용할 수 있습니다. 음소 단위로 분해한 사전을 준다면 음소단위로 오타를 교정해줄 수도 있지요. 하지만 한국어로 공개된 데이터셋은 찾아봤는데 없는 것 같습니다 😥

구글에서는 여러 서적에서 어휘를 분석해서 알려주는 N-gram viewer가 있고 여러 나라 언어의 데이터도 공개되어있습니다. 하지만 여기에 한국어는 포함이 안되어있습니다. 한국어에서 사용하려면 직접 bigram 데이터셋을 구축하셔야합니다.

그래서 제가 국립국어원 모두의 말뭉치 데이터셋을 이용해서 Bigram 데이터셋을 구축하는 프로젝트를 진행하고있습니다. symspellpy-ko라는 프로젝트인데요. symspellpy의 기능을 한글 음소분해와 함께 제공해주고 한국어 데이터셋도 포함해서 사용할 수 있습니다.

추가로 symspellpy의 word_segmentation 함수 내부의 아래 코드에서 부분때문에 음소분해를 하면 다시 합쳐버려서 원하는 결과가 나오지 않는 문제가 있어서 해당 부분도 수정했습니다 😁

phrase = unicodedata.normalize("NFKC", phrase).replace("\u002D", "")

아쉬운 점

100만개의 Bigram을 추출해서 데이터셋을 만들었지만 아직도 사전에 이상한 글자가 껴있거나 사전에 없는 부족한 어절들도 있는 것 같습니다. 한국어가 조사나 어미가 많아서 필요한 어절의 개수가 훨씬 더 많아서 그럴 것입니다. 그래선지 lookup_compound 와 word_segmentation의 결과가 제가 원하던 것처럼 매끄럽지 않습니다. 😩😭

군대에 있다보니 프로젝트 진행이 쉽지많은 않네요 ㅠ 그래도 계속 발전시켜보겠습니다. 한글 오타교정 기능이 필요하거나 관련 데이터가 필요하다면 많이 부족하지만 유용하게 사용하셨으면 합니다.

--

--

김희규
김희규

Written by 김희규

나는 최고의 선수다. 나를 최고라고 믿지 않는 사람은 최고가 될 수 없다.

No responses yet