DTM (Document-Term Matrix)이란?
DTM은 여러 문서에서 단어의 빈도를 기록한 행렬(matrix)입니다. BoW와 유사하지만, 여러 문서 간의 단어 빈도수를 한꺼번에 분석할 수 있도록 확장된 형태입니다.
- 행(Row): 문서 (Document)
- 열(Column): 단어 (Term)
- 값(Value): 특정 문서에 특정 단어가 등장한 빈도수
DTM은 m x n 형태의 행렬로, `m`은 문서의 개수, `n`은 고유 단어의 개수를 의미합니다.
DTM의 예시
다음과 같은 예제 문서들을 통해 DTM을 만들어 보겠습니다.
문서 1: "나는 오늘 밥을 먹었다"
문서 2: "밥을 먹고 운동을 했다"
문서 3: "오늘 운동을 마치고 밥을 먹었다"
1. 토큰화(Tokenization) 및 어휘 사전 생성
모든 문서에서 고유한 단어들을 추출하여 어휘 사전을 생성합니다.
Vocabulary: ['나는', '오늘', '밥을', '먹었다', '먹고', '운동을', '했다', '마치고']
2. DTM 생성
각 문서에서 단어 빈도를 어휘 사전 순서에 따라 벡터로 변환합니다.
나는 | 오늘 | 밥을 | 먹었다 | 먹고 | 운동을 | 했다 | 마치고 | |
문서 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
문서 2 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 |
문서 3 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 |
3. Python을 사용한 DTM 구현
`CountVectorizer`를 사용하면 Python에서 쉽게 DTM을 생성할 수 있습니다.
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
# 예제 문서 리스트
corpus = [
"나는 오늘 밥을 먹었다",
"밥을 먹고 운동을 했다",
"오늘 운동을 마치고 밥을 먹었다"
]
# CountVectorizer를 사용해 DTM 생성
vectorizer = CountVectorizer()
dtm = vectorizer.fit_transform(corpus)
# DTM을 Pandas DataFrame으로 변환
dtm_df = pd.DataFrame(dtm.toarray(), columns=vectorizer.get_feature_names_out())
print(dtm_df)
위에서 작성한 결과와 동일한 결과가 출력되었죠.
DTM의 장단점
장점
- 문서를 수치화하여 머신러닝 모델에 쉽게 활용할 수 있습니다.
- 단순한 구현으로 빠르게 분석을 시작할 수 있습니다.
단점
- 단어의 순서 정보가 손실됩니다.
- 문서가 많아질수록 희소 행렬(Sparse Matrix) 문제가 발생합니다.
- 어휘 사전이 클수록 메모리 사용량이 크게 증가합니다.
DTM의 확장: TF-IDF (Term Frequency-Inverse Document Frequency)
BoW와 DTM의 단점을 보완하기 위해 TF-IDF (Term Frequency-Inverse Document Frequency)가 자주 사용됩니다. TF-IDF는 단어 빈도뿐만 아니라 문서 내에서의 중요도를 반영하여 가중치를 조정합니다.
TF-IDF란?
TF-IDF는 단어 빈도(Term Frequency, TF)와 역문서 빈도(Inverse Document Frequency, IDF)를 결합하여 각 단어의 중요도를 측정하는 방법입니다.
BoW와 DTM이 단순히 단어의 빈도만을 고려하는 반면, TF-IDF는 문서 내에서의 빈도뿐만 아니라 해당 단어가 전체 문서에서 얼마나 자주 등장하는지를 고려하여 단어의 가중치를 계산합니다.
TF-IDF는자주 등장하는 단어(예: "the", "is")는 여러 문서에 걸쳐 자주 사용되므로 중요도가 낮다고 판단하고, 특정 문서에서 드물게 등장하는 단어는 해당 문서를 특징짓는 중요한 단어로 간주합니다.
TF-IDF의 수식
TF-IDF는 두 가지 지표를 결합하여 계산합니다.
1️⃣ Term Frequency (TF)
TF(Term Frequency)는 특정 단어가 한 문서에서 얼마나 자주 등장하는지를 측정하기 위해 모든 단어에 대해 각 문서에서의 빈도를 계산합니다.
\[
\text{TF}(t, d) = \frac {\text {단어 } t \text {의 빈도}}{\text {문서 } d \text {의 전체 단어 수}}
\]
다음과 같은 예제 문서들을 통해 TF를 계산해보겠습니다.
문서 1: "나는 오늘 밥을 먹었다"
문서 2: "밥을 먹고 운동을 했다"
문서 3: "오늘 운동을 마치고 밥을 먹었다"
우선, 문서에서 고유한 단어들을 추출하여 어휘 사전을 만듭니다.
Vocabulary: ['나는', '오늘', '밥을', '먹었다', '먹고', '운동을', '했다', '마치고']
TF 값 계산
각 문서에서 각 단어의 빈도를 계산한 후, 문서 내 총 단어 수로 나눠서 TF 값을 구합니다.
문서별 TF 계산
- 문서 1: "나는 오늘 밥을 먹었다" (단어 수: 4개)
- 문서 2: "밥을 먹고 운동을 했다" (단어 수: 4개)
- 문서 3: "오늘 운동을 마치고 밥을 먹었다" (단어 수: 5개)
단어 | 문서 1 (TF) | 문서 2 (TF) | 문서 3 (TF) |
나는 | 1/4 = 0.25 | 0/4 = 0 | 0/5 = 0 |
오늘 | 1/4 = 0.25 | 0/4 = 0 | 1/5 = 0.2 |
밥을 | 1/4 = 0.25 | 1/4 = 0.25 | 1/5 = 0.2 |
먹었다 | 1/4 = 0.25 | 0/4 = 0 | 1/5 = 0.2 |
먹고 | 0/4 = 0 | 1/4 = 0.25 | 0/5 = 0 |
운동을 | 0/4 = 0 | 1/4 = 0.25 | 1/5 = 0.2 |
했다 | 0/4 = 0 | 1/4 = 0.25 | 0/5 = 0 |
마치고 | 0/4 = 0 | 0/4 = 0 | 1/5 = 0.2 |
결과에서 보다시피 각 단어는 3개의 TF 값을 가집니다. (각 문서에서의 TF 값).
예를 들어, "밥을"이라는 단어의 경우
- 문서 1에서의 TF 값: 0.25
- 문서 2에서의 TF 값: 0.2
- 문서 3에서의 TF 값: 약 0.17
따라서, 모든 단어가 각 문서별로 TF 값을 갖게 되어, 어휘 사전에 포함된 모든 단어에 대해 3개의 TF 값이 생성됩니다.
2️⃣ Inverse Document Frequency (IDF)
IDF는 특정 단어가 전체 문서에서 얼마나 드물게 등장하는지를 측정합니다.
단어가 많은 문서에서 등장할수록 IDF 값이 낮아지고, 반대로 특정 문서에서만 등장하는 단어는 IDF 값이 높아집니다.
이렇게 함으로써, 모든 문서에 자주 등장하는 불필요한 단어들은 중요도가 낮아지고, 특정 문서에서만 등장하는 특별한 단어들은 중요도가 높아집니다.
\[
\text {IDF}(t) = \log\left(\frac {\text {총 문서 수}}{1 + \text {단어 } t \text {가 포함된 문서 수}}\right)
\]
- 총 문서 수: 데이터에 포함된 전체 문서의 개수입니다.
- 단어 t가 포함된 문서 수: 단어 t가 등장한 문서의 개수입니다.
- 로그를 사용하는 이유: 값이 너무 커지지 않도록 조절하기 위해서입니다.
- 1을 더하는 이유: 단어가 포함된 문서 수가 0이 되는 경우를 방지하기 위해서입니다.
IDF 값 계산
다음과 같은 예제 문서들을 통해 IDF를 계산해 보겠습니다.
문서 1: "나는 오늘 밥을 먹었다"
문서 2: "밥을 먹고 운동을 했다"
문서 3: "오늘 운동을 마치고 밥을 먹었다"
우선, 문서에서 고유한 단어들을 추출하여 어휘 사전을 만듭니다.
Vocabulary: ['나는', '오늘', '밥을', '먹었다', '먹고', '운동을', '했다', '마치고']
단어 | 포함된 문서 수 | IDF 계산 | IDF 값 |
나는 | 1 | log(3 / (1 + 1)) = log(3 / 2) | 약 0.1761 |
오늘 | 2 | log(3 / (1 + 2)) = log(3 / 3) | 0 |
밥을 | 3 | log(3 / (1 + 3)) = log(3 / 4) | 약 -0.1249 |
먹었다 | 2 | log(3 / (1 + 2)) = log(3 / 3) | 0 |
먹고 | 1 | log(3 / (1 + 1)) = log(3 / 2) | 약 0.1761 |
운동을 | 2 | log(3 / (1 + 2)) = log(3 / 3) | 0 |
했다 | 1 | log(3 / (1 + 1)) = log(3 / 2) | 약 0.1761 |
마치고 | 1 | log(3 / (1 + 1)) = log(3 / 2) | 약 0.1761 |
위 결과를 보면,
단어 "오늘"은 문서 1과 문서 3에서 2번 등장합니다.
- \(\text {IDF}(\text {오늘}) = \log\left(\frac {3}{1 + 2}\right) = \log(1) = 0\)
- 단어 "나는"은 문서 1에서만 등장합니다.
- \(\text {IDF}(\text {나는}) = \log\left(\frac {3}{1 + 1}\right) = \log(1.5) \approx 0.176\)
- 단어 "밥을"은 모든 문서에서 등장합니다.
- \(\text {IDF}(\text {밥을}) = \log\left(\frac {3}{1 + 3}\right) = \log(0.75) \approx -0.125\)
- "오늘": 여러 문서에 등장하므로 IDF 값이 낮습니다.(0에 가까움).
- "나는": 특정 문서에만 등장하므로 IDF 값이 높습니다.(약 0.176).
- "밥을": 모든 문서에 등장하므로 IDF 값이 매우 낮습니다.(심지어 음수).
이렇게 IDF를 통해 자주 등장하는 단어에는 낮은 가중치, 드물게 등장하는 단어에는 높은 가중치를 부여함으로써, 문서의 특징을 더 잘 반영할 수 있습니다.
결과를 보셨다시피, IDF는 특정 단어가 역수(Inverse)를 사용합니다.
다시 말해, 문서에서 자주 등장하는 단어일수록 중요도가 낮고, 반대로 드물게 등장하는 단어일수록 중요도가 높습니다.
왜 많이 등장하는 단어는 낮은 가중치를 받나요?
많이 등장하는 단어들(예: "the", "is", "and")은 대부분의 문서에서 자주 나타나므로 문서의 내용을 구별하는 데 별로 도움이 되지 않습니다. 반면, 특정 문서에만 등장하는 고유한 단어는 해당 문서를 특징짓는 중요한 단어가 되기 때문입니다.
즉, 많이 등장하는 단어는 대부분의 문서에서 흔히 나타나는 단어일 가능성이 높아서 중요도가 낮습니다. → 낮은 IDF 값
적게 등장하는 단어는 특정 문서에만 등장할 가능성이 높아서 그 문서를 설명하는 중요한 단어일 수 있습니다. → 높은 IDF 값
TF-IDF란?
TF-IDF(Term Frequency-Inverse Document Frequency)는 단어의 빈도(TF)와 역면서 빈도(IDF)를 곱하여 각 단어의 가중치를 계산하는 방식입니다. 이를 통해, 단어가 문서에서 자주 등장하지만 모든 문서에 공통적으로 나타나는 단어는 낮은 가중치를 받고, 특정 문서에서만 자주 등장하는 단어는 높은 가중치를 받습니다.
TF와 IDF를 결합한 TF-IDF 공식은 다음과 같습니다.
\[
\text {TF-IDF}(t, d) = \text {TF}(t, d) \times \text {IDF}(t)
\]
- \( t \): 특정 단어(term)
- \( d \): 특정 문서(document)
- TF: 특정 단어가 문서 내에서 얼마나 자주 등장하는지 나타냄
- IDF: 특정 단어가 전체 문서에서 얼마나 드물게 등장하는지 나타냄
TF-IDF 계산
앞서 사용한 예제 문서들을 다시 사용해 TF-IDF를 직접 계산해 보겠습니다.
단어 | 문서 1(TF) | 문서 2(TF) | 문서 3(TF) | IDF | 문서 1 (TF-IDF) |
문서 2 (TF-IDF) |
문서 2 (TF-IDF) |
나는 | 0.25 | 0 | 0 | 0.1761 | 0.044 | 0 | 0 |
오늘 | 0.25 | 0 | 0.2 | 0 | 0 | 0 | 0 |
밥을 | 0.25 | 0.25 | 0.2 | -0.1249 | -0.0312 | -0.0312 | -0.025 |
먹었다 | 0.25 | 0.2 | 0 | 0 | 0 | 0 | |
먹고 | 0 | 0.25 | 0 | 0.1761 | 0 | 0.0440 | 0 |
운동을 | 0 | 0.25 | 0.2 | 0 | 0 | 0 | 0 |
했다 | 0 | 0.25 | 0 | 0.1761 | 0 | 0.0440 | 0 |
마치고 | 0 | 0 | 0.2 | 0.1761 | 0 | 0 | 0.0352 |
- "나는"이라는 단어는 문서 1에서만 등장했기 때문에, 문서1에서 높은 TF-IDF 값을 가집니다.
- "밥을"은 모든 문서에서 등장하기 때문에, TF는 높지만 IDF가 낮아 TF-IDF 값이 작아집니다.
- "마치고"는 문서 3에서만 등장하기 때문에 문서3에서 높은 TF-IDF 값을 가집니다.
Python을 사용한 TF-IDF 계산
Python의 `TfidfVectorizer`를 사용해 직접 TF-IDF를 계산할 수도 있습니다.
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd
# 예제 문서 리스트
corpus = [
"나는 오늘 밥을 먹었다",
"밥을 먹고 운동을 했다",
"오늘 운동을 마치고 밥을 먹었다"
]
# TfidfVectorizer를 사용해 TF-IDF 행렬 생성
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(corpus)
# TF-IDF 행렬을 Pandas DataFrame으로 변환
tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=vectorizer.get_feature_names_out())
print(tfidf_df)
TF-IDF의 활용
- 문서 분류: 중요한 단어를 찾아 문서의 주제를 분류하는 데 활용합니다.
- 문서 유사도 분석: TF-IDF 벡터를 이용해 코사인 유사도(Cosine Similarity)를 계산하여 문서 간 유사도를 평가합니다.
- 검색 엔진 최적화: 사용자의 검색어와 문서 간 관련성을 평가하여 검색 결과의 순위를 매기는 데 활용됩니다.
TF-IDF의 장단점
장점
- 단어의 빈도뿐만 아니라 중요도를 고려하므로 BoW보다 효율적입니다.
- 중요한 단어에 높은 가중치를 부여해 문서의 특성을 잘 파악할 수 있습니다.
단점
- 문서가 많아질수록 희소 행렬(Sparse Matrix) 문제가 여전히 존재합니다.
- 문서 내 단어의 순서 정보가 손실됩니다.
- 단어의 문맥을 이해하지 못해 의미가 유사한 단어를 구분하지 못합니다.
하지만,
TF-IDF는 단어의 빈도와 가중치를 기반으로 하여 문맥을 반영하지 않습니다.
이를 보완하기 위해 Word2Vec, GloVe, BERT와 같은 임베딩(embedding) 기법들이 사용됩니다.
이러한 기법들은 단어 간의 문맥적 관계를 반영하여 더 나은 성능을 제공합니다.