Bag of Words 모델은 문서가 가지는 모든 단어(Words)를 문맥이나 순서를 무시하고 일괄적으로 단어에 대해 빈도 값을 부여해 피처 값을 추출하는 모델이다. 문서 내 모든 단어를 한꺼번에 봉투 안에 넣은 뒤에 흔들어서 섞는다는 의미로 BOW모델이라고 한다. 아래에 나와있는 2개의 문장이 있다고 가정하고 이 문장을 BOW의 단어 수 (Word Count) 기반으로 피처를 추출해 보았다.
문장1: "My wife lliks to watch baseball games and my daugther likes to watch baseball games too"
문장2: "My wife likes to play baseball"
Bow Process
1. 문장1과 문장2에 있는 모든 단어에서 중복을 제거하고 각 단어(feature or term)를 칼럼 형태로 나열한다. 그러고 나서 각 단어에 고유의 인덱스를 다음과 같이 부여한다.
'and':0, 'baseball':1, 'daugther':2, 'games':3, 'like':4, 'my':5, 'play':6, 'to':7, 'too':8, 'watch':9, 'wife':10
2. 개별 문장에서 해당 단어가 나타나는 횟수를 각 단어에 기재한다. baseball -> 문장1:2, 문장2:1
and | baseball | daugther | 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기반의 NLP 연구는 여러 가지 제약에 부딪히고 있다.
1. 문맥 의미 반영 부족: BOW는 단어의 순서를 고려하지 않기 때문에 문장 내에서 단어의 문맥적인 의미가 무시된다. 물론 이를 보완하기 위해 n_gram 기법을 활용할 수 있지만, 제한적인 부분에 그치므로 언어의 많은 부분을 차지하는 문맥적인 해석을 처리하지 못하는 단점이 있다.
2. 회소 행렬 문제: BOW로 피처 벡터화를 수행하면 희소 행렬 형태의 데이터 세트가 만들어지기 쉽다. 많은 문서에서 단어를 추출하면 매우 많은 단어가 칼럼으로 만들어진다. 문서마다 서로 다른 단어로 구성되기에 단어가 문서마다 나타나지 않는 경우가 더 많다. 즉, 매우 많은 문서에서 단어의 총개수는 수만~수십만 개가 될 수 있는데, 하나의 문서에 있는 단어는 이 중 극히 일부분이므로 대부분의 데이터는 0 값으로 채워지게 된다. 이처럼 대규모의 칼럼으로 구성된 행렬에서 대부분의 값이 0으로 채워지는 행렬을 희소 행렬(Sparse Matrix)이라고 한다. 이와는 반대로 대부분의 값이 0이 아닌 의미 있는 값으로 채워져 있는 행렬을 밀집 행렬(Dense Matrix)이라고 한다. 희소 행렬은 일반적으로 ML 알고리즘의 수행 시간과 예측 성능을 떨어뜨리기 때문에 희소 행렬을 위한 특별 기법이 마련되어 있다.
BOW 피처 벡터화
ML 알고리즘은 일반적으로 숫자형 피처를 데이터로 입력받아 동작하기 때문에 텍스트와 같은 데이터는 머신러닝 알고리즘에 바로 입력할 수가 없다. 따라서 텍스트는 특정 의미를 가지는 숫자형 값인 벡터 값으로 변환해야 하는데, 이러한 변환을 피처 벡터화라고 한다. 예를 들어 피처 벡터화는 각 문서의 텍스트를 단어로 추출해 피처로 할당하고, 각 단어의 발생 빈도와 같은 값을 이 피처에 값으로 부여해 각 문서를 이 단어 피처의 발생 빈도 값으로 구성된 벡터로 만드는 기법이다. 피처 벡터화는 기존 텍스트 데이터를 또 다른 형태의 피처의 조합으로 변경하기 때문에 넓은 범위의 피처 추출에 포함한다.
NOW 모델에서 피처 벡터화를 수행한다는 것은 모든 문서에서 모든 단어를 칼럼 형태로 나열하고 각 문서에서 해당 단어의 횟수나 정규화된 빈도를 값으로 부여하는 데이터 세트 모델로 변경하는 것이다. 예를 들어 M개의 텍스트 문서가 있고, 이 문서에서 모든 단어를 추출해 나열했을 때 N개의 단어가 있다고 가정하면 문서의 피처 벡터화를 수행하면 M개의 문서는 각각 N개의 값이 할당된 피처의 벡터 세트가 된다. 결과적으로 M X N개의 단어 피처로 이뤄진 행렬을 구성하게 된다.
일반적으로 BOW의 피처 벡터화는 두 가지 방식이 있다.
- 카운트 기반의 벡터화
- TF-IDF 기반의 벡터화
단어 피처에 값을 부여할 때 각 문서에서 해당 단어가 나타나는 횟수를 부여하는 경우를 카운트 벡터화라고 한다. 이 방식에서는 카운트 값이 높을수록 중요한 단어로 인식된다. 그러나 카운트만 부여할 경우 그 문서의 특징을 나타내기보단 언어의 특성상 문장에서 자주 사용될 수밖에 없는 단어까지 높은 값을 부여하게 된다. 이러한 문제를 보완하기 위해 TF-IDF 벡터화를 사용한다. TF-IDF는 개별 문서에서 자주 나타나는 단어에 높은 가중치를 주되, 모든 문서에서 전반적으로 나타나는 단어에 대해서는 페널티를 주는 방식으로 값을 부여한다.
사이킷런의 Count 및 TF-IDF 벡터화 구현: CountVectorizer, TfidfVectorizer
사이킷런의 CountVectorizer 클래스는 카운트 기반의 벡터화를 구현한 클래스이다. 사이킷런의 CountVectorizer 클래스는 단지 피처 벡터화만 수행하지는 않으며 소문자 일괄 변환, 토큰화, 스톱 워드 필터링 등의 텍스트 전처리도 함께 수행한다. CountVectorizer에 이러한 텍스트 전처리 및 피처 벡터화를 위한 입력 파라미터를 설정해 동작한다. CountVectorizer 역시 사이킷런의 다른 피처 변환 클래스와 마찬가지로 fit()과 transform()을 통해 피처 벡터화된 객체를 반환한다. 텍스트의 피처 벡터화 방법은 아래와 같다.
1. 영어의 경우 모든 문자를 소문자로 변경.
2. 디폴트로 단어 기준으로 n_gram_range를 반영해 각 단어를 토큰화한다.
3. 텍스트 정규화를 수행한다.
TF-IDF 벡터화는 TfidVectorizer 클래스를 이용한다. 파라미터와 변환 방법은 CountVectorizer와 동일하다.
BOW 벡터화를 위한 희소 행렬
모든 문서에 있는 단어를 추출해 이를 피처로 벡터화하는 방법은 필연적으로 많은 피처 칼럼을 만들 수밖에 없다. 모든 문서에 있는 단어를 중복을 제거하고 피처로 만들면 일반적으로 수만 개에서 수십만 개의 단어가 만들어진다. 만일 n-gram을 (1,2)나 (1,3)으로 증가시키면 칼럼 수는 더욱 증가할 수밖에 없다. 그러나 이러한 대규모의 행렬이 생성되더라도 레코드의 각 문서가 가지는 단어의 수는 제한적이기 때문에 이 행렬의 값은 대부분 0이 차지할 수밖에 없다. BOW 형태를 가진 언어 모델의 피처 벡터화는 대부분 희소 행렬이다.
이 희소 행렬은 너무 많은 불필요한 0 값이 메모리 공간에 할당되어 메모리 공간이 많이 필요하며, 행렬의 크기가 커서 연산 시에도 데이터 액세스를 위한 시간이 많이 소요된다. 따라서 이러한 희소 행렬은 물리적으로 적은 메모리 공간을 차지할 수 있도록 변환해야 하는데, 대표적인 방법으로 COO 형식과 CSR 형식이 있다. 일반적으로 큰 희소 행렬을 저장하고 계산을 수행하는 능력이 CSR 형식이 더 뛰어나기 때문에 CSR을 많이 사용한다.
희소 행렬 - COO 형식
COO 형식은 0이 아닌 데이터만 별도의 데이터 배열에 저장하고, 그 데이터가 가리키는 행과 열의 위치를 별도의 배열로 저장하는 방식이다. 예를 들어 [ [3, 0, 1], [0, 2, 0] ]과 같은 2차원 데이터가 있다고 가정해보자. 0이 아닌 데이터가 있는 위치를 (row, col)로 표시하면 (0, 0), (0, 2), (1, 1)가 된다. row와 col을 별도의 배열로 저장하면 row는 [0, 0, 1], col는 [0, 2, 1] 가 된다. 사이파이의 sparse 패키지는 희소 행렬 변환을 위한 다양한 모듈을 제공한다.
희소 행렬 - CSR 형식
CSR 형식은 COO 형식이 행과 열의 위치를 나타내기 위해 반복적인 위치 데이터를 사용해야 하는 문제점을 해결한 방식이다.
행 위치 배열을 보면 순차적인 같은 값이 반복적으로 나타남을 알 수 있다(0이 2번, 1이 5번). 행 위치 배열이 0부터 순차적으로 증가하는 값으로 이뤄졌다는 특성을 고려하면 행 위치 배열의 고유한 값의 시작 위치만 표기하는 방식으로 이러한 반복을 제거할 수 있다.
위와 같이 고유 값의 시작 위치만 알고 있으며 얼마든지 행 위치 배열을 다시 만들 수 있기 때문에 COO 방식보다 메모리가 적에 들고 빠른 연산이 가능하다.
사이파이의 csr_matrix 클래스를 사용 구현해보았다.
실제 사용 시에는 아래와 같이 밀집 행렬을 생성 파라미터로 입력하면 COO나 CSR 희소 행렬로 생성한다.
'Machine Learning' 카테고리의 다른 글
[파이썬 머신러닝 완벽가이드(위키북스)] : 텍스트 분석 part4 감성 분석 (0) | 2021.12.13 |
---|---|
[파이썬 머신러닝 완벽가이드(위키북스)] : 텍스트 분석 part3 뉴스그룹 분류 (0) | 2021.12.13 |
[파이썬 머신러닝 완벽가이드(위키북스)] : 텍스트 분석 (0) | 2021.12.09 |
[파이썬 머신러닝 완벽가이드(위키북스)] : Clustering (군집화) Part2 (0) | 2021.12.06 |
[파이썬 머신러닝 완벽가이드(위키북스)] : Clustering (군집화) Part1 (0) | 2021.12.01 |
댓글