TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 머신러닝>

[머신러닝] 텍스트 분석: BOW(Bag of Words)

by 기록자_Recordian 2024. 10. 30.
728x90
반응형
텍스트 분석이란?
 

[머신러닝] 텍스트 분석

이전 내용 [머신러닝] 군집화 (Clustering)군집화(Clustering) [군집]군집은 비슷한 샘플을 클러스터 또는 비슷한 샘플의 그룹으로 할당하는 작업으로, 데이터 분석, 고객 분류, 추천 시스템, 검색 엔

puppy-foot-it.tistory.com


이전 내용
 

[머신러닝] 텍스트 분석: 텍스트 정규화

텍스트 분석이란? [머신러닝] 텍스트 분석이전 내용 [머신러닝] 군집화 (Clustering)군집화(Clustering) [군집]군집은 비슷한 샘플을 클러스터 또는 비슷한 샘플의 그룹으로 할당하는 작업으로, 데이

puppy-foot-it.tistory.com


Bag of Words - BOW

 

Bag of Words 모델은 문서가 가지는 모든 단어(Words)를 문맥이나 순서를 무시하고 일괄ㅈ거으로 단어에 대해 빈도 값을 부여해 피처 값을 추출하는 모델이다.

문서 내 모든 단어를 한꺼번에 봉투(Bag) 안에 넣은 뒤에 흔들어서 섞는다는 의미로 Bag of Words 모델이라고 한다.

 

[BOW 모델의 장점]

쉽고 빠른 구축에 있다. 단순히 단어의 발생 횟수에 기반하고 있지만, 예상보다 문서의 특징을 잘 나타낼 수 있는 모델이어서 전통적으로 여러 분야에서 활용도가 높다.

 

[BOW 모델의 단점]

  • 문맥 의미(Semantic Context) 반영 부족: BOW는 단어의 순서를 고려하지 않기 때문에 문장 내에서 단어의 문맥적인 의미가 무시된다. 이를 보완하기 위해 n_gram 기법을 활용할 수 있으나, 제한적인 부분에 그친다.
  • 희소 행렬 문제(희소성, 희소 행렬): BOW로 피처 벡터화를 수행하면 희소 행렬 형태의 데이터 세트가 만들어지기 쉽다. 희소 행렬은 일반적으로 ML 알고리즘의 수행 시간과 예측 성능을 떨어뜨리기 때문에 희소 행렬을 위한 특별한 기법이 마련돼 있다.

※ 희소 행렬(Sparse Matrix): 대규모의 칼럼으로 구성된 행렬에서 대부분의 값이 0으로 채워지는 행렬

※ 밀집 행렬(Dense Matrix): 대부분의 값이 0이 아닌 의미 있는 값으로 채워져 있는 행렬

 

[단어 수(Word Count) 기반으로 피처 추출]

아래의 2개의 문장을 Bag of Words 단어 수 기반으로 피처를 추출해 본다.

 

문장1.

My wife likes to watch baseball games and my daughter likes to watch baseball games too

 

문장2.

My wife likes to play baseball

 

▶ 문장 1, 2에 있는 모든 단어에서 중복을 제거하고 각 단어(feature 또는 term) 를 칼럼 형태로 나열한 뒤, 각 단어에 고유의 인덱스를 부여한다.

그리고 개별 문장에서 해당 단어가 나타나는 횟수(Occurrence)를 각 단어(단어 인덱스)에 기재한다.

Index 0 1 2 3 4 5 6 7 8 9 10
단어 and baseball daughter games likes my play to too watch wife
문장 1 1 2 1 2 2 2   2 1 2 1
문장 2   1     1 1 1 1     1

BOW 피처 벡터화

 

머신러닝 알고리즘은 일반적으로 숫자형 피처를 데이터로 입력받아 동작하기 때문에 텍스트와 같은 데이터는 머신러닝 알고리즘에 바로 입력할 수가 없다. 따라서 텍스트는 특정 의미를 가지는 숫자형 값인 벡터 값으로 변환해야 하며, 이러한 변환을 피처 벡터화라고 한다.

피처 벡터화는 기존 텍스트 데이터를 또 다른 형태의 피처의 조합으로 변경하기 때문에 넓은 범위의 피처 추출에 포함된다.

 

BOW 모델에서 피처 벡터화를 수행한다는 것은 모든 문서에서 모든 단어를 칼럼 형태로 나열하고 각 문서에 해당 단어의 횟수나 정규화된 빈도를 값으로 부여하는 데이터 세트 모델로 변경하는 것이다.

 

[BOW의 피처 벡터화 방식]

  • 카운트 기반의 벡터화: 단어 피처에 값을 부여할 때 각 문서에서 해당 단어가 나타나는 횟수(Count)를 부여하는 방식. 카운트 값이 높을수록 중요한 단어로 인식된다. 그러나 카운트만 부여할 경우 그 문서의 특징을 나타내기 보다는 언어의 특성상 문장에서 자주 사용될 수 밖에 없는 단어까지 높은 값을 부여하게 된다.
  • TF-IDF(Term Frequency - Inverse Document Frequency) 기반의 벡터화: 카운트 기반의 문제를 보완하기 위해 사용되는 방식. 개별 문서에서 자주 나타나는 단어에 높은 가중치를 주되, 모든 문서에서 전반적으로 자주 나타나는 단어에 대해서는 패널티를 주는 방식으로 값 부여.

▶ 문서마다 텍스트가 길고 문서의 개수가 많은 경우 카운트 방식보다는 TF-IDF 방식을 사용하는 것이 더 좋은 예측성능을 보장할 수 있다.


사이킷런으로 피처 벡터화 구현

 

  • CountVerorizer 클래스: 카운트 기반의 벡터화를 구현한 클래스
  • TfidfVectorizer 클래스: TF-IDF 벡터화 구현한 클래스 (파라미터와 변환 방법은 CounterVectorizer 클래스와 동일)

사이킷런의 CountVectorizer 클래스는 단지 피처 벡터화만 수행하지는 않으며 소문자 일괄 변환, 토큰화, 스톱 워드 필터링 등의 텍스트 전처리도 함께 수행한다. 해당 클래스 역시 사이킷런의 다른 피처 변환 클래스와 마찬가지로 fit()과 transform()을 통해 피처 벡터화된 객체를 반환한다.

 

[CountVectorizer의 입력 파라미터]

파라미터 명 파라미터 설명
max_df 전체 문서에 걸쳐서 너무 높은 빈도수를 가지는 단어 피처를 제외하기 위한 파라미터.
max_df = 100과 같이 정수 값을 가지면 전체 문서에 걸쳐 100개 이하로 나타나는 단어만 피처로 추출한다.
max_df = 0.95와 같이 부동소수점 값(0.0 ~ 1.0)을 가지면 전체 문서에 걸쳐 빈도수 0~95% 까지의 단어만 피처로 추출하고 나머지 상위 5%는 피처로 추출하지 않는다.
min_df 전체 문서에 걸쳐서 너무 낮은 빈도수를 가지는 단어 피처를 제외하기 위한 파라미터.
min_df =2 와 같이 정수 값을 가지면 전체 문서에 걸쳐서 2번 이하로 나타나는 단어는 피처로 추출하지 않는다.
min_df = 0.2와 같이 부동소수점 값(0.0 ~ 1.0)을 가지면 전체 문서에 걸쳐서 하위 2% 이하의 빈도수를 가지는 단어는 피처로 추출하지 않는다.
max_features 추출하는 피처의 개수를 제한하며 정수로 값을 지정.
max_features = 2000 으로 지정할 경우 가장 높은 빈도를 가지는 단어 순으로 정렬해 2000개까지만 피처로 추출.
stop_words 'english'로 지정하면 영어의 스톱 워드로 지정된 단어는 추출에서 제외
n_gram_range Bag of Words 모델의 단어 순서를 어느 정도 보강하기 위한 n_gram 범위 설정. (튜플 형태)
예를 들어 (1, 1) 로 지정하면 토큰화된 단어를 1개씩 피처로 추출.
(1, 2)로 지정하면 토큰화된 단어를 1개씩(minimum 1), 그리고 순서대로 2개씩(maximum 2) 묶어서 피처로 추출.
analyzer 피처 추출을 수행한 단위 지정. (디폴트는 'word')
Word가 아니라 character의 특정 범위를 피처로 만드는 특정한 경우 등을 적용할 때 사용
token_pattern 토큰화를 수행하는 정규 표현식 패턴 지정.
디폴트 값은 '\b\w\w+\b 로, 공백 또는 개행 문자 등으로 구분된 단어 분리자(\b) 사이의 2문자 (문자 또는 숫자) 이상의 단어(word)를 토큰으로 분리.
analyzer = 'word'로 설정했을 때만 변경 가능하나 디폴트 값을 변경할 경우는 거의 발생하지 않음.
tokenizer 토큰화를 별도의 커스텀 함수로 이용시 적용.
일반적으로 CountTokenizer 클래스에서 어근 변환 시 이를 수행하는 별도의 함수를 tokenizer 파라미터에 적용하면 된다.

 

[CountVetorizer 클래스를 이용한 텍스트 피처 벡터화 방법]

  • 영어의 경우 모든 문자를 소문자로 변경하는 등의 전처리 작업 수행
  • 디폴트로 단어 기준으로 n_gram_range를 반영해 각 단어를 토큰화
  • 텍스트 정규화 수행 (stop_words 파라미터가 주어진 경우 스톱 워드 필터링만 가능)
  • max_df, min_df, max_features 등의 파라미터를 이용해 토큰화된 단어를 피처로 추출하고 단어 빈도수 벡터 값 적용

Stemming과 Lammatization 같은 어근 변환은 CountVectorizer에서 직접 지원하지는 않으나 tokenizer 파라미터에 커스텀 어근 변환 함수를 적용하여 어근 변환을 수행할 수 있다.


BOW 벡터화를 위한 희소 행렬 - COO 형식 / CSR 형식

 

사이킷런의 CountVectorizer / TfidfVectorizer 를 이용해 텍스트를 피처 단위로 벡터화해 변환하고 CSR 형태의 희소 행렬을 반환한다. 희소 행렬은 너무 많은 불필요한 0 값이 메모리 공간에 할당되어 메모리 공간이 많이 필요하며, 행렬의 크기가 커서 연산 시에도 데이터 액세스를 위한 시간이 많이 소모된다.

따라서 이러한 희소 행렬을 물리적으로 적은 메모리 공간을 차지할 수 있도록 변환해야 하며, 대표적으로 아래와 같은 방식이 있다.

  • 희소 행렬 - COO 형식: 0이 아닌 데이터만 별도의 데이터 배열에 저장하고, 그 데이터가 가리키는 행과 열의 위치를 별도의 배열로 저장하는 방식. 주로 사이파이(Scipy)의 sparse 패키지 사용. 그러나 행과 열의 위치를 나타내기 위해서 반복적인 위치 데이터를 사용해야 하는 문제점이 있다.
  • 희소 행렬 - CSR 형식: COO 형식의 문제점을 해결한 방식으로, 행 위치 배열에 내에 있는 고유한 값의 시작 위치만 다시 별도의 위치 배열로 가지는 변환 방식. 사이파이의 csr_matrix 클래스를 이용.

일반적으로는 큰 희소 행렬을 저장하고 계산을 수행하는 능력이 CSR 형식이 더 뛰어나기 때문에 CSR을 더 많이 사용한다.

 

[희소 행렬 - COO 형식]

사이파이의 sparse를 이용해 희소 행렬 변환을 COO 형식으로 수행.

먼저 [[3, 0, 1], [0, 2, 0]]을 넘파이의 ndarray 객체로 만들고 COO 형식의 희소 행렬로 변환.

import numpy as np

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

# 밀집 행렬을 사이파의 coo_matrix 클래스를 이용해 COO 형식의 희소 행렬로 변환
# 0이 아닌 데이터를 별도의 배열 데이터로 만들고, 행 위치배열과 열 위치 배열을 각각 생성
# coo_matrix() 내에 생성 파라미터로 입력

from scipy import sparse

# 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_matrix 클래스를 이용해 COO 형식의 희소 행렬로 변환
- 0이 아닌 데이터를 별도의 배열 데이터로 만들고, 행 위치배열과 열 위치 배열을 각각 생성
- coo_matrix() 내에 생성 파라미터로 입력

 

sparse_coo는 COO 형식의 희소 행렬 객체 변수이다. 이를 toarray() 메서드를 이용해 다시 밀집 형태의 행렬로 출력해 본다.

sparse_coo.toarray()

다시 원래의 데이터 형태로 추출된다.

 

[희소 행렬 - CSR 형식]

CSR 방식의 변환은 사이파이의 csr_matrix 클래스를 이용하면 되며, 0이 아닌 데이터 배열과 열 위치 배열, 그리고 행 위치 배열의 고유한 값의 시작 위치 배열을 csr_matrix 의 생성 파라미터로 입력하면 된다.

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 = ([1, 5, 1, 4, 3, 2, 5, 6, 3, 2, 7, 8, 1])

# 행 위치와 열 위치 각각 배열
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 희소 행렬로 생성한다.

dense3 = 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]])

coo = sparse.coo_matrix(dense3)
csr = sparse.csr_matrix(dense3)

 

사이킷런의 CountVectorizer나 TfidfVectorizer 클래스로 변환된 피처 벡터와 행렬은 모두 사이파이의 CSR 형태의 희소 행렬이다.


다음 내용

 

[머신러닝] 텍스트 분석: 분류 실습

텍스트 분석이란? [머신러닝] 텍스트 분석이전 내용 [머신러닝] 군집화 (Clustering)군집화(Clustering) [군집]군집은 비슷한 샘플을 클러스터 또는 비슷한 샘플의 그룹으로 할당하는 작업으로, 데이

puppy-foot-it.tistory.com


[출처]

파이썬 머신러닝 완벽 가이드

728x90
반응형