TF-IDF + CNN은 단어 중요도를 반영했지만, 여전히 단어의 의미는 표현하지 못했습니다. "cat"과 "dog"이 둘 다 동물이라는 걸 모르죠. 그리고 단어 순서도 제대로 활용하지 못했어요.
필요한 것:
1. 단어의 의미를 표현하는 밀집 벡터
2. 단어 순서를 고려하는 모델
Word2Vec은 단어를 밀집 벡터(Dense Vector)로 변환합니다. 비슷한 의미의 단어는 비슷한 벡터로 표현되죠!
"비슷한 문맥에 나타나는 단어는 비슷한 의미를 가진다"
- John Rupert Firth
예시:
• "The cat is sleeping on the sofa"
• "The dog is sleeping on the sofa"
• "The hamster is sleeping on the sofa"
→ cat, dog, hamster는 비슷한 위치에 나타남
→ Word2Vec은 이들을 비슷한 벡터로 표현!
Word2Vec을 직접 학습하려면 엄청난 데이터가 필요합니다. 대신 Google이 학습한 모델을 가져와서 쓸 수 있어요!
# Gensim으로 Word2Vec 불러오기 from gensim.models import KeyedVectors # Google News 사전학습 모델 (300차원) # 다운로드: https://code.google.com/archive/p/word2vec/ wv = KeyedVectors.load_word2vec_format( 'GoogleNews-vectors-negative300.bin', binary=True ) # 단어 벡터 확인 print(wv['cat'].shape) # (300,) # 유사 단어 찾기 print(wv.most_similar('cat', topk=5)) # [('cats', 0.76), ('dog', 0.76), ('kitten', 0.71), ...] # 단어 연산! print(wv.most_similar(positive=['king', 'woman'], negative=['man'])) # [('queen', 0.71), ...] # king - man + woman = queen!
이제 단어의 의미를 표현할 수 있게 됐습니다! 다음은 순서를 처리할 차례예요. RNN (Recurrent Neural Network)이 바로 그 주인공입니다.
RNN의 수식:
$$ h_t = \tanh(W_{hh} h_{t-1} + W_{xh} x_t + b_h) $$
$$ y_t = W_{hy} h_t + b_y $$
• $h_t$: 현재 시점의 hidden state
• $x_t$: 현재 시점의 입력 (단어 임베딩)
• $h_{t-1}$: 이전 시점의 hidden state (이전 단어들의 정보!)
이제 Word2Vec 임베딩과 RNN을 결합해봅시다!
import torch import torch.nn as nn class RNNClassifier(nn.Module): def __init__(self, vocab_size, embedding_dim, hidden_dim, num_classes, pretrained_embeddings=None): super().__init__() # Word2Vec 임베딩 (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! # RNN self.rnn = nn.RNN( input_size=embedding_dim, hidden_size=hidden_dim, batch_first=True # [batch, seq, feature] ) # 분류기 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] # RNN output, hidden = self.rnn(embedded) # output: [batch, seq_len, hidden_dim] # hidden: [1, batch, hidden_dim] # 마지막 hidden state 사용 final_hidden = hidden.squeeze(0) # [batch, hidden_dim] return self.fc(final_hidden) # 모델 생성 model = RNNClassifier( vocab_size=20000, embedding_dim=300, # Word2Vec 차원 hidden_dim=128, num_classes=4, pretrained_embeddings=word2vec_weights # Word2Vec 가중치 )
RNN은 순서를 처리하지만, 장기 의존성(Long-term Dependency) 문제가 있습니다:
문제:
"The cat, which was sitting on the mat, was cute"
→ "cat"과 "was"가 멀리 떨어져 있으면 관계를 잊어버림 😢
다음 Step 12에서 LSTM + GloVe로 장기기억을 추가해봅시다! 🚀