Vectores binarios
Los vectores binarios son una forma especial de representación de datos que convierte los vectores tradicionales de coma flotante de alta dimensión en vectores binarios que sólo contienen 0s y 1s. Esta transformación no sólo comprime el tamaño del vector, sino que también reduce los costes de almacenamiento y cálculo, al tiempo que conserva la información semántica. Cuando la precisión de las características no críticas no es esencial, los vectores binarios pueden mantener eficazmente la mayor parte de la integridad y utilidad de los vectores originales en coma flotante.
Los vectores binarios tienen una amplia gama de aplicaciones, sobre todo en situaciones en las que la eficiencia computacional y la optimización del almacenamiento son cruciales. En los sistemas de IA a gran escala, como los motores de búsqueda o los sistemas de recomendación, el procesamiento en tiempo real de cantidades masivas de datos es clave. Al reducir el tamaño de los vectores, los vectores binarios ayudan a disminuir la latencia y los costes computacionales sin sacrificar significativamente la precisión. Además, los vectores binarios son útiles en entornos con recursos limitados, como los dispositivos móviles y los sistemas integrados, donde la memoria y la capacidad de procesamiento son limitadas. Mediante el uso de vectores binarios, es posible implementar funciones complejas de IA en estos entornos restringidos manteniendo un alto rendimiento.
Visión general
Los vectores binarios son un método de codificación de objetos complejos (como imágenes, texto o audio) en valores binarios de longitud fija. En Milvus, los vectores binarios se representan típicamente como matrices de bits o matrices de bytes. Por ejemplo, un vector binario de 8 dimensiones puede representarse como [1, 0, 1, 1, 0, 0, 1, 0]
.
El siguiente diagrama muestra cómo los vectores binarios representan la presencia de palabras clave en el contenido de un texto. En este ejemplo, se utiliza un vector binario de 10 dimensiones para representar dos textos diferentes(Texto 1 y Texto 2), donde cada dimensión corresponde a una palabra del vocabulario: 1 indica la presencia de la palabra en el texto, mientras que 0 indica su ausencia.
Representación vectorial binaria del contenido del texto
Los vectores binarios presentan las siguientes características
Almacenamiento eficiente: Cada dimensión requiere sólo 1 bit de almacenamiento, lo que reduce significativamente el espacio de almacenamiento.
Cálculo rápido: La similitud entre vectores puede calcularse rápidamente mediante operaciones a nivel de bit, como XOR.
Longitud fija: La longitud del vector permanece constante independientemente de la longitud del texto original, lo que facilita la indexación y la recuperación.
Sencillo e intuitivo: Refleja directamente la presencia de palabras clave, lo que lo hace adecuado para determinadas tareas de recuperación especializadas.
Los vectores binarios pueden generarse mediante diversos métodos. En el tratamiento de textos, pueden utilizarse vocabularios predefinidos para establecer los bits correspondientes en función de la presencia de palabras. En el tratamiento de imágenes, los algoritmos de hashing perceptual (como pHash) pueden generar características binarias de las imágenes. En las aplicaciones de aprendizaje automático, las salidas de los modelos pueden binarizarse para obtener representaciones vectoriales binarias.
Tras la vectorización binaria, los datos pueden almacenarse en Milvus para su gestión y recuperación de vectores. El diagrama siguiente muestra el proceso básico.
Utilizar vectores binarios en Milvus
Aunque los vectores binarios destacan en escenarios específicos, tienen limitaciones en su capacidad expresiva, lo que dificulta la captura de relaciones semánticas complejas. Por lo tanto, en escenarios del mundo real, los vectores binarios se utilizan a menudo junto con otros tipos de vectores para equilibrar la eficiencia y la expresividad. Para más información, consulte Vector denso y Vector disperso.
Utilizar vectores binarios en Milvus
Añadir campo vectorial
Para utilizar vectores binarios en Milvus, defina primero un campo vectorial para almacenar vectores binarios al crear una colección. Este proceso incluye.
Establecer
datatype
en el tipo de datos de vectores binarios admitido, es decir,BINARY_VECTOR
.Especificar las dimensiones del vector utilizando el parámetro
dim
. Tenga en cuenta quedim
debe ser múltiplo de 8, ya que los vectores binarios deben convertirse en una matriz de bytes al insertarlos. Cada 8 valores booleanos (0 ó 1) se empaquetarán en 1 byte. Por ejemplo, sidim=128
, se requiere un array de 16 bytes para la inserción.
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://localhost:19530")
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="binary_vector", datatype=DataType.BINARY_VECTOR, dim=128)
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("binary_vector")
.dataType(DataType.BinaryVector)
.dimension(128)
.build());
import { DataType } from "@zilliz/milvus2-sdk-node";
schema.push({
name: "binary vector",
data_type: DataType.BinaryVector,
dim: 128,
});
export primaryField='{
"fieldName": "pk",
"dataType": "VarChar",
"isPrimary": true,
"elementTypeParams": {
"max_length": 100
}
}'
export vectorField='{
"fieldName": "binary_vector",
"dataType": "BinaryVector",
"elementTypeParams": {
"dim": 128
}
}'
export schema="{
\"autoID\": true,
\"fields\": [
$primaryField,
$vectorField
],
\"enableDynamicField\": true
}"
En este ejemplo, se añade un campo vectorial llamado binary_vector
para almacenar vectores binarios. El tipo de datos de este campo es BINARY_VECTOR
, con una dimensión de 128.
Establecer parámetros de índice para el campo vectorial
Para acelerar las búsquedas, es necesario crear un índice para el campo vectorial binario. La indexación puede mejorar significativamente la eficiencia de recuperación de datos vectoriales a gran escala.
index_params = client.prepare_index_params()
index_params.add_index(
field_name="binary_vector",
index_name="binary_vector_index",
index_type="BIN_IVF_FLAT",
metric_type="HAMMING",
params={"nlist": 128}
)
import io.milvus.v2.common.IndexParam;
import java.util.*;
List<IndexParam> indexParams = new ArrayList<>();
Map<String,Object> extraParams = new HashMap<>();
extraParams.put("nlist",128);
indexParams.add(IndexParam.builder()
.fieldName("binary_vector")
.indexType(IndexParam.IndexType.BIN_IVF_FLAT)
.metricType(IndexParam.MetricType.HAMMING)
.extraParams(extraParams)
.build());
import { MetricType, IndexType } from "@zilliz/milvus2-sdk-node";
const indexParams = {
indexName: "binary_vector_index",
field_name: "binary_vector",
metric_type: MetricType.HAMMING,
index_type: IndexType.BIN_IVF_FLAT,
params: {
nlist: 128,
},
};
export indexParams='[
{
"fieldName": "binary_vector",
"metricType": "HAMMING",
"indexName": "binary_vector_index",
"indexType": "BIN_IVF_FLAT",
"params":{"nlist": 128}
}
]'
En el ejemplo anterior, se crea un índice denominado binary_vector_index
para el campo binary_vector
, utilizando el tipo de índice BIN_IVF_FLAT
. El metric_type
se establece en HAMMING
, lo que indica que se utiliza la distancia de Hamming para la medición de la similitud.
Además de BIN_IVF_FLAT
, Milvus admite otros tipos de índice para vectores binarios. Para más detalles, consulte Índices de vectores binarios. Además, Milvus soporta otras métricas de similitud para vectores binarios. Para más información, consulte Tipos de métricas.
Crear colección
Una vez completados los ajustes de vectores binarios e índices, cree una colección que contenga vectores binarios. El siguiente ejemplo utiliza el método create_collection
para crear una colección llamada my_binary_collection
.
client.create_collection(
collection_name="my_binary_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_binary_collection")
.collectionSchema(schema)
.indexParams(indexParams)
.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_dense_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_binary_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
Insertar datos
Tras crear la colección, utiliza el método insert
para añadir datos que contengan vectores binarios. Ten en cuenta que los vectores binarios deben proporcionarse en forma de matriz de bytes, donde cada byte representa 8 valores booleanos.
Por ejemplo, para un vector binario de 128 dimensiones, se requiere una matriz de 16 bytes (ya que 128 bits ÷ 8 bits/byte = 16 bytes). A continuación se muestra un código de ejemplo para insertar datos.
def convert_bool_list_to_bytes(bool_list):
if len(bool_list) % 8 != 0:
raise ValueError("The length of a boolean list must be a multiple of 8")
byte_array = bytearray(len(bool_list) // 8)
for i, bit in enumerate(bool_list):
if bit == 1:
index = i // 8
shift = i % 8
byte_array[index] |= (1 << shift)
return bytes(byte_array)
bool_vectors = [
[1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0] + [0] * 112,
[0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1] + [0] * 112,
]
data = [{"binary_vector": convert_bool_list_to_bytes(bool_vector) for bool_vector in bool_vectors}]
client.insert(
collection_name="my_binary_collection",
data=data
)
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;
private static byte[] convertBoolArrayToBytes(boolean[] booleanArray) {
byte[] byteArray = new byte[booleanArray.length / Byte.SIZE];
for (int i = 0; i < booleanArray.length; i++) {
if (booleanArray[i]) {
int index = i / Byte.SIZE;
int shift = i % Byte.SIZE;
byteArray[index] |= (byte) (1 << shift);
}
}
return byteArray;
}
List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
{
boolean[] boolArray = {true, false, false, true, true, false, true, true, false, true, false, false, true, true, false, true};
JsonObject row = new JsonObject();
row.add("binary_vector", gson.toJsonTree(convertBoolArrayToBytes(boolArray)));
rows.add(row);
}
{
boolean[] boolArray = {false, true, false, true, false, true, false, false, true, true, false, false, true, true, false, true};
JsonObject row = new JsonObject();
row.add("binary_vector", gson.toJsonTree(convertBoolArrayToBytes(boolArray)));
rows.add(row);
}
InsertResp insertR = client.insert(InsertReq.builder()
.collectionName("my_binary_collection")
.data(rows)
.build());
const data = [
{ binary_vector: [1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1] },
{ binary_vector: [1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1] },
];
client.insert({
collection_name: "my_binary_collection",
data: data,
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"data\": $data,
\"collectionName\": \"my_binary_collection\"
}"
Realizar una búsqueda por similitud
La búsqueda por similitud es una de las características principales de Milvus, que le permite encontrar rápidamente los datos más similares a un vector de consulta basándose en la distancia entre vectores. Para realizar una búsqueda por similitud utilizando vectores binarios, prepare el vector de consulta y los parámetros de búsqueda y, a continuación, llame al método search
.
Durante las operaciones de búsqueda, los vectores binarios también deben proporcionarse en forma de matriz de bytes. Asegúrate de que la dimensionalidad del vector de consulta coincide con la dimensión especificada al definir dim
y de que cada 8 valores booleanos se convierten en 1 byte.
search_params = {
"params": {"nprobe": 10}
}
query_bool_list = [1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0] + [0] * 112
query_vector = convert_bool_list_to_bytes(query_bool_list)
res = client.search(
collection_name="my_binary_collection",
data=[query_vector],
anns_field="binary_vector",
search_params=search_params,
limit=5,
output_fields=["pk"]
)
print(res)
# Output
# data: ["[{'id': '453718927992172268', 'distance': 10.0, 'entity': {'pk': '453718927992172268'}}]"]
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.BinaryVec;
import io.milvus.v2.service.vector.response.SearchResp;
Map<String,Object> searchParams = new HashMap<>();
searchParams.put("nprobe",10);
boolean[] boolArray = {true, false, false, true, true, false, true, true, false, true, false, false, true, true, false, true};
BinaryVec queryVector = new BinaryVec(convertBoolArrayToBytes(boolArray));
SearchResp searchR = client.search(SearchReq.builder()
.collectionName("my_binary_collection")
.data(Collections.singletonList(queryVector))
.annsField("binary_vector")
.searchParams(searchParams)
.topK(5)
.outputFields(Collections.singletonList("pk"))
.build());
System.out.println(searchR.getSearchResults());
// Output
//
// [[SearchResp.SearchResult(entity={pk=453444327741536775}, score=0.0, id=453444327741536775), SearchResp.SearchResult(entity={pk=453444327741536776}, score=7.0, id=453444327741536776)]]
query_vector = [1,0,1,0,1,1,1,1,1,1,1,1];
client.search({
collection_name: 'my_binary_collection',
data: query_vector,
limit: 5,
output_fields: ['pk'],
params: {
nprobe: 10
}
});
export searchParams='{
"params":{"nprobe":10}
}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_binary_collection\",
\"data\": $data,
\"annsField\": \"binary_vector\",
\"limit\": 5,
\"searchParams\":$searchParams,
\"outputFields\": [\"pk\"]
}"
Para obtener más información sobre los parámetros de búsqueda de similitudes, consulte Búsqueda básica de RNA.