벡터 임베딩 완벽 가이드: AI가 의미를 이해하는 방법


벡터 임베딩이란?

벡터 임베딩(Vector Embedding)은 단어, 문장, 문서와 같은 텍스트를 고차원 공간의 숫자 벡터로 변환하는 기술입니다. 이는 컴퓨터가 텍스트의 의미를 이해하고 비교할 수 있게 해주는 핵심 기술입니다.

간단히 말해, “강아지”라는 단어를 [0.2, 0.8, 0.3, ...] 같은 숫자 배열로 변환하는 것입니다. 이때 의미가 비슷한 단어들은 벡터 공간에서 가까이 위치하게 됩니다.

왜 벡터로 변환하나요?

컴퓨터는 숫자만 이해합니다. 텍스트를 직접 비교하거나 계산할 수 없습니다. 하지만 벡터로 변환하면:

# 텍스트로는 불가능
"강아지""개"가 얼마나 유사한가? → 계산 불가

# 벡터로는 가능
[0.2, 0.8, 0.3] 과 [0.25, 0.75, 0.35]의 거리 = 0.12 → 매우 유사!

임베딩의 진화 과정

1세대: One-Hot Encoding (2000년대 초반)

각 단어를 고유한 위치에 1을 표시하는 방식입니다.

어휘: ["강아지", "고양이", "새"]

"강아지" = [1, 0, 0]
"고양이" = [0, 1, 0]
"새" = [0, 0, 1]

문제점:
- 모든 단어가 동일한 거리 (의미 관계 X)
- 어휘 크기만큼 차원 증가
- 10,000 단어 = 10,000 차원 벡터

2세대: Word2Vec (2013)

단어의 의미를 학습하는 첫 번째 혁신입니다.

# Skip-gram 방식
"강아지가 공을 물다"

중심 단어: "공"
주변 단어: "강아지", "물다"

목표: "공"으로부터 주변 단어를 예측
→ 비슷한 맥락의 단어 = 비슷한 벡터

결과:
"강아지""개""반려견"  (벡터 공간에서 가까움)
"공""공놀이""장난감"

Word2Vec의 유명한 성질: 단어 연산

king - man + woman ≈ queen
paris - france + korea ≈ seoul

# 실제 벡터 연산
vector("king") - vector("man") + vector("woman")
= vector("queen")

3세대: BERT & Transformers (2018~)

문맥을 고려한 동적 임베딩입니다.

# Word2Vec: 같은 단어 = 항상 같은 벡터
"은행에서 돈을 찾았다""은행" = [0.1, 0.2, ...]
"강 은행에 앉아있다""은행" = [0.1, 0.2, ...]  # 동일!

# BERT: 문맥에 따라 다른 벡터
"은행에서 돈을 찾았다""은행" = [0.1, 0.2, ...]  # bank
"강 은행에 앉아있다""은행" = [0.5, 0.1, ...]  # river bank

4세대: 현대 임베딩 (2023~)

OpenAI, Cohere 등의 대규모 임베딩 모델입니다.

특징:
- 문장/문서 수준 임베딩
- 다국어 지원
- 작업 특화 (검색, 분류, 클러스터링)
- 고차원 (1536, 3072 차원)

OpenAI text-embedding-3-large:
"이 제품은 정말 훌륭합니다"
→ [0.01, -0.23, 0.45, ..., 0.12]  # 3072개 숫자

임베딩의 핵심 개념

1. 차원(Dimension)

벡터의 길이, 즉 숫자의 개수입니다.

# 2차원 임베딩 (시각화 가능)
"강아지" = [0.8, 0.2]
"고양이" = [0.7, 0.3]

# 실제 임베딩 (고차원)
"강아지" = [0.1, 0.2, 0.3, ..., 0.15]  # 1536 차원

차원이 높을수록:

  • ✅ 더 정확한 의미 표현
  • ✅ 미묘한 차이 구분 가능
  • ❌ 저장 공간 증가
  • ❌ 계산 비용 증가

2. 유사도 측정

두 벡터가 얼마나 비슷한지 계산하는 방법입니다.

코사인 유사도 (가장 일반적)

from numpy import dot
from numpy.linalg import norm

def cosine_similarity(a, b):
    return dot(a, b) / (norm(a) * norm(b))

# 예시
vec_dog = [0.8, 0.2, 0.1]
vec_puppy = [0.75, 0.25, 0.15]
vec_car = [0.1, 0.1, 0.9]

similarity(vec_dog, vec_puppy)  # 0.98 - 매우 유사
similarity(vec_dog, vec_car)    # 0.23 - 전혀 다름

유클리디안 거리

import numpy as np

def euclidean_distance(a, b):
    return np.linalg.norm(a - b)

# 거리가 가까울수록 유사
distance(vec_dog, vec_puppy)  # 0.15 - 가까움
distance(vec_dog, vec_car)    # 1.25 - 멀리 떨어짐

내적(Dot Product)

def dot_product(a, b):
    return np.dot(a, b)

# 값이 클수록 유사
dot_product(vec_dog, vec_puppy)  # 0.65 - 유사
dot_product(vec_dog, vec_car)    # 0.15 - 다름

3. 밀집 vs 희소 벡터

밀집 벡터 (Dense Vector)

# 대부분의 값이 0이 아님
[0.23, 0.45, 0.12, 0.67, 0.34, 0.89]

특징:
- 현대 임베딩의 기본
- 의미 정보가 압축됨
- 차원 수 적음 (512-3072)

희소 벡터 (Sparse Vector)

# 대부분의 값이 0
[0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 1, 0, ...]

특징:
- One-hot, TF-IDF
- 차원 수 많음 (10,000+)
- 해석 가능

주요 임베딩 모델 비교

OpenAI Embeddings

from openai import OpenAI

client = OpenAI()

response = client.embeddings.create(
    model="text-embedding-3-small",
    input="벡터 임베딩을 배우는 중입니다"
)

vector = response.data[0].embedding  # 1536 차원
모델차원성능비용 (1M 토큰)용도
text-embedding-3-small1536⭐⭐⭐$0.02일반 용도
text-embedding-3-large3072⭐⭐⭐⭐⭐$0.13고성능 필요시
ada-002 (구버전)1536⭐⭐⭐$0.10레거시

Cohere Embeddings

import cohere

co = cohere.Client('your-api-key')

response = co.embed(
    texts=["텍스트 1", "텍스트 2"],
    model='embed-multilingual-v3.0',
    input_type='search_document'
)

vectors = response.embeddings
모델차원특징비용
embed-english-v3.01024영어 특화$0.10/1M
embed-multilingual-v3.01024100개 언어$0.10/1M

특징: input_type 파라미터로 용도 최적화

  • search_document: 문서 저장용
  • search_query: 검색 쿼리용
  • classification: 분류 작업용

Sentence-BERT (오픈소스)

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2')

sentences = ["강아지가 공을 물었다", "개가 장난감을 가지고 논다"]
embeddings = model.encode(sentences)

# 유사도 계산
from sentence_transformers import util

similarity = util.cos_sim(embeddings[0], embeddings[1])
print(similarity)  # 0.85 - 높은 유사도

장점:

  • ✅ 무료
  • ✅ 로컬 실행 가능
  • ✅ 다양한 사전학습 모델

단점:

  • ❌ 상용 모델보다 성능 낮음
  • ❌ GPU 필요 (빠른 처리 위해)

실전 활용 사례

1. 의미 기반 검색

# 전통적 키워드 검색
query = "강아지 사료"
# → "강아지", "사료" 단어가 포함된 문서만 검색

# 임베딩 기반 의미 검색
query_vec = get_embedding("강아지 사료")
# → "반려견 먹이", "애견 식품", "puppy food" 모두 검색 가능

구현 예제:

from openai import OpenAI
import numpy as np

client = OpenAI()

# 문서 임베딩
documents = [
    "강아지 사료 판매합니다",
    "반려견 건강식품 추천",
    "고양이 간식 저렴하게",
    "애견 훈련 프로그램"
]

doc_embeddings = []
for doc in documents:
    emb = client.embeddings.create(
        model="text-embedding-3-small",
        input=doc
    ).data[0].embedding
    doc_embeddings.append(emb)

# 검색 쿼리
query = "개 먹이 구매"
query_emb = client.embeddings.create(
    model="text-embedding-3-small",
    input=query
).data[0].embedding

# 유사도 계산
from scipy.spatial.distance import cosine

similarities = []
for i, doc_emb in enumerate(doc_embeddings):
    sim = 1 - cosine(query_emb, doc_emb)
    similarities.append((documents[i], sim))

# 정렬 및 출력
similarities.sort(key=lambda x: x[1], reverse=True)

for doc, score in similarities:
    print(f"{score:.3f} - {doc}")

# 출력:
# 0.912 - 강아지 사료 판매합니다
# 0.867 - 반려견 건강식품 추천
# 0.543 - 애견 훈련 프로그램
# 0.321 - 고양이 간식 저렴하게

2. 텍스트 분류

# 카테고리 정의
categories = {
    "기술": "프로그래밍, 소프트웨어, AI, 데이터",
    "스포츠": "축구, 야구, 농구, 올림픽",
    "음식": "요리, 레시피, 레스토랑, 맛집"
}

# 카테고리 임베딩
category_embeddings = {}
for cat, desc in categories.items():
    emb = get_embedding(desc)
    category_embeddings[cat] = emb

# 새 문서 분류
new_doc = "파이썬으로 웹 스크래핑하는 방법"
doc_emb = get_embedding(new_doc)

# 가장 가까운 카테고리 찾기
best_category = None
best_score = -1

for cat, cat_emb in category_embeddings.items():
    score = cosine_similarity(doc_emb, cat_emb)
    if score > best_score:
        best_score = score
        best_category = cat

print(f"{new_doc}{best_category} ({best_score:.2f})")
# "파이썬으로 웹 스크래핑하는 방법 → 기술 (0.89)"

3. 추천 시스템

# 사용자가 좋아한 영화
liked_movies = [
    "인터스텔라 - SF 우주 탐험",
    "마션 - 화성 생존기"
]

# 영화 DB
all_movies = [
    "그래비티 - 우주 재난",
    "어벤져스 - 슈퍼히어로 액션",
    "컨택트 - 외계 생명체 접촉",
    "타이타닉 - 로맨스 드라마"
]

# 임베딩
liked_embeddings = [get_embedding(m) for m in liked_movies]
movie_embeddings = [get_embedding(m) for m in all_movies]

# 사용자 프로필 (좋아한 영화의 평균)
user_profile = np.mean(liked_embeddings, axis=0)

# 추천 점수 계산
recommendations = []
for movie, emb in zip(all_movies, movie_embeddings):
    score = cosine_similarity(user_profile, emb)
    recommendations.append((movie, score))

recommendations.sort(key=lambda x: x[1], reverse=True)

print("추천 영화:")
for movie, score in recommendations[:3]:
    print(f"{score:.3f} - {movie}")

# 출력:
# 0.921 - 그래비티 - 우주 재난
# 0.843 - 컨택트 - 외계 생명체 접촉
# 0.512 - 어벤져스 - 슈퍼히어로 액션

4. 중복 제거

# 비슷한 문서 찾기
documents = [
    "AI가 세상을 바꾼다",
    "인공지능이 세계를 변화시킨다",
    "강아지를 키우는 방법",
    "AI 기술의 혁신적 발전"
]

embeddings = [get_embedding(doc) for doc in documents]

# 유사도 행렬 계산
threshold = 0.9  # 90% 이상 유사하면 중복

duplicates = []
for i in range(len(documents)):
    for j in range(i+1, len(documents)):
        sim = cosine_similarity(embeddings[i], embeddings[j])
        if sim > threshold:
            duplicates.append((i, j, sim))

for i, j, sim in duplicates:
    print(f"중복 발견 ({sim:.2f}):")
    print(f"  - {documents[i]}")
    print(f"  - {documents[j]}")

# 출력:
# 중복 발견 (0.96):
#   - AI가 세상을 바꾼다
#   - 인공지능이 세계를 변화시킨다

임베딩 최적화 기법

1. 차원 축소

고차원 벡터를 저차원으로 변환하여 저장 공간과 계산 속도를 개선합니다.

from sklearn.decomposition import PCA

# 1536 차원 → 256 차원 축소
pca = PCA(n_components=256)
reduced_embeddings = pca.fit_transform(original_embeddings)

# 성능 비교
원본: 1536 차원, 6KB/벡터, 100ms 검색
축소: 256 차원, 1KB/벡터, 15ms 검색
정확도 손실: 약 5%

2. 양자화 (Quantization)

float32 → int8로 변환하여 메모리를 4배 절감합니다.

import numpy as np

# float32 (4 bytes)
original = np.array([0.123, 0.456, 0.789], dtype=np.float32)

# int8 (1 byte) - [-128, 127] 범위로 매핑
def quantize(vector, scale=127):
    return (vector * scale).astype(np.int8)

quantized = quantize(original)
# [16, 58, 100]

# 복원
def dequantize(vector, scale=127):
    return vector.astype(np.float32) / scale

restored = dequantize(quantized)
# [0.126, 0.457, 0.787] - 약간의 손실

3. 제품 양자화 (Product Quantization)

대규모 벡터 DB에서 사용되는 고급 압축 기법입니다.

# 1536 차원 벡터를 6개 sub-vector로 분할
# 각 256 차원 sub-vector를 코드북으로 압축

원본: 1536 dim × 4 bytes = 6KB
PQ 압축: 6 codes × 1 byte = 6 bytes
압축률: 1000:1
검색 속도: 10-100배 향상

임베딩 평가 방법

1. 의미 유사도 테스트

# 테스트 세트
test_pairs = [
    ("강아지", "개", True),  # 유사해야 함
    ("강아지", "자동차", False),  # 다르게 있어야 함
    ("king", "queen", True),
    ("king", "apple", False)
]

model = SentenceTransformer('model-name')

correct = 0
for word1, word2, should_be_similar in test_pairs:
    emb1 = model.encode(word1)
    emb2 = model.encode(word2)

    similarity = cosine_similarity(emb1, emb2)
    is_similar = similarity > 0.7

    if is_similar == should_be_similar:
        correct += 1

accuracy = correct / len(test_pairs)
print(f"정확도: {accuracy:.2%}")

2. 검색 성능 평가

# 검색 품질 지표
# Precision@K: 상위 K개 중 관련 문서 비율
# Recall@K: 전체 관련 문서 중 상위 K개에 포함된 비율

def precision_at_k(retrieved, relevant, k):
    retrieved_k = retrieved[:k]
    relevant_count = len(set(retrieved_k) & set(relevant))
    return relevant_count / k

def recall_at_k(retrieved, relevant, k):
    retrieved_k = retrieved[:k]
    relevant_count = len(set(retrieved_k) & set(relevant))
    return relevant_count / len(relevant)

# 예시
query = "파이썬 튜토리얼"
retrieved_docs = search(query, k=10)  # 검색 결과 10개
relevant_docs = [1, 3, 5, 12, 15]  # 실제 관련 문서 ID

p = precision_at_k(retrieved_docs, relevant_docs, k=10)
r = recall_at_k(retrieved_docs, relevant_docs, k=10)

print(f"Precision@10: {p:.2%}")
print(f"Recall@10: {r:.2%}")

주의사항과 한계

1. 도메인 특화 필요성

일반 임베딩 모델은 특정 도메인에서 성능이 떨어질 수 있습니다.

# 일반 모델
"피고인은 무죄를 주장했다"
"환자는 증상이 없다고 말했다"
→ 유사도: 0.75 (높음) - 문장 구조가 비슷

# 법률 특화 모델
"피고인은 무죄를 주장했다"
"환자는 증상이 없다고 말했다"
→ 유사도: 0.35 (낮음) - 도메인이 다름

해결책: 도메인 데이터로 Fine-tuning

2. 언어 간 차이

# 다국어 모델에서도 언어별 성능 차이 존재
영어: 95% 정확도
한국어: 88% 정확도
소수 언어: 70% 정확도

→ 한국어 특화 모델 사용 고려

3. 길이 제한

# 대부분의 모델은 토큰 수 제한 있음
OpenAI: 8191 토큰
BERT: 512 토큰

# 긴 문서 처리 방법
1. 청킹: 문서를 나누어 각각 임베딩
2. 요약: 문서 요약 후 임베딩
3. 계층적 임베딩: 단락 → 섹션 → 전체

결론

벡터 임베딩은 현대 AI의 핵심 기술입니다. 이를 통해:

  • 🔍 의미 기반 검색: 키워드가 아닌 의미로 검색
  • 🎯 추천 시스템: 유사한 아이템 찾기
  • 📊 텍스트 분류: 카테고리 자동 분류
  • 🔄 중복 제거: 비슷한 콘텐츠 식별
  • 🤖 RAG 시스템: 정확한 정보 검색

시작하기:

  1. OpenAI 또는 Cohere API로 간단히 시작
  2. 작은 프로젝트로 개념 익히기
  3. 성능 평가 및 최적화
  4. 필요시 커스텀 모델 고려

벡터 임베딩을 마스터하면 AI 애플리케이션의 품질을 획기적으로 향상시킬 수 있습니다!