NLP

NLP 전처리

형태소

형태소란?

형태소 분석이란?

Tokenizing 라이브러리

영어 Tokenizing 라이브러리

  • 1) NLTK

    • 파이썬에서 영어 텍스트 전처리 작업을 하는데 많 쓰이는 라이브러리로, 이 라이브러리는 50여 개가 넘는 말뭉치 리소를 활용해 영어 텍스트를 분석할 수 있게 제공 한다. 직관적으로 함수를 쉽게 사용할 수 있게 구성돼 있어 빠르게 텍스트 전처리를 할 수 있다. 또한 단어 단위 토크나이징문장 단위 토크나이징을 하는 모듈이 따로 있으며 ‘a’, ‘the’ 같은 관사나 ‘is’와 같이 자주 의미는 별로 없지만 자주 등장하는 단어인 불용어들을 모아 불용어 사전을 구성하고 있어 따로 불용어를 정의할 필요없이 바로 사용가능하다.
  • 2) Spacy

    • NLTK와 같은 오픈소스 라이브러리이다. 주로 상업용 목적으로 만들어졌다는 점이 NLTK와 다르며, 영어를 포함한 8개 언어에 대한 자연어 전처리 모듈을 제공하고, 빠른 속도로 전처리할 수 있다. 원하는 언어에 대한 전처리를 한 번에 해결할 수 있다는 장점이 있으며, 특히 딥러닝 언어 모델의 개발도 지원하고 있어 매력적이다. NLTK와 다르게 단어 단위, 문장 단위 토크나이징을 한가지 모듈을 통해 처리한다.
  • 이러한 영어 토크나이징 도구는 한국어에 적용할 수 없다!!

한글 토크나이징 라이브러리

  • 자연어 처리에서 각 언어마다 모두 특징이 다르기 때문에 천편일률적으로 동일한 방법을 적용하기는 어렵다. 한국어 자연어 처리에 많이 사용되는 파이썬 라이브러리 KoNLPy를 소개하겠다.

KoNLPy

  • 1) KoNLPy(지도학습 기법으로 학습)

    • 한글 자연어 처리를 쉽고 간결하게 처리할 수 있도록 만들어진 오픈소스 라이브러리이다. 또한 국내에 이미 만드어져 사용되고 있는 여러 형태소 분석기를 사용할 수 있게 허용한다. 형태소 분석으로 형태소 단위의 토크나이징을 가능하게 할뿐만 아니라 구문 분석을 가능하게 해서 언어 분석을 하는 데 유용한 도구다.

    • 한글 텍스트의 경우에는 형태소 단위 토크나이징이 필요할 때가 있다. KoNLPy에서는 여러 형태소 분석기를 제공하며, 각 형태소 분석기별로 분석한 결과가 다르므로 자신의 분석 데이터에 맞는 형태소 분석기를 선택해서 사용할 것을 권한다. Mecab의 경우 원도우에서는 사용할 수 없으니 참고해서 사용하자.

      • Hannanum (한나눔)
      • Kkma (꼬꼬마)
      • Komoran (코모란)
      • Mecab (메케브)
      • Okt(Twitter)
  • KAIST에서 개발된 한나눔은 다음과 같은 메소드를 제공한다.

한나눔 형태소 분석기

  • 서울대학교에서 개발된 한나눔은 다음과 같은 메소드를 제공한다.

꼬꼬마 형태소 분석기

  • 코모란은 다음과 같은 메소드를 제공한다.

코모란 형태소 분석기

  • mecab은 은전한닢이란 의미를 지니고 있으며(TMI인듯), 빠르고 성능이 우수한 것으로 알려져 있다. 다음과 같은 메소드를 제공한다.

Mecab 형태소 분석기

  • Okt는 구 Twitter로 불리며, Twitter에서 만들었다. 다음과 같은 메소들르 제공한다.

Okt 형태소 분석기

macOS에서 설치

1
2
3
4
5
6
7
8
9
# JPype1은 파이썬에서 자바 클래스를 사용할 수 있도록 만들어주는 라이브러리이다.
# 만약 window라면 https://www.lfd.uci.edu/~gohlke/pythonlibs/#jpype에서 맞는 사양을 설치
# 64bit-python3.6 버전이라면 JPype1-0.63-cp36-cp36m-win_amd64.whl 을 설치하면된다.
# pip install JPype1-0.63-cp36-cp36m-win_amd64.whl

# MacOs에선
conda install -c conda-forge jpype1

pip install konlpy

  • 형태소 분석기 사용법

    • 각각의 형태소 분석기는 클래스생성만 다르고 나머지는 동일한 함수를 사용하므로 아래 예시에서는 형태소 분석기 중 제일 속도가 빠르다고 알려져 있는 Mecab을 사용할 것이다.

    • pos를 통해 얻는 품사의 태깅의 의미를 알고 싶다면 클릭

    • tokenizer.morphs()

      • 텍스트를 형태소 단위로 나눈다. 옵션으로는 norm과 stem이있다. 각각 True 혹은 False 값을 받으며, norm은 normalize의 약자로서 문장을 정규화하는 역힐을 하고, stem은 각 단어에서 어간을 추출하는 기능(예시: 해야지 -> 하다)이다. 각각 True로 설정하면 각 기능이 적용 된다. 둘 다 default는 False이다.
    • tokenizer.nouns()

      • 텍스트에서 명사만 뽑아낸다.
    • tokenizer.phrases()

      • 텍스트에서 어절을 뽑아낸다.
    • tokenizer.pos()

      • 위의 세 함수는 추출기인 반면에, pos 함수는 태깅함수이다. 각 품사를 태깅하는 역할을 한다. norm, stem 옵션이 존재하며 join=True로 하게 되면 (형태소, 품사)의 형태에서 형태소/품사 형태로 붙여서 리스트화한다.
    • 어떤 형태소 분석기를 사용할지는 자신이 가진 데이터로 실험 삼아 형태소 분석을 해보고 속도나 품질을 비교해서 고르는 것이 좋다. 자신의 분석에서 사전에 추가해야할 단어들이 있다면 사용자 사전에 추가해 주면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from konlpy.tag import Mecab
tokenizer = Mecab()

# 형태소 단위로 나누기
tokenizer.morphs("아버지가방에들어가신다.")

# 결과
["아버지", "가", "방", "에", "들어가", "신다"]

# 품사 태그
tokenizer.pos('아버지가방에들어가신다.')
```

##### 결과
``` bash
[('아버지', 'NNG'), ('가', 'JKS'), ('방', 'NNG'), ('에', 'JKB'), ('들어가', 'VV'), ('신다', 'EP+EC')]
  • Mecab에 사용자 사전 추가하기

    • Komoran은 추가할 내용의 txt를 만들어 객체 생성시 userdic 파라미터에 만든 path를 입력해 주면되고, Hannanum은 konlpy/java/data/kE/dic_user.txt에 존재하며 여기에 새로운 단어를 형식에 맞춰 추가해주면된다. Kkma는 ~/anaconda3/lib/python3.6/site-packages/konlpy/java/kkma-2.0.jar 압축파일 내의 .dic 형식의 파일들이 dictionary 파일이므로 압축을 푼뒤 해당 품사에 맞는 파일에 단어를 추가해주면된다. 그리고나서 다시 .jar로 압축을 해주고 원본은 만약을 위해 다른 곳에 보관하며 사용한다.

    • 형태소 분석기를 사용하다 보면 가장 신경 써야 하는 점이 중요 token들을 어떻게 처리해야 할지다. 예를들면 우리가 ‘천리마전자’라는 기업의 데이터 분석 팀에 속해 있고 천리마저나에 관한 Corpus를 분석하거나 이로부터 임베딩을 만들어야 한다고 가정해보자. 이 경우 ‘천리마전자’라는 token은 섬세하게 처리해야한다. 만약 ‘천리마전자 텔레비전 정말 좋네요’라는 가상의 리뷰를 분석한다면 천리마 전자 보다 천리마전자로 분석됐을 때 임베딩 품질이 더 좋을 것이다. 이럴 경우 사용자 사전에 추가하여 하나의 토큰으로 분석될 수 있도록 강제해야한다.

  • 2) Khaiii 사용법 (지도학습기법으로 학습)
    • reference
    • Khaiii(Kakao Hangul Analyzer iii)는 kakao가 2018년 말 공개한 오픈소스 한국어 형태소 분석기다. 국립국어원이 구축한 세종 코퍼스를 이용해 CNN 모델을 적용해 학습했다. Khaiii의 아키텍처는 입력 문장을 문자 단위로 읽어 들인 뒤 convolution filter가 이 문자들을 슬라이딩해 가면서 정보를 추출한다. 출력 노드에서는 이렇게 모은 정보들을 종합해 형태소의 경계와 품사 태그를 예측한다. 카카오 측 설명에 따르면 모델을 C++로 구현해 GPU 없이도 형태소 분석이 가능하며 실행 속도 역시 빠르다고 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from khaiii import KhaiiiApi
tokenizer = KhaiiiApi()

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

for word in data:
token.extend([str(m).split("/")[0] for m in word.morphs])

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

# 품사 정보 확인 tagging
for word in data:
token.extend([str(m) for m in word.morphs])
결과
1
['아버지/NNG', '가/JKS', '방에/NNG', '들/VV', '어/EC', '가/VV', '시/EP', 'ㄴ다/EC']
  • 3) soynlp (비지도학습으로 학습)

    • 형태소 분석, 품사 판별 등을 지원하는 파이썬 기반 한국어 자연어 처리 패키지다. 데이터 패턴을 스스로 학습하는 비지도 학습 접근법을 지향하기 때문에 하나의 문장 혹은 문서에서보다는 어느 정도 규모가 있으면서 동질적인 문서 집합(homogeneous documents)에서 잘 작동한다.

    • soynlp 패키지에 포함된 형태소 분석기는 데이터의 통계량을 확인해 만든 단어 점수 표로 작동한다. 단어 점수는 크게 응집확률(Cohesion Probability) 과 브랜칭 엔트로피(Branching Entropy)를 활용한다. 구체적으로는 주어진 문자령이 유기적으로 연결돼 함께 자주 나타나고(응집 확률이 높을 때), 그 단어 앞뒤로 다양한 조사, 어미 혹은 다른 단어가 등장하는 경우(브랜칭 엔트로피가 높을 때) 해당 문자열을 형태소로 취급한다.

    • 예를 들어, 주어진 Corpus에서 ‘꿀잼’이라는 단어가 연결돼 자주 나타났다면 ‘꿀잼’을 형태소라고 본다(응집 확률이 높음). 한편 ‘꿀잼’ 앞에 ‘영화’, ‘정말’, ‘너무’ 등 문자열이, 뒤에 ‘ㅋㅋ’, ‘ㅎㅎ’, ‘!!’ 등 패턴이 다양하게 나타났다면 이 역시 ‘꿀잼’을 형태소로 취급한다.(브랜칭 엔트로피가 높음)

    • Cohesion score + L-Tokenizer

      • Cohesion score는 한국어의 단어 추출을 위하여 character n-gram 을 이용한다. 새로운 개념을 설명하기 위해 새로운 단어가 만들어지기 때문에 모든 단어를 포함하는 사전은 존재할 수 없다. 학습데이터를 이용하는 supervised algorithms 은 가르쳐주지 않은 단어를 인식하기가 어렵다. 실질적으로는 사전에 등록되지 않은 단어는 형태소 분석을 할 수 없다는 것이다. 통계 기반 단어 추출 기법은 ‘우리가 분석하려는 데이터에서 최대한 단어를 인식’하여 학습데이터를 기반으로 하는 supervised approach 를 보완하기 위한 방법이다. 단어의 경계에 가까워질수록 P(xy|x)의 값이 커지고, 단어의 경계를 넘어서면 P(xy|x)의 값이 줄어든다. 이 현상을 이용하여 L part 에서 단어를 추출할 수 있는 character n-gram 기반 score 를 정의한다.

      • 필자가 이해하기로는 간단하게 말하면 corpus에서 사용되는 단어 중 의미를 갖는 단위를 나누는 기준으로써 Cohesion score가 높은 것을 사용한다는 것이다.

      • 참조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from soynlp.word import WordExtractor

sentence = [데이터]
word_extractor = WordExtractor(min_frequency=100,
min_cohesion_forward=0.05,
min_right_branching_entropy=0.0)

word_extractor.train(sentence)
# model 저장
word_extractor.save(model_fname_and_path)

# 위에서 저장한 모델 load
import math
from soynlp.tokenizer import LTokenizer

model_fname = '위에서 저장했던 model path'

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

word_extractor.load(model_fname)
scores = word_extractor.word_scores()
scores = {key ; (scores[key].cohesion_forward * math.exp(scores[key].min_right_branching_entropy)) for key in scores.keys()}
tokenizer = LTokenizer(scores=scores)
tokens = tokenizer.tokenize('애비는 종이었다.')
  • 4) 구글 센텐스피스(sentencepiece)

    • 구글에서 공개한 비지도 학습기반 형태소 분석 패키지이며, 1994년 제안된 바이트 페어 인코딩(BPE : Byte Pair Encoding)기법 등을 지원하며 pip 설치를 통해 파이썬 콘솔에서도 사용할 수 있다.

    • BPE의 기본 원리

      • Corpus에서 가장 많이 등장한 문자열을 병합해 문자열을 압축하는 것
      • 예시

        • aaabdaaabac
        • 위의 문자열에서는 aa가 가장 많이 나타났다. 이를 Z로 치환하면 원래 문자열을 다음과 같이 압축할 수 있다.
        • ZabdZabac
        • 이번에는 ab가 가장 많이 나타났으므로 Y로 치환하겠다.
        • ZYdZYac
      • 자연어 처리에서 BPE가 처음 쓰인 것은 기계 번역 분야다. BPE를 활용해 토크나이즈하는 메커니즘의 핵심은 원하는 어휘 집합 크기가 될 때까지 반복적으로 고빈도 문자열들을 병합해 어휘 집합에 추가한다. 이것이 BPE학습이다. 학습이 끝난 이후의 예측과정은 문장 내 각 어절(띄어쓰기로 문장을 나눈 것)에 어휘 집합에 있는 subword가 포함돼 있을 경우 해당 subword를 어절에서 분리한다.(최장 일치 기준) 이후 어절의 나머지에서 어휘 집합에 있는 subword를 다시 찾고, 또 분리한다. 어절 끝까지 찾았는데 어휘 집합에 없으면 미등록 단어(Unknown word)로 취급한다.

    • BERT 모델은 BPE로 학습한 어휘 집합을 쓴다. BPE는 문자열 기반의 비지도 학습 기법이기 때문에 데이터만 확보할 수 있다면 어떤 언어에든 적용이 가능하다. 물론 BERT 모델에 사용할 수 있는 어휘 집합으로 쓸 수 있게 하기 위해서는 언더바(_) 문자를 ‘##’로 바꾸고 [PAD], [UNK], [CLS], [MASK], [SEP] 등 스페셜 토큰을 추가한다.

  • 구글이 공개한 BERT 모델 코드에서 BPE로 학습한 어휘 집합으로 토큰을 분리하는 클래스를 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sentencepiece as spm
train ="""--input=input_file_path \
--model_prefix=sentence \
--vocab_size=32000 \
--model_type=bpe --character_coverage=0.9995"""

spm.SentencePieceTrainer.Train(train)

from bert.tokenization import FullTokenizer

vocab_fname = "vocabulary_file_path.vocab"
tokenizer = FullTokenizer(vocab_file=vocab_fname, do_lower_case=False)

tokenizer.tokenize("집에좀 가자")
결과
1
['집에', '##좀', '가자']

soynlp 형태소 분석이나 BPE 방식의 토크나이즈 기법은 띄어쓰기에 따라 분석 결과가 크게 달라지므로 이들 모델을 학습하기 전 띄어쓰기 교정을 먼저 적용하면 그 분석 품질이 개선될 수 있다.

띄어쓰기 교정

  • soynlp에서는 띄어쓰기 교정 모듈도 제공한다. Corpus에서 띄어쓰기 패턴을 학습한 뒤 해당 패턴대로 교정을 수행한다. 예를 들어, 학습 데이터에서 ‘하자고’라는 문자 앞뒤로 다수의 공백이 발견됐다면 예측단계에서 ‘하자고’가 출현한다면 앞뒤를 띄어서 교정하는 방식이다.
1
2
3
4
5
6
7
8
9
10
11
12
from soyspacing.countbase importCountSpace

# corpus가 띄어쓰기가 이미 올바로 되어있어야 품질이 높아질 것이다.
corpus_fname = 'corpus_path'
model_fname = '저장할때 사용할 모델 path'

model = CountSpace()
model.train(corpus_fname)
model.save(model_fname, json_format=False)

model.load_model(model_fname, json_format=False)
model.correct("어릴때보고 지금다시봐도 재밌어요")
결과
1
[어릴때 보고 지금 다시봐도 재밌어요]