브렌쏭의 Veritas_Garage

[AI번역] 엘라스틱서치의 벡터 검색: 설계 이면의 논리 본문

[Project_하다]/[Project_공부]

[AI번역] 엘라스틱서치의 벡터 검색: 설계 이면의 논리

브렌쏭 2024. 8. 22. 10:46

엘라스틱서치의 벡터 검색: 설계 이면의 논리

ElasticSearch가 벡터 검색을 지원하는 방법

 

Vector search in Elasticsearch: The rationale behind the design

There are different ways to implement a vector database, which have different trade-offs. In this blog, you'll learn more about how vector search has been integrated into Elastisearch and the trade-of...

www.elastic.co

 

 

작성자: Adrien Grand

2023년 7월 5일

엘라스틱서치의 벡터 검색의 특성과 설계가 어떻게 이루어졌는지 궁금하신가요? 항상 그렇듯이 설계 결정에는 장단점이 따릅니다. 이 블로그는 엘라스틱서치에서 벡터 검색을 구축한 방법을 설명하는 것을 목표로 합니다.

벡터 검색은 아파치 루씬(Apache Lucene)을 통해 엘라스틱서치에 통합됩니다

루씬에 대한 배경부터 설명하자면, 루씬은 데이터를 불변의 세그먼트로 구성하며, 이 세그먼트는 주기적으로 병합됩니다. 더 많은 문서를 추가하려면 더 많은 세그먼트를 추가해야 합니다. 기존 문서를 수정하려면 새로운 세그먼트를 원자적으로 추가하고 이전 버전의 문서를 삭제된 것으로 표시해야 합니다. 세그먼트 내의 각 문서는 배열의 인덱스와 유사한 문서 ID로 식별됩니다. 이러한 접근 방식을 사용하는 이유는 인덱스를 관리하는 데 있습니다. 인덱스는 제자리에서 수정하는 데는 적합하지 않지만, 효율적으로 병합할 수 있습니다.

루씬은 역색인 외에도 세그먼트 내에 저장된 필드(문서 저장소), 문서 값(컬럼형 스토리지), 용어 벡터(문서별 역색인), 다차원 포인트를 지원합니다. 벡터도 동일한 방식으로 통합됩니다:

  • 새로운 벡터는 인덱스 시점에 메모리에 버퍼링됩니다.
  • 이러한 메모리 내 버퍼는 인덱스 시점 버퍼의 크기가 초과되거나 변경 사항을 표시해야 할 때 세그먼트의 일부로 직렬화됩니다.
  • 세그먼트는 전체 세그먼트 수를 제어하고 세그먼트별 검색 시점의 오버헤드를 제한하기 위해 백그라운드에서 주기적으로 병합됩니다. 세그먼트의 일부이므로 벡터도 병합되어야 합니다.
  • 검색은 인덱스의 모든 세그먼트에서 상위 벡터 히트를 결합해야 합니다.
  • 벡터 검색은 삭제된 문서로 표시된 문서를 제외하기 위해 라이브 문서 집합을 살펴보아야 합니다.
  • 위의 시스템은 루씬의 작동 방식에 의해 구동됩니다.

루씬은 현재 벡터를 인덱싱하기 위해 계층적 탐색 가능한 소형 세계(HNSW) 알고리즘을 사용합니다. 높은 수준에서 HNSW는 유사한 벡터가 연결될 가능성이 높은 그래프로 벡터를 구성합니다. HNSW는 비교적 간단하고 벡터 검색 알고리즘에 대한 벤치마크에서 성능이 우수하며, 증분 삽입을 지원하기 때문에 벡터 검색에 인기 있는 선택입니다. 루씬의 HNSW 구현은 데이터를 디스크에 유지하고 페이지 캐시를 활용하여 자주 액세스되는 데이터에 대한 액세스 속도를 높이는 루씬의 지침을 따릅니다.

근사 벡터 검색은 엘라스틱서치의 _search API에서 knn 섹션을 통해 노출됩니다. 이 기능을 사용하면 루씬의 벡터 검색 기능을 직접 활용할 수 있습니다. 벡터는 엘라스틱서치의 스크립팅 API에도 통합되어 있어 정확한 브루트포스 검색을 수행하거나 재점수화(rescoring)를 위해 벡터를 활용할 수 있습니다.

이제 아파치 루씬을 통해 벡터 검색을 통합하는 것의 장단점을 살펴보겠습니다.

단점

아파치 루씬을 활용하여 벡터 검색을 수행하는 것의 주요 단점은 루씬이 벡터를 세그먼트에 묶는다는 점에서 비롯됩니다. 그러나 장점 섹션에서 보게 되듯이, 벡터를 세그먼트에 묶는 것은 효율적인 사전 필터링, 효율적인 하이브리드 검색, 가시성 일관성 등과 같은 주요 기능을 가능하게 합니다.

  • 병합 시 HNSW 그래프를 다시 계산해야 함: 세그먼트 병합 시 일반적으로 기본 병합 정책에 따라 N개의 입력 세그먼트를 하나의 세그먼트로 병합해야 합니다. 루씬은 현재 삭제되지 않은 가장 큰 입력 세그먼트에서 HNSW 그래프의 복사본을 생성한 다음 다른 세그먼트에서 벡터를 가져와 이 HNSW 그래프에 추가합니다. 이 접근 방식은 인덱스 수명 동안 단일 HNSW 그래프를 제자리에서 변경하는 것과 비교할 때 세그먼트가 병합되는 동안 인덱스 시점의 오버헤드를 초래합니다.
  • 검색 시 여러 세그먼트에서 결과를 결합해야 함: 인덱스가 여러 세그먼트로 구성되어 있기 때문에, 검색은 각 세그먼트에서 상위 k개의 벡터를 계산한 다음 이 세그먼트별 상위 k개의 히트를 글로벌 상위 k개의 히트로 병합해야 합니다. 이 접근 방식은 세그먼트를 병렬로 검색하여 지연 시간에 미치는 영향을 줄일 수 있지만, 단일 HNSW 그래프를 검색하는 것보다 여전히 오버헤드가 발생합니다.
  • 데이터 세트 크기에 맞게 RAM을 확장해야 최적의 성능 유지: HNSW 그래프를 탐색하려면 많은 랜덤 액세스가 필요합니다. 효율적으로 수행하려면 데이터 세트를 페이지 캐시에 맞춰야 하며, 이는 관리되는 벡터 데이터 세트 크기에 따라 RAM 크기를 조정해야 합니다. 디스크 친화적인 액세스 패턴을 가진 HNSW 외의 다른 벡터 검색 알고리즘도 존재하지만, 이들은 더 높은 쿼리 지연 시간이나 더 낮은 회수율과 같은 다른 단점을 가지고 있습니다.

장점

  • RAM 크기를 초과하는 데이터 세트 확장 가능: 데이터가 디스크에 저장되므로 엘라스틱서치는 로컬 호스트에서 사용할 수 있는 총 RAM 크기를 초과하는 데이터 세트를 허용합니다. 페이지 캐시에 맞출 수 있는 HNSW 데이터 비율이 줄어들수록 성능이 저하됩니다. 이전 섹션에서 설명한 대로, 성능에 민감한 사용자는 데이터 세트 크기에 맞춰 RAM 크기를 조정해야 최적의 성능을 유지할 수 있습니다.
  • 잠금 없는 검색: 데이터를 제자리에서 업데이트하는 시스템은 일반적으로 동시 인덱싱 및 검색 시 스레드 안전성을 보장하기 위해 잠금을 사용해야 합니다. 루씬의 세그먼트 기반 인덱스는 검색 시, 심지어 동시 인덱싱의 경우에도 잠금을 필요로 하지 않습니다. 대신 인덱스를 구성하는 세그먼트 집합은 정기적으로 원자적으로 업데이트됩니다.
  • 증분 변경 지원: 언제든지 새로운 벡터를 추가, 제거 또는 업데이트할 수 있습니다. 다른 일부 근사 최근접 검색 알고리즘은 전체 벡터 데이터 세트를 제공받아야 합니다. 그런 다음 모든 벡터가 제공되면 인덱스 학습 단계가 실행됩니다. 이러한 다른 알고리즘의 경우, 벡터 데이터 세트에 대한 중요한 업데이트는 학습 단계를 다시 완료해야 하며, 이는 계산적으로 비용이 많이 들 수 있습니다.
  • 다른 데이터 구조와의 가시성 일관성: 루씬에 낮은 수준으로 통합함으로써 인덱스의 시점 보기에서 다른 데이터 구조와 일관성을 자동으로 얻을 수 있습니다. 문서를 업데이트하여 벡터와 다른 키워드 필드를 업데이트하는 경우, 동시 검색에서는 업데이트 이전에 생성된 시점 보기에서는 벡터 필드의 이전 값과 키워드 필드의 이전 값을 보거나, 업데이트 후에 생성된 시점 보기에서는 벡터 필드와 키워드 필드의 새 값을 보게 됩니다. 마찬가지로, 문서가 삭제된 것으로 표시되면, 벡터 저장소를 포함한 모든 데이터 구조는 이를 무시하거나, 삭제 전에 생성된 시점 보기를 운영하는 경우 이를 보게 됩니다.
  • 증분 스냅샷: 벡터가 세그먼트의 일부라는 사실은 스냅샷이 증분적으로 유지되도록 도와줍니다. 이는 두 개의 연속적인 스냅샷이 특히 더 큰 세그먼트에서 대부분의 세그먼트를 공유한다는 사실을 활용합니다. 단일 HNSW 그래프를 제자리에서 변경하는 방식으로는 증분 스냅샷이 불가능할 것입니다.
  • 필터링 및 하이브리드 지원: 루씬에 직접 통합함으로써 임의의 루씬 필터로

벡터 검색을 사전 필터링하거나, 벡터 쿼리에서 발생한 히트를 전통적인 전체 텍스트 쿼리의 히트와 결합하는 등의 기능을 효율적으로 통합할 수 있습니다.

  • 다른 기능과의 호환성: 벡터 저장소는 다른 루씬 데이터 구조와 유사하기 때문에, 벡터와 벡터 검색은 자동으로 다음과 같은 많은 기능과 호환됩니다:
    • 집계
    • 문서 수준 보안
    • 필드 수준 보안
    • 인덱스 정렬
    • 스크립트를 통한 벡터 접근(예: script_score 쿼리 또는 재순위 지정에서)

전망: 인덱싱과 검색의 분리

다른 블로그에서 논의된 대로, 엘라스틱서치의 미래 버전은 인덱싱과 검색 작업을 다른 인스턴스에서 실행할 것입니다. 구현은 기본적으로 인덱싱 노드에서 지속적으로 스냅샷을 생성하고 검색 노드에서 이를 복원하는 것처럼 보일 것입니다. 이는 벡터 인덱싱의 높은 비용이 검색에 영향을 미치는 것을 방지하는 데 도움이 될 것입니다. 인덱싱과 검색을 분리하는 것은 여러 세그먼트가 아닌 단일 공유 HNSW 그래프로는 불가능할 것입니다.

결론

일반적으로 엘라스틱서치는 벡터 검색 기능을 다른 엘라스틱서치 기능과 통합하여 우수한 벡터 검색 기능을 제공합니다:

  • 벡터 검색은 지원되는 필터 중 가장 정교한 필터로 사전 필터링할 수 있습니다.
  • 벡터 히트는 임의의 쿼리 히트와 결합할 수 있습니다.
  • 벡터 검색은 집계, 문서 수준 보안, 필드 수준 보안, 인덱스 정렬 등과 호환됩니다.
  • 벡터를 포함하는 인덱스는 _refresh, _flush, _snapshot API에 대해 다른 인덱스와 동일한 의미를 유지합니다. 또한 상태 비저장 엘라스틱서치에서 인덱싱과 검색의 분리를 지원할 것입니다.

'[Project_하다] > [Project_공부]' 카테고리의 다른 글

[RAG] RAG와 ELK  (0) 2024.08.26
[Python] Django Basics  (0) 2024.08.26
[AI번역] SIMD 명령어로 벡터 검색 가속화  (0) 2024.08.22
[ELK] Logstash  (0) 2024.08.16
[ELK] Elastic Search  (0) 2024.08.12
[RAG] LangChain :: LLM 도화지  (0) 2024.08.11
[LLM_AI] "Bleeding Edge" Prompt Engineering  (0) 2024.08.11
Comments