루씬의 스칼라 양자화 이해하기

자동 바이트 양자화, 세그먼트별 양자화, & 성능 인사이트를 포함하여 Elastic이 어떻게 Lucene에 스칼라 양자화를 도입했는지 살펴보세요.

Elastic 자격증을 취득하고 싶으신가요? 다음 Elasticsearch Engineer 교육이 언제 진행되는지 알아보세요! 지금 무료 클라우드 체험을 시작하거나, 내 로컬 기기에서 Elastic을 사용해 볼 수 있습니다.

Lucene의 자동 바이트 정량화

HNSW는 벡터를 저장하고 검색하는 강력하고 유연한 방법이지만, 빠르게 실행하려면 상당한 양의 메모리를 필요로 합니다. 예를 들어, 768차원의 1MM float32 벡터를 쿼리하려면 약 1,000,0004(768+12)=3120000000바이트\약3GB1,000,000 * 4 * (768 + 12) = 3120000000 바이트 \약 3GB의 램이 필요합니다. 상당한 수의 벡터를 검색하기 시작하면 비용이 많이 듭니다. 약 75%75\% 적은 메모리를 사용하는 한 가지 방법은 바이트 정량화를 사용하는 것입니다. Lucene과 그에 따른 Elasticsearch는 한동안 바이트바이트 벡터 인덱싱을 지원했지만 이러한 벡터를 구축하는 것은 사용자의 책임이었습니다. 이제 곧 Lucene에 int8int8 스칼라 양자화가 도입될 예정입니다.

스칼라 양자화 101

모든 양자화 기술은 원시 데이터의 손실 변환으로 간주됩니다. 즉, 공간 확보를 위해 일부 정보가 손실된다는 의미입니다. 스칼라 양자화에 대한 자세한 설명은 다음을 참조하세요: 스칼라 양자화 101을 참조하세요. 높은 수준에서 스칼라 양자화는 손실 압축 기술입니다. 간단한 계산을 통해 리콜에 거의 영향을 미치지 않으면서도 공간을 크게 절약할 수 있습니다.

아키텍처 살펴보기

Elasticsearch 작업에 익숙하신 분들은 이러한 개념에 이미 익숙하실 수도 있지만, 검색을 위한 문서 배포에 대한 간략한 개요는 다음과 같습니다.

각 Elasticsearch 인덱스는 여러 개의 샤드로 구성됩니다. 각 샤드는 단일 노드에만 할당할 수 있지만, 인덱스당 여러 개의 샤드를 사용하면 노드 간에 컴퓨팅 병렬 처리를 할 수 있습니다.

각 샤드는 하나의 루씬 인덱스로 구성됩니다. Lucene 인덱스는 여러 개의 읽기 전용 세그먼트로 구성됩니다. 색인하는 동안 문서가 버퍼링되고 주기적으로 읽기 전용 세그먼트로 플러시됩니다. 특정 조건이 충족되면 이러한 세그먼트는 백그라운드에서 더 큰 세그먼트로 병합될 수 있습니다. 이 모든 것은 구성할 수 있으며 나름의 복잡성을 가지고 있습니다. 그러나 세그먼트와 병합에 대해 이야기할 때는 읽기 전용 Lucene 세그먼트와 이러한 세그먼트의 자동 주기적 병합에 대해 이야기하고 있습니다. 세그먼트 병합 및 디자인 결정에 대해 자세히 알아보세요.

루씬의 세그먼트별 정량화

Lucene의 모든 세그먼트에는 개별 벡터, HNSW 그래프 인덱스, 양자화된 벡터, 계산된 사분위수 등이 저장됩니다. 간결성을 위해 여기서는 Lucene이 정량화된 벡터와 원시 벡터를 저장하는 방식에 초점을 맞추겠습니다. 모든 세그먼트에 대해 vecvec 파일의 원시 벡터, 양자화된 벡터 및 단일 보정 승수 플로트( veqveq), 그리고 vmqvmq 파일 내의 양자화 관련 메타데이터를 추적합니다.

그림 1: 원시 벡터 스토리지 파일의 단순화된 레이아웃. 부동소수점부동 소수점 값은 4바이트이므로 차원4numVectors차원 * 4 * numVectors의 디스크 공간을 차지합니다. 정량화 중이므로 HNSW 검색 중에는 로드되지 않습니다. 특별히 요청된 경우에만 사용됩니다(예 재점수를 통한 무차별 대입) 또는 세그먼트 병합 중 재정량화를 위해 사용합니다.

그림 2: .veq.veq의 단순화된 레이아웃 파일을 만듭니다. (차원+4)numVectors(차원 + 4)*numVectors의 공간을 차지하며 검색 중에 메모리에 로드됩니다. 4바이트는4바이트는 정확도와 기억력을 높이기 위해 점수를 조정하는 데 사용되는 보정 승수 플로트를 설명하는 값입니다.

그림 3: 메타데이터 파일의 단순화된 레이아웃. 여기에서 이 세그먼트에 대해 계산된 사분위수와 함께 양자화 및 벡터 구성을 추적합니다.

따라서 각 세그먼트에 대해 양자화된 벡터뿐만 아니라 이러한 양자화된 벡터를 만드는 데 사용된 분위수와 원래의 원시 벡터를 저장합니다. 그렇다면 왜 원시 벡터를 보관하는 것일까요?

사용자와 함께 성장하는 정량화

Lucene은 주기적으로 읽기 전용 세그먼트를 플러시하기 때문에 각 세그먼트는 모든 데이터의 일부만 볼 수 있습니다. 즉, 전체 데이터의 해당 샘플 세트에 대해서만 계산된 사분위수가 직접 적용됩니다. 샘플이 전체 말뭉치를 적절히 대표한다면 큰 문제가 되지 않습니다. 하지만 Lucene을 사용하면 다양한 방식으로 인덱스를 정렬할 수 있습니다. 따라서 세그먼트별 사분위수 계산에 편향을 추가하는 방식으로 정렬된 데이터를 인덱싱할 수 있습니다. 또한 원할 때마다 데이터를 플러시할 수 있습니다! 샘플 세트는 하나의 벡터일 정도로 작을 수 있습니다. 또 다른 장점은 병합이 발생하는 시기를 제어할 수 있다는 점입니다. 기본값과 주기적 병합이 설정되어 있지만, _force_merge API를 통해 원할 때마다 병합을 요청할 수 있습니다. 그렇다면 어떻게 하면 이 모든 유연성을 허용하면서도 좋은 리콜을 제공하는 우수한 정량화를 제공할 수 있을까요?

루씬의 벡터 양자화는 시간이 지남에 따라 자동으로 조정됩니다. Lucene은 읽기 전용 세그먼트 아키텍처로 설계되었기 때문에 각 세그먼트의 데이터가 변경되지 않았음을 보장하고 업데이트가 가능한 시점을 코드에 명확하게 구분합니다. 즉, 세그먼트 병합 중에 필요에 따라 사분위수를 조정하고 벡터를 다시 정량화할 수 있습니다.

그림 4: 서로 다른 사분위수를 가진 세 가지 예시 세그먼트.

하지만 재정량화에는 비용이 많이 들지 않을까요? 약간의 오버헤드가 있긴 하지만, Lucene은 지능적으로 사분위수를 처리하고 필요한 경우에만 완전히 정량화합니다. 그림 4의 세그먼트를 예로 들어 보겠습니다. 세그먼트 AA와 B에는B에는 각각 1,000개의1,000개의 문서를, 세그먼트 C에는C에는 100개의100개의 문서만 제공한다고 가정해 보겠습니다. Lucene은 사분위수의 가중 평균을 취하고 그 결과 병합된 사분위수가 세그먼트의 원래 사분위수에 충분히 근접하면 해당 세그먼트를 다시 정량화할 필요가 없으며 새로 병합된 사분위수를 활용합니다.

그림 5: 세그먼트 AA와 B에는B에는 1000개의1000개의 문서가 있고 C에는C에는 100개의100개의 문서만 있는 병합된 사분위수의 예입니다.

그림 5에서 시각화된 상황을 보면 병합된 결과 사분위수가 AA와 BB의 원래 사분위수와 매우 유사하므로 벡터를 정량화하는 것이 정당화되지 않음을 알 수 있습니다. 세그먼트 CC, 너무 많이 벗어난 것 같습니다. 결과적으로 CC의 벡터는 새로 병합된 사분위수 값으로 다시 정량화됩니다.

실제로 병합된 사분위수가 원래의 사분위수와 극적으로 다른 극단적인 경우가 있습니다. 이 경우 각 세그먼트에서 샘플을 가져와서 사분위수를 완전히 다시 계산합니다.

정량화 성능 & 숫자

그렇다면 속도가 빠르며 여전히 좋은 기억력을 제공하나요? c3-standard-8 GCP 인스턴스에서 실험을 실행하여 수집한 수치는 다음과 같습니다. float32float32와 공정한 비교를 위해 메모리에 원시 벡터를 저장할 수 있을 만큼 큰 인스턴스를 사용했습니다. 최대 내부 제품을 사용하여 400,000개의400,000개의 Cohere Wiki 벡터를 색인화했습니다.

그림 6: 양자화된 벡터와 원시 벡터의 Recall@10. 양자화된 벡터의 검색 성능은 원시 벡터보다 훨씬 빠르며, 5개만 더 수집해도 리콜을 빠르게 복구할 수 있습니다( 양자화@15양자화@15로 표시).

그림 6은 그 이야기를 보여줍니다. 예상대로 리콜 차이가 있긴 하지만, 그 차이는 크지 않습니다. 그리고 벡터를 5개만 더 수집하면 리콜 차이가 사라집니다. 이 모든 것이 2\배2\배 빠른 세그먼트 병합과 플로트32플로트32 벡터의 1/4 메모리로 가능합니다.

결론

Lucene은 어려운 문제에 대한 고유한 솔루션을 제공합니다. 정량화에는 '훈련' 또는 '최적화' 단계가 필요하지 않습니다. Lucene에서는 그냥 작동합니다. 데이터가 바뀌어도 벡터 인덱스를 '다시 학습'해야 할 염려가 없습니다. Lucene은 중요한 변경 사항을 감지하고 데이터의 수명 기간 동안 이를 자동으로 처리합니다. 이 기능을 Elasticsearch에 언제 도입할지 기대해 주세요!

자주 묻는 질문

스칼라 양자화란 무엇인가요?

스칼라 양자화는 손실 압축 기술입니다. 간단한 계산을 통해 리콜에 거의 영향을 주지 않으면서도 공간을 크게 절약할 수 있습니다.

관련 콘텐츠

최첨단 검색 환경을 구축할 준비가 되셨나요?

충분히 고급화된 검색은 한 사람의 노력만으로는 달성할 수 없습니다. Elasticsearch는 여러분과 마찬가지로 검색에 대한 열정을 가진 데이터 과학자, ML 운영팀, 엔지니어 등 많은 사람들이 지원합니다. 서로 연결하고 협력하여 원하는 결과를 얻을 수 있는 마법 같은 검색 환경을 구축해 보세요.

직접 사용해 보세요