빈도 기반 텍스트 분석 (Frequency based Text Analysis)

kiana
8 min readJun 12, 2019
Photo by Nick Hillier on Unsplash

문서를 분석하기 위해서는 텍스트를 숫자로 바꿔주는 과정이 필요하다. 이를 가리켜 embedding 혹은 vectorizing 이라고 한다.

One Hot Encoding 은 학습 데이터의 모든 단어를 포함하는 어휘 목록을 만들고 단어를 어휘 목록의 인덱스로 표현하는 방법이다. 간단하고 이해하기 쉽지만 단어의 수만큼 차원이 필요하므로 대부분의 값이 0이 된다. 이러한 데이터를 sparse data 라고 하며 담고 있는 정보에 비해 용량을 많이 찾이하므로 비효율적인 형태라고 할 수 있다.

문장을 vectorize 하는 방법으로는 Bag of Words 가 있다. Bag of Words 는 문장에 등장하는 단어들의 빈도를 이용하는 방법이다. 단어의 순서와 상관없이, 해당 단어가 문서에 몇 번 나왔느냐만 따진다.

아래는 scikit-learn 의 CountVectorizer 를 사용하여 Bag of Words 로 embedding 을 하는 방법이다. Bag of Words 모델은 빈도를 나타내는 모델이므로 CountVectorizer 를 사용할 수 있다.

결과를 살펴보자.

우리가 만든 CountVectorizer 에 들어간 단어들이다. ‘is’와 ‘the’ 는 stopwords 로 지정했기 때문에 corpus 에 등장하더라도 포함되지 는다.
새로운 데이터를 모델에 넣을 때 transform() 을 쓴다. 원래 데이터를 주면 변형된 데이터로 나타내준다. ‘I did not see the third document’ 를 입력하면 training dataset 에 등장하는 ‘document’ 와 ‘third’ 만 표시가 된다.
전체 학습 데이터를 모델에 넣어서 bag of words 기법으로 나타낸 것이다.

CountVectorizer 과 비슷한 기능의 클래스로 HashingVectorizer 가 있다. HashingVectorizer 는 해시 함수 (hash function) 를 사용하여 적은 메모리와 빠른 속도로 BoW 벡터를 만든다.

CountVectorizer 는 모든 작업을 메모리 상에서 수행하므로, 처리할 문서의 크기가 커지면 속도가 느려지거나 실행이 불가능해진다. 이 때, HashingVectorizer 를 사용하면 단어에 대해 index 번호를 생성하기 때문에 메모리와 실행 시간을 줄일 수 있다. (자세한 내용은 링크를 참고)

feature 의 수가 개별 단어의 수보다 적을 경우에는 서로 다른 단어가 같은 index 값을 갖게 될 수 있지만, corpus 가 충분히 클 경우에는 크게 문제가 되지 않는다.

TF-IDF 는 Term Frequency — Inverse Term Frequency 의 약자로, 포함되어 있는 단어의 중요성에 따라 단어와 document 의 연관성을 계산하는 방법이다. 문서에 등장하는 단어의 상대적인 중요성을 수치로 정규화하여 점수를 계산한다.

아래 문장들에서 ‘Mr.Green’ 의 Term Frequency 를 살펴보자.

이번에는 ‘the green plant’ 의 Term Frequency 를 살펴보자.

하지만 이렇게 단어의 등장 빈도만으로는 문장을 제대로 파악할 수 없다. 모든 문서에 공통적으로 나오는 단어는 변별력이 없기 때문이다. 많은 문서에 나오는 단어에 대해서는 penalty 를 줘야 한다. 그래서 등장하는 것이 Inverse Document Frequency 개념이다.

IDF (term, D) = log (전체 document 의 수 / term 을 포함하고 있는 document 의 수)

IDF 는 위 식으로 나타낼 수 있다. 만약에 해당 term 이 모든 문서에 등장한다면 IDF는 log 1=0 이 될것이다. 하지만 만약에 100개 문서 중에 하나의 문서에만 등장하는 term 이라면 IDF 는 log(100/1)=2 가 될 것이다.

이 외에도 IDF 를 측정하는 방법에는 여러 가지가 있지만, Scikit-Learn 에서는 아래와 같이 계산한다:

1을 더하는 이유는, 분모가 0이 되는 일이 없도록 하기 위해서이다.

그렇다면 위에 등장하는 단어들의 IDF 를 살펴보자.

N = 3, df(mr.) = 1, df(green) = 3, df(the) = 1, df(plant) = 2

이번에는 Term Frequency 와 Inverse Term Frequency 를 곱한 값을 살펴보자.

이제는 이 수치를 normalize 해 줘야 한다. 문서의 길이차이가 나기 때문에 L2 normalization 을 시행한다. 각각의 요소를 제곱한 값을 합하고 이에 대해 루트를 취해준다 (=제곱합의 루트값, 즉 원점과 해당 벡터의 직선 거리를 의미한다.) 해당 단어의 TF-IDF 를 모든 단어들의 TF-IDF 의 제곱합의 루트값으로 나눠준다.

이제는 각각 ‘Green’, ‘Mr.Green’, ‘The green plant’ 의 쿼리에 대한 TF-IDF 를 알아보기 위해 Summation 을 실행한다.

이제 corpus ‘a’ 에서는 ‘Mr.Green’ 이, corpus ‘b’ 와 ‘c’ 에서는 ‘the green plant’ 가 tf-idf 가 가장 높게 나왔다.

Scikit-Learn 의 TfidfVectorizer 를 이용해보자.

이제 Tf-idf 를 이용하여 변형한 데이터로, 문서 간 유사도를 측정할 수 있다. 문서 간 유사한 정도를 볼 때에는 코사인 유사도 (Cosine Similarity) 를 측정한다.

코사인 유사도 (Cosine Similarity) 는 데이터의 방향이나 양 등을 따져야 해서 데이터가 벡터로 표현되어야 할 때 사용된다. 벡터의 크기보다는 거리를 측정하기 위해 사용되는데, 두 벡터의 방향이 완전히 동일하면 1, 완전히 반대면 -1의 값을 가지고 직교하면 0의 값을 가진다.

문서에 등장하는 단어가 얼마나 같은지 보다, 두 문서간 등장하는 단어들이 유사한 중요도로 등정하는지, 벡터의 거리를 알고 싶기 때문이다.

마찬가지로, Scikit-Learn 패키지를 사용하여 알고자 하는 term 을 query 로 입력하였을 때, 어떤 문서와 가장 유사도가 높은지를 살펴볼 수 있다. query 를 ‘Mr. Green’ 으로 지정하였을 때 세 문서(문장) 중에서 어떤 문서와 가장 유사도가 높은지 알아보자.

유의할 점은, 입력하는 query 를 CountVectorizer 를 사용하여 우리 모델에 적절한 벡터 형태로 바꿔줘야 한다는 것이다.

query 인 ‘Mr. Green’ 을 transform() 을 사용하여 우리 모델에 알맞은 벡터 형태로 변형해준다. 그 뒤에 q 를 이전에 만든 Bag of Word 으로 나타내어주면 다음과 같은 array 가 나온다.

X[0] 은 우리가 입력한 corpus 의 첫 번째 문장이다. 위 결과창은 X[0] 을 Bag of Words array 로 나타낸 것이다.

각 문장과 query 의 코사인 유사도를 구하면 위와 같다. 첫 번째 문장의 코사인 유사도가 가장 높은 것으로 보아, 첫 번째 문장이 ‘Mr. Green’ 과 가장 관련이 깊다.

빈도 기반 Embedding 은 이해하기 쉽고 간단하지만 여러 단점이 있다.

우선, Bag of Words 기법은 문장의 의미나 맥락을 convey 하지 못한다. 맥락을 파악하지 못하기 때문에 부정어나 동음이의어를 처리하기가 어렵다. 또, embedding vector 의 차원이 너무 크고 sparse 하다는 단점이 있다.

--

--