Buscar con embeddings de vectores
En esta página, se muestra cómo usar Firestore para realizar operaciones K-cerca de vectores vecino (KNN) con las siguientes técnicas:
- Almacena valores vectoriales
- Crea y administra índices de vectores de KNN
- Hacer una consulta de K-vecino más cercano (KNN) con uno de los vectores compatibles medidas de distancia
Almacena embeddings de vectores
Puedes crear valores vectoriales como incorporaciones de texto a partir de tu y almacenarlos en documentos de Firestore.
Operación de escritura con una embedding de vector
En el siguiente ejemplo, se muestra cómo almacenar una incorporación de vector en un documento de Firestore:
Python
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]) });
Calcula incorporaciones vectoriales con una Cloud Function
Calcular y almacenar incorporaciones vectoriales cada vez que se actualiza un documento o puedes configurar una función de Cloud Run:
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, }); }
Crea y administra índices vectoriales
Antes de realizar una búsqueda de vecino más cercano con tus incorporaciones vectoriales, debes crear un índice correspondiente. En los siguientes ejemplos, se muestran cómo crear y administrar índices vectoriales.
Crea un índice vectorial
Antes de crear un índice vectorial, actualiza a la versión más reciente de Google Cloud CLI:
gcloud components update
Para crear un índice vectorial, usa 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
Donde:
- collection-group es el ID del grupo de colecciones.
- vector-field es el nombre del campo que contiene la embedding de vector.
- database-id es el ID de la base de datos.
- vector-configuration incluye el vector
dimension
y el tipo de índice.dimension
es un número entero hasta 2,048. El tipo de índice debe serflat
. Da formato a la configuración del índice de la siguiente manera:{"dimension":"DIMENSION", "flat": "{}"}
.
En el siguiente ejemplo, se crea un índice compuesto, que incluye un índice de vectores para el campo vector-field
y un índice ascendente para el campo color
. Puedes usar este tipo de índice para realizar un filtro previo de datos
antes de buscar un vecino más cercano.
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
Enumerar todos los índices vectoriales
gcloud
gcloud firestore indexes composite list --database=database-id
Reemplaza database-id por el ID de la base de datos.
Borra un índice vectorial
gcloud
gcloud firestore indexes composite delete index-id --database=database-id
Donde:
- index-id es el ID del índice que se borrará.
Usa
indexes composite list
para recuperar el ID del índice. - database-id es el ID de la base de datos.
Describir un índice vectorial
gcloud
gcloud firestore indexes composite describe index-id --database=database-id
Donde:
- index-id es el ID del índice que se describirá. Usar o
indexes composite list
para recuperar el ID del índice. - database-id es el ID de la base de datos.
Haz una consulta de vecino más cercano
Puedes realizar una búsqueda de similitud para encontrar los vecinos más cercanos de la embedding de vector. Las búsquedas de similitud requieren índices vectoriales. Si no existe un índice, Firestore sugiere crear uno nuevo con gcloud CLI.
En el siguiente ejemplo, se encuentran 10 vecinos más cercanos del vector de consulta.
Python
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();
Distancias vectoriales
Las consultas de vecino más cercano admiten las siguientes opciones para la distancia vectorial:
EUCLIDEAN
: Mide la distancia de EUCLIDEAN entre los vectores. Para obtener más información, consulta Euclidea.COSINE
: Compara vectores según el ángulo entre ellos, lo que te permite medir la similitud que no se basa en la magnitud de los vectores. Recomendamos usarDOT_PRODUCT
con vectores normalizados de unidades en lugar de la distancia de COSINE, que es matemáticamente equivalente con un mejor rendimiento. Para obtener más información, consulta Similitud coseno para obtener más información.DOT_PRODUCT
: Es similar aCOSINE
, pero se ve afectado por la magnitud de laos vectores. Para obtener más información, consulta Producto de punto.
Elige la medida de distancia
Dependiendo de si todas tus incorporaciones vectoriales están normalizadas, puedes determinar qué medida de distancia usar para encontrar la medida de distancia. Una incorporación de vectores normalizada tiene una magnitud (longitud) de exactamente 1.0.
Además, si sabes con qué medida de distancia se entrenó tu modelo, esa medición de distancia para calcular la distancia entre tu vector de las incorporaciones.
Datos normalizados
Si tienes un conjunto de datos en el que todas las incorporaciones vectoriales están normalizadas, entonces las tres
las mediciones de distancia proporcionan
los mismos resultados de búsqueda semántica. En esencia, aunque cada medida de distancia muestra un valor diferente, esos valores se ordenan de la misma manera. Cuando las incorporaciones se normalizan, DOT_PRODUCT
suele ser la más eficiente en términos de procesamiento, pero la diferencia es despreciable en la mayoría de los casos. Sin embargo, si tus
aplicación es muy sensible al rendimiento, DOT_PRODUCT
podría ayudar con
ajustar el rendimiento.
Datos no normalizados
Si tienes un conjunto de datos en el que las incorporaciones vectoriales no están normalizadas,
entonces no es matemáticamente correcto usar DOT_PRODUCT
como distancia
medir porque el producto punto no mide la distancia. Según
cómo se generaron las incorporaciones y qué tipo de búsqueda se prefiere,
la medición de distancia COSINE
o EUCLIDEAN
produce
resultados de la búsqueda que son subjetivamente mejores que las otras mediciones de distancia.
La experimentación con COSINE
o EUCLIDEAN
podría
necesaria para determinar cuál es la mejor
para tu caso de uso.
No sé si los datos están normalizados o no normalizados
Si no estás seguro de si tus datos están normalizados o no y deseas usar
DOT_PRODUCT
, te recomendamos que uses COSINE
en su lugar.
COSINE
es como DOT_PRODUCT
con normalización integrada.
La distancia medida con COSINE
varía de 0
a 2
. Un resultado
cerca de 0
indica que los vectores son muy similares.
Cómo aplicar un filtro previo a los documentos
Para filtrar previamente los documentos antes de encontrar los vecinos más cercanos, puedes combinar un
la búsqueda de similitud con otros operadores de consulta. Se admiten los filtros compuestos and
y or
. Para obtener más información sobre los filtros de campo admitidos, consulta Operadores de consulta.
Python
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();
Cómo recuperar la distancia del vector calculada
Puedes recuperar la distancia vectorial calculada asignando un
Nombre de la propiedad de salida de distance_result_field
en la consulta FindNearest
, como
como se muestra en el siguiente ejemplo:
Python
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')); });
Si deseas usar una máscara de campo para mostrar un subconjunto de campos de documento junto con una distanceResultField
, también debes incluir el valor de distanceResultField
en la máscara de campo, como se muestra en el siguiente ejemplo:
Python
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' });
Especifica un umbral de distancia
Puedes especificar un umbral de similitud que muestre solo documentos dentro del umbral. El comportamiento del campo de umbral depende de la medición de distancia eliges:
- Las distancias
EUCLIDEAN
yCOSINE
limitan el umbral a los documentos en los que de distancia es menor o igual que el umbral especificado. Estas distancias disminuyen a medida que los vectores se vuelven más similares. - La distancia de
DOT_PRODUCT
limita el umbral a los documentos en los que la distancia es mayor o igual que el umbral especificado. Distancias del producto punto aumentan a medida que los vectores se vuelven más similares.
En el siguiente ejemplo, se muestra cómo especificar un umbral de distancia para mostrar hasta 10 documentos más cercanos que se encuentren, como máximo, a 4.5 unidades de distancia con la métrica de distancia EUCLIDEAN
:
Python
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); });
Limitaciones
Cuando trabajes con incorporaciones vectoriales, ten en cuenta las siguientes limitaciones:
- La dimensión de incorporación máxima admitida es 2,048. Para almacenar índices más grandes, usa reducción de dimensiones.
- La cantidad máxima de documentos que se pueden mostrar con una consulta de vecino más cercano es de 1,000.
- La búsqueda vectorial no admite objetos de escucha de instantáneas en tiempo real.
- Solo las bibliotecas cliente de Python y Node.js admiten la búsqueda vectorial.
¿Qué sigue?
- Obtén más información sobre las prácticas recomendadas para Firestore.
- Comprende las lecturas y escrituras a gran escala.