by museonghwang

TF-IDF 개념 및 실습

|

  1. Vectorization
  2. TF-IDF(단어 빈도-역 문서 빈도, Term Frequency-Inverse Document Frequency)
  3. TF-IDF 계산 방법
    • 3.1 TF(Term Frequency, 단어 빈도) : $tf(d,t)$
    • 3.2 DF(Document Frequency, 문서 빈도) : $df(t)$
    • 3.3 IDF(Inverse Document Frequency, 역 문서 빈도) : $idf(d, t)$
  4. TF-IDF 계산
  5. TF-IDF 구현
  6. 사이킷런을 이용한 DTM과 TF-IDF
  7. Summary
  8. Bag of Words 기반의 DTM, TF-IDF와의 딥 러닝의 관계

DTM 내에 있는 각 단어에 대한 중요도를 계산할 수 있는 TF-IDF 가중치에 대해서 알아보겠습니다. TF-IDF를 사용하면, 기존의 DTM을 사용하는 것보다 보다 많은 정보를 고려하여 문서들을 비교할 수 있습니다. TF-IDF가 DTM보다 항상 좋은 성능을 보장하는 것은 아니지만, 많은 경우에서 DTM보다 더 좋은 성능을 얻을 수 있습니다.



1. Vectorization

텍스트를 컴퓨터가 이해하고, 효율적으로 처리하게 하기 위해서는 컴퓨터가 이해할 수 있도록 텍스트를 적절히 숫자로 변환해야 하며, 이를 Vectorization 이라 지칭할 수 있습니다. 상황별 Vectorization을 정리하겠습니다.

  1. 벡터화에 신경망을 사용하지 않을 경우
    • 단어에 대한 벡터 표현 방법 : 원-핫 인코딩
    • 문서에 대한 벡터 표현 방법 : Document Term Matrix(DTM), TF-IDF
  2. 벡터화에 신경망을 사용하는 경우 (2008 ~ 2018)
    • 단어에 대한 벡터 표현 방법 : 워드 임베딩(Word2Vec, GloVe, FastText, Embedding layer)
    • 문서에 대한 벡터 표현 방법 : Doc2Vec, Sent2Vec
  3. 문맥을 고려한 벡터 표현 방법 (2018 - present)
    • ELMo, BERT
    • Pretrained Language Model의 시대


원-핫 인코딩과 DTM, TF-IDF는 Vectorization에 신경망을 사용하지 않을 경우 주로 사용합니다. 이때 원-핫 인코딩은 단어에 대한 벡터 표현 방법이며, Document Term Matrix(DTM)과 TF-IDF는 문서에 대한 벡터 표현 방법 입니다.


2. TF-IDF(단어 빈도-역 문서 빈도, Term Frequency-Inverse Document Frequency)

TF-IDF(Term Frequency-Inverse Document Frequency)단어의 빈도와 역 문서 빈도(문서의 빈도에 특정 식을 취함)를 사용 하여 Document Term Matrix(DTM) 내의 각 단어들마다 중요한 정도를 가중치로 주는 방법 입니다. 우선 DTM을 만든 후, TF-IDF 가중치를 부여합니다.

TF-IDF는 전체 문서에서 빈출되는 단어의 중요도는 낮다고 판단하고, 특정 문서에서만 빈출되는 단어는 중요도가 높다고 판단합니다. 즉, DTM 에서 추가적으로 중요한 단어에 가중치를 주는 방식을 TF-IDF 라고 하며, TF-IDF 값은 단어의 중요도와 비례 하는데 TF-IDF 기준으로 중요한 단어는 값이 Up, 중요하지 않은 값이 Down 하게 됩니다.

image


TF-IDF는 단어의 중요도를 고려하기 때문 에, 일반적으로 단순히 문서 내 단어의 출현빈도만 고려하는 DTM보다 문서에서 유의미한 정보를 얻어내는 데 더욱 효과적 입니다.

TF-IDF는 주로 문서의 유사도를 구하는 작업, 검색 시스템에서 검색 결과의 중요도를 정하는 작업, 문서 내에서 특정 단어의 중요도를 구하는 작업 등에 쓰일 수 있습니다. 또한 벡터이므로 인공 신경망의 입력으로도 사용할 수 있습니다.


3. TF-IDF 계산 방법

TF-IDF는 TF(Term Frequency, 단어 빈도)IDF(Inverse Document Frequency, 역 문서 빈도) 라는 두 값을 곱한 결과를 의미 합니다.

\[TF-IDF = TF × IDF\]


TF, DF, IDF 각각을 정의하겠습니다.

  • $d$ : 문서
  • $t$ : 단어
  • $n$ : 문서의 총 개수


또한 설명을 위해 4개의 문서가 있다고 가정하겠습니다. 문서 내 띄어쓰기를 기준으로 토큰화(tokenization)한다고 가정하겠습니다.

  1. 문서 1 : 먹고 싶은 사과
  2. 문서 2 : 먹고 싶은 바나나
  3. 문서 3 : 길고 노란 바나나 바나나
  4. 문서 4 : 저는 과일이 좋아요


3.1 TF(Term Frequency, 단어 빈도) : $tf(d,t)$

TF(Term Frequency, 단어 빈도)특정 문서 $d$ 에서의 특정 단어 $t$ 의 등장 횟수를 의미 하며, 사실 TF는 Document Term Matrix(DTM)의 각 단어들이 가진 값들 입니다. DTM이 각 문서에서의 각 단어의 등장 빈도를 나타내는 값이었기 때문입니다.

- 과일이 길고 노란 먹고 바나나 사과 싶은 저는 좋아요
문서1 0 0 0 1 0 1 1 0 0
문서2 0 0 0 1 1 0 1 0 0
문서3 0 1 1 0 2 0 0 0 0
문서4 1 0 0 0 0 0 0 1 1


image


Document Term Matrix(DTM)은 Bag of Words 가설 기반으로, Bag of Words를 직역하면 단어들의 가방을 의미하며, 가방에 문장의 단어들을 넣고 흔든다면, 단어의 순서는 무의미해집니다. 즉 단어의 순서는 무시하고, 오직 단어의 빈도수에만 집중하는 방법을 의미합니다.

image


3.2 DF(Document Frequency, 문서 빈도) : $df(t)$

DF(Document Frequency, 문서 빈도)특정 단어 $t$ 가 등장한 문서의 수를 의미 합니다. 여기서 특정 단어가 각 문서, 또는 문서들에서 몇 번 등장했는지는 관심가지지 않으며, 오직 특정 단어 $t$ 가 몇 개의 문서에 출현했는지 개수에만 관심 을 가집니다.

예를 들어 위 DTM에서 바나나의 df값은 2입니다. 왜냐하면 위 DTM에서의 문서2와 문서3에서 등장했습니다. 문서3에서 바나나가 두 번 등장했지만, 그것은 중요한 게 아닙니다. 심지어 바나나란 단어가 문서2에서 100번 등장했고, 문서3에서 200번 등장했다고 하더라도 바나나의 df는 2가 됩니다.

단어 DF(문서 빈도)
과일이 1
길고 1
노란 1
먹고 2
바나나 2
사과 1
싶은 2
저는 1
좋아요 1


3.3 IDF(Inverse Document Frequency, 역 문서 빈도) : $idf(d, t)$

IDF(Inverse Document Frequency, 역 문서 빈도)DF 값의 역수 입니다.

\[idf(d,t) = log\left(\cfrac{n}{1 + df(t)}\right)\]


IDF는 DF의 역수를 취하는것이 맞습니다. 하지만 단순히 역수를 취하지 않고 $log$ 와 분모에 1을 더해줍니다. 만약 $log$ 를 사용하지 않았을 때, IDF를 그냥 DF의 역수로 사용한다면 총 문서의 수 $n$ 이 커질 수록, IDF의 값은 기하급수적으로 커지게 됩니다. 그렇기 때문에 $log$ 를 사용합니다. 로그 내 분모에 1을 더해주는 이유는 특정 단어가 전체 문서에서 출현하지 않는 경우, 분모가 0이 되는 상황을 방지하기 위함입니다.

왜 log가 필요한지 $n=1,000,000$ 일 때의 예를 들어봅시다. $log$ 의 밑은 10을 사용한다고 가정하였을 때 결과는 아래와 같습니다.

image


즉, 로그를 사용하지 않으며 idf의 값은 기하급수적으로 커질 수 있습니다. IDF에 로그를 씌우는 이유 를 정리하겠습니다.

  • TF-IDF는 모든 문서에서 자주 등장하는 단어는 중요도가 낮다고 판단 하며, 특정 문서에서만 자주 등장하는 단어는 중요도가 높다고 판단 합니다.
  • 즉, TF-IDF 값이 낮으면 중요도가 낮은 것이며, TF-IDF 값이 크면 중요도가 큰 것입니다.
  • 불용어 등과 같이 자주 쓰이는 단어들은 비교적 자주 쓰이지 않는 단어들 보다 최소 수십 배 자주 등장합니다.
  • 비교적 자주 쓰이지 않는 단어들조차 희귀 단어들과 비교하면 또 최소 수백 배는 더 자주 등장하는 편입니다.
  • log를 씌워주지 않으면, 희귀 단어들에 엄청난 가중치가 부여될 수 있으므로, 로그를 씌우면 이런 격차를 줄이는 효과가 있습니다.


4. TF-IDF 계산

이제, 앞서 DTM을 설명하기위해 들었던 위의 예제를 가지고 TF-IDF(단어 빈도-역 문서 빈도, Term Frequency-Inverse Document Frequency) 에 대해 이해해보겠습니다. 설명을 위해 4개의 문서를 다시 가져오겠습니다. 이때 문서 내 띄어쓰기를 기준으로 토큰화(tokenization)한다고 가정하겠습니다.

  1. 문서 1 : 먹고 싶은 사과
  2. 문서 2 : 먹고 싶은 바나나
  3. 문서 3 : 길고 노란 바나나 바나나
  4. 문서 4 : 저는 과일이 좋아요


위 문서에 대한 TF(Term Frequency, 단어 빈도) 는 특정 문서 $d$ 에서의 특정 단어 $t$ 의 등장 횟수를 의미하며, TF를 구하면 다음과 같습니다. TF는 DTM의 각 단어들이 가진 값들 입니다.

- 과일이 길고 노란 먹고 바나나 사과 싶은 저는 좋아요
문서1 0 0 0 1 0 1 1 0 0
문서2 0 0 0 1 1 0 1 0 0
문서3 0 1 1 0 2 0 0 0 0
문서4 1 0 0 0 0 0 0 1 1


우선 TF는 앞서 사용한 DTM을 그대로 사용하면, 그것이 각 문서에서의 각 단어의 TF가 됩니다. 이제 IDF를 위한 DF를 구해보겠습니다. DF(Document Frequency, 문서 빈도) 는 특정 단어 $t$ 가 등장한 문서의 수를 의미하며, 오직 특정 단어 $t$ 가 몇 개의 문서에 출현했는지 개수에만 관심을 가집니다.

단어 DF(문서 빈도)
과일이 1
길고 1
노란 1
먹고 2
바나나 2
사과 1
싶은 2
저는 1
좋아요 1


이제 구해야할 것은 TF와 곱해야할 값인 IDF(Inverse Document Frequency, 역 문서 빈도) 로, DF 값의 역수 입니다. 로그는 자연 로그를 사용하도록 하겠습니다.

단어 IDF(역 문서 빈도)
과일이 ln(4/(1+1)) = 0.693
길고 ln(4/(1+1)) = 0.693
노란 ln(4/(1+1)) = 0.693
먹고 ln(4/(1+2)) = 0.287
바나나 ln(4/(1+2)) = 0.287
사과 ln(4/(1+1)) = 0.693
싶은 ln(4/(1+2)) = 0.287
저는 ln(4/(1+1)) = 0.693
좋아요 ln(4/(1+1)) = 0.693


문서의 총 수는 4이기 때문에 ln 안에서 분자는 4로 동일합니다. 분모의 경우에는 각 단어가 등장한 문서의 수(DF)를 의미하는데, 예를 들어서 ‘먹고’의 경우에는 총 2개의 문서(문서1, 문서2)에 등장했기 때문에 2라는 값을 가집니다. 각 단어에 대해서 IDF의 값을 비교해보면 문서 1개에만 등장한 단어와 문서 2개에만 등장한 단어는 값의 차이를 보입니다. IDF 값을 보면 2회 등장한 단어들이 값이 더 낮습니다. IDF는 여러 문서에서 등장한 단어의 가중치를 낮추는 역할을 하기 때문 입니다.

TF-IDF를 계산해보겠습니다. 각 단어의 TF는 DTM에서의 각 단어의 값과 같으므로, 앞서 사용한 DTM에서 단어 별로 위의 IDF값을 곱해주면 TF-IDF 값을 얻습니다.

image


- 과일이 길고 노란 먹고 바나나 사과 싶은 저는 좋아요
문서1 0 0 0 0.287 0 0.693 0.287 0 0
문서2 0 0 0 0.287 0.287 0 0.287 0 0
문서3 0 0.693 0.693 0 0.575 0 0 0 0
문서4 0.693 0 0 0 0 0 0 0.693 0.693


같은 단어라도 TF-IDF값은 다른데, 이는 해당 문서의 TF값에 영향을 받기 때문입니다. 문서2에서의 바나나의 TF-IDF 가중치와 문서3에서의 바나나의 TF-IDF 가중치가 다른 것을 볼 수 있습니다. 수식적으로 말하면, TF가 각각 1과 2로 달랐기 때문인데 TF-IDF에서의 관점에서 보자면 TF-IDF는 특정 문서에서 자주 등장하는 단어는 그 문서 내에서 중요한 단어로 판단하기 때문입니다. 문서2에서는 바나나를 한 번 언급했지만, 문서3에서는 바나나를 두 번 언급했기 때문에 문서3에서의 바나나를 더욱 중요한 단어라고 판단하는 것입니다.

즉, TF-IDF는 모든 문서에서 자주 등장하는 단어는 중요도가 낮다고 판단하며, 특정 문서에서만 자주 등장하는 단어는 중요도가 높다고 판단합니다.


5. TF-IDF 구현

위의 계산 과정을 파이썬으로 직접 구현해보겠습니다. 앞의 설명에서 사용한 4개의 문서를 docs에 저장합니다.

from math import log
import pandas as pd
 
docs = [
    '먹고 싶은 사과',
    '먹고 싶은 바나나',
    '길고 노란 바나나 바나나',
    '저는 과일이 좋아요'
] 
 
vocab = list(set(w for doc in docs for w in doc.split()))
vocab.sort()
print('단어장의 크기 :', len(vocab))
print(vocab)
[output]
단어장의 크기 : 9
['과일이', '길고', '노란', '먹고', '바나나', '사과', '싶은', '저는', '좋아요']


TF, IDF, 그리고 TF-IDF 값을 구하는 함수를 구현합니다.

# 총 문서의 수
N = len(docs) 
 
def tf(t, d):
    return d.count(t)
 
def idf(t):
    df = 0
    for doc in docs:
        df += t in doc
    return log(N/(df+1))
 
def tfidf(t, d):
    return tf(t,d)* idf(t)


TF를 구해보겠습니다. 다시 말해 DTM을 데이터프레임에 저장하여 출력해보겠습니다.

result = []

# 각 문서에 대해서 아래 연산을 반복
for i in range(N):
    result.append([])
    d = docs[i]
    for j in range(len(vocab)):
        t = vocab[j]
        result[-1].append(tf(t, d))

tf_ = pd.DataFrame(result, columns = vocab)

image


정상적으로 DTM이 출력되었습니다. 각 단어에 대한 IDF 값을 구해보겠습니다.

result = []
for j in range(len(vocab)):
    t = vocab[j]
    result.append(idf(t))

idf_ = pd.DataFrame(result, index=vocab, columns=["IDF"])

image


위에서 수기로 구한 IDF 값들과 정확히 일치합니다. TF-IDF 행렬을 출력해보겠습니다.

result = []
for i in range(N):
    result.append([])
    d = docs[i]
    for j in range(len(vocab)):
        t = vocab[j]
        result[-1].append(tfidf(t,d))

tfidf_ = pd.DataFrame(result, columns = vocab)

image


TF-IDF의 가장 기본적인 식에 대해서 학습하고 구현하였습니다. 사실 실제 TF-IDF 구현시 위에서 배운 식과는 다른 조정된 식을 사용하는데, 그 이유는 위의 기본적인 식을 바탕으로 한 구현에는 몇 가지 문제점이 존재하기 때문입니다. 만약 전체 문서의 수 $n$ 이 4인데, $df(f)$ 의 값이 3인 경우에는 $df(f)$ 에 1이 더해지면서 $log$ 항의 분자와 분모의 값이 같아지게 됩니다. 이는 $log$ 의 진수값이 1이 되면서 $idf(d,t)$ 의 값이 0이 됨을 의미합니다. 식으로 표현하면 $idf(d,t)=log(n/(df(t)+1))=0$ 입니다.

IDF의 값이 0이라면 더 이상 가중치의 역할을 수행하지 못합니다. 아래 사이킷런의 TF-IDF 구현체 또한 위의 식에서 조정된 식을 사용하고 있습니다.


6. 사이킷런을 이용한 DTM과 TF-IDF

사이킷런을 통해 DTM과 TF-IDF를 만들어보겠습니다. CountVectorizer 를 사용하면 DTM을 만들 수 있습니다.

from sklearn.feature_extraction.text import CountVectorizer

corpus = [
    'you know I want your love',
    'I like you',
    'what should I do ',    
]

vector = CountVectorizer()

# 코퍼스로부터 각 단어의 빈도수를 기록
print(vector.fit_transform(corpus).toarray())

# 각 단어와 맵핑된 인덱스 출력
print(vector.vocabulary_)
[output]
[[0 1 0 1 0 1 0 1 1]
 [0 0 1 0 0 0 0 1 0]
 [1 0 0 0 1 0 1 0 0]]
{'you': 7, 'know': 1, 'want': 5, 'your': 8, 'love': 3, 'like': 2, 'what': 6, 'should': 4, 'do': 0}


사이킷런은 TF-IDF를 자동 계산해주는 TfidfVectorizer 를 제공합니다. 사이킷런의 TF-IDF는 위에서 배웠던 보편적인 TF-IDF 기본 식에서 조정된 식을 사용합니다. 요약하자면, IDF의 로그항의 분자에 1을 더해주며, 로그항에 1을 더해주고, TF-IDF에 L2 정규화라는 방법으로 값을 조정하는 등의 차이로 TF-IDF가 가진 의도는 여전히 그대로 갖고 있습니다.

from sklearn.feature_extraction.text import TfidfVectorizer

corpus = [
    'you know I want your love',
    'I like you',
    'what should I do ',    
]

tfidfv = TfidfVectorizer().fit(corpus)
print(tfidfv.transform(corpus).toarray())
print(tfidfv.vocabulary_)
[output]
[[0.         0.46735098 0.         0.46735098 0.         0.46735098 0.         0.35543247 0.46735098]
 [0.         0.         0.79596054 0.         0.         0.         0.         0.60534851 0.        ]
 [0.57735027 0.         0.         0.         0.57735027 0.         0.57735027 0.         0.        ]]
{'you': 7, 'know': 1, 'want': 5, 'your': 8, 'love': 3, 'like': 2, 'what': 6, 'should': 4, 'do': 0}


7. Summary

TF-IDF(Term Frequency-Inverse Document Frequency) 는 Document Term Matrix(DTM) 내의 각 단어들마다 중요한 정도를 가중치로 주는 방법으로, DTM 에서 추가적으로 중요한 단어에 가중치를 주는 방식을 TF-IDF 라고 하며, TF-IDF는 전체 문서에서 빈출되는 단어의 중요도는 낮다고 판단하고, 특정 문서에서만 빈출되는 단어는 중요도가 높다고 판단합니다. 그러므로 TF-IDF 값은 단어의 중요도와 비례 하는데 TF-IDF 기준으로 중요한 단어는 값이 Up, 중요하지 않은 값이 Down 하게 됩니다.

TF-IDF는 여전히 현업에서도 굉장히 많이 쓰이는 벡터화 방법입니다. 문서를 벡터화한다면 각 문서 간의 유사도 를 구할 수 있는데, 문서 간 유사도를 구할 수 있다면 다음과 같은 Task들을 수행할 수 있습니다.

  1. 문서 클러스터링
  2. 유사한 문서 찾기
  3. 문서 분류 문제


또한 Vectorization 방법 중 벡터화에 신경망을 사용하지 않을 경우를 정리할 수 있습니다.

  • 벡터화에 신경망을 사용하지 않을 경우
    • 단어에 대한 벡터 표현 방법 : 원-핫 인코딩 Vs 워드 임베딩
    • 문서에 대한 벡터 표현 방법 : Document Term Matrix(DTM)
    • 문서에 대한 벡터 표현 방법에 가중치를 넣는 방법 : TF-IDF


문서가 있을 때, 이를 DTM으로 표현한다면 문서 하나가 벡터가 됩니다. 만약 문서가 있을 때, 문서 내의 모든 단어를 워드 임베딩 또는 원-핫 인코딩 으로 표현한다면 단어 하나는 벡터가 되고, 문서 하나는 행렬이 됩니다.

또한 인공 신경망으로 단어 임베딩과 유사하게 문서 임베딩 벡터를 얻는 방법도 존재합니다.

  • Ex) Doc2Vec, Sent2Vec, Universal Sentence Encoder, ELMo, BERT


8. Bag of Words 기반의 DTM, TF-IDF와의 딥 러닝의 관계

  • DTM과 TF-IDF를 이용한 NLP
    • DTM과 TF-IDF는 사실 일반적으로 (딥 러닝이 아닌) 머신 러닝 자연어 처리와 사용합니다.
    • 인공 신경망(딥 러닝)의 입력으로 사용하는 경우는 흔한 경우는 아닙니다.
    • 딥 러닝에서는 입력으로 워드 임베딩이라는 보다 강력한 방법이 이미 존재하기 때문입니다.
    • 그럼에도 TF-IDF는 검색 시스템, 추천 알고리즘 등으로 여전히 수요가 많습니다.