자연어처리

tf-idf 단어 관련성 분석

coding art 2022. 7. 1. 17:55
728x90

Term Frequency: 문장(document) 에 특정 단어가 나타나는 빈도 수

Inverse Document Frequency : log (1 + 전체 문장(document) 수) /(1 + 문장 별로 특정 단어가 나타나는 횟수)

 

텍스트 데이타 분석 시에 부여된 클라스 값이 긍정적이나 부정으로 분류가 가능한 여러 문서에서 걸쳐서 빈번하게 출현하는 단어들 중에 이들이 쓸만한 정보 내지는 무언가 뚜렷한 정보를 포함하지 않는 경우들이 왕왕 있을 수 있다. 따라서 특징 벡터에 포함되어 나타나는 이러한 경향을 찾아내어 필터링할 수 있는 중요한 기법으로서 문서 빈도수(Document Frequency) 대비 단어 출현 빈도수(Term Frequency)를 알아보자.

Inverse Document Frequency에서 idf(t,d) 는 자연로그를 사용하여 다음과 같이 정의 된다.

nd는 전체 문서의 수이며 df(t,d)는 단어 t를 포함하고 있는 문서의 수이다. “1+”의 1은 df(t,d) 의 값이 0 일 때 분모의 값이 0 이 되는 것을 방지하기 위한 옵션이다. log 값을 취하는 이유는 단어 t를 포함하고 있는 문서의 수 df(t,d)가 작은 값일 때 분수 값 계산 결과가 너무 커질 수 있으므로 값을 조절하기 위한 수단이다.

 

scikit-learn 라이브러리 모듈이 제공하는 idf(t,d)에 해당하는 class 명령 TdidfTransformer를 실행해 보자.

3번째 문서에서 ‘is’ 는 가장 큰 단어 빈도수 값을 가짐을 알 수 있다. 하지만 특징 벡터를 tfidf로 변환 후 ‘is’ 란 단어가 3번째 문서에서 상대적으로 작은 tfidf 값 0.45임을 알 수 있다. 그 이유는 첫 번째 와 두 번째 문서에서도 ‘is’가 포함되어 있는 것으로 보아 뭔가 쓸모가 있으면서도 튀는 정보를 제공하지는 않는 것으로 보이기 때문이다.

 

이미 표준형 idf(t,d)를 정의하였으나 scikit-learm 에서 사용하는 공식은 약간의 차이가 있으며 다음과 같이 보정한다. 자연로그 함수를 사용함에 유의하자.

아울러 tfidf(t,d) 도 살짝 수정된 형태이다.

한편 TfidTransformer class 명령에서는 아래의 공식을 사용하는 L2 normalization 이 적용된다.

위에 정의된 공식을 사용하여 3번째 문장에서 2번 출현하는 ‘and’ 의 경우 0.5 를 계산해보자.

3번째 문장에서 3번 출현하는 ‘is’ 의 경우 0.45 를 계산해보자.

3번째 문장에 나타나는 단어들에 대해서 모조리 tfidf를 계산하면 다음과 같이 주어진다.

[3.39, 3.0, 3.39, 1.29, 1.29, 1.29, 2.0, 1.69, 1.29]

 

이 데이타를 사용하여 L2 normalization을 해보자.

이 데이타에서 두 번째 항이 0.45임을 알 수 있다.

 

사이킷런에서 CountVectorizer를 사용하면 DTM(Data Term Matrix)이 얻어지고, TfidfTransformer를 사용하면 tf-idf 행렬이 얻어진다. 이와 같이 객체를 만든 후 fit_transform() 이라는 함수를 사용하여 tf-idf를 얻어내자.

 

fit_transform() 이외에도, 그냥 transform() 이라는 함수가 더 있다

 

- fit_transform() 은 생성한 객체 (vector, tfidf_transformer) 에다가 해당 문서 혹은 단어들의 벡터값을 저장하면서 벡터화를 진행 하는 함수이다

- transform() 은 학습 데이터를 통해서 얻어진 벡터값을 보고, 그것에 따라 벡터화를 진행하는 함수이다

만약 훈련 데이터를 토대로 'a' 라는 단어에 대해서 벡터값을 [0 1 0] 이라고 생성해놓았다면, 테스트 데이터에서 등장한 'a' 에 대해서도 [0 1 0] 이라고 벡터화를 진행하는 것이다

 

만약에 테스트 데이터에 대해서도 fit_transform 을 쓰게 되면, 테스트 데이터의 단어들까지도 반영해서 벡터값을 형성하기 때문에, 훈련 데이터에서의 'a' 는 [0 1 0] 으로 벡터화를 해놨는데, 테스트 데이터에서의 'a' 는 [0 0.8 0.2] 이런식으로 벡터화가 진행되면서, 예측 모델이 두 개의 'a' 를 마치 다른 단어로 인식하게 되는 경우가 발생한다

 

 

 

#bag_of_words.py

import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer

count = CountVectorizer()
docs = np.array([
        'The sun is shining',
        'The weather is sweet',
        'The sun is shining and the weather is sweet, one and one is two'])
bag = count.fit_transform(docs)

print(count.vocabulary_)
print(bag.toarray()) # 인덱싱된 단어의 출현 횟수

tfidf = TfidfTransformer(use_idf=True, norm='l2', smooth_idf=True)
np.set_printoptions(precision=2)
print(tfidf.fit_transform(count.fit_transform(docs)).toarray())