본문으로 건너뛰기

"Retrieval" 태그 — 3개 게시물

RAG, 검색, 임베딩 관련 글

모든 태그 보기

Multi Vector and Dataset Geometry

· 약 11분
김태한
AI Research Engineer, Brain Crew

TL;DR

Multi-vector retrieval 알고리즘 선택은 **유사도 함수(MaxSim/SumSim/Top-K Sum)**와 데이터 기하학(Isotropic/Anisotropic/Multi-Kernel) 두 축에 의해 결정된다. PLAID는 moderate variance + MaxSim 조합에, MUVERA는 isotropic 데이터에 최적화되어 있으며 이 조건을 벗어나면 성능이 급격히 저하된다. 흥미롭게도 SumSim은 mean-pooling으로 단일 벡터 MIPS로 환원 가능하며, ColPali는 per-token HNSW + 이진화로 32배 압축하면서도 94% recall을 유지할 수 있다. 임베딩 모델 학습 시 variance/isotropy 정규화를 통해 인덱싱 친화적 기하학을 설계할 수 있다는 점이 핵심이다.

Key Takeaways

  • 알고리즘 선택 전 데이터 기하학 분석이 필수: PLAID/MUVERA 도입 전에 intra-document variance와 anisotropy 정도를 정량화하라. 잘못된 조합은 centroid pruning 붕괴나 recall 저하를 초래한다.
  • SumSim은 특수 인프라가 불필요: 내적의 bilinearity를 활용하면 mean-pooled 단일 벡터 MIPS로 환원되어 표준 HNSW/DiskANN으로 처리 가능하다.
  • ColPali의 고정 cardinality는 게임 체인저: 1030개 고정 패치는 per-token HNSW + 이진화(32x 압축)로 효율적 처리가 가능하며, ~94% recall을 유지한다.
  • Contrastive learning이 기하학을 결정: InfoNCE 손실의 온도 파라미터와 hard negative mining이 multi-kernel anisotropic 분포를 생성하며, 이것이 인덱싱 효율성에 직접 영향을 미친다.
  • 모델 설계와 인덱싱은 독립적이지 않음: 학습 단계에서 variance/isotropy 정규화를 통해 PLAID나 MUVERA에 최적화된 임베딩 분포를 유도할 수 있다. 이는 PoBE(Preference-Optimized Binary Embedding) 같은 접근에 직접 활용 가능하다.

상세 내용

Multi-Vector Retrieval의 두 가지 핵심 축

전통적인 retrieval은 point-to-point MIPS(Maximum Inner Product Search)로 해결되지만, multi-vector retrieval은 본질적으로 집합 대 집합(set-to-set) 유사도 문제다. 각 문서와 쿼리는 단일 벡터가 아닌 임베딩 집합으로 표현된다: Q = {q₁, ..., qₘ}, P = {p₁, ..., pₙ}.

이 문제 공간은 두 개의 독립적인 축으로 분류할 수 있다:

  1. 유사도 함수(Similarity Function): 두 집합 간 유사도를 계산하는 방법
  2. 데이터 기하학(Dataset Geometry): 임베딩 공간 내 분포의 통계적 구조

대부분의 기존 논의는 MaxSim + ColBERT 스타일 분포라는 단일 조합에 집중되어 있으며, 이는 전체 문제 공간의 일부에 불과하다.

유사도 함수의 분류 체계

MaxSim (Chamfer Similarity)

ColBERT의 표준 접근법으로, 각 쿼리 토큰에 대해 가장 잘 매칭되는 문서 토큰을 찾아 합산한다:

sim_MaxSim(Q, P) = Σᵢ max_j ⟨qᵢ, pⱼ⟩
  • 비대칭적: 쿼리 토큰에 대해서만 합산 (문서→쿼리 방향은 고려하지 않음)
  • Fine-grained alignment: 각 쿼리 토큰이 독립적으로 최적 매칭을 찾음
  • Winner-takes-all 특성: 각 쿼리 토큰에 대해 단 하나의 문서 토큰만 기여

SumSim (All-Pairs)

모든 가능한 쿼리-문서 토큰 쌍의 유사도를 합산한다:

sim_SumSim(Q, P) = Σᵢ Σⱼ ⟨qᵢ, pⱼ⟩
  • 대칭적: 모든 토큰이 동등하게 기여
  • Cross-modal matching, group recommendation 등에 사용
  • PLAID의 pruning 전략과 근본적으로 비호환

Top-K Sum

각 쿼리 토큰에 대해 상위 k개 문서 토큰의 유사도를 합산:

sim_TopK(Q, P) = Σᵢ Σ(j∈TopK) ⟨qᵢ, pⱼ⟩
  • MaxSim (k=1)과 SumSim (k=|P|) 사이의 중간 지대
  • k 증가 시 PLAID의 centroid pruning 효율성 감소

Symmetric Chamfer

양방향 MaxSim의 평균:

sim_SymChamfer(Q, P) = 1/2 [sim_MaxSim(Q, P) + sim_MaxSim(P, Q)]

저자의 핵심 지적: 현재 multi-vector retrieval 담론은 MaxSim을 문제 자체와 동일시하고 있으며, 다른 유사도 함수들은 체계적으로 무시되고 있다.

Dataset Geometry: 세 가지 기하학적 체제

임베딩 분포의 통계적 구조는 인덱싱 알고리즘의 성능을 근본적으로 결정한다.

Gaussian Isotropic

모든 방향에서 균일한 분산을 가진 분포:

v ~ N(μ, σ²I)
  • 이론적 분석에서 자주 가정되지만 실제로는 드묾
  • 랜덤 하이퍼플레인 기반 LSH가 이론적 보장을 제공하는 체제

Gaussian Anisotropic

방향에 따라 분산이 다른 분포:

v ~ N(μ, Σ), Σ ≠ σ²I
  • 특정 방향(주성분)으로 데이터가 "늘어난" 형태
  • SimHash 같은 각도 기반 해싱에 문제 발생

Multi-Kernel Anisotropic

여러 Gaussian 클러스터의 혼합, 각 클러스터가 비등방적:

v ~ Σₖ πₖ · N(μₖ, Σₖ)
  • 실제 학습된 임베딩의 현실적 분포
  • ColBERTv2, ColPali 등이 이 체제에 속함
  • Contrastive learning의 자연스러운 결과물

세 가지 기하학적 체제의 비교. 실제 multi-vector 임베딩은 Multi-Kernel Anisotropic 체제에 해당한다.

Contrastive Training이 만드는 기하학

ColBERT의 InfoNCE 손실 함수는 특정 기하학적 구조를 유도한다:

온도 파라미터 τ의 역할

L_InfoNCE = -log [exp(⟨q, p⁺⟩/τ) / Σᵢ exp(⟨q, pᵢ⟩/τ)]
  • τ는 클러스터 tightness를 제어하는 파라미터
  • 낮은 τ → 더 촘촘한 클러스터 (equi-angular 형태)
  • 높은 τ → 더 넓게 퍼진 분포

Hard Negative Mining의 영향

의미적으로 유사하지만 관련 없는 문서(hard negatives)를 명시적으로 구분하도록 학습하면:

L_hard = -log [exp(⟨q, p⁺⟩/τ) / (exp(⟨q, p⁺⟩/τ) + λ·Σ exp(⟨q, p⁻_hard⟩/τ))]
  • 클러스터 간 경계 방향으로 방향성 늘어남(directional stretching) 발생
  • Within-kernel 공분산이 비구형(non-spherical)이 됨
  • 결과적으로 multi-kernel anisotropic 분포 생성

핵심 통찰: 하나의 금융 문서 내에서도 토큰들은 여러 의미적 역할(전문 용어, 기능어, 맥락 수식어)을 수행하며, 각각이 임베딩 공간의 다른 영역(다른 커널)에 매핑된다.

기하학 정량화 지표

Intra-document variance (문서 내 분산)

σ²_intra(d) = 1/|P_d| Σᵢ ‖pᵢ - μ_d‖²
μ_d = 1/|P_d| Σᵢ pᵢ
  • 높은 variance: 토큰들이 여러 의미 영역에 분산
  • PLAID의 centroid pruning 효율성에 직접 영향

Residual magnitude (잔차 크기)

centroid 할당 후 잔차:

r_i = p_i - c_assigned(i)
  • 큰 잔차: 양자화 시 재구성 노이즈 증가
  • PLAID Stage 3의 신뢰도에 영향

문서별 intra-document variance 분포. ColPali 같은 vision 모델은 텍스트 모델보다 훨씬 높은 분산을 보인다.

PLAID의 기하학적 한계

PLAID는 3단계 파이프라인으로 작동한다:

  1. Centroid Pruning: 쿼리 토큰과 관련 없는 centroid 제거
  2. Document Filtering: 관련 centroid만 포함한 문서로 후보 축소
  3. Verification: 압축된 표현으로 정확한 MaxSim 계산

Centroid Pruning의 선택성 붕괴

문서 d가 활성화하는 고유 centroid 수:

n_d = |{c_i : ∃p ∈ P_d, c_i = argmin_c ‖p - c‖}|

PLAID의 선택성(제거되는 문서 비율):

selectivity = 1 - (n_d · n_query) / (C · N_docs)
  • 높은 σ²_intra (예: ColPali) → n_d가 크게 증가
  • C = 65,536인 경우, n_d가 수천 개로 증가하면 pruning이 거의 아무것도 제거하지 못함
  • 결과: 거의 모든 문서가 Stage 2로 진입 → 계산 비용 급증

SumSim에서의 PLAID 붕괴

MaxSim의 "winner-takes-all" 특성:

  • 각 쿼리 토큰에 대해 단 하나의 문서 토큰만 승리
  • 비매칭 centroid는 max 연산에서 무시됨

SumSim의 "all-contribute" 특성:

  • 모든 문서 토큰이 모든 쿼리 토큰에 기여
  • Centroid pruning의 상한 기반 제거가 무의미해짐

PLAID Stage 2의 pruning 로직:

if max_i ⟨q_i, c_k⟩ < θ → centroid c_k 제거

이는 MaxSim에서만 유효하며, SumSim에서는:

contribution(c_k) = Σᵢ Σ(p∈cluster_k) ⟨qᵢ, p⟩

단일 쿼리 토큰과의 최대 유사도로 전체 기여를 상한할 수 없음.

Quantization 품질과 Residual Magnitude

ColBERTv2는 centroid ID + 2-bit 양자화 잔차로 압축:

재구성 오차: ‖p_i - (c_i + r_q)‖
MaxSim 근사 오차: |MaxSim_true - MaxSim_approx| ≤ Σᵢ ‖ε_i‖

MaxSim에서는 이것이 유효하지만, large residual을 가진 anisotropic 데이터에서는 2-bit 양자화가 significant noise를 도입한다.

Residual magnitude 분포. 큰 잔차는 양자화 노이즈를 증가시켜 PLAID의 정확도를 저하시킨다.

MUVERA의 기하학적 한계

MUVERA는 SimHash 기반 파티셔닝으로 multi-vector 집합을 고정 차원 벡터(FDE)로 변환한다.

SimHash와 Anisotropy의 불협화음

b개의 랜덤 하이퍼플레인 {h₁, ..., h_b}를 사용. Isotropic 데이터에서 충돌 확률:

P_collision(u, v) = 1 - θ_uv/π

이는 각도만으로 결정되며, 균일 분포에서 이론적 보장이 성립한다.

그러나 Anisotropic 데이터에서는:

Σ = [σ₁² 0; 0 σ₂²], σ₁² >> σ₂²

주축(high variance 방향) u에 수직인 랜덤 하이퍼플레인이:

  • ℓ₂ 거리에서 가까운 두 벡터를 다른 버킷으로 분리
  • 유효 각도 θ_eff > θ_uv

Anisotropic 클러스터에서 SimHash의 문제. 주축에 수직인 하이퍼플레인이 가까운 이웃을 분리한다.

Over-Partitioning 문제

올바른 충돌 확률:

P_collision^(b) = (1 - θ_eff/π)^b

b에 대해 지수적으로 감소하여, 큰 b (fine-grained FDE)는 nearest neighbor 쌍을 다른 파티션으로 분리할 확률이 높아진다.

트레이드오프 딜레마:

  • 작은 b: coarse FDE → 낮은 discrimination, 많은 false positive
  • 큰 b: fine-grained FDE → nearest neighbor 분리, recall 저하

Multi-kernel anisotropic 공간에서 elongated within-kernel 분포는 SimHash의 이론적 보장을 무효화한다.

실용적 해결책 #1: SumSim은 Mean-Pooling으로 충분하다

내적의 **bilinearity (쌍선형성)**를 활용한 핵심 증명:

sim_SumSim(Q, P) = Σᵢ Σⱼ ⟨qᵢ, pⱼ⟩
= Σᵢ Σⱼ qᵢᵀpⱼ
= (Σᵢ qᵢ)ᵀ (Σⱼ pⱼ)
= |Q| · |P| · ⟨μ_Q, μ_P⟩

여기서 μ_Q = (1/|Q|)Σᵢ qᵢ, μ_P = (1/|P|)Σⱼ pⱼ

SumSim이 mean-pooled 벡터 간 단일 내적으로 환원되는 과정.

실무 함의:

  • SumSim 기반 retrieval (aggregate affinity, group recommendation, cross-modal matching)
  • Mean-pooled representation을 오프라인 계산
  • 표준 ANN 시스템으로 인덱싱: HNSW, DiskANN, ScaNN
  • PLAID도, MUVERA도, 커스텀 인프라도 불필요

코드 예시:

# Multi-vector 대신 단일 벡터로 변환
doc_embedding_pooled = doc_embeddings.mean(dim=0)
query_embedding_pooled = query_embeddings.mean(dim=0)

# 표준 HNSW 인덱스 사용
index = hnswlib.Index(space='ip', dim=embedding_dim)
index.add_items(doc_embeddings_pooled)
results = index.knn_query(query_embedding_pooled, k=10)

# 필요 시 full multi-vector로 re-rank
for doc_id in results:
exact_score = compute_sumsim(query_embeddings, doc_embeddings[doc_id])

실용적 해결책 #2: ColPali의 고정 Cardinality 활용

ColPali는 ColBERT와 근본적으로 다른 특성을 가진다:

  • ColBERT: 텍스트 길이에 따라 가변적인 토큰 수 (50~500+)
  • ColPali: 페이지당 정확히 1030개 임베딩 (1024 image patches + 6 instruction tokens)

이 고정 cardinality는 완전히 다른 최적화 전략을 가능하게 한다.

Vespa의 Per-Token Nearest Neighbor 접근

Phase 0 (Pre-filtering):
각 쿼리 토큰 qᵢ에 대해:
- 이진화 임베딩으로 Hamming 거리 계산
- k₀개 nearest page-patch 검색
- 해당 페이지들의 union 생성

Phase 1 (Approximate MaxSim):
생존 페이지들을 inverted Hamming distance로 스코어링
상위 k₁개 유지

Phase 2 (Exact MaxSim):
상위 k₁개를 full-precision float MaxSim으로 re-rank

Vespa 스타일 ColPali 검색 워크플로우. 이진화 + per-token HNSW + 단계적 reranking.

저장 비용 비교 (10억 페이지 기준):

구성페이지당 저장량총 저장량Phase 0 Recall
ColPali float32526 KB~526 TB100%
ColPali bfloat16263 KB~263 TB~99%
ColPali binarized16.5 KB~16.5 TB~94%
Centroid index (k=30)7.7 KB~7.7 TBvariable

32배 압축 + 최소 recall 손실이 핵심이다.

이진화 구현:

def binarize_embeddings(embeddings):
"""각 차원을 평균 기준으로 이진화"""
threshold = embeddings.mean(dim=-1, keepdim=True)
binary = (embeddings > threshold).to(torch.uint8)
# 128 dim → 16 bytes
return torch.packbits(binary, dim=-1)

def hamming_distance(binary1, binary2):
"""Hamming 거리 계산"""
xor = binary1 ^ binary2
return torch.popcnt(xor).sum()

# Per-token HNSW 인덱스
per_token_index = [
hnswlib.Index(space='hamming', dim=16) # 16 bytes
for _ in range(1030) # 각 패치 위치별 인덱스
]

# Phase 0: Per-token nearest neighbor
candidate_pages = set()
for query_token in query_embeddings:
binary_query = binarize_embeddings(query_token)
for token_idx, index in enumerate(per_token_index):
nearest = index.knn_query(binary_query, k=100)
candidate_pages.update(nearest)

# Phase 1: Approximate MaxSim with binary
# Phase 2: Exact MaxSim with float32

의사결정 프레임워크

유사도 함수데이터 기하학권장 접근법핵심 고려사항
SumSimAnyMean-pool → 표준 HNSWMulti-vector 인프라 완전 불필요
MaxSimMulti-kernel, moderate σ²_intraPLAIDMS MARCO 스케일에서 검증됨
MaxSimMulti-kernel, high σ²_intraPLAID + large codebookStage-2 selectivity 실증 검증 필요
MaxSimAnyMUVERA + re-rankRecall 검증 필수, b 신중 조정
Top-K SumMulti-kernel커스텀 파이프라인PLAID pruning 직접 적용 불가
MaxSim (ColPali)High variance, fixed cardinalityPer-token HNSW + 이진화32x 압축, ~94% recall
MaxSim (소규모)<10M 문서Brute-force float MaxSim특수 인프라 불필요

실무 체크리스트:

  1. 데이터 기하학 먼저 분석:

    # Intra-document variance 측정
    for doc in corpus:
    embeddings = model.encode(doc)
    variance = embeddings.var(dim=0).mean()
    print(f"σ²_intra = {variance:.4f}")

    # Anisotropy 측정 (주성분 분산 비율)
    pca = PCA()
    pca.fit(all_embeddings)
    explained_variance_ratio = pca.explained_variance_ratio_
    anisotropy = explained_variance_ratio[0] / explained_variance_ratio[-1]
  2. 유사도 함수 확인: MaxSim이 정말 필요한가? SumSim으로 충분하지 않은가?

  3. 스케일 고려: 10M 문서 미만이면 brute-force도 실용적

  4. 고정 cardinality 여부: ColPali 같은 경우 per-token 접근 고려

Embedding Model 설계 시사점

저자의 가장 중요한 통찰: 학습 단계에서 인덱싱 친화적 기하학을 유도할 수 있다.

Intra-document Variance 정규화 (PLAID 최적화)

def variance_regularization_loss(embeddings, target_variance):
"""문서 내 분산을 목표값으로 정규화"""
doc_variance = embeddings.var(dim=0).mean()
return (doc_variance - target_variance) ** 2

# 전체 손실에 추가
total_loss = info_nce_loss + λ_var * variance_reg_loss

σ²_target을 codebook 크기에서 좋은 PLAID selectivity를 보이는 값으로 설정하면, 인덱싱 효율성을 모델에 내재화할 수 있다.

Within-kernel Isotropy 정규화 (MUVERA 최적화)

def isotropy_regularization_loss(embeddings, cluster_assignments):
"""각 클러스터 내에서 등방성 유도"""
loss = 0
for cluster_id in unique(cluster_assignments):
cluster_embeds = embeddings[cluster_assignments == cluster_id]
cov_matrix = torch.cov(cluster_embeds.T)
eigenvalues = torch.linalg.eigvalsh(cov_matrix)
# 고유값 분산 최소화 → 구형 클러스터
loss += eigenvalues.var()
return loss

더 둥근 클러스터 형태를 유도하여 랜덤 하이퍼플레인 파티셔닝과의 호환성 향상.

PoBE(Preference-Optimized Binary Embedding)에의 적용:

class GeometryAwareBinaryEmbedding(nn.Module):
def forward(self, x):
# 표준 임베딩
embeddings = self.encoder(x)

# Variance 정규화 (PLAID용)
var_loss = variance_reg(embeddings, target=0.15)

# Isotropy 정규화 (이진화 친화적)
iso_loss = isotropy_reg(embeddings, self.clusters)

# 이진화
binary_embeds = torch.sign(embeddings - embeddings.mean())

return binary_embeds, var_loss + iso_loss

핵심 메시지: 임베딩 모델 설계와 인덱싱 인프라는 독립적 선택이 아니다. 모델이 학습하는 기하학이 인덱스 성능을 직접 결정하며, 그 기하학에 대한 제어는 일반적으로 인식되는 것보다 훨씬 크다.

열린 연구 질문

Geometry-Aware Hashing

랜덤 하이퍼플레인 대신 실제 공분산 구조 기반 파티셔닝:

  • k-means LSH: 주성분 방향으로 파티션
  • Learned hashing: 데이터 분포를 직접 학습

Visual Retrieval의 적절한 유사도 함수

ColPali가 MaxSim을 사용하는 것은 ColBERT로부터의 관성일 수 있음:

  • 이미지 패치 간 유사도에는 다른 aggregation이 더 적절할 수 있음
  • Spatial pooling, attention-weighted sum 등의 대안 탐구 필요

모델 스케일에 따른 기하학 변화

더 큰 모델이 더 isotropic한 임베딩을 생성하는가?

  • 표현력 증가가 clustering 구조를 어떻게 변화시키는가?
  • 인덱싱 친화도와

You don’t need Elasticsearch : BM25 is now in Postgres

· 약 8분
최재훈
LEAD (AI Research Engineer), Brain Crew

TL;DR

Postgres의 기본 전문 검색(Full-Text Search)은 키워드 반복 남용, 문서 길이 편향, 희귀 단어 처리 실패 등의 문제로 실무에서 한계가 있었습니다. 이를 해결하기 위해 Elasticsearch를 추가하는 것이 일반적이었지만, 이제 pg_textsearch 확장을 통해 BM25 알고리즘을 Postgres에서 직접 사용할 수 있습니다. BM25는 TF 포화(Term Frequency Saturation), IDF(Inverse Document Frequency), 문서 길이 정규화를 통해 검색 품질을 대폭 개선하며, pgvector와 결합한 하이브리드 검색으로 RAG 파이프라인과 AI 에이전트의 검색 성능을 향상시킬 수 있습니다.

Key Takeaways

  • 인프라 단순화: BM25를 Postgres에서 직접 사용하면 Elasticsearch 같은 별도 검색 클러스터, 데이터 동기화 파이프라인, 추가 운영 부담 없이 검색 품질을 크게 개선할 수 있습니다.
  • BM25의 핵심 개선사항: TF Saturation(키워드 반복 스팸 방지), IDF(희귀 단어 가중치 증가), Length Normalization(짧고 집중된 문서 우대)으로 Postgres 기본 검색의 4가지 주요 문제를 해결합니다.
  • 하이브리드 검색의 필요성: RAG와 AI 에이전트는 정확한 키워드 매칭(BM25)과 의미적 유사성(Vector Search)을 모두 필요로 하며, Reciprocal Rank Fusion으로 두 방식을 결합할 수 있습니다.
  • 실무 적용 간편성: CREATE EXTENSION pg_textsearchCREATE INDEX USING bm25 만으로 즉시 적용 가능하며, 기존 pgvector와 함께 사용하면 한 쿼리로 하이브리드 검색을 구현할 수 있습니다.
  • 99%의 사용 사례에 충분: 페타바이트급 로그 수집이 아닌 일반적인 애플리케이션 검색(문서, 제품 카탈로그, 지원 티켓 등)에는 Postgres + BM25 + Vector 조합이 충분하며 운영 복잡도를 크게 낮춥니다.

상세 내용

Postgres 검색의 현실과 복잡성의 덫

Postgres는 Stripe, Instagram, Spotify를 비롯한 수백만 개발자의 표준 데이터베이스입니다. 검색 기능 역시 모든 앱에 필수적입니다—제품 카탈로그, 문서, 사용자 콘텐츠, 지원 티켓, 그리고 최근에는 답변 생성 전에 관련 문서를 찾아야 하는 AI 에이전트와 RAG 파이프라인까지.

개발자들은 자연스럽게 Postgres로 검색을 구현하려 하지만 곧 한계에 부딪힙니다. 그러면 다음 단계는? Elasticsearch, Algolia, Typesense 같은 전문 검색 시스템 도입입니다.

그 순간부터:

  • 별도 클러스터를 운영하고 24/7 가동 상태를 유지해야 합니다
  • Postgres와 검색 시스템 간 데이터 동기화 파이프라인을 구축해야 합니다
  • 검색 결과가 오래되거나 누락된 이유를 디버깅해야 합니다
  • On-call 대응 시스템 목록에 또 하나가 추가됩니다
  • 매달 수천 달러를 매니지드 서비스에 지불하거나, 운영 전문가를 고용해야 합니다

물론 1%는 이런 복잡성이 필요합니다. 페타바이트급 로그 수집을 위한 Elasticsearch, 실시간 분석을 위한 Clickhouse, Google과 OpenAI의 맞춤형 인프라. 하지만 나머지 99%는 이런 복잡성이 필요 없습니다. 이미 사용 중인 데이터베이스에서 더 나은 검색만 있으면 됩니다.

데모 앱에서 Native Search, BM25, Vector Search, Hybrid Search를 직접 비교해볼 수 있습니다.

Postgres Native Search의 4가지 핵심 문제

검색의 주요 목표는 주어진 쿼리에 대해 가장 관련성 높고 유용한 결과를 반환하는 것입니다. 간단해 보이지만, 실제로는 전혀 그렇지 않습니다.

구체적인 예시를 위해 다음과 같은 문서들이 있다고 가정해봅시다:

📄 Database Connection Pooling Guide
"Database connection pooling improves application performance.
A pool maintains reusable connections. Configure pool size based on workload."

📄 PostgreSQL Authentication Setup
"Set up PostgreSQL database authentication methods.
Configure pg_hba.conf for password, certificate, and LDAP authentication."

📄 Generic Blog Post (스팸)
"Database database database. Learn about database. Database is important.
Database database database. More database info."

📄 EXPLAIN ANALYZE Quick Tip (15 단어)
"Use EXPLAIN ANALYZE to find slow PostgreSQL queries.
Shows execution plan and actual timing."

📄 Complete PostgreSQL Query Tuning Guide (80 단어)
"This comprehensive PostgreSQL guide covers query tuning. PostgreSQL query
performance depends on proper use of EXPLAIN and EXPLAIN ANALYZE..."

Problem 1: 키워드 스터핑이 승리한다

"database"를 검색하면, Native Postgres는 키워드 개수로 순위를 매깁니다. "database"를 12번 반복한 스팸 문서가 1위를 차지하고, 실제 유용한 가이드는 하위에 랭크됩니다.

Problem 1

Problem 2: 흔한 단어가 지배한다

"database authentication"을 검색하면, "database"는 10개 이상의 문서에 등장하고, "authentication"은 단 1개에만 등장합니다. 어떤 단어가 실제로 찾고자 하는 내용을 식별할까요?

Native Postgres는 두 단어를 동등하게 취급합니다. BM25는 "authentication"이 진짜 시그널임을 압니다.

Problem 2

Problem 3: 긴 문서가 승리한다

"EXPLAIN ANALYZE"를 검색하면, 80단어 가이드는 8번 언급하고, 15단어 팁은 2번 언급합니다. Native는 긴 문서를 더 높게 랭크합니다.

하지만 짧은 팁은 전체가 EXPLAIN ANALYZE에 관한 것입니다. 이것이 최고의 결과입니다.

Problem 3

Problem 4: All-or-Nothing 매칭

"database connection pooling"을 검색하면, Native는 Boolean AND를 사용합니다. 세 단어가 모두 있는 문서만 매치됩니다. 15개 중 2개만 결과로 나옵니다.

OR로 바꾸면? 13개 결과가 나오지만, 많은 문서가 동일한 점수를 갖습니다. 어떤 것이 실제로 관련 있는지 알 방법이 없습니다.

Problem 4

해결책: BM25 알고리즘

좋은 소식은 검색 업계가 이미 1990년대에 이 문제를 해결했다는 것입니다. 단지 Postgres에 추가되지 않았을 뿐입니다. 그것이 바로 BM25(Best Matching 25)입니다.

BM25는 Elasticsearch, Solr, Lucene 등 거의 모든 프로덕션 검색 시스템을 구동하며, 위의 문제들을 정확히 해결합니다:

Term Frequency Saturation (TF 포화) - 단어를 12번 언급한다고 해서 문서가 12배 더 관련성 있는 것은 아닙니다. 몇 번 언급 후에는 추가 반복이 거의 도움이 되지 않습니다. 스팸이 패배합니다.

Inverse Document Frequency (IDF) - 희귀한 단어가 더 중요합니다. "Database"는 어디에나 있으므로 노이즈입니다. "Authentication"은 한 번만 등장하므로 시그널입니다. BM25는 이에 따라 가중치를 부여합니다.

Length Normalization (길이 정규화) - 쿼리에 집중된 15단어 팁이 지나가듯 언급한 80단어 문서를 이깁니다. BM25는 문서 길이를 조정합니다.

Ranked Retrieval (순위 기반 검색) - 모든 문서가 의미 있는 관련성 점수를 받으며, 단순히 "매치" 또는 "매치 안 됨"이 아닙니다. 부분 매치도 나타나지만 낮은 순위로 표시됩니다.

BM25 Venn Diagram

이것이 Google이 처음부터 작동한 방식입니다. 검색의 기본 요건입니다.

Postgres에서 BM25 사용하기

pg_textsearch는 BM25를 Postgres에 도입합니다:

CREATE EXTENSION pg_textsearch;
CREATE INDEX ON articles USING bm25(content);

SELECT * FROM articles
ORDER BY content <@> to_bm25query('database performance')
LIMIT 10;

BM25 수식의 이해

BM25 점수는 다음과 같이 계산됩니다:

score(D,Q) = Σ IDF(qi) · [f(qi,D) · (k1+1)] / [f(qi,D) + k1·(1-b + b·|D|/avgdl)]

여기서:

  • f(qi,D): 문서 D에서 키워드 qi가 등장하는 횟수
  • |D|: 문서 D의 길이(단어 수)
  • avgdl: 컬렉션의 평균 문서 길이
  • k1: TF 포화를 제어하는 파라미터 (일반적으로 1.2~2.0)
  • b: 길이 정규화를 제어하는 파라미터 (0~1, 일반적으로 0.75)

AI 에이전트와 RAG를 위한 하이브리드 검색

AI 에이전트와 RAG 파이프라인도 검색이 필요합니다. 그리고 BM25만으로는 해결할 수 없는 문제가 있습니다.

사용자가 "why is my database slow?"라고 물으면, "query optimization"이나 "index tuning"과 직접적인 키워드 매치가 없습니다. BM25는 아무것도 찾지 못합니다. 에이전트가 실패합니다.

Vector Search는 의미를 이해합니다. "slow database"가 "performance optimization"과 관련되어 있다는 것을 압니다. 하지만 벡터는 반대 문제가 있습니다: 너무 퍼지(fuzzy)합니다. 에러 코드 PG-1234를 검색하면 벡터는 일반적인 에러 문서를 반환하지, 정확한 에러 코드가 있는 문서를 반환하지 않습니다.

해결책: 둘 다 사용하는 것입니다.

Query: error PG-1234

  • BM25 finds: 정확한 코드가 있는 문서
  • Vectors find: 일반적인 에러 문서
  • Hybrid finds: 정확한 코드 문서 ✓

Query: why is my database slow

  • BM25 finds: 없음 (키워드 매치 없음)
  • Vectors find: 성능 최적화 문서
  • Hybrid finds: 성능 문서 ✓

Query: fix connection timeout

  • BM25 finds: 타임아웃 설정 문서
  • Vectors find: 트러블슈팅 가이드
  • Hybrid finds: 둘 다, 관련성 순으로 정렬 ✓

이것이 모든 주요 AI 검색 시스템이 하이브리드 검색을 사용하는 이유입니다.

  • LangChain의 EnsembleRetriever는 Reciprocal Rank Fusion을 사용하여 BM25와 벡터를 결합합니다
  • Cohere Rerank는 BM25를 첫 번째 단계 리트리버로 권장합니다
  • Pinecone은 sparse와 dense 벡터를 결합하는 하이브리드 검색을 추가했습니다

Postgres에서 하이브리드 검색 구현하기

pgvector와 함께 Postgres에서도 가능합니다:

-- Reciprocal Rank Fusion을 사용한 하이브리드 검색
WITH bm25 AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY content <@> to_bm25query($1)) as rank
FROM docs LIMIT 20
),
vector AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2) as rank
FROM docs LIMIT 20
)
SELECT id, 1.0/(60+bm25.rank) + 1.0/(60+vector.rank) as score
FROM bm25 FULL JOIN vector USING (id)
ORDER BY score DESC LIMIT 10;

키워드 + 의미. 하나의 데이터베이스에서.

Hybrid Search

핵심 요점

대부분의 애플리케이션은 별도의 검색 인프라가 필요하지 않습니다. BM25와 벡터 검색을 Postgres에서 직접 사용하면:

  • 운영 복잡도 감소: 별도 클러스터, 동기화 파이프라인, 추가 모니터링 없음
  • 일관성 보장: 단일 트랜잭션 내에서 데이터와 검색 인덱스 업데이트
  • 비용 절감: 수천 달러의 매니지드 검색 서비스 비용 제거
  • 개발 속도 향상: 이미 익숙한 Postgres SQL로 검색 구현
  • RAG 최적화: BM25의 정확성과 벡터의 의미 이해를 결합

물론 페타바이트급 로그 검색이나 밀리초 단위 레이턴시가 필요한 대규모 검색 서비스는 전문 솔루션이 필요합니다. 하지만 99%의 사용 사례—일반적인 앱의 문서 검색, 제품 카탈로그, 지원 티켓, RAG 파이프라인—에는 Postgres + pg_textsearch + pgvector가 충분하며, 훨씬 단순합니다.

References

Optimizing Compound Retrieval Systems

· 약 5분
최재훈
LEAD (AI Research Engineer), Brain Crew

TL;DR

Compound Retrieval Systems는 여러 검색 방법을 결합하여 성능을 향상시키는 시스템이지만, 각 구성 요소의 최적 조합을 찾는 것은 복잡한 문제입니다. 본 논문은 compound retrieval의 성능을 체계적으로 최적화하는 방법론을 제시하며, sparse retrieval, dense retrieval, reranking 등 다양한 구성 요소 간의 상호작용을 분석합니다. 실험 결과, 단순히 더 많은 구성 요소를 추가하는 것이 아니라 적절한 조합과 파라미터 튜닝이 성능 향상의 핵심임을 보여줍니다.

Key Takeaways

  • 구성 요소의 다양성보다 최적 조합이 중요: 더 많은 retrieval 방법을 추가한다고 해서 반드시 성능이 향상되는 것은 아니며, 각 구성 요소 간의 시너지를 고려한 선택이 필수적입니다.
  • Reranking의 전략적 활용: Reranking 모델의 위치와 적용 범위가 전체 시스템 성능에 큰 영향을 미치므로, computational budget과 latency 요구사항에 따라 신중히 설계해야 합니다.
  • 체계적인 하이퍼파라미터 최적화: Retrieval pipeline의 각 단계별 파라미터(top-k, fusion weights 등)를 독립적이 아닌 상호의존적으로 튜닝해야 최적 성능을 달성할 수 있습니다.
  • 도메인별 맞춤 설계 필요: 데이터셋과 쿼리 특성에 따라 최적의 compound retrieval 구성이 크게 달라지므로, 범용 솔루션보다는 도메인 특화 최적화가 효과적입니다.
  • 비용-성능 트레이드오프 분석: 성능 향상의 한계 비용(marginal cost)을 정량적으로 측정하여, 프로덕션 환경에서 실용적인 시스템을 구축할 수 있습니다.

상세 내용

Compound Retrieval Systems의 개요

현대의 정보 검색 시스템은 단일 검색 방법에 의존하지 않고, 여러 retrieval 기법을 조합하는 compound 접근법을 채택하고 있습니다. 전통적인 sparse retrieval(BM25 등), neural dense retrieval(bi-encoder 기반), 그리고 cross-encoder를 활용한 reranking을 결합함으로써 각 방법의 장점을 극대화하고 단점을 보완할 수 있습니다.

그러나 이러한 시스템의 설계 공간은 방대합니다. 어떤 구성 요소를 선택할지, 각 단계에서 몇 개의 문서를 유지할지, 결과를 어떻게 융합할지 등 수많은 결정 사항이 존재하며, 이들의 조합은 기하급수적으로 증가합니다.

최적화 프레임워크

본 논문에서 제시하는 최적화 프레임워크는 다음과 같은 핵심 요소들을 고려합니다:

1. 구성 요소 선택 (Component Selection)

Compound retrieval system의 첫 번째 단계는 사용할 retrieval 방법들을 선택하는 것입니다. 주요 옵션은:

  • Sparse retrievers (BM25, SPLADE 등)
  • Dense retrievers (DPR, Contriever, ColBERT 등)
  • Rerankers (Cross-encoder, monoBERT 등)

각 구성 요소는 서로 다른 특성을 가지며, 일부는 lexical matching에 강하고, 다른 일부는 semantic similarity 포착에 우수합니다. 핵심은 이들 간의 complementarity(상보성)를 최대화하는 것입니다.

2. 파이프라인 구조 설계

검색 파이프라인의 구조는 성능과 효율성에 직접적인 영향을 미칩니다:

  • Sequential cascading: 첫 단계에서 대량의 문서를 필터링하고, 이후 단계에서 정밀하게 재정렬
  • Parallel fusion: 여러 retriever를 독립적으로 실행하고 결과를 융합
  • Hybrid approaches: 위 두 방식의 조합

3. 하이퍼파라미터 튜닝

각 단계별로 최적화해야 할 주요 파라미터들:

  • Top-k values: 각 retrieval 단계에서 유지할 문서 수
  • Fusion weights: 여러 retriever의 결과를 결합할 때 각각에 부여할 가중치
  • Reranking depth: Reranker가 처리할 후보 문서의 수

이러한 파라미터들은 서로 독립적이지 않으며, 한 파라미터의 최적값이 다른 파라미터의 설정에 따라 변할 수 있습니다.

실험 결과 및 인사이트

논문에서 수행한 광범위한 실험은 여러 중요한 발견을 제공합니다:

성능 포화 현상 (Performance Saturation)

일정 수준 이상으로 구성 요소를 추가하거나 파라미터를 증가시켜도 성능 향상이 미미해지는 지점이 존재합니다. 예를 들어, reranking depth를 100에서 1000으로 늘려도 성능 개선이 1% 미만인 경우가 많았습니다. 이는 프로덕션 환경에서 computational budget을 효율적으로 할당하는 데 중요한 시사점을 제공합니다.

Retriever 다양성의 중요성

단순히 많은 retriever를 사용하는 것보다, 서로 다른 특성을 가진 retriever를 조합하는 것이 더 효과적입니다. Lexical과 semantic retrieval을 결합하면 각각을 독립적으로 사용할 때보다 상당한 성능 향상을 보이지만, 유사한 특성의 dense retriever를 여러 개 추가하는 것은 제한적인 이득만을 제공합니다.

Reranking의 전략적 배치

Reranking 모델을 언제, 어디에 배치하느냐가 시스템 전체의 효율성을 결정합니다. 초기 단계에 너무 일찍 reranking을 적용하면 computational cost가 급증하고, 너무 늦게 적용하면 이미 관련 문서들이 필터링된 후일 수 있습니다. 실험 결과, 중간 규모(top-100~500)의 후보 집합에 reranking을 적용하는 것이 가장 효과적이었습니다.

최적화 알고리즘

논문은 compound retrieval system을 최적화하기 위한 체계적인 접근법을 제안합니다:

1. Grid Search with Early Stopping

모든 가능한 조합을 탐색하는 것은 비현실적이므로, 중요한 파라미터에 집중한 coarse-to-fine grid search를 수행합니다. 성능 개선이 정체되면 해당 방향의 탐색을 중단하여 효율성을 높입니다.

2. Bayesian Optimization

파라미터 공간이 연속적이거나 상호작용이 복잡한 경우, Bayesian optimization을 활용하여 적은 iteration으로 최적점에 근접할 수 있습니다.

3. Multi-objective Optimization

실무에서는 성능뿐만 아니라 latency, computational cost, memory usage 등 여러 목표를 동시에 고려해야 합니다. Pareto frontier를 활용하여 trade-off를 명확히 하고, 요구사항에 맞는 설정을 선택할 수 있습니다.

실무 적용 가이드라인

도메인 특화 최적화

일반적인 benchmark(MS MARCO, Natural Questions 등)에서 우수한 성능을 보인 설정이 특정 도메인(법률, 의료, 기술 문서 등)에서는 최적이 아닐 수 있습니다. 따라서:

  • 타겟 도메인의 대표적인 쿼리 셋으로 validation을 수행
  • 도메인 특화 retriever fine-tuning 고려
  • 쿼리 길이, 문서 길이 등 데이터 특성에 맞춰 파라미터 조정

점진적 개선 전략

기존 시스템을 한 번에 교체하기보다는:

  1. 현재 시스템을 baseline으로 설정하고 성능 측정
  2. 가장 큰 bottleneck 식별 (낮은 recall, 부정확한 ranking 등)
  3. 해당 문제를 해결할 구성 요소를 우선적으로 추가/개선
  4. A/B 테스트를 통해 실제 사용자 만족도 검증

모니터링 및 지속적 최적화

프로덕션 환경에서는:

  • 쿼리 분포의 변화 모니터링
  • 구성 요소별 latency 및 성능 기여도 추적
  • 정기적인 재최적화 스케줄 수립

한계점 및 향후 연구 방향

논문은 다음과 같은 한계점을 인정하고 향후 연구 방향을 제시합니다:

  • 동적 최적화: 현재 프레임워크는 정적 설정을 가정하지만, 쿼리 특성에 따라 동적으로 파이프라인을 조정하는 방법 연구 필요
  • 자동화: 최적화 과정의 자동화 수준을 높여 전문 지식 없이도 효과적인 시스템 구축 가능하도록 개선
  • 신규 구성 요소 통합: GPT-4 등 LLM 기반 reranking, generative retrieval 등 새로운 기법들을 프레임워크에 통합하는 방법 탐구

References