Home Vector Database Overview
Post
Cancel

Vector Database Overview

Vector Database

목차


Vector Database의 역할

Vector Database는 텍스트, 이미지, 음성 등을 숫자 벡터로 저장하고 의미적으로 유사한 항목을 빠르게 검색하는 데 특화된 저장소다. 전통 데이터베이스(SQL)와 다르게 의미 기반 검색에 최적화되어 있다.

1
2
3
4
5
6
7
전통 데이터베이스 (정확 매칭):
SELECT * FROM users WHERE age = 25
→ 정확히 25세만 찾음

벡터 데이터베이스 (의미 유사도):
SELECT * FROM documents WHERE embedding SIMILAR_TO query_embedding
→ 의미가 비슷한 모든 문서 찾음

사용 사례

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1. RAG (Retrieval Augmented Generation)
   Q: "Python 설치 방법"
   → 비슷한 문서 검색
   → LLM에 주입

2. 추천 시스템
   사용자 A의 취향 벡터
   → 비슷한 취향의 사용자 B 찾기
   → 비슷한 상품 추천

3. 이미지 검색
   사진 벡터
   → 비슷한 사진 검색 (고양이 사진 찾기)

4. 이상 탐지
   정상 거래 벡터
   → 비슷하지 않은 거래 탐지

벡터 DB vs SQL DB

1
2
3
4
5
6
7
8
9
10
11
SQL Database:
- 데이터: 구조화된 행과 열
- 검색: 정확 매칭, 범위 쿼리
- 인덱스: B-Tree
- 예: PostgreSQL, MySQL

Vector Database:
- 데이터: 고차원 벡터
- 검색: 유사도 기반
- 인덱스: LSH, HNSW, IVF
- 예: Pinecone, Weaviate, Milvus

Embedding Vector

“embedding은 무엇을 숫자로 표현하는가?”

Embedding은 텍스트, 이미지 등을 의미를 담은 고차원 숫자 벡터로 변환한 것이다. 의미적으로 비슷한 것은 벡터 공간에서도 가까워진다.

1
2
3
4
5
6
7
8
텍스트: "좋은 영화야"
↓ Embedding Model
벡터: [0.23, -0.15, 0.88, ..., 0.42]
(384차원 또는 1536차원)

의미 관계:
"좋은 영화" 벡터 ≈ "훌륭한 영화" 벡터
"나쁜 영화" 벡터는 멀음

Embedding의 성질

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 의미 보존
   - 비슷한 의미 → 비슷한 벡터
   - 반대 의미 → 멀은 벡터

2. 방향성
   - 벡터의 방향이 의미를 담음
   - 크기는 의미와 무관

3. 대수 연산
   king - man + woman ≈ queen
   (개념적 유사성도 대수적으로 표현)

예:
"서울" - "한국" + "일본" ≈ "도쿄"
"좋음" + "상품" ≈ "우수한 제품"

Embedding 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from sentence_transformers import SentenceTransformer

# 모델 로드 (384차원)
model = SentenceTransformer('all-MiniLM-L6-v2')

# 단일 문자열
text = "Python 프로그래밍"
embedding = model.encode(text)
print(embedding.shape)  # (384,)
print(embedding[:5])    # [0.12, -0.34, 0.56, ...]

# 배치 처리
texts = [
    "Python 프로그래밍",
    "자바 개발",
    "강아지는 귀엽다"
]
embeddings = model.encode(texts)
print(embeddings.shape)  # (3, 384)

차원의 의미

1
2
3
4
5
6
7
8
9
차원: 벡터의 길이
- 작은 차원 (256, 384): 빠르고 저렴
  → RAG, 검색
  
- 큰 차원 (1536, 3072): 정확하지만 느림
  → 이미지, 복잡한 의미 포착
  
절충:
대부분의 RAG: 384-768차원으로 충분

“cosine similarity는 무엇을 비교하는가?”

Cosine Similarity는 두 벡터가 같은 방향을 얼마나 가리키는지 비교한다. 벡터 공간에서 각도를 측정한다.

1
2
3
4
5
6
7
벡터 A: [1, 0, 0]
벡터 B: [1, 0.1, 0]
벡터 C: [0, 1, 0]

코사인 유사도:
sim(A, B) = 0.99 (거의 같은 방향)
sim(A, C) = 0 (수직, 전혀 다른 방향)

유사도 계산

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
코사인 유사도 공식:
similarity = (A · B) / (||A|| × ||B||)

범위: -1 ~ 1 (보통 0 ~ 1)
- 1: 같은 방향
- 0: 수직 (관계없음)
- -1: 반대 방향

벡터 A = [3, 4]
벡터 B = [5, 12]

내적: 3×5 + 4×12 = 63
||A||: √(9+16) = 5
||B||: √(25+144) = 13

sim(A,B) = 63 / (5×13) = 0.97

다른 유사도 지표

1
2
3
4
5
6
7
8
9
10
11
12
13
L2 거리 (Euclidean Distance):
- 두 점 사이 직선 거리
- 차원 증가하면 신뢰도 떨어짐
- 사용: 이미지, 비디오

Dot Product:
- 내적 직접 사용
- 빠르지만 벡터 크기 영향받음
- 사용: 대규모 검색

Manhattan Distance:
- 격자 거리
- 계산 빠르지만 정확도 낮음

구현 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 벡터들
query = np.array([[0.1, 0.5, 0.3]])  # (1, 3)
documents = np.array([
    [0.12, 0.48, 0.32],  # 유사
    [0.9, 0.1, 0.1],     # 다름
    [0.11, 0.51, 0.29]   # 유사
])

# 유사도 계산
similarities = cosine_similarity(query, documents)[0]
# [0.99, 0.12, 0.98]

# 순서대로 정렬
sorted_indices = np.argsort(-similarities)
# [0, 2, 1]

Index

“Index는 무엇을 위해 필요한가?”

Index는 백만 개 이상의 벡터를 빠르게 검색하기 위한 구조다. 모든 벡터와 비교하면 시간이 오래 걸리므로 인덱싱이 필수다.

1
2
3
4
5
6
7
8
9
인덱스 없이 (Brute Force):
100만 개 벡터 × 쿼리 벡터 비교
→ 모든 내적 계산
→ 시간: ~초 단위 (느림)

인덱스 있이 (ANN):
클러스터링 활용
→ 관련 영역만 검색
→ 시간: ~밀리초 (빠름)

“ANN index는 왜 정확도와 속도 trade-off가 있는가?”

ANN (Approximate Nearest Neighbor)은 정확한 최근접을 보장하지 않고 근사값을 빠르게 찾기 때문에 속도를 얻는 대신 정확도를 포기한다.

1
2
3
4
5
6
7
8
9
정확 검색 (Exact NN):
- 방식: 모든 벡터와 비교
- 정확도: 100%
- 속도: 느림 (초 단위)

근사 검색 (ANN):
- 방식: 인덱싱으로 효율화
- 정확도: ~95% (조정 가능)
- 속도: 빠름 (밀리초)

인기 있는 인덱스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
HNSW (Hierarchical Navigable Small World):
- 계층적 구조
- 속도: 빠름
- 정확도: 높음
- 메모리: 중간
- 사용: Pinecone, Weaviate

IVF (Inverted File):
- 클러스터링 기반
- 속도: 매우 빠름
- 정확도: 중간
- 메모리: 적음
- 사용: FAISS (Facebook)

LSH (Locality Sensitive Hashing):
- 해시 기반
- 속도: 빠름
- 정확도: 낮음
- 메모리: 적음

FAISS (Facebook AI Similarity Search):
- 프레임워크
- 여러 인덱스 방식 지원
- 오픈소스, 고성능

인덱스 구성

1
2
3
4
5
6
7
Partition Phase:
전체 벡터 공간을 여러 영역으로 분할
→ 각 영역에 대표 점(centroid) 지정

Search Phase:
쿼리 벡터와 가장 가까운 centroid 찾기
→ 그 영역의 벡터들만 상세 검색

구현 예시 (FAISS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import faiss
import numpy as np

# 벡터 준비
vectors = np.random.random((100000, 384)).astype('float32')

# HNSW 인덱스 생성
index = faiss.IndexHNSWFlat(384, 32)
index.add(vectors)

# 쿼리
query = np.random.random((1, 384)).astype('float32')
distances, indices = index.search(query, k=5)
# indices: [[0, 234, 5678, ...]]  (상위 5개)

Metadata Filtering

“metadata filter는 언제 필요한가?”

Metadata Filtering은 벡터 검색 결과를 속성(메타데이터)로 추가 필터링해 더 정확한 결과를 얻을 때 사용한다.

1
2
3
4
5
6
7
8
9
10
필터 없이:
Q: "좋은 영화"
→ 검색: [영화1(2020), 영화2(2023), 영화3(2010)]
(연도 상관없음)

필터 있이:
Q: "최근 5년의 좋은 영화"
Metadata: year >= 2021
→ 검색 후 필터: [영화2(2023)]
(최신 영화만)

메타데이터 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
문서 청크:
{
  "id": "doc123_chunk5",
  "text": "Python은 ...",
  "embedding": [0.1, -0.2, ...],
  "metadata": {
    "source": "tutorial.md",
    "version": "2.1",
    "date": "2026-05-23",
    "category": "programming",
    "difficulty": "beginner"
  }
}

메타데이터 필터링

1
2
3
4
5
6
7
8
9
필터 조건:
- 동등: category == "programming"
- 범위: difficulty >= "intermediate"
- 목록: source IN ["guide.md", "faq.md"]
- 범위: date BETWEEN "2024-01-01" AND "2026-12-31"

조합:
category == "programming" AND difficulty >= "beginner"
AND date >= "2025-01-01"

구현 예시 (Pinecone)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pinecone

# 초기화
pinecone.init(api_key="xxx")
index = pinecone.Index("documents")

# 메타데이터와 함께 저장
vectors_with_metadata = [
    ("id1", [0.1, 0.2, ...], {"category": "python", "year": 2025}),
    ("id2", [0.15, 0.25, ...], {"category": "java", "year": 2024}),
]
index.upsert(vectors=vectors_with_metadata)

# 필터링 검색
query_embedding = [0.12, 0.22, ...]
results = index.query(
    vector=query_embedding,
    top_k=5,
    filter={"year": {"$gte": 2024}}  # 2024년 이후만
)
# [('id1', 0.98), ...] (필터 조건 만족)

Recall과 Latency

“성능 지표 Recall과 Latency의 의미”

Recall은 검색 정확도, Latency는 검색 속도를 나타낸다. RAG에서는 두 지표 모두 중요하고 균형을 맞춰야 한다.

1
2
3
4
5
6
7
8
9
10
Recall:
- 정의: "실제 최근접 이웃 중 찾은 비율"
- 범위: 0~1 (높을수록 좋음)
- 예: Recall@5 = 0.95
  (상위 5개 중 95%가 정답)

Latency:
- 정의: "검색 요청부터 응답까지의 시간"
- 범위: 밀리초
- 예: 10ms (RAG: 50-100ms 목표)

Trade-off

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
높은 Recall (정확도 우선):
- 방식: Brute Force 또는 조건 완화
- Recall: 99%
- Latency: 1000ms (느림)
- 사용: 오프라인 분석

높은 속도 (Latency 우선):
- 방식: 빠른 인덱싱 (IVF 적극 활용)
- Recall: 80%
- Latency: 10ms (빠름)
- 사용: 실시간 검색

균형 (일반적):
- 방식: HNSW, 메타데이터 필터링
- Recall: 95%
- Latency: 50ms
- 사용: RAG, 추천 시스템

최적화 전략

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. 인덱스 매개변수 조정
   - HNSW의 ef (expansion factor) 증가 → 정확도 ↑, 속도 ↓
   - IVF의 nprobe (탐색 프로브) 증가 → 정확도 ↑, 속도 ↓

2. Quantization (양자화)
   - 벡터 압축 (float32 → int8)
   - 메모리 90% 감소, 속도 향상
   - 정확도 약간 손실

3. Caching
   - 자주 검색되는 쿼리 캐싱
   - Latency 대폭 감소

4. 배치 처리
   - 여러 쿼리를 한 번에 처리
   - 처리량 향상

성능 측정 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time
from sklearn.metrics import recall_score

def evaluate_vector_db(vectors, queries, top_k=5):
    start_time = time.time()
    
    # 검색 (HNSW)
    distances, indices = index.search(queries, k=top_k)
    
    # 속도 측정
    latency = (time.time() - start_time) / len(queries)  # 평균 ms
    
    # 정확도 측정 (정답과 비교)
    found = sum(1 for idx in indices if idx in ground_truth)
    recall = found / (len(queries) * top_k)
    
    print(f"Recall@{top_k}: {recall:.2%}")
    print(f"Avg Latency: {latency*1000:.1f}ms")
    
    return {"recall": recall, "latency": latency}

정리

개념설명
Embedding의미를 담은 고차원 벡터
Similarity벡터 간 유사도 측정
Cosine각도 기반 유사도
Index빠른 검색을 위한 자료 구조
ANN근사 최근접 탐색
Metadata벡터 추가 속성 (필터링용)
Recall검색 정확도
Latency검색 속도

This post is licensed under CC BY 4.0 by the author.