Passer au contenu principal
Le jeu de données Laion-400M contient 400 millions d’images avec des légendes en anglais. Laion propose désormais un jeu de données encore plus vaste, mais son utilisation sera similaire. Le jeu de données contient l’URL de l’image, les embeddings de l’image et de sa légende, un score de similarité entre l’image et sa légende, ainsi que des métadonnées, par exemple la largeur et la hauteur de l’image, la licence et un indicateur NSFW. Nous pouvons utiliser ce jeu de données pour illustrer la recherche approximative des plus proches voisins dans ClickHouse.

Préparation des données

Les embeddings et les métadonnées sont stockés dans des fichiers distincts parmi les données brutes. Une étape de préparation des données permet de télécharger les données, de fusionner les fichiers, de les convertir en CSV et de les importer dans ClickHouse. Vous pouvez utiliser pour cela le script download.sh suivant :
Le script process.py est défini comme suit :
Pour lancer le pipeline de préparation des données, exécutez :
Le jeu de données est réparti en 410 fichiers, chacun contenant env. 1 million de lignes. Si vous souhaitez travailler sur un sous-ensemble plus restreint des données, modifiez simplement les limites, par ex. seq 0 9 | .... (Le script Python ci-dessus est très lent (~2 à 10 minutes par fichier), consomme beaucoup de mémoire (41 GB par fichier), et les fichiers CSV obtenus sont volumineux (10 GB chacun) ; soyez donc prudent. Si vous avez suffisamment de RAM, augmentez la valeur de -P1 pour accroître le parallélisme. Si cela reste trop lent, envisagez une meilleure procédure d’ingestion — par exemple en convertissant les fichiers .npy en Parquet, puis en effectuant tout le reste du traitement avec ClickHouse.)

Créer une table

Pour créer une table sans index dans un premier temps, exécutez :
Pour importer les fichiers CSV dans ClickHouse :
Notez que la colonne id n’est là qu’à titre d’illustration et que le script la renseigne avec des valeurs non uniques. Pour effectuer une recherche vectorielle approximative par force brute, exécutez :
target est un tableau de 512 éléments et un paramètre client. Une méthode pratique pour obtenir de tels tableaux sera présentée à la fin de l’article. Pour l’instant, nous pouvons utiliser comme target l’embedding d’une image aléatoire d’un set LEGO. Résultat

Effectuer une recherche approximative de similarité vectorielle avec un index de similarité vectorielle

Définissons maintenant deux index de similarité vectorielle sur la table.
Les paramètres et les considérations de performance pour la création de l’index et la recherche sont décrits dans la documentation. La définition d’index ci-dessus spécifie un index HNSW utilisant la “distance cosinus” comme métrique de distance, avec le paramètre “hnsw_max_connections_per_layer” défini sur 64 et le paramètre “hnsw_candidate_list_size_for_construction” défini sur 256. L’index utilise des nombres à virgule flottante bfloat16 en demi-précision comme quantification afin d’optimiser l’utilisation de la mémoire. Pour construire et matérialiser l’index, exécutez ces instructions SQL :
La création et l’enregistrement de l’index peuvent prendre quelques minutes, voire plusieurs heures, selon le nombre de lignes et les paramètres de l’index HNSW. Pour effectuer une recherche vectorielle, il suffit d’exécuter à nouveau la même requête :
Résultat
La latence de la requête a fortement diminué, car les plus proches voisins ont été récupérés à l’aide de l’index vectoriel. La recherche de similarité vectorielle utilisant un index de similarité vectorielle peut renvoyer des résultats légèrement différents de ceux d’une recherche par force brute. Un index HNSW peut potentiellement atteindre un taux de rappel proche de 1 (la même précision qu’une recherche par force brute) grâce à une sélection rigoureuse des paramètres HNSW et à l’évaluation de la qualité de l’index.

Création d’embeddings avec des UDF

On cherche généralement à créer des embeddings pour de nouvelles images ou de nouvelles légendes d’image, puis à rechercher dans les données des paires image/légende similaires. On peut utiliser des UDF pour créer le vecteur target sans quitter le client. Il est important d’utiliser le même modèle pour créer les données et les nouveaux embeddings utilisés pour les recherches. Les scripts suivants utilisent le modèle ViT-B/32, qui est également celui sur lequel repose le jeu de données.

Embeddings de texte

Commencez par enregistrer le script Python suivant dans le répertoire user_scripts/ de votre répertoire de données ClickHouse, puis rendez-le exécutable (chmod +x encode_text.py). encode_text.py:
Créez ensuite encode_text_function.xml à un emplacement indiqué par <user_defined_executable_functions_config>/path/to/*_function.xml</user_defined_executable_functions_config> dans le fichier de configuration du serveur ClickHouse.
Vous pouvez désormais utiliser :
La première exécution sera lente, car elle charge le modèle, mais les suivantes seront rapides. Nous pouvons ensuite copier le résultat dans SET param_target=... et écrire facilement des requêtes. Sinon, la fonction encode_text() peut être utilisée directement comme argument de la fonction cosineDistance :
Notez que l’UDF encode_text() elle-même peut nécessiter quelques secondes pour effectuer le calcul et renvoyer le vecteur d’embedding.

Embeddings d’images

Les embeddings d’images peuvent être créés de la même manière, et nous fournissons un script Python capable de générer un embedding à partir d’une image stockée localement sous forme de fichier. encode_image.py
encode_image_function.xml
Récupérez une image d’exemple pour la recherche :
Exécutez ensuite cette requête pour générer l’embedding de l’image ci-dessus :
La requête de recherche complète est :
Dernière modification le 2 juillet 2026