벡터 데이터베이스 — pgvector 와 Qdrant · ANN 인덱스와 재현율 · 퀴즈
퀴즈: ANN 인덱스와 재현율
문항 8개. 정답과 해설은 풀어 본 뒤에 보여 드립니다.
recall@10 은 무엇을 뜻하나?
- 10개 질의 중 첫 결과가 정답 범주였던 비율
- 인덱스가 돌려준 10건 중 정확 검색의 상위 10건에 들어 있는 비율을 질의들에 대해 평균한 값
- 인덱스 검색이 정확 검색보다 빨랐던 질의의 비율
- 상위 10건의 코사인 유사도 평균을 1 에 대한 비율로 나타낸 값
HNSW 의 `hnsw.ef_search` 를 올리면 무슨 일이 생기나?
- 인덱스가 다시 만들어져 디스크 크기가 커진다
- m 이 함께 커져 그래프의 간선 수가 늘어난다
- 군집을 더 많이 뒤지므로 IVFFlat 의 probes 와 같은 효과다
- 검색할 때 살펴보는 후보가 늘어 재현율이 오르고 그만큼 느려진다
IVFFlat 에서 `ivfflat.probes` 를 `lists` 와 같게 두면?
- 모든 군집을 뒤지므로 정확 검색과 같은 결과가 나오고 속도 이점이 사라진다
- 군집 중심만 비교하므로 가장 빠르지만 재현율은 최저가 된다
- 플래너가 IVFFlat 을 버리고 순차 탐색으로 바꿔 탄다
- lists 를 넘는 값이라 오류가 나고 질의가 실패한다
`select ... from articles where embedding <=> q < 0.3` 이 인덱스를 타지 않는 이유는?
- 0.3 이 너무 작아 후보가 없어 인덱스가 빈 결과를 돌려주기 때문
- 거리 조건은 HNSW 가 아니라 IVFFlat 만 지원하기 때문
- pgvector 인덱스는 order by 거리 limit k 꼴에서만 타고, 이 조건은 순차 탐색으로 떨어지기 때문
- 비교 연산이 부동소수라 인덱스가 정렬 순서를 보장하지 못하기 때문
HNSW 검색에 `where lang = 'en'` 을 붙였더니 `limit 10` 인데 3건만 온다. 이유는?
- where 열에 B-tree 가 없으면 플래너가 벡터 인덱스를 포기하기 때문
- 인덱스가 조건을 모른 채 ef_search 개의 후보만 내놓고 그 뒤에 where 가 걸리기 때문
- HNSW 그래프가 조건에 맞지 않는 노드를 지워 버리기 때문
- limit 이 필터보다 먼저 적용되는 PostgreSQL 의 실행 순서 때문
서브쿼리로 `limit 200` 을 먼저 뽑고 필터를 거는 over-fetch 가 pgvector 0.6 에서 그대로는 안 통하는 이유는?
- 서브쿼리 안에서는 벡터 연산자를 쓸 수 없기 때문
- limit 200 은 정렬을 두 번 하게 해 결과 순서가 뒤바뀌기 때문
- 부분 인덱스가 없으면 서브쿼리가 순차 탐색으로 실행되기 때문
- 인덱스 스캔은 ef_search 개 이상을 내놓지 않으므로 ef_search 도 함께 올려야 하기 때문
RRF 가 벡터 검색과 전문 검색을 점수 정규화 없이 합칠 수 있는 이유는?
- 각 목록의 순위만 써서 1/(k+순위) 를 더하므로 점수 단위가 달라도 상관없기 때문
- 두 검색의 점수를 0~1 로 나눈 뒤 평균하므로 단위가 사라지기 때문
- 전문 검색 점수를 코사인 거리로 바꾸는 함수가 내장돼 있기 때문
- 벡터 검색 결과를 먼저 뽑고 그 안에서만 낱말을 찾기 때문
목표 재현율을 만족하는 `ef_search` 를 찾았다. 운영에서 어디에 거는 것이 맞나?
- 인덱스를 만들 때 with (ef_search = N) 으로 인덱스에 굽는다
- postgresql.conf 는 못 고치므로 애플리케이션 코드마다 set 을 넣는다
- alter database ... set 으로 기본값을 못박아 세션마다 set 을 빠뜨리는 일을 없앤다
- recall.py 의 기본값을 고쳐 두면 서버도 그 값을 읽는다