Le système de recherche par image de deuxième génération
Cet article est la deuxième partie de The Journey to Optimizing Billion-scale Image Search par UPYUN. Si vous avez manqué la première partie, cliquez ici.
Le système de recherche par image de deuxième génération
Le système de recherche par image de deuxième génération choisit techniquement la solution CNN + Milvus. Le système est basé sur les vecteurs de caractéristiques et fournit un meilleur support technique.
Extraction de caractéristiques
Dans le domaine de la vision par ordinateur, l'utilisation de l'intelligence artificielle est devenue courante. De même, l'extraction des caractéristiques du système de recherche par image de deuxième génération utilise le réseau neuronal convolutif (CNN) comme technologie sous-jacente
Le terme CNN est difficile à comprendre. Nous nous attacherons ici à répondre à deux questions :
- Que peut faire le CNN ?
- Pourquoi utiliser le CNN pour une recherche d'images ?
1-meme.jpg
Il existe de nombreux concours dans le domaine de l'IA et la classification d'images est l'un des plus importants. La classification d'images consiste à déterminer si le contenu de l'image concerne un chat, un chien, une pomme, une poire ou d'autres types d'objets.
Que peut faire le CNN ? Il peut extraire des caractéristiques et reconnaître des objets. Il extrait des caractéristiques de plusieurs dimensions et mesure à quel point les caractéristiques d'une image sont proches de celles d'un chat ou d'un chien. Nous pouvons choisir les plus proches comme résultat d'identification, ce qui indique si le contenu d'une image spécifique concerne un chat, un chien ou autre chose.
Quel est le lien entre la fonction d'identification d'objets du CNN et la recherche par image ? Ce que nous voulons, ce n'est pas le résultat final de l'identification, mais le vecteur de caractéristiques extrait de plusieurs dimensions. Les vecteurs de caractéristiques de deux images au contenu similaire doivent être proches.
Quel modèle CNN dois-je utiliser ?
La réponse est VGG16. Pourquoi le choisir ? Tout d'abord, le VGG16 a une bonne capacité de généralisation, c'est-à-dire qu'il est très polyvalent. Deuxièmement, les vecteurs de caractéristiques extraits par VGG16 ont 512 dimensions. S'il y a très peu de dimensions, la précision peut être affectée. S'il y a trop de dimensions, le coût du stockage et du calcul de ces vecteurs de caractéristiques est relativement élevé.
L'utilisation de CNN pour extraire les caractéristiques des images est une solution courante. Nous pouvons utiliser VGG16 comme modèle et Keras + TensorFlow pour l'implémentation technique. Voici l'exemple officiel de Keras :
from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input
import numpy as np
model = VGG16(weights=’imagenet’, include_top=False)
img_path = ‘elephant.jpg’
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
features = model.predict(x)
Les caractéristiques extraites ici sont des vecteurs de caractéristiques.
1. Normalisation
Pour faciliter les opérations ultérieures, on normalise souvent les features :
Ce qui est utilisé par la suite est également le vecteur normalisé norm_feat
.
2. Description de l'image
L'image est chargée à l'aide de la méthode image.load_img
de keras.preprocessing
:
from keras.preprocessing import image
img_path = 'elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))
En fait, il s'agit de la méthode TensorFlow appelée par Keras. Pour plus de détails, voir la documentation TensorFlow. L'objet image final est en fait une instance de PIL Image (le PIL utilisé par TensorFlow).
3. Conversion en octets
Dans la pratique, le contenu des images est souvent transmis par le réseau. Par conséquent, au lieu de charger des images à partir d'un chemin, nous préférons convertir les données en octets directement en objets image, c'est-à-dire en PIL Images :
import io
from PIL import Image
# img_bytes: 图片内容 bytes
img = Image.open(io.BytesIO(img_bytes))
img = img.convert('RGB')
img = img.resize((224, 224), Image.NEAREST)
L'image ci-dessus est identique au résultat obtenu par la méthode image.load_img. Il y a deux choses auxquelles il faut faire attention :
- Vous devez effectuer une conversion RVB.
- Vous devez redimensionner (resize est le deuxième paramètre de
load_img method
).
4. Traitement des bordures noires
Les images, telles que les captures d'écran, peuvent parfois présenter de nombreuses bordures noires. Ces bordures noires n'ont aucune valeur pratique et causent beaucoup d'interférences. C'est pourquoi la suppression des bordures noires est également une pratique courante.
Une bordure noire est essentiellement une ligne ou une colonne de pixels où tous les pixels sont (0, 0, 0) (image RVB). Pour supprimer la bordure noire, il faut trouver ces lignes ou colonnes et les supprimer. Il s'agit en fait d'une multiplication matricielle 3D dans NumPy.
Exemple de suppression de bordures noires horizontales :
# -*- coding: utf-8 -*-
import numpy as np
from keras.preprocessing import image
def RemoveBlackEdge(img):
Args:
img: PIL image instance
Returns:
PIL image instance
"""
width = img.width
img = image.img_to_array(img)
img_without_black = img[~np.all(img == np.zeros((1, width, 3), np.uint8), axis=(1, 2))]
img = image.array_to_img(img_without_black)
return img
C'est à peu près de cela que je veux parler en utilisant CNN pour extraire des caractéristiques d'image et mettre en œuvre d'autres traitements d'image. Jetons maintenant un coup d'œil aux moteurs de recherche vectorielle.
Moteur de recherche vectoriel
Le problème de l'extraction des vecteurs de caractéristiques des images a été résolu. Les problèmes restants sont les suivants :
- Comment stocker les vecteurs caractéristiques ?
- Comment calculer la similarité des vecteurs caractéristiques, c'est-à-dire comment effectuer une recherche ? Le moteur de recherche vectorielle open-source Milvus peut résoudre ces deux problèmes. Jusqu'à présent, il fonctionne bien dans notre environnement de production.
3-milvus-logo.png
Milvus, le moteur de recherche vectorielle
Extraire des vecteurs de caractéristiques d'une image est loin d'être suffisant. Nous devons également gérer dynamiquement ces vecteurs de caractéristiques (ajout, suppression et mise à jour), calculer la similarité des vecteurs et renvoyer les données vectorielles dans l'intervalle de voisinage le plus proche. Le moteur de recherche vectorielle open-source Milvus exécute ces tâches de manière satisfaisante.
Le reste de cet article décrit les pratiques spécifiques et les points à noter.
1. Exigences pour l'unité centrale
Pour utiliser Milvus, votre processeur doit supporter le jeu d'instructions avx2. Pour les systèmes Linux, utilisez la commande suivante pour vérifier quels jeux d'instructions votre processeur prend en charge :
cat /proc/cpuinfo | grep flags
Vous obtiendrez alors quelque chose comme :
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti intel_ppin tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm xsaveopt cqm_llc cqm_occup_llc dtherm ida arat pln pts
Ce qui suit les drapeaux est le jeu d'instructions pris en charge par votre unité centrale. Bien entendu, ces informations sont beaucoup plus nombreuses que celles dont j'ai besoin. Je veux juste voir si un jeu d'instructions spécifique, comme avx2, est supporté. Il suffit d'ajouter un grep pour le filtrer :
cat /proc/cpuinfo | grep flags | grep avx2
Si aucun résultat n'est retourné, cela signifie que ce jeu d'instructions spécifique n'est pas supporté. Vous devez alors changer de machine.
2. Planification de la capacité
La planification de la capacité est notre première préoccupation lorsque nous concevons un système. Quelle quantité de données devons-nous stocker ? De quelle quantité de mémoire et d'espace disque ces données ont-elles besoin ?
Faisons quelques calculs rapides. Chaque dimension d'un vecteur est un float32. Un type float32 occupe 4 octets. Un vecteur de 512 dimensions nécessite donc 2 Ko d'espace de stockage. De même :
- Mille vecteurs à 512 dimensions nécessitent 2 Mo de stockage.
- Un million de vecteurs à 512 dimensions nécessitent 2 Go de stockage.
- 10 millions de vecteurs à 512 dimensions nécessitent 20 Go de stockage.
- 100 millions de vecteurs à 512 dimensions nécessitent 200 Go de stockage.
- Un milliard de vecteurs à 512 dimensions nécessite 2 To de stockage.
Si nous voulons stocker toutes les données dans la mémoire, le système a besoin d'au moins la capacité de mémoire correspondante.
Il est recommandé d'utiliser l'outil officiel de calcul de la taille : Milvus sizing tool.
En fait, notre mémoire n'est peut-être pas si grande que cela (cela n'a pas vraiment d'importance si vous n'avez pas assez de mémoire). Milvus efface automatiquement les données sur le disque). Outre les données vectorielles d'origine, nous devons également prendre en compte le stockage d'autres données telles que les journaux.
3. Configuration du système
Pour plus d'informations sur la configuration du système, voir la documentation Milvus :
- Configuration du serveur Milvus : https://milvus.io/docs/v0.10.1/milvus_config.md
4. Conception de la base de données
Collection et partition
- La collection est également connue sous le nom de table.
- La partition fait référence aux partitions à l'intérieur d'une collection.
L'implémentation sous-jacente de la partition est en fait la même que celle de la collection, sauf qu'une partition se trouve sous une collection. Mais avec les partitions, l'organisation des données devient plus flexible. Nous pouvons également interroger une partition spécifique dans une collection afin d'obtenir de meilleurs résultats.
Combien de collections et de partitions pouvons-nous avoir ? Les informations de base sur les collections et les partitions se trouvent dans les métadonnées. Milvus utilise SQLite (intégration interne de Milvus) ou MySQL (nécessite une connexion externe) pour la gestion interne des métadonnées. Si vous utilisez SQLite par défaut pour gérer les métadonnées, vous subirez de graves pertes de performances lorsque le nombre de collections et de partitions est trop important. Par conséquent, le nombre total de collections et de partitions ne doit pas dépasser 50 000 (Milvus 0.8.0 limite ce nombre à 4 096). Si vous devez définir un nombre plus important, il est recommandé d'utiliser MySQL via une connexion externe.
La structure de données prise en charge par la collecte et la partition de Milvus est très simple, à savoir ID + vector
. En d'autres termes, la table ne comporte que deux colonnes : ID et données vectorielles.
Remarque :
- Les ID doivent être des nombres entiers.
- Nous devons nous assurer que l'ID est unique au sein d'une collection plutôt qu'au sein d'une partition.
Filtrage conditionnel
Lorsque nous utilisons des bases de données traditionnelles, nous pouvons spécifier des valeurs de champ comme conditions de filtrage. Bien que Milvus ne filtre pas exactement de la même manière, nous pouvons mettre en œuvre un filtrage conditionnel simple à l'aide de collections et de partitions. Par exemple, nous disposons d'une grande quantité de données d'images et les données appartiennent à des utilisateurs spécifiques. Nous pouvons alors diviser les données en partitions par utilisateur. Par conséquent, utiliser l'utilisateur comme condition de filtrage revient en fait à spécifier la partition.
Données structurées et cartographie vectorielle
Milvus ne prend en charge que la structure de données ID + vecteur. Mais dans les scénarios d'entreprise, ce dont nous avons besoin, c'est de données structurées porteuses de sens pour l'entreprise. En d'autres termes, nous devons trouver des données structurées par le biais de vecteurs. Par conséquent, nous devons gérer les relations de mappage entre les données structurées et les vecteurs par le biais de l'ID.
structured data ID <--> mapping table <--> Milvus ID
Sélection de l'index
Vous pouvez consulter les articles suivants :
- Types d'index : https://www.milvus.io/docs/v0.10.1/index.md
- Comment sélectionner un index : https://medium.com/@milvusio/how-to-choose-an-index-in-milvus-4f3d15259212
5. Traitement des résultats de la recherche
Les résultats de recherche de Milvus sont une collection d'ID + distance :
- ID : l'ID dans une collection.
- Distance : une valeur de distance de 0 ~ 1 indique le niveau de similarité ; plus la valeur est petite, plus les deux vecteurs sont similaires.
Filtrer les données dont l'ID est -1
Lorsque le nombre de collections est trop faible, les résultats de la recherche peuvent contenir des données dont l'ID est -1. Nous devons les filtrer nous-mêmes.
Pagination
La recherche de vecteurs est très différente. Les résultats de la requête sont triés par ordre décroissant de similarité, et les résultats les plus similaires (topK) sont sélectionnés (topK est spécifié par l'utilisateur au moment de la requête).
Milvus ne prend pas en charge la pagination. Nous devons mettre en œuvre la fonction de pagination nous-mêmes si nous en avons besoin dans le cadre de notre activité. Par exemple, si nous avons dix résultats sur chaque page et que nous ne voulons afficher que la troisième page, nous devons spécifier que topK = 30 et ne renvoyer que les dix derniers résultats.
Seuil de similarité pour les entreprises
La distance entre les vecteurs de deux images est comprise entre 0 et 1. Si nous voulons décider si deux images sont similaires dans un scénario commercial spécifique, nous devons spécifier un seuil dans cette fourchette. Les deux images sont similaires si la distance est inférieure au seuil, ou elles sont très différentes l'une de l'autre si la distance est supérieure au seuil. Vous devez ajuster le seuil en fonction de vos besoins professionnels.
Cet article a été rédigé par rifewang, utilisateur de Milvus et ingénieur logiciel d'UPYUN. Si vous aimez cet article, n'hésitez pas à venir lui dire bonjour @ https://github.com/rifewang.
- Extraction de caractéristiques
- Moteur de recherche vectoriel
- Milvus, le moteur de recherche vectorielle
On This Page
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word