본문 바로가기

자연어처리(NLP)

2. BOW(Bag of Words)기반 '카운트 벡터(Count Vector)' [초등학생도 이해하는 자연어처리]

반응형

 안녕하세요 '코딩 오페라'블로그를 운영하고 있는 저는 'Master.M'입니다.

현재 저는 '초등학생도 이해하는 자연어 처리'라는 주제로 자연어 처리(NLP)에 대해 포스팅을 하고 있습니다. 제목처럼 진짜 핵심 내용을 쉽게 설명하는 것을 목표로 하고 있으니 자연어 처리(NLP)에 입문하고 싶은 분들은 많은 관심 부탁드립니다. 오늘 알아볼 내용은 'BOW(Bag of Words)'입니다.

 

BOW(Bag of Words) 란?

 우리는 어떻게 글을 이해할까요? 아마 가장 간단한 방법은 글에 나와있는 단어의 빈도를 이용하는 것일 겁니다. 이유는 당연합니다. 축구에 관한 글에서는 '축구'라는 단어가 많이 나올 것이고, BTS관련 기사에서는 'BTS'가 많이 나오기 때문입니다. 이러한 상식으로부터 출발한 것이 바로 'BOW(Bag of Words)'입니다.

BOW(Bag of Words)

BOW는 문서들을 토큰 화하여 중복된 토큰들을 제거한 뒤 이를 위와 같이 가방에 넣습니다. 이후 가방 안에 들어있는 토큰들을 문서의 특성에 맞게 특성 벡터(feature vector)로 만들어줍니다. 여기서 '가방'이라는 이해를 돕는 추상적인 개념이 등장하기 때문에 Bag of Words라고 불립니다.

 

카운트 벡터(Count Vector)

 위에서 가방을 통해 특성 벡터(feature vector)를 만든다고 했는데, 그럼 어떻게 만드는 걸까요? 

가장 흔히 사용하는 방법이 '카운트 벡터'입니다. '카운트 벡터'란 단어의 문서에서의 빈도(카운트된 수)를 통해 해당 단어의 특성 벡터를 정의하는 개념입니다. 

예를 들어 위와 같이 "I am a elementary school student"와 "And I am a boy"라는 문장들이 있을 때 이를 토큰화 하고 중복된 것을 제거한 뒤 가방에 넣어주면 오른쪽과 같이 됩니다. 

 

그런 다음 가방 속의 토큰들이 문서에서 사용된 빈도수를 표로 나타내면 위와 같습니다. 이 수들은 해당 단어의 특성을 나타냅니다.

 

이러한 특성을 가지고 문장의 특성 벡터를 만들 수 있습니다. 위는 "And I am a boy"라는 문장은 And:1, I:2, am:2, a:2, boy:1의 특성을 가지므로 이를 벡터화하면 1, 2, 2, 2, 1이 됩니다. 이 특성 벡터를 카운트 벡터라고 합니다.

 

위 과정을 코드로 나타내면 다음과 같습니다. 

text = ["I am a elementary school student", "And I am a boy"]

# Word tokenized sentence
from nltk import word_tokenize
text_tokenzied = [word_tokenize(sentence) for sentence in text]
print(text_tokenzied)
[['I', 'am', 'a', 'elementary', 'school', 'student'], ['And', 'I', 'am', 'a', 'boy']]

 먼저 text의 문장들을 정의해주고 word_tokenize를 이용해 토큰화를 해줍니다.

 

 

# Count the words
from collections import Counter

vocab_counter = Counter()
for sentence in text_tokenzied:
    vocab_counter.update(sentence)
    
print(vocab_counter)
Counter({'I': 2, 'am': 2, 'a': 2, 'elementary': 1, 'school': 1, 'student': 1, 'And': 1, 'boy': 1})

그다음 Counter를 통해 문서 전체에 단어들의 빈도를 도출합니다.

 

 

# Vocabulary set
vocab = []
for key, value in vocab_counter.items():
    vocab.append(key)

print(vocab)
['I', 'am', 'a', 'elementary', 'school', 'student', 'And', 'boy']

위에서 도출된 데이터를 가공해 가방에 들어갈 중복되지 않는 단어 set을 만들어줍니다.

 

 

# Count vector
text_count_vector = []

for sentence in text_tokenzied:
    sentence_vector = []
    
    for word in sentence:
        sentence_vector.append(vocab_counter[word])
    text_count_vector.append(sentence_vector)
    
print(text_count_vector)
[[2, 2, 2, 1, 1, 1], [1, 2, 2, 2, 1]]

마지막으로 문장들을 원하는 카운터 벡터로 변환합니다. 여기서 각 단어들이 빈도수로 대체되는 것을 확인할 수 있습니다.

 

위에서 우리는 문장을 카운터 벡터로 변환하였습니다. 그러나 이 경우 저희가 직접 함수를 만들어 전처리를 했는데요. 좀 더 쉬운 방법은 없을까요? 방법은 바로 'sklearn' 패키지를 사용하는 것입니다. 'sklearn'는 다양한 함수들을 손쉽게 사용할 수 있는 패키지입니다. 'sklearn'에 대한 자세한 사항은 추후 설명드리도록 하겠습니다. 

 

from sklearn.feature_extraction.text import CountVectorizer

vector = CountVectorizer()
text = ["I am a elementary school student. And I am a boy"]

vocab_counter_sklearn = vector.fit_transform(text).toarray()

# Count of vocabulary
print(vocab_counter_sklearn)

# Index of list
print('vocabulary :',vector.vocabulary_)
[[2 1 1 1 1 1]]
vocabulary : {'am': 0, 'elementary': 3, 'school': 4, 'student': 5, 'and': 1, 'boy': 2}

 위에서 sklearn의 CountVectorizer을 사용하여 카운트 벡터를 만들었습니다. 여기서 'vocab_counter_sklearn'은 각 단어들의 빈도수를, 'vector.vocabulary_'는 'vocab_counter_sklearn'의 인덱스를 나타냅니다. 예를 들어 'am'의 인덱스는 '0'입니다. 'vocab_counter_sklearn'의 0번 인덱스는 '2'이므로 'am'의 빈도수는 '2'가 되는 것입니다.

 그런데 위에서 직접 만든 카운트 벡터와 비교하면 단어수가 줄어들었습니다. 그 이유는 sklearn.CountVectorizer는 글자 수가 1이면 이를 무시하고 진행합니다(불용어 제거). 이렇게 하면 모델의 학습 성능이 좋아지기 때문입니다.

 

정리

- BOW(Bag of Words): 문서들을 토큰 화하여 중복된 토큰들을 제거한 뒤 이를 집합(가방)으로 만듦. 이후 집합(가방) 안에 들어있는 토큰들을 문서의 특성에 맞게 특성 벡터(feature vector)로 만들어줌.

 

- 카운트 벡터(Count Vector): BOW를 이용해 특성 벡터를 만드는 방법 중 하나로, 단어의 문서에서의 빈도(카운트된 수)를 통해 해당 단어의 특성 벡터를 정의하는 개념.

 

  지금 까지 저희는 '카운트 벡터(Count Vector)'에 대해 알아보았습니다. 도움이 되셨나요? 만약 되셨다면 구독 및 좋아요로 표현해 주시면 정말 많은 힘이 됩니다. 궁금한 사항 혹은 앞으로 다루어 주었으면 좋을 주제가 있으시면 댓글 남겨주시면 감사하겠습니다. 저는 '코딩 오페라'의 'Master.M'이었습니다. 감사합니다.

반응형