개요
자연어 처리에서 단어를 컴퓨터가 이해할 수 있는 수치 벡터로 변환하는 기술입니다. 단어의 의미와 관계를 고차원 벡터 공간에서 표현하여, 비슷한 의미의 단어들이 벡터 공간에서도 가까운 위치에 배치되도록 합니다.
기존의 한계
컴퓨터의 한계
- 컴퓨터는 텍스트를 직접 이해할 수 없음
- 숫자로만 연산이 가능
- 단어 간의 의미적 관계를 파악하기 어려움
기존 방법의 문제점
원-핫 인코딩(One-hot encoding) 방식:
문제점: - 벡터 크기가 전체 어휘 수만큼 커짐 - 단어 간 유사성을 표현할 수 없음 - 희소 벡터(sparse vector) 문제
핵심 개념
분산 표현(Distributed Representation)
"사과" = [0.2, -0.4, 0.7, 0.1, -0.3]
"바나나" = [0.3, -0.3, 0.8, 0.2, -0.2]
"자동차" = [-0.1, 0.6, -0.2, 0.9, 0.4]
특징: - 고정된 차원의 밀집 벡터(dense vector) - 각 차원이 단어의 특정 의미적 특성을 표현 - 유사한 단어들은 유사한 벡터값을 가짐
임베딩 방법
1. Word2Vec
Google에서 개발한 대표적인 임베딩 기법
CBOW (Continuous Bag of Words)
- 주변 단어들로 중심 단어를 예측
- 예: "나는 [?] 먹었다" → "사과"
Skip-gram
- 중심 단어로 주변 단어들을 예측
- 예: "사과" → ["나는", "를", "먹었다"]
# Word2Vec 사용 예시
from gensim.models import Word2Vec
sentences = [["나는", "사과를", "먹었다"],
["그는", "바나나를", "좋아한다"]]
model = Word2Vec(sentences, vector_size=100, window=5, min_count=1)
vector = model.wv['사과를'] # 사과의 벡터 표현
2. GloVe (Global Vectors)
Stanford에서 개발한 방법 특징: - 전역 통계 정보 활용 - 단어 동시 출현 행렬 기반 - Word2Vec보다 안정적인 성능
3. FastText
Facebook에서 개발 특징: - 서브워드(subword) 정보 활용 - 미등록 단어(OOV) 처리 가능 - 형태소가 풍부한 언어에 효과적
# FastText 예시
from gensim.models import FastText
model = FastText(sentences, vector_size=100, window=5, min_count=1)
# "사과들"이 학습 데이터에 없어도 "사과"와 유사한 벡터 생성 가능
임베딩의 수학적 성질
벡터 연산을 통한 의미 관계 표현
코사인 유사도
두 벡터 간의 유사도 측정:
import numpy as np
def cosine_similarity(vec1, vec2):
return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
# 사과와 바나나의 유사도
similarity = cosine_similarity(apple_vector, banana_vector)
실제 응용 사례
1. 검색 엔진
# 쿼리와 문서의 의미적 유사도 계산
query_vector = get_embedding("맛있는 과일")
doc_vectors = [get_embedding(doc) for doc in documents]
similarities = [cosine_similarity(query_vector, doc_vec) for doc_vec in doc_vectors]
2. 추천 시스템
# 사용자가 좋아한 상품과 유사한 상품 추천
user_items = ["사과", "바나나", "오렌지"]
user_vector = np.mean([get_embedding(item) for item in user_items], axis=0)
recommendations = find_similar_items(user_vector, all_items)
3. 감정 분석
# 문장의 감정을 벡터로 표현
sentence = "이 영화는 정말 재미있었다"
sentence_vector = np.mean([get_embedding(word) for word in sentence.split()], axis=0)
sentiment = classify_sentiment(sentence_vector)
최신 발전: 문맥적 임베딩
기존 임베딩의 한계
- 하나의 단어에 하나의 고정된 벡터
- 문맥에 따른 의미 변화 반영 불가 예: "은행"
- "강가의 은행에서 낚시를 했다"
- "은행에서 돈을 인출했다"
해결책: BERT, GPT 등
- 문맥에 따라 동적으로 임베딩 생성
- Transformer 구조 기반
- 양방향 문맥 정보 활용
# BERT 기반 문맥적 임베딩
from transformers import BertTokenizer, BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
model = BertModel.from_pretrained('bert-base-multilingual-cased')
# 같은 단어 "은행"이지만 문맥에 따라 다른 벡터 생성
sentence1 = "강가의 은행에서 낚시를 했다"
sentence2 = "은행에서 돈을 인출했다"
임베딩 품질 평가
1. 내재적 평가
- 단어 유사도 테스트
- 유추 문제 (King - Man + Woman = Queen)
- 클러스터링 품질
2. 외재적 평가
- 하위 작업(downstream task)에서의 성능
- 분류, 번역, 질의응답 등에서의 개선 효과
실무에서의 고려사항
1. 차원 수 선택
- 너무 낮으면: 의미 정보 손실
- 너무 높으면: 계산 비용 증가, 과적합
- 일반적으로 100-300 차원 사용
2. 학습 데이터
- 도메인 특화 데이터 사용 권장
- 데이터 크기와 품질이 성능에 직접적 영향