Step 10 CNN: TF-IDF로 단어 중요도 반영

이미지가 아닌 텍스트에서의 CNN 활용

1. 이전 Step의 문제점

MLP + Bag of Words는 간단했지만, 모든 단어를 똑같이 취급했습니다. 하지만 실제로는 어떤 단어는 중요하고, 어떤 단어는 그렇지 않죠!

❌ Bag of Words의 문제

모든 단어가 동등하게 취급됨 "Apple announces new iPhone with revolutionary camera" BoW에서는 모든 단어가 1로 카운팅 Apple: 1 announces: 1 new: 1 iPhone: 1 with: 1 핵심 단어 별로 안 중요

"Apple", "iPhone", "camera"는 핵심 키워드인데,
"with", "new" 같은 흔한 단어와 똑같이 취급됩니다. 😞

2. TF-IDF: 단어의 중요도를 측정하자

TF-IDF (Term Frequency - Inverse Document Frequency)는 단어가 얼마나 중요한지 숫자로 나타냅니다!

📊 TF-IDF 계산 원리

TF-IDF = TF × IDF

1. TF (Term Frequency): 문서 내에서 단어가 얼마나 자주 나오나?
$$ \text{TF}(t, d) = \frac{\text{단어 } t \text{의 등장 횟수}}{\text{문서 } d \text{의 총 단어 수}} $$
2. IDF (Inverse Document Frequency): 이 단어가 여러 문서에서 흔한가?
$$ \text{IDF}(t) = \log \frac{\text{전체 문서 수}}{\text{단어 } t \text{를 포함한 문서 수}} $$
3. TF-IDF: 두 값을 곱하면 끝!
$$ \text{TF-IDF}(t, d) = \text{TF}(t, d) \times \text{IDF}(t) $$

TF-IDF 예시 문서1: "iPhone is great" 문서2: "Galaxy is great" 문서3: "Pixel is great" TF-IDF 분석 "iPhone" • TF = 1/3 (문서1에서만 1번) • IDF = log(3/1) = 1.09 (3개 중 1개 문서) → TF-IDF = 0.36 (높음! 특징적) "is" • TF = 1/3 (문서1에서 1번) • IDF = log(3/3) = 0 (모든 문서에 있음) → TF-IDF = 0 (낮음! 흔한 단어) 💡 핵심: 자주 나오지만 다른 문서엔 없는 단어 = 높은 TF-IDF
from sklearn.feature_extraction.text import TfidfVectorizer

# 문서 리스트
documents = [
    "iPhone is great",
    "Galaxy is great",
    "Pixel is great"
]

# TF-IDF 벡터화
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(documents)

# 결과 확인
print(vectorizer.get_feature_names_out())
# ['galaxy' 'great' 'iphone' 'is' 'pixel']

print(tfidf_matrix[0].toarray())  # 문서1의 TF-IDF
# [[0.    0.408 0.707 0.    0.   ]]
#   galaxy great iphone is   pixel
#          ↑     ↑ → iPhone이 가장 높음!

3. 텍스트에서 CNN 사용하기

CNN은 이미지만을 위한 게 아닙니다! 텍스트에서도 지역적 패턴(n-gram)을 찾을 수 있어요.

🔍 텍스트 CNN의 핵심 아이디어

텍스트에서 CNN 동작 원리 "The cat sat on the mat" 임베딩 매트릭스 (6 × d) The cat sat on the mat 필터 (크기 3) "The cat sat" 특징 추출 연속된 3개 단어 패턴 감지 💡 여러 크기의 필터(2, 3, 4-gram)를 사용해서 다양한 패턴 포착

텍스트 CNN의 장점:

n-gram 패턴 감지: "not good" 같은 연속된 단어 조합 인식
위치 불변성: "very good"이 문장 어디 있든 인식
병렬 처리: RNN보다 빠름

이미지 CNN과 차이점:
• 1D Conv 사용 (가로 방향만 슬라이딩)
• 필터 크기 = n-gram 크기 (보통 2, 3, 4)

4. TextCNN 아키텍처

TF-IDF + CNN을 결합한 모델을 만들어봅시다!

🏗️ 모델 구조

TextCNN 아키텍처 Input TF-IDF 20,000 차원 Reshape → [seq_len, 1] Conv1D 100 filters kernel=3 + ReLU MaxPool Global 가장 강한 특징만 FC 4 classes Softmax TF-IDF로 중요 단어 강조 → CNN으로 패턴 추출 예상 성능: 약 89-91% (MLP 대비 +3%p)
import torch
import torch.nn as nn

class TextCNN(nn.Module):
    def __init__(self, vocab_size, num_classes=4):
        super().__init__()
        
        # TF-IDF 입력을 1D Conv에 맞게 변환
        # [batch, vocab_size] → [batch, 1, vocab_size]
        
        # 여러 크기의 필터 사용 (2-gram, 3-gram, 4-gram)
        self.conv1 = nn.Conv1d(1, 100, kernel_size=3)
        self.conv2 = nn.Conv1d(1, 100, kernel_size=4)
        self.conv3 = nn.Conv1d(1, 100, kernel_size=5)
        
        self.dropout = nn.Dropout(0.5)
        self.fc = nn.Linear(300, num_classes)  # 100*3 = 300
    
    def forward(self, x):
        # x: [batch, vocab_size]
        x = x.unsqueeze(1)  # [batch, 1, vocab_size]
        
        # 3가지 크기의 필터로 특징 추출
        x1 = torch.relu(self.conv1(x))  # [batch, 100, L1]
        x2 = torch.relu(self.conv2(x))  # [batch, 100, L2]
        x3 = torch.relu(self.conv3(x))  # [batch, 100, L3]
        
        # Global Max Pooling (각 필터별 최댓값)
        x1 = torch.max(x1, dim=2)[0]  # [batch, 100]
        x2 = torch.max(x2, dim=2)[0]
        x3 = torch.max(x3, dim=2)[0]
        
        # 결합
        x = torch.cat([x1, x2, x3], dim=1)  # [batch, 300]
        x = self.dropout(x)
        x = self.fc(x)
        
        return x

# 모델 생성
model = TextCNN(vocab_size=20000, num_classes=4)
print(f"파라미터 수: {sum(p.numel() for p in model.parameters()):,}")

✅ TF-IDF + CNN의 장점

🤔 여전히 남은 문제

TF-IDF + CNN으로 개선했지만, 여전히 한계가 있습니다:

문제점:
• TF-IDF는 여전히 희소 벡터 (대부분이 0)
• 단어 간 의미 관계를 표현 못 함
• "cat"과 "dog"이 둘 다 동물인 걸 모름

다음 Step 11에서 Word2Vec + RNN으로 단어 의미를 학습해봅시다! 🚀