단어의 등장 순서를 고려하지 않는 빈도수 기반의 단어 표현 방법인 Bag of Words에 대해 알아보자
1. Bag of Words란?
Bag of Words란 단어들의 순서는 전혀 고려하지 않고, 단어들의 출현 빈도(frequency)에만 집중하는 텍스트 데이터의 수치화 표현 방법이다.
BoW를 만드는 과정을 이렇게 두 가지 과정으로 볼 수 있다.
1) 우선, 각 단어에 고유한 정수 인덱스를 부여한다.
2) 각 인덱스의 위치에 단어 토큰의 등장 횟수를 기록한 벡터를 만든다.
한국어 예제를 통해 BoW에 대해서 이해해본다.
문서1: 정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다.
위의 문서1에 대해서 BoW를 만들어본다
아래의 코드는 입력된 문서에 대해서 단어 집합(vocaburary)을 만들어 인덱스를 할당하고, BoW를 만드는 코드이다.
from konlpy.tag import Okt
import re
okt=Okt()
token=re.sub("(\.)","","정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다.")
# 정규 표현식을 통해 온점을 제거하는 정제 작업입니다.
token=okt.morphs(token)
# OKT 형태소 분석기를 통해 토큰화 작업을 수행한 뒤에, 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]
2. Bag of Words의 다른 예제들
BoW에 있어서 중요한 것은 단어의 등장 빈도이다. 즉, 인덱스의 순서는 전혀 상관없다.
문서1에 대한 인덱스 할당을 임의로 바꾸고 BoW를 만든다고 가정해본다.
# ('발표': 0, '가': 1, '정부': 2, '하는': 3, '소비자': 4, '과': 5, '물가상승률': 6, '느끼는': 7, '은': 8, '다르다': 9)
[1, 2, 1, 1, 1, 1, 2, 1, 1, 1]
문서2: 소비자는 주로 소비하는 상품을 기준으로 물가상승률을 느낀다.
만약, 위의 코드에 문서2로 입력으로 하여 인덱스 할당과 BoW를 만드는 것을 진행한다면 아래 같이 나온다.
('소비자': 0, '는': 1, '주로': 2, '소비': 3, '하는': 4, '상품': 5, '을': 6, '기준': 7, '으로': 8, '물가상승률': 9, '느낀다': 10)
[1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1]
문서1과 문서2를 합쳐서 (이를 문서3이라고 명명합시다.) BoW를 만들 수도 있다.
문서3: 정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다. 소비자는 주로 소비하는 상품을 기준으로 물가상승률을 느낀다.
위의 코드에 문서3을 입력으로 하여 인덱스 할당과 BoW를 만든다면 아래와 같은 결과가 나온다.
('정부': 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]
BoW는 각 단어가 등장한 횟수를 수치화하는 텍스트 표현 방법이기 때문에, 주로 어떤 단어가 얼마나 등장했느지를 기준으로 문서가 어떤 성격의 문서인지를 판단하는 작업에 쓰인다.
즉, 분류 문제나 여러 문서 간의 유사도를 구하는 문제에 주로 쓰인다.
'달리기', '체력', '근력'과 같은 단어가 자주 등장하면 해당 문서를 체육 관련 문서로 분류할 수 있을 것이며, '미분', '방정식', '부등식'과 같은 단어가 자주 등장한다면 수학 관련 문서로 분류할 수 있습니다.
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}
알파벳 I는 BoW를 만드는 과정에서 사라진것을 알수 있는데, 이는 CountVectorizer가 기본적으로 길이가 2이상인 문자에 대해서만 토큰으로 인식하기 때문이다.
영어에서는 길이가 짧은 문자를 제거하는 것 또한 전처리 작업으로 고려되기도 한다.
주의할 것은 CountVectorizer는 단지 띄어쓰기만을 기준으로 단어를 자르는 낮은 수준의 토큰화를 진행하고 BoW를 만든다는 점이다. 이는 한국어에 CountVectorizer를 적용하면, 조사 등의 이유로 제대로 BoW가 만들어지지 않음을 의미한다.
4. 불용어를 제거한 BoW 만들기
BoW를 만들때 불용어를 제거하는 일은 자연어 처리의 정확도를 높이기 위해서 선택할 수 있는 전처리 기법이다.
영어의 BoW를 만들기 위해 사용하는 CountVectorizer는 불용어를 지정하면, 불용어는 제외하고 BoW를 만들 수 있도록 불용어 제거 기능을 지원하고 있다.
4-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}
4-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}
# everything 사라짐
4-3) NLTKL에서 지원하는 불용어 사용
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}
* 정리:
1. BoW는 단어들의 순서는 고려하지 않고 출현 빈도에만 집중하는 수치화 표현방법이다.
2. BoW는 분류 문제나 여러 문서 간의 유사도를 구하는 문제에 주로 쓰인다.
3. 단어의 빈도를 Count하여 Vector로 만드는 CountVectorizer클래스가 있다.
'Deep learning > NLP(자연어처리)' 카테고리의 다른 글
TF-IDF(Term Frequency-Inverse Document Frequency) (0) | 2020.03.03 |
---|---|
문서 단어 행렬(Document-Term Matrix, DTM) (0) | 2020.03.03 |
카운트 기반의 단어 표현(Count based word Representation) (0) | 2020.03.02 |
정규 표현식(Regular Expression) (0) | 2020.03.01 |
어간 추출(Stemming) and 표제어 추출(Lemmatization) (0) | 2020.03.01 |