Spanner에서 벡터 임베딩을 색인화하고 쿼리하는 근사 최근접 이웃 찾기

이 페이지에서는 대략적인 코사인 거리, 대략적인 유클리드 거리, 대략적인 내적 벡터 함수를 사용하여 벡터 색인을 만들고 벡터 임베딩을 쿼리하는 방법을 설명합니다. 이러한 함수를 사용하면 Spanner에서 근사 최근접 이웃(ANN)을 찾을 수 있습니다. 데이터 세트가 작으면 K-최근접 이웃(KNN)을 사용하여 정확한 K-최근접 벡터를 찾을 수 있습니다. 하지만 데이터 세트가 증가하면 KNN 검색의 지연 시간과 비용도 증가합니다. ANN을 사용하면 지연 시간과 비용을 크게 줄이면서 근사 K-최근접 이웃을 찾을 수 있습니다.

근사 K-최근접 이웃

ANN 검색에서 K-반환 벡터는 실제 Top-K-최근접 이웃이 아닙니다. 때때로 Top-K-최근접 이웃에 없는 일부 벡터가 반환됩니다. 이를 리콜 손실이라고 부릅니다. 허용되는 리콜 손실의 양은 특정 사용 사례에 따라 다릅니다. 하지만 대부분의 사례에서 약간의 리콜 감소가 데이터베이스 성능 향상으로 이어진다면 합리적인 것으로 간주됩니다.

Spanner 근사 거리 함수에 대한 자세한 내용은 다음을 참조하세요.

벡터 색인

Spanner는 특수한 벡터 색인을 사용하여 ANN 벡터 검색 시간을 단축합니다. 이 색인은 매우 효율적인 최근접 이웃 알고리즘인 Google 연구팀의 확장 가능한 최근접 이웃(ScaNN)을 활용합니다.

벡터 색인은 트리 기반 구조를 사용해서 데이터를 파티셔닝하고 더 빠른 검색을 지원합니다. Spanner는 2단계 및 3단계 트리 구성을 모두 제공합니다.

  • 2단계 트리 구성: 리프 노드(num_leaves)에 해당 중심과 함께 밀접하게 관련된 벡터 그룹이 포함됩니다. 루트 레벨은 모든 리프 노드의 중심으로 구성됩니다.
  • 3단계 트리 구성: 2단계 트리와 개념이 비슷하지만 리프 노드 중심이 추가로 파티셔닝되어 루트 레벨(num_leaves)을 구성하는 추가 브랜치 레이어(num_branches)가 도입되었습니다.

또한 특정 거리 측정항목을 사용하여 벡터 색인을 빌드해야 합니다. distance_typeCOSINE, DOT_PRODUCT, EUCLIDEAN 중 하나로 설정하여 사용 사례에 가장 적합한 거리 측정항목을 선택할 수 있습니다.

자세한 내용은 VECTOR INDEX을 참조하세요.

제한사항

Spanner 벡터 색인에는 다음 제한사항이 있습니다.

  • ALTER VECTOR INDEX는 지원되지 않습니다.

벡터 색인 만들기

올바른 재현율 및 성능을 위해 벡터 색인의 최적화 효율을 높이기 위해서는 임베딩이 포함된 대부분의 행이 데이터베이스에 기록된 후 벡터 색인을 만드는 것이 좋습니다. 또한 새 데이터를 삽입한 후 벡터 색인을 주기적으로 다시 빌드해야 할 수 있습니다. 자세한 내용은 벡터 색인 다시 빌드를 참조하세요.

코사인 거리를 사용해서 임베딩 열 DocEmbedding이 있는 Documents 테이블에 2단계 트리와 1,000개 리프 노드가 있는 벡터 색인을 만들려면 다음 안내를 따르세요.

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(DocEmbedding)
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

3단계 트리와 1,000,000개 리프 노드가 있는 벡터 색인을 만들려면 다음 안내를 따르세요.

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);

임베딩 열이 null을 허용하는 경우 WHERE column_name IS NOT NULL 절을 사용해서 선언해야 합니다.

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

벡터 임베딩 쿼리

벡터 색인을 쿼리하려면 다음 3개의 근사 거리 함수 중 하나를 사용합니다.

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

근사 거리 함수를 사용할 때의 제한사항은 다음과 같습니다.

  • 벡터 색인을 사용하기 위한 쿼리 힌트를 제공해야 합니다.
  • 상수 표현식을 거리 함수의 하나의 인수로 사용해야 합니다(예: 매개변수 또는 리터럴).
  • 쿼리 또는 서브 쿼리에서 근사 거리 함수를 사용할 때 쿼리가 특정 형식을 따라야 합니다. 거리 함수가 유일한 ORDER BY 키이고, 한도를 지정해야 합니다.

자세한 제한사항 목록은 근사 거리 함수 참조 페이지를 참조하세요.

[1.0, 2.0, 3.0]에 가장 가까운 벡터 100개를 검색하려면:

SELECT DocId
FROM Documents@{FORCE_INDEX=DocEmbeddingIndex}
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

임베딩 열이 null을 허용하는 경우:

SELECT DocId
FROM Documents@{FORCE_INDEX=DocEmbeddingIndex}
WHERE NullableDocEmbedding IS NOT NULL
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

권장사항

다음 권장사항에 따라 벡터 색인을 최적화하고 쿼리 결과를 개선할 수 있습니다.

벡터 검색 옵션 조정

최적 벡터 검색 값은 사용 사례, 벡터 데이터 세트, 쿼리 벡터에 따라 달라집니다. 특정 워크로드의 최적 값을 찾기 위해서는 반복 조정을 수행해야 할 수 있습니다.

적합한 값을 선택할 때 따라야 할 유용한 가이드라인은 다음과 같습니다.

  • tree_depth(트리 레벨): 색인을 작성하려는 테이블의 행 수가 1,000만 개 미만이면 tree_depth2를 사용합니다. 그렇지 않고 tree_depth3을 사용하면 행이 최대 100억 개까지 포함된 테이블을 지원합니다.

  • num_leaves: 데이터 세트에서 행 수의 제곱근을 사용합니다. 값이 클수록 벡터 색인 빌드 시간이 증가할 수 있습니다. num_leavestable_row_count/1000보다 크게 설정하면 소규모 리프 수가 과도하게 늘어나고 성능이 저하될 수 있으므로 피해야 합니다.

  • num_leaves_to_search: 이 옵션은 검색되는 색인의 리프 노드 수를 지정합니다. num_leaves_to_search를 늘리면 재현율이 향상되지만 지연 시간과 비용도 증가합니다. CREATE VECTOR INDEX 문에 정의된 총 리프 수의 1%에 해당하는 숫자를 num_leaves_to_search의 값으로 사용하는 것이 좋습니다. 필터 절을 사용하는 경우 이 값을 늘려서 검색 범위를 넓힙니다.

적절한 재현율이 달성되었지만 쿼리 비용이 너무 높아서 최대 QPS가 낮아지면 다음 단계에 따라 num_leaves를 늘려보세요.

  1. num_leaves를 원래 값의 k 배수로 설정합니다(예: 2 * sqrt(table_row_count)).
  2. num_leaves_to_search를 원래 값의 k 배수로 설정합니다.
  3. 실험적으로 num_leaves_to_search를 줄여서 재현율을 유지하면서 비용 및 QPS를 개선해보세요.

재현율 개선

재현율이 악화되는 데에는 다음과 같은 여러 가지 이유가 있습니다.

  • num_leaves_to_search가 너무 작음: 일부 쿼리 벡터의 최근접 이웃을 찾는 것이 어려워서 더 많은 리프를 검색하기 위해 num_leaves_to_search를 늘리면 재현율 개선에 도움이 될 수 있습니다. 최근 쿼리가 처리하기 어려운 벡터를 더 많이 포함되도록 변경되었을 수 있습니다.

  • 벡터 색인을 다시 빌드해야 함: 벡터 색인의 트리 구조가 처음 생성될 때 데이터 세트에 맞게 최적화되었으며, 이후 변경되지 않았습니다. 따라서 초기 벡터 색인을 만든 후 약간 다른 벡터가 추가되면 트리 구조의 효율성이 저하되어 재현율이 저하될 수 있습니다.

벡터 색인 다시 빌드

다운타임 없이 벡터 색인을 다시 빌드하려면 다음 안내를 따르세요.

  1. 현재 벡터 색인과 동일한 임베딩 열에서 새 벡터 색인을 만들고 OPTIONS과 같은 매개변수를 적절하게 업데이트합니다.
  2. 색인 만들기가 완료되면 벡터 검색 쿼리를 업데이트하기 위해 새 색인을 가리키도록 FORCE_INDEX 힌트를 변경합니다. 이렇게 하면 쿼리에 새로운 벡터 색인이 사용됩니다. 새 쿼리에서 num_leaves_to_search를 다시 조정해야 할 수도 있습니다.
  3. 오래된 벡터 색인을 삭제합니다.

다음 단계