19.06.06
본 글은 https://wikidocs.net/book/2155을 참고하여 작성되었음을 밝힙니다.
학습 키워드
- 토큰화(Tokenization)
- 단어 토큰화(Word Tokenization)
- 문장 토큰화(Sentence Tokenization)
- 형태소 토큰화(Morpheme Tokenization)
- KoNLPy
- 품사 부착(Part-of-speech tagging)
1. 토큰화(Tokenization)
주어진 코퍼스(corpus)에서 토큰(token)이라 불리는 단위로 나누는 작업을 토큰화(Tokenization)이라고 함. 보통 의미있는 단위로 토큰을 정의함.
2. 토큰화에서 고려해야할 사항
- 구두점이나 특수 문자를 단순 제외해서는 안 된다.
예를 들어, 온점(.)의 경우는 문장의 경계를 알 수 있는데 도움이 될 수도 있음.
그리고 단어 자체의 구두점 ex) Ph.D이나 AT&T 같은 경우. 19/06/06와 같이 날짜를 나타내는 경우 등의 이미가 사라지므로 단순 제외해서는 안된다.
- 줄임말과 단어 내에 띄어쓰기가 있는 경우.
New York이라는 단어나 rock ’n’ roll 이라는 단어는 하나의 단어지만 중간에 띄어쓰기가 존재한다. 사용용도에 따라서, 하나의 단어 사이에 띄어쓰기가 있는 경우에도 하나의 토큰으로 봐야하는 경우도 있을 수 있으므로, 토큰화 작업은 저러한 단어를 하나로 인식할 수 있는 능력도 가져야한다.
- 표준 Tokenization 예제 (Penn Treebank Tokenization)
표준으로 쓰이고 있는 Tokenization 중 하나인 Penn Treebank Tokenization의 규칙에 대해서 소개하고, Tokenization의 결과를 확인하자.
규칙 1. 하이푼으로 구성된 단어는 하나로 유지한다.
규칙 2. doesn’t와 같이 아포스트로피로 ‘접어’가 함께하는 단어는 분리해준다.
from nltk.tokenize import TreebankWordTokenizer
tokenizer = TreebankWordTokenizer()
text = "Starting a home-based restaurant may be an ideal. it doesn't have a food chain or restaurant of their own."
tokenizer.tokenize(text)['Starting','a','home-based','restaurant','may','be','an','ideal.',
'it','does',"n't",'have','a','food','chain','or','restaurant','of',
'their','own','.']
결과를 보면, 각각 규칙1과 2에 따라서 home-based는 하나의 토큰으로 취급하였으며, doesn’t의 경우 does와 n’t는 분리되었음을 볼 수 있습니다.
3. 문장 토큰화(Sentence Tokenization)
토큰의 단위가 문장( sentence) 일 때, 어떻게 토큰화를 수행해야할까?
이 작업은 갖고 있는 코퍼스 내에서 문장 단위로 구분하는 작업으로 때로는 문장 분류(Sentence Segmentation)이라고도 부름.
그렇다면 어떻게 주어진 코퍼스로부터 문장 단위로 분류할 수 있을까? 직관적으로 생각해보면 ?나 온점(.) 등을 기준으로 문장을 잘라낼 수 있겠지만, 사실 온점(.)의 경우 명확한 구분자(boundary) 는 아니다. ex) omicro03@gmail.com 과 같이 문장 내에서 존재할 수도 있다.
따라서 우리가 사용하는 코퍼스가 어떤 국적의 언어인지, 또는 해당 코퍼스 내에서 특수문자들이 어떻게 사용되고 있는지에 따라서 직접 규칙들을 정의해볼 수 있다.
NLTK에서는 영어 문장의 토큰화를 수행하는 sent_tokenize를 지원하고 있다. NLTK를 통해 문장 토큰화를 실습해보고, 문장 토큰화를 이해해보자.
from nltk.tokenize import sent_tokenizetext = "His barber kept his word. But keeping such a huge secret to himself was driving him crazy. Finally, the barber went up a mountain and almost to the edge of a cliff. He dug a hole in the midst of some reeds. He looked about, to mae sure no one was near."print(sent_tokenize(text))['His barber kept his word.', 'But keeping such a huge secret to himself was driving him crazy.', 'Finally, the barber went up a mountain and almost to the edge of a cliff.', 'He dug a hole in the midst of some reeds.', 'He looked about, to mae sure no one was near.']
출력 결과를 보면 모든 문장을 온점(.)을 기준으로 구분해내었음을 알 수 있다. 그러면 문장 중간에 온점이 여러 번 등장하는 경우를 보자.
text = "I am actively looking for Ph.D. students. and you are a Ph.D student."print(sent_tokenize(text))['I am actively looking for Ph.D. students.', 'and you are a Ph.D student.']
위 출력된 결과를 보면 sent_tokenzie 메서드는 문장 내의 온점이 있더라도 온점만을 구분자로 사용하지 않았기 때문에 성공적으로 인식하는 것을 알 수 있다.
4. 이진 분류기(Binary Classifier)
문장 토큰화에서의 예외 사항을 발생시키는 온점의 처리를 위해서 입력에 따라 두 개의 클래스로 분류하는 이진 분류기(Binary Classifier)를 사용하기도 한다.
두 개의 클래스란.
1) 온점(.)이 단어의 일부분일 경우.즉, 온점이 약어(Abbreivation)로 쓰이는 경우
2) 온점(.)이 정말로 문장의 구분자(boundary)일 경우
이진 분류기는 임의로 정한 여러가지 규칙을 코딩한 함수일 수도 있으며, 머신러닝을 통해 구현한 모델일 수도 있다.
온점(.)이 어떤 클래스에 속하는지 결정을 위해서는 어떤 온점이 약어(Abbreviation)으로 쓰이는지 알아야한다. 때문에 이진 분류기 구현에서 약어 사전(Abbreviation Dictionary)은 유용하게 쓰인다.
https://public.oed.com/how-to-use-the-oed/abbreviations/ (영어권 약어사전)
이러한 문장 토큰화를 수행하는 오픈 소스로는 NLTK, OpenNLP, 스탠포드 CoreNLP, splitta, LingPipe 등이 있다. 문장 토큰화 규칙을 짤 때, 발생할 수 있는 여러가지 예외사항을 다룬 참고 자료로 아래 링크를 참고
https://tech.grammarly.com/blog/posts/How-to-Split-Sentences.html
5. 한국어에서의 토큰화의 어려움.
영어는 New York과 같은 합성어나 he’s와 같이 줄임말에 대한 예외처리만 한다면, 띄어쓰기(space)를 기준으로 하는 띄어쓰기 토큰화를 수행해도 단어 토큰화가 잘 작동한다. 거의 대부분의 경우, 단어로 띄어쓰기가 이루어지기 때문에 띄어쓰기 토큰화와 단어 토큰화가 같기 때문이다.
하지만, 한국어는 영어와는 달리 띄어쓰기만으로 토큰화를 하기에 부족하다. 한국어의 경우 띄어쓰기 단위가 되는 단위를 ‘어절’이라고 하는데 즉, 어절의 토큰화는 한국어 NLP에서 지양된다. 그 이유는 어절 토큰화와 단어 토큰화가 같지 않기 때문인데, 그 근본적인 이유는 한국어가 영어와는 다른 형태를 가지는 언어인 교착어라는 점에서 기인한다. 교착어란 조사, 어미 등을 붙여서 말을 만드는 언어를 말한다.
1) 한국어는 교착어다.
그라는 단어 하나에도 ‘그가’, ‘그와’, ‘그를’ 등의 다양한 조사가 ‘그’라는 글자 뒤에 띄어쓰기 없이 붙게 된다. 자연어 처리를 하다보면 같은 단어임에도 서로 다른 조사가 붙어서 다른 단어로 인식되면 자연어 처리가 힘들고 번거로워지기 때문에 대부분의 한국어 NLP에서 조사는 분리해줄 필요가 있다.
한국어 토큰화에서는 형태소(morpheme)라는 개념을 알아야 하는데, 형태소(morpheme) 이란 뜻을 가진 가장 작은 말의 단위를 말한다. 형태소는 자립 형태소와 의존 형태소로 나눠진다.
자립 형태소 : 접사, 어미, 조사와 상관없이 자립하여 사용할 수 있는 형태소. 그 자체로 단어가 될 수 있다. 체언(명사, 대명사, 수사), 수식언(관형사, 부사), 감탄사 등이 있다.
의존 형태소 : 다른 형태소와 결합하여 사용되는 형태소. 접사, 어미, 조사, 어간을 의미한다.
냉면이 딥러닝책을 읽었다
자립 형태소 : 냉면, 딥러닝책
의존 형태소 : -이, -을, 읽-, -었, -다
따라서 한국어에서 영어 단어 토큰화와 유사한 형태를 얻으려면 어절 토큰화가 아니라 형태소 토큰화를 수행해야한다.
2) 한국어는 띄어쓰기가 영어보다 잘 지켜지지 않는다.
한국어는 수많은 코퍼스에서 띄어쓰기가 무시되는 경우가 많아 자연어 처리가 어려워짐.
6. 품사 부착(Part-of-speech tagging)
단어는 표기는 같지만, 품사에 따라서 단어의 의미가 달라진다. 예를 들어 영어에서 fly는 동사로 ‘날다’라는 의미를 가지고 있지만, 명사로는 ‘파리’의 의미를 갖는다. 이처럼 단어의 의미를 제대로 파악하기 위해서는 해당 단어가 어떤 품사로 쓰였는지 보는 것이 주요 지표가 될 수 있다. 그에 따라 단어 토큰화 과정에서 각 단어가 어떤 품사로 쓰였는지 구분해 놓기도 하는데, 이 작업을 품사 부착(Part-of-speech tagging)이라고 한다.
7. NLTK와 KoNLPy를 이용한 언어, 한국어 토큰화 실습
NLTK에서는 영어 코퍼스에 품사 부착 기능을 지원함
from nltk.tokenize import word_tokenize
text = "I am actively lookin for Ph.D. students. and you are a Ph.D student."
print(word_tokenize(text))['I', 'am', 'actively', 'lookin', 'for', 'Ph.D.', 'students', '.', 'and', 'you', 'are', 'a', 'Ph.D', 'student', '.']from nltk.tag import pos_tag
x = word_tokenize(text)pos_tag(x)[('I', 'PRP'),
('am', 'VBP'),
('actively', 'RB'),
('lookin', 'JJ'),
('for', 'IN'),
('Ph.D.', 'NNP'),
('students', 'NNS'),
('.', '.'),
('and', 'CC'),
('you', 'PRP'),
('are', 'VBP'),
('a', 'DT'),
('Ph.D', 'NNP'),
('student', 'NN'),
('.', '.')]
Penn Treebank POG Tag에서 (PRP : 인칭 대명사), (VBP : 동사), (RB : 부사), (VBG : 현재부사), (IN : 전치사), (NNP : 고유 명사), (NNS : 복수형 명사), (CC : 접속사), (DT : 관사)
한국어 자연어 처리를 위해서는 KoNLPy(코엔엘파이)라는 파이썬 패키지를 사용한다. 코엔엘파이를 통해서 사용할 수 있는 형태소 분석기로 Okt(Open Korea Text), 메캅(Mecab), 코모란(Komoran), 한나눔(Hannanum), 꼬꼬마(Kkma)가 있다.
한국어 NLP에서 형태소 분석기를 사용한다는 것은 단어 토큰화가 아니라 정확히는 형태소(morpheme) 단위로 형태소 토큰화 (morpheme tokenization)를 수행하게 됨을 뜻한다.
Okt 형태소 분석기로 토큰화한 예제
from konlpy.tag import Okt
okt = Okt()
print(okt.morphs("현충일에 공부를 해봐요"))
['현충일', '에', '공부', '를', '해봐요']print(okt.pos("현충일에 공부를 해봐요"))
[('현충일', 'Noun'), ('에', 'Josa'), ('공부', 'Noun'), ('를', 'Josa'), ('해봐요', 'Verb')]print(okt.nouns("현충일에 공부를 해봐요"))
['현충일', '공부']
1) morphs : 형태소 추출
2) pos : 품사 부착(Part-of-speech tagging)
3) nouns : 명사 추출
꼬꼬마 형태소 분석기를 사용한 예제
from konlpy.tag import Kkma
kkma = Kkma()
print(kkma.morphs("현충일에 공부를 해봐요"))
['현충일', '에', '공부', '를', '해보', '아요']print(kkma.pos("현충일에 공부를 해봐요"))
[('현충일', 'NNG'), ('에', 'JKM'), ('공부', 'NNG'), ('를', 'JKO'), ('해보', 'VV'), ('아요', 'EFN')]print(kkma.nouns("현충일에 공부를 해봐요"))
['현충일', '공부']
참고자료
한국어 형태소 분석기 성능 비교 : https://iostream.tistory.com/144