En esta página, se describe cómo encontrar vecinos más cercanos aproximados (ANN), crear índices de vectores y consultar incorporaciones de vectores con las siguientes funciones de distancia de ANN en Spanner:
APPROX_COSINE_DISTANCE
APPROX_EUCLIDEAN_DISTANCE
APPROX_DOT_PRODUCT
Cuando un conjunto de datos es pequeño, puedes usar el algoritmo de K-vecinos más cercanos (KNN) para encontrar los vectores k más cercanos exactos. Sin embargo, a medida que crece tu conjunto de datos, también aumentan la latencia y el costo de una búsqueda de KNN. Puedes usar ANN para encontrar los k vecinos más cercanos aproximados con una latencia y un costo significativamente reducidos.
K-vecinos más cercanos aproximados
En una búsqueda de ANN, los vectores k devueltos no son los k vecinos más cercanos verdaderos, ya que la búsqueda de ANN calcula distancias aproximadas y es posible que no considere todos los vectores del conjunto de datos. En ocasiones, se devuelven algunos vectores que no se encuentran entre los k vecinos más cercanos. Esto se conoce como pérdida de recuperación. La cantidad de pérdida de recuperación que es aceptable para ti depende del caso de uso, pero, en la mayoría de los casos, perder un poco de recuperación a cambio de un mejor rendimiento de la base de datos es una compensación aceptable.
Para obtener más detalles sobre las funciones de distancia aproximada de Spanner, consulta los siguientes vínculos:
APPROX_COSINE_DISTANCE
en GoogleSQLAPPROX_EUCLIDEAN_DISTANCE
en GoogleSQLAPPROX_DOT_PRODUCT
en GoogleSQL
Índice vectorial
Spanner acelera las búsquedas de vectores de ANN con un índice de vectores especializado. Este índice aprovecha el algoritmo de vecino más cercano altamente eficiente Scalable Nearest Neighbor (ScaNN) de Google Research.
El índice de vectores usa una estructura basada en un árbol para particionar los datos y facilitar búsquedas más rápidas. Spanner ofrece configuraciones de árbol de dos y tres niveles:
- Configuración de árbol de dos niveles: Los nodos hoja (
num_leaves
) contienen grupos de vectores estrechamente relacionados junto con su centroide correspondiente. El nivel raíz consta de los centroides de todos los nodos hoja. - Configuración de árbol de tres niveles: Es similar en concepto a un árbol de dos niveles, pero introduce una capa de rama adicional (
num_branches
), desde la cual los centroides de los nodos hoja se particionan aún más para formar el nivel raíz (num_leaves
).
Spanner elige un índice por ti. Sin embargo, si sabes que un índice específico funciona mejor, puedes usar la sugerencia FORCE_INDEX
para elegir el índice de vectores más adecuado para tu caso de uso.
Para obtener más información, consulta las declaraciones VECTOR INDEX
.
Limitaciones
- No puedes dividir previamente los índices vectoriales. Para obtener más información, consulta la Descripción general de la división previa.
Crea un índice vectorial
Para optimizar la recuperación y el rendimiento de un índice de vectores, te recomendamos que hagas lo siguiente:
Crea tu índice de vectores después de que se escriban en tu base de datos la mayoría de las filas con incorporaciones. También es posible que debas volver a compilar el índice de vectores periódicamente después de insertar datos nuevos. Para obtener más información, consulta Cómo volver a compilar el índice vectorial.
Usa la cláusula
STORING
para almacenar una copia de una columna en el índice de vectores. Si un valor de columna se almacena en el índice de vectores, Spanner realiza el filtrado en el nivel de hoja del índice para mejorar el rendimiento de las consultas. Te recomendamos que almacenes una columna si se usa en una condición de filtrado. Para obtener más información sobre el uso deSTORING
en un índice, consulta Crea un índice para búsquedas solo de índice.
Cuando crees tu tabla, la columna de incorporación debe ser un array del tipo de datos FLOAT32
(recomendado) o FLOAT64
, y tener una anotación vector_length que indique la dimensión de los vectores.
La siguiente instrucción DDL crea una tabla Documents
con una columna de incorporación DocEmbedding
con una longitud de vector:
CREATE TABLE Documents (
DocId INT64 NOT NULL,
...
DocEmbedding ARRAY<FLOAT32>(vector_length=>128),
) PRIMARY KEY (DocId);
Después de propagar tu tabla Documents
, puedes crear un índice vectorial con un árbol de dos niveles y 1,000 nodos hoja en una tabla Documents
con una columna de incorporación DocEmbedding
usando la distancia del coseno:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(DocEmbedding)
STORING (WordCount)
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
Para crear un índice de vectores con un árbol de tres niveles y 1,000,000 de nodos hoja, haz lo siguiente:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(NullableDocEmbedding)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);
Si tu columna de incorporación no está marcada como NOT NULL
en la definición de la tabla, debes declararla con una cláusula WHERE COLUMN_NAME IS NOT NULL
:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(NullableDocEmbedding)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
Filtra un índice vectorial
También puedes crear un índice de vectores filtrado para encontrar los elementos más similares en tu base de datos que coincidan con la condición del filtro. Un índice de vectores filtrado indexa de forma selectiva las filas que satisfacen las condiciones de filtro especificadas, lo que mejora el rendimiento de la búsqueda.
En el siguiente ejemplo, la tabla Documents
tiene una columna llamada Category
.
En nuestra búsqueda vectorial, queremos indexar la categoría "Tecnología", por lo que creamos una columna generada que se evalúa como NULL si no se cumple la condición de la categoría.
CREATE TABLE Documents (
DocId INT64 NOT NULL,
Category STRING(MAX),
NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
DocEmbedding ARRAY<FLOAT32>(vector_length=>128),
) PRIMARY KEY (DocId);
Luego, creamos un índice de vectores con un filtro. El índice de vectores TechDocEmbeddingIndex
solo indexa documentos de la categoría "Tecnología".
CREATE VECTOR INDEX TechDocEmbeddingIndex
ON Documents(DocEmbedding)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
Cuando Spanner ejecuta la siguiente consulta, que tiene filtros que coinciden con TechDocEmbeddingIndex
, selecciona automáticamente TechDocEmbeddingIndex
y se acelera con él. La búsqueda solo se realiza en documentos de la categoría "Tecnología". También puedes usar {@FORCE_INDEX=TechDocEmbeddingIndex}
para forzar a Spanner a usar TechDocEmbeddingIndex
de forma explícita.
SELECT *
FROM Documents
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
ORDER BY APPROX_(....)
LIMIT 10;
Consulta embeddings de vectores
Para consultar un índice de vectores, usa una de las tres funciones de distancia aproximada:
APPROX_COSINE_DISTANCE
APPROX_EUCLIDEAN_DISTANCE
APPROX_DOT_PRODUCT
Las restricciones cuando se usan las funciones de distancia aproximada incluyen lo siguiente:
- La función de distancia aproximada debe calcular la distancia entre una columna de incorporación y una expresión constante (por ejemplo, un parámetro o un literal).
- El resultado de la función de distancia aproximada se debe usar en una cláusula
ORDER BY
como la única clave de ordenamiento, y se debe especificar unLIMIT
después delORDER BY
. - La consulta debe filtrar de forma explícita las filas que no están indexadas. En la mayoría de los casos, esto significa que la consulta debe incluir una cláusula
WHERE <column_name> IS NOT NULL
que coincida con la definición del índice de vectores, a menos que la columna ya esté marcada comoNOT NULL
en la definición de la tabla.
Para obtener una lista detallada de las limitaciones, consulta la página de referencia de la función de distancia aproximada.
Ejemplos
Para buscar los 100 vectores más cercanos a [1.0, 2.0, 3.0]
, haz lo siguiente:
SELECT DocId
FROM Documents
WHERE WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100
Si la columna de embedding admite valores nulos, sucede lo siguiente:
SELECT DocId
FROM Documents
WHERE NullableDocEmbedding IS NOT NULL AND WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100
Prácticas recomendadas
Sigue estas prácticas recomendadas para optimizar tus índices vectoriales y mejorar los resultados de las búsquedas.
Ajusta las opciones de búsqueda vectorial
El valor más óptimo de la búsqueda de vectores depende del caso de uso, el conjunto de datos de vectores y los vectores de búsqueda. Es posible que debas realizar ajustes iterativos para encontrar los mejores valores para tu carga de trabajo específica.
A continuación, se incluyen algunos lineamientos útiles que puedes seguir cuando elijas valores adecuados:
tree_depth
(nivel del árbol): Si la tabla que se indexa tiene menos de 10 millones de filas, usa untree_depth
de2
. De lo contrario, untree_depth
de3
admite tablas de hasta 10,000 millones de filas aproximadamente.num_leaves
: Usa la raíz cuadrada de la cantidad de filas del conjunto de datos. Un valor más grande puede aumentar el tiempo de compilación del índice de vectores. Evita establecernum_leaves
como un valor mayor quetable_row_count/1000
, ya que esto genera hojas demasiado pequeñas y un rendimiento deficiente.num_leaves_to_search
: Esta opción especifica cuántos nodos hoja del índice se buscan. Aumentarnum_leaves_to_search
mejora la recuperación, pero también incrementa la latencia y el costo. Recomendamos usar un número que sea el 1% de la cantidad total de hojas definidas en la instrucciónCREATE VECTOR INDEX
como el valor denum_leaves_to_search
. Si usas una cláusula de filtro, aumenta este valor para ampliar la búsqueda.
Si se logra una recuperación aceptable, pero el costo de la consulta es demasiado alto, lo que genera un QPS máximo bajo, intenta aumentar num_leaves
siguiendo estos pasos:
- Establece
num_leaves
en algún múltiplo k de su valor original (por ejemplo,2 * sqrt(table_row_count)
). - Establece
num_leaves_to_search
para que sea el mismo múltiplo k de su valor original. - Experimenta con la reducción de
num_leaves_to_search
para mejorar el costo y las QPS, y mantener la recuperación.
Mejora la recuperación
Existen varias posibilidades para que empeore la recuperación, incluidas las siguientes:
num_leaves_to_search
es demasiado pequeño: Es posible que te resulte más difícil encontrar los vecinos más cercanos para algunos vectores de búsqueda, por lo que aumentarnum_leaves_to_search
para buscar más hojas puede ayudar a mejorar la recuperación. Es posible que las búsquedas recientes hayan cambiado para incluir más de estos vectores desafiantes.Es necesario volver a compilar el índice de vectores: La estructura de árbol del índice de vectores se optimiza para el conjunto de datos en el momento de la creación y, luego, permanece estática. Por lo tanto, si se agregan vectores significativamente diferentes después de crear el índice de vectores inicial, la estructura del árbol podría ser subóptima, lo que generaría una recuperación más deficiente.
Vuelve a compilar el índice de vectores
Para volver a compilar tu índice de vectores sin tiempo de inactividad, haz lo siguiente:
- Crea un índice vectorial nuevo en la misma columna de incorporación que el índice vectorial actual y actualiza los parámetros (por ejemplo,
OPTIONS
) según corresponda. - Una vez que se complete la creación del índice, usa la sugerencia
FORCE_INDEX
para apuntar al índice nuevo y actualizar la búsqueda de vectores. Esto garantiza que la búsqueda use el nuevo índice de vectores. Es posible que también debas volver a ajustarnum_leaves_to_search
en tu nueva búsqueda. - Descarta el índice vectorial desactualizado.
¿Qué sigue?
Obtén más información sobre las funciones
APPROXIMATE_COSINE_DISTANCE()
,APPROXIMATE_EUCLIDEAN_DISTANCE()
yAPPROXIMATE_DOT_PRODUCT()
de GoogleSQL.Obtén más información sobre las sentencias
VECTOR INDEX
de GoogleSQL.Prueba la guía para comenzar a usar la Búsqueda de vectores de Spanner para ver un ejemplo paso a paso del uso de la ANN.