<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Mayya Sharipova - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Mayya Sharipova - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/kr/search-labs/author/mayya-sharipova</link>
    </image>
    <link>https://www.elastic.co/kr/search-labs/author/mayya-sharipova</link>
    <atom:link href="https://www.elastic.co/kr/search-labs/rss/author/mayya-sharipova.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[kr]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 13:34:59 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Elasticsearch에서 NVIDIA cuVS를 활용하여 벡터 색인화 속도 최대 12배 향상: GPU 가속화 챕터 2]]></title>
    <description><![CDATA[Elasticsearch가 GPU 가속 벡터 색인화와 NVIDIA cuVS로 어떻게 거의 12배 더 높은 색인화 처리량을 달성하는지 알아보세요.]]></description>
    <content:encoded><![CDATA[<p>올해 초 Elastic은 NVIDIA와의 <a href="https://ir.elastic.co/news/news-details/2025/Elastic-Brings-Enterprise-Data-to-NVIDIA-AI-Factories/default.aspx">협업</a>을 통해 Elasticsearch에 GPU 가속화를 도입하여 <a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>와 통합한다고 발표했으며, <a href="https://www.nvidia.com/en-us/on-demand/session/gtc25-S71286/">NVIDIA GTC의 세션</a>과 다양한 <a href="https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia">블로그</a>에 자세히 설명되어 있습니다. 이 게시물은 NVIDIA 벡터 검색 팀과의 공동 엔지니어링 활동에 대한 업데이트입니다.</p><h2>요약</h2><p>먼저 현황을 간략하게 설명하겠습니다. Elasticsearch는 강력한 벡터 데이터베이스로서, 풍부한 기능과 대규모 유사성 검색을 위한 뛰어난 성능을 제공하며 자리매김했습니다. 스칼라 양자화, Better Binary Quantization(<a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">BBQ</a>), <a href="https://www.elastic.co/blog/accelerating-vector-search-simd-instructions">SIMD</a> 벡터 연산, 그리고 <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a>와 같은 디스크 효율적인 알고리즘을 통해 벡터 워크로드를 효율적이고 유연하게 관리할 수 있는 다양한 옵션을 제공합니다.</p><p>NVIDIA cuVS를 벡터 검색 작업을 위한 호출 가능한 모듈로 통합함으로써 벡터 색인화 성능과 효율성을 크게 향상해 대규모 벡터 워크로드를 보다 효과적으로 지원하는 것을 목표로 합니다.</p><h2>당면 과제</h2><p>고성능 벡터 데이터베이스를 구축하는 데 있어 매우 어려운 과제 중 하나는 벡터 인덱스, 즉 <a href="https://arxiv.org/abs/1603.09320">HNSW</a> 그래프를 구성하는 것입니다. 인덱스 구축은 각 벡터가 다른 수많은 벡터와 비교되기 때문에 수백만, 심지어 수십억 개의 산술 연산으로 빠르게 진행됩니다. 또한 압축 및 병합과 같은 인덱스 수명 주기 작업은 색인화의 전체 컴퓨팅 오버헤드를 더 증가시킬 수 있습니다. 데이터 볼륨과 관련 벡터 임베딩이 기하급수적으로 증가함에 따라 대규모 병렬 처리와 높은 처리량의 연산을 위해 구축된 가속 컴퓨팅 GPU는 이러한 워크로드를 처리하는 데 최적의 위치에 있습니다.</p><h2>Elasticsearch-GPU 플러그인 입력</h2><p><a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>는 GPU 가속 벡터 검색 및 데이터 클러스터링을 위한 오픈 소스 CUDA-X 라이브러리로, AI 및 추천 워크로드를 위한 빠른 인덱스 구축 및 임베딩 검색을 가능하게 합니다.</p><p>Elasticsearch는 커뮤니티가 개발하고 NVIDIA가 관리하는 오픈 소스 라이브러리인 <a href="https://mvnrepository.com/artifact/com.nvidia.cuvs/cuvs-java">cuvs-java</a>를 통해 cuVS를 사용합니다. cuvs-java 라이브러리는 경량이며, <a href="https://docs.rapids.ai/api/cuvs/nightly/c_api/">cuVS C API</a>를 기반으로 <a href="https://openjdk.org/projects/panama/">Panama</a> Foreign Function을 사용하여 cuVS 기능을 관용적인 Java 방식으로 노출하면서도 최신 기술과 뛰어난 성능을 유지합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7fd7361099da05a/6a17e920be608670af00477f/5f6daa1eb07f704a6707d9e6b7ccb81d0abaa8c9-566x419.png" alt="Elasticsearch가 NVIDIA cuVS, CPU 및 GPU 색인화로 작동하는 방식" /><p>cuvs-java 라이브러리는 <a href="https://github.com/elastic/elasticsearch/pull/135545">새로운 Elasticsearch 플러그인</a>에 통합되어 있습니다. 따라서 GPU에서의 벡터 색인화는 동일한 Elasticsearch 노드와 프로세스에서 외부 코드나 하드웨어를 프로비저닝할 필요 없이 수행할 수 있습니다. 인덱스 구축 중에 cuVS 라이브러리가 설치되고 GPU가 존재하며 구성된 경우 Elasticsearch는 GPU를 사용하여 벡터 색인화 프로세스를 가속화합니다. 벡터는 GPU에 전달되어 <a href="https://arxiv.org/abs/2308.15136">CAGRA</a> 그래프를 구성합니다. 이 그래프는 HNSW 형식으로 변환되어 CPU에서 벡터를 즉시 검색할 수 있게 됩니다. 구축된 그래프의 최종 형식은 CPU에서 구축되는 것과 동일합니다. 이를 통해 Elasticsearch는 기본 하드웨어가 지원할 경우 높은 처리량의 벡터 색인화에 GPU를 활용할 수 있으며, 동시에 CPU 자원을 다른 작업(동시 검색, 데이터 처리 등)에 사용할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt485f55f29d6df5c4/6a17e922be6086dcf3004785/3ea255bd9bfd7983f78143c5eba999d2149d72be-671x356.png" alt="" /><h2>인덱스 구축 가속화</h2><p>Elasticsearch에 GPU 가속화를 통합하는 과정에서 cuvs-java에 여러 가지 개선 사항이 적용되었으며, 특히 효율적인 데이터 입출력 및 함수 호출에 중점을 두었습니다. 핵심적인 개선 사항 중 하나는 <a href="https://github.com/rapidsai/cuvs/blob/2cf5fa7666d703dccbe655f8214656b0952bb69b/java/cuvs-java/src/main/java/com/nvidia/cuvs/CuVSMatrix.java">cuVSMatrix</a>를 사용하여 Java 힙, 오프힙 또는 GPU 메모리에 저장된 벡터를 투명하게 모델링하는 것입니다. 이를 통해 데이터가 메모리와 GPU 간에 효율적으로 이동하여 수십억 개의 벡터를 불필요하게 복사하는 것을 방지할 수 있습니다.</p><p>이러한 기본 제로 카피 추상화 덕분에 GPU 메모리로의 전송과 그래프 검색이 모두 직접 이루어질 수 있습니다. 색인화하는 동안 벡터는 먼저 Java 힙의 메모리에 버퍼링된 다음 GPU로 전송되어 CAGRA 그래프를 구성합니다. 그 후 그래프는 GPU에서 검색되어 HNSW 형식으로 변환된 후 디스크에 유지됩니다.</p><p>병합 시점에 벡터는 이미 디스크에 저장되어 있으므로 Java 힙을 완전히 우회합니다. 인덱스 파일은 메모리 맵핑 방식으로 처리되며 데이터는 GPU 메모리로 직접 전송됩니다. 또한 이 설계는 float32 또는 int8과 같은 다양한 비트 폭을 쉽게 수용할 수 있으며 다른 양자화 방식으로도 자연스럽게 확장됩니다.</p><h2>자, 그럼 성능은 어떨까요?</h2><p>수치를 살펴보기 전에, 약간의 배경 설명이 필요합니다. Elasticsearch에서 세그먼트 병합은 보통 색인 과정 중 백그라운드에서 자동으로 실행되기 때문에, 이를 단독으로 분리해 벤치마크하기가 어렵습니다. 재현 가능한 결과를 얻기 위해, 이번에는 강제 병합을 사용하여 통제된 실험 환경에서 세그먼트 병합을 명시적으로 트리거했습니다. 강제 병합은 백그라운드 병합과 동일한 기본 병합 작업을 수행하므로, 실제 색인 워크로드에서는 정확한 향상 폭이 다를 수 있지만, 성능 개선 효과를 가늠하는 유용한 지표로 볼 수 있습니다.</p><p>이제 수치를 확인해 보겠습니다.</p><p>초기 벤치마크 결과는 매우 희망적입니다. 로컬로 연결된 NVMe 저장 공간이 있는 AWS <code>g6.4xlarge</code> 인스턴스에서 벤치마크를 실행했습니다. Elasticsearch의 단일 노드는 기본 최적의 색인화 스레드 수(8개 - 물리적 코어당 1개)를 사용하고 <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/merge">병합 스로틀링</a>을 비활성화하도록 구성되었습니다(빠른 NVMe 디스크에는 적용하기 어렵습니다).</p><p>데이터 세트에는 <a href="https://github.com/elastic/rally-tracks/blob/master/openai_vector/README.md">OpenAI Rally 벡터 트랙</a>에서 가져온 1,536차원의 260만 개 벡터를 사용했으며, <a href="https://github.com/elastic/elasticsearch/pull/137072">base64 스트링</a>으로 인코딩하고 float32 <em>hnsw</em>로 색인화했습니다. 모든 시나리오에서 구성된 그래프는 최대 95%의 재현율을 달성했습니다. 결과는 다음과 같습니다.</p><ul><li><p><strong>색인화 처리량:</strong> 인메모리 버퍼 플러시 중 그래프 구성을 GPU로 이동하여 처리량이 약 12배 증가합니다.</p></li><li><p><strong>강제 병합:</strong> 색인화가 완료된 후에도 GPU는 세그먼트 병합을 계속 가속화하여 강제 병합 단계의 속도를 최대 7배까지 높입니다.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfea4ee13b5a3b10d/6a17e923e9ea879c6aa9c616/f60ea9ee5996e456f393ffd195ee7eada6e5a7c2-948x387.png" alt="" /><ul><li><p><strong>CPU 사용량:</strong> 그래프 구성을 GPU로 오프로드하면 평균 및 최대 CPU 사용량이 많이 감소합니다. 아래 그래프는 색인화 및 병합 중 CPU 사용량을 보여주며, 이러한 작업이 GPU에서 실행될 때 GPU 작업량이 얼마나 낮아지는지를 강조합니다. GPU 색인화 중 CPU 사용률이 낮아지면 CPU 사이클이 확보되어 검색 성능 향상에 활용할 수 있습니다.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80ff9c53f9b6884a/6a17e925445de9ee4b4d0187/5e680a5fc41700a877f3d8b2e5ce18ebd3f37a0b-1600x562.png" alt="" /><ul><li><p><strong>재현율:</strong> CPU와 GPU 실행 간의 정확도는 사실상 동일하지만, GPU로 구축된 그래프는 재현율이 약간 더 높습니다.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5cbe084eca27b8e4/6a17e926faa913317093c8b7/48a2b7758606bd321712b7d8378cd2640e652a4e-1384x544.png" alt="" /><h2>가격 측면에서 비교</h2><p>앞선 비교에서는 의도적으로 동일한 하드웨어를 사용했으며 유일한 차이점은 색인 과정에서 GPU를 사용했는지 여부였습니다. 이 설정은 순수 연산 성능의 영향을 분리하여 파악하는 데 유용하지만 비용 관점에서의 비교도 가능합니다.</p><p>GPU 가속 구성과 거의 동일한 시간당 요금으로 CPU 전용 구성을 프로비저닝할 수 있습니다. 이 경우 비교 가능한 CPU 및 메모리 리소스가 약 두 배(32 vCPU(AMD EPYC), 64GB RAM)로 제공되며 색인 스레드 수를 16개로 두 배 늘릴 수 있습니다.</p><p>공정하고 일관된 비교를 위해 GPU를 명시적으로 비활성화한 상태로 AWS g6.8xlarge 인스턴스에서 CPU 전용 실험을 수행했습니다. 이를 통해 다른 모든 하드웨어 특성을 동일하게 유지하면서 GPU 가속과 CPU 전용 색인 간의 비용 대비 성능 트레이드오프를 평가할 수 있었습니다.</p><p>예상대로 더 강력한 CPU 인스턴스는 위 섹션의 벤치마크와 비교했을 때 향상된 성능을 보여줍니다. 그러나 이 더 강력한 CPU 인스턴스를 원래의 GPU 가속 결과와 비교해 보면 GPU는 여전히 상당한 성능 향상을 제공합니다. 색인화 처리량에서 <strong>약 5배</strong>, 강제 병합에서 <strong>약 6배</strong>의 성능 향상을 보이며, 동시에 최대 <strong>95%</strong>의 재현율을 달성하는 그래프를 구축할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94b5eb6f95ba307d/6a17e928abe0f255d4dfea35/8ffa58cae3ad175ef2932a351aeef4c34a1407b9-948x394.png" alt="" /><h2>결론</h2><p>엔드 투 엔드 시나리오에서 NVIDIA cuVS를 사용한 GPU 가속화는 색인화 처리량을 거의 12배 향상하고 강제 병합 지연 시간을 7배 단축하는 동시에 CPU 사용률을 크게 낮춥니다. 이는 벡터 색인화 및 병합 워크로드가 GPU 가속화를 통해 상당한 성능 향상을 얻을 수 있음을 보여줍니다. 비용을 고려한 비교에서도 GPU 가속화는 색인화 처리량을 약 5배, 강제 병합 작업 속도를 약 6배 향상하는 등 상당한 성능 향상을 제공합니다.</p><p>GPU 가속 벡터 색인화는 현재 Elasticsearch 9.3의 기술 미리 보기로 계획되어 있으며, 2026년 초에 출시될 예정입니다.</p><p>더 많은 소식을 기대해 주세요.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</guid>
    <category><![CDATA[벡터 데이터베이스]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Hemant Malik,Corey Nolet,Manas Singh,Mithun Radhakrishnan,Mayya Sharipova,Lorenzo Dematte,Ben Frederickson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1248d51633bd75d9/6a17e92ae9ea8714b3a9c61a/08f7469a4daaf67b7c5999585aae179b6680c78d-896x746.png" length="0" type="image/png"/>
    <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[HNSW 그래프 병합 속도 향상]]></title>
    <description><![CDATA[여러 개의 HNSW 그래프를 작성하는 데 드는 오버헤드를 줄이기 위해, 특히 그래프 병합 비용을 줄이기 위해 저희가 해온 작업을 살펴보세요.]]></description>
    <content:encoded><![CDATA[<p>과거에는 여러 개의 <a href="https://www.elastic.co/kr/search-labs/blog/hnsw-graph">HNSW 그래프를</a> 검색해야 할 때 발생하는 몇 가지 문제와 이를 완화할 수 있는 방법에 대해 논의한<a href="https://www.elastic.co/kr/search-labs/blog/multi-graph-vector-search">적이 있습니다.</a> 당시 저희는 계획했던 몇 가지 추가 개선 사항을 암시했습니다. 이 게시물은 그 작업의 정점입니다.</p><p>왜 여러 개의 그래프를 사용해야 하나요? 이는 불변 세그먼트라는 루씬의 아키텍처 선택에 따른 부작용입니다. 대부분의 아키텍처 선택과 마찬가지로 장단점이 있습니다. 예를 들어, 저희는 최근에 서버리스 Elasticsearch를 정식 버전으로 출시했습니다. 이러한 맥락에서 우리는 효율적인 인덱스 복제, 인덱스와 쿼리 계산을 분리하고 독립적으로 자동 확장하는 기능 등 불변 세그먼트를 통해 매우 중요한 이점을 얻었습니다. 벡터 양자화의 경우 세그먼트 병합을 통해 데이터 특성에 맞게 파라미터를 업데이트할 수 있습니다. 이러한 맥락에서 데이터 특성을 측정하고 인덱싱 선택을 재검토할 수 있는 기회를 갖는다는 것은 다른 장점도 있다고 생각합니다.</p><p>이 글에서는 여러 개의 HNSW 그래프를 작성하는 데 드는 오버헤드를 크게 줄이고 특히 그래프를 병합하는 데 드는 비용을 줄이기 위해 수행한 작업에 대해 설명합니다.</p><h3>배경</h3><p>관리 가능한 세그먼트 수를 유지하기 위해 Lucene은 주기적으로 세그먼트를 병합해야 하는지 여부를 확인합니다. 이는 현재 세그먼트 수가 기본 세그먼트 크기와 병합 정책에 따라 결정되는 목표 세그먼트 수를 초과하는지 확인하는 것과 같습니다. 개수를 초과하면 제약 조건을 위반하는 동안 Lucene은 세그먼트 그룹을 병합합니다. 이 프로세스는 <a href="https://blog.mikemccandless.com/2011/02/visualizing-lucenes-segment-merges.html">다른 곳에</a> 자세히 설명되어 있습니다.</p><p>Lucene은 쓰기 증폭에서 대수적 증가를 달성하기 때문에 비슷한 크기의 세그먼트를 병합하는 방법을 선택합니다. 벡터 인덱스의 경우 쓰기 증폭은 벡터가 그래프에 삽입되는 횟수입니다. Lucene은 약 10개의 그룹으로 세그먼트를 병합하려고 시도합니다. 결과적으로 벡터는 대략 {10}\left (\frac{n}{n_0}\right )번 그래프에 삽입되며, 여기서  인덱스 벡터 수이고  예상되는 기본 세그먼트 벡터 수입니다. 대수적 증가로 인해 쓰기 증폭은 거대한 인덱스의 경우에도 한 자릿수입니다. 그러나 그래프를 병합하는 데 소요되는 총 시간은 쓰기 증폭에 선형적으로 비례합니다.</p><p>HNSW 그래프를 병합할 때 가장 큰 세그먼트의 그래프를 유지하고 다른 세그먼트의 벡터를 삽입하는 작은 최적화를 이미 수행했습니다. 이것이 위의 9/10 요소의 이유입니다. 아래에서는 병합하는 모든 그래프의 정보를 사용하여 훨씬 더 나은 결과를 얻을 수 있는 방법을 보여줍니다.</p><h3>HNSW 그래프 병합</h3><p>이전에는 가장 큰 그래프를 유지하고 벡터가 포함된 그래프를 무시하고 다른 그래프에서 벡터를 삽입했습니다. 아래에서 사용하는 핵심 인사이트는 우리가 폐기하는 각 HNSW 그래프에 포함된 벡터에 대한 중요한 근접성 정보가 포함되어 있다는 것입니다. 이 정보를 사용하여 적어도 일부 벡터의 삽입을 가속화하고자 합니다.</p><p>병합 정책을 구축하는 데 사용할 수 있는 원자 연산이므로 작은 그래프  )를 큰 그래프 )에 삽입하는 문제에 중점을 둡니다.</p><p>전략은 큰 그래프에 삽입할  정점 하위 집합을 찾는 것입니다. 그런 다음 작은 그래프에서 이러한 정점의 연결성을 사용하여 나머지 정점  삽입하는 속도를 높입니다. 아래에서는 작은 그래프와 큰 그래프에서 각각  및  를 사용하여 정점  이웃을 나타냅니다. 프로세스를 개략적으로 설명하면 다음과 같습니다.</p><p><code>MERGE-HNSW</code></p><p><code>Inputs </code><code> and </code></p><p><code>1</code><code>Find </code><code> to insert into </code><code> using COMPUTE-JOIN-SET</code>
<code>2</code><code>Insert each vertex </code><code> into </code>
<code>3</code><code>for </code><code> do</code>
<code>4</code>
<code>5</code>
<code>6</code><code>FAST-SEARCH-LAYER</code>
<code>7</code><code>SELECT-NEIGHBORS-HEURISTIC</code>
<code>8</code></p><p>아래에서 설명하는 절차를 사용하여 집합  계산합니다(1줄). 그런 다음 표준 HNSW 삽입 절차(2줄)를 사용하여  모든 정점을 큰 그래프에 삽입합니다. 삽입하지 않은 각 정점에 대해 삽입한 이웃 정점과 그 이웃 정점을 큰 그래프(4줄과 5줄)에서 찾습니다. 이 집합으로 시드된 <code>FAST-SEARCH-LAYER</code> 절차(6줄)를 사용하여 HNSW <a href="https://arxiv.org/pdf/1603.09320">논문</a> (7줄)에서 <code>SELECT-NEIGHBORS-HEURISTIC</code> 후보를 찾습니다. 사실상 <code>SEARCH-LAYER</code> 을 <code>INSERT</code> 방법(논문의 알고리즘 1)의 후보 집합을 찾는 것으로 대체하는 것이며, 그 외에는 변경 사항이 없습니다. 마지막으로 방금 삽입한 버텍스를  추가합니다(8행).</p><p>이 기능이 작동하려면  모든 버텍스에  이웃이 하나 이상 있어야 합니다. 실제로, 우리는  버텍스에 대해 최대 레이어 연결성인 일부   실제 HNSW 그래프에서는 정점 각도가 상당히 분산되어 있는 것을 관찰할 수 있습니다. 아래 그림은 Lucene HNSW 그래프의 최하위 레이어에 대한 일반적인 버텍스 도의 누적 밀도 함수를 보여줍니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc815e1a9c3bb0d06/6a17e178ec0f89308a5a6564/44f001b3a1bc6627e172fed5c52a02cfdcb4cd66-1324x898.png" alt="HNSW 그래프: 버텍스 차수 분포 예시" /><p> 고정값을 사용하는 방법과 버텍스 차수의 함수로 만드는 방법을 살펴봤습니다. 두 번째 선택은 그래프 품질에 미치는 영향을 최소화하면서 속도를 크게 향상시킬 수 있으므로 다음과 같이 선택했습니다.</p><p>정의상 |는 작은 그래프에서 정점  차수와 같다는 점에 유의하세요. 하한이 2라는 것은 차수가 2보다 작은 모든 정점을 삽입한다는 의미입니다.</p><p>간단한 계산 인수를 통해  신중하게 선택하면   직접 삽입하기만 하면 된다는 것을 알 수 있습니다. 구체적으로 그래프의 끝 꼭지점 중 하나를  정확히 삽입하면 그래프의 가장자리에 색을 입힙니다.   최소  이웃을 가지려면  에지를 채색해야 한다는 것을 알 수 있습니다. 또한 다음을 기대합니다.</p><p>여기서  _U\left [N_s(U)|\right] 는 작은 그래프에서 평균 버텍스 차수입니다.  각 정점 u에 대해 최대  의 가장자리를 색칠합니다. 따라서 채색할 에지의 총 개수는 최대 _U\left [|N_s(U)|\right]입니다.  신중하게 선택하면 이 수의 가장자리에 가깝게 색을 칠할 수 있으므로 모든 정점을 포함하려면  다음을 만족해야 합니다.</p><p>이는  {1}{4}|V_s|=\frac{1}{5}|V_s|를 의미합니다.</p><p><code>SEARCH-LAYER</code> 이 런타임을 지배한다면 병합 시간을 최대  단축할 수 있음을 시사합니다. 쓰기 증폭의 대수적 증가를 감안할 때, 이는 매우 큰 인덱스의 경우에도 일반적으로 하나의 그래프를 작성할 때보다 빌드 시간이 두 배밖에 걸리지 않는다는 것을 의미합니다.</p><p>이 전략의 위험은 그래프 품질이 손상된다는 점입니다. 처음에는 노옵으로 시도했습니다 <code>FAST-SEARCH-LAYER</code>. 특히 단일 세그먼트로 병합할 때 지연 시간에 따른 리콜이 영향을 받을 정도로 그래프 품질이 저하되는 것으로 나타났습니다. 그런 다음 그래프를 제한적으로 검색하여 다양한 대안을 탐색했습니다. 결국 가장 효과적인 선택은 가장 단순한 것이었습니다. <code>SEARCH-LAYER</code> 을 사용하되 <code>ef_construction</code>. 이 매개변수화를 통해 우수한 품질의 그래프를 얻으면서도 병합 시간을 평균 30% 조금 넘게 줄일 수 있었습니다.</p><h3>조인 집합 계산</h3><p>좋은 조인 집합을 찾는 것은 HNSW 그래프 커버링 문제로 공식화할 수 있습니다. 욕심 휴리스틱은 최적의 그래프 커버를 근사화하기 위한 간단하고 효과적인 휴리스틱입니다. 우리가 취하는 접근 방식은 한 번에 하나씩 정점을 골라 이득이 감소하는 순서로  추가하는 방식입니다. 이득은 다음과 같이 정의됩니다:</p><p>여기서 는  벡터  개수를 나타내고  은 표시 함수입니다. 이득에는  추가한 버텍스 수의 변화, 즉 이 포함되는데, 이는 덜 커버되는 버텍스를 추가함으로써 목표에 더 가까워지기 때문입니다. 이득 계산은 아래 그림에서 중앙 주황색 버텍스에 대해 설명합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7a353d56fa09af5f/6a17e17a2f4a5c33f7fa8825/fe11cd55a7d94d9e0b0f4d47ea309a99075c355d-540x474.png" alt="HNSW 그래프에서 조인 집합 J에 추가할 버텍스 게인" /><p>각 버텍스  대해 다음과 같은 상태를 유지합니다:</p><ol><li><p>오래되었는지 여부,</p></li><li><p>그 이득 ),</p></li><li><p> 인접한 정점의 개수를 로 표시합니다,</p></li><li><p>타이 브레이킹에 사용되는 [0,1] 범위의 난수입니다.</p></li></ol><p>조인 집합을 계산하는 의사 코드는 다음과 같습니다.</p><p><code>COMPUTE-JOIN-SET</code></p><p><code>Inputs </code></p><p><code>1</code>
<code>2</code>
<code>3</code><code>for </code><code> do</code>
<code>4</code>
<code>5</code>
<code>6</code>
<code>7</code><code>while </code><code> do
8</code><code> maximum gain vertex in </code>
<code>9</code><code>Remove the state for </code><code> from </code>
<code>10</code><code>if </code><code> is not stale then</code>
<code>11</code>
<code>12</code>
<code>13</code><code>for </code><code> do</code>
<code>14</code><code>mark </code><code> as stale if </code>
<code>15</code><code>mark neighbors of </code><code> stale if </code><code>
16</code><code>
17</code><code>else
18</code><code>
19</code><code>if </code><code> then
20</code><code>
21</code><code>return </code></p><p>먼저 1~5줄에서 상태를 초기화합니다.</p><p>메인 루프의 각 반복에서 처음에는 최대 이득 버텍스(8번째 줄)를 추출하여 무작위로 동점을 끊습니다. 변경하기 전에 버텍스의 게인이 오래되었는지 확인해야 합니다. 특히,  버텍스를 추가할 때마다 다른 버텍스의 이득에 영향을 미칩니다:</p><ol><li><p>모든 이웃이  추가 이웃을 가지고 있기 때문에 이득이 바뀔 수 있습니다 (14행).</p></li><li><p>이제 이웃이 완전히 커버되면 모든 이웃의 이득이 바뀔 수 있습니다(14~16줄).</p></li></ol><p>게인을 지연 방식으로 다시 계산하므로 버텍스의 게인을  삽입하려는 경우에만 다시 계산합니다(18~20행). 이득은 항상 감소하기 때문에 삽입해야 하는 버텍스를 놓칠 수 없습니다.</p><p>종료 시점을 결정하기 위해  추가한 버텍스의 총 이득을 추적하기만 하면 됩니다. 또한,  {exit}적어도 하나의 버텍스는 0이 아닌 이득을 가지므로 항상 진전이 있습니다.</p><h3>결과</h3><p>지원되는 세 가지 거리 메트릭(유클리드, 코사인, 내적 곱)을 모두 포함하는 네 가지 데이터 세트에 대해 실험을 진행했습니다:</p><ol><li><p>쿼라-E5-small: 522931개 문서, 384개 차원, 코사인 유사도를 사용합니다,</p></li><li><p>코히어-위키백과-v2: 1백만 개의 문서, 768개의 차원, 코사인 유사도를 사용합니다,</p></li><li><p>요점 1백만 문서, 960개 차원, 유클리드 거리 사용, 그리고</p></li><li><p>코히어-위키백과-v3: 1M 문서, 1024 크기, 최대 내부 제품 사용.</p></li></ol><p>각 데이터 세트에 대해 두 가지 양자화 수준을 평가합니다:</p><ol><li><p>int8 - 차원당 1바이트 정수를 사용하고</p></li><li><p>BBQ - 차원당 단일 비트를 사용합니다.</p></li></ol><p>마지막으로, 각 실험에서 두 가지 검색 깊이에서 검색 품질을 평가하고 인덱스를 구축한 후와 단일 세그먼트로 강제 병합한 후를 조사했습니다.</p><p>요약하면, 모든 경우에서 그래프 품질을 유지하면서 인덱싱 및 병합 속도를 일관되게 크게 향상시켜 검색 성능을 향상시켰습니다.</p><h4>실험 1: int8 정량화</h4><p>베이스라인에서 제안된 변경 사항인 후보까지의 평균 속도 향상은 다음과 같습니다:</p><p>색인 시간 속도 향상: <strong>1.</strong></p><p>강제 병합 속도 향상: <strong>1.</strong></p><p>이는 다음과 같은 실행 시간 분석에 해당합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8422be122ef1a9c9/6a17e17bbe6086522e00467d/5f9f6d487b4c74c4998aa47465912c1a9743e928-734x479.png" alt="기준 및 후보 병합 전략에 대한 인덱싱 및 병합 시간" /><p>정확성을 위해 정확한 시간은 다음과 같습니다.</p><p></p><p>색인</p><p></p><p>병합</p><p></p><p>데이터 세트</p><p>기준선</p><p>후보</p><p>구축</p><p>후보</p><p>쿼라-E5-small</p><p>112.41s</p><p>81.55s</p><p>113.81s</p><p>70.87s</p><p>위키-코히어-v2</p><p>158.1s</p><p>122.95s</p><p>425.20s</p><p>239.28s</p><p>요점</p><p>141.82s</p><p>119.26s</p><p>536.07s</p><p>279.05s</p><p>위키-코히어-v3</p><p>211.86s</p><p>168.22s</p><p>654.97s</p><p>414.12s</p><p>아래에는 여러 세그먼트가 있는 인덱스(모든 벡터를 인덱싱한 후 기본 병합 전략의 최종 결과)와 단일 세그먼트로 강제 병합한 후의 두 가지 검색 깊이인 recall@10과 recall@100에서 후보(점선)와 기준선을 비교한 리콜 대 지연 시간 그래프가 나와 있습니다. 곡선이 더 높고 왼쪽으로 갈수록 더 좋은데, 이는 더 낮은 지연 시간에서 더 높은 회상률을 의미합니다.</p><p>보시다시피, 여러 세그먼트 인덱스의 경우 Cohere v3 데이터 세트의 후보가 더 우수하고 다른 모든 데이터 세트의 경우 약간 떨어지지만 거의 비슷합니다. 단일 세그먼트로 병합한 후 리콜 곡선은 모든 경우에 대해 거의 동일합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf674622469bf6a90/6a17e17dfbc5f874a84919c9/9195297f19f90b172d832ba56bdada7b0d8a768b-985x392.png" alt="인덱스 구축 후 지연 시간 대비 @10 및 @100 리콜하기" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltde89f7e94ac823f4/6a17e17fe9ea876429a9c507/c866f32d579ae2b841e92ca733dd29c0e214ac6b-986x386.png" alt="단일 세그먼트로 병합 후 지연 시간 대비 @10 및 @100 리콜하기" /><h4>실험 2: BBQ 정량화</h4><p>기준선에서 후보까지의 평균 속도 향상은 다음과 같습니다:</p><p>색인 시간 속도 향상: <strong>1.</strong></p><p>강제 병합 속도 향상: <strong>1.</strong></p><p>이는 다음과 같은 실행 시간 분석에 해당합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b0eb305222fa5c2/6a17e18125daab514f08a18c/11be0cf6b63480dc703409329357ea4847b55051-740x415.png" alt="기준 및 후보 병합 전략에 대한 인덱싱 및 병합 시간" /><p>정확성을 위해 정확한 시간은 다음과 같습니다.</p><p></p><p>색인</p><p></p><p>병합</p><p></p><p>데이터 세트</p><p>기준선</p><p>후보</p><p>구축</p><p>후보</p><p>쿼라-E5-small</p><p>70.71s</p><p>58.25s</p><p>59.38s</p><p>40.15s</p><p>위키-코히어-v2</p><p>203.08s</p><p>142.27s</p><p>107.27s</p><p>85.68s</p><p>요점</p><p>110.35s</p><p>105.52s</p><p>323.66s</p><p>202.2s</p><p>위키-코히어-v3</p><p>313.43s</p><p>190.63s</p><p>165.98s</p><p>159.95s</p><p>여러 세그먼트 인덱스의 경우, 기준선이 약간 더 나은 코히어 v2를 제외한 거의 모든 데이터 세트에서 후보가 더 우수합니다. 단일 세그먼트 인덱스의 경우 리콜 곡선은 모든 경우에 대해 거의 동일합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb12174abeadbcdd1/6a17e1822f4a5c4cc2fa8829/7da1be27bab5a7f5f9c9a1882ea35761a4a0ba5c-973x383.png" alt="인덱스 구축 후 지연 시간 대비 @10 및 @100 리콜하기" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1b88d4f7fec9905/6a17e184414c64a8de9450b4/a26af30a56c55a12addee7e4fb7e8f606f366668-979x386.png" alt="단일 세그먼트로 병합된 지연 시간 대비 @10 및 @100을 기억해 보세요." /><h3>결론</h3><p>이 블로그에서 설명한 알고리즘은 곧 출시될 Lucene 10.2와 이를 기반으로 하는 Elasticsearch 릴리즈에서 사용할 수 있습니다. 사용자는 이 새 버전에서 병합 성능이 향상되고 인덱스 빌드 시간이 단축되는 이점을 누릴 수 있습니다. 이 변경은 벡터 및 하이브리드 검색을 위한 빠르고 효율적인 Lucene과 Elasticsearch를 만들기 위한 지속적인 노력의 일환입니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hnsw-graphs-speed-up-merging</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hnsw-graphs-speed-up-merging</guid>
    <category><![CDATA[벡터 데이터베이스]]></category>
    <category><![CDATA[Lucene]]></category>
    <dc:creator><![CDATA[Thomas Veasey,Mayya Sharipova]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9fca6a01d0541cfb/6a17e187033c8d46486bb0e1/49a6c880f5dedd0fa502ece5be124824ee218cc0-1792x1024.png" length="0" type="image/png"/>
    <pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>