지난번 포스팅에서는 임베딩 모델 설명에 들어가기 앞서 토크나이저에 대해 알아봤는데요.
아직 못보신 분들은 미리 보시는 것을 추천드립니다.
자연어처리 기초 토크나이저의 모든것! 종류부터 최신 트렌드까지!
지난 포스팅에서는 자연어 처리가 무엇이고, 어떤 방법들이 있는지 정말 간단하게 알아봤습니다.혹시 아직 자연어처리에 대한 개념이 잘 안잡혔다고 느껴지신다면,아래의 포스팅을 먼저 보고
jaeyoon-95.tistory.com
이번 포스팅부터는 임베딩 방법론에 대해 하나씩 살펴볼텐데요.
오늘은 그 첫 번째로 TF-IDF에 대해 살펴볼까 합니다.
임베딩은 어떤 방법이 있는지 궁금하시다면 아래 포스팅도 참고해주세요.
자연어 처리 임베딩의 등장 배경과 종류
어느 순간부터 딥러닝이 점점 핫해지기 시작했는데요.OpenAI의 ChatGPT가 공개되면서 LLM에 대한 관심이 폭주하기 시작했어요.이번 포스팅 시리즈는 초기 LM부터 LLM까지 자연어 처리 모델이 어떻게
jaeyoon-95.tistory.com
그럼 본격적으로 TF-IDF에 대해서 알아보도록 하겠습니다.

TF-IDF란?
TF-IDF(Term Frequency-Inverse Document Frequency)는 문서에서 단어의 중요도를 계산하는 방법인데요. 간단하게 말씀드리자면, 각 단어가 문서 내에서 얼마나 중요한지를 측정하는 데 사용됩니다. TF는 단어 빈도인 Term Frequency와 IDF Inverse Document Frequency의 약자입니다. 살짝 어렵게 느껴지실 수도 있다고 생각되는데요. 조금만 더 쉽게 풀어서 말씀드려볼게요.
예를들어 '아기 돼지 삼형제'라는 동화책을 읽는다고 상상해 볼게요. 이 동화책에는 어떤 단어가 가장 많이 나올까요?
'돼지', '집', '늑대' 등이 가장 많이 나올것이라는 생각이 듭니다. 그럼 자연스럽게 '돼지'라는 단어가 많이 나오기 때문에, 중요하다고 생각할 수 있을텐데요. 생각해보면 '돼지'는 다른 동화책에서도 정말 많이 사용이 되죠. 그렇다면, 이 '돼지'는 특별한 단어라고 할 수 있을까요? '특별하다' 보다는 '흔하다'에 더 가깝게 되죠. 그럼 '아기 돼지 삼형제'라는 동화책 내에서 등장하는 다른 단어인 '벽돌집'은 비교적 다른 동화책에 덜 나오기 때문에 이 책 내에서는 더 특별한 단어라고 볼 수 있습니다.
이것이 TF-IDF의 기본 개념인데요. 문서에서 중요한(특별한) 단어를 찾기 위해 만들어진 방법입니다.
TF
첫 번째로 TF에 대해 먼저 설명드릴게요. TF는 간단하게 '단어가 얼마나 자주 나오니?'를 측정한다고 생각해주세요.
'돼지'가 책에서 10번 나왔고, '벽돌집'이 5번 나왔다고 할게요. '돼지'는 얼핏보면 문서 내에서 더 중요하게 보일 수 있죠.
하지만 앞서 말씀드린 것 처럼 '돼지'는 다른 동화책에서도 흔히 등장하는 단어입니다.
따라서 더 중요하고, 특별한 것이 아니다고 여겨집니다.
그럼 TF는 어떻게 계산하는지 한번 알아볼게요.

단어가 그 문서에 얼마나 많이 나왔는지를 나타내는 값이라고 볼수 있습니다. 전체 문서 단어중 해당 단어가 차지하는 비율과 같으니까요!
따라서 숫자가 클수록 더 많이 나온 단어라는 것을 알 수 있습니다.
예를 들어 '돼지'는 10번, '늑대'는 6번, '벽돌집'은 5번, 전체 단어개수가 100개라고 해볼게요.
'벽돌집'의 TF를 구해보겠습니다.

'벽돌집'은 전체 문서의 5%만큼 등장한 단어입니다.
IDF
IDF는 '얼마나 희귀한 단어인가?'에 대해 계산하는 방법입니다.
이 방법은 빠르게 수식 먼저 보고 설명드릴게요.

어떤 단어가 많은 문서에 등장하면 흔한 단어라 중요도가 낮아지는데요.
반대로 어떤 단어가 특정 문서에서만 나오면 희귀하고 중요할 가능성이 높습니다.
따라서 우리는 자주 등장하는 단어의 중요도를 먼저 낮춰줘야합니다. 동시에 희귀한 단어의 중요도는 높여야합니다.
위의 수식을 살펴보면 단어가 등장한 문서의 개수가 많을수록 분모가 커질텐데요. 따라서 자연스레 그 중요도는 낮아지게 됩니다.
참고로 분모의 '단어 t를 포함한 문서의 개수'는 '문서 빈도수'이며 DF(Document Frequency)라고도 합니다.
따라서 아래와 같이 수식을 수정할수도 있습니다.

예를 들어서 아래 단어들의 IDF값을 계산해볼게요. 전체 문서의 개수는 100개라고 가정할게요!
| 단어 | 등장한 문서 수DF(t) | 전체 문서의 개수/DF(t) |
| 돼지 | 90 | 90/100 = 1.11 |
| 늑대 | 50 | 100/50 = 2 |
| 벽돌집 | 10 | 100/10 = 10 |
많이 등장할수록 더 수치가 적은 것을 확인할 수 있습니다. 따라서 IDF를 정리하자면 희귀한 단어를 찾아내는 방법입니다.
그런데 수식에서 우리는 log를 빼고 계산했었죠. 이건 왜 씌우는 걸까요? 생각보다 아주 간단한 이유인데요.
아주 극단적으로 100개 문서중 1개의 문서에만 등장하는 단어, 그리고 100개 문서에 모두 등장하는 단어를 생각해볼게요.
전자는 100이 될테고, 후자는 1이 되겠죠. 값 차이가 너무 커지게 됩니다.
따라서 이러한 값이 너무 커지거나 작아지지 않게 log함수를 씌우게 됩니다.
TF-IDF
TF, IDF를 각각 알아봤으니 TF-IDF가 최종적으로 가지는 의미를 알아볼까요?

앞에서 계산했던, TF와 IDF를 곱하면 TF-IDF값을 얻을 수 있습니다.
TF는 단어가 얼마나 나오는지 알 수 있고, IDF는 단어가 얼마나 희귀한지 알 수 있습니다. 그럼 이 두 값을 곱한 TF-IDF는요?
바로 해석해 보자면 't 문서에서 많이 나오면서, 전체 문서에서 희소하게 나오는 단어'를 알 수 있습니다. 즉, t문서 내에서만 중요한 단어를 알 수 있는 것입니다.
예시를 한번 살펴볼게요. 전체 문서 개수는 100개, 해당 문서 내 단어 개수도 100개라고 가정해볼게요.
각각 문장, 문서에 등장하는 빈도수는 돼지-10/90, 늑대-6/50, 벽돌집 5/10입니다.
| 단어 | TF | IDF | TF-IDF |
| 돼지 | 10/100=0.10 | log(100/90)=0.05 | 0.10x0.05=0.005 |
| 늑대 | 6/100=0.06 | log(100/50)=0.3 | 0.06x0.3=0.018 |
| 벽돌집 | 5/100=0.05 | log(100/10)=1.0 | 0.05x1.0=0.05 |
위의 결과로 봐서 이 문서는 '벽돌집'이 가장 중요한 단어라고 할 수 있습니다.
구현
그럼 이제 코드로 간단하게 구현해볼게요.
라이브러리 미사용
import math
# 예시 문서
documents = [
"고양이는 귀엽다",
"강아지는 충성스럽다",
"고양이와 강아지는 친하다"
]
# 단어 목록 생성
def build_vocab(documents):
vocab = set()
for doc in documents:
vocab.update(doc.split())
return vocab
# TF 계산
def compute_tf(doc):
tf_dict = {}
words = doc.split()
total_words = len(words)
for word in words:
tf_dict[word] = tf_dict.get(word, 0) + 1 / total_words
return tf_dict
# IDF 계산
def compute_idf(documents, vocab):
idf_dict = {}
N = len(documents)
for word in vocab:
count = sum(1 for doc in documents if word in doc.split())
idf_dict[word] = math.log(N / (1 + count)) # +1 to avoid division by zero
return idf_dict
# TF-IDF 계산
def compute_tfidf(documents):
vocab = build_vocab(documents)
idf_dict = compute_idf(documents, vocab)
tfidf_docs = []
for doc in documents:
tf_dict = compute_tf(doc)
tfidf_doc = {word: tf_dict[word] * idf_dict.get(word, 0) for word in tf_dict}
tfidf_docs.append(tfidf_doc)
return tfidf_docs, vocab
# TF-IDF 계산 결과 출력
tfidf_docs, vocab = compute_tfidf(documents)
print("단어 목록:", vocab)
print("TF-IDF 결과:")
for idx, doc_tfidf in enumerate(tfidf_docs):
print(f"문서 {idx + 1}: {doc_tfidf}")
라이브러리 사용
라이브러리를 사용하면 엄청 간단하게 이용할 수 있습니다.
from sklearn.feature_extraction.text import TfidfVectorizer
# 예시 문서
documents = [
"고양이는 귀엽다",
"강아지는 충성스럽다",
"고양이와 강아지는 친하다"
]
# TfidfVectorizer 객체 생성
vectorizer = TfidfVectorizer()
# 문서에 대한 TF-IDF 계산
tfidf_matrix = vectorizer.fit_transform(documents)
# 결과 출력
print("단어 목록:", vectorizer.get_feature_names_out())
print("TF-IDF 행렬:\n", tfidf_matrix.toarray())
장단점
| 장점 | 단점 |
| 💡 간단하고 직관적 - 계산이 쉽고 직관적이어서 구현하기 쉬움 - 문서의 특성을 반영한 단어 중요도 측정 가능 💡 중요 단어를 효과적으로 추출 💡 불필요 단어 필터링 가능 - 문서에서 흔한 단어를 제외할 수 있음(그리고, 하지만 등) 💡 검색 엔진과 추천 시스템에서 유용 - 가장 관련있는 문서를 찾는 데 사용할 수 있음 - ex) 강아지 사료 추천 검색시 TF-IDF를 활용하여 관련 키워드가 가장 많이 포함된 문서를 상위에 노출시킬 수 있음 |
💡문맥을 반영하지 못함 - 개별 단어 빈도만 고려하고, 문맥이나 단어 순서 반영이 어려움 💡희소 행렬 문제 - 문서가 많아질수록 각 문서마다 등장하는 단어수가 적어지고, 대부분 0이 되는 희소 행렬이 생김 - ex) 1만 개의 문서에서 한 단어가 3개의 문서에만 등장하면, 대부분 값이 0인 행렬이 만들어져 메모리가 낭비할 수 있음 💡단어 길이나 변형을 고려하지 않음 - 먹다, 먹었다 등의 같은 의미의 단어가 다르게 취급됨 💡단어의 상대적인 중요도를 반영하지 못할 수 있음 - 매우 중요한 단어더라도 자주 등장하면 중요도가 낮아짐 |
때문에 TF-IDF는 문서에서 중요한 단어를 뽑고싶거나, 검색, 문서의 요약, 키워드 추출이 필요한 경우에 사용하시는 것을 추천드립니다.
사용하는 곳
| 사용처 | 설명 |
| 검색 엔진 | TF-IDF를 이용하면 해당 키워드가 가장 중요하게 여겨지는 문서 검색을 할 수 있습니다. 위의 장점에도 적어두었지만 '강아지 사료 추천'검색시 관련 키워드가 가장 중요하게 여겨진 문서가 상위 노출되게 됩니다. |
| 문서 분류 | 스팸 메일 분류와 같은 문서 분류에서도 높은 성능을 보이는데요. 스팸 문서에 자주 등장하는 단어가 특정 문서에서 중요도 높게 여겨진다면, 해당 메일은 스팸으로 분류할 수 있습니다. |
| 텍스트 요약 | 뉴스에서 나온 핵심 단어를 추출하는 데 도움이 됩니다. |
| 감성 분석 | 긍정적인 단어와 부정적인 단어가 중요하게 여겨진 문서를 찾아서, 긍정인지 부정인지 분류할 수 있습니다. |
| 키워드 추출 | 텍스트 요약과 비슷하게 핵심 키워드를 찾아낼 수 있습니다. |
이번 포스팅에서는 통계적 임베딩 방법인 TF-IDF에 대해 아주 자세하게 알아봤습니다.
다음 포스팅에서는 또 다른 통계적 임베딩 방법인 LSA에 대해 알아볼게요!
질문이나 의견 있으시면 언제든지 댓글 남겨주세요!

'인공지능공부 > 자연어처리' 카테고리의 다른 글
| LSA를 활용한 문서 의미 분석과 통계적 임베딩 (0) | 2025.03.19 |
|---|---|
| 자연어처리 기초 토크나이저의 모든것! 종류부터 최신 트렌드까지! (0) | 2025.03.08 |
| 자연어 처리 임베딩의 등장 배경과 종류 (0) | 2025.03.06 |
| Self-Attentive Sentence Embedding(SASE) 구현해보기 (0) | 2023.03.22 |
| Self-Attentive Sentence Embedding(SASE)의 모든 것 (1) | 2023.03.21 |