Vecteur binaire
Les vecteurs binaires sont une forme particulière de représentation des données qui convertit les vecteurs traditionnels à virgule flottante en vecteurs binaires ne contenant que des 0 et des 1. Cette transformation permet non seulement de comprimer la taille du vecteur, mais aussi de réduire les coûts de stockage et de calcul tout en conservant les informations sémantiques. Lorsque la précision des caractéristiques non critiques n'est pas essentielle, les vecteurs binaires peuvent effectivement conserver la majeure partie de l'intégrité et de l'utilité des vecteurs à virgule flottante d'origine.
Les vecteurs binaires ont un large éventail d'applications, en particulier dans les situations où l'efficacité du calcul et l'optimisation du stockage sont cruciales. Dans les systèmes d'intelligence artificielle à grande échelle, tels que les moteurs de recherche ou les systèmes de recommandation, le traitement en temps réel de quantités massives de données est essentiel. En réduisant la taille des vecteurs, les vecteurs binaires permettent de diminuer la latence et les coûts de calcul sans sacrifier de manière significative la précision. En outre, les vecteurs binaires sont utiles dans les environnements à ressources limitées, tels que les appareils mobiles et les systèmes intégrés, où la mémoire et la puissance de traitement sont limitées. L'utilisation de vecteurs binaires permet de mettre en œuvre des fonctions d'intelligence artificielle complexes dans ces environnements restreints tout en maintenant des performances élevées.
Vue d'ensemble
Les vecteurs binaires sont une méthode de codage d'objets complexes (tels que des images, du texte ou de l'audio) en valeurs binaires de longueur fixe. Dans Milvus, les vecteurs binaires sont généralement représentés sous forme de tableaux de bits ou de tableaux d'octets. Par exemple, un vecteur binaire à 8 dimensions peut être représenté sous la forme [1, 0, 1, 1, 0, 0, 1, 0]
.
Le diagramme ci-dessous montre comment les vecteurs binaires représentent la présence de mots clés dans le contenu d'un texte. Dans cet exemple, un vecteur binaire à 10 dimensions est utilisé pour représenter deux textes différents(Texte 1 et Texte 2), où chaque dimension correspond à un mot du vocabulaire : 1 indique la présence du mot dans le texte, tandis que 0 indique son absence.
Représentation vectorielle binaire du contenu d'un texte
Les vecteurs binaires présentent les caractéristiques suivantes.
Stockage efficace : Chaque dimension ne nécessite qu'un bit de stockage, ce qui réduit considérablement l'espace de stockage.
Calcul rapide : La similarité entre les vecteurs peut être calculée rapidement à l'aide d'opérations sur les bits telles que XOR.
Longueur fixe : La longueur du vecteur reste constante quelle que soit la longueur du texte original, ce qui facilite l'indexation et la recherche.
Simple et intuitif : Il reflète directement la présence de mots-clés, ce qui le rend adapté à certaines tâches de recherche spécialisées.
Les vecteurs binaires peuvent être générés par différentes méthodes. Dans le traitement de texte, des vocabulaires prédéfinis peuvent être utilisés pour définir les bits correspondants en fonction de la présence de mots. Pour le traitement des images, les algorithmes de hachage perceptuel (comme pHash) peuvent générer des caractéristiques binaires des images. Dans les applications d'apprentissage automatique, les sorties du modèle peuvent être binarisées pour obtenir des représentations vectorielles binaires.
Après la vectorisation binaire, les données peuvent être stockées dans Milvus pour la gestion et la récupération des vecteurs. Le diagramme ci-dessous illustre le processus de base.
Utiliser des vecteurs binaires dans Milvus
Bien que les vecteurs binaires excellent dans des scénarios spécifiques, leur capacité d'expression est limitée, ce qui rend difficile la capture de relations sémantiques complexes. Par conséquent, dans les scénarios réels, les vecteurs binaires sont souvent utilisés avec d'autres types de vecteurs pour équilibrer l'efficacité et l'expressivité. Pour plus d'informations, voir Vecteur dense et Vecteur clairsemé.
Utiliser des vecteurs binaires dans Milvus
Ajouter un champ de vecteurs
Pour utiliser les vecteurs binaires dans Milvus, il faut d'abord définir un champ vectoriel pour stocker les vecteurs binaires lors de la création d'une collection. Ce processus comprend
Définir
datatype
comme étant le type de données vectorielles binaires pris en charge, c'est-à-direBINARY_VECTOR
.Spécifier les dimensions du vecteur à l'aide du paramètre
dim
. Notez quedim
doit être un multiple de 8, car les vecteurs binaires doivent être convertis en tableaux d'octets lors de l'insertion. Toutes les 8 valeurs booléennes (0 ou 1) seront regroupées dans un octet. Par exemple, sidim=128
, un tableau de 16 octets est nécessaire pour l'insertion.
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
}"
Dans cet exemple, un champ vectoriel nommé binary_vector
est ajouté pour stocker des vecteurs binaires. Le type de données de ce champ est BINARY_VECTOR
, avec une dimension de 128.
Définition des paramètres d'index pour le champ vectoriel
Pour accélérer les recherches, un index doit être créé pour le champ vectoriel binaire. L'indexation peut améliorer considérablement l'efficacité de la recherche de données vectorielles à grande échelle.
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}
}
]'
Dans l'exemple ci-dessus, un index nommé binary_vector_index
est créé pour le champ binary_vector
, en utilisant le type d'index BIN_IVF_FLAT
. L'adresse metric_type
est définie sur HAMMING
, ce qui indique que la distance de Hamming est utilisée pour mesurer la similarité.
Outre BIN_IVF_FLAT
, Milvus prend en charge d'autres types d'index pour les vecteurs binaires. Pour plus de détails, voir Index des vecteurs binaires. En outre, Milvus prend en charge d'autres mesures de similarité pour les vecteurs binaires. Pour plus d'informations, voir Types de métriques.
Création d'une collection
Une fois les paramètres du vecteur binaire et de l'index terminés, créez une collection contenant des vecteurs binaires. L'exemple ci-dessous utilise la méthode create_collection
pour créer une collection nommée 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
}"
Insérer des données
Après avoir créé la collection, utilisez la méthode insert
pour ajouter des données contenant des vecteurs binaires. Notez que les vecteurs binaires doivent être fournis sous la forme d'un tableau d'octets, où chaque octet représente 8 valeurs booléennes.
Par exemple, pour un vecteur binaire de 128 dimensions, un tableau de 16 octets est nécessaire (puisque 128 bits ÷ 8 bits/octet = 16 octets). Vous trouverez ci-dessous un exemple de code pour l'insertion de données.
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\"
}"
Effectuer une recherche de similarité
La recherche de similarité est l'une des fonctions principales de Milvus, qui vous permet de trouver rapidement les données les plus similaires à un vecteur d'interrogation en fonction de la distance entre les vecteurs. Pour effectuer une recherche de similarité à l'aide de vecteurs binaires, préparez le vecteur d'interrogation et les paramètres de recherche, puis appelez la méthode search
.
Lors des opérations de recherche, les vecteurs binaires doivent également être fournis sous la forme d'un tableau d'octets. Veillez à ce que la dimensionnalité du vecteur de requête corresponde à la dimension spécifiée lors de la définition de dim
et à ce que toutes les 8 valeurs booléennes soient converties en 1 octet.
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\"]
}"
Pour plus d'informations sur les paramètres de recherche de similarité, reportez-vous à la section Recherche ANN de base.