Files
rpa_vision_v3/core/embedding/similarity.py
Dom a27b74cf22 v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution
- Frontend v4 accessible sur réseau local (192.168.1.40)
- Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard)
- Ollama GPU fonctionnel
- Self-healing interactif
- Dashboard confiance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:23:51 +01:00

389 lines
9.7 KiB
Python

"""
Similarity - Calculs de Similarité et Distance
Fonctions pour calculer différentes métriques de similarité et distance
entre vecteurs d'embeddings.
"""
import numpy as np
from typing import Union, List
def cosine_similarity(vec1: np.ndarray, vec2: np.ndarray) -> float:
"""
Calculer similarité cosinus entre deux vecteurs
similarity = (vec1 · vec2) / (||vec1|| * ||vec2||)
Args:
vec1: Premier vecteur
vec2: Deuxième vecteur
Returns:
Similarité cosinus dans [-1, 1]
1 = identiques, 0 = orthogonaux, -1 = opposés
Raises:
ValueError: Si dimensions ne correspondent pas
"""
if vec1.shape != vec2.shape:
raise ValueError(
f"Vectors must have same shape: {vec1.shape} vs {vec2.shape}"
)
# Produit scalaire
dot_product = np.dot(vec1, vec2)
# Normes
norm1 = np.linalg.norm(vec1)
norm2 = np.linalg.norm(vec2)
# Éviter division par zéro
if norm1 == 0 or norm2 == 0:
return 0.0
# Similarité cosinus
similarity = dot_product / (norm1 * norm2)
# Clamp dans [-1, 1] pour éviter erreurs numériques
similarity = np.clip(similarity, -1.0, 1.0)
return float(similarity)
def euclidean_distance(vec1: np.ndarray, vec2: np.ndarray) -> float:
"""
Calculer distance euclidienne (L2) entre deux vecteurs
distance = ||vec1 - vec2||_2 = sqrt(sum((vec1 - vec2)^2))
Args:
vec1: Premier vecteur
vec2: Deuxième vecteur
Returns:
Distance euclidienne (>= 0)
Raises:
ValueError: Si dimensions ne correspondent pas
"""
if vec1.shape != vec2.shape:
raise ValueError(
f"Vectors must have same shape: {vec1.shape} vs {vec2.shape}"
)
return float(np.linalg.norm(vec1 - vec2))
def manhattan_distance(vec1: np.ndarray, vec2: np.ndarray) -> float:
"""
Calculer distance de Manhattan (L1) entre deux vecteurs
distance = sum(|vec1 - vec2|)
Args:
vec1: Premier vecteur
vec2: Deuxième vecteur
Returns:
Distance de Manhattan (>= 0)
Raises:
ValueError: Si dimensions ne correspondent pas
"""
if vec1.shape != vec2.shape:
raise ValueError(
f"Vectors must have same shape: {vec1.shape} vs {vec2.shape}"
)
return float(np.sum(np.abs(vec1 - vec2)))
def dot_product(vec1: np.ndarray, vec2: np.ndarray) -> float:
"""
Calculer produit scalaire entre deux vecteurs
dot = vec1 · vec2 = sum(vec1 * vec2)
Args:
vec1: Premier vecteur
vec2: Deuxième vecteur
Returns:
Produit scalaire
Raises:
ValueError: Si dimensions ne correspondent pas
"""
if vec1.shape != vec2.shape:
raise ValueError(
f"Vectors must have same shape: {vec1.shape} vs {vec2.shape}"
)
return float(np.dot(vec1, vec2))
def normalize_l2(vector: np.ndarray, epsilon: float = 1e-10) -> np.ndarray:
"""
Normaliser un vecteur avec norme L2
normalized = vector / ||vector||_2
Args:
vector: Vecteur à normaliser
epsilon: Valeur minimale pour éviter division par zéro
Returns:
Vecteur normalisé (norme L2 = 1.0)
"""
norm = np.linalg.norm(vector)
if norm < epsilon:
return vector
return vector / norm
def normalize_l1(vector: np.ndarray, epsilon: float = 1e-10) -> np.ndarray:
"""
Normaliser un vecteur avec norme L1
normalized = vector / sum(|vector|)
Args:
vector: Vecteur à normaliser
epsilon: Valeur minimale pour éviter division par zéro
Returns:
Vecteur normalisé (norme L1 = 1.0)
"""
norm = np.sum(np.abs(vector))
if norm < epsilon:
return vector
return vector / norm
def batch_cosine_similarity(vectors: List[np.ndarray],
query: np.ndarray) -> np.ndarray:
"""
Calculer similarité cosinus entre une requête et un batch de vecteurs
Args:
vectors: Liste de vecteurs
query: Vecteur de requête
Returns:
Array de similarités
"""
# Convertir en matrice
matrix = np.array(vectors)
# Normaliser
matrix_norm = matrix / (np.linalg.norm(matrix, axis=1, keepdims=True) + 1e-10)
query_norm = query / (np.linalg.norm(query) + 1e-10)
# Produit matriciel
similarities = np.dot(matrix_norm, query_norm)
# Clamp
similarities = np.clip(similarities, -1.0, 1.0)
return similarities
def pairwise_cosine_similarity(vectors: List[np.ndarray]) -> np.ndarray:
"""
Calculer matrice de similarité cosinus entre tous les vecteurs
Args:
vectors: Liste de vecteurs
Returns:
Matrice de similarité (n x n)
"""
# Convertir en matrice
matrix = np.array(vectors)
# Normaliser
matrix_norm = matrix / (np.linalg.norm(matrix, axis=1, keepdims=True) + 1e-10)
# Produit matriciel
similarity_matrix = np.dot(matrix_norm, matrix_norm.T)
# Clamp
similarity_matrix = np.clip(similarity_matrix, -1.0, 1.0)
return similarity_matrix
def angular_distance(vec1: np.ndarray, vec2: np.ndarray) -> float:
"""
Calculer distance angulaire entre deux vecteurs
distance = arccos(cosine_similarity) / π
Args:
vec1: Premier vecteur
vec2: Deuxième vecteur
Returns:
Distance angulaire dans [0, 1]
"""
similarity = cosine_similarity(vec1, vec2)
angle = np.arccos(np.clip(similarity, -1.0, 1.0))
return float(angle / np.pi)
def jaccard_similarity(vec1: np.ndarray, vec2: np.ndarray) -> float:
"""
Calculer similarité de Jaccard pour vecteurs binaires
similarity = |intersection| / |union|
Args:
vec1: Premier vecteur binaire
vec2: Deuxième vecteur binaire
Returns:
Similarité de Jaccard dans [0, 1]
"""
if vec1.shape != vec2.shape:
raise ValueError(
f"Vectors must have same shape: {vec1.shape} vs {vec2.shape}"
)
intersection = np.sum(np.logical_and(vec1, vec2))
union = np.sum(np.logical_or(vec1, vec2))
if union == 0:
return 0.0
return float(intersection / union)
def hamming_distance(vec1: np.ndarray, vec2: np.ndarray) -> float:
"""
Calculer distance de Hamming pour vecteurs binaires
distance = nombre de positions différentes
Args:
vec1: Premier vecteur binaire
vec2: Deuxième vecteur binaire
Returns:
Distance de Hamming
"""
if vec1.shape != vec2.shape:
raise ValueError(
f"Vectors must have same shape: {vec1.shape} vs {vec2.shape}"
)
return float(np.sum(vec1 != vec2))
# ============================================================================
# Fonctions de conversion
# ============================================================================
def similarity_to_distance(similarity: float,
method: str = "cosine") -> float:
"""
Convertir similarité en distance
Args:
similarity: Valeur de similarité
method: Méthode ("cosine", "angular")
Returns:
Distance correspondante
"""
if method == "cosine":
# distance = 1 - similarity (pour cosine dans [0, 1])
return 1.0 - similarity
elif method == "angular":
# distance angulaire
angle = np.arccos(np.clip(similarity, -1.0, 1.0))
return float(angle / np.pi)
else:
raise ValueError(f"Unknown method: {method}")
def distance_to_similarity(distance: float,
method: str = "euclidean") -> float:
"""
Convertir distance en similarité
Args:
distance: Valeur de distance
method: Méthode ("euclidean", "manhattan")
Returns:
Similarité correspondante dans [0, 1]
"""
if method in ["euclidean", "manhattan"]:
# similarity = 1 / (1 + distance)
return 1.0 / (1.0 + distance)
else:
raise ValueError(f"Unknown method: {method}")
# ============================================================================
# Fonctions utilitaires
# ============================================================================
def is_normalized(vector: np.ndarray,
norm_type: str = "l2",
tolerance: float = 1e-6) -> bool:
"""
Vérifier si un vecteur est normalisé
Args:
vector: Vecteur à vérifier
norm_type: Type de norme ("l2" ou "l1")
tolerance: Tolérance pour la vérification
Returns:
True si normalisé, False sinon
"""
if norm_type == "l2":
norm = np.linalg.norm(vector)
elif norm_type == "l1":
norm = np.sum(np.abs(vector))
else:
raise ValueError(f"Unknown norm type: {norm_type}")
return abs(norm - 1.0) < tolerance
def compute_centroid(vectors: List[np.ndarray]) -> np.ndarray:
"""
Calculer le centroïde (moyenne) d'un ensemble de vecteurs
Args:
vectors: Liste de vecteurs
Returns:
Vecteur centroïde
"""
if not vectors:
raise ValueError("Cannot compute centroid of empty list")
matrix = np.array(vectors)
return np.mean(matrix, axis=0)
def compute_variance(vectors: List[np.ndarray]) -> float:
"""
Calculer la variance d'un ensemble de vecteurs
Args:
vectors: Liste de vecteurs
Returns:
Variance totale
"""
if not vectors:
raise ValueError("Cannot compute variance of empty list")
matrix = np.array(vectors)
return float(np.var(matrix))