1. Bag of Words란?
Bag of Words란 단어들의 순서는 전혀 고려하지 않고, 오직 단어들의 출현 빈도에만 집중하는 텍스트 데이터의 수치화 표현 방법이다. 직역하면 단어들의 가방이라는 의미이다. 단어들이 들어있는 가방이있다. 갖고있는 텍스트 문서에 있는 단어들을 가방에 전부 넣어 흔들고 섞었다 만약 문서에 특정 단어가 N번 등장 했다면, 이 가방에는 그 특정 단어가 N개있게 된다. 가방을 흔들어서 단어를 섞었기 때문에 순서는 중요하지 않다.
BoW 만드는 과정
(1) 우선, 각 단어에 고유한 정수 인덱스를 부여한다.
(2) 각 인덱스의 위치에 단어 토큰의 등장 횟수를 기록한 벡터를 만든다.
※예시※
문서: 정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다.
from konlpy.tag import Okt
import re
okt=Okt()
token=re.sub("(\.)","","정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다.")
# 정규 표현식을 통해 온점 제거
token=okt.morphs(token)
word2index={}
bow=[]
for voca in token:
if voca not in word2index.keys():
word2index[voca]=len(word2index)
# token을 읽으면서, word2index에 없는 (not in) 단어는 새로 추가, 있는 단어는 넘긴다
bow.insert(len(word2index)-1,1)
# BoW 전체에 전부 기본값 1을 넣어준다. 단어의 개수는 최소 1개 이상이기 때문이다.
else:
index=word2index.get(voca)
# 재등장하는 단어의 인덱스를 받아온다.
bow[index]=bow[index]+1
# 재등장한 단어는 해당하는 인덱스의 위치에 1을 더해준다. (단어의 개수를 세는 것이다.)
print(word2index)
{'정부': 0, '가': 1, '발표': 2, '하는': 3, '물가상승률': 4, '과': 5, '소비자': 6, '느끼는': 7, '은': 8, '다르다': 9}
bow
[1, 2, 1, 1, 2, 1, 1, 1, 1, 1]
각 단어에 대해서 인덱스를 부여한 결과는 첫번째 출력 결과이고, 두번째 출력 결과가 바로 BoW 이다. 두번째 출력 결과를 보면, 물가상승률의 인덱스는 4이며, 처음에서의 물가상승률은 2번 언급되었기 때문에 인덱스4에 해당 하는 값이 2임을 알수 있습니다.
2. BoW의 다른예제들
인덱스의 할당을 임의로 바꾸고 그에 따른 BoW를 만든다고 해보자.
문서: 소비자는 주로 소비하는 상품을 기준으로 물가상승률을 느낀다.
('소비자': 0, '는': 1, '주로': 2, '소비': 3, '하는': 4, '상품': 5, '을': 6, '기준': 7, '으로': 8, '물가상승률': 9, '느낀다': 10)
[1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1]
문서: 정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다. 소비자는 주로 소비하는 상품을 기준으로 물가상승률을 느낀다.
('정부': 0, '가': 1, '발표': 2, '하는': 3, '물가상승률': 4, '과': 5, '소비자': 6, '느끼는': 7, '은': 8, '다르다': 9, '는': 10, '주로': 11, '소비': 12, '상품': 13, '을': 14, '기준': 15, '으로': 16, '느낀다': 17)
[1, 2, 1, 2, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1]
3. CountVectorizer 클래스로 BoW 만들기
사이킷 런에서는 단어의 빈도를 Count 하여 Vector로 만드는 CountVectorizer클래스를 지원한다. 이를 이용하면 보다 빠르게 BoW를 만들 수 있다.
from sklearn.feature_extraction.text import CountVectorizer
corpus = ['you know I want your love. because I love you.']
vector = CountVectorizer()
print(vector.fit_transform(corpus).toarray())
print(vector.vocabulary_)
[[1 1 2 1 2 1]]
{'you': 4, 'know': 1, 'want': 3, 'your': 5, 'love': 2, 'because': 0}
예제 문장에서 you와 love는 두 번씩 언급되었으므로 각각 인덱스 2와 인덱스 4에서 2의 값을 가지며, 그 외의 값에서는 1의 값을 가지는 것을 볼 수 있다. 또한 알파벳 I는 BoW를 만드는 과정에서 사라졌는데, 이는 CountVectorizer가 기본적으로 길이가 2이상인 문자에 대해서만 토큰으로 인식하기 때문이다.
주의할 것은 CountVectorizer는 단지 띄어쓰기만을 기준으로 단어를 자르는 낮은 수준의 토큰화를 진행하고 BoW를 만든다는 점이다. 이는 영어의 경우 띄어쓰기만으로 토큰화가 수행되기 때문에 문제가 없지만 한국어에 CountVectorizer를 적용하면, 조사 등의 이유로 제대로 BoW가 만들어지지 않음을 의미한다.
4. 불용어를 제거한 BoW 만들기
BoW를 만들 때 불용어를 제거하는 일은 자연어처리의 정확도를 높일 수 있다.
(1) 사용자가 직접 정의한 불용어 사용
from sklearn.feature_extraction.text import CountVectorizer
text=["Family is not an important thing. It's everything."]
vect = CountVectorizer(stop_words=["the", "a", "an", "is", "not"])
print(vect.fit_transform(text).toarray())
print(vect.vocabulary_)
[[1 1 1 1 1]]
{'family': 1, 'important': 2, 'thing': 4, 'it': 3, 'everything': 0}
(2) CountVectorizer에서 제공하는 자체 불용어 사용
from sklearn.feature_extraction.text import CountVectorizer
text=["Family is not an important thing. It's everything."]
vect = CountVectorizer(stop_words="english")
print(vect.fit_transform(text).toarray())
print(vect.vocabulary_)
[[1 1 1]]
{'family': 0, 'important': 1, 'thing': 2}
(3) NLTK에서 지원하는 불용어 사용
from sklearn.feature_extraction.text import CountVectorizer
from nltk.corpus import stopwords
text=["Family is not an important thing. It's everything."]
sw = stopwords.words("english")
vect = CountVectorizer(stop_words =sw)
print(vect.fit_transform(text).toarray())
print(vect.vocabulary_)
[[1 1 1 1]]
{'family': 1, 'important': 2, 'thing': 3, 'everything': 0}
이렇게 각기 다른 방법으로 불용어를 삭제하여 정확도를 높일 수 있다.
'머신러닝 > 딥러닝을 이용한 자연어처리 입문' 카테고리의 다른 글
원-핫 인코딩(One-hot encoding) (0) | 2020.03.30 |
---|---|
RNN(Recurrent Neural Network) (0) | 2020.01.02 |
데이터의 분리(Splitting Data) (0) | 2019.11.19 |
불용어(Stopword) (0) | 2019.11.11 |
어간 추출(Stemming) and 표제어 추출(Lemmatization) (0) | 2019.11.01 |