Vetor binário
Os vectores binários são uma forma especial de representação de dados que convertem os vectores tradicionais de vírgula flutuante de elevada dimensão em vectores binários que contêm apenas 0s e 1s. Esta transformação não só comprime o tamanho do vetor, como também reduz os custos de armazenamento e computacionais, mantendo a informação semântica. Quando a precisão para caraterísticas não críticas não é essencial, os vectores binários podem efetivamente manter a maior parte da integridade e utilidade dos vectores de vírgula flutuante originais.
Os vectores binários têm uma vasta gama de aplicações, especialmente em situações em que a eficiência computacional e a otimização do armazenamento são cruciais. Em sistemas de IA de grande escala, como motores de pesquisa ou sistemas de recomendação, o processamento em tempo real de grandes quantidades de dados é fundamental. Ao reduzir o tamanho dos vectores, os vectores binários ajudam a diminuir a latência e os custos computacionais sem sacrificar significativamente a precisão. Além disso, os vectores binários são úteis em ambientes com recursos limitados, como dispositivos móveis e sistemas incorporados, onde a memória e a capacidade de processamento são limitadas. Através da utilização de vectores binários, podem ser implementadas funções de IA complexas nestes ambientes restritos, mantendo um elevado desempenho.
Panorama geral
Os vectores binários são um método de codificação de objectos complexos (como imagens, texto ou áudio) em valores binários de comprimento fixo. Em Milvus, os vectores binários são normalmente representados como matrizes de bits ou matrizes de bytes. Por exemplo, um vetor binário de 8 dimensões pode ser representado como [1, 0, 1, 1, 0, 0, 1, 0]
.
O diagrama abaixo mostra como os vectores binários representam a presença de palavras-chave no conteúdo do texto. Neste exemplo, é utilizado um vetor binário de 10 dimensões para representar dois textos diferentes(Texto 1 e Texto 2), em que cada dimensão corresponde a uma palavra do vocabulário: 1 indica a presença da palavra no texto, enquanto 0 indica a sua ausência.
Representação vetorial binária do conteúdo do texto
Os vectores binários têm as seguintes caraterísticas
Armazenamento eficiente: Cada dimensão requer apenas 1 bit de armazenamento, reduzindo significativamente o espaço de armazenamento.
Computação rápida: A semelhança entre vectores pode ser rapidamente calculada utilizando operações bit a bit como XOR.
Comprimento fixo: O comprimento do vetor permanece constante, independentemente do comprimento do texto original, facilitando a indexação e a recuperação.
Simples e intuitivo: Reflecte diretamente a presença de palavras-chave, tornando-o adequado para determinadas tarefas de recuperação especializadas.
Os vectores binários podem ser gerados através de vários métodos. No processamento de texto, podem ser utilizados vocabulários predefinidos para definir bits correspondentes com base na presença de palavras. Para o processamento de imagens, os algoritmos de hashing percetual (como o pHash) podem gerar caraterísticas binárias das imagens. Nas aplicações de aprendizagem automática, os resultados dos modelos podem ser binarizados para obter representações vectoriais binárias.
Após a vectorização binária, os dados podem ser armazenados no Milvus para gestão e recuperação de vectores. O diagrama abaixo mostra o processo básico.
Utilizar vectores binários no Milvus
Embora os vectores binários sejam excelentes em cenários específicos, têm limitações na sua capacidade de expressão, o que dificulta a captura de relações semânticas complexas. Por isso, em cenários do mundo real, os vectores binários são frequentemente utilizados juntamente com outros tipos de vectores para equilibrar a eficiência e a expressividade. Para obter mais informações, consulte Vetor denso e Vetor esparso.
Utilizar vectores binários no Milvus
Adicionar campo de vetor
Para utilizar vectores binários no Milvus, comece por definir um campo vetorial para armazenar vectores binários ao criar uma coleção. Este processo inclui.
Definir
datatype
como o tipo de dados de vetor binário suportado, ou seja,BINARY_VECTOR
.Especificar as dimensões do vetor utilizando o parâmetro
dim
. Observe quedim
deve ser um múltiplo de 8, pois os vetores binários devem ser convertidos em uma matriz de bytes ao serem inseridos. Cada 8 valores booleanos (0 ou 1) serão empacotados em 1 byte. Por exemplo, sedim=128
, é necessária uma matriz de 16 bytes para a inserção.
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
}"
Neste exemplo, é adicionado um campo de vetor chamado binary_vector
para armazenar vectores binários. O tipo de dados deste campo é BINARY_VECTOR
, com uma dimensão de 128.
Definir parâmetros de índice para o campo de vetor
Para acelerar as pesquisas, deve ser criado um índice para o campo de vetor binário. A indexação pode melhorar significativamente a eficiência da recuperação de dados vectoriais em grande 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}
}
]'
No exemplo acima, um índice chamado binary_vector_index
é criado para o campo binary_vector
, usando o tipo de índice BIN_IVF_FLAT
. O metric_type
é definido como HAMMING
, indicando que a distância de Hamming é utilizada para a medição da semelhança.
Para além de BIN_IVF_FLAT
, o Milvus suporta outros tipos de índices para vectores binários. Para mais detalhes, consulte Índices de vectores binários. Além disso, o Milvus suporta outras métricas de similaridade para vectores binários. Para obter mais informações, consulte Tipos de métricas.
Criar coleção
Quando as definições do vetor binário e do índice estiverem concluídas, crie uma coleção que contenha vectores binários. O exemplo abaixo usa o método create_collection
para criar uma coleção chamada 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
}"
Inserir dados
Depois de criar a coleção, use o método insert
para adicionar dados que contenham vetores binários. Note que os vectores binários devem ser fornecidos sob a forma de uma matriz de bytes, em que cada byte representa 8 valores booleanos.
Por exemplo, para um vetor binário de 128 dimensões, é necessário um vetor de 16 bytes (uma vez que 128 bits ÷ 8 bits/byte = 16 bytes). Segue-se um exemplo de código para inserir dados.
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\"
}"
Efetuar pesquisa de semelhanças
A pesquisa por similaridade é uma das principais caraterísticas do Milvus, permitindo-lhe encontrar rapidamente os dados que são mais semelhantes a um vetor de consulta com base na distância entre vectores. Para efetuar uma pesquisa por semelhança utilizando vectores binários, prepare o vetor de consulta e os parâmetros de pesquisa e, em seguida, chame o método search
.
Durante as operações de pesquisa, os vectores binários também têm de ser fornecidos sob a forma de uma matriz de bytes. Certifique-se de que a dimensionalidade do vetor de consulta corresponde à dimensão especificada ao definir dim
e que cada 8 valores booleanos são convertidos em 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 obter mais informações sobre os parâmetros de pesquisa de similaridade, consulte Pesquisa ANN básica.