milvus-logo
LFAI
Home
  • Guia do utilizador

Vetor esparso

Os vectores esparsos representam palavras ou frases utilizando a incorporação de vectores em que a maioria dos elementos é zero, sendo que apenas um elemento diferente de zero indica a presença de uma palavra específica. Os modelos de vectores esparsos, como o SPLADEv2, superam os modelos densos na pesquisa de conhecimentos fora do domínio, na consciência das palavras-chave e na interpretabilidade. São particularmente úteis na recuperação de informação, no processamento de linguagem natural e nos sistemas de recomendação, em que a combinação de vectores esparsos para a recuperação com um modelo grande para a classificação pode melhorar significativamente os resultados da recuperação.

No Milvus, a utilização de vectores esparsos segue um fluxo de trabalho semelhante ao dos vectores densos. Envolve a criação de uma coleção com uma coluna de vetor esparso, a inserção de dados, a criação de um índice e a realização de pesquisas de semelhança e consultas escalares.

Neste tutorial, você aprenderá a:

  • Preparar embeddings de vetores esparsos;
  • Criar uma coleção com um campo de vetor esparso;
  • Inserir entidades com embeddings de vetor esparso;
  • Indexar a coleção e executar a pesquisa ANN em vetores esparsos.

Para ver os vetores esparsos em ação, consulte hello_sparse.py.

notas

Atualmente, o suporte para vectores esparsos é uma funcionalidade beta na versão 2.4.0, com planos para a tornar geralmente disponível na versão 3.0.0.

Preparar embeddings de vectores esparsos

Para usar vectores esparsos no Milvus, prepare embeddings vectoriais num dos formatos suportados:

  • Matrizes esparsas: Utilize a família de classes scipy.sparse para representar os seus embeddings esparsos. Este método é eficiente para lidar com dados de grande escala e de alta dimensão.

  • Lista de dicionários: Represente cada embedding esparso como um dicionário, estruturado como {dimension_index: value, ...}, onde cada par chave-valor representa o índice de dimensão e seu valor correspondente.

    Exemplo:

    {2: 0.33, 98: 0.72, ...}
    
  • Lista de Iteráveis de Tuplas: Semelhante à lista de dicionários, mas utiliza um iterável de tuplas, [(dimension_index, value)], para especificar apenas as dimensões não nulas e os seus valores.

    Exemplo:

    [(2, 0.33), (98, 0.72), ...]
    

O exemplo a seguir prepara embeddings esparsos gerando uma matriz esparsa aleatória para 10.000 entidades, cada uma com 10.000 dimensões e uma densidade de esparsidade de 0,005.

# Prepare entities with sparse vector representation
import numpy as np
import random

rng = np.random.default_rng()

num_entities, dim = 10000, 10000

# Generate random sparse rows with an average of 25 non-zero elements per row
entities = [
    {
        "scalar_field": rng.random(),
        # To represent a single sparse vector row, you can use:
        # - Any of the scipy.sparse sparse matrices class family with shape[0] == 1
        # - Dict[int, float]
        # - Iterable[Tuple[int, float]]
        "sparse_vector": {
            d: rng.random() for d in random.sample(range(dim), random.randint(20, 30))
        },
    }
    for _ in range(num_entities)
]

# print the first entity to check the representation
print(entities[0])

# Output:
# {
#     'scalar_field': 0.520821523849214,
#     'sparse_vector': {
#         5263: 0.2639375518635271,
#         3573: 0.34701499565746674,
#         9637: 0.30856525997853057,
#         4399: 0.19771651149001523,
#         6959: 0.31025067641541815,
#         1729: 0.8265339135915016,
#         1220: 0.15303302147479103,
#         7335: 0.9436728846033107,
#         6167: 0.19929870545596562,
#         5891: 0.8214617920371853,
#         2245: 0.7852255053773395,
#         2886: 0.8787982039149889,
#         8966: 0.9000606703940665,
#         4910: 0.3001170013981104,
#         17: 0.00875671667413136,
#         3279: 0.7003425473001098,
#         2622: 0.7571360018373428,
#         4962: 0.3901879090102064,
#         4698: 0.22589525720196246,
#         3290: 0.5510228492587324,
#         6185: 0.4508413201390492
#     }
# }

notas

As dimensões do vetor têm de ser do tipo Python int ou numpy.integer, e os valores têm de ser do tipo Python float ou numpy.floating.

Para gerar embeddings, também pode utilizar o pacote model integrado na biblioteca PyMilvus, que oferece uma gama de funções de embedding. Para obter detalhes, consulte Embeddings.

Criar uma coleção com um campo vetorial esparso

Para criar uma coleção com um campo de vetor esparso, defina o tipo de dados do campo de vetor esparso como DataType.SPARSE_FLOAT_VECTOR. Ao contrário dos vectores densos, não é necessário especificar uma dimensão para os vectores esparsos.

from pymilvus import MilvusClient, DataType

# Create a MilvusClient instance
client = MilvusClient(uri="http://localhost:19530")

# Create a collection with a sparse vector field
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="scalar_field", datatype=DataType.DOUBLE)
# For sparse vector, no need to specify dimension
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR) # set `datatype` to `SPARSE_FLOAT_VECTOR`

client.create_collection(collection_name="test_sparse_vector", schema=schema)

Para obter detalhes sobre os parâmetros comuns de coleção, consulte create_collection() .

Inserir entidades com embeddings de vetores esparsos

Para inserir entidades com embeddings vetoriais esparsos, basta passar a lista de entidades para o método insert() método.

# Insert entities
client.insert(collection_name="test_sparse_vector", data=entities)

Indexar a coleção

Antes de efetuar pesquisas de similaridade, crie um índice para a coleção. Para obter mais informações sobre tipos e parâmetros de índices, consulte add_index() e create_index().

# Index the collection

# Prepare index 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", # the type of index to be created. set to `SPARSE_INVERTED_INDEX` or `SPARSE_WAND`.
    metric_type="IP", # the metric type to be used for the index. Currently, only `IP` (Inner Product) is supported.
    params={"drop_ratio_build": 0.2}, # the ratio of small vector values to be dropped during indexing.
)

# Create index
client.create_index(collection_name="test_sparse_vector", index_params=index_params)

Para a construção de índices em vectores esparsos, tenha em atenção o seguinte:

  • index_type: O tipo de índice a ser construído. Opções possíveis para vectores esparsos:

    • SPARSE_INVERTED_INDEX: Um índice invertido que mapeia cada dimensão para os seus vectores não nulos, facilitando o acesso direto aos dados relevantes durante as pesquisas. Ideal para conjuntos de dados com dados esparsos mas de alta dimensão.

    • SPARSE_WAND: Utiliza o algoritmo Weak-AND (WAND) para contornar rapidamente candidatos improváveis, concentrando a avaliação naqueles com maior potencial de classificação. Trata as dimensões como termos e os vectores como documentos, acelerando as pesquisas em conjuntos de dados grandes e esparsos.

  • metric_type: Apenas a métrica de distância IP (Inner Product) é suportada para vectores esparsos.

  • params.drop_ratio_build: O parâmetro de índice utilizado especificamente para vectores esparsos. Controla a proporção de valores de vectores pequenos que são excluídos durante o processo de indexação. Este parâmetro permite o ajuste fino do compromisso entre eficiência e precisão, desconsiderando pequenos valores ao construir o índice. Por exemplo, se drop_ratio_build = 0.3, durante a construção do índice, todos os valores de todos os vectores esparsos são reunidos e ordenados. Os 30% mais pequenos destes valores não são incluídos no índice, reduzindo assim a carga de trabalho computacional durante a pesquisa.

Para obter mais informações, consulte Índice na memória.

Depois de a coleção ser indexada e carregada na memória, utilize o método search() para recuperar os documentos relevantes com base na consulta.

# Load the collection into memory
client.load_collection(collection_name="test_sparse_vector")

# Perform ANN search on sparse vectors

# for demo purpose we search for the last inserted vector
query_vector = entities[-1]["sparse_vector"]

search_params = {
    "metric_type": "IP",
    "params": {"drop_ratio_search": 0.2}, # the ratio of small vector values to be dropped during search.
}

search_res = client.search(
    collection_name="test_sparse_vector",
    data=[query_vector],
    limit=3,
    output_fields=["pk", "scalar_field"],
    search_params=search_params,
)

for hits in search_res:
    for hit in hits:
        print(f"hit: {hit}")
        
# Output:
# hit: {'id': '448458373272710786', 'distance': 7.220192909240723, 'entity': {'pk': '448458373272710786', 'scalar_field': 0.46767865218233806}}
# hit: {'id': '448458373272708317', 'distance': 1.2287548780441284, 'entity': {'pk': '448458373272708317', 'scalar_field': 0.7315987515699472}}
# hit: {'id': '448458373272702005', 'distance': 0.9848432540893555, 'entity': {'pk': '448458373272702005', 'scalar_field': 0.9871869181562156}}

Ao configurar os parâmetros de pesquisa, tenha em atenção o seguinte:

  • params.drop_ratio_search: O parâmetro de pesquisa utilizado especificamente para vectores esparsos. Esta opção permite o ajuste fino do processo de pesquisa, especificando o rácio dos valores mais pequenos no vetor de consulta a ignorar. Ajuda a equilibrar a precisão e o desempenho da pesquisa. Quanto mais pequeno for o valor definido para drop_ratio_search, menos estes valores pequenos contribuem para a pontuação final. Ao ignorar alguns valores pequenos, o desempenho da pesquisa pode ser melhorado com um impacto mínimo na precisão.

Efetuar consultas escalares

Para além da pesquisa ANN, o Milvus também suporta consultas escalares em vectores esparsos. Estas consultas permitem-lhe obter documentos com base num valor escalar associado ao vetor esparso. Para mais informações sobre os parâmetros, consulte query().

Filtrar entidades com campo_escalar maior que 3:

# Perform a query by specifying filter expr
filter_query_res = client.query(
    collection_name="test_sparse_vector",
    filter="scalar_field > 0.999",
)

print(filter_query_res[:2])

# Output:
# [{'pk': '448458373272701862', 'scalar_field': 0.9994093623822689, 'sparse_vector': {173: 0.35266244411468506, 400: 0.49995484948158264, 480: 0.8757831454277039, 661: 0.9931875467300415, 1040: 0.0965644046664238, 1728: 0.7478245496749878, 2365: 0.4351981580257416, 2923: 0.5505295395851135, 3181: 0.7396837472915649, 3848: 0.4428485333919525, 4701: 0.39119353890419006, 5199: 0.790219783782959, 5798: 0.9623121619224548, 6213: 0.453134149312973, 6341: 0.745091438293457, 6775: 0.27766478061676025, 6875: 0.017947908490896225, 8093: 0.11834774166345596, 8617: 0.2289179265499115, 8991: 0.36600416898727417, 9346: 0.5502803921699524}}, {'pk': '448458373272702421', 'scalar_field': 0.9990218525410719, 'sparse_vector': {448: 0.587817907333374, 1866: 0.0994109958410263, 2438: 0.8672442436218262, 2533: 0.8063794374465942, 2595: 0.02122959867119789, 2828: 0.33827054500579834, 2871: 0.1984412521123886, 2938: 0.09674275666475296, 3154: 0.21552987396717072, 3662: 0.5236313343048096, 3711: 0.6463911533355713, 4029: 0.4041993021965027, 7143: 0.7370485663414001, 7589: 0.37588241696357727, 7776: 0.436136394739151, 7962: 0.06377989053726196, 8385: 0.5808192491531372, 8592: 0.8865005970001221, 8648: 0.05727503448724747, 9071: 0.9450633525848389, 9161: 0.146037295460701, 9358: 0.1903032660484314, 9679: 0.3146636486053467, 9974: 0.8561339378356934, 9991: 0.15841573476791382}}]

Filtra entidades por chave primária:

# primary keys of entities that satisfy the filter
pks = [ret["pk"] for ret in filter_query_res]

# Perform a query by primary key
pk_query_res = client.query(
    collection_name="test_sparse_vector", filter=f"pk == '{pks[0]}'"
)

print(pk_query_res)

# Output:
# [{'scalar_field': 0.9994093623822689, 'sparse_vector': {173: 0.35266244411468506, 400: 0.49995484948158264, 480: 0.8757831454277039, 661: 0.9931875467300415, 1040: 0.0965644046664238, 1728: 0.7478245496749878, 2365: 0.4351981580257416, 2923: 0.5505295395851135, 3181: 0.7396837472915649, 3848: 0.4428485333919525, 4701: 0.39119353890419006, 5199: 0.790219783782959, 5798: 0.9623121619224548, 6213: 0.453134149312973, 6341: 0.745091438293457, 6775: 0.27766478061676025, 6875: 0.017947908490896225, 8093: 0.11834774166345596, 8617: 0.2289179265499115, 8991: 0.36600416898727417, 9346: 0.5502803921699524}, 'pk': '448458373272701862'}]

Limites

Ao utilizar vectores esparsos em Milvus, considere os seguintes limites:

PERGUNTAS FREQUENTES

  • Que métrica de distância é suportada para vectores esparsos?

    Os vectores esparsos apenas suportam a métrica de distância IP (Inner Product) devido à elevada dimensionalidade dos vectores esparsos, o que torna a distância L2 e a distância cosseno impraticáveis.

  • Pode explicar a diferença entre SPARSE_INVERTED_INDEX e SPARSE_WAND, e como posso escolher entre eles?

    OSPARSE_INVERTED_INDEX é um índice invertido tradicional, enquanto o SPARSE_WAND utiliza o algoritmo Weak-AND para reduzir o número de avaliações de distância IP completas durante a pesquisa. O SPARSE_WAND é normalmente mais rápido, mas o seu desempenho pode diminuir com o aumento da densidade do vetor. Para escolher entre eles, realize experiências e benchmarks com base no seu conjunto de dados e caso de utilização específicos.

  • Como devo escolher os parâmetros drop_ratio_build e drop_ratio_search?

    A escolha de drop_ratio_build e drop_ratio_search depende das caraterísticas dos seus dados e dos seus requisitos de latência/rendimento e precisão da pesquisa.

  • Que tipos de dados são suportados para embeddings esparsos?

    A parte da dimensão deve ser um inteiro de 32 bits sem sinal, e a parte do valor pode ser um número de ponto flutuante de 32 bits não negativo.

  • A dimensão de uma incorporação esparsa pode ser qualquer valor discreto dentro do espaço uint32?

    Sim, com uma exceção. A dimensão de uma incorporação esparsa pode ser qualquer valor no intervalo de [0, maximum of uint32). Isso significa que você não pode usar o valor máximo de uint32.

  • As pesquisas em segmentos crescentes são conduzidas através de um índice ou por força bruta?

    As pesquisas em segmentos crescentes são realizadas através de um índice do mesmo tipo que o índice do segmento selado. Para novos segmentos crescentes antes de o índice ser construído, é usada uma pesquisa de força bruta.

  • É possível ter vetores esparsos e densos em uma única coleção?

    Sim, com suporte a vários tipos de vetores, é possível criar coleções com colunas de vetores esparsos e densos e executar pesquisas híbridas nelas.

  • Quais são os requisitos para que os embeddings esparsos sejam inseridos ou pesquisados?

    As incorporações esparsas devem ter pelo menos um valor diferente de zero e os índices de vetor devem ser não negativos.

Traduzido porDeepLogo

Try Zilliz Cloud for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started
Feedback

Esta página foi útil?