본문 바로가기

AI Theory/NLP

텍스트 데이터 전처리 | 1. 토큰화(tokenization)

텍스트 데이터 전처리 (Text Data Preprocessing)

크롤링 등으로 얻어낸 코퍼스 데이터가 전처리되지 않은 상태일 경우, 용도에 맞게 토큰화, 정제, 정규화 등을 수행하는 전처리 과정을 지난다.

 

이 중 토큰화 내용 정리


토큰화(tokenization)

  • 주어진 corpus에서 token이라는 단위로 나누는 작업
  • 보통 의미있는 단위로 token을 정의한다
  • 크게 단어 토큰화, 문장 토큰화가 있다.

단어 토큰화(word tokenization)

  • 토큰의 기준을 단어(word)로 하는 경우
  • 여기서 단어는 단어 단위 외에도 단어구, 의미를 갖는 문자열로도 설정할 수 있다.
  • 띄어쓰기, 구두점만으로 토큰화하는 것은 불가능하다.
  • 특히 한국어의 경우 띄어쓰기만으로는 단어 토큰을 구분하기 어렵다.

예를 들어, "Don't eat John's cookies." 이라는 문장을 단어 토큰화 한다고 하자.
이때 "Don't"와 "John's"를 어떻게 토큰화해야 할까?

정답은 사용하는 토큰화 툴에 따라 다르다.

corpus를 토큰화하기 위해서, NLTK는 word_tokenizer, WordPunctTokenizer를, keras에서는 text_to_word_sequence 등 다양한 토큰화 툴을 제공한다.

1. NLTK의 word_tokenizer

from nltk.tokenize import word_tokenize

word_tokenize("Don't eat John's cookies.")
# word_tokenizer: Don't를 Do와 n't로 분리, John's는 John과 's로 분리
['Do', "n't", "eat", "John", "'s", "cookies", "."]

2. NLTK의 WordPunctTokenizer

from nltk.tokenize import WordPunctTokenizer

WordPunctTokenizer().tokenize("Don't eat John's cookies.")
# WordPunctTokenizer: Don't를 Don과 '와 t로 분리, John's는 John과 '과 s로 분리
["Don", "'", t", "eat", "John", "'", "s", "cookies", "."]

3. keras의 text_to_word_sequence

from tensorflow.keras.preprocessing.text import text_to_word_sequence

text_to_word_sequence("Don't eat John's cookies.")
# text_to_word_sequence: 모든 알파벳을 소문자로 변경하고 마침표나 컴마 등의 구두점 제거. 아포스트로피는 보존
["don't", "eat", "john's", "cookies"]

이렇게 토큰화를 수행하는데 있어 단순히 띄어쓰기만으로 토큰화를 하는 게 아니라 더 섬세한 고려가 필요하다.

  • 구두점이나 특수문자를 제외하면 안된다.: Ph.D, R&D 등
  • 줄임말과 단어 내에 띄어쓰기가 있을 수 있다: New York 등

따라서 위와 같은 단어들을 하나로 인식할 수 있는 능력을 가져야 한다.

표준으로 쓰이고 있는 토큰화 방법 중 하나로 Penn Treebank Tokenization이 있다.

  • 하이푼(-)으로 구성된 단어는 한 단어로 유지
  • doesn't와 같이 아포스트로피로 '접어'가 함께하는 단어는 분리

4. NLTK의 TreebankWordTokenizer

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))

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)

  • 토큰의 단위가 단어가 아니라 문장일 수 있다.
  • 결국 corpus 내에서 문장 단위로 구분하는 작업으로 문장 분류(sentence segmentation)이라고도 불린다.
  • ?나 !는 꽤 명확한 구분자(boundary) 역할을 하지만, 마침표(.)는 그렇지 않을 수 있다. 따라서 어떤 언어인지에 따라 다른 규칙을 설정해야 한다.

영어의 경우 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 make sure no one was near."
print('문장 토큰화1 :',sent_tokenize(text))
문장 토큰화1 : ['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 make sure no one was near.']

한국어의 경우 박상길님이 개발하신 KSS(Korean Sentence Splitter)로 한국어 문장 토큰화를 수행할 수 있다.

!pip install kss

import kss

text = '딥 러닝 자연어 처리가 재미있기는 합니다. 그런데 문제는 영어보다 한국어로 할 때 너무 어렵습니다. 이제 해보면 알걸요?'
print('한국어 문장 토큰화 :',kss.split_sentences(text))
한국어 문장 토큰화 : ['딥 러닝 자연어 처리가 재미있기는 합니다.', '그런데 문제는 영어보다 한국어로 할 때 너무 어렵습니다.', '이제 해보면 알걸요?']

그러나 한국어의 경우 영어와는 다르게 교착어이기 때문에 띄어쓰기만으로 토큰화하기 어렵다. 즉 어절 토큰화 방식으로 한국어 토큰화 하는 것이 어렵다. 따라서 한국어의 경우 형태소(morpheme) 라는 개념을 이해하고 자립 형태소와 의존 형태소를 구분하는 형태소 토큰화를 수행해야 한다.

또한 영어와 마찬가지로 한국어의 경우 품사에 따라 단어의 뜻이 달라지기 때문에 품사 태깅(part of speech tagging)을 단어 토큰화 과정에서 수행해야 한다.

# NLTK를 이용한 영어 토큰화와 품사 태깅

from nltk.tokenize import word_tokenize
from nltk.tag import pos_tag

text = "I am actively looking for Ph.D. students. and you are a Ph.D. student."
tokenized_sentence = word_tokenize(text)

print('단어 토큰화 :',tokenized_sentence)
print('품사 태깅 :',pos_tag(tokenized_sentence))
단어 토큰화 : ['I', 'am', 'actively', 'looking', 'for', 'Ph.D.', 'students', '.', 'and', 'you', 'are', 'a', 'Ph.D.', 'student', '.']
품사 태깅 : [('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를 이용한 형태소 토큰화

from konlpy.tag import Okt
from konlpy.tag import Kkma

okt = Okt()
kkma = Kkma()

# okt
print('OKT 형태소 추출 :',okt.morphs("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))
print('OKT 품사 태깅 :',okt.pos("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))
print('OKT 명사 추출 :',okt.nouns("열심히 코딩한 당신, 연휴에는 여행을 가봐요")) 

#kkma
print('꼬꼬마 형태소 분석 :',kkma.morphs("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))
print('꼬꼬마 품사 태깅 :',kkma.pos("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))
print('꼬꼬마 명사 추출 :',kkma.nouns("열심히 코딩한 당신, 연휴에는 여행을 가봐요"))  

# okt
OKT 형태소 추출 : ['열심히', '코딩', '한', '당신', ',', '연휴', '에는', '여행', '을', '가봐요']
OKT 품사 태깅 : [('열심히', 'Adverb'), ('코딩', 'Noun'), ('한', 'Josa'), ('당신', 'Noun'), (',', 'Punctuation'), ('연휴', 'Noun'), ('에는', 'Josa'), ('여행', 'Noun'), ('을', 'Josa'), ('가봐요', 'Verb')]
OKT 명사 추출 : ['코딩', '당신', '연휴', '여행']

# kkma
꼬꼬마 형태소 분석 : ['열심히', '코딩', '하', 'ㄴ', '당신', ',', '연휴', '에', '는', '여행', '을', '가보', '아요']
꼬꼬마 품사 태깅 : [('열심히', 'MAG'), ('코딩', 'NNG'), ('하', 'XSV'), ('ㄴ', 'ETD'), ('당신', 'NP'), (',', 'SP'), ('연휴', 'NNG'), ('에', 'JKM'), ('는', 'JX'), ('여행', 'NNG'), ('을', 'JKO'), ('가보', 'VV'), ('아요', 'EFN')]
꼬꼬마 명사 추출 : ['코딩', '당신', '연휴', '여행']

어떤 형태소 분석기를 사용하느냐에 따라서 결과가 다르다. 따라서 사용 목적에 따라 적절한 형태소 분석기를 선택하고 사용하면 된다. 예를 들어 속도가 중요한 경우 메캅을 사용하면 좋다.


https://wikidocs.net/21693