자연어 처리(Natural Language Processing, NLP)는 사용자가 의도한 바를 이해하고, 문맥에 맞는 응답을 생성하며, 여러 언어적 요소를 분석하는 방법입니다. 이 과정에서 중요한 기술 중 하나가 임베딩(Embedding)입니다. 임베딩은 단어를 벡터 공간에 매핑하여 의미적 관계를 더 수치적으로 표현할 수 있도록 도와줍니다. 오늘은 파이토치를 사용하여 자연어 처리를 위한 단어 임베딩을 구현해 보겠습니다.
1. 임베딩이란?
임베딩은 보통 고차원 데이터를 저차원으로 변환하는 방법으로, 특히 텍스트와 같이 비정형 데이터를 다룰 때 중요합니다. 예를 들어, ‘사과’, ‘바나나’, ‘오렌지’라는 세 단어는 각각의 의미가 서로 다르지만, 이 단어를 벡터로 변환할 경우 비슷한 거리로 표현할 수 있습니다. 이는 딥러닝 모델이 의미를 이해하는 데 도움이 됩니다.
2. 임베딩의 종류
- 원-핫 인코딩(One-hot Encoding)
- 워드 투 벡터(Word2Vec)
- 글로베(GloVe)
- 임베딩 레이어(Embeddings Layer)
2.1 원-핫 인코딩
원-핫 인코딩은 각 단어를 고유한 벡터로 변환합니다. 예를 들어, ‘사과’, ‘바나나’, ‘오렌지’라는 단어는 각각 [1, 0, 0], [0, 1, 0], [0, 0, 1]로 표현할 수 있습니다. 하지만 이 방법은 단어 간의 유사성을 고려하지 않습니다.
2.2 워드 투 벡터(Word2Vec)
워드 투 벡터는 단어의 맥락을 고려하여 밀집 벡터를 생성합니다. 이 방법은 ‘Skip-gram’과 ‘Continuous Bag of Words’ (CBOW) 방식으로 구현할 수 있습니다. 각 단어는 주변 단어들을 통해 학습되며, 의미론적 거리도 유지합니다.
2.3 글로베(GloVe)
GloVe는 단어의 동시 발생 행렬을 분해하여 의미적 유사성을 학습하는 방법입니다. 문맥 정보를 기반으로 전 세계 단어의 통계를 통해 임베딩을 변화시킵니다.
2.4 임베딩 레이어(Embeddings Layer)
딥러닝 프레임워크에서 제공하는 임베딩 레이어를 사용하면 직접적으로 단어를 저차원 벡터로 변환할 수 있습니다. 실시간으로 데이터를 학습하면서 의미가 잘 반영된 벡터를 생성합니다.
3. 파이토치를 활용한 임베딩
이제 파이토치(PyTorch)를 사용하여 임베딩을 실제로 구현해 보겠습니다. 먼저, 필요한 라이브러리를 가져오겠습니다.
python
import torch
import torch.nn as nn
import torch.optim as optim
from torchtext.datasets import PennTreebank
from torchtext.data import Field, TabularDataset, BucketIterator
import numpy as np
import random
import spacy
nlp = spacy.load('en_core_web_sm')
3.1 데이터 준비
Penn Treebank 데이터셋을 사용하여 간단한 예제를 만들어 보겠습니다. 이 데이터셋은 자연어 처리에서 널리 사용됩니다.
python
TEXT = Field(tokenize='spacy', lower=True)
train_data, valid_data, test_data = PennTreebank.splits(TEXT)
TEXT.build_vocab(train_data, max_size=10000, min_freq=2)
vocab_size = len(TEXT.vocab)
3.2 임베딩 모델 정의
임베딩 레이어를 포함한 간단한 신경망 모델을 만들어 보겠습니다.
python
class EmbeddingModel(nn.Module):
def __init__(self, vocab_size, embedding_dim):
super(EmbeddingModel, self).__init__()
self.embedding = nn.Embedding(vocab_size, embedding_dim)
self.fc = nn.Linear(embedding_dim, vocab_size)
def forward(self, x):
embedded = self.embedding(x)
return self.fc(embedded)
3.3 모델 훈련
이제 모델을 훈련시켜 보겠습니다. 손실 함수와 옵티마이저를 정의하고 훈련 루프를 작성합니다.
python
def train(model, iterator, optimizer, criterion):
model.train()
epoch_loss = 0
for batch in iterator:
optimizer.zero_grad()
output = model(batch.text)
loss = criterion(output.view(-1, vocab_size), batch.target.view(-1))
loss.backward()
optimizer.step()
epoch_loss += loss.item()
return epoch_loss / len(iterator)
embedding_dim = 100
model = EmbeddingModel(vocab_size, embedding_dim)
optimizer = optim.Adam(model.parameters())
criterion = nn.CrossEntropyLoss()
# Iterators
train_iterator, valid_iterator, test_iterator = BucketIterator.splits(
(train_data, valid_data, test_data),
batch_size=64,
device=device
)
# Training
for epoch in range(10):
train_loss = train(model, train_iterator, optimizer, criterion)
print(f'Epoch {epoch + 1}, Train Loss: {train_loss:.3f}')
4. 단어 임베딩 시각화
임베딩이 잘 학습되었는지 확인하기 위해, 후처리 과정을 통해 특정 단어들의 임베딩 벡터를 시각화해 보겠습니다.
python
def visualize_embeddings(model, word):
embedding_matrix = model.embedding.weight.data.numpy()
word_index = TEXT.vocab.stoi[word]
word_embedding = embedding_matrix[word_index]
# 유사한 단어 탐색
similarities = np.dot(embedding_matrix, word_embedding)
similar_indices = np.argsort(similarities)[-10:]
similar_words = [TEXT.vocab.itos[idx] for idx in similar_indices]
return similar_words
print(visualize_embeddings(model, 'apple'))
5. 결론
오늘은 딥러닝과 파이토치를 사용하여 자연어 처리를 위한 임베딩에 대해 알아보았습니다. 우리는 기본적인 임베딩 개념부터 데이터셋 준비, 모델 정의, 훈련, 시각화까지의 전 과정을 살펴보았습니다. 임베딩은 NLP의 중요한 기초기술이며, 다양한 문제를 해결하는 데 유용하게 사용될 수 있습니다. 실무에 적용하기 위해 여러 가지 기법을 연구하는 것이 좋습니다.
6. 참고 자료
- https://pytorch.org/docs/stable/index.html
- https://spacy.io/usage/linguistic-features#vectors-similarity
- https://www.aclweb.org/anthology/D15-1170.pdf