Step 12 LSTM: GloVe로 장기기억 추가

Vanishing Gradient 문제 해결 - 긴 문맥 이해

1. RNN의 근본적인 문제

RNN은 순서를 처리하지만, 긴 문장에서는 초반 단어를 잊어버립니다. 이를 장기 의존성 문제(Long-term Dependency Problem)라고 해요.

❌ RNN의 장기 의존성 문제

장기 의존성 문제 ✅ 짧은 문장 (RNN 괜찮음) "The cat was cute" ❌ 긴 문장 (RNN 어려움) "The cat, which was sitting on the mat in the living room, and had been sleeping for hours, finally woke up" "cat"과 "woke" 사이가 너무 멀어서 관계를 잊어버림!

왜 이런 일이 일어날까요?

Vanishing Gradient (기울기 소실):
역전파 시 기울기가 계속 곱해지면서 0에 가까워짐
→ 초반 정보가 후반까지 전달되지 못함

예시: 0.5 × 0.5 × 0.5 × ... (20번) = 0.00000095 😱

2. LSTM: 장기기억의 해결사

LSTM (Long Short-Term Memory)은 RNN의 이 문제를 해결합니다. 핵심은 Cell State라는 "장기기억 고속도로"를 만드는 거예요!

🧠 LSTM의 핵심 구조

LSTM Cell 구조 Cell State (장기기억 고속도로) x_t Forget 뭘 잊을까? Input 뭘 기억할까? Output 뭘 출력할까? h_t LSTM의 3가지 게이트 1. Forget Gate: 어떤 정보를 잊을지 결정 2. Input Gate: 어떤 새 정보를 저장할지 결정 3. Output Gate: 어떤 정보를 출력할지 결정

LSTM의 핵심 아이디어:

Cell State: 장기기억을 담는 "고속도로"
  → 정보가 거의 변하지 않고 긴 거리를 이동 가능

3개의 Gate: 정보를 선택적으로 추가/제거
  → 중요한 정보는 오래 기억, 불필요한 정보는 잊기

Gradient 전달: Cell State 덕분에 기울기 소실 방지
  → 100개 이상의 시간 단계도 학습 가능!

3. GloVe: Word2Vec의 개선 버전

GloVe (Global Vectors)는 Word2Vec과 비슷하지만, 전체 말뭉치의 통계를 활용해서 더 나은 임베딩을 만듭니다.

🌍 GloVe vs Word2Vec

특징 Word2Vec GloVe
학습 방식 예측 기반 (주변 단어 예측) 통계 기반 (동시 등장 행렬)
학습 속도 느림 빠름
희귀 단어 약함 강함
성능 좋음 더 좋음
사전학습 모델 Google News (300d) Wikipedia (50d, 100d, 200d, 300d)
# GloVe 사전학습 모델 다운로드
# https://nlp.stanford.edu/projects/glove/

import numpy as np

def load_glove(filepath):
    embeddings = {}
    with open(filepath, 'r', encoding='utf-8') as f:
        for line in f:
            values = line.split()
            word = values[0]
            vector = np.asarray(values[1:], dtype='float32')
            embeddings[word] = vector
    return embeddings

# GloVe 로드 (100차원)
glove = load_glove('glove.6B.100d.txt')

print(f"단어 수: {len(glove):,}")  # 400,000개
print(glove['cat'].shape)  # (100,)

4. LSTM + GloVe 모델

이제 LSTM과 GloVe를 결합해서 장기 의존성 문제를 해결해봅시다!

🏗️ 모델 아키텍처

LSTM Text Classifier Input 토큰 인덱스 [42, 108, ...] GloVe 100차원 사전학습 🔒 Frozen LSTM 128 units 장기 기억 3개 게이트 cell + hidden FC 4 classes Softmax GloVe (freeze) → LSTM → 분류 예상 성능: 약 91-93% (RNN 대비 +1-2%p)
import torch
import torch.nn as nn

class LSTMClassifier(nn.Module):
    def __init__(self, vocab_size, embedding_dim, hidden_dim, num_classes, pretrained_embeddings=None):
        super().__init__()
        
        # GloVe 임베딩 (freeze)
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
        
        if pretrained_embeddings is not None:
            self.embedding.weight.data.copy_(pretrained_embeddings)
            self.embedding.weight.requires_grad = False  # 🔒 Freeze!
        
        # LSTM (RNN 대신!)
        self.lstm = nn.LSTM(
            input_size=embedding_dim,
            hidden_size=hidden_dim,
            batch_first=True,
            num_layers=2,  # 2층 LSTM
            dropout=0.3    # 층 사이 Dropout
        )
        
        # 분류기
        self.dropout = nn.Dropout(0.5)
        self.fc = nn.Linear(hidden_dim, num_classes)
    
    def forward(self, x):
        # x: [batch, seq_len]
        embedded = self.embedding(x)  # [batch, seq_len, embedding_dim]
        
        # LSTM
        output, (hidden, cell) = self.lstm(embedded)
        # output: [batch, seq_len, hidden_dim]
        # hidden: [num_layers, batch, hidden_dim]
        # cell: [num_layers, batch, hidden_dim] ← LSTM만 있음!
        
        # 마지막 층의 hidden state 사용
        final_hidden = hidden[-1]  # [batch, hidden_dim]
        
        return self.fc(self.dropout(final_hidden))

# 모델 생성
model = LSTMClassifier(
    vocab_size=20000,
    embedding_dim=100,  # GloVe 차원
    hidden_dim=128,
    num_classes=4,
    pretrained_embeddings=glove_weights  # GloVe 가중치
)

✅ LSTM + GloVe의 장점

🤔 여전히 개선 여지가?

LSTM으로 많이 개선했지만, 아직 더 나아질 수 있습니다:

남은 문제:
• 임베딩이 고정(freeze)되어 있음 → 우리 데이터에 최적화 안 됨
• 모든 단어가 똑같이 중요하게 취급됨
• "not", "very" 같은 중요한 수식어를 강조하지 못함

다음 Step 13에서 Attention + FastText로 중요 단어를 집중해봅시다! 🚀