Aller au contenu principal

Similarité Sémantique

Mesurer la proximité entre textes

Une fois vos textes transformés en vecteurs par mistral-embed, vous pouvez quantifier à quel point deux textes sont sémantiquement proches. C’est le coeur de la recherche sémantique : comparer des significations, pas des mots.

Dans cette leçon, vous allez implémenter les principales métriques de similarité et voir comment elles fonctionnent en pratique avec des exemples Python concrets.

Distance euclidienne

La distance euclidienne mesure la longueur du segment entre deux points dans l’espace vectoriel. Plus la distance est petite, plus les textes sont similaires.

import numpy as np
from mistralai import Mistral

client = Mistral(api_key=api_key)

def get_embedding(text):
    """Obtenir l'embedding d'un texte via mistral-embed."""
    response = client.embeddings.create(
        model="mistral-embed",
        inputs=[text],
    )
    return np.array(response.data[0].embedding)

# Trois phrases à comparer
phrase_a = "Le machine learning transforme les données en prédictions."
phrase_b = "L'apprentissage automatique convertit les données en résultats."
phrase_c = "La recette de la tarte aux pommes est simple."

emb_a = get_embedding(phrase_a)
emb_b = get_embedding(phrase_b)
emb_c = get_embedding(phrase_c)

# Distances euclidiennes
dist_ab = np.linalg.norm(emb_a - emb_b)
dist_ac = np.linalg.norm(emb_a - emb_c)

print(f"Distance A-B (sémantiquement proches): {dist_ab:.4f}")
print(f"Distance A-C (sémantiquement éloignés): {dist_ac:.4f}")
# A-B sera nettement inférieur à A-C

Similarité cosinus

La similarité cosinus est la métrique la plus utilisée pour les embeddings. Elle mesure l’angle entre deux vecteurs, indépendamment de leur magnitude. Le résultat est compris entre -1 et 1.

from sklearn.metrics.pairwise import cosine_similarity

# Avec scikit-learn
sim_ab = cosine_similarity([emb_a], [emb_b])[0][0]
sim_ac = cosine_similarity([emb_a], [emb_c])[0][0]

print(f"Similarité cosinus A-B: {sim_ab:.4f}")  # ~0.92 (très proches)
print(f"Similarité cosinus A-C: {sim_ac:.4f}")  # ~0.35 (éloignés)

Vous pouvez aussi l’implémenter manuellement :

def cosine_sim(vec_a, vec_b):
    """Similarité cosinus manuelle."""
    dot = np.dot(vec_a, vec_b)
    norm_a = np.linalg.norm(vec_a)
    norm_b = np.linalg.norm(vec_b)
    return dot / (norm_a * norm_b)

print(f"Vérification manuelle A-B: {cosine_sim(emb_a, emb_b):.4f}")

Matrice de similarité

Pour comparer plusieurs documents entre eux, vous pouvez construire une matrice de similarité :

from sklearn.metrics.pairwise import cosine_similarity

documents = [
    "Mistral AI développe des modèles de langage.",
    "Les LLMs génèrent du texte à partir de prompts.",
    "La cuisine française est réputée dans le monde.",
    "Le NLP est une branche de l'intelligence artificielle.",
    "Les croissants sont meilleurs le matin.",
]

# Encoder tous les documents
response = client.embeddings.create(
    model="mistral-embed",
    inputs=documents,
)
embeddings = np.array([d.embedding for d in response.data])

# Matrice de similarité (5x5)
sim_matrix = cosine_similarity(embeddings)

print("Matrice de similarité:")
for i, doc in enumerate(documents):
    for j, doc2 in enumerate(documents):
        if j > i:
            print(f"  [{i}]-[{j}]: {sim_matrix[i][j]:.3f}")

Recherche sémantique simple

Voici un mini moteur de recherche sémantique complet :

def recherche_semantique(query, documents, embeddings, top_k=3):
    """Trouve les documents les plus pertinents pour une requête."""
    # Encoder la requête
    query_response = client.embeddings.create(
        model="mistral-embed",
        inputs=[query],
    )
    query_embedding = np.array(query_response.data[0].embedding)

    # Calculer les similarités
    similarities = cosine_similarity(
        [query_embedding], embeddings
    )[0]

    # Trier par similarité décroissante
    ranked_indices = np.argsort(similarities)[::-1][:top_k]

    results = []
    for idx in ranked_indices:
        results.append({
            "document": documents[idx],
            "score": similarities[idx],
            "index": int(idx),
        })
    return results

# Exemple d'utilisation
query = "Comment fonctionne l'IA de Mistral ?"
results = recherche_semantique(query, documents, embeddings)

for r in results:
    print(f"  Score: {r['score']:.3f} -- {r['document']}")

Seuil de pertinence

En pratique, vous devez définir un seuil de similarité en dessous duquel un document est considéré comme non pertinent :

SEUIL_PERTINENCE = 0.7

def recherche_avec_seuil(query, documents, embeddings, seuil=SEUIL_PERTINENCE):
    """Recherche avec filtrage par seuil de pertinence."""
    results = recherche_semantique(query, documents, embeddings, top_k=len(documents))
    return [r for r in results if r["score"] >= seuil]

# Un seuil de 0.7 est un bon point de départ
# Ajustez selon votre cas d'usage :
# - 0.8+ : très strict (peu de résultats, haute pertinence)
# - 0.6-0.7 : équilibré
# - 0.5 : permissif (beaucoup de résultats, pertinence variable)

Points clés à retenir

  • La similarité cosinus est la métrique standard pour comparer des embeddings
  • Utilisez sklearn.metrics.pairwise.cosine_similarity pour des calculs rapides
  • Une matrice de similarité permet de comparer tous les documents entre eux
  • Définissez un seuil de pertinence adapté à votre cas d’usage (0.7 est un bon défaut)
  • La recherche sémantique trouve des résultats par signification, pas par correspondance exacte de mots