Comparaison de différents plongements vectoriels
Cet article a été initialement publié dans The New Stack et est republié ici avec autorisation.
En quoi les embeddings vectoriels générés par différents réseaux neuronaux diffèrent-ils, et comment pouvez-vous les évaluer dans votre Jupyter Notebook ?
Les grands modèles de langage (LLM) sont tendance et nous faisons face à un nouveau paradigme d’applications linguistiques comme ChatGPT. Les bases de données vectorielles seront un élément central de la stack. Il est donc important de comprendre les vecteurs et pourquoi ils sont si importants.
Ce projet démontre la différence entre les embeddings vectoriels selon les modèles et montre comment utiliser plusieurs collections de données vectorielles dans un seul Jupyter Notebook. Dans cet article, nous verrons ce que sont les embeddings vectoriels, pourquoi ils sont importants et comment comparer différents embeddings vectoriels dans votre Jupyter Notebook.
Que sont les embeddings vectoriels et pourquoi sont-ils importants ?
D’où viennent les embeddings vectoriels ?
En termes simples, les embeddings vectoriels sont des représentations numériques des données. Ils sont principalement utilisés pour représenter des données non structurées. Les données non structurées sont des images, des vidéos, de l’audio, du texte, des images moléculaires et d’autres types de données qui n’ont pas de structure formelle. Les embeddings vectoriels sont générés en faisant passer les données d’entrée dans un réseau neuronal pré-entraîné et en prenant la sortie de l’avant-dernière couche.
Les réseaux neuronaux ont des architectures différentes et sont entraînés sur différents jeux de données, ce qui rend l’embedding vectoriel de chaque modèle unique. C’est pourquoi travailler avec des données non structurées et des embeddings vectoriels est difficile. Nous verrons plus tard comment des modèles ayant la même base, affinés sur différents jeux de données, peuvent produire des embeddings vectoriels différents.
Les différences entre les réseaux neuronaux signifient également que nous devons utiliser des modèles distincts pour traiter diverses formes de données non structurées et générer leurs embeddings. Par exemple, vous ne pouvez pas utiliser un modèle sentence transformer pour générer des embeddings pour une image. À l’inverse, vous ne voudriez pas utiliser ResNet50, un modèle d’image, pour générer des embeddings pour des phrases. Il est donc important de trouver des modèles adaptés à votre type de données.
Comment comparer les embeddings vectoriels ?
Ensuite, voyons comment les comparer. Cette section compare trois modèles multilingues différents basés sur MiniLM de Hugging Face. Il existe de nombreuses façons de comparer des vecteurs. Dans cet exemple, nous utilisons la métrique de distance L2 et un index de fichier inversé comme index vectoriel.
Pour ce projet, j’ai développé certaines de mes données, inspirées par la sortie récente de l’album de Taylor Swift, définies directement dans le Jupyter Notebook. Vous pouvez utiliser mes exemples ou créer vos propres phrases. Une fois que nous avons les données, nous obtenons les différents embeddings et stockons deux ensembles d’embeddings dans une base de données vectorielle comme Milvus. Nous les interrogeons afin d’effectuer les comparaisons en utilisant les embeddings du troisième modèle.
Nous cherchons à voir si les résultats de recherche sont différents et à quel point les résultats de recherche s’écartent les uns des autres. Dans un contexte de production, vous voudriez connaître les résultats que vous souhaitez voir, puis les comparer aux résultats renvoyés.
Comparer les embeddings vectoriels de différents modèles
Les trois modèles que nous comparons sont le modèle de paraphrase multilingue de base utilisant MiniLM de Sentence Transformers, une version qui a été affinée pour la détection d’intention, et un modèle que Sprylab a affiné sans préciser sur quoi il avait été entraîné. Trouver trois modèles compatibles que je pouvais exécuter sur mon ordinateur portable a été l’une des parties les plus difficiles de ce projet.
Nous avons besoin de vecteurs de même longueur/dimension pour comparer des plongements vectoriels. Dans cet exemple, nous utilisons des vecteurs à 384 dimensions conformément au modèle MiniLM Sentence Transformer. Notez que cela ne signifie pas que nous créons 384 « fonctionnalités » ou « catégories » pour chaque élément de données, mais plutôt que nous générons une représentation abstraite des données dans un espace à 384 dimensions. Par exemple, aucune des dimensions ne représentera quelque chose comme une partie du discours, le nombre de mots dans la phrase, le fait que quelque chose soit un nom propre ou quoi que ce soit de conceptuel de ce type.
Données de comparaison des plongements vectoriels
Nous utilisons des modèles sentence transformer, ce qui signifie que nos données doivent être sous forme de phrases. Je recommande d’avoir au moins 50 phrases à comparer. Le notebook d’exemple en contient 51. Je recommande également d’utiliser des données qui présentent une certaine similarité. Dans cet exemple, j’ai utilisé des paroles de quatre chansons de Taylor Swift : « Speak Now », « Starlight », « Sparks Fly » et « Haunted ».
J’ai choisi ces chansons parce que beaucoup de paroles forment des phrases complètes, il est donc facile de passer du format de paroles au format de phrases. Je voulais aussi tester une hypothèse. Les trois premières chansons sont plus ou moins des chansons d’amour, tandis que la dernière, « Haunted », ressemble davantage à une chanson de rupture. J’émets donc l’hypothèse qu’il est moins probable que « Haunted » apparaisse dans les résultats renvoyés pour les trois premières chansons que celles-ci n’apparaissent les unes pour les autres.
Comparer des plongements vectoriels dans votre Jupyter Notebook
Passons au code. Avec Milvus Lite, une version légère de Milvus, vous pouvez comparer vos plongements vectoriels directement dans un Jupyter Notebook. Pour cet exemple de code, vous devrez exécuter ! pip install pymilvus milvus sentence-transformers. Vous n’avez besoin que de quelques imports pour ce projet ; la bibliothèque time est facultative. Après avoir exécuté les imports, démarrez le default_server de Milvus et configurez une connexion.
from sentence_transformers import SentenceTransformer
from milvus import default_server
from pymilvus import connections, utility, FieldSchema, CollectionSchema, DataType, Collection
from time import time
default_server.start()
connections.connect(host="127.0.0.1", port=default_server.listen_port)
Obtenir les modèles sentence transformer depuis Hugging Face
L’étape 1 consiste à obtenir nos modèles de plongement vectoriel. Nous utilisons la série paraphrase multilingual MiniLM pour cet exemple. Le premier est la version canonique. Les deux suivants sont les versions affinées différemment. Ce choix de modèle nous donne un exemple clair de la façon dont l’affinage peut modifier sensiblement vos vecteurs.
v12 = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
ft3_v12 = SentenceTransformer("Sprylab/paraphrase-multilingual-MiniLM-L12-v2-fine-tuned-3")
ft5_v12 = SentenceTransformer("hroth/psais-paraphrase-multilingual-MiniLM-L12-v2-5shot")
Charger les jeux de données dans deux collections dans une base de données vectorielle
J’ai répertorié ci-dessous 51 phrases de l’album « Speak Now » de Taylor Swift. N’hésitez pas à copier-coller cet extrait de code. Ou lisez-le si vous le souhaitez ; c’est une excellente parolière.
# inspiré par Speak Now (Taylor's Version)
# Paroles de : Speak Now, Starlight, Sparks Fly, Haunted
sentences = [
"Je ne suis pas le genre de fille qui devrait faire irruption sans cérémonie lors d'une occasion avec voile blanc, mais tu n'es pas le genre de garçon qui devrait épouser la mauvaise fille",
"Je me faufile et je vois tes amis et sa petite famille snob tous vêtus de pastel et elle est en train de crier sur une demoiselle d'honneur quelque part au fond d'une pièce, portant une robe en forme de pâtisserie",
"Ce n'est sûrement pas ce que tu pensais que ce serait.",
"Je me perds dans une rêverie où je me lève et dis : 'Ne dis pas oui, enfuis-toi maintenant, je te retrouverai quand tu sortiras de l'église par la porte de derrière.'",
"N'attends pas, ne prononce pas un seul vœu, tu dois m'écouter et ils ont dit : 'Parlez maintenant'.",
"Des gestes affectueux sont échangés.",
"Et l'orgue se met à jouer une chanson qui ressemble à une marche funèbre.",
"Et je me cache dans les rideaux, il semble que je n'aie pas été invitée par ta charmante future mariée.",
"Elle avance dans l'allée comme une reine de concours de beauté.",
"Mais je sais que tu souhaites que ce soit moi tu souhaites que ce soit moi n'est-ce pas ?",
"J'entends le prêtre dire : 'Parlez maintenant ou taisez-vous à jamais'",
"Il y a le silence, il y a ma dernière chance.",
"Je me lève, les mains tremblantes, tous les regards sur moi",
"Des regards horrifiés de tout le monde dans la pièce mais moi je ne regarde que toi.",
"Et tu diras : 'Enfuyons-nous maintenant' je te retrouverai quand j'aurai quitté mon smoking à la porte de derrière",
"Bébé, je n'ai pas prononcé mes vœux Tellement heureuse que tu aies été là Quand ils ont dit : 'Parlez maintenant'",
"J'ai dit : 'Oh mon Dieu, quelle mélodie merveilleuse'",
"C'était la meilleure nuit, jamais je n'oublierais comment nous bougions.",
"Tout l'endroit était tiré à quatre épingles et nous dansions, dansions comme si nous étions faits de lumière d'étoiles",
"J'ai rencontré Bobby sur la promenade l'été 45",
"Il est venu me chercher tard une nuit par la fenêtre nous avions dix-sept ans et étions fous, courant à l'état sauvage, sauvage.",
"Je ne me souviens plus de la chanson qu'il jouait quand nous sommes entrés.",
"La nuit où nous nous sommes faufilés à une fête de yacht club en prétendant être une duchesse et un prince.",
"Il a dit : 'Regarde-toi, à t'inquiéter autant de choses que tu ne peux pas changer Tu passeras toute ta vie à chanter le blues Si tu continues à penser comme ça'",
"Il essayait de faire ricocher des pierres sur l'océan en me disant 'Ne vois-tu pas la lumière des étoiles, la lumière des étoiles, ne rêves-tu pas de choses impossibles'",
"Ooh, ooh il parle comme un fou Ooh, ooh il danse avec moi Ooh, ooh nous pourrions nous marier Avoir dix enfants et leur apprendre à rêver",
"Ta façon de bouger est comme une véritable tempête de pluie.",
"Et je suis un château de cartes",
"Tu es le genre d'imprudent qui devrait me faire fuir mais au fond je sais que je n'irai pas loin",
"Et tu te tenais là devant moi juste assez près pour me toucher",
"Assez près pour espérer que tu ne pouvais pas voir à quoi je pensais",
"Laisse tout tomber maintenant",
"Rejoins-moi sous la pluie battante",
"Embrasse-moi sur le trottoir",
"Enlève la douleur",
"Parce que je vois des étincelles jaillir, chaque fois que tu souris",
"Prends-moi avec ces yeux verts, bébé quand les lumières s'éteignent",
"Donne-moi quelque chose qui me hantera quand tu ne seras pas là",
"Mon esprit oublie de me rappeler que tu es une mauvaise idée",
"Tu me touches une fois et c'est vraiment quelque chose tu découvres que je suis encore meilleure que tu ne l'avais imaginé",
"Je reste sur mes gardes face au reste du monde mais avec toi, je sais que ça ne sert à rien"
"Et je pourrais attendre patiemment mais j'aimerais vraiment que tu le fasses"
"Je passe mes doigts dans tes cheveux et je regarde les lumières s'affoler",
"Continue simplement à garder les yeux sur moi c'est juste assez mal pour que ça paraisse juste",
"Et entraîne-moi dans l'escalier ne veux-tu pas chuchoter doucement et lentement, je suis captivée par toi, bébé comme un feu d'artifice",
"Toi et moi marchons sur une ligne fragile je l'ai su tout ce temps, Mais je n'aurais jamais pensé vivre assez longtemps pour la voir se briser",
"Il fait sombre et tout est beaucoup trop silencieux Et je ne peux plus faire confiance à rien maintenant Et ça t'envahit comme si tout était une grosse erreur",
"Oh, je retiens mon souffle Je ne te perdrai pas à nouveau",
"Quelque chose a rendu tes yeux froids",
"Allez, allez, ne me laisse pas comme ça Je pensais t'avoir compris",
"Quelque chose a terriblement mal tourné tu es tout ce que je voulais",
"Je ne peux pas respirer chaque fois que tu es parti je ne peux plus revenir en arrière, je suis hantée",
"Je sais simplement Que tu n'es pas parti, tu ne peux pas être parti, non",
]
Nous devons d’abord définir les schémas pour insérer les données dans une base de données vectorielle. Nous avons le même schéma pour les deux collections dans cet exemple, nous n’avons donc besoin d’en créer qu’un seul. Veillez toutefois à créer deux collections.
Une fois les collections prêtes, nous encodons toutes les phrases en embeddings pour leurs modèles et définissons les paramètres de l’index vectoriel. Nous utilisons L2 comme métrique de distance et un index de fichier inversé avec quatre centroïdes. Après tout, il n’y a que 51 entrées. Ensuite, nous formatons les données et les chargeons dans Milvus.
# object should be inserted in the format of (title, date, location, speech embedding)
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="sentence", dtype=DataType.VARCHAR, max_length=500),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=DIMENSION),
]
schema = CollectionSchema(fields=fields)
collection_v12 = Collection(name=COLLECTION_V12, schema=schema)
collection_v12_ft5 = Collection(name=COLLECTION_V12_Q, schema=schema)
v12_embeds = {}
v12_q_embeds = {}
for sentence in sentences:
v12_embeds[sentence] = v12.encode(sentence)
v12_q_embeds[sentence] = ft5_v12.encode(sentence)
index_params = {
"index_type": "IVF_FLAT",
"metric_type": "L2",
"params": {"nlist": 4},
}
collection_v12.create_index(field_name="embedding", index_params=index_params)
collection_v12.load()
collection_v12_ft5.create_index(field_name="embedding", index_params=index_params)
collection_v12_ft5.load()
for sentence in sentences:
v12_insert = [
{
"sentence": sentence,
"embedding": v12_embeds[sentence]
}
]
ft_insert = [
{
"sentence": sentence,
"embedding": v12_q_embeds[sentence]
}
]
collection_v12.insert(v12_insert)
collection_v12_ft5.insert(ft_insert)
collection_v12.flush()
collection_v12_ft5.flush()
Interroger les magasins vectoriels pour comparer les embeddings
Ensuite, il est temps de comparer. Dans cet exemple, nous utiliserons les deux premières phrases. Nous utilisons le troisième modèle pour générer les embeddings vectoriels correspondants.
search_embeds = {}
search_data = []
for sentence in sentences[0:2]:
vector_embedding = ft3_v12.encode(sentence)
search_embeds[sentence] = vector_embedding
search_data.append(vector_embedding)
Maintenant, nous interrogeons Milvus. Je mesure également le temps pour cela. Les temps des recherches que j’ai obtenus sont indiqués ci-dessous. Veillez à transmettre le même type de métrique dans les paramètres de recherche.
start1 = time()
res_v12 = collection_v12.search(
data=search_data, # Embeded search value
anns_field="embedding", # Search across embeddings
param={"metric_type": "L2",
"params": {"nprobe": 2}},
limit = 3, # Limit to top_k results per search
output_fields=["sentence"])
time1 = time() - start1
print(f"Time for first search: {time1}")
start2 = time()
res_v12_ft5 = collection_v12_ft5.search(
data=search_data, # Embeded search value
anns_field="embedding", # Search across embeddings
param={"metric_type": "L2",
"params": {"nprobe": 2}},
limit = 3, # Limit to top_k results per search
output_fields=["sentence"])
time2 = time() - start2
print(f"Time for second search: {time2}")
Examinons maintenant les résultats et comparons. Nous observons des résultats étonnamment similaires entre le modèle original et celui affiné par Sprylab. La seule différence est que le premier résultat renvoyé est la phrase elle-même. Cela nous indique que les résultats en deux et trois sont plus similaires aux deux phrases d’exemple de recherche qu’ils ne le sont entre eux dans ces deux espaces vectoriels.
for i, hits in enumerate(res_v12):
for hit in hits:
print(f"Query sentence: {sentences[i]}")
print(f"Nearest Neighbor Number {i}: {hit.entity.get('sentence')} ---- {hit.distance}\n")
Ensuite, comparons les deux modèles fine-tunés. À partir de ces résultats, nous verrons que la phrase commençant par « I’m on my guard for the rest of the world … » est sémantiquement similaire à nos phrases de recherche, car elle apparaît dans les deux comparaisons. Deux points intéressants ici sont : 1) des résultats différents par rapport à la première requête, et 2) la phrase de la deuxième requête n’apparaît pas dans les trois premiers résultats pour la première, mais l’inverse est vrai.
for hit in hits:
print(f"Query sentence: {sentences[i]}")
print(f"Nearest Neighbor Number {i}: {hit.entity.get('sentence')} ---- {hit.distance}\n")
Résumé
Dans ce tutoriel, nous avons découvert les embeddings vectoriels et la manière de les comparer, et nous avons effectué nous-mêmes quelques comparaisons avec nos données personnalisées. En bonus, nous montrons également un exemple de la façon de travailler avec deux collections différentes en même temps. C’est ainsi que vous pouvez interroger différents espaces vectoriels latents.
Nous avons montré la différence entre un modèle et certaines de ses versions fine-tunées. Nous avons également vu comment un résultat apparaissait dans les deux espaces d’embedding. Un résultat de requête présent dans plusieurs représentations vectorielles signifie que cette requête doit être sémantiquement similaire de nombreuses manières. Essayez de faire cela avec des modèles d’images, des modèles de langage de dimensions différentes ou vos données pour les prochaines étapes.
Continuer à lire

How to Choose the Best Embedding Model for RAG in 2026: 10 Models Benchmarked
We benchmarked 10 embedding models on cross-modal, cross-lingual, long-document, and dimension compression tasks. See which one fits your RAG pipeline.

Why Not All VectorDBs Are Agent-Ready
Explore why choosing the right vector database is critical for scaling AI agents, and why traditional solutions fall short in production.

8 Latest RAG Advancements Every Developer Should Know
Explore eight advanced RAG variants that can solve real problems you might be facing: slow retrieval, poor context understanding, multimodal data handling, and resource optimization.



