텍스트 전처리

MostlyFor·2023년 1월 3일

자연어처리

목록 보기
1/11

해당 글은 wikidocs의 '딥 러닝을 이용한 자연어 처리'를 학습하고 내용을 재구성하여 다음과 같은 순서로 작성되었습니다.

순서

  1. 텍스트 전처리 (Text Preprocessing)
  • 문장에서 불필요한 부분들 제거
  1. 단어의 다양한 표현 방법
  • 모델이 학습할 수 있게 text를 숫자로 표현하는 방법
  1. 다양한 모델들
  • 숫자로 표현된 단어들을 input으로 가지고 목적에 맞는 output을 내는 다양한 모델들

참고 자료
https://wikidocs.net/31767

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

토큰화(Tokenization)

토큰은 의미있는 단위로 상황에 따라 다름. 때에 따라 단어가 될 수도 있고 문장이 될 수도 있음.

보통 토큰이라 불리는 단위로 나누는 작업을 토큰화라고 함.

토큰의 단위가 단어일 경우 (Word tokenization)

토큰화에서 고려해야할 사항

  1. 구두점이나 특수 문자를 단순 제외하지 말 것

    Ph.D 는 나누면 해석이 안됨.

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

    rock ‘n’ roll

단어 토큰화에서 n-gram이라는 방법 도입.

N-gram 이란 문장을 단어별로 하나씩 토큰화 할 경우 문맥적인 의미가 무시되는데, 이를 해결하기 위해 도입된 것

n개 단어 크기 윈도우를 만들어 단어들을 토큰화함.

ex) Rusty bikes were dusted off in garages around the world.

n=2 인 경우 → (rusty, bikes), (bikes,were) ,,,

Stop word

Stop word란 분석에 큰 의미가 없는 단어를 말함. ex) a, the 등등

제거하지 않으면 자주 반복되서 중요한 단어로 인식될 가능성이 있음.

print('영어 stop words 갯수:',len(nltk.corpus.stopwords.words('english')))
print(nltk.corpus.stopwords.words('english')[:179])
  • stopwords 패키지에 보면 didn’t, couldn’t 같은 단어가 있는데 이거 짤리면 부정 의미 없어지는거 아닌가?

  • 마침표, 쉼포도 없어지는데 이건 그냥 지워도 되는거 아닌가? 문장 구분하려고 하나?

from nltk.tokenize import word_tokenize
from nltk.tokenize import WordPunctTokenizer
from tensorflow.keras.preprocessing.text import text_to_word_sequence

토큰의 단위가 문장일 경우 (Sentence Tokenization)

코퍼스가 정리가 안되어 있을 경우 일단 문장으로 쪼개고 단어로 쪼갤 수도 있음.

한국어의 경우 KSS(Korean Sentence Splitter) 라이브러리를 이용해 볼 수 있음.

#영어
from nltk.tokenize import sent_tokenize
#한국어
import kss
kss.split_sentences(text)

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

똑같이 생긴 단어라고 할지라도 품사에 따라 의미가 달라짐

ex) fly 날다, 파리

따라서 정확히 파악을 하기 위해서는 품사가 필요함.

각 단어가 어떤 품사로 쓰였는지를 나타내기 위해 품사 태깅을 함.

from nltk.tag import pos_tag

pos_tag(tokenized_sentence)

한국어 토큰화가 어려운 이유 - 교착어, 띄어쓰기

한국어는 영어와 달리 조사가 있기 때문에 같은 단어임에도 조사가 붙어서 다른 단어로 인식되는 경우가 있음. 따라서 조사는 분리해서 분석해야함.

이를 위해서 한국어 토큰화에서는 형태소란 개념을 반드시 이해해야함. 형태소란 뜻을 가진 가장 작은 단위로서 자립 형태소와 의존 형태소가 있음.

자립 형태소 : 접사, 어미, 조사와 상관없이 자립하여 사용할 수 있는 형태소.

의존 형태소 : 혼자 쓰일 수 없는 형태소, 접사, 어미, 조사, 어간을 말함.

  • 형태소 예시 에디가 책을 읽었다. 자립 형태소 : 에디, 책 의존 형태소 : -가, -을, 읽 - , -었, -다

한국어에서는 영어 단어 토큰화와 유사한 형태를 얻으려면 어절 토큰화가 아니라 형태소 토큰화를 해야함.

특히 한국어는 띄어쓰기를 안하는 경우가 많아서 이해하기 힘듦

#형태소 분리
from konlpy.tag import Okt
from konlpy.tag import Kkma

위 내용들을 이용한 간단한 실습

#문장 분리
import kss

text = '세계 최대 산유국인 사우디아라비아의 국부펀드 PIF(Public Investment Fund)가 SK이노베이션(096770)의 자회사로 전기자동차 배터리를 생산하는 SK(034730)온에 1조 원 이상의 투자를 추진한다. PIF는 사우디의 실권자인 무함마드 빈 살만 왕세자가 이사회 의장이며 세계 최대 기업인 아람코의 소유주이기도 하다. 경제의 석유 의존도를 낮추려는 무함마드 왕세자가 전략적 투자처로 ‘K배터리’를 선정한 셈이어서 향후 행보가 주목된다.'

sentences=kss.split_sentences(text)

for i in range(0, len(sentences)):
  print('{}번째 문장 : {}'.format(i+1,sentences[i]))

#형태소로 분리
from konlpy.tag import Okt
from konlpy.tag import Kkma

for j in range(0,2):
  x=Okt()
  if j==1:
    x=Kkma()
  for i in range(0, len(sentences)):
    print('')
    print(i+1,'번째 문장 ', '형태소 분석 : ',x.morphs(sentences[i]))
    print(i+1,'번째 문장 ', '품사 태깅 : ',x.pos(sentences[i]))
    print(i+1,'번째 문장 ', '명사 추출 : ',x.nouns(sentences[i]))

정제(Cleaning), 정규화(Normalization)

정제

: 갖고 있는 코퍼스로부터 노이즈 데이터 제거

불필요한 단어의 제거, (1) 기본 단어 (2) 등장 빈도가 적은 단어 (3) 길이가 짧은 a 같은 단어.

정규화

: 표현 방법이 다른 단어들을 통합시켜서 같은 단어로 만듦. 예를 들면 USA와 US는 하나의 단어로 정규화 가능.

또 대소문자 통합으로도 정규화 가능

정규 표현식을 사용할 수도 있음.

어간 추출(Stemming), 표제어 추출(Lemmatization)

Stemming

정해진 규칙만 보고 단어의 어미를 자르는 어림짐작의 작업.

stem - 어간, stemming - 어간 추출

  • 알고리즘으로 짜르는 거라 단순함
from nltk.stem import LancasterStemmer

stemmer = LancasterStemmer()

print(stemmer.stem('working'),stemmer.stem('works'),stemmer.stem('worked'))
print(stemmer.stem('amusing'),stemmer.stem('amuses'),stemmer.stem('amused'))
print(stemmer.stem('happier'),stemmer.stem('happiest'))
print(stemmer.stem('fancier'),stemmer.stem('fanciest'))

Lemmatization

표제어(Lemma)는 한글로는 기본 사전형 단어라는 뜻.

즉, am, are, is 에서의 표제어는 파생 전 형태인 be가 됨.

이를 하기 위해서는 어간과 접사를 구분해야함.

cats → cat(어간) + -s(접사)

  • 좀 더 정교해서 시간이 좀 오래걸림. + 품사를 넣어줘야함.
from nltk.stem import WordNetLemmatizer

lemma = WordNetLemmatizer()
print(lemma.lemmatize('amusing','v'),lemma.lemmatize('amuses','v'),lemma.lemmatize('amused','v'))
print(lemma.lemmatize('happier','a'),lemma.lemmatize('happiest','a'))
print(lemma.lemmatize('fancier','a'),lemma.lemmatize('fanciest','a'))

한국어 어간 추출

어간 : 용언(동사, 형용사) 을 활용할 때 모양이 변하지 않는 부분 즉 의미를 가진 부분

어미 : 어간 뒤에 붙어서 문법적 기능을 수행하는 부분, 변함.

불용어(Stopword)

빈도는 높지만 큰 의미가 없는 단어들. 따라서 제거 해야함.

예를 들어 I, my, me 등등

NLTK가 정의한 영어 불용어를 활용할 수 있고, 한국어에서는 토큰화 후에 조사, 접속사 등을 제거하거나 직접 정의하는 등의 방법이 있음.

# 직접 stopword를 정의한 예시
okt = Okt()

ex ='손절가를 정하지 않으면 안 돼, 그렇지 않으면 청산이거든. 예컨대 트레이딩 할 때는 중요한 게 있지.'

stop_words = '손절가를 안 돼 , 그렇지 않으면 예컨대'

stop_words = set(stop_words.split(' '))
ex=okt.morphs(ex)

result = [word for word in ex if word not in stop_words]

정규 표현식(Regular Expression)

정규 표현식이란 특정한 규칙을 가진 문자열의 집합을 표현하는데 사용하는 형식 언어.

정규 표현식을 이용하면 특정 규칙이 있는 텍스트 데이터를 빠르게 정제할 수 있음.

이를 이용하여 사용자 정의 토큰화를 빠르게 만들 수 있음.

문자열 분석에서 대부분 정규표현식을 사용한다.

정규표현식이란 문자열을 처리하는 방법 중의 하나로 특정한 조건의 문자를 검색하거나 치환하는 과정을 매우 간편하게 처리 할 수 있도록 하는 수단이다.

  • 짧은 실습
    import re
    
    # '.'은 한개의 임의의 문자를 말함. 이거 leetcode wildcard 문제와 비슷함.
    r = re.compile('a.c')
    
    r.search('kkk')
    
    r.search('abc14324231432acc')
    
    #출력 결과 - 위치를 알려줌
    <re.Match object; span=(0, 3), match='abc'>
    
    # s는 공백을 의미하며 +는 1개 이상임을 의미 
    text = """100 John    PROF
    101 James   STUD
    102 Mac   STUD"""
    
    re.split('\s+',text)
    
    #출력 결과
    ['100', 'John', 'PROF', '101', 'James', 'STUD', '102', 'Mac', 'STUD']

정수 인코딩(Integer Encoding)

토큰화 다음 단계로서 각 단어를 고유한 정수에 매핑 시키는 것.

왜? →

컴퓨터는 숫자로 일처리를 하므로 텍스트를 숫자로 바꾸는게 성능면에서 유리한데 텍스트를 숫자로 바꾸는 여러가지 방법이 있음.

이를 위해서 각 단어와 맵핑되는 고유한 인덱스를 부여해야함.

방법 1. dictionary

방법 2. Counter 사용

방법 3. NLTK FreqDist 사용

방법 4. Kears 텍스트 전처리 사용 (이게 제일 유용한 듯)

#보니까 여러 문장들을 토큰화 했네. 그럼 이제 숫자로 바꿔줘야겠다. 
preprocessed_sentences = [['barber', 'person'], ['barber', 'good', 'person'], ['barber', 'huge', 'person'], ['knew', 'secret'], ['secret', 'kept', 'huge', 'secret'], ['huge', 'secret'], ['barber', 'kept', 'word'], ['barber', 'kept', 'word'], ['barber', 'kept', 'secret'], ['keeping', 'keeping', 'huge', 'secret', 'driving', 'barber', 'crazy'], ['barber', 'went', 'huge', 'mountain']]

tokenizer = Tokenizer()

#입력한 텍스트로부터 단어 빈도 수가 높은 순으로 낮은 정수 인덱스 부여.
tokenizer.fit_on_texts(preprocessed_sentences)

print('실제 단어가 몇 개씩 있는지 : ',tokenizer.word_counts)
print(tokenizer.word_index)

#정해진 단어 인덱스를 가지고 문장 변환.
print(tokenizer.texts_to_sequences(preprocessed_sentences))

#만약 모든 단어를 다 쓰기 싫다면 == 빈도가 많은 단어 들만 쓰고 싶다면?

vocab_size = 5
# num_words는 0부터 시작해서 0~4까지 단어 보존인데
#단어는 1부터 시작이라 +1 넣어줘야함
# num_words가 0부터 시작하는 이유는 padding 때문임.
tokenizer = Tokenizer(num_words=vocab_size+1) 
tokenizer.fit_on_texts(preprocessed_sentences)
#여기선 적용이 아직 안됨.
#변환하는 작업인 texts_to_sequences에서 적용됨.
print(tokenizer.word_index)

#단어 집합에 없는 단어가 사라지는데, 이를 보존하고 싶다면 oov이용
#OOV가 인덱스 1에 저장되므로 vocab_size +2 해야함.
tokenizer = Tokenizer(num_words = vocab_size + 2, oov_token = 'OOV')
print('단어 OOV의 인덱스 : {}'.format(tokenizer.word_index['OOV']))

패딩(Padding)

각 문장의 길이가 서로 다른데 이를 하나의 행렬로 만들기 위해서 동일하게 맞춰주는 것.

마찬가지로 케라스를 이용하여 할 수 있다.

encoded = tokenizer.texts_to_sequences(preprocessed_sentences)
print(encoded)

padded = pad_sequences(encoded)

padded

한국어 전처리 패키지

PyKoSpacing

띄어쓰기가 되어 있지 않은 문장을 띄어쓰기를 한 문장으로 변환해주는 패키지. 이미 대용량 코퍼스를 학습하여 만들어진 모델로 상당한 정확성이 있음.

Py-Hanspell

네이버 한글 맞춤법 검사기를 바탕으로 맞춤법+띄어쓰기를 고쳐주는 라이브러리

soynlp

품사 태깅, 단어 토큰화를 지원해주는 라이브러리

비지도 학습으로 단어를 토큰화해서 신조어를 잘 구분함.

0개의 댓글