corpus에서 토큰화를 진행하기 전 후에 cleaning과 normalization을 해야한다. cleaning의 경우 대부분 토큰화 전에 우선 진행한다. 토큰화를 한 후에도 용도에 맞게 토큰화가 제대로 진행되지 않았다면 cleaning을 한번 더 진행하여 용도에 맞게 바꿔준다. 이때 완벽하게 하기는 어렵기 때문에 어느정도 합의하며 진행한다.
- 대소문자 통합
- 노이즈 제거
2-1. 등장빈도가 너무 작은 경우
2-2. 길이가 너무 짧은 경우
2-3. 불용어
2-4. 목적과 맞지 않은 경우- stemming, lemmatization
영어 텍스트를 사용할 때 대문자 텍스트와 소문자 텍스트가 다른 토큰으로 구분이 된다. 따라서 이를 통합하면 사용되는 토큰 수를 줄일 수 있다. 하지만 무조건 소문자로 변환하는 것은 옳지 않다. 예를들어 미국을 뜻하는 US를 소문자 변환하면 us라는 우리라는 뜻으로 바뀌고 회사명의 경우 소문자로 바꾸면 의미가 변색될 수 있기 때문이다. 경험상 대부분의 경우에서 소문자로 바꾸는게 성능이 좋았던 것 같다.
사실 노이즈로 분류하는게 명확한 케이스도 있고 아닌 케이스도 있다.
만약 영어데이터가 너무짧은 경우 'my company is' 이런식으로 끊긴 불완전한 데이터가 있을 수 있다. 이런 경우는 명확히 노이즈이지만 분석 목적과 맞지 않은 경우도 노이즈라고 볼 수 있다. 이 부분은 분석 목적에 맞게 제거하는 것이 타당하다.
예를들어 10만개의 메일 데이터를 가지고 스팸메일 분류를 한다고 가정할 때 특정 단어가 메일 전체에서 5번밖에 안들어가면 이는 분석할 가치가 떨어진다.
노이즈 제거에서 이미 설명했기 때문에 생략하겠다.
불용어는 텍스트 내에서 자주 등장하지만 큰 의미를 갖지 않는 단어를 의미한다. 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', '.']
어간 추출(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
표제어 추출의 경우 그냥하면 어간추출처럼 나오지만 품사형태를 뒤에 적어주면 그 형태에 맞춰서 나온다. 따라서 속도를 고려하지 않고 성능만 봤을때는 표제어 추출이 더 낫지만 각 단어에 품사를 지정해주기 어렵기 때문에 적절히 타협을 봐야한다.
이 외에 다양한 어간추출 알고리즘과 한국어 전용 패키지도 있으니 확인하길 바란다.
하면서 깨달은 점