HNSW_SQ

HNSW_SQ combine les graphes Hierarchical Navigable Small World (HNSW) avec la quantification scalaire (SQ), créant ainsi une méthode d'indexation vectorielle avancée qui offre un compromis contrôlable entre la taille et la précision. Par rapport aux graphes HNSW standard, ce type d'index maintient une vitesse élevée de traitement des requêtes tout en augmentant légèrement le temps de construction de l'index.

Vue d'ensemble

HNSW_SQ combine deux techniques d'indexation : HNSW pour une navigation rapide dans les graphes et SQ pour une compression vectorielle efficace.

HNSW

HNSW construit un graphe multicouche où chaque nœud correspond à un vecteur de l'ensemble de données. Dans ce graphe, les nœuds sont connectés en fonction de leur similarité, ce qui permet de parcourir rapidement l'espace de données. La structure hiérarchique permet à l'algorithme de recherche de réduire le nombre de voisins candidats, ce qui accélère considérablement le processus de recherche dans les espaces à haute dimension.

Pour plus d'informations, voir HNSW.

SQ

Le SQ est une méthode de compression des vecteurs qui consiste à les représenter avec moins de bits. Par exemple :

  • SQ8 utilise 8 bits, cartographiant les valeurs en 256 niveaux. Pour plus d'informations, voir IVF_SQ8.

  • SQ6 utilise 6 bits pour représenter chaque valeur en virgule flottante, ce qui donne 64 niveaux discrets.

Hnsw Sq Hnsw Sq

Cette réduction de la précision diminue considérablement l'empreinte mémoire et accélère le calcul tout en conservant la structure essentielle des données.

SQ4UCompatible with Milvus 2.6.8+

Pour les scénarios exigeant une vitesse d'interrogation extrême et une utilisation minimale de la mémoire, Milvus introduit SQ4U, une quantification scalaire uniforme de 4 bits. Il s'agit d'une forme agressive de quantification scalaire qui comprime la valeur en virgule flottante de chaque dimension en un entier non signé de 4 bits.

Le "U" de SQ4U signifie Uniforme. Contrairement à la quantification scalaire non uniforme, qui calcule généralement les valeurs minimales et maximales indépendamment pour chaque dimension (quantification par dimension), SQ4U applique une stratégie de quantification uniforme globale:

  1. Statistiques globales: Le système calcule une seule valeur minimale vmin et une seule plage de valeurs vdiff qui s'applique à toutes les dimensions du vecteur (ou au segment entier du vecteur).

  2. Cartographie uniforme: La plage de valeurs globale est divisée en 16 intervalles égaux. Chaque valeur à virgule flottante du vecteur, quelle que soit la dimension à laquelle elle appartient, est mappée sur un entier de 4 bits (0-15) à l'aide de ces paramètres partagés.

Avantages en termes de performances :

  • Taux de compression 8x : La taille est divisée par 8 par rapport à FP32 et par 2 par rapport à SQ8, ce qui réduit considérablement la pression exercée sur la bande passante de la mémoire, qui est souvent le goulot d'étranglement de la recherche vectorielle.

  • Optimisation SIMD : La structure compacte permet aux unités centrales modernes (AVX2/AVX-512) de traiter plus de dimensions par cycle. L'utilisation de paramètres globaux élimine la nécessité de charger des valeurs d'échelle/de décalage variables pendant le calcul de la distance, ce qui permet de maintenir le pipeline d'instructions entièrement saturé.

  • Efficacité de la mémoire cache : Des vecteurs de plus petite taille signifient que plus de données sont stockées dans le cache de l'unité centrale, ce qui réduit la latence causée par l'accès à la mémoire.

En raison du partage global des paramètres, SQ4U donne les meilleurs résultats sur des données normalisées ou des ensembles de données dont la distribution des valeurs est cohérente d'une dimension à l'autre.

SNPD + SQ

HNSW_SQ combine les forces de HNSW et de SQ pour permettre une recherche efficace du plus proche voisin. Voici comment fonctionne le processus :

  1. Compression des données : SQ compresse les vecteurs à l'aide de sq_type (par exemple, SQ6 ou SQ8), ce qui réduit l'utilisation de la mémoire. Cette compression peut réduire la précision, mais elle permet au système de traiter des ensembles de données plus importants.

  2. Construction du graphique : Les vecteurs compressés sont utilisés pour construire un graphe HNSW. Comme les données sont compressées, le graphe résultant est plus petit et plus rapide à rechercher.

  3. Recherche de candidats : Lorsqu'un vecteur de requête est fourni, l'algorithme utilise les données compressées pour identifier rapidement un ensemble de voisins candidats dans le graphe HNSW.

  4. Affinage des résultats (facultatif) : Les résultats initiaux des candidats peuvent être affinés pour une meilleure précision, en fonction des paramètres suivants :

    • refine: Contrôle si cette étape d'affinage est activée. Lorsqu'il est défini sur true, le système recalcule les distances à l'aide de représentations plus précises ou non compressées.

    • refine_type: Spécifie le niveau de précision des données utilisées pendant l'affinage (par exemple, SQ6, SQ8, BF16). Un choix de précision plus élevé, tel que FP32, peut donner des résultats plus précis, mais nécessite plus de mémoire. La précision doit être supérieure à celle de l'ensemble de données compressées d'origine de sq_type.

    • refine_k: Agit comme un facteur d'agrandissement. Par exemple, si votre top k est 100 et que refine_k est 2, le système reclasse les 200 premiers candidats et renvoie les 100 meilleurs, ce qui améliore la précision globale.

Pour obtenir la liste complète des paramètres et des valeurs valides, reportez-vous à la section Paramètres de l'index.

Création d'un index

Pour construire un index HNSW_SQ sur un champ de vecteurs dans Milvus, utilisez la méthode add_index(), en spécifiant les paramètres index_type, metric_type et d'autres paramètres pour l'index.

from pymilvus import MilvusClient

# Prepare index building params
index_params = MilvusClient.prepare_index_params()

index_params.add_index(
    field_name="your_vector_field_name", # Name of the vector field to be indexed
    index_type="HNSW_SQ", # Type of the index to create
    index_name="vector_index", # Name of the index to create
    metric_type="L2", # Metric type used to measure similarity
    params={
        "M": 64, # Maximum number of neighbors each node can connect to in the graph
        "efConstruction": 100, # Number of candidate neighbors considered for connection during index construction
        "sq_type": "SQ6", # Scalar quantizer type
        "refine": true, # Whether to enable the refinement step
        "refine_type": "SQ8" # Precision level of data used for refinement
    } # Index building params
)

Dans cette configuration :

  • index_type: Le type d'index à construire. Dans cet exemple, la valeur est HNSW_SQ.

  • metric_type: La méthode utilisée pour calculer la distance entre les vecteurs. Les valeurs prises en charge sont COSINE, L2 et IP. Pour plus d'informations, reportez-vous à la section Types de métriques.

  • params: Options de configuration supplémentaires pour la construction de l'index. Pour plus d'informations, reportez-vous à la section Paramètres de construction de l'index.

Une fois les paramètres de l'index configurés, vous pouvez créer l'index en utilisant directement la méthode create_index() ou en transmettant les paramètres de l'index dans la méthode create_collection. Pour plus d'informations, reportez-vous à la section Créer une collection.

Recherche sur l'index

Une fois l'index construit et les entités insérées, vous pouvez effectuer des recherches de similarité sur l'index.

search_params = {
    "params": {
        "ef": 10, # Parameter controlling query time/accuracy trade-off
        "refine_k": 1 # The magnification factor
    }
}

res = MilvusClient.search(
    collection_name="your_collection_name", # Collection name
    anns_field="vector_field",  # Vector field name
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],  # Query vector
    limit=3,  # TopK results to return
    search_params=search_params
)

Dans cette configuration :

Paramètres de l'index

Cette section présente une vue d'ensemble des paramètres utilisés pour construire un index et effectuer des recherches dans l'index.

Paramètres de construction d'index

Le tableau suivant répertorie les paramètres qui peuvent être configurés sur params lors de la création d'un index.

Paramètre

Description de l'index

Plage de valeurs

Suggestion de réglage

HNSW

M

Nombre maximal de connexions (ou d'arêtes) que chaque nœud peut avoir dans le graphe, y compris les arêtes sortantes et entrantes.

Ce paramètre affecte directement la construction de l'index et la recherche.

Type: Entier

Plage de valeurs: [2, 2048]

Valeur par défaut: 30 (jusqu'à 30 arêtes sortantes et 30 arêtes entrantes par nœud)

Une valeur plus élevée de M conduit généralement à une plus grande précision, mais augmente la charge de mémoire et ralentit à la fois la construction de l'index et la recherche.

Envisagez d'augmenter M pour les ensembles de données à haute dimensionnalité ou lorsqu'un rappel élevé est crucial.

Pensez à diminuer M lorsque l'utilisation de la mémoire et la vitesse de recherche sont des préoccupations majeures.

Dans la plupart des cas, nous vous recommandons de fixer une valeur comprise dans cette fourchette : [5, 100].

efConstruction

Nombre de voisins candidats pris en compte pour la connexion lors de la construction de l'index.

Un plus grand nombre de candidats est évalué pour chaque nouvel élément, mais le nombre maximum de connexions réellement établies est toujours limité par M.

Type: Entier

Plage de valeurs: [1, int_max]

Valeur par défaut: 360

Une valeur plus élevée ( efConstruction ) permet généralement d'obtenir un index plus précis, car un plus grand nombre de connexions potentielles sont explorées. Cependant, cela entraîne également un allongement du temps d'indexation et une augmentation de l'utilisation de la mémoire pendant la construction.

Envisagez d'augmenter efConstruction pour améliorer la précision, en particulier dans les scénarios où le temps d'indexation est moins critique.

Pensez à diminuer efConstruction pour accélérer la construction de l'index lorsque les ressources sont limitées.

Dans la plupart des cas, nous vous recommandons de définir une valeur comprise dans cette fourchette : [50, 500].

SQ

sq_type

Spécifie la méthode de quantification scalaire pour la compression des vecteurs. Chaque option offre un équilibre différent entre la compression et la précision :

  • SQ4U: Encode les vecteurs à l'aide d'une quantification uniforme sur 4 bits. Ce mode offre la vitesse et la compression les plus élevées.

  • SQ6: Encode les vecteurs à l'aide d'entiers de 6 bits.

  • SQ8: Encode les vecteurs en utilisant des entiers de 8 bits.

  • BF16: Utilise le format Bfloat16.

  • FP16: Utilise le format standard à virgule flottante de 16 bits.

Type: Chaîne

Plage: [ SQ4U, SQ6, SQ8, BF16, FP16 ]

Valeur par défaut: SQ8

Le choix de sq_type dépend des besoins spécifiques de l'application. SQ4U est choisi pour une vitesse maximale et une efficacité de la mémoire. SQ6 ou SQ8 peuvent convenir pour des performances équilibrées. En revanche, si la précision est primordiale, BF16 ou FP16 peuvent être préférés.

refine

Indicateur booléen qui contrôle si une étape d'affinage est appliquée pendant la recherche. L'affinage consiste à reclasser les résultats initiaux en calculant les distances exactes entre le vecteur de la requête et les candidats.

Type: Booléen

Portée: [true, false]

Valeur par défaut: false

Définissez true si une grande précision est essentielle et que vous pouvez tolérer des temps de recherche légèrement plus lents. Utilisez false si la vitesse est une priorité et qu'un compromis mineur au niveau de la précision est acceptable.

refine_type

Détermine la précision des données utilisées pour l'affinage.

Cette précision doit être supérieure à celle des vecteurs compressés (définie par sq_type), ce qui affecte à la fois la précision des vecteurs reclassés et leur encombrement en mémoire.

Type: Chaîne

Portée:[ SQ6, SQ8, BF16, FP16, FP32 ]

Valeur par défaut: Aucune

Utilisez FP32 pour une précision maximale avec un coût mémoire plus élevé, ou SQ6/SQ8 pour une meilleure compression. BF16 et FP16 offrent une alternative équilibrée.

Paramètres de recherche spécifiques à l'index

Le tableau suivant répertorie les paramètres qui peuvent être configurés dans search_params.params lors d'une recherche dans l'index.

Paramètre

Paramètre Description

Plage de valeurs

Suggestion de réglage

HNSW

ef

Contrôle l'étendue de la recherche lors de l'extraction des plus proches voisins. Il détermine le nombre de nœuds visités et évalués en tant que plus proches voisins potentiels.

Ce paramètre n'affecte que le processus de recherche et s'applique exclusivement à la couche inférieure du graphe.

Type: Entier

Plage de valeurs: [1, int_max]

Valeur par défaut: limit (TopK plus proches voisins à retourner)

Une valeur plus élevée de ef permet généralement d'améliorer la précision de la recherche, car un plus grand nombre de voisins potentiels sont pris en compte. Cependant, cela augmente également le temps de recherche.

Envisagez d'augmenter ef lorsqu'il est essentiel d'obtenir un rappel élevé et que la vitesse de recherche est moins importante.

Pensez à diminuer ef pour privilégier les recherches plus rapides, en particulier dans les scénarios où une légère réduction de la précision est acceptable.

Dans la plupart des cas, nous vous recommandons de fixer une valeur comprise dans cette fourchette : [K, 10K].

SQ

refine_k

Facteur d'agrandissement qui contrôle le nombre de candidats supplémentaires examinés au cours de l'étape d'affinage, par rapport aux K premiers résultats demandés.

Type: Flottant

Plage de valeurs: [1, float_max)

Valeur par défaut: 1

Des valeurs plus élevées de refine_k peuvent améliorer le rappel et la précision, mais augmentent également le temps de recherche et l'utilisation des ressources. Une valeur de 1 signifie que le processus d'affinement ne prend en compte que les K premiers résultats.