NLP / Cleaning, Normalization, Stemming, Lemmatization

몽뭉뭉·2024년 1월 17일

NLP 스터디

목록 보기
1/10

corpus에서 토큰화를 진행하기 전 후에 cleaning과 normalization을 해야한다. cleaning의 경우 대부분 토큰화 전에 우선 진행한다. 토큰화를 한 후에도 용도에 맞게 토큰화가 제대로 진행되지 않았다면 cleaning을 한번 더 진행하여 용도에 맞게 바꿔준다. 이때 완벽하게 하기는 어렵기 때문에 어느정도 합의하며 진행한다.

우선 다음과 같은 Cleaning 및 Normalization을 사용할 수 있다.

  1. 대소문자 통합
  2. 노이즈 제거
    2-1. 등장빈도가 너무 작은 경우
    2-2. 길이가 너무 짧은 경우
    2-3. 불용어
    2-4. 목적과 맞지 않은 경우
  3. stemming, lemmatization

1. 대소문자 통합

영어 텍스트를 사용할 때 대문자 텍스트와 소문자 텍스트가 다른 토큰으로 구분이 된다. 따라서 이를 통합하면 사용되는 토큰 수를 줄일 수 있다. 하지만 무조건 소문자로 변환하는 것은 옳지 않다. 예를들어 미국을 뜻하는 US를 소문자 변환하면 us라는 우리라는 뜻으로 바뀌고 회사명의 경우 소문자로 바꾸면 의미가 변색될 수 있기 때문이다. 경험상 대부분의 경우에서 소문자로 바꾸는게 성능이 좋았던 것 같다.

2. 노이즈제거

사실 노이즈로 분류하는게 명확한 케이스도 있고 아닌 케이스도 있다.
만약 영어데이터가 너무짧은 경우 'my company is' 이런식으로 끊긴 불완전한 데이터가 있을 수 있다. 이런 경우는 명확히 노이즈이지만 분석 목적과 맞지 않은 경우도 노이즈라고 볼 수 있다. 이 부분은 분석 목적에 맞게 제거하는 것이 타당하다.

2-1. 등장빈도가 너무 작은 경우

예를들어 10만개의 메일 데이터를 가지고 스팸메일 분류를 한다고 가정할 때 특정 단어가 메일 전체에서 5번밖에 안들어가면 이는 분석할 가치가 떨어진다.

2-2. 길이가 너무 짧은 경우

노이즈 제거에서 이미 설명했기 때문에 생략하겠다.

2-3. 불용어

불용어는 텍스트 내에서 자주 등장하지만 큰 의미를 갖지 않는 단어를 의미한다. ex) a, the ... 이를 제거하는 것이 분석에 있어서 좋다.

[불용어 확인하기]

from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from konlpy.tag import Okt
stopword_list=stopwords.words('english')
print('불용어 갯수: ',len(stopword_list))
print('불용어 리스트: ',stopword_list[:10])

#output
#불용어 갯수:  179
#불용어 리스트:  ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're"]

[불용어 제거하기]

from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize 
from konlpy.tag import Okt

example = "Family is not an important thing. It's everything."
stopwords_list = stopwords.words('english')
example_tokens= word_tokenize(example)

result=[]

for word in example_tokens:
    if word not in stopwords_list:
        result.append(word)
print('토큰제거 전:',example_tokens)
print('토큰제거 후: ',result)

#output
#토큰제거 전: ['Family', 'is', 'not', 'an', 'important', 'thing', '.', 'It', "'s", 'everything', '.']
#토큰제거 후:  ['Family', 'important', 'thing', '.', 'It', "'s", 'everything', '.']

Stemmig, Lemmatization

어간 추출(Stemming), 표제어 추출(Lemmatization)
이 두개는 단어를 일반화 시켜서 문서 내의 단어 수를 줄이는 방법들이다.

stemming은 어간(Stem)을 추출하는 작업이다. 정해진 규칙만 보고 단어의 어미를 자르는 어림짐작의 작업이라고 볼 수도 있다. 따라서 사전에 없는 단어도 다수 존재한다.

표제어(Lemma)는 기본 사전형 단어를 의미한다. 예를 들어 are, is 의 단어는 be가 되는 것이다.
이처럼 lemmatization은 표제어를 추출하는 작업이다.

[에러난 코드]

from nltk.stem import PorterStemmer
from nltk.tokenize import word_tokenize

stemmer = PorterStemmer()

sentence = "This was not the map we found in Billy Bones's chest, but an accurate copy, complete in all things--names and heights and soundings--with the single exception of the red crosses and the written notes."
tokenized_sentence = word_tokenize(sentence)

print("어간 추출 전 :", tokenized_sentence)
print("어간 추출 후 :", stemmer.stem(tokenized_sentence))

output
#어간 추출 전 : ['This', 'was', 'not', 'the', 'map', 'we', 'found', 'in', 'Billy', 'Bones', "'s", 'chest', ',', 'but', 'an', 'accurate', 'copy', ',', 'complete', 'in', 'all', 'things', '--', 'names', 'and', 'heights', 'and', 'soundings', '--', 'with', 'the', 'single', 'exception', 'of', 'the', 'red', 'crosses', 'and', 'the', 'written', 'notes', '.']
# stem부분 에러 코드: 'list' object has no attribute 'lower'

요즘 코딩 실력에 좌절하기 때문에 상기시키고자 에러코드까지 넣었다.
stemming을 실행할때 tokenized_sentence 코드가 list형태라 lower를 사용할 수 없다는 것이다.
따라서 for문을 이용해서 stemming을 해야한다.

print("어간 추출 후 :", [stemmer.stem(i) for i in tokenized_sentence])

이러면 잘 나온다

[Lemmatization]

import nltk
from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
print('표제어추출: ',lemmatizer.lemmatize('dies', 'v'))

output
#표제어추출:  die

표제어 추출의 경우 그냥하면 어간추출처럼 나오지만 품사형태를 뒤에 적어주면 그 형태에 맞춰서 나온다. 따라서 속도를 고려하지 않고 성능만 봤을때는 표제어 추출이 더 낫지만 각 단어에 품사를 지정해주기 어렵기 때문에 적절히 타협을 봐야한다.


이 외에 다양한 어간추출 알고리즘과 한국어 전용 패키지도 있으니 확인하길 바란다.

하면서 깨달은 점

  1. 라이브러리 import를 잘하자. 중간에 쉬다 와서 다시시작했는데 nltk import 안하고 그냥 돌리다가 10분동안 헤맸다. 화난다...
  2. text="i'm still hungry"를 그냥 인덱싱하면 i,',m,s,t,i,... 이런식으로 한글자씩 나뉜다. 우리가 원하는 결과는 이게 아니므로 토큰화를 먼저 진행해서 리스트 형태로 만들어야한다. 토크나이저를 사용하거나 아니면 공백을 기준으로 하거나는 자유이다 ^^ 최종적으로 리스트 속에 있는 'still' 같은 형식으로 만들면 된다
  3. 다시한번 2번을 강조하고 싶다. 텍스트 작업을 하면서 바로 리스트 안에 넣고 싶으면 [i for i abc] 같은 형식을 사용하자!

0개의 댓글