임베딩으로 텍스트를 벡터로 바꾸고 나면, "질문 벡터와 가장 가까운 문서 벡터"를 찾아야 한다. 이 글은 그 검색을 담당하는 알고리즘을 kNN(개념) → 왜 느린가 → ANN → HNSW(실전 표준) 순서로 정리한다. - kNN(k-Nearest Neighbors) = 어떤 벡터(쿼리)와 가장 가까운 k개의 이웃 벡터를 찾는 알고리즘 - 예를 들어 - 문서...
임베딩으로 텍스트를 벡터로 바꾸고 나면, "질문 벡터와 가장 가까운 문서 벡터"를 찾아야 한다.
이 글은 그 검색을 담당하는 알고리즘을 kNN(개념) → 왜 느린가 → ANN → HNSW(실전 표준) 순서로 정리한다.
어떤 거리를 쓰는지는 임베딩 모델이 권장하는 것을 따르는 게 안전하다 — 모델이 그 거리로 학습됐기 때문이다.
코사인은 사실 내적으로 계산된다.
cos = (A·B) / (|A|·|B|)인데, 저장할 때 벡터를 L2 정규화(크기를 1로)하면|A|=|B|=1이라 코사인이 그냥 내적(A·B)이 된다. 그래서 대부분의 벡터 DB가 정규화 후 빠른 내적으로 코사인을 처리한다(OpenSearchcosinesimil, faiss cosine). HNSW는 유사도가 아니라 거리로 다루므로1 − 코사인으로 변환해 쓴다.
정직하게 kNN을 하려면 쿼리 벡터와 모든 문서 벡터의 거리를 다 계산해야 한다.
이걸 brute-force kNN이라고 한다. 차원이 높고 데이터가 많으면 실시간 서비스가 불가능하다. 그래서 "정확함을 조금 포기하고 훨씬 빠르게" 찾는 기술이 필요해진다.
ANN(Approximate Nearest Neighbor)은 정확한 이웃을 찾는 대신 이런 목표를 갖는다.
대표 ANN 알고리즘으로 HNSW, FAISS의 IVF+PQ, ScaNN, Annoy 등이 있다. 이 중 벡터 DB·검색 엔진이 사실상 표준으로 채택한 것이 HNSW다.
HNSW(Hierarchical Navigable Small World graph)는 이름에 구조가 다 들어 있다.
이 구조 덕에 데이터가 늘어도 속도 저하가 적고, 검색 속도가 O(log N)에 가까워진다.
한 줄로 줄이면 — "kNN을 실시간에 쓸 수 있게 만든 알고리즘이 HNSW"다.
ef_search=200 기준 97~99%.HNSW는 튜닝이 직관적이다.
M — 그래프의 branching factor(노드당 연결 수)ef_construction — 인덱스 빌드 시 정확도ef_search — 검색 시 품질특히 ef_search는 속도 ↔ 정확도 슬라이더처럼 쓴다.
운영 중에 품질과 속도를 조절하기 쉽다.
파라미터 상호작용(M↔ef_construction), 구현체별(FAISS·Lucene·Qdrant·pgvector·Milvus) 성능 차이, 필터링 충돌 같은 심화 주제는 HNSW 심화 — 파라미터 튜닝과 구현체별 성능 차이에서 다룬다.
HNSW가 빠른 대가는 메모리다. 세 가지가 올라간다.
예를 들어 1천만 벡터에 1024차원이면 벡터만 약 40GB다. "벡터를 메모리에 올린다"가 곧 HNSW의 비용이다.
즉 "HNSW = 무조건 풀 메모리"가 아니라, 규모와 트래픽에 따라 일부를 디스크로 내릴 수 있다.
OpenSearch에서 vector 인덱스를 만들 때 이렇게 설정한다.
{
"method": {
"name": "hnsw",
"engine": "faiss",
"space_type": "cosinesimil"
}
}name: hnsw → ANN 기반 그래프 사용engine: faiss → Facebook FAISS 엔진(가장 빠른 축)space_type: cosinesimil → 코사인 유사도로 계산즉 OpenSearch는 내부적으로 kNN을 ANN(HNSW) 방식으로 최적화한 그래프 탐색으로 처리한다.
OpenSearch 를 벡터 DB 로 운영하는 실전(native 메모리·샤드)은 OpenSearch를 벡터 DB로 굴리며 알게 된 것, 검색 품질(hybrid·rerank)은 OpenSearch로 RAG 검색 품질 높이기에서 다룬다.
ef_search로 속도와 정확도를 조절한다.