필터링 방식 (pre-filtering·ACORN)

필터링 방식 (pre-filtering·ACORN)

벡터 검색에 구조화된 스칼라 조건을 섞고 싶을 때 Weaviate는 강력한 필터링 검색을 제공해요. 질의 벡터에 가장 가까운 벡터이면서도 특정 조건을 만족하는 객체를 찾는 것이죠. 핵심은 필터를 벡터 검색보다 먼저 적용하는 pre-filtering(사전 필터링) 방식이에요.

출처: Weaviate 공식 문서 - Filtering

본문

post-filtering vs pre-filtering

필터를 벡터 검색 에 적용하는 post-filtering(후 필터링)은 두 가지 단점이 있어요. 먼저 필터가 이미 줄어든 후보 목록에 적용되니 결과에 몇 개가 들어올지 예측하기 어렵고, 필터가 매우 제한적일 때(데이터셋의 작은 비율만 매칭) 원래 벡터 검색 결과에 매칭이 하나도 없을 수도 있어요.

pre-filtering은 벡터 검색을 시작하기 전에 조건에 맞는 후보를 정해 "허용 목록(allow-list)"을 만들고, 벡터 검색은 그 목록에 있는 후보만 고려해요. Weaviate는 이 과정에서도 brute-force(전수 검색)를 쓰지 않기 때문에 효율적이에요. 역색인과 HNSW 인덱스를 함께 쓰는 덕분이죠.

효율적인 pre-filtered 검색

샤드마다 역색인(inverted index)과 HNSW 인덱스가 나란히 있기 때문에 pre-filtering이 효율적으로 동작해요.

  1. 역색인(전통적인 검색 엔진과 비슷한 구조)으로 조건에 맞는 후보의 허용 목록을 만들어요. 이 목록은 사실상 uint64 id 리스트라서 효율을 잃지 않고 아주 커질 수 있어요.
  2. 허용 목록을 HNSW 인덱스에 넘겨 벡터 검색을 수행해요. 그래프의 어떤 노드의 엣지를 따라서 가더라도 허용 목록에 있는 id만 결과에 추가하죠. 종료 조건은 필터 없는 검색과 동일해요 — 원하는 개수에 도달하고 추가 후보가 결과 품질을 높이지 않으면 멈춰요.

필터 전략

HNSW 인덱스 타입에 대해 두 가지 필터 전략을 지원해요.

  • ACORN — v1.34부터 기본값이에요. 필터 조건을 충족하지 못하는 객체는 거리 계산에서 제외하고, 다중 홉 접근으로 관련 영역에 더 빨리 도달하며, 필터에 맞는 추가 진입점을 무작위로 심어 수렴을 빠르게 해요. 필터가 질의 벡터와 상관이 낮을 때, 즉 질의 벡터와 가장 비슷한 그래프 영역의 많은 객체를 필터가 제외할 때 특히 큰 데이터셋에서 눈에 띄게 빨라져요.
  • Sweeping — 루트 노드에서 시작해 그래프를 훑으며 각 노드의 질의 벡터 거리를 계산하되, 필터의 허용 목록을 계속 참고해요. 필터에 맞지 않으면 그 노드를 건너뛰고 탐색을 계속하죠. 컬렉션 설정에서 HNSW 벡터 인덱스의 filterStrategy 필드로 켤 수 있어요.

indexFilterable / indexRangeFilters

  • indexFilterable은 Roaring Bitmap으로 매칭 기반 필터링을 빠르게 만들어요. 데이터를 청크로 나눠 각각에 맞는 저장 전략을 적용해서 고압축·빠른 집합 연산이 가능해요. 큰 데이터셋을 다룬다면 이 인덱스를 쓰는 편이 필터링 속도를 크게 높여줘요.
  • indexRangeFilters는 int·number·date 프로퍼티의 숫자 범위 필터링용 인덱스예요. 내부적으로 Roaring Bitmap 슬라이스로 구현되기 때문에 64비트 정수로 저장할 수 있는 값만 지원해요. 새로 만드는 프로퍼티에서만 쓸 수 있고, 기존 프로퍼티는 이 범위 인덱스로 바꿀 수 없어요.

recall과 flat-search cutoff

Weaviate의 HNSW 구현은 그래프 링크를 정상적으로 모두 따라가다가 결과 집합을 고려할 때만 필터 조건을 적용하기 때문에 그래프 무결성이 유지되고, 필터링된 검색의 recall(재현율)은 필터 없는 검색보다 나빠지지 않아요.

필터가 매우 제한적이 되면 HNSW 탐색이 사실상 전수 검색과 비슷해져요. 그럴 때는 필터로 이미 좁혀진 부분집합만 브루트포스로 검색하는 편이 전체 데이터셋을 도는 것보다 훨씬 효율적이죠. 이 전환 지점(컷오프)은 기본적으로 데이터셋 크기의 약 15%에서 동작하고, 컬렉션별로 vectorIndexConfig 설정에서 조절할 수 있어요.

더 알아보기