Vectores dispersos
Los vectores dispersos son un método importante de representación de datos en la recuperación de información y el procesamiento del lenguaje natural. Aunque los vectores densos son populares por su excelente capacidad de comprensión semántica, los vectores dispersos suelen ofrecer resultados más precisos cuando se trata de aplicaciones que requieren una correspondencia exacta de palabras clave o frases.
Visión general
Un vector disperso es una representación especial de vectores de alta dimensión en la que la mayoría de los elementos son cero y sólo unas pocas dimensiones tienen valores distintos de cero. Esta característica hace que los vectores dispersos sean particularmente eficaces en el manejo de datos a gran escala, de alta dimensión pero dispersos. Las aplicaciones más comunes son.
Análisis de textos: Representación de documentos como vectores bolsa de palabras, en los que cada dimensión corresponde a una palabra y sólo las palabras que aparecen en el documento tienen valores distintos de cero.
Sistemas de recomendación: Matrices de interacción usuario-artículo, en las que cada dimensión representa la valoración de un usuario para un artículo concreto, y la mayoría de los usuarios sólo interactúan con unos pocos artículos.
Tratamiento de imágenes: Representación de características locales, centrándose sólo en los puntos clave de la imagen, lo que da lugar a vectores dispersos de alta dimensión.
Como se muestra en el diagrama siguiente, los vectores densos suelen representarse como matrices continuas en las que cada posición tiene un valor (por ejemplo, [0.3, 0.8, 0.2, 0.3, 0.1]
). En cambio, los vectores dispersos sólo almacenan elementos distintos de cero y sus índices, a menudo representados como pares clave-valor (por ejemplo, [{2: 0.2}, ..., {9997: 0.5}, {9999: 0.7}]
). Esta representación reduce significativamente el espacio de almacenamiento y aumenta la eficiencia computacional, especialmente cuando se trata de datos de dimensiones extremadamente altas (por ejemplo, 10.000 dimensiones).
Representación de vectores dispersos
Los vectores dispersos pueden generarse utilizando varios métodos, como TF-IDF (Term Frequency-Inverse Document Frequency) y BM25 en el tratamiento de textos. Además, Milvus ofrece métodos prácticos para ayudar a generar y procesar vectores dispersos. Para más detalles, consulte Embeddings.
Para los datos de texto, Milvus también proporciona capacidades de búsqueda de texto completo, lo que le permite realizar búsquedas de vectores directamente en los datos de texto sin procesar sin utilizar modelos de incrustación externos para generar vectores dispersos. Para más información, consulte Búsqueda de texto completo.
Tras la vectorización, los datos pueden almacenarse en Milvus para su gestión y recuperación de vectores. El diagrama siguiente ilustra el proceso básico.
Utilizar vectores dispersos en Milvus
Además de los vectores dispersos, Milvus también admite vectores densos y vectores binarios. Los vectores densos son ideales para capturar relaciones semánticas profundas, mientras que los vectores binarios sobresalen en escenarios como comparaciones rápidas de similitud y deduplicación de contenidos. Para obtener más información, consulte Vectores densos y vectores binarios.
Utilizar vectores dispersos en Milvus
Milvus soporta la representación de vectores dispersos en cualquiera de los siguientes formatos.
Matriz dispersa (utilizando la clase
scipy.sparse
)from scipy.sparse import csr_matrix # Create a sparse matrix row = [0, 0, 1, 2, 2, 2] col = [0, 2, 2, 0, 1, 2] data = [1, 2, 3, 4, 5, 6] sparse_matrix = csr_matrix((data, (row, col)), shape=(3, 3)) # Represent sparse vector using the sparse matrix sparse_vector = sparse_matrix.getrow(0)
Lista de diccionarios (formateada como
{dimension_index: value, ...}
)# Represent sparse vector using a dictionary sparse_vector = [{1: 0.5, 100: 0.3, 500: 0.8, 1024: 0.2, 5000: 0.6}]
SortedMap<Long, Float> sparseVector = new TreeMap<>(); sparseVector.put(1L, 0.5f); sparseVector.put(100L, 0.3f); sparseVector.put(500L, 0.8f); sparseVector.put(1024L, 0.2f); sparseVector.put(5000L, 0.6f);
Lista de Iteradores de tupla (formateada como
[(dimension_index, value)]
)# Represent sparse vector using a list of tuples sparse_vector = [[(1, 0.5), (100, 0.3), (500, 0.8), (1024, 0.2), (5000, 0.6)]]
Añadir campo vectorial
Para utilizar vectores dispersos en Milvus, defina un campo para almacenar vectores dispersos al crear una colección. Este proceso incluye.
Establecer
datatype
al tipo de datos de vectores dispersos soportado,SPARSE_FLOAT_VECTOR
.No es necesario especificar la dimensión.
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://localhost:19530")
client.drop_collection(collection_name="my_sparse_collection")
schema = client.create_schema(
auto_id=True,
enable_dynamic_fields=True,
)
schema.add_field(field_name="pk", datatype=DataType.VARCHAR, is_primary=True, max_length=100)
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.setEnableDynamicField(true);
schema.addField(AddFieldReq.builder()
.fieldName("pk")
.dataType(DataType.VarChar)
.isPrimaryKey(true)
.autoID(true)
.maxLength(100)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("sparse_vector")
.dataType(DataType.SparseFloatVector)
.build());
import { DataType } from "@zilliz/milvus2-sdk-node";
const schema = [
{
name: "metadata",
data_type: DataType.JSON,
},
{
name: "pk",
data_type: DataType.Int64,
is_primary_key: true,
},
{
name: "sparse_vector",
data_type: DataType.SparseFloatVector,
}
];
export primaryField='{
"fieldName": "pk",
"dataType": "VarChar",
"isPrimary": true,
"elementTypeParams": {
"max_length": 100
}
}'
export vectorField='{
"fieldName": "sparse_vector",
"dataType": "SparseFloatVector"
}'
export schema="{
\"autoID\": true,
\"fields\": [
$primaryField,
$vectorField
]
}"
En este ejemplo, se añade un campo vectorial llamado sparse_vector
para almacenar vectores dispersos. El tipo de datos de este campo es SPARSE_FLOAT_VECTOR
.
Establecer parámetros de índice para el campo vectorial
El proceso de creación de un índice para vectores dispersos es similar al de los vectores densos, pero con diferencias en el tipo de índice especificado (index_type
), la métrica de distancia (metric_type
) y los parámetros del índice (params
).
index_params = client.prepare_index_params()
index_params.add_index(
field_name="sparse_vector",
index_name="sparse_inverted_index",
index_type="SPARSE_INVERTED_INDEX",
metric_type="IP",
params={"drop_ratio_build": 0.2},
)
import io.milvus.v2.common.IndexParam;
import java.util.*;
List<IndexParam> indexes = new ArrayList<>();
Map<String,Object> extraParams = new HashMap<>();
extraParams.put("drop_ratio_build", 0.2);
indexes.add(IndexParam.builder()
.fieldName("sparse_vector")
.indexName("sparse_inverted_index")
.indexType(IndexParam.IndexType.SPARSE_INVERTED_INDEX)
.metricType(IndexParam.MetricType.IP)
.extraParams(extraParams)
.build());
const indexParams = await client.createIndex({
index_name: 'sparse_inverted_index',
field_name: 'sparse_vector',
metric_type: MetricType.IP,
index_type: IndexType.SPARSE_WAND,
params: {
drop_ratio_build: 0.2,
},
});
export indexParams='[
{
"fieldName": "sparse_vector",
"metricType": "IP",
"indexName": "sparse_inverted_index",
"indexType": "SPARSE_INVERTED_INDEX",
"params":{"drop_ratio_build": 0.2}
}
]'
En el ejemplo anterior
Se crea un índice de tipo
SPARSE_INVERTED_INDEX
para el vector disperso. Para vectores dispersos, puede especificarSPARSE_INVERTED_INDEX
oSPARSE_WAND
. Para más detalles, consulte Índices de vectores dispersos.Para vectores dispersos,
metric_type
sólo admiteIP
(producto interior), utilizado para medir la similitud entre dos vectores dispersos. Para más información sobre similitud, consulte Tipos de métricas.drop_ratio_build
es un parámetro de índice opcional específico para vectores dispersos. Controla la proporción de valores de vectores pequeños excluidos durante la construcción del índice. Por ejemplo, con{"drop_ratio_build": 0.2}
, el 20% más pequeño de los valores del vector se excluirá durante la creación del índice, reduciendo el esfuerzo computacional durante las búsquedas.
Crear colección
Una vez completados los ajustes de vectores dispersos e índices, puede crear una colección que contenga vectores dispersos. El siguiente ejemplo utiliza el método create_collection
para crear una colección llamada my_sparse_collection
.
client.create_collection(
collection_name="my_sparse_collection",
schema=schema,
index_params=index_params
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_sparse_collection")
.collectionSchema(schema)
.indexParams(indexes)
.build();
client.createCollection(requestCreate);
import { MilvusClient } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({
address: 'http://localhost:19530'
});
await client.createCollection({
collection_name: 'my_sparse_collection',
schema: schema,
index_params: indexParams
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_sparse_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
Insertar datos
Después de crear la colección, inserta datos que contengan vectores dispersos.
sparse_vectors = [
{"sparse_vector": {1: 0.5, 100: 0.3, 500: 0.8}},
{"sparse_vector": {10: 0.1, 200: 0.7, 1000: 0.9}},
]
client.insert(
collection_name="my_sparse_collection",
data=sparse_vectors
)
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.response.InsertResp;
List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
{
JsonObject row = new JsonObject();
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(1L, 0.5f);
sparse.put(100L, 0.3f);
sparse.put(500L, 0.8f);
row.add("sparse_vector", gson.toJsonTree(sparse));
rows.add(row);
}
{
JsonObject row = new JsonObject();
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(10L, 0.1f);
sparse.put(200L, 0.7f);
sparse.put(1000L, 0.9f);
row.add("sparse_vector", gson.toJsonTree(sparse));
rows.add(row);
}
InsertResp insertR = client.insert(InsertReq.builder()
.collectionName("my_sparse_collection")
.data(rows)
.build());
const data = [
{ sparse_vector: { "1": 0.5, "100": 0.3, "500": 0.8 } },
{ sparse_vector: { "10": 0.1, "200": 0.7, "1000": 0.9 } },
];
client.insert({
collection_name: "my_sparse_collection",
data: data,
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"data": [
{"sparse_vector": {"1": 0.5, "100": 0.3, "500": 0.8}},
{"sparse_vector": {"10": 0.1, "200": 0.7, "1000": 0.9}}
],
"collectionName": "my_sparse_collection"
}'
## {"code":0,"cost":0,"data":{"insertCount":2,"insertIds":["453577185629572534","453577185629572535"]}}
Realizar búsqueda de similitud
Para realizar una búsqueda de similitudes utilizando vectores dispersos, prepare el vector de consulta y los parámetros de búsqueda.
# Prepare search parameters
search_params = {
"params": {"drop_ratio_search": 0.2}, # Additional optional search parameters
}
# Prepare the query vector
query_vector = [{1: 0.2, 50: 0.4, 1000: 0.7}]
En este ejemplo, drop_ratio_search
es un parámetro opcional específico para vectores dispersos, que permite ajustar pequeños valores en el vector de consulta durante la búsqueda. Por ejemplo, con {"drop_ratio_search": 0.2}
, el 20% de los valores más pequeños del vector de consulta se ignorarán durante la búsqueda.
A continuación, ejecute la búsqueda de similitud utilizando el método search
.
res = client.search(
collection_name="my_sparse_collection",
data=query_vector,
limit=3,
output_fields=["pk"],
search_params=search_params,
)
print(res)
# Output
# data: ["[{'id': '453718927992172266', 'distance': 0.6299999952316284, 'entity': {'pk': '453718927992172266'}}, {'id': '453718927992172265', 'distance': 0.10000000149011612, 'entity': {'pk': '453718927992172265'}}]"]
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.SparseFloatVec;
import io.milvus.v2.service.vector.response.SearchResp;
Map<String,Object> searchParams = new HashMap<>();
searchParams.put("drop_ratio_search", 0.2);
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(10L, 0.1f);
sparse.put(200L, 0.7f);
sparse.put(1000L, 0.9f);
SparseFloatVec queryVector = new SparseFloatVec(sparse);
SearchResp searchR = client.search(SearchReq.builder()
.collectionName("my_sparse_collection")
.data(Collections.singletonList(queryVector))
.annsField("sparse_vector")
.searchParams(searchParams)
.topK(3)
.outputFields(Collections.singletonList("pk"))
.build());
System.out.println(searchR.getSearchResults());
// Output
//
// [[SearchResp.SearchResult(entity={pk=453444327741536759}, score=1.31, id=453444327741536759), SearchResp.SearchResult(entity={pk=453444327741536756}, score=1.31, id=453444327741536756), SearchResp.SearchResult(entity={pk=453444327741536753}, score=1.31, id=453444327741536753)]]
client.search({
collection_name: 'my_sparse_collection',
data: {1: 0.2, 50: 0.4, 1000: 0.7},
limit: 3,
output_fields: ['pk'],
params: {
drop_ratio_search: 0.2
}
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "my_sparse_collection",
"data": [
{"1": 0.2, "50": 0.4, "1000": 0.7}
],
"annsField": "sparse_vector",
"limit": 3,
"searchParams":{
"params":{"drop_ratio_search": 0.2}
},
"outputFields": ["pk"]
}'
## {"code":0,"cost":0,"data":[{"distance":0.63,"id":"453577185629572535","pk":"453577185629572535"},{"distance":0.1,"id":"453577185629572534","pk":"453577185629572534"}]}
Para obtener más información sobre los parámetros de búsqueda de similitudes, consulte Búsqueda básica de RNA.
Límites
Cuando utilice vectores dispersos en Milvus, tenga en cuenta los siguientes límites:
Actualmente, sólo se admite la métrica de distancia IP para vectores dispersos. La alta dimensionalidad de los vectores dispersos hace que las distancias L2 y coseno sean poco prácticas.
Para los campos de vectores dispersos, sólo se admiten los tipos de índice SPARSE_INVERTED_INDEX y SPARSE_WAND.
Tipos de datos admitidos para vectores dispersos:
- La parte de dimensión debe ser un entero de 32 bits sin signo;
- La parte de valor puede ser un número de coma flotante de 32 bits no negativo.
Los vectores dispersos deben cumplir los siguientes requisitos para la inserción y la búsqueda:
- Al menos un valor del vector es distinto de cero;
- Los índices del vector no son negativos.
FAQ
¿Puede explicar la diferencia entre SPARSE_INVERTED_INDEX y SPARSE_WAND, y cómo puedo elegir entre ellos?
SPARSE_INVERTED_INDEX es un índice invertido tradicional, mientras que SPARSE_WAND utiliza el algoritmo Weak-AND para reducir el número de evaluaciones de distancia IP completa durante la búsqueda. SPARSE_WAND suele ser más rápido, pero su rendimiento puede disminuir al aumentar la densidad del vector. Para elegir entre ellos, realice experimentos y pruebas comparativas basadas en su conjunto de datos y caso de uso específicos.
¿Cómo debo elegir los parámetros drop_ratio_build y drop_ratio_search?
La elección de drop_ratio_build y drop_ratio_search depende de las características de los datos y de los requisitos de latencia/rendimiento y precisión de la búsqueda.
¿Puede la dimensión de una incrustación dispersa ser cualquier valor discreto dentro del espacio uint32?
Sí, con una excepción. La dimensión de una incrustación dispersa puede ser cualquier valor en el intervalo de
[0, maximum of uint32)
. Esto significa que no se puede utilizar el valor máximo de uint32.¿Las búsquedas en segmentos crecientes se realizan a través de un índice o por fuerza bruta?
Las búsquedas en segmentos crecientes se realizan a través de un índice del mismo tipo que el índice de segmento sellado. Para nuevos segmentos crecientes antes de que se construya el índice, se utiliza una búsqueda por fuerza bruta.
¿Es posible tener vectores dispersos y densos en una misma colección?
Sí, con el soporte de tipos de vectores múltiples, puede crear colecciones con columnas de vectores tanto dispersos como densos y realizar búsquedas híbridas en ellas.