Toute base de données clients grossit avec le temps. Nouvelles saisies, imports CSV, fusions d’entreprises, formulaires en ligne mal filtrés : les doublons s’accumulent silencieusement. Un client peut apparaître sous « Martin Dupont », « M. Dupont », « Dupont Martin », ou avec une adresse e-mail légèrement différente. Pour les équipes commerciales, comptables ou marketing, ces doublons créent des erreurs, des doublons de communication et une perte de confiance dans les données.
Les approches classiques de déduplication (comparaison exacte, distance de Levenshtein) fonctionnent pour les cas simples. Mais elles échouent sur les variations sémantiques, les abréviations, les inversions prénom/nom ou les entreprises avec plusieurs noms commerciaux. C’est là que les embeddings deviennent puissants.
Dans ce tutoriel, vous allez construire un système de détection de doublons basé sur des représentations vectorielles de vos données clients, capable de détecter des similarités que les outils classiques manquent.
Comprendre les embeddings appliqués à la déduplication
Un embedding est une représentation numérique d’un texte sous forme de vecteur dans un espace à plusieurs dimensions. L’idée clé est que deux textes sémantiquement proches auront des vecteurs proches dans cet espace.
Pourquoi les embeddings surpassent les approches classiques
- Distance de Levenshtein : mesure les différences de caractères. « Jean Martin » vs « Martin Jean » = distance élevée malgré l’identité probable
- Comparaison exacte : ne capte pas « SARL Diallo » vs « Diallo Transports SARL »
- Embeddings : capturent la similarité sémantique même avec des formulations différentes
Les modèles d’embeddings utiles pour ce cas
- sentence-transformers (all-MiniLM-L6-v2) : rapide, efficace pour les textes courts en anglais
- paraphrase-multilingual-MiniLM-L12-v2 : multilingue, excellent pour le français
- Ollama + embeddings locaux : pour un déploiement entièrement local et confidentiel
💡 Conseil : pour des données clients françaises ou africaines, privilégiez un modèle multilingue. Les noms propres, les termes d’adresse et les noms d’entreprises varient fortement selon les contextes culturels et linguistiques.
Préparer les données clients pour la déduplication
Structure typique d’une base clients
import pandas as pd
# Exemple de base clients avec potentiels doublons
data = {
"client_id": [1, 2, 3, 4, 5, 6],
"first_name": ["Moussa", "Moussa", "Awa", "Sophie", "Soph.", "Abdou"],
"last_name": ["Ndiaye", "N'Diaye", "Fall", "Dupont", "Dupont", "Diallo"],
"company": ["Diallo Transport SARL", "Ste Diallo Transports", "Fall Distribution", "Dupont Conseil", "Conseil Dupont SAS", "Diallo & Fils"],
"email": ["moussa@diallo.sn", "commercial@diallo.sn", "contact@falldist.sn", "s.dupont@gmail.com", "sophie.dupont@outlook.com", "a.diallo@gmail.com"]
}
df = pd.DataFrame(data)
print(df)
Créer un champ de comparaison unifié
def create_comparison_field(row) -> str:
"""Crée un champ texte unifié pour la comparaison."""
parts = []
# Normaliser prénom + nom
name = f"{row.get('first_name', '')} {row.get('last_name', '')}".strip().lower()
parts.append(name)
# Ajouter l'entreprise si disponible
if row.get('company'):
parts.append(row['company'].lower())
# Ajouter le domaine e-mail comme signal supplémentaire
if row.get('email') and '@' in row['email']:
domain = row['email'].split('@')[1].lower()
parts.append(domain)
return " | ".join(parts)
df['comparison_field'] = df.apply(create_comparison_field, axis=1)
print(df['comparison_field'])
Générer les embeddings et calculer les similarités
Installation et génération avec sentence-transformers
pip install sentence-transformers numpy pandas scikit-learn
from sentence_transformers import SentenceTransformer
import numpy as np
# Charger le modèle multilingue
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
# Générer les embeddings pour tous les clients
texts = df['comparison_field'].tolist()
embeddings = model.encode(texts, normalize_embeddings=True)
print(f"Shape des embeddings: {embeddings.shape}")
# (6, 384) - 6 clients, vecteurs de dimension 384
Calculer la matrice de similarité
from sklearn.metrics.pairwise import cosine_similarity
# Matrice de similarité cosinus
similarity_matrix = cosine_similarity(embeddings)
# Convertir en DataFrame pour la lisibilité
sim_df = pd.DataFrame(
similarity_matrix,
index=df['client_id'],
columns=df['client_id']
)
# Afficher les paires avec similarité > 0.8
threshold = 0.80
pairs = []
n = len(df)
for i in range(n):
for j in range(i + 1, n):
score = similarity_matrix[i][j]
if score > threshold:
pairs.append({
"id_1": df.iloc[i]['client_id'],
"nom_1": df.iloc[i]['comparison_field'],
"id_2": df.iloc[j]['client_id'],
"nom_2": df.iloc[j]['comparison_field'],
"similarite": round(score, 4)
})
duplicates_df = pd.DataFrame(pairs).sort_values('similarite', ascending=False)
print(duplicates_df)
Construire un pipeline complet de déduplication
Système de détection automatique
class ClientDeduplicator:
def __init__(self, model_name='paraphrase-multilingual-MiniLM-L12-v2', threshold=0.82):
self.model = SentenceTransformer(model_name)
self.threshold = threshold
def prepare_texts(self, df: pd.DataFrame) -> list[str]:
"""Prépare les textes de comparaison."""
return df.apply(create_comparison_field, axis=1).tolist()
def find_duplicates(self, df: pd.DataFrame) -> pd.DataFrame:
"""Trouve les paires de doublons potentiels."""
texts = self.prepare_texts(df)
embeddings = self.model.encode(texts, normalize_embeddings=True)
sim_matrix = cosine_similarity(embeddings)
duplicates = []
for i in range(len(df)):
for j in range(i + 1, len(df)):
score = sim_matrix[i][j]
if score >= self.threshold:
row_i = df.iloc[i]
row_j = df.iloc[j]
duplicates.append({
"id_1": row_i.get('client_id', i),
"label_1": texts[i],
"id_2": row_j.get('client_id', j),
"label_2": texts[j],
"score": round(score, 4),
"confidence": "haute" if score > 0.92 else "moyenne"
})
return pd.DataFrame(duplicates).sort_values('score', ascending=False)
def generate_report(self, df: pd.DataFrame, output_file="doublons_detectes.csv"):
"""Génère un rapport CSV des doublons."""
duplicates = self.find_duplicates(df)
duplicates.to_csv(output_file, index=False, encoding='utf-8-sig')
print(f"✅ {len(duplicates)} paires de doublons détectées")
print(f"📄 Rapport exporté : {output_file}")
return duplicates
# Utilisation
deduplicator = ClientDeduplicator(threshold=0.82)
rapport = deduplicator.generate_report(df)
Gérer la performance sur de grandes bases
La matrice de similarité complète est en O(n²). Pour 10 000 clients, cela représente 50 millions de comparaisons. Il faut optimiser.
Utiliser FAISS pour la recherche approximative
import faiss
def find_duplicates_faiss(df: pd.DataFrame, model, threshold=0.82, top_k=10):
"""Version scalable avec FAISS pour les grandes bases."""
texts = df.apply(create_comparison_field, axis=1).tolist()
embeddings = model.encode(texts, normalize_embeddings=True).astype('float32')
# Construire l'index FAISS
dimension = embeddings.shape[1]
index = faiss.IndexFlatIP(dimension) # produit scalaire = cosinus si normalisé
index.add(embeddings)
# Rechercher les top-k voisins pour chaque client
scores, indices = index.search(embeddings, top_k + 1) # +1 car inclut soi-même
duplicates = []
for i in range(len(df)):
for rank in range(1, top_k + 1): # Skip index 0 (soi-même)
j = indices[i][rank]
score = scores[i][rank]
if j > i and score >= threshold: # éviter les doublons de paires
duplicates.append({
"id_1": df.iloc[i].get('client_id', i),
"id_2": df.iloc[j].get('client_id', j),
"score": round(float(score), 4)
})
return pd.DataFrame(duplicates)
# Résultats
dupes_faiss = find_duplicates_faiss(df, model)
print(f"Doublons trouvés : {len(dupes_faiss)}")
💡 Conseil : sur des bases de plus de 50 000 contacts, combinez le clustering géographique (regrouper d’abord par ville ou domaine e-mail) avec la recherche FAISS. Cela réduit considérablement le nombre de comparaisons sans perdre en qualité de détection.
Valider et traiter les doublons détectés
Processus de validation humaine
Ne supprimez jamais automatiquement des enregistrements basés uniquement sur un score de similarité. Mettez en place un processus de validation :
- Score > 0.95 : doublon très probable, traitement automatique possible avec notification
- Score entre 0.82 et 0.95 : à valider manuellement par un opérateur
- Score < 0.82 : non signalé, trop de faux positifs
Interface simple de validation
def create_validation_interface(duplicates_df: pd.DataFrame) -> pd.DataFrame:
"""Prépare un fichier de validation pour les opérateurs."""
validation = duplicates_df.copy()
validation['decision'] = '' # 'fusionner', 'ignorer', 'examiner'
validation['traite_par'] = ''
validation['date_traitement'] = ''
# Colonnes pour faciliter la décision
validation['lien_client_1'] = validation['id_1'].apply(
lambda x: f"https://votre-crm.com/clients/{x}"
)
validation['lien_client_2'] = validation['id_2'].apply(
lambda x: f"https://votre-crm.com/clients/{x}"
)
output_file = "doublons_a_valider.xlsx"
validation.to_excel(output_file, index=False)
print(f"Fichier de validation créé : {output_file}")
return validation
Conclusion
Détecter les doublons dans une base clients avec des embeddings est une approche bien plus robuste que les méthodes classiques. Elle capture les similarités sémantiques, gère les variations orthographiques et culturelles, et s’adapte naturellement au français et aux langues africaines.
En combinant un modèle multilingue comme sentence-transformers, un index FAISS pour la performance, et un processus de validation humaine pour les cas ambigus, vous obtenez un système de déduplication fiable, scalable et adaptable à vos données.
La qualité d’une base clients détermine directement la qualité de vos actions marketing, commerciales et comptables. Un investissement dans la déduplication rapporte rapidement en termes d’efficacité opérationnelle.
Vous voulez aller plus loin sur l’IA appliquée aux données métier ? Retrouvez nos tutoriels sur ITSkillsCenter.io pour développer des compétences concrètes en Python, NLP et automatisation.