데이터 한 그릇

텍스트 분석(3) - Bag of Words (BOW) 본문

머신러닝/텍스트 분석

텍스트 분석(3) - Bag of Words (BOW)

장사이언스 2021. 9. 2. 17:28

Bag of Words - BOW

 

BOW 모델은 문서가 가지는 모든 단어들을 문맥과 순서를 무시하고 일괄적으로 단어에 대한 빈도 값을 부여해 피처 값을 추출하는 모델이다. 비유적으로, 양념감자를 예시로들 수 있다. 문서에 있는 모든 단어들을 추출하여 양념감자 팩에 넣고 뒤섞는 것이 이에 비유될 수 있다. Bag of Words 자체가 이런 비유에서 모델 이름이 생성됐다.

 

프로세스는 다음과 같다.  

 

만일 문장 1 과 문장 2가 있다면 문장1과 문장2의 모든 단어들을 중복을 제외하고 추출한후 칼럼으로 나열한다. 그리개별문장들을 인덱스로 설정하고 각 인덱스에서 칼럼에 나열되어 있는 단어들의 횟수들을 value 값으로 측정한다.

 

이러한 프로세스를 가지는 BOW 는 쉽고 빠른 구축을 할 수 있다는 장점이 있다. 하지만 단점도 존재한다.

 

먼저 문맥과 순서를 제외하고 피처값을 설정하기 때문에, 문맥해석에 어려움을 겪는다. 그리고 희소 행렬 문제가 발생한다. 희소 행렬이란 대부분의 칼럼 값이 0으로 채워지는 행렬을 의미한다. 만일 대부분의 칼럼 값이 값이 채워져 있다면 밀집 행렬이라고 부른다.

 

희소 행렬 문제가 발생하는 이유는 BOW 모델의 프로세스에 있다. BOW 는 여러 문서들이 있을 때 모든 문서들의 단어들을 추출하여 칼럼으로 나열한다. 각 문서들은 추출한 칼럼을 기반으로 각 문서의 단어 빈도 횟수를 칼럼 값으로 설정한다. 하지만, 각 문서에서 사용되는 단어를 추출하여 칼럼을 만들기 때문에 칼럼의 개수는 무수히 많지만, 각 문서마다 사용하는 단어는 당연히 상이하기 때문에 문서마다 각 칼럼 값들이 0으로 채워질 확률이 높다. 희소 행렬은 일반적으로 ML 모델의 성능을 저하하기 때문에 이는 BOW 모델의 단점이라고 할 수 있다.

 

 


BOW 피처 벡터화

 

머신러닝은 일반적으로 숫자형 피처를 데이터로 입력받아 그 임무를 수행한다. 따라서 머신러닝 기반의 텍스트 분석 또한 모델에 사용되는 데이터가 숫자형 값을 가지는 피처를 가져야만 한다. 따라서 텍스트는 특정 의미를 가지는 숫자형 값인 벡터 값으로 변환해야 하는데 이러한 변환을 피처 벡터화라고 부른다.

 

 

BOW 모델은 모든 문서에서 중복을 제외하고 사용되는 단어 N개를 뽑아 피처화 한다. 그리고 각 M개의 문서들은 칼럼으로 선정된 단어들의 빈도 횟수를 피처 값으로 측정한다. 따라서 만들어지는 행렬의 모습은 M X N 이다.

 

일반적으로 BOW 모델의 피처 벡터화는 두 가지 방식을 취한다.

 

  1. 카운트 기반의 벡터화
  2. TF-IDF(Term Frequency - inverse Document Frequency) 기반의 벡터화

카운트 기반의 벡터화는 위에 설명한 방식과 같지만 단점이 존재한다. 카운트 기반의 벡터화는 빈도 횟수가 높은 단어를 중요도가 높은 단어로 측정한다. 하지만 모든 문서에서 공통적으로 많이 사용되는 단어들조차 중요도가 높은 단어로 선정한다. 이를 보완하여 나타난 피처 벡터화 방식이 TF-IDF 방식이다. TF-IDF 의 피처 벡터화 방식 또한 빈도수가 높은 단어를 중요도가 높은 단어로 측정하지만, 모든 문서에서 보편적으로 많이 사용되는 단어에 대해서는 페널티를 부여한다. 이를 통해 단어에 대한 가중치의 균형을 맞춘다.

 

BOW 피처 벡터화에서 사용되는 parameter 값들

 

  1. max_df : 전체 문서에 걸쳐서 너무 높은 빈도수를 기록하는 피처는 활용도가 낮은 피처일 가능성이 크다. 왜냐하면 스톱 워드와 비슷한 문법적인 특성으로 많이 사용될 가능성이 있기 때문에다. 따라서 너무 높은 빈도수를 기록하는 피처에 대해 대비하기 위해서 max_df 파라미터를 사용한다. 만일 1000으로 값을 설정했다면, 빈도수가 1000이상인 피처를 제거하게 된다.
  2. min_df : 전체 문서에 걸쳐서 너무 낮은 빈도수를 기록하는 피처는 활용도가 낮은 피처일 가능성이 크다. 따라서 파라미터 값을 설정하여 그러한 피처를 제거하게 된다. 만일 2로 설정하게 되면, 2이하인 피처는 제외한다.
  3. max_features : 추출하는 피처의 개수를 제한한다. 모든 문서의 단어들을 중복을 제거하고 칼럼을 설정한다면 무수히 많은 칼럼이 생성될텐데 사전에 그 개수를 제안하는 파라미터값이다.
  4. stop_words : 'english' 로 설정하면 문법상만 의미있는 영어 단어들(will, is 등등) 을 제거한다.

등등

 


BOW 방식의 피터 벡터화를 위한 희소 행렬

 

희소 행렬이란 피처 값들이 0이 대다수인 행렬을 의미한다. 앞서 설명한듯이 BOW 방식은 모든 문서의 단어들을 중복을 제외하고 피처로 설정한 이후 각 문서마다 피처에 따른 단어의 빈도수를 피처 값으로 설정한 방식이다. 따라서 각 인덱스는 피처 값으로 0을 기록할 확률이 높다. 따라서 BOW 방식은 희소 행렬일 가능성이 매우 높다. 실제로 BOW 방식의 피터 벡터화를 수행한 대부분의 행렬이 희소 행렬이다.

 

BOW 방식의 피처 벡터화가 희소 행렬이기 때문에, 메모리 문제가 발생한다. 왜냐하면 불필요한 0 값들이 무수히 많기 때문이다. 따라서 BOW 방식은 무수히 많은 메모리를 차지하는 희소 행렬을 메모리를 덜 차지하게 처리해야만 한다. 그 방식으로 두 가지 방식이 존재하는데 바로 COO 형식과 CSR 형식이다. COO 방식과 CSR 방식은 희소 행렬이 메모리를 덜 차지하게끔 처리한다.

 

COO 방식보단 CSR 방식이 행렬에 대해서 저장하거나 계산할 때 더 유리하다. 먼저 COO 방식부터 살펴보도록 하자.

 

1. COO 

 

COO 방식의 전반적인 프로세스를 살펴보면, 먼저 0값이 아닌 데이터를 배열 형식으로 저장하고, 0값이 아닌 데이터이 행 위치와 열 위치를 배열 형식으로 저장한다. 코드는 다음과 같다.

 

import numpy as np
from scipy import sparse


dense = np.array([[3,0,1],
                 [0,2,0]])


#0이 아닌 데이터 추출
 
data = np.array([3,1,2])

#행 위치와 열 위치를 각각 배열로 생성

row_pos =  np.array([0,0,1])
col_pos = np.array([0,2,1])

#sparse 패키지의 coo_matrix 를 이용해 COO 형식으로 희소 행렬 생성

sparse_coo = sparse.coo_matrix((data, (row_pos, col_pos)))

 

COO 방식과 CSR 방식은 모두 SCIPY 패키지를 이용한다.

 

2. CSR 방식

 

CSR 방식은 COO 방식과 비슷하지만 ROW 위치에 대해서 기록한 배열을 수정함으로써 메모리를 더 줄여 희소 행렬을 처리한다. 예를 들어서 0이 아닌 데이터의 ROW 의 위치가 [1,1,1,1,1,2,2,3,3] 라고 한다면, ROW 위치 값 데이터의 고유 값이 변하는 위치를 배열로 저장해둔다. 위의 ROW 위치 데이터의 고유 값이 변하는 위치를 배열로 저장하면 다음과 같다. [0,5,7]. 여기서 추가적으로 배열에 이 데이터의 개수를 지정해야만 한다. 따라서 [0,5,7,9] 이다.

 

 

코드는 다음과 같다.

 

from scipy import sparse

dense2 = np.array([[0,0,1,0,0,5],
                  [1,4,0,3,2,5],
                  [0,6,0,3,0,0],
                  [2,0,0,0,0,0],
                  [0,0,0,7,0,8],
                  [1,0,0,0,0,0]])

# 0이 아닌 데이터 추출

data2 = np.array([1,5,1,4,3,2,5,6,3,2,7,8,1])

#행 위치와 열 위치를 각각 array로 생성

row_pos = np.array([0,0,1,1,1,1,1,2,2,3,4,4,5])
col_pos = np.array([2,5,0,1,3,4,5,1,3,0,3,5,0])

#COO 형식으로 변환

sparse_coo = sparse.coo_matrix((data2,(row_pos,col_pos)))

#행 위치 배열의 고유한 값의 시작 위치 인덱스를 배열로 생성

row_pos_ind = np.array([0,2,7,9,10,12,13])

#CSR 형식으로 변환

sparse_csr = sparse.csr_matrix((data2, col_pos, row_pos_ind))

print('COO 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인')
print(sparse_coo.toarray())
print('CSR 변환된 데이터가 제대로 되었는지 다시 Dense로 출력 확인')
print(sparse_csr.toarray())

 

COO 방식과 CSR 방식 모두 똑같이 행렬을 저장하고 있음을 확인할 수 있다.

실제적으로 이를 사용할 때, 0이아닌 데이터 배열, ROW,COL 위치 배열을 지정하지 않고 sparse.coo_matrix(dense2) 나 sparse.csr_matrix(dense2) 로 사용해도 된다. 즉, 배열을 바로 파라미터로 사용해도 된다.

 

 

 

'머신러닝 > 텍스트 분석' 카테고리의 다른 글

텍스트 분석(4) - 감성 분석  (0) 2021.09.05
텍스트 분석(2) - 텍스트 전처리, 정규화  (0) 2021.09.01
텍스트 분석(1)  (0) 2021.09.01
Comments