데이터과학 삼학년

[Text preprocessing] Tokenization 본문

Natural Language Processing

[Text preprocessing] Tokenization

Dan-k 2020. 3. 19. 19:38
반응형

자연어 처리에서 텍스트를 전처리하는 것은 매우 중요한 일이다.

TEXT를 컴퓨터가 인식할 수 있는 숫자의 형태로 어떻게 바꿔줄 것인가에 대해 알아보고자 한다.

 

Tokenization (토큰화)

토큰화는 주어진 문서 코퍼스(corpus)를 토큰(token)이라는 단위로 자르는 작업을 말한다.

영어의 경우 NLTK, 한국어의 경우 KonLPY를 통해 토큰화를 할 수 있다.

 

예를 들어

 

문장 : 나는 집에 갑니다.

토큰화 : 나는, 집에, 갑니다

 

로 자르는 것을 말한다. 영어의 경우 대게 띄어쓰기를 기준으로 토큰화를 하면 되지만, 한글의 경우 조사, 접속사가 있기 때문에 띄어쓰기 단위로 토큰화를 하는 것은 무리가 있다.

 

일단 영어를 가지고 토큰화에 대해 더 알아보면 

Don't be fooled by the dark sounding name, Mr. Jone's Orphanage is as cheery as cheery goes for a pastry shop.

이 문장에서 아포스트로피가 들어간 Don't 의 경우, 어떻게 토큰화를 할 수 있을까?

  • Don't

  • Don t

  • Dont

  • Do n't

여러가지 케이스가 존재할 수 있지만, 우리의 목적에 따라 토큰화를 하는 라이브러리를 선택하여 토큰을 진행하면 된다. 그렇다면 nltk에서 토큰화 방법인 word_tokenize와 WordPunctTokenizer를 비교해보자.

word_tokenize

from nltk.tokenize import word_tokenize  
print(word_tokenize("Don't be fooled by the dark sounding name, Mr. Jone's Orphanage is as cheery as cheery goes for a pastry shop.")) 

['Do', "n't", 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name', ',', 'Mr.', 'Jone', "'s", 'Orphanage', 'is', 'as', 
'cheery', 'as', 'cheery', 'goes', 'for', 'a', 'pastry', 'shop', '.']  

 

Don't -> Do, n't   //  Jone's -> Jone, 's 로 분리 된다.

 

WordPunctTokenizer

from nltk.tokenize import WordPunctTokenizer  
print(WordPunctTokenizer().tokenize("Don't be fooled by the dark sounding name, Mr. Jone's Orphanage is as cheery as cheery goes for a pastry shop."))

['Don', "'", 't', 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name', ',', 'Mr', '.', 'Jone', "'", 's', 'Orphanage', 'is', 'as', 'cheery', 'as', 'cheery', 'goes', 'for', 'a', 'pastry', 'shop', '.']  

Don't -> Don, ', t   //  Jone's -> Jone, ', s 로 분리 된다.

 

케라스 또한 토큰화 도구인 text_to_word_sequence 을 지원한다.

text_to_word_sequence

from tensorflow.keras.preprocessing.text import text_to_word_sequence
print(text_to_word_sequence("Don't be fooled by the dark sounding name, Mr. Jone's Orphanage is as cheery as cheery goes for a pastry shop."))

["don't", 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name', 'mr', "jone's", 'orphanage', 'is', 'as', 'cheery', 'as', 'cheery', 'goes', 'for', 'a', 'pastry', 'shop']

Don't -> Don't //  Jone's -> Jone's 로 유지하는 것을 볼 수 있다.

 

 

Tokenization (토큰화)에서 고려할 사항

1. 구두점이나 특수 문자를 단순히 제외해서는 안된다.

예를 들어 Ph.D 나 AT&T 의 경우, 단순히 특수문자를 제거하면 그 의미자체가 바뀌는 것이 존재한다.

 

2. 줄임말과 단어 내 띄어쓰기가 있는 경우

New York 단어가 있을때 단순히 띄어쓰기로 토큰하면 New, York 으로 분리되어 고유 명사의 의미가 훼손된다.

 

3. 표준 토큰화 예제

표준으로 쓰이고 있는 토큰화 방법 중 하나인 Penn Treebank 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."
print(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', '.'] 

 

 

Sentence Tokenization (문장 토큰화)

전체 단위의 글이 있으면, 이것을 문장 단위로 자르는 것이 중요하다. 이는 문장 분류(sentence segmentation)이라고도 한다.

토큰화 단위가 단어가 아니라 문장일때 토큰화하는 방법에 대해 알아본다.

직관적으로 생각했을 때, 문장을 구분하는 방법은 ? . ! 등의 기준으로 잘라내면 되지 않을까 라고 생각할 수 있지만, 그렇지않다.

예를 들면 만약 문장에 IP 192.101.00.01 이라는 것이나 혹은 Ph.D 같은 경우는 온점(.)을 기준으로 문장 단위 토큰화를 하긴 어렵다.

 

영어의 경우, NLTK에서 sent_tokenize 라는 문장단위 토큰화 모듈을 제공해준다.

from nltk.tokenize import 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."
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.']

온점이 등장하는 경우에 대해서도 위 모듈이 잘 작동하는지 실험해보면

from nltk.tokenize import sent_tokenize
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.']

잘 작동한다.

 

영어의 경우에는 NLTK에 sent_tokenize 가 있다면

한글은 어떻게 할까?

한국어에 대한 문장 토큰화 도구는 KSS (Korean Sentence Splitter) 가 있다.

!pip install kss

import kss
text='딥 러닝 자연어 처리가 재미있기는 합니다. 그런데 문제는 영어보다 한국어로 할 때 너무 어려워요. 농담아니에요. 이제 해보면 알걸요?'
print(kss.split_sentences(text))

['딥 러닝 자연어 처리가 재미있기는 합니다.', '그런데 문제는 영어보다 한국어로 할 때 너무 어려워요.',
'농담아니에요.', '이제 해보면 알걸요?']

 

 

Binary Classifier (이진 분류기)

문장 토큰화에서 예외 사항을 발생시키는 온점(.) 처리를 위해 입력에 따라 2개 클래스로 분류하는 이진 분류기를 사용하기도 한다.

 

 여기서 말하는 두 개의 클래스는
1. 온점(.)이 단어의 일부분일 경우. 즉, 온점이 약어(abbreivation)로 쓰이는 경우
2. 온점(.)이 정말로 문장의 구분자(boundary)일 경우

 

이진 분류기는 룰기반으로 작성한 코드일 수도 있고,

머신러닝을 통해 학습된 모델을 쓸수도 있다.

먼저, 영어에서 첫번째 케이스인 온점이 약어로 쓰인 경우는 약어사전(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

 

 

한국어 토큰화의 어려움

한국어는 영어와 달리 띄어쓰기만으로 토큰화를 하기는 부족하다. 

한국어의 띄어쓰기 단위는 어절이기 때문이다. 특히 한국어는 교착어가 존재한다. 교착어란 조사, 어미등을 붙여 만든 언어를 의미함.

 

  1. 한국어는 교착어이다.

    • 형태소 (morpheme)이란 개념이 있다 --> 뜻을 가진 가장 작은 말의 단위

      • 자립 형태소 : 접사, 어미, 조사와 상관없이 자립하여 사용할 수 있는 형태소. 그 자체로 단어가 됨. (체언(명사, 대명사, 수사), 수식언(관형사, 부사), 감탄사

      • 의존 형태소 : 다른 형태소와 결합하여 사용되는 형태소. 접사, 어미, 어간을 의미

      • 예시) 나는 밥을 먹었다.

        • 자립 형태소 : 나, 밥

        • 의존 형태소 : -는, -을, 먹-,었,-다

  2. 띄어쓰기가 잘 지켜지지 않는다.(영어보다)

    • 띄어쓰기를 철저하게 지키지 않아도 의미를 이해할 수 있음

    • 특히 영어에 비해 띄어쓰기가 잘 지켜지지 않음

품사 태깅 (Part-of-speech tagging)

토큰화했을 때, 토큰화 된 것이 조사인지 명사인지 품사를 태깅하여 주는 것을 말한다.

 

 

NLTK, KonLPY 실습

NLTK를 이용해 영어 문장에 대해 토큰화 후 품사 태깅 진행

Penn Treebank POG Tags에서 PRP는 인칭 대명사, VBP는 동사, RB는 부사, VBG는 현재부사, IN은 전치사, NNP는 고유 명사, NNS는 복수형 명사, CC는 접속사, DT는 관사를 의미

from nltk.tokenize import word_tokenize
text="I am actively looking for Ph.D. students. and you are a Ph.D. student."
print(word_tokenize(text))

['I', 'am', 'actively', 'looking', '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'), ('looking', 'VBG'),
('for', 'IN'), ('Ph.D.', 'NNP'), ('students', 'NNS'), ('.', '.'), ('and', 'CC'), 
('you', 'PRP'), ('are', 'VBP'), ('a', 'DT'), ('Ph.D.', 'NNP'), ('student', 'NN'), ('.', '.')]

 

한국어 자연어 처리를 위해서는 KoNLPy("코엔엘파이"라고 읽습니다)라는 파이썬 패키지를 사용할 수 있습니다. 코엔엘파이를 통해서 사용할 수 있는 형태소 분석기로 Okt(Open Korea Text), 메캅(Mecab), 코모란(Komoran), 한나눔(Hannanum), 꼬꼬마(Kkma)가 있음 (Okt는 기존에는 Twitter라는 이름을 갖고있었으나 0.5.0 버전부터 이름이 변경되어 인터넷에는 아직 Twitter로 많이 알려져있으므로 학습 시 참고바랍니다.)

 

morphts : 형태소 추출

pos : 품사태깅

nouns : 명사 추출

[okt 예시]

from konlpy.tag import Okt  
okt=Okt()  


print(okt.morphs("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))

['열심히', '코딩', '한', '당신', ',', '연휴', '에는', '여행', '을', '가봐요']  



print(okt.pos("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))  

[('열심히','Adverb'), ('코딩', 'Noun'), ('한', 'Josa'), ('당신', 'Noun'), (',', 'Punctuation'), 
('연휴', 'Noun'), ('에는', 'Josa'), ('여행', 'Noun'), ('을', 'Josa'), ('가봐요', 'Verb')]  



print(okt.nouns("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))  

['코딩', '당신', '연휴', '여행']  

[kkma 예시]

from konlpy.tag import Kkma  
kkma=Kkma()  


print(kkma.morphs("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))

['열심히', '코딩', '하', 'ㄴ', '당신', ',', '연휴', '에', '는', '여행', '을', '가보', '아요']  



print(kkma.pos("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))

[('열심히','MAG'), ('코딩', 'NNG'), ('하', 'XSV'), ('ㄴ', 'ETD'), ('당신', 'NP'), (',', 'SP'), 
('연휴', 'NNG'), ('에', 'JKM'), ('는', 'JX'), ('여행', 'NNG'), ('을', 'JKO'), ('가보', 'VV'),
('아요', 'EFN')]  



print(kkma.nouns("열심히 코딩한 당신, 연휴에는 여행을 가봐요")) 

['코딩', '당신', '연휴', '여행']  

 

KoNLPy 는 지원하는 라이브러리마다 성능이 다르고, 토큰화 방식도 차이가 있기 때문에 분석자가 잘 선택하여 적용하면 됨

필자는 대용량의 데이터를 분석할 경우, mecab 이 토큰화 속도가 빨라서 mecab을 이용하는 편이다.

 

출처 : https://wikidocs.net/21698

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

728x90
반응형
LIST
Comments