일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 자카드 유사도
- 병합 정렬
- 희소행렬
- COO
- 데이터분석
- CSC
- sparse matrix
- merge sort
- 정렬 알고리즘
- CountVectorizer
- jaccard similarity
- 주피터 노트북
- insertion sort
- csr
- 아나콘다 가상환경
- 파이썬 가상환경
- 자연어처리
- 문서-단어 행렬
- 파이썬
- 삽입정렬
- scipy
- dok
- Today
- Total
시래 블로그
단어 수 세서 문서-단어 행렬 만들기, CountVectorizer 본문
텍스트 데이터를 분석할 때 가장 흔히 사용하는 방법이 단어 수를 세는 것입니다. 각각의 텍스트에 등장한 단어 수를 알면 이를 기반으로 키워드를 추출한다거나, 표절 검사, 문서 분류 등 다양한 분석을 할 수 있습니다.
예를 들어 아래와 같은 세 개의 문장이 있을 때,
- 누구나 한번쯤은 사랑에 웃고
- 누구나 한번쯤은 사랑에 울고
- 그것이 바로 사랑 사랑 사랑이야
문장별로 단어가 몇 번 등장했는지 표(행렬)로 나타낼 수 있습니다.
누구나 | 한번쯤 | 사랑 | 웃고 | 울고 | 그것 | 바로 | |
문장1 | 1 | 1 | 1 | 1 | |||
문장2 | 1 | 1 | 1 | 1 | |||
문장3 | 3 | 1 | 1 |
이렇게 한 번 정리를 해놓으면, 이후 여러 가지 분석에 이용할 수 있습니다. 예를 들어 '사랑이 5번 등장했으니, 무언가 중요한 단어가 않을까'라거나, '문장1과 문장2는 문장3보다 비슷하구나' 식으로 말이죠.
자연어 처리에서는 위와 같은 표를 '문서-단어 행렬'(document-term matrix)이라고 부릅니다.
또한 여기서는 단어가 사용된 순서는 무시되었는데, 이를 'bag of words'라고 부릅니다. 예를 들어 문장1은 네 개의 단어(누구나, 한번쯤, 사랑, 웃고)가 순서 없이 뒤섞여 들어 있는 가방에 비유한 것입니다.
수학적으로 보면 위 표는 각각의 문장과 단어를 벡터로 나타내준 것입니다. 따라서 행렬과 벡터의 성질을 이용한 알고리즘(분류, 클러스터링, 차원축소 등)을 적용해볼 수도 있습니다.
사이킷런의 CountVectorizer
사이킷런(sklearn)의 CountVectorizer는 각 문서에 어떤 단어가 몇 번 등장했는지를 파악할 때 사용합니다. 단어를 세서(count) 문서를 벡터화(vectorize)한다는 의미입니다. 이는 문서에서 특성(어떤 단어가 몇 번 등장했는지)을 추출하는 것이으로 볼 수 있기 때문에, CountVectorizer는 sklearn.feature_extraction 항목에 있습니다.
다음과 같이 세 개의 문장을 담은 text를 fit_transfrom시켜주면,
from sklearn.feature_extraction.text import CountVectorizer
text = ["누구나 한번쯤은 사랑에 웃고",
"누구나 한번쯤은 사랑에 울고",
"그것이 바로 사랑 사랑 사랑이야"]
count_vec = CountVectorizer()
m = count_vec.fit_transform(text)
m.toarray()
아래와 같은 결과를 얻을 수 있습니다.
문서-단어 행렬에서 각각의 열(column)이 의미하는 바는 vocabulary_를 통해 확인할 수 있습니다.
count_vec.vocabulary_
예를 들어 1행은 누구나, 8행은 한점쯤은 ... 등을 나타낸다는 의미입니다.
여기서 '사랑'이라는 같은 단어를 CountVectorizer는 사랑에, 사랑, 사랑이야라는 각기 다른 단어로 인식했다는 것을 알 수 있습니다. 이를 해결하는 방법은 미리 전처리를 해주든가, 아니면 내가 쓰고자 하는 토크나이저를 CountVectorizer의 초깃값으로 넣어주는 것입니다.
count_vec = CountVectorizer(tokenizer=mytokenizer)
CountVectorizer의 디폴트 값은 영어에 맞춰져 있기 때문에 한국어 자연어 처리에는 맞지 않습니다. 대신 tokenizer 인자에 한국어 형태소 분석기(konlpy 등)를 넣어주면 됩니다.
CountVectorizer는 토크나이징+벡터화를 동시에 해줍니다. 만약 토크나이징을 먼저 한 다음 list of lists 형식의 텍스트 데이터를 넣어주고 싶다면 다음과 같이 해주면 됩니다.
from sklearn.feature_extraction.text import CountVectorizer
text = [['누구나','한번쯤','사랑', '웃고'],
['누구나','한번쯤','사랑', '울고'],
['그것', '바로', '사랑', '사랑', '사랑']]
count_vec = CountVectorizer(tokenizer=lambda x: x, lowercase=False)
m = count_vec.fit_transform(text)
tokenizer 인자에는 list를 받아서 그대로 list를 내보내는 함수를 넣어줍니다. 또한 소문자화를 하지 않도록 설정해야 에러가 나지 않습니다.
그런데 저는 문서-단어 행렬을 만들어야 하는 일이 있을 때, 사이킷런의 CountVectorizer를 불러오기보다는 자체 제작한 걸 사용하곤 합니다. 그 방법에 대해 알아보겠습니다.
CountVectorzier 자체 제작하기
사이킷런 API처럼 사용할 수 있게 myCountVectorzier 클래스를 정의했습니다. 토크나이징은 이미 되어있다고 가정합니다.
import numpy as np
class myCountVectorizer():
def __init__(self):
self.vocabulary_ = {}
def fit(self, docs):
self.vocabulary_ = {}
for words in docs:
for word in words:
self.vocabulary_[word] = self.vocabulary_.get(word, len(self.vocabulary_))
return self
def transform(self, docs):
m = np.zeros((len(docs), len(self.vocabulary_)))
for i, words in enumerate(docs):
for word in words:
j = self.vocabulary_.get(word)
if j is not None:
m[i, j] += 1
return m
def fit_transform(self, docs):
self.fit(docs)
return self.transform(docs)
fit 단계에서 하는 일은 단어 사전을 만드는 일입니다. 단어마다 인덱스를 부여해서, 이후에 어떤 단어가 행렬의 몇 번 행을 나타내는지 알고자 할 때 사용합니다. 이 단계를 건너 뛰고 이미 만들어져 있는 단어 사전을 이용할 수도 있습니다.
위 코드에서는 딕셔너리의 get 메서드를 이용했습니다. get(k, d)은 딕셔너리에 키가 있으면 해당하는 값을 반환하고, 아니면 d를 반환합니다.
transform 단계에서는 본격적으로 문서-단어 행렬을 만듭니다. 사용법은 CountVectorizer와 비슷합니다.
count_vec = myCountVectorizer()
m = count_vec.fit_transform(text)
m
참고로 문서-단어 행렬은 대규모 데이터에서 대부분의 값이 0이 될 것입니다. 하나의 텍스트에 2000개 종류의 단어를 사용한다 해도, 전체 단어 셋은 몇 만 개는 될 것이기 때문입니다. 이는 메모리 부족 문제를 초래하기 쉽기 때문에, 사이킷런의 CountVectorizer는 희소행렬을 사용합니다. 희소행렬은 반환하도록 만드는 건 np.ones를 dok_matrix로만 바꾸어주면 됩니다. 희소행렬에 대한 좀 더 자세한 내용은 여기를 참조해주세요.
from scipy.sparse import dok_matrix
class sparseCountVectorizer(myCountVectorizer):
def transform(self, docs):
m = dok_matrix((len(docs), len(self.vocabulary_)))
for i, words in enumerate(docs):
for word in words:
j = self.vocabulary_.get(word)
if j is not None:
m[i, j] += 1
return m.tocsr()
'데이터 과학' 카테고리의 다른 글
아나콘다 가상환경 명령어 모음 (0) | 2021.07.01 |
---|---|
병합 정렬(Merge sort) (0) | 2020.03.15 |
삽입 정렬(Insertion sort) (0) | 2020.03.14 |
자카드 유사도의 변형과 활용, 상세 설명 (0) | 2020.02.06 |
파이썬 scipy 희소행렬 설명 (coo, csr, dok) (0) | 2020.02.05 |