JINWOOJUNG

[ NLP ] Tokenization 본문

NLP, LLM, Multi-modal

[ NLP ] Tokenization

Jinu_01 2025. 4. 13. 12:40
728x90
반응형

본 포스팅은 [딥 러닝을 이용한 자연어 처리 입문]을 기반으로 공부한 내용을 정리하는 포스팅입니다. 

 

https://wikidocs.net/book/2155


2D, 3D Data(Image, PCD)를 처리하는 과정에서 전처리가 요구되는 것처럼, 자연어 처리에서도 전처리가 필요하다. 자연어 처리에서의 전처리는 목적에 맞게 토큰화(Tokenization)&정제(Cleaning)&정규화(Normalization)을 수행하게 된다. 본 포스팅에서는 Tokenization에 대해서 알아보자.

 

Word Tokenization

자연어 처리에서 처리하는 데이터를 코퍼스(Corpus)라 한다. 

  • Corpus
    • 말뭉치
    • 자연어 처리에서 특정 목적에 따라 수집된 텍스트 데이터

주어진 코퍼스에서 토큰(Token)이라 불리는 단위로 나누는 작업을 토큰화(Tokenization)이라고 한다. 토큰의 기준은 상황에 따라 다를 수 있는데, 토큰의 기준을 단어(Word)로 하는 경우를 단어 토큰화(Word Tokenization)이라 한다. 이때, 단어는 단어 단위 외에도 단어구, 의미를 갖는 문자열이 될 수도 있다. 

 

만약 아래의 입력에 대해서 구두점(Punctuation)을 제외시킨 후 단어 토큰화를 진행 해 보자. 이때, 구두점이란 마침표, 컴마, 물음표 등의 기호를 의미한다. 

  • 입력 : Time is an illusion. Lunchtime double so!
  • 출력 : "Time", "is", "an", "illustion", "Lunchtime", "double", "so"

이처럼 Tokenization 과정에서의 기준을 해당 데이터를 사용하는 목적에 따라 잘 정의해야 한다. 영어 문장의 경우 아포스트로피(Apostrophe, ')가 존재하는데, 이를 처리하는데 있어서 다양한 방법이 있다.

  • Don't
    • Don't
    • Don t
    • Dont
    • Do n't
  • Jone's
    • Jone's
    • Jone s
    • Jone
    • Jones

Apostrophe를 처리하는데 있어서, 기존에 공개된 도구들은 각각 다른 기준을 가지고 처리하게 된다. 

 

NLTK - word_tokenize는 [ (Don't , Do n't), (Jone's, Jone 's) ]로 분리한 것을 확인할 수 있다. 

from nltk.tokenize import word_tokenize
print('단어 토큰화1 :',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."))

# 단어 토큰화1 : ['Do', "n't", 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name', ',', 'Mr.', 'Jone', "'s", 'Orphanage', 'is', 'as', 'cheery', 'as', 'cheery', 'goes', 'for', 'a', 'pastry', 'shop', '.']

 

 

NLTK - wordPunctTokenizer는 [ (Don't , Don 't), (Jone's, Jone 's) ]로 분리한 것을 확인할 수 있는데, 이는 구두점을 별도로 분류하는 특징을 가지기 때문이다. 

from nltk.tokenize import WordPunctTokenizer
print('단어 토큰화2 :',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."))

# 단어 토큰화2 : ['Don', "'", 't', 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name', ',', 'Mr', '.', 'Jone', "'", 's', 'Orphanage', 'is', 'as', 'cheery', 'as', 'cheery', 'goes', 'for', 'a', 'pastry', 'shop', '.']

 

Tensorflow:keras - text_to_word_sequence는 [ (Don't , don't), (Jone's, jone's) ]로 분리함과 동시에 모든 알파벳을 소문자로 바꾸는 것을 확인할 수 있다. 또한, 마침표, 컴마 등의 구두점을 제거하면서 Apostrophe는 보존하는 것을 확인할 수 있다.

from tensorflow.keras.preprocessing.text import text_to_word_sequence
print('단어 토큰화3 :',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."))

# 단어 토큰화3 : ["don't", 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name', 'mr', "jone's", 'orphanage', 'is', 'as', 'cheery', 'as', 'cheery', 'goes', 'for', 'a', 'pastry', 'shop']

 

 

Penn Treebank Tokenization

 

Tokenization은 단순히 구두점을 없애고, 공백을 기준으로 자르는 과정이라고 정의할 순 없다. 

 

  • 구두점이나 특수 문자를 단순 제외하면 안 된다.

구두점문장의 경계, 의미를 포함하고 있기도 한다. 예를 들어, 마침표의 경우 문장의 경계를 의미하기 때문에 단순히 제외해서는 않된다. 또한, 단어 자체에 구두점을 포함하고 있는 경우도 존재한다. Ph.D, AT&T, 45.55 등 하나의 단어 내에서 구두점을 포함하고 있는 경우도 존재한다. 

 

  • 줄임말과 단어 내에 띄어쓰기가 있는 경우가 존재한다.

앞서 소개한 것 처럼 Apostrophe는 줄임말을 의미한다. 예를 들어, "we're"은 "we are"의 줄임맘이다. 이때, "re"를 접어(Clitic)이라 한다. 혹은 New York과 같이 하나의 단어 내에 띄어쓰기가 존재하는 경우도 있다. 이때는 하나의 단어로 인식해야 한다. 

 

이 외에도 여러가지를 고려해야 하기 때문에 Tokenization에는 여러 표준이 존재하는데, 그 중 하나가 Penn Treebank Tokenization이다. Penn Treebank Tokenization은 다음과 같은 규칙이 존재한다.

  1. 하이푼으로 구성된 단어는 하나로 유지한다.
  2. doesn't와 같이 Apostrophe로 '접어'가 함께하는 단어는 분리한다. 

 

아래 예시를 보면 위 규칙이 잘 적용된 것을 확인할 수 있다. 하나 특이한 점은 'ideal.'은 단어와 마침표가 붙어있지만, 'own','.'은 서로 다른 Token으로 분리된 것을 확인할 수 있다. 

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

Token의 단위가 문장인 경우를 문장 토큰화(Sentence Tokenization)이라 하며, 문장 분류(Sentence Segmentation)라고도 부르기도 한다. 단순히 마침표, 물음표, 느낌표 단위로 문장을 구분하면 되니 쉽다고 생각할 수 있지만, 항상 마침표가 문장의 끝에 존재하는 것은 아니다. 

 

  • IP 192.168.56.31 서버에 들어가서 로그 파일 저장해서 aaa@gmail.com로 결과 좀 보내줘. 그 후 점심 먹으러 가자.

만약 위와 같은 문장이 입력으로 들어온다면, '보내줘.','가자.'에서 문장을 끊어야 하지만, 이미 그 전에 마침표는 등장한다. 이처럼 Corpus가 어떤 언어인지, 특수문자가 어떤 식으로 활용되는지에 따라 특정한 규칙들을 정의할 필요가 있다. 

 

NLTK - sent_tokenize는 단순히 마침표 단위로 문장을 구분하는 것이 아니기 때문에, 'Ph.D'을 문장 내의 단어로 인식한 것을 확인할 수 있다. 

from nltk.tokenize import sent_tokenize
text = "I am actively looking for Ph.D. students. and you are a Ph.D student."
print('문장 토큰화2 :',sent_tokenize(text))

# 문장 토큰화2 : ['I am actively looking for Ph.D. students.', 'and you are a Ph.D student.']

 

한국어의 경우 KSS(Korean Sentence Splitter)를 통해 문장 토큰화를 진행할 수 있다.

import kss

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

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

 

Korean Tokenization

영어는 합성어(New York), 줄임말(We're)에 대한 예외처리가 충분히 진행된다면, 띄어쓰기를 기준으로 토큰화를 수행해도 단어 토큰화가 잘 진행된다. 이는 영어가 분절어에 가깝기 때문인데, 하나의 단어가 하나의 의미 단위를 거의 그대로 표현함과 동시에 각 단어가 띄어쓰기로 구분되기 때문이다.

 

하지만 한국어의 경우 교착어이기 때문에 단어가 조사, 어미 등 문법적인 요소가 붙어서 구성된다. 이때, 한국어의 띄어쓰기 단위는 '어절'인데, 어절안에는 여러 의미 단위(형태소)가 결합되어 있기 때문에 NLP에서 어절 토큰화는 지양되고 있다. 예를들어 '나는 학교에 갔다'라는 문장이 있다고 해 보자. 띄어쓰기 기준으로 보면 '나는','학교에','갔다'라고 구분할 수 있다. 하지만 실제 의미 단위로 구분지어 보면 '나','-는','학교','-에','가','-았','다'로 봐야하기 때문이다. 

 

교착어의 특성

한국어에는 영어와 달리 '조사'가 존재한다. 예를 들어 '그'라는 주어/목적어에 다양한 조사들이 결합해서 '그가','그에게','그는' 등 다양한 단어가 띄어쓰기 없이 만들어진다. 자연어 처리에서는 같은 단어임에도 서로 다른 조사가 붙어 다른 단어로 인식되면 자연어 처리가 힘들어 지기 때문에 조사의 분리가 필요하다. 즉, 한국어는 어절이 독립적인 단어로 구성되는 것이 아니라 조사 등의 무언가가 붙어있는 경우가 많아서 이를 전부 분리해줘야 한다.

 

어절을 정확하게 분리하기 위해서는 형태소(Morpheme)를 이해해야 한다. 

  • 형태소 : 뜻을 가진 가장 작은 말의 단위
    • 자립 형태소 : 접사, 어미, 조사와 없이 자립적으로 사용할 수 있는 형태소로, 그 자체로 단어가 된다.
    • 의존 형태소 : 다른 형태소와 결합하여 사용되는 형태소로, 접사, 어미, 조사, 어간을 의미한다.

앞서 예시로 든 '나는 학교에 간다'에서 자립 형태소는 '나','학교','가'가 되며, 의존 형태소는 '-는','-에','-았','-다'가 된다. 이처럼 한국어는 형태소 토큰화가 요구된다. 

 

또한, 한국어는 영어와 달리 띄어쓰기가 지켜지지 않는 경우가 많다. 영어는 띄어쓰기가 지켜지지 않으면 이해하기 힘든 문장들이 대부분이지만, 한국어는 띄어쓰기가 지켜지지 않아도 어느정도 이해할 수 있기 때문이다.

 


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

언어에는 단어의 표기는 동일하지만, 품사에 따라 의미가 달라지는 경우가 존재한다. 영어의 'fly'는 '날다'라는 동사지만, '파리'라는 명사의 의미도 있다. 한국어의 '못' 역시 명사로는 망치를 이용해서 특정 물건을 고정하기 위한 물건이라는 의미지만, 부사로서의 '못'은 동사를 할 수 없다는 의미를 가진다. 

 

이처럼 단어의 의미를 명확하게 파악하기 위해서는 해당 단어의 품사를 이해하는 것이 중요하며, 단어 토큰화 과정에서는 품사 태깅을 통해 각 단어의 품사를 구분한다. 

 

  • English

NLTK에서는 Penn Treebank POS Tags라는 기준을 사용하여 품사를 태깅한다.

 

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

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'), ('.', '.')]

 

 

  • Korean

한국어 자연어처리의 형태소 분석기는 Okt(Open Korea Text), 꼬꼬마(Kkma) 등이 있다. 

 

Okt의 결과를 보면 형태소 분석 과정에서 조사를 분리함을 확인할 수 있다.

from konlpy.tag import Okt

okt = Okt()

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

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

 

꼬꼬마의 경우 형태소 분석 결과가 Okt와 다른 것을 확인할 수 있다. 이처럼 한국어 NLP의 다양한 형태소 분석기 중 특정 Task에 맞춰서 결정할 필요가 있다.

from konlpy.tag import Kkma

kkma = Kkma()

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

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