머신러닝

텍스트 전처리(1) - 토큰화, 정제, 정규화

Potwings 2024. 9. 3. 10:03

토큰화

주어진 문장에서의미 부여가 가능한 단위를 찾는 것

 

단순 공백 기준으로 잘라낼 경우 아래와 같은 이슈가 있다.

  • 단어나 숫자에 특수 문자가 들어가는 경우 존재 (ex. AT&T, $45.55)
  • 줄임말과 단어 내에 띄어쓰기가 있는 경우 존재 (ex. We’re, I’m)

이와 같은 이유로 인해 섬세한 알고리즘이 필요하다.

 

품사 태깅

단어가 어떤 품사로 사용되었는지 구분해놓는 것. 토큰화 후 진행 가능하다.

어떤 품사로 쓰였는지에 따라 단어의 의미가 달라질 수 있어 진행한다.

ex) fly - [동사 : 날다], [명사 : 파리]

 

실습

영어 - NLTK 라이브러리 활용

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

text = "I'd like to stay at home all day long. I love my house."
tokenized_sentence = word_tokenize(text)

print('단어 토큰화 :',tokenized_sentence)
print('품사 태깅 :',pos_tag(tokenized_sentence))
## 실행 결과 ##
단어 토큰화 : ['I', "'d", 'like', 'to', 'stay', 'at', 'home', 'all', 'day', 'long', '.', 'I', 'love', 'my', 'house', '.']
품사 태깅 : [('I', 'PRP'), ("'d", 'MD'), ('like', 'VB'), ('to', 'TO'), ('stay', 'VB'), ('at', 'IN'), ('home', 'NN'), ('all', 'DT'), ('day', 'NN'), ('long', 'RB'), ('.', '.'), ('I', 'PRP'), ('love', 'VBP'), ('my', 'PRP$'), ('house', 'NN'), ('.', '.')]

 

한글 토큰화와 그 문제점

한글화 진행 시에는 토큰화에 더 큰 문제점이 있다.

 

1. 한글은 조사, 어미 등을 붙여서 말을 만드는 교착어이다.

예를 들어 한국어의 ‘그녀(she/her)’라는 주어나 목적어가 들어간 문장이 있다고 해보자. 이 경우 단어에 ‘그녀가’, ‘그녀에게’, ‘그녀를’ 과 같이 다양한 조사가 붙을 것이다. 같은 단어임에도 서로 다른 조사가 붙어 다른 단어로 인식이 되면 자연어 처리가 힘들고 번거로워지는 경우가 많다.

따라서 한국어 자연어 처리에선 조사를 분리해줄 필요가 있고 이를 위해 형태소 기준 토큰화를 진행한다.

 

2. 한국어는 띄어쓰기가 영어보다 잘 지켜지지 않는다.

  • Tobeornottobethatisthequestion

영어는 띄어쓰기가 없으면 쉽게 알아보기 힘들다.

  • 제가이렇게띄어쓰기를전혀하지않고글을썼다고하더라도글을이해할수있습니다.

반면 한국어는 띄어쓰기가 되지 않아도 어느 정도 내용을 알아볼 수 있다.

이로 인해 띄어쓰기가 무시되는 경우가 많아져 자연어 처리에 어려움을 겪는다.

 

KoNLPy 라이브러리 활용하여 진행하며 KoNLPy 형태소 분석기들은 공통적으로 아래 메소드 제공한다.

  • morphs : 형태소 추출
  • pos : 품사 태깅(Part-of-speech tagging)
  • nouns : 명사 추출
from konlpy.tag import Okt

okt = Okt()
text = "아 퇴근하고 싶다. 월요일부터 집에 가고 싶다니 완전 럭키비키잖아"

# OKT 형태소 분석기 사용
print('OKT 형태소 분석 :',okt.morphs(text))
print('OKT 품사 태깅 :',okt.pos(text))
print('OKT 명사 추출 :',okt.nouns(text))
## 실행 결과 ##
OKT 형태소 분석 : ['아', '퇴근', '하고', '싶다', '.', '월요일', '부터', '집', '에', '가고', '싶다니', '완전', '럭키', '비키잖아']
OKT 품사 태깅 : [('아', 'Exclamation'), ('퇴근', 'Noun'), ('하고', 'Josa'), ('싶다', 'Verb'), ('.', 'Punctuation'), ('월요일', 'Noun'), ('부터', 'Josa'), ('집', 'Noun'), ('에', 'Josa'), ('가고', 'Verb'), ('싶다니', 'Verb'), ('완전', 'Noun'), ('럭키', 'Noun'), ('비키잖아', 'Verb')]
OKT 명사 추출 : ['퇴근', '월요일', '집', '완전', '럭키']

 

어떤 형태소 분석기를 사용하느냐에 따라 결과가 다르게 나온다.

필요 용도에 어떤 형태소 분석기가 가장 적절한지 판단하여 사용하면 된다.

from konlpy.tag import Kkma

kkma = Kkma()
text = "아 퇴근하고 싶다. 월요일부터 집에 가고 싶다니 완전 럭키비키잖아"

# 꼬꼬마 형태소 분석기 사용
print('꼬꼬마 형태소 분석 :',kkma.morphs(text))
print('꼬꼬마 품사 태깅 :',kkma.pos(text))
print('꼬꼬마 명사 추출 :',kkma.nouns(text))
## 실행 결과 ##
꼬꼬마 형태소 분석 : ['I', "'", 'd', 'like', 'to', 'stay', 'at', 'home', 'all', 'day', 'long', '.', 'I', 'love', 'my', 'house', '.']
꼬꼬마 품사 태깅 : [('I', 'OL'), ("'", 'SS'), ('d', 'OL'), ('like', 'OL'), ('to', 'OL'), ('stay', 'OL'), ('at', 'OL'), ('home', 'OL'), ('all', 'OL'), ('day', 'OL'), ('long', 'OL'), ('.', 'SF'), ('I', 'OL'), ('love', 'OL'), ('my', 'OL'), ('house', 'OL'), ('.', 'SF')]
꼬꼬마 명사 추출 : []

 


문장 토큰화

문장단위로 의미를 나누기 위해 사용한다.

 

영어 - NLTK의 from nltk.tokenize import 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))
## 실행 결과 ##
한국어 문장 토큰화 : ['딥 러닝 자연어 처리가 재미있기는 합니다.', '그런데 문제는 영어보다 한국어로 할 때 너무 어렵습니다.', '이제 해보면 알걸요?']

 


정규화

표현 방법이 다른 단어들을 같은 단어로 통합하는 작업

정규화 기법

규칙에 기반한 표기가 다른 단어들의 통합

  • 같은 의미를 가지고 있으나 표기가 다른 단어들을 하나의 단어로 정규화할 수 있다.
  • ex) (US, USA), (uh-huh, uhhuh)

 

대, 소문자 통합

  • 대, 소문자를 통합할 경우 단어의 개수를 줄일 수 있다. 영어에서 대부분은 소문자로 작성되므로 소문자 변환 작업으로 이루어진다.
  • 떄론 무작정 대문자와 소문자가 구분되어야하는 상황도 있다.
  • 대안으로 문장의 맨앞의 단어만 대문자로 변경하고 나머지는 대,소문자 유지하는 방법이 있다.

 

표제어 추출

  • 단어들로부터 표제어(기본 사전형 단어)를 추출하는 행위 어간과 접사로 단어를 나누어 준다.
    from nltk.stem import WordNetLemmatizer
    
    lemmatizer = WordNetLemmatizer()
    
    words = ['policy', 'doing', 'organization', 'have', 'going', 'love', 'lives', 'fly', 'dies', 'watched', 'has', 'starting']
    
    print('표제어 추출 전 :',words)
    print('표제어 추출 후 :',[lemmatizer.lemmatize(word) for word in words])
    
    
    ## 실행 결과 ##
    표제어 추출 전 : ['policy', 'doing', 'organization', 'have', 'going', 'love', 'lives', 'fly', 'dies', 'watched', 'has', 'starting']
    표제어 추출 후 : ['policy', 'doing', 'organization', 'have', 'going', 'love', 'life', 'fly', 'dy', 'watched', 'ha', 'starting']
    ​


  • 추출 결과에서의 dy와 ha처럼 의미를 알 수 없는 적절하지 못한 단어가 있다. 이런 경우 본래 단어의 품사 정보를 알아야 정확한 결과를 얻을 수 있다.
    print(lemmatizer.lemmatize('dies', 'v'))
    print(lemmatizer.lemmatize('watched', 'v'))
    print(lemmatizer.lemmatize('has', 'v'))
    ## 실행 결과 ##
    die
    watch
    have

어간 추출

  • 표제어 추출보다 일반적으로 속도가 빠르다. 그 중 포터 어간 추출기는 정밀하게 설계되어 정확도가 높아 영어 자연어 처리에서 준수한 선택이다.그 외에도 NLTK에는 랭커스터 스태머 알고리즘이 있다.
    from nltk.stem import PorterStemmer
    from nltk.stem import LancasterStemmer
    
    porter_stemmer = PorterStemmer()
    lancaster_stemmer = LancasterStemmer()
    
    words = ['policy', 'doing', 'organization', 'have', 'going', 'love', 'lives', 'fly', 'dies', 'watched', 'has', 'starting']
    print('어간 추출 전 :', words)
    print('포터 스테머의 어간 추출 후:',[porter_stemmer.stem(w) for w in words])
    print('랭커스터 스테머의 어간 추출 후:',[lancaster_stemmer.stem(w) for w in words])
    
    ## 실행 결과 ##
    # 둘은 서로 다른 알고리즘을 사용하기 때문에 다른 결과가 나온다
    어간 추출 전 : ['policy', 'doing', 'organization', 'have', 'going', 'love', 'lives', 'fly', 'dies', 'watched', 'has', 'starting']
    포터 스테머의 어간 추출 후: ['polici', 'do', 'organ', 'have', 'go', 'love', 'live', 'fli', 'die', 'watch', 'ha', 'start']
    랭커스터 스테머의 어간 추출 후: ['policy', 'doing', 'org', 'hav', 'going', 'lov', 'liv', 'fly', 'die', 'watch', 'has', 'start']
    
  • 허나 이런 규칙에 기반한 어간 추출은 제대로 된 일반화를 수행하지 못할 수도 있다.
    ex) organization → organ (완전히 다른 단어임에도 이와 같이 어간 추출이 된다)

정제

갖고 있는 말뭉치로부터 불필요한 데이터를 제거한다.

정제 방식의 예시

등장 빈도가 적은 단어 제거

  • 입력 받은 데이터 100000건 중에서 5번밖에 등장하지 않은 단어가 있다면 이는 직관적으로 분류에 도움이 되지 않는 데이터임을 알 수 있다.

 

길이가 짧은 단어 제거

  • 영어에서는 길이가 짧은 단어의 대부분이 불용어라 제거하는 의미가 있다. 또 구두점들까지도 한번에 제거할 수 있다.
  • 허나 한국어에서는 크게 유효하지 않을 수 있다. 이는 영어와 한국어 단어에서 각 한 글자가 가진 의미의 크기가 다르기 때문이다.

 

불용어 제거

  • 큰 의미가 없는 단어(분석에 큰 도움이 되지 않음)를 제거한다.
  • 예시 
    from nltk.corpus import stopwords
    from nltk.tokenize import word_tokenize 
    
    example = "Family is not an important thing. It's everything."
    stop_words = set(stopwords.words('english')) 
    
    word_tokens = word_tokenize(example)
    
    result = []
    for word in word_tokens: 
        if word not in stop_words: 
            result.append(word) 
    
    print('불용어 제거 전 :',word_tokens) 
    print('불용어 제거 후 :',result)
    
    ## 실행 결과 ##
    불용어 제거 전 : ['Family', 'is', 'not', 'an', 'important', 'thing', '.', 'It', "'s", 'everything', '.']
    불용어 제거 후 : ['Family', 'important', 'thing', '.', 'It', "'s", 'everything', '.']
    

 

  • 한국어에서 불용어 제거 예시 [한국어 불용어 리스트 : https://www.ranks.nl/stopwords/korean]
    from konlpy.tag import Okt
    
    okt = Okt()
    
    example = "고기를 아무렇게나 구우려고 하면 안 돼. 고기라고 다 같은 게 아니거든. 예컨대 삼겹살을 구울 때는 중요한 게 있지."
    stop_words = "를 아무렇게나 구 우려 고 안 돼 같은 게 구울 때 는"
    
    stop_words = set(stop_words.split(' '))
    word_tokens = okt.morphs(example)
    
    result = [word for word in word_tokens if not word in stop_words]
    
    print('불용어 제거 전 :',word_tokens) 
    print('불용어 제거 후 :',result)
    
    ## 실행 결과 ##
    불용어 제거 전 : ['고기', '를', '아무렇게나', '구', '우려', '고', '하면', '안', '돼', '.', '고기', '라고', '다', '같은', '게', '아니거든', '.', '예컨대', '삼겹살', '을', '구울', '때', '는', '중요한', '게', '있지', '.']
    불용어 제거 후 : ['고기', '하면', '.', '고기', '라고', '다', '아니거든', '.', '예컨대', '삼겹살', '을', '중요한', '있지', '.']
    

 

 

 

참고 원문 : https://wikidocs.net/21694

 

02. 텍스트 전처리(Text preprocessing)

텍스트 전처리는 풀고자 하는 문제의 용도에 맞게 텍스트를 사전에 처리하는 작업입니다. 요리를 할 때 재료를 제대로 손질하지 않으면, 요리가 엉망이 되는 것처럼 텍스트에 제대로 전…

wikidocs.net

 

 

'머신러닝' 카테고리의 다른 글

AI의 두 번의 겨울 - XOR 문제, 기울기 소실 문제  (3) 2024.10.29