콘텐츠로 이동

개요

자연어 처리에서 단어를 컴퓨터가 이해할 수 있는 수치 벡터로 변환하는 기술입니다. 단어의 의미와 관계를 고차원 벡터 공간에서 표현하여, 비슷한 의미의 단어들이 벡터 공간에서도 가까운 위치에 배치되도록 합니다.

기존의 한계

컴퓨터의 한계

  • 컴퓨터는 텍스트를 직접 이해할 수 없음
  • 숫자로만 연산이 가능
  • 단어 간의 의미적 관계를 파악하기 어려움

기존 방법의 문제점

원-핫 인코딩(One-hot encoding) 방식:

"사과" = [1, 0, 0, 0, 0]
"바나나" = [0, 1, 0, 0, 0]
"자동차" = [0, 0, 1, 0, 0]

문제점: - 벡터 크기가 전체 어휘 수만큼 커짐 - 단어 간 유사성을 표현할 수 없음 - 희소 벡터(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)
# "사과들"이 학습 데이터에 없어도 "사과"와 유사한 벡터 생성 가능

임베딩의 수학적 성질

벡터 연산을 통한 의미 관계 표현

King - Man + Woman ≈ Queen
한국 - 서울 + 도쿄 ≈ 일본

코사인 유사도

두 벡터 간의 유사도 측정:

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. 학습 데이터

  • 도메인 특화 데이터 사용 권장
  • 데이터 크기와 품질이 성능에 직접적 영향

3. 전처리

# 한국어 전처리 예시
import re
from konlpy.tag import Okt

def preprocess_korean(text):
    # 특수문자 제거
    text = re.sub(r'[^가-힣\s]', '', text)

    # 형태소 분석
    okt = Okt()
    tokens = okt.morphs(text)

    return tokens