Défi de la redondance sémantique dans les catalogues
Dans les bases de données e-commerce, les vendeurs utilisent fréquemment des formulations variées pour référencer un même produit. Un smartphone peut ainsi apparaître sous des intitulés distincts incluant des synonymes, des abréviations ou des structures syntaxiques différentes. Les algorithmes traditionnels basés sur la distance de Levenshtein ou l'intersection de mots-clés échouent face à cette variabilité lexicale, générant soit des faux négatifs, soit des collisions erronées.
Le modèle BGE-M3 (BAAI General Embedding) contourne cette limitation en projetant le texte dans un espace vectoriel de haute dimension (1024 ou 3072 dimensions selon la configuration). Il capture l'intention sémantique profonde plutôt que la simple structure de surface, permettant de calculer une similarité cosinus fiable entre des descriptions textuellement distinctes mais conceptuellement identiques.
Déploiement du service d'inférence
Pour intégrer cette capacité dans un système de production, le modèle doit être exposé via une API REST. L'approche standard consiste à utiliser FastAPI pour servir les embeddings et calculer les scores de similarité. Cette architecture fonctionne efficacement sur des instances CPU standard, éliminant la nécessité d'infrastructures GPU coûteuses pour des charges de travail modérées.
Le service expose un endpoint principal acceptant deux chaînes de caractères, les tokenise, génère les vecteurs via le modèle BGE-M3, et retourne leur similarité cosinus normalisée.
Intégration programmatique et traitements par lots
L'intégration dans les pipelines de données existants nécessite des clients API robustes. Voici plusieurs approches pour interagir avec le service d'inférence.
Client API asynchrone pour validations unitaires
Pour des vérifications en temps réel, l'utilisation de requêtes asynchrones optimise les performances I/O.
import httpx
import asyncio
async def evaluate_similarity(client: httpx.AsyncClient, source: str, target: str) -> float:
endpoint = "http://localhost:8000/api/v1/compare"
payload = {"text_source": source, "text_target": target}
response = await client.post(endpoint, json=payload)
response.raise_for_status()
return response.json().get("cosine_score", 0.0)
async def main():
async with httpx.AsyncClient(timeout=10.0) as session:
score = await evaluate_similarity(
session,
"Samsung Galaxy S24 Ultra 512Go Titane Noir",
"Galaxy S24 Ultra 512GB Black Titanium 5G"
)
print(f"Indice de similarité calculé : {score:.2%}")
asyncio.run(main())
Traitement par lots avec exécution parallèle
Lors de l'audit de catalogues complets, l'évaluation de toutes les combinaisons possibles (paires) nécessite une approche multithreadée pour saturer la bande passante réseau sans bloquer le thread principle.
import pandas as pd
import requests
from itertools import combinations
import concurrent.futures
def compute_pair_similarity(args):
idx_a, idx_b, text_a, text_b = args
payload = {"text_source": text_a, "text_target": text_b}
resp = requests.post("http://localhost:8000/api/v1/compare", json=payload, timeout=5)
return idx_a, idx_b, resp.json().get("cosine_score", 0.0)
def audit_catalog(file_path: str, threshold: float = 0.80):
dataframe = pd.read_excel(file_path)
catalog_items = dataframe["description"].tolist()
# Génération des paires combinatoires
pairs = [(i, j, catalog_items[i], catalog_items[j])
for i, j in combinations(range(len(catalog_items)), 2)]
duplicates_found = []
# Limitation à 5 workers pour éviter la surcharge du serveur d'inférence
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
future_map = {executor.submit(compute_pair_similarity, p): p for p in pairs[:500]}
for future in concurrent.futures.as_completed(future_map):
i, j, score = future.result()
if score > threshold:
duplicates_found.append({
"row_a": i, "snippet_a": catalog_items[i][:30],
"row_b": j, "snippet_b": catalog_items[j][:30],
"match_pct": f"{score:.1%}"
})
pd.DataFrame(duplicates_found).to_csv("audit_report.csv", index=False)
Interception au niveau du backend (Middleware Django)
Pour prévenir la duplication à la source, un décorateur de vue peut intercepter les requêtes de création de produits et valider la nouveauté sémantique avant l'insertion en base.
from functools import wraps
from django.http import JsonResponse
from datetime import timedelta
from django.utils import timezone
def prevent_semantic_duplicate(model_class, lookback_days=14, similarity_limit=0.88):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
if request.method == "POST":
incoming_desc = request.POST.get("product_description", "")
cat_id = request.POST.get("category_id")
# Récupération des descriptions récentes dans la même catégorie
recent_entries = model_class.objects.filter(
category_id=cat_id,
created_at__gte=timezone.now() - timedelta(days=lookback_days)
).values_list("product_description", "pk")
for existing_desc, existing_pk in recent_entries:
# Appel au service d'embedding externe
sim_score = calculate_embedding_cosine(incoming_desc, existing_desc)
if sim_score > similarity_limit:
return JsonResponse({
"error_code": "SEMANTIC_DUPLICATE",
"message": "Produit trop similaire à une référence existante",
"conflicting_id": existing_pk
}, status=409)
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
Validation empirique des métriques
L'évaluation du modèle sur des corpus e-commerce réels démontre sa capacité à différencier les variantes sémantiques des produits distincts.
| Scénario | Texte A | Texte B | Score BGE-M3 | Analyse |
|---|---|---|---|---|
| Variantes lexicales | Lenovo ThinkPad X1 Carbon Gen 11 i7 32Go 1To | Thinkpad X1 Carbon 11ème Gén i7-1365U 32GB RAM 1TB SSD | 93.5% | Identification correcte du même châssis et configuration. |
| Interférence de catégorie | Razer DeathAdder V3 Pro Souris Gaming 30K DPI | Razer Viper V3 Pro Souris Esport 35K DPI | 68.2% | Marque et usage identiques, mais modèles physiques distincts. |
| Alignement multilingue | DJI Mini 4 Pro Drone 4K HDR Capteur 1/1.3 | DJI Mini 4 Pro Drone with 4K HDR and 1/1.3-inch CMOS | 91.0% | Excellente projection cross-lingue pour les spécifications techniques. |
Optimisations et paramètres de configuration
Calibrage des seuils de décision
Le seuil de similarité ne doit pas être statique. Il doit être ajusté en fonction de la granularité de la taxonomie du catalogue :
- > 88% : Fusion automatique ou blocage. Indique une duplication quasi-certaine (ex: électronique grand public).
- 75% - 88% : File d'attente pour validation manuelle. Utile pour les catégories avec de nombreuses variantes (ex: mode, où la couleur ou la taille modifie le texte sans changer le produit de base).
- < 75% : Considéré comme produit distinct.
Normalisation préalable des données
Le modèle est sensible au bruit textuel. Appliquer une fonction de nettoyage régulier avant l'envoi à l'API stabilise la distribution des scores et réduit les faux positifs causés par des termes promotionnels.
import re
def normalize_catalog_text(raw_input: str) -> str:
# Suppression des caractères spéciaux et parenthèses
stripped = re.sub(r"[\(\)\[\]【】{}]", "", raw_input)
# Normalisation des espaces multiples
stripped = re.sub(r"\s+", " ", stripped).strip()
# Filtrage des termes marketing non pertinents pour l'identité du produit
noise_dictionary = ["livraison gratuite", "promo", "stock limité", "cadeau inclus", "certifié"]
for noise in noise_dictionary:
stripped = stripped.replace(noise, "")
return stripped.lower()
Profilage des performances sur CPU
L'inférence de BGE-M3 sur des processeurs standards (ex: Intel Xeon ou AMD EPYC d'entrée de gamme) offre un débit suffisant pour la plupart des opérations de back-office e-commerce. Les latences observées pour le calcul de similarité (incluant la tokenisation et le forward pass) sont les suivantes :
- Textes courts (< 40 tokens) : ~110ms par requête synchrone.
- Descriptions moyennes (40-100 tokens) : ~180ms par requête.
- Textes longs (> 100 tokens) : ~300ms, nécessitant une attention patriculière au timeout réseau.
Pour maximiser le throughput, l'utilisation de l'endpoint de batch inference (traitement de multiples phrases en une seule passe de modèle) est recommandée lors des audits nocturnes de catalogues.