NLP | Bag of Words, scikit-learn의 CounterVectorizer 한국어
in Machine Learning / Nlp
BoW란 무엇인가
Bag of Words (BoW)
단어들의 순서는 전혀 고려하지 않고, 단어들의 출현 빈도(
frequency
)에만 집중하는 텍스트 데이터의 수치화 표현 방법
BoW 특징
- 자주 발생하는 단어가 문서의 특징을 나타낸다는 것을 가정
- 문서 벡터의 차원은 데이터 내 발생하는 모든 단어의 개수와 동일
- 합성어를 독립적인 단어로 개별처리 (ex. log off)
BoW를 만드는 방법
- 각 단어에 고유한 정수 인덱스를 부여한다.
- 각 인덱스의 위치에 단어의 등장 횟수를 기록한 벡터를 만든다.
사이킷런 CounterVectorizer
각 단어에 정수 인덱스를 부여하고, for문을 돌면서 카운트되도록 직접 구현할 수도 있지만, scikit-learn에 이미 구현되어 있다.
from sklearn.feature_extraction.text import CountVectorizer
text = ["가을 아침 내겐 정말 커다란 기쁨이야 가을 아침 내겐 정말 커다란 행복이야 응석만 부렸던 내겐",
"이른 아침 작은 새들 노랫소리 들려오면 언제나 그랬듯 아쉽게 잠을 깬다"]
cv = CounterVectorizer() # vectorizer 개체 생성
X = cv.fit_transform(text) # 리스트 안의 문자열이 각각 문서로 취급된다
print(X.shape) # (문서 개수, 전체 문서에서 등장하는 단어 개수)
### (2, 19)
print(X) # (문서 인덱스, 단어 인덱스) count
'''
(0, 0) 2
(0, 10) 2
(0, 4) 3
(0, 16) 2
(0, 17) 2
(0, 2) 1
(0, 18) 1
(0, 12) 1
(0, 7) 1
(1, 10) 1
(1, 13) 1
(1, 14) 1
(1, 8) 1
(1, 5) 1
(1, 6) 1
(1, 11) 1
(1, 1) 1
(1, 9) 1
(1, 15) 1
(1, 3) 1
'''
print(X.toarray())
'''
[[2 0 1 0 3 0 0 1 0 0 2 0 1 0 0 0 2 2 1]
[0 1 0 1 0 1 1 0 1 1 1 1 0 1 1 1 0 0 0]]
'''
print(cv.vocabulary_) # 단어-인덱스 정보
'''
{'가을': 0, '아침': 10, '내겐': 4, '정말': 16, '커다란': 17, '기쁨이야': 2, '행복이야': 18, '응석만': 12, '부렸던': 7, '이른': 13, '작은': 14, '새들': 8, '노랫소리': 5, '들려오면': 6, '언제나': 11, '그랬듯': 1, '아쉽게': 9, '잠을': 15, '깬다': 3}
'''
X.toarray()
의 [0][0]은 첫번째 문장(문서)에서 단어 인덱스가 0인 단어의 등장 빈도수- 인덱스가 0인 단어는 ‘가을’
- 첫번째 문서에 ‘가을’ 단어가 두 개있으므로 제대로 count됨을 알 수 있다.
CountVectorizer는 토큰화 + 벡터화 동시에 해준다.
- tokenizing(토큰화): 문장을 단어 단위로 끊어줌 (띄어쓰기 기준 split)
- vectorizing(벡터화): 단어들을 수치형 데이터(vector)로 바꿔줌
보통 띄어쓰기 기준으로 단어를 끊어 토큰화하고 벡터화하기 때문에, 띄어쓰기로 단어가 나뉘지 않는 한국어에는 적용하기 어렵다.
ex. 사랑을, 사랑이구나, 사랑 -> 모두 다른 단어로 처리됨
한국어 BoW를 만들고 싶을 때
아래 두 방법이 사실 똑같긴 한데,
1) CountVecetorizer 객체에 tokenizer
옵션을 konlpy
로 설정하기
from konlpy.tag import Okt
from sklearn.feature_extraction.text import CountVecetorizer
okt = Okt()
cv = CountVecetorizer(tokenizer=lambda x: okt.nouns(x))
2) konlpy
등으로 tokenizing을 먼저 하고, CountVectorizer
의 옵션을 조절하여 vectorizing만 수행
nouns = []
for doc in documents:
nouns.append(okt.nouns(doc))
cv = CountVectorizer(tokenizer=lambda x: x, lowercase=False) # lowercase 인자를 꼭 False로 해야 에러가 안난다.
X = cv.fit_transform(nouns)
2번 방법으로 하면 토큰화된 결과를 한 번 확인할 수 있음
N-grams
연속된 N개의 단어를 기준으로 텍스트 분석을 수행
TF-IDF
상대적으로 자주 발생하는 단어가 더 중요하다는 점을 반영
- 단순한 빈도수가 아니라 상대적 빈도수가 중요하다
- (특정 단어 빈도수) / (전체 단어 빈도수) * log (데이터 내 총 문서의 개수/데이터 내 특정 단어가 들어간 문서의 개수)
- 모든 문서에서 전반적으로 나타나는 단어의 점수를 줄여줌 (ex. 그러나, 그리고 등)
from sklearn.feature_extraction.text import TfidfVectorizer
# TfidfVectorizer을 불러옵니다. (stop_words 는 영어로 설정)
vectorizer = TfidfVectorizer(stop_words = 'english')
X = vectorizer.fit_transform(df_clean['Review Text'].str.lower())