Vettore binario
I vettori binari sono una forma speciale di rappresentazione dei dati che converte i tradizionali vettori in virgola mobile ad alta dimensione in vettori binari contenenti solo 0 e 1. Questa trasformazione non solo comprime le dimensioni del vettore, ma riduce anche i costi di archiviazione e di calcolo, mantenendo le informazioni semantiche. Questa trasformazione non solo comprime le dimensioni del vettore, ma riduce anche i costi di archiviazione e di calcolo, mantenendo le informazioni semantiche. Quando la precisione per le caratteristiche non critiche non è essenziale, i vettori binari possono effettivamente mantenere la maggior parte dell'integrità e dell'utilità dei vettori originali in virgola mobile.
I vettori binari hanno un'ampia gamma di applicazioni, in particolare in situazioni in cui l'efficienza computazionale e l'ottimizzazione della memorizzazione sono fondamentali. Nei sistemi di intelligenza artificiale su larga scala, come i motori di ricerca o i sistemi di raccomandazione, l'elaborazione in tempo reale di enormi quantità di dati è fondamentale. Riducendo le dimensioni dei vettori, i vettori binari aiutano a ridurre la latenza e i costi di calcolo senza sacrificare in modo significativo la precisione. Inoltre, i vettori binari sono utili in ambienti con risorse limitate, come i dispositivi mobili e i sistemi embedded, dove la memoria e la potenza di elaborazione sono limitate. Grazie all'uso dei vettori binari, è possibile implementare complesse funzioni di intelligenza artificiale in questi ambienti limitati, mantenendo alte le prestazioni.
Panoramica
I vettori binari sono un metodo per codificare oggetti complessi (come immagini, testo o audio) in valori binari di lunghezza fissa. In Milvus, i vettori binari sono tipicamente rappresentati come array di bit o di byte. Ad esempio, un vettore binario a 8 dimensioni può essere rappresentato come [1, 0, 1, 1, 0, 0, 1, 0]
.
Il diagramma seguente mostra come i vettori binari rappresentano la presenza di parole chiave nel contenuto del testo. In questo esempio, un vettore binario a 10 dimensioni viene utilizzato per rappresentare due testi diversi(Testo 1 e Testo 2), dove ogni dimensione corrisponde a una parola del vocabolario: 1 indica la presenza della parola nel testo, mentre 0 indica la sua assenza.
Rappresentazione vettoriale binaria del contenuto del testo
I vettori binari hanno le seguenti caratteristiche.
Memorizzazione efficiente: Ogni dimensione richiede solo 1 bit di memoria, riducendo in modo significativo lo spazio di archiviazione.
Rapidità di calcolo: La somiglianza tra vettori può essere calcolata rapidamente utilizzando operazioni bitwise come XOR.
Lunghezza fissa: La lunghezza del vettore rimane costante indipendentemente dalla lunghezza del testo originale, facilitando l'indicizzazione e il recupero.
Semplice e intuitivo: Riflette direttamente la presenza di parole chiave, il che lo rende adatto ad alcuni compiti di ricerca specializzati.
I vettori binari possono essere generati con diversi metodi. Nell'elaborazione del testo, si possono utilizzare vocabolari predefiniti per impostare i bit corrispondenti in base alla presenza delle parole. Per l'elaborazione delle immagini, gli algoritmi di hashing percettivo (come pHash) possono generare caratteristiche binarie delle immagini. Nelle applicazioni di apprendimento automatico, i risultati dei modelli possono essere binarizzati per ottenere rappresentazioni vettoriali binarie.
Dopo la vettorizzazione binaria, i dati possono essere archiviati in Milvus per la gestione e il recupero dei vettori. Il diagramma seguente mostra il processo di base.
Utilizzare i vettori binari in Milvus
Sebbene i vettori binari siano eccellenti in scenari specifici, hanno dei limiti nella loro capacità espressiva, rendendo difficile catturare relazioni semantiche complesse. Pertanto, negli scenari reali, i vettori binari sono spesso utilizzati insieme ad altri tipi di vettori per bilanciare efficienza ed espressività. Per ulteriori informazioni, consultare Vettori densi e vettori sparsi.
Utilizzare i vettori binari in Milvus
Aggiungere un campo vettoriale
Per utilizzare i vettori binari in Milvus, occorre innanzitutto definire un campo vettoriale per memorizzare i vettori binari quando si crea una collezione. Questo processo comprende.
Impostare
datatype
sul tipo di dati vettoriali binari supportati, cioèBINARY_VECTOR
.Specificare le dimensioni del vettore usando il parametro
dim
. Si noti chedim
deve essere un multiplo di 8, poiché i vettori binari devono essere convertiti in array di byte al momento dell'inserimento. Ogni 8 valori booleani (0 o 1) saranno impacchettati in 1 byte. Ad esempio, sedim=128
, per l'inserimento è necessario un array di 16 byte.
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
}"
In questo esempio, viene aggiunto un campo vettoriale chiamato binary_vector
per memorizzare vettori binari. Il tipo di dati di questo campo è BINARY_VECTOR
, con una dimensione di 128.
Impostare i parametri dell'indice per il campo vettoriale
Per velocizzare le ricerche, è necessario creare un indice per il campo vettoriale binario. L'indicizzazione può migliorare significativamente l'efficienza di recupero dei dati vettoriali su larga scala.
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}
}
]'
Nell'esempio precedente, viene creato un indice chiamato binary_vector_index
per il campo binary_vector
, utilizzando il tipo di indice BIN_IVF_FLAT
. metric_type
è impostato su HAMMING
, a indicare che la distanza di Hamming è utilizzata per la misurazione della similarità.
Oltre a BIN_IVF_FLAT
, Milvus supporta altri tipi di indice per i vettori binari. Per maggiori dettagli, consultare la sezione Indici di vettori binari. Inoltre, Milvus supporta altre metriche di similarità per i vettori binari. Per ulteriori informazioni, consultare Tipi di metriche.
Creare la collezione
Una volta completate le impostazioni dei vettori binari e degli indici, creare una collezione che contenga vettori binari. L'esempio seguente utilizza il metodo create_collection
per creare un insieme chiamato 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
}"
Inserire i dati
Dopo aver creato la collezione, utilizzare il metodo insert
per aggiungere dati contenenti vettori binari. Si noti che i vettori binari devono essere forniti sotto forma di array di byte, dove ogni byte rappresenta 8 valori booleani.
Ad esempio, per un vettore binario di 128 dimensioni, è necessario un array di 16 byte (poiché 128 bit ÷ 8 bit/byte = 16 byte). Di seguito è riportato un esempio di codice per l'inserimento di dati.
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\"
}"
Eseguire la ricerca di similarità
La ricerca per similarità è una delle caratteristiche principali di Milvus, che consente di trovare rapidamente i dati più simili a un vettore di interrogazione in base alla distanza tra i vettori. Per eseguire una ricerca di similarità utilizzando vettori binari, preparare il vettore di interrogazione e i parametri di ricerca, quindi chiamare il metodo search
.
Durante le operazioni di ricerca, i vettori binari devono essere forniti anche sotto forma di array di byte. Assicurarsi che la dimensione del vettore di interrogazione corrisponda a quella specificata nella definizione di dim
e che ogni 8 valori booleani siano convertiti in 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\"]
}"
Per ulteriori informazioni sui parametri di ricerca della similarità, consultare la sezione Ricerca di base di RNA.