Vector Store avec FAISS
FAISS : recherche vectorielle haute performance
FAISS (Facebook AI Similarity Search) est une bibliothèque open source de Meta, conçue pour la recherche de similarité dans des ensembles de vecteurs de grande taille. Elle est rapide, efficace en mémoire, et fonctionne aussi bien avec quelques centaines qu’avec des millions de vecteurs.
Installation et premiers pas
# pip install faiss-cpu
# ou faiss-gpu pour l'accélération GPU
import faiss
import numpy as np
Créer un index FAISS
L’index le plus simple est IndexFlatL2, qui effectue une recherche exhaustive par distance L2 (euclidienne) :
# Supposons que nos embeddings sont une matrice NumPy
# embeddings.shape = (n_chunks, 1024)
# Créer l'index
dimension = embeddings.shape[1] # 1024 pour mistral-embed
index = faiss.IndexFlatL2(dimension)
# Ajouter les vecteurs
index.add(embeddings.astype(np.float32)) # FAISS requiert float32
print(f"Index créé: {index.ntotal} vecteurs de dimension {dimension}")
Types d’index FAISS
FAISS propose plusieurs types d’index selon vos besoins :
dimension = 1024
# 1. IndexFlatL2 : recherche exacte, distance L2
# Le plus précis, mais le plus lent pour de gros volumes
index_flat_l2 = faiss.IndexFlatL2(dimension)
# 2. IndexFlatIP : recherche exacte, produit scalaire (inner product)
# Utilisez avec des vecteurs normalisés pour la similarité cosinus
index_flat_ip = faiss.IndexFlatIP(dimension)
# 3. IndexIVFFlat : recherche approximative avec partitionnement
# Plus rapide pour de gros volumes, légère perte de précision
nlist = 100 # Nombre de partitions (clusters)
quantizer = faiss.IndexFlatL2(dimension)
index_ivf = faiss.IndexIVFFlat(quantizer, dimension, nlist)
# Nécessite un entraînement avant utilisation :
# index_ivf.train(embeddings)
# index_ivf.add(embeddings)
# 4. IndexHNSWFlat : graphe de voisins (très rapide en recherche)
index_hnsw = faiss.IndexHNSWFlat(dimension, 32) # 32 = nombre de voisins
Recherche dans l’index
from mistralai import Mistral
client = Mistral(api_key=api_key)
def search_index(query, index, chunks, k=5):
"""Rechercher les chunks les plus proches d'une requête."""
# 1. Encoder la requête
response = client.embeddings.create(
model="mistral-embed",
inputs=[query],
)
query_vector = np.array([response.data[0].embedding], dtype=np.float32)
# 2. Rechercher dans FAISS
distances, indices = index.search(query_vector, k)
# 3. Formater les résultats
results = []
for i, (dist, idx) in enumerate(zip(distances[0], indices[0])):
if idx >= 0: # FAISS retourne -1 si pas assez de résultats
results.append({
"rank": i + 1,
"distance": float(dist),
"text": chunks[idx]["text"],
"source": chunks[idx].get("source", ""),
})
return results
# Exemple de recherche
results = search_index("Comment fonctionne le RAG ?", index, chunks)
for r in results:
print(f" #{r['rank']} (dist: {r['distance']:.4f}) [{r['source']}]")
print(f" {r['text'][:100]}...")
Similarité cosinus avec FAISS
Pour utiliser la similarité cosinus au lieu de la distance L2, normalisez vos vecteurs :
def create_cosine_index(embeddings):
"""Créer un index FAISS avec similarité cosinus."""
# Normaliser les vecteurs (norme L2 = 1)
norms = np.linalg.norm(embeddings, axis=1, keepdims=True)
normalized = (embeddings / norms).astype(np.float32)
# Utiliser IndexFlatIP (produit scalaire = cosinus pour vecteurs normalisés)
dimension = normalized.shape[1]
index = faiss.IndexFlatIP(dimension)
index.add(normalized)
return index
cosine_index = create_cosine_index(embeddings)
Sauvegarder et charger un index
# Sauvegarder
faiss.write_index(index, "rag_faiss.index")
print("Index FAISS sauvegardé")
# Charger
index_loaded = faiss.read_index("rag_faiss.index")
print(f"Index chargé: {index_loaded.ntotal} vecteurs")
Index pour gros volumes
Pour des millions de vecteurs, utilisez un index IVF (Inverted File) :
def create_ivf_index(embeddings, nlist=100, nprobe=10):
"""Créer un index IVF pour de gros volumes."""
dimension = embeddings.shape[1]
data = embeddings.astype(np.float32)
# Créer le quantizer et l'index IVF
quantizer = faiss.IndexFlatL2(dimension)
index = faiss.IndexIVFFlat(quantizer, dimension, nlist)
# Entraîner l'index (nécessaire pour IVF)
print("Entraînement de l'index...")
index.train(data)
# Ajouter les vecteurs
index.add(data)
# Nombre de partitions à explorer lors de la recherche
index.nprobe = nprobe # Plus grand = plus précis, plus lent
print(f"Index IVF créé: {index.ntotal} vecteurs, {nlist} partitions")
return index
# Pour 100K+ vecteurs
# large_index = create_ivf_index(embeddings, nlist=256, nprobe=20)
Points clés à retenir
- FAISS est la référence pour la recherche vectorielle rapide
IndexFlatL2pour les petits corpus (moins de 100K vecteurs), recherche exacteIndexIVFFlatpour les gros corpus, recherche approximative mais rapide- Normalisez les vecteurs et utilisez
IndexFlatIPpour la similarité cosinus - Sauvegardez l’index sur disque avec
faiss.write_indexpour éviter de reconstruire