
La puissance et les pièges de la recherche d’images vectorielles
de toute plateforme de commerce électronique. Les utilisateurs s’attendent à voir des résultats pertinents et ils les veulent rapidement. Pour cette raison, les équipes de commerce électronique s’efforcent constamment d’améliorer à la fois les performances et la qualité de recherche perçue afin de satisfaire les utilisateurs et d’éviter le désabonnement.
Veuillez jeter un œil aux trois images suivantes.

Ils se ressemblent d’un point de vue visuel, mais les éléments de ces images n’ont absolument aucun rapport. Ce contraste démontre à la fois les capacités et les inconvénients inhérents à la recherche d’images.
Toutes les images utilisées dans cet article proviennent de notre base de données interne que nous utilisons à des fins d’expérimentation et de tests.
Un cas d’utilisation de la recherche d’images consiste à détecter les produits en double. Bien que les titres et les descriptions de produits puissent certainement être utilisés pour rechercher des doublons, certaines listes comportent des titres légèrement (voire entièrement) différents tout en utilisant exactement les mêmes images. La détection de ces annonces est également très importante.
Étant donné que les plateformes de commerce électronique servent généralement des millions de produits, nous avons besoin d’outils et de méthodes efficaces pour effectuer tout type de recherche à grande échelle.
Dans cet article, je vais vous montrer comment configurer une base de données vectorielle d’incorporations de vecteurs d’images et effectuer des recherches dans cette base de données. J’expliquerai également en détail les avantages et les limites de la recherche d’images vectorielles.
Voici un aperçu de l’article :
- Convertissez les images en vecteurs : Transformez les données visuelles en intégrations consultables.
- Créez une collection Milvus : Configurez une collection Milvus, qui est la principale unité logique d’organisation des données dans la base de données vectorielles Milvus.
- Effectuez la recherche d’images : Recherchez des images cibles dans cette collection.
- Interprétez les résultats : Parcourez quelques exemples et interprétez les résultats de la recherche.
Commençons par obtenir nos vecteurs.
Convertir des images en vecteurs
La première étape consiste à convertir les images en vecteurs, qui sont des représentations numériques de données visuelles. La taille du vecteur est critique et la taille optimale dépend de l’application. Les longueurs de 128, 512 ou 768 dimensions sont des choix courants. Lorsque nous augmentons la taille, nous capturons plus d’informations et attendons des résultats plus précis, mais cela se fait au prix d’une plus grande taille de stockage et éventuellement d’une plus grande latence dans la recherche.
Nous avons besoin d’un modèle d’intégration pour convertir les images en vecteurs. Nous pouvons former notre propre modèle, mais il existe plusieurs modèles prêts à l’emploi, gratuits et payants.
Par exemple, le bloc de code suivant prend une image JPEG et la convertit en un vecteur à 512 dimensions à l’aide du logiciel open source clip-ViT-B-32 modèle.
from PIL import Image
from sentence_transformers import SentenceTransformer
sku_image = Image.open("sample_image.jpeg")
model = SentenceTransformer('clip-ViT-B-32')
image_vector = model.encode(sku_image)
type(image_vector), image_vector.shape
(numpy.ndarray, (512,))
La variable image_vector est un tableau Numpy de taille 512.
Créer une collection Milvus
Milvus est une base de données vectorielle et une collection dans Milvus est un tableau bidimensionnel avec des colonnes et des lignes fixes. Chaque colonne représente un champ et chaque ligne représente une entité, qui est une image dans notre cas.
Nous allons créer une collection avec deux champs : un champ d’identification (tel qu’un SKU de produit) et un champ vectoriel correspondant pour l’image de ce SKU.
Il existe plusieurs façons de créer et d’interagir avec une collection. Je préfère utiliser Python chaque fois que je le peux, donc je vais utiliser le pymilvus module.
La première étape consiste à créer un objet client. C’est ainsi que vous pouvez vous connecter à votre base de données vectorielles dans Milvus.
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://....") # your milvus db uri
Ensuite, nous définissons un schéma pour notre collection :
schema = client.create_schema(auto_id=False, enable_dynamic_field=True)
# Add fields to schema
schema.add_field(field_name="sku_id", datatype=DataType.VARCHAR, max_length=512, is_primary=True)
schema.add_field(field_name="image_vector", datatype=DataType.FLOAT_VECTOR, dim=512)
Le schéma comporte deux champs : sku_id et image_vector.
Ensuite, nous pouvons créer la collection en utilisant le create_collection méthode:
collection_name = "test_collection"
client.create_collection(
collection_name=collection_name,
schema=schema,
index_params=index_params
)
Nous pouvons maintenant ajouter un index à nos champs. Un index est crucial pour une collection Milvus ou toute base de données vectorielles. Il accélère les vitesses de recherche et réduit la latence des requêtes, en particulier lorsque vous travaillez avec de grands ensembles de données vectorielles.
Une option pour ajouter un index consiste à utiliser le create_index fonction.
index_params = client.prepare_index_params()
# index for the image vector
index_params.add_index(
field_name="image_vector",
index_name="image_vector_idx",
index_type="IVF_FLAT",
metric_type="COSINE", # Using COSINE similarity (common for images). Can also be L2 or IP.
)
# index for "sku_id" (primary key)
index_params.add_index(
field_name="sku_id",
index_name="sku_id_idx",
index_type="INVERTED"
)
collection_name = "test_collection"
client.create_index(
collection_name=collection_name,
index_params=index_params
)
Dans la dernière étape, nous chargeons la collection et vérifions son statut.
client.load_collection(collection_name=collection_name)
# check load status
res = client.get_load_state(
collection_name=collection_name
)
print(res)
{'state': <LoadState: Loaded>}
Nous avons créé la collection, mais elle est actuellement vide. Pour confirmer que notre nouvelle collection a été créée avec succès, nous pouvons lister toutes les collections de la base de données en utilisant le list_collections méthode.
client.list_collections()
['test_collection']
L’étape suivante consiste à insérer des entités (c’est-à-dire des skus et des vecteurs d’images).
Insérer des entités
Nous pouvons maintenant charger des données dans notre collection. Pour ce faire, les données doivent être formatées sous forme de dictionnaire, où les clés correspondent aux noms de champs de la collection. On peut alors utiliser une liste de ces dictionnaires pour insérer plusieurs entités à la fois.
J’ai des données vectorielles stockées dans un Pandas DataFrame comme indiqué ci-dessous :

Nous pouvons utiliser le to_dict méthode pour convertir ce DataFrame en une liste de dictionnaires où chaque dictionnaire représente une entité de notre collection.
df.to_dict(orient="enregistrements")
[{'sku_id': 'HBCV00009LIR5S',
'image_vector': array([ 0.1206549 , 0.00597879, -0.07224327, 0.02327867, -0.09490156,
0.02150885, 0.10642719, -0.10139938, 0.03159734, 0.05613545,
-0.07615539, -0.15523671, -0.10006154, 0.05045145, 0.07733533,
-0.03749327, -0.02301577, 0.13337888, 0.00096778, 0.05047926,
...
Once we have all the entities as a list of dictionaries, we can use the insert method to load entities into our collection:
# convert dataframe to a list of dictionaries
data = df.to_dict(orient="records")
# insert data into collection
res = client.insert(
collection_name=collection_name,
data=data
)
print(res)
{'insert_count': 10000, 'ids': ['HBCV00009LIR5S', 'HBCV00001U46VH' ...]
Nous venons de charger 10 000 entités, mais les collections contiennent généralement beaucoup plus de données (par exemple plusieurs millions d’entités). Nous ne pouvons pas charger des millions de vecteurs à la fois. Dans de tels cas, nous pouvons diviser les données en lots, puis les charger séquentiellement dans la collection. Par exemple, la boucle for ci-dessous parcourt l’ensemble du DataFrame et insère 10 000 entités par lot.
batch_size = 10000
for i in range(0, len(df), batch_size):
data = df.iloc[i:i+batch_size,].to_dict(orient="records")
res = client.insert(
collection_name=collection_name,
data=data
)
Nous avons maintenant une collection avec des données sku et vectorielles. L’étape suivante consiste à rechercher des images dans cette collection.
Effectuer la recherche d’images
Pour rechercher une image, nous devons d’abord la convertir en un vecteur de la même taille que les vecteurs stockés dans notre collection.
Milvus propose différentes méthodes de recherche telles que la recherche de base, la recherche par plage et la recherche hybride. Nous effectuerons une recherche de base, qui est en fait une recherche du voisin le plus proche (ANN). Il localise un sous-ensemble de représentations vectorielles vectorielles en fonction du vecteur de requête utilisé dans la recherche, compare le vecteur de requête aux vecteurs du sous-ensemble et renvoie les résultats les plus similaires.
Le bloc de code suivant lit une image JPEG, la convertit en vecteur en utilisant le même modèle que celui utilisé lors de la création de la collection, puis recherche ce vecteur d’image dans la collection.
collection_name = "test_collection"
model = SentenceTransformer('clip-ViT-B-32')
search_image = Image.open("image_to_search.jpeg")
search_image_vector = model.encode(search_image)
res = client.search(
collection_name=collection_name,
anns_field="image_vector",
data=[search_image_vector],
limit=3,
search_params={"metric_type": "COSINE"}
)
Le anns_field Le paramètre spécifie le champ vectoriel à utiliser dans la recherche. Nous transmettons ensuite le vecteur d’image cible au data paramètre. Le limit Le paramètre indique à Milvus combien de résultats renvoyer (c’est-à-dire que 3 renvoie les 3 vecteurs les plus similaires de la collection).
La sortie du search La méthode est une liste de dictionnaires comme suit :
print(res[0])
[{'my_id': 'HBCV0000BLJIBF', 'distance': 0.9814613848423661, 'entity': {}}
{'my_id': 'HBCV00003Z49OT', 'distance': 0.9504563808441162, 'entity': {}}
{'my_id': 'HBCV0000DCK7ML', 'distance': 0.9104360342025757, 'entity': {}}]
Vérification des résultats
La recherche d’images utilisant des vecteurs est très efficace et précise. Si exactement la même image (ou une image très similaire) existe dans la collection, vous êtes presque assuré de la trouver.
Dans les exemples ci-dessous, l’image la plus à gauche est celle recherchée et les images restantes constituent les premiers résultats de recherche.
Mêmes images
Dans les exemples suivants, plusieurs occurrences de la même image existent dans la collection et la recherche vectorielle a pu les détecter.


Images similaires et associées
Dans les exemples ci-dessous, nous pouvons clairement voir que les images récupérées sont très similaires et directement liées à l’image cible.


Images similaires mais sans rapport
Dans certains cas, la recherche vectorielle trouve des images visuellement similaires mais totalement indépendantes dans leur contexte. Voici un exemple d’un tel cas :

L’image recherchée est une éponge de bain, mais les résultats incluent des jouets en peluche qui se ressemblent.
Voici quelques autres exemples qui incluent des images visuellement similaires mais totalement indépendantes :


La recherche d’images dans une base de données vectorielles est très utile dans de nombreux cas. Nous pouvons facilement détecter des images identiques ainsi que des images ayant une apparence visuelle similaire. Cependant, comme le montre l’exemple ci-dessus, la similitude visuelle n’indique pas toujours que les produits sont identiques ou même liés.
Un autre inconvénient de la recherche d’images pour les plateformes de commerce électronique est que des produits totalement différents peuvent partager exactement la même image. Prenons par exemple les smartphones : de nombreux modèles distincts semblent presque identiques de l’extérieur. Se fier uniquement à la recherche d’images sans contexte supplémentaire, tel que le titre du produit ou le nom du modèle, peut facilement conduire à des résultats trompeurs.
Une solution pour surmonter les lacunes susmentionnées de la recherche d’images, nous pouvons mettre en œuvre une recherche hybride, qui combine les vecteurs d’image et de texte. La recherche hybride garantit que les résultats sont non seulement visuellement similaires, mais également conceptuellement précis.
Dans le prochain article, j’expliquerai étape par étape comment créer une collection Milvus avec plusieurs champs vectoriels et effectuer une recherche hybride. Nous testerons également cette approche en utilisant les mêmes exemples de cet article pour voir comment la recherche hybride élimine les résultats de recherche non pertinents.
Merci d’avoir lu! S’il vous plaît laissez-moi savoir si vous avez des commentaires.



