Books/한국어 임베딩

[Books] 한국어 전처리

언킴 2022. 3. 7. 21:49
반응형

이기창님께서 작성하신 한국어 임베딩을 정리 및 요약했습니다.

 

영어의 경우 상대적으로 전처리하는 작업이 수월하다. 하지만 한국어의 경우 띄어쓰기가 사람마다 제각각일 수 있고, 단어의 순서가 바뀌더라도 해석하는데 있어 문제가 되지 않는다. 그로 인해 다른 언어에 비해 전처리하는 과정이 어렵다. 하지만 이 책에서는 한국어를 전처리하는 과정을 상세히 작성해주었기 때문에 많은 도움이 될 것이다.

 

본 책에서는 위키피디아, 네이버 영화 말뭉치 등 여러 오픈 데이터를 기반으로 한국어 전처리를 수행하였다. 한국어 형태소 분석기에는 은전한닢, 꼬꼬마, 한나눔, Okt, 코모란 등 5개의 오픈소스 형태소 분석기가 존재하는데 KoNLPy에서 모두 사용 가능하도록 제공해주고 있다. 또한, 형태소 분석기를 지도 학습 형태소 분석기와 비지도 학습 형태소 분석기로 나누고 있다. 지도 학습 형태소 분석기로는 KoNLPy와 Khaiii가 있고, 비지도 학습 형태소 분석기는 soynlp와 구글 센텐스피스 등이 존재한다. 

 

KoNLPy

KoNLPy는 한국어 자연어 처리 패키지이며, C++, 자바 등 다른 언어로 개발된 오픈소스들을 한군데에 묶어 쉽게 사용할 수 있도록 돕는다. 각각의 형태소 분석기 마다 장단점이 존재한다. Okt의 경우 SNS와 같은 도메인에서 성능이 좋다고 알려져있고, Mecab(은전한닢)의 경우 속도가 빠르다고 알려져있다. 본인이 선호하는 형태소 분석기를 사용해 적절히 활용하면 된다. 

 

from konlpy.tag import Okt, Komoran, Mecab, Hannanum, Kkma 

def get_tokenizer(tokenizer_name):
    if tokenizer_name == 'komoran':
        tokenizer = Komoran()
    
    elif tokenizer_name == 'okt':
        tokenizer = Okt()

    elif tokenizer_name == 'mecab':
        tokenizer = Mecab()

    elif tokenizer_name == 'hannanum':
        tokenizer = Hannanum()

    elif tokenizer_name == 'kkma':
        tokenizer = Kkma()

    return tokenizer 

tokenizer = get_tokenizer('komoran')
tokenizer.morphs('아버지가방에들어가신다')
# ['아버지', '가방', '에', '들어가', '시', 'ㄴ다']

tokenizer.pos('아버지가방에들어가신다')
# [('아버지', 'NNG'), ('가방', 'NNP'), ('에', 'JKB'), ('들어가', 'VV'), ('시', 'EP'), ('ㄴ다', 'EC')]

 

 

Khaiii

Khaiii(KaKao Hangul Analyzer III)는 카카오가 2018년 말에 공개한 오픈소스 한국어 형태소 분석기이다. Khaiii는 입력 문장을 문자 단위로 읽어 들인 뒤 Convolution filter가 문자를 슬라이딩 해가면서 정보를 추출한다. 카카오의 설명에 따르면 모델을 C++로 구현해 GPU없이도 형태소 분석이 가능하며 속도 역시 빠르다고 한다. 

 

khaiii architecture

# khaiii
from khaiii import KhaiiiApi
tokenizer = KhaiiiApi()

data = tokenizer.analyze('아버지가방에들어가신다')

tokens = []
for word in data:
    tokens.extend([str(m).split('/')[0] for m in word.morphs])

tokens
# ['아버지', '가', '방', '에', '들어가', '시', 'ㄴ다']

tokens = []
for word in data:
    tokens.extend([str(m) for m in word.morphs])

tokens
# ['아버지/NNG', '가/JKS', '방/NNG', '에/JKB', '들어가/VV', '시/EP', 'ㄴ다/EC']

 

 

 

soynlp

기존의 지도 학습 기반 형태소 분석은 사전에 언어 전문가들이 직접 형태소 경계나 품사 정보를 모델에 가르쳐줘서 학습된 모델이지만, 비지도 학습 기반 형태소 분석은 데이터의 패턴을 모델이 스스로 학습하게 함으로써 형태소를 분석하는 방법이다. 데이터에 자주 등장하는 단어들을 형태소로 인식한다. soynlp는 비지도 학습 기반의 형태소 분석 중 하나이다.

 

soynlp는 데이터 패턴을 스스로 학습하는 비지도 학습 접근법을 지향하기 때문에 하나의 문장 혹은 문서에서보다는 어느 정도 규모가 있으면서 동질적인 문서 집합에서 잘 작동한다. soynlp에서는 단어 점수를 크게 응집 확률(Cohesion Probability)과 브랜칭 엔트로피(Branching Entropy)를 활용한다. 

 

  • '꿀잼' 이라는 단어가 연결돼 자주 나타났다면 '꿀잼'을 형태소라고 본다 -> 응집 확률이 높은 경우 
  • '꿀잼' 앞에 '영화', '정말' 등 문자열이, 뒤에 'ㅋㅋ', 'ㅎㅎ' 등의 패턴이 다양하게 나타났다면 '꿀잼'을 형태소로 취급한다. -> 브랜칭 엔트로피가 높은 경우 

 

우리가 가지고 있는 말뭉치의 통계량을 바탕으로 하기 때문에 지도 학습 기반 방법과는 달리 별도의 학습 과정을 거쳐야 한다. soynlp의 단어 점수를 학습하는 코드는 다음과 같다. 

 

# soynlp 
from soynlp.word import WordExtractor 
import sys
sys.path.append('..')
corpus_fname = '/notebooks/embedding/data/processed/processed_ratings.txt'
model_fname = '/notebooks/embedding/data/processed/soyword.model'

sentences = [sent.strip() for sent in open(corpus_fname, 'r').readlines()]

word_extractor = WordExtractor(min_frequency=100, min_cohesion_forward=0.05, min_right_branching_entropy=0.0)

word_extractor

# soynlp 학습하기.
word_extractor.train(sentences)

word_extractor.save(model_fname)

 

soynlp는 입력 문장의 왼쪽부터 문자 단위로 슬라이딩해 가면서 단어 점수가 높은 문자열을 우선으로 형태소로 취급해 분리한다. 한국어는 명사에 조사가 붙거나, 용언(형용사/동사)에 어미가 붙어 활용되는 교착어이기 때문에 왼쪽부터 슬라이딩해 가면서 분석해도 높은 품질을 기대할 수 있기 때문이다. 

 

import math 
from soynlp.word import WordExtractor 
from soynlp.tokenizer import LTokenizer 

model_fname = '/notebooks/embedding/data/processed/soyword.model'

word_extractor = WordExtractor(
    min_frequency=100, 
    min_cohesion_forward=0.05, 
    min_right_braching_entropy=0.0
    )

word_extractor.load(model_fname)
scores = word_extractor.word_scores()
scores = {key:(scores[key].cohesion_forward \
    *math.exp(scores[key].right_branching_entropy)) for key in scores.keys()}

tokenizer = LTokenizer(scores=scores)
tokens = tokenizer.tokenize('애비는 종이었다')

tokens

계산된 스코어를 기준으로 각 단어들을 형태소 분석을 진행한다. 

 

 

구글 센텐스피스(sentencepiece)

센텐스피스(sentencepiece)는 구글에서 공개한 비지도 학습 기반 형태소 분석 패키지다. 1994년 제안된 BPE(Byte Pair Encoding) 기법을 지원한다. BPE의 기본 원리는 말뭉치에서 가장 많이 등장한 문자열을 병합해 문자열을 압축하는 것이다. 우리가 원하는 사전의 크기가 될 때까지 반복적으로 고빈도 문자열들을 병합해 어휘 집합에 추가한다. 그 후 문장 내 각 어절에 어휘 집합에 있는 서브워드가 포함돼 있을 경우 해당 서브워드를 어절에서 분리한다. 이후 어절의 나머지에서 어휘 집합에 있는 서브워드를 다시 찾고, 또 분리한다. 끝까지 찾았을 때 어휘 집합에 없으면 Unknown word로 취급한다. 보다 자세한 내용은 여기를 참고하길 바란다.  BPE의 코드는 다음과 같다. 

import sentencepiece as spm
train = """--input=/notebooks/embedding/data/processed/processed_wiki_ko.txt \
    --model_prefix=sentpiece \
    --vocab_size=32000 \
    --model_type=bpe --character_coverage=0.9995"""

spm.SentencePieceTrainer.Train(train)

BPE 모델이 제대로 학습되고 있는 것을 확인할 수 있다. BPE로 학습한 어휘를 BERT에서도 사용할 수 있다고 한다. 문자열 기반 비지도 학습이기에 데이터만 충분히 확보된다면 BERT에서 필요로하는 [MASK], [PAD] 등의 스페셜 토큰을 추가한 후 적용할 수 있을 것이다.