벡터 임베딩으로 검색

이 페이지에서는 Cloud Firestore를 사용하여 K-최근접을 실행하는 방법을 보여줍니다. 이웃 (KNN) 벡터는 다음 기법을 사용하여 검색합니다.

  • 벡터 값 저장
  • KNN 벡터 색인 만들기 및 관리
  • 지원되는 벡터 중 하나를 사용하여 K-최근접 이웃 (KNN) 쿼리 수행 거리 측정

벡터 임베딩 저장

텍스트 임베딩과 같은 벡터 값을 Cloud Firestore 데이터에 저장하고 이를 Cloud Firestore 문서에 저장합니다.

벡터 임베딩을 사용한 쓰기 작업

다음 예는 벡터 임베딩을 문서 Cloud Firestore개:

Python
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector

firestore_client = firestore.Client()
collection = firestore_client.collection("coffee-beans")
doc = {
    "name": "Kahawa coffee beans",
    "description": "Information about the Kahawa coffee beans.",
    "embedding_field": Vector([1.0, 2.0, 3.0]),
}

collection.add(doc)
Node.js
import {
  Firestore,
  FieldValue,
} from "@google-cloud/firestore";

const db = new Firestore();
const coll = db.collection('coffee-beans');
await coll.add({
  name: "Kahawa coffee beans",
  description: "Information about the Kahawa coffee beans.",
  embedding_field: FieldValue.vector([1.0 , 2.0, 3.0])
});

Cloud 함수로 벡터 임베딩 계산

문서가 업데이트 또는 생성될 때마다 벡터 임베딩을 계산하고 저장하기 위해 Cloud 함수를 설정할 수 있습니다.

Python
@functions_framework.cloud_event
def store_embedding(cloud_event) -> None:
  """Triggers by a change to a Firestore document.
  """
  firestore_payload = firestore.DocumentEventData()
  payload = firestore_payload._pb.ParseFromString(cloud_event.data)

  collection_id, doc_id = from_payload(payload)
  # Call a function to calculate the embedding
  embedding = calculate_embedding(payload)
  # Update the document
  doc = firestore_client.collection(collection_id).document(doc_id)
  doc.set({"embedding_field": embedding}, merge=True)
Node.js
/**
 * A vector embedding will be computed from the
 * value of the `content` field. The vector value
 * will be stored in the `embedding` field. The
 * field names `content` and `embedding` are arbitrary
 * field names chosen for this example.
 */
async function storeEmbedding(event: FirestoreEvent<any>): Promise<void> {
  // Get the previous value of the document's `content` field.
  const previousDocumentSnapshot = event.data.before as QueryDocumentSnapshot;
  const previousContent = previousDocumentSnapshot.get("content");

  // Get the current value of the document's `content` field.
  const currentDocumentSnapshot = event.data.after as QueryDocumentSnapshot;
  const currentContent = currentDocumentSnapshot.get("content");

  // Don't update the embedding if the content field did not change
  if (previousContent === currentContent) {
    return;
  }

  // Call a function to calculate the embedding for the value
  // of the `content` field.
  const embeddingVector = calculateEmbedding(currentContent);

  // Update the `embedding` field on the document.
  await currentDocumentSnapshot.ref.update({
    embedding: embeddingVector,
  });
}

벡터 색인 만들기 및 관리

벡터 임베딩으로 최근접 이웃 검색을 수행하려면 먼저 해당하는 색인을 만들어야 합니다 다음 예시는 벡터 색인을 만들고 관리하는 방법을 보여줍니다.

벡터 색인 만들기

벡터 색인을 만들기 전에 Google Cloud CLI의 최신 버전으로 업그레이드하세요.

gcloud components update

벡터 색인을 만들려면 gcloud firestore indexes composite create를 사용합니다.

gcloud
gcloud firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config field-path=vector-field,vector-config='vector-configuration' \
--database=database-id

각 항목의 의미는 다음과 같습니다.

  • collection-group은 컬렉션 그룹의 ID입니다.
  • vector-field는 벡터 임베딩이 포함된 필드의 이름입니다.
  • database-id는 데이터베이스의 ID입니다.
  • vector-configuration에는 dimension 벡터와 색인 유형이 포함됩니다. dimension은 최대 2048까지의 정수입니다. 색인 유형은 flat이어야 합니다. 색인 구성의 형식을 다음과 같이 지정합니다. {"dimension":"DIMENSION", "flat": "{}"}

다음 예시에서는 vector-field 필드의 벡터 색인과 color 필드의 오름차순 색인을 포함하는 복합 색인을 만듭니다. 이러한 유형의 색인을 사용하여 최근접 이웃 검색을 수행하기 전에 데이터를 사전 필터링할 수 있습니다.

gcloud
gcloud firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config=order=ASCENDING,field-path="color" \
--field-config field-path=vector-field,vector-config='{"dimension":"1024", "flat": "{}"}' \
--database=database-id

모든 벡터 색인 나열

gcloud
gcloud firestore indexes composite list --database=database-id

database-id를 데이터베이스 ID로 바꿉니다.

벡터 색인 삭제

gcloud
gcloud firestore indexes composite delete index-id --database=database-id

각 항목의 의미는 다음과 같습니다.

  • index-id는 삭제할 색인의 ID입니다. indexes composite list를 사용하여 색인 ID를 검색합니다.
  • database-id는 데이터베이스의 ID입니다.

벡터 색인 설명

gcloud
gcloud firestore indexes composite describe index-id --database=database-id

각 항목의 의미는 다음과 같습니다.

  • index-id는 설명할 색인의 ID입니다. indexes composite list를 사용하여 색인 ID를 검색합니다.
  • database-id는 데이터베이스의 ID입니다.

최근접 이웃 쿼리 수행

유사성 검색을 수행하여 벡터 임베딩의 최근접 이웃을 찾을 수 있습니다. 유사성 검색에는 벡터 색인이 필요합니다. 색인이 없으면 Cloud Firestore는 만들 색인을 제안합니다. (gcloud CLI 사용)

다음 예에서는 쿼리 벡터의 최근접 이웃 10개를 찾습니다.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Requires a single-field vector index
vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
Node.js
import {
  Firestore,
  FieldValue,
  VectorQuery,
  VectorQuerySnapshot,
} from "@google-cloud/firestore";

// Requires a single-field vector index
const vectorQuery: VectorQuery = coll.findNearest({
  vectorField: 'embedding_field',
  queryVector: [3.0, 1.0, 2.0],
  limit: 10,
  distanceMeasure: 'EUCLIDEAN'
});

const vectorQuerySnapshot: VectorQuerySnapshot = await vectorQuery.get();

벡터 거리

최근접 이웃 쿼리는 벡터 거리에 대한 다음 옵션을 지원합니다.

  • EUCLIDEAN: 벡터 사이의 EUCLIDEAN 거리를 측정합니다. 자세한 내용은 유클리드를 참조하세요.
  • COSINE: 벡터 크기를 기반으로 하지 않는 유사성을 측정할 수 있도록 벡터 사이의 각도를 기준으로 벡터를 비교합니다. COSINE 거리 대신 DOT_PRODUCT를 단위 정규화된 벡터와 함께 사용하는 것이 좋습니다. 이는 수학적으로 동일하며 성능이 더 우수합니다. 자세한 내용은 코사인 유사성을 참조하세요.
  • DOT_PRODUCT: COSINE과 유사하지만 벡터의 크기에 영향을 받습니다. 자세한 내용은 내적을 참조하세요.

거리 측정 선택

모든 벡터 임베딩이 정규화되었는지 여부에 따라 거리 측정을 찾는 데 사용할 거리 측정을 결정합니다. 정규화된 벡터 임베딩의 크기(길이)는 정확히 1.0입니다.

또한 모델이 학습한 거리 측정을 알고 있다면 거리 측정값을 사용하여 벡터 간 거리를 계산합니다. 임베딩을 학습합니다

정규화된 데이터

모든 벡터 임베딩이 정규화된 데이터 세트가 있는 경우 세 가지 모두 거리 측정은 동일한 시맨틱 검색 결과를 제공합니다. 기본적으로 거리 측정은 다른 값을 반환하며 해당 값은 동일한 방식으로 정렬됩니다. 임베딩이 정규화될 때는 일반적으로 DOT_PRODUCT가 가장 계산 효율적이지만 대부분의 경우 그 차이는 무시할 수 있을 정도입니다. 하지만 애플리케이션이 성능에 매우 민감한 경우 DOT_PRODUCT가 성능 조정에 도움이 될 수 있습니다.

비정규화된 데이터

벡터 임베딩이 정규화되지 않은 데이터 세트가 있는 경우 DOT_PRODUCT를 거리로 사용하는 것은 수학적으로 올바르지 않습니다. 내적은 거리를 측정하지 않기 때문입니다. 상황에 따라 임베딩이 어떻게 생성되었는지, 어떤 검색 유형이 선호되는지 COSINE 또는 EUCLIDEAN 거리 측정은 검색 결과에 중점을 둡니다. 사용 사례에 가장 적합한 방식을 결정하려면 COSINE 또는 EUCLIDEAN를 실험해야 할 수 있습니다.

데이터가 정규화되었는지 또는 비정규화되었는지 확실하지 않음

데이터가 정규화되었는지 여부가 확실하지 않고 DOT_PRODUCT를 사용하려는 경우 대신 COSINE를 사용하는 것이 좋습니다. COSINE는 정규화가 내장된 DOT_PRODUCT와 같습니다. COSINE을 사용하여 측정한 거리의 범위는 0~2입니다. 결과가 0에 가까우면 벡터가 매우 유사하다는 것을 나타냅니다.

문서 사전 필터링

최근접 이웃을 찾기 전에 문서를 사전 필터링하려면 유사성 검색을 수행할 수 있습니다. andor 복합 필터가 지원됩니다. 지원되는 필드 필터에 대한 자세한 내용은 쿼리 연산자를 참조하세요.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Similarity search with pre-filter
# Requires a composite vector index
vector_query = collection.where("color", "==", "red").find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
Node.js
// Similarity search with pre-filter
// Requires composite vector index
const preFilteredVectorQuery: VectorQuery = coll
    .where("color", "==", "red")
    .findNearest({
      vectorField: "embedding_field",
      queryVector: [3.0, 1.0, 2.0],
      limit: 5,
      distanceMeasure: "EUCLIDEAN",
    });

const vectorQueryResults = await preFilteredVectorQuery.get();

계산된 벡터 거리를 가져옵니다.

벡터 거리를 FindNearest 쿼리의 distance_result_field 출력 속성 이름입니다. 다음 예에 나와 있습니다.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}, Distance: {doc.get('vector_distance')}")
Node.js
const vectorQuery: VectorQuery = coll.findNearest(
    {
      vectorField: 'embedding_field',
      queryVector: [3.0, 1.0, 2.0],
      limit: 10,
      distanceMeasure: 'EUCLIDEAN',
      distanceResultField: 'vector_distance'
    });

const snapshot: VectorQuerySnapshot = await vectorQuery.get();

snapshot.forEach((doc) => {
  console.log(doc.id, ' Distance: ', doc.get('vector_distance'));
});

필드 마스크를 사용하여 distanceResultField와 함께 문서 필드의 하위 집합을 반환하려면 다음 예와 같이 필드 마스크에 distanceResultField 값도 포함해야 합니다.

Python
vector_query = collection.select(["color", "vector_distance"]).find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)
Node.js
const vectorQuery: VectorQuery = coll
    .select('color', 'vector_distance')
    .findNearest({
      vectorField: 'embedding_field',
      queryVector: [3.0, 1.0, 2.0],
      limit: 10,
      distanceMeasure: 'EUCLIDEAN',
      distanceResultField: 'vector_distance'
    });

거리 기준 지정

다음 범위 내의 문서만 반환하는 유사성 임곗값을 지정할 수 있습니다. 임곗값입니다. 임곗값 필드의 동작은 거리 측정에 따라 다릅니다. 선택할 수 있습니다.

  • EUCLIDEANCOSINE 거리는 기준점을 다음 문서로 제한합니다. 거리는 지정된 임계값보다 작거나 같습니다. 이 거리 벡터가 더 유사해질수록 감소한다는 것을 알 수 있습니다.
  • DOT_PRODUCT 거리에서 거리가 다음과 같은 문서로 기준점을 제한합니다. 지정된 임계값보다 크거나 같음 내적 거리 증가하기만 하면 됩니다.

다음 예시에서는 EUCLIDEAN 거리 측정항목을 사용하여 거리 기준을 지정하여 EUCLIDEAN 거리 측정항목을 사용하여 4.5 단위 이하의 가장 가까운 문서 10개를 반환하는 방법을 보여줍니다.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_threshold=4.5,
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}")
Node.js
const vectorQuery: VectorQuery = coll.findNearest({
  vectorField: 'embedding_field',
  queryVector: [3.0, 1.0, 2.0],
  limit: 10,
  distanceMeasure: 'EUCLIDEAN',
  distanceThreshold: 4.5
});

const snapshot: VectorQuerySnapshot = await vectorQuery.get();

snapshot.forEach((doc) => {
  console.log(doc.id);
});

제한사항

벡터 임베딩으로 작업할 때 다음 제한사항에 유의하세요.

  • 지원되는 최대 임베딩 차원은 2048입니다. 더 큰 색인을 저장하려면 차원 축소를 사용합니다.
  • 최근접 이웃 쿼리에서 반환할 최대 문서 수는 1,000개입니다.
  • 벡터 검색은 실시간 스냅샷 리스너를 지원하지 않습니다.
  • Python 및 Node.js 클라이언트 라이브러리만 벡터 검색을 지원합니다.

다음 단계