๐ PROMPT POUR CURSOR AGENT - EMBEDDINGS & SYNTHรSE
OH PUTAIN YES! ๐ฅ Allez, je te fais un prompt de MALADE pour la section Embeddings + la Synthรจse finale! Let's finish strong! (เธ'ฬ-'ฬ)เธ
OH PUTAIN YES! ๐ฅ Allez, je te fais un prompt de MALADE pour la section Embeddings + la Synthรจse finale! Let's finish strong! (เธ'ฬ-'ฬ)เธ
๐ PROMPT POUR CURSOR AGENT - EMBEDDINGS & SYNTHรSE
# MISSION: Ajouter les Sections Embeddings et Synthรจse Comparative
## ๐ฏ CONTEXTE
Les sections TF-IDF et BM25 sont complรจtes et fonctionnent bien. Maintenant, tu dois:
1. **Implรฉmenter la section Embeddings** (la plus avancรฉe)
2. **Crรฉer la section Synthรจse Comparative** (comparaison des 3 techniques)
3. **Finaliser l'application** avec une page d'accueil complรจte
**Public cible:** รtudiants francophones en dรฉveloppement web (niveau bac+2/3)
**Objectif:** Montrer la puissance des embeddings vectoriels et guider les รฉtudiants dans le choix de la bonne technique selon leur cas d'usage.
---
## ๐๏ธ ARCHITECTURE FINALE DE L'APPLICATION
### Structure complรจte:
๐ EXPLORATEUR DE RECHERCHE TEXTUELLE
โโโ ๐ Accueil โ โโโ Prรฉsentation gรฉnรฉrale โ โโโ Navigation guidรฉe โ โโโ Quick Start โ โโโ ๐ 1. TF-IDF (โ FAIT) โโโ ๐ฏ 2. BM25 (โ FAIT) โ โโโ ๐ง 3. Embeddings Vectoriels (โ ร CRรER) โ โโโ ๐ Introduction & Limites Lexicales โ โโโ ๐ข Concepts des Embeddings โ โโโ ๐ Recherche Interactive โ โโโ ๐ Exploration & Visualisations โ โโโ ๐ Exemple Pas-ร -Pas โ โโโ โ๏ธ Comparaison avec BM25/TF-IDF โ โโโ ๐จ Hybrid Search (BM25 + Embeddings) โ โโโ โก Performance โ โโโ ๐ 4. Synthรจse Comparative (โ ร CRรER) โโโ ๐ Tableau Comparatif โโโ ๐ฏ Guide de Dรฉcision โโโ ๐ผ Cas d'Usage Rรฉels โโโ ๐ฌ Benchmark Comparatif โโโ ๐ Recommandations Finales
---
## ๐ CONTENU PรDAGOGIQUE - EMBEDDINGS
### ๐ Sous-section 1: Introduction & Limites Lexicales
**Structure:**
#### **1.1 Rappel TF-IDF/BM25**
Encadrรฉ rรฉcapitulatif:
```python
st.info("""
๐ **Ce que vous avez appris:**
- **TF-IDF:** Recherche par frรฉquence des mots
- **BM25:** TF-IDF amรฉliorรฉ avec saturation
**Principe commun:** Recherche LEXICALE (mots exacts)
""")
1.2 Les 4 Fails Critiques des Approches Lexicales
Fail #1: Synonymes
Exemple interactif avec deux colonnes:
col1, col2 = st.columns(2)
with col1:
st.markdown("**Query:**")
st.code("voiture rapide")
with col2:
st.markdown("**Document:**")
st.code("automobile vรฉloce")
st.error("โ TF-IDF/BM25 Score: 0.0 (aucun mot commun)")
st.success("โ
Embeddings Score: 0.94 (sens identique!)")
Visualisation:
# Graphique montrant le matching lexical vs sรฉmantique
# Diagramme de Venn ou similaire
Fail #2: Polysรฉmie
Exemple avec "Apple":
st.markdown("""
### ๐ Problรจme de la Polysรฉmie
**Mot:** "Apple"
**Contexte 1:** "Apple fait de bons ordinateurs" โ Entreprise tech ๐ป
**Contexte 2:** "Apple est un fruit dรฉlicieux" โ Fruit ๐
**TF-IDF/BM25:** Les deux matchent pareil (mot identique)
**Embeddings:** Distingue les deux sens selon le contexte! โ
""")
Fail #3: Relations Conceptuelles
Exemple "Paris capitale France":
# Tableau comparatif
data = {
'Document': [
'Paris est une belle ville',
'La France est un grand pays'
],
'TF-IDF': [0.0, 0.45],
'Embeddings': [0.88, 0.31]
}
st.table(pd.DataFrame(data))
st.markdown("""
**Pourquoi?**
- TF-IDF match "France" dans Doc 2 โ
- Embeddings comprend que "Paris" est liรฉ ร "capitale France" ๐คฏ
""")
Fail #4: Paraphrases
Exemple frappant:
phrase_a = "Le chat poursuit la souris"
phrase_b = "Le fรฉlin traque le rongeur"
st.markdown(f"""
**Phrase A:** {phrase_a}
**Phrase B:** {phrase_b}
**Sens:** IDENTIQUE โ
**Mots communs:** 2/6 ("le" ร2)
- TF-IDF Similarity: ~0.15
- Embeddings Similarity: ~0.91 ๐ฅ
""")
1.3 La Solution: Embeddings Vectoriels
Grande encadrรฉ avec animation ou graphique:
st.success("""
๐ง **Les Embeddings: La Rรฉvolution Sรฉmantique**
Au lieu de compter des mots, on capture le SENS dans un espace vectoriel dense.
**Principe:**
Texte โ Neural Network โ Vecteur de nombres โ Comparaison gรฉomรฉtrique
**Rรฉsultat:**
"voiture" et "automobile" deviennent des vecteurs PROCHES dans l'espace!
""")
Visualisation conceptuelle:
# Schรฉma simplifiรฉ du pipeline
# Texte โ [Tokenizer] โ [BERT/Transformer] โ [Pooling] โ Vector (384 dim)
๐ข Sous-section 2: Concepts des Embeddings
Structure avec expanders:
2.1 Sparse vs Dense
Tableau comparatif visuel:
col1, col2 = st.columns(2)
with col1:
st.markdown("### ๐ TF-IDF (Sparse)")
st.code("""
Vocabulaire: 10,000 mots
Doc: "Le chat mange"
Vector: [0, 0, 0, ..., 0.5, 0, ..., 0.8, ...]
โโโโโโโโโโโโฌโโโโโโโโโโโ
99.97% de zรฉros!
Dimensions: 10,000
Non-zรฉros: ~3 (0.03%)
""")
with col2:
st.markdown("### ๐ง Embeddings (Dense)")
st.code("""
Doc: "Le chat mange"
Vector: [0.234, -0.891, 0.456, ..., -0.123]
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
Toutes valeurs non-nulles!
Dimensions: 384
Non-zรฉros: 384 (100%)
""")
st.markdown("""
**Avantage Dense:**
- Chaque dimension capture un "concept" sรฉmantique
- Pas de dimensions gaspillรฉes
- Reprรฉsentation beaucoup plus riche! โจ
""")
2.2 Le Pipeline: De Texte ร Vecteur
Schรฉma dรฉtaillรฉ avec รฉtapes:
# Utiliser st.graphviz ou crรฉer un flowchart avec plotly
st.markdown("""
### ๐ Pipeline de Transformation
```text
1. Texte Brut
"Le chat mange du poisson"
2. Tokenization
["le", "chat", "mange", "du", "poisson"]
3. Neural Network (Transformer)
- Embedding Layer: mots โ vecteurs initiaux
- Attention Layers (ร12): capture le contexte
- Pooling: agrรฉgation en un seul vecteur
4. Vecteur Final
[0.234, -0.891, 0.456, ..., -0.123] (384 dimensions)
""")
**Explication du Transformer:**
```python
with st.expander("๐ค Qu'est-ce qu'un Transformer?"):
st.markdown("""
### Architecture BERT/Sentence-BERT
**Attention Mechanism (le cลur):**
Imagine la phrase: "La banque est fermรฉe"
**Question:** "banque" = institution financiรจre ou bord de riviรจre?
**L'Attention regarde le contexte:**
- Mot "fermรฉe" โ Institution financiรจre (probable) โ
- Si c'รฉtait "La banque du fleuve" โ Bord de riviรจre
**Mรฉcanisme:**
Chaque mot "regarde" les autres mots pour comprendre son sens.
```
Phrase: "Le chat noir mange"
Attention de "noir":
- Vers "chat": 0.8 โ Fort! (noir dรฉcrit chat)
- Vers "mange": 0.1 โ Faible
- Vers "le": 0.1
```
**Aprรจs 12 couches d'attention:**
Le rรฉseau a une comprรฉhension profonde du sens! ๐ง
""")
# Visualisation des attention weights (si possible)
2.3 L'Entraรฎnement: Comment le Rรฉseau Apprend
st.markdown("""
### ๐ Masked Language Modeling
**Tรขche d'entraรฎnement:**
```python
# Phrase originale
"Le chat mange du poisson"
# Phrase maskรฉe
"Le [MASK] mange du poisson"
# Objectif
Prรฉdire que [MASK] = "chat"
Pourquoi รงa marche?
Pour prรฉdire correctement, le rรฉseau DOIT comprendre:
- "mange" โ sujet vivant (pas "table")
- "du poisson" โ prรฉdateur (pas "vache")
- Contexte franรงais โ vocabulaire adaptรฉ
Aprรจs des milliards d'exemples: Le rรฉseau apprend des reprรฉsentations sรฉmantiques riches! โจ """)
Animation ou schรฉma du processus d'entraรฎnement
#### **2.4 Les Dimensions: Que Reprรฉsentent-elles?**
```python
st.markdown("""
### ๐ L'Espace Vectoriel
**Exemple conceptuel (simplifiรฉ):**
Dimension 0: "vivant vs non-vivant" chat โ +0.9 | table โ -0.7
Dimension 1: "concret vs abstrait" pomme โ +0.8 | amour โ -0.6
Dimension 2: "positif vs nรฉgatif (sentiment)" heureux โ +0.9 | triste โ -0.8
Dimension 3: "animal vs objet" chien โ +0.85 | voiture โ -0.75 ... Dimension 383: "???" (combinaison complexe)
**En rรฉalitรฉ:**
Les 384 dimensions sont des **combinaisons complexes** de milliers de concepts.
Impossible ร interprรฉter directement, mais les relations รฉmergent naturellement! ๐ช
""")
# Visualisation PCA/t-SNE montrant les clusters
Visualisation Interactive:
# Widget pour explorer l'espace vectoriel
st.subheader("๐จ Visualisation 2D de l'Espace Vectoriel")
# Exemple avec mots prรฉdรฉfinis
example_words = [
"chat", "chien", "hamster", # Animaux
"voiture", "camion", "vรฉlo", # Vรฉhicules
"heureux", "joyeux", "content", # รmotions
"table", "chaise", "lit" # Meubles
]
# Option de personnalisation
custom_words = st.text_input(
"Ou entrez vos propres mots (sรฉparรฉs par des virgules):",
placeholder="amour, haine, guerre, paix"
)
if custom_words:
words = [w.strip() for w in custom_words.split(",")]
else:
words = example_words
# Encoder et visualiser
embeddings = model.encode(words)
# PCA ou t-SNE
# Plotly scatter avec labels
# Colorier par catรฉgorie si possible
๐ Sous-section 3: Recherche Interactive
Interface:
st.title("๐ Recherche Sรฉmantique")
# Query input
query = st.text_input(
"Votre recherche:",
placeholder="animal domestique",
help="Essayez des synonymes, des paraphrases, des concepts!"
)
# Paramรจtres
with st.expander("โ๏ธ Paramรจtres Avancรฉs"):
col1, col2 = st.columns(2)
with col1:
model_name = st.selectbox(
"Modรจle d'embeddings:",
[
"paraphrase-multilingual-MiniLM-L12-v2", # 384 dim, rapide
"paraphrase-multilingual-mpnet-base-v2", # 768 dim, meilleur
"distiluse-base-multilingual-cased-v2" # 512 dim, รฉquilibrรฉ
],
help="Plus de dimensions = meilleur mais plus lent"
)
with col2:
top_k = st.slider("Nombre de rรฉsultats:", 1, 20, 5)
# Comparaison multi-techniques
compare_options = st.multiselect(
"Comparer avec:",
["BM25", "TF-IDF", "Hybrid (BM25+Embeddings)"],
default=[]
)
if st.button("๐ Rechercher", type="primary"):
with st.spinner("Recherche en cours..."):
# Calcul des rรฉsultats
results_embeddings = embedding_search.search(query, top_k)
# Si comparaisons demandรฉes
if "BM25" in compare_options:
results_bm25 = bm25_search.search(query, top_k)
# ... etc
# Affichage
st.success(f"โ
{len(results_embeddings)} rรฉsultats trouvรฉs")
# Layout selon comparaisons
if compare_options:
# Multi-colonnes pour comparaisons
cols = st.columns(len(compare_options) + 1)
with cols[0]:
st.markdown("### ๐ง Embeddings")
for r in results_embeddings:
# Afficher rรฉsultat
pass
# ... autres colonnes
else:
# Simple colonne
for i, result in enumerate(results_embeddings, 1):
with st.container():
st.markdown(f"### {i}. [Score: {result['score']:.3f}]")
# Badge catรฉgorie si disponible
if 'category' in result:
st.badge(result['category'])
# Titre/Snippet
st.markdown(f"**{result['title']}**")
st.caption(result['text'][:200] + "...")
# Bouton pour explication
with st.expander("๐ Voir les dรฉtails"):
st.json(result) # ou affichage plus structurรฉ
Features importantes:
- Highlight des concepts matchรฉs (pas des mots exacts!)
- Score de confiance avec interprรฉtation
- Suggestions de recherches similaires
- Export des rรฉsultats
๐ Sous-section 4: Exploration & Visualisations
Contenu:
4.1 Visualisation 3D Interactive
st.subheader("๐ Espace Vectoriel 3D")
# Options de visualisation
viz_docs = st.multiselect(
"Sรฉlectionner des documents ร visualiser:",
options=range(len(corpus)),
format_func=lambda i: f"Doc {i}: {corpus[i][:50]}...",
default=list(range(min(20, len(corpus))))
)
# Ajouter query
include_query = st.checkbox("Inclure la query dans la visualisation", value=True)
# Rรฉduction dimensionnelle
reduction_method = st.radio(
"Mรฉthode de rรฉduction:",
["PCA (rapide)", "t-SNE (meilleur clustering)", "UMAP (compromis)"],
horizontal=True
)
if st.button("Gรฉnรฉrer la visualisation 3D"):
# Code de rรฉduction + plot Plotly 3D interactif
# Lignes reliant query aux top 3 rรฉsultats
# Couleurs par catรฉgorie si disponible
pass
Visualisation attendue:
- Scatter 3D rotatif (Plotly)
- Query en rouge (star/diamond)
- Documents en bleu
- Lignes vertes vers top rรฉsultats
- Hover showing text snippet
4.2 Heatmap de Similaritรฉ
st.subheader("๐ฅ Matrice de Similaritรฉ")
# Option de filtrage
num_docs_viz = st.slider(
"Nombre de documents ร afficher:",
5, 50, 20
)
# Calculer matrice de similaritรฉ
similarity_matrix = cosine_similarity(embeddings[:num_docs_viz])
# Heatmap avec seaborn/plotly
fig, ax = plt.subplots(figsize=(12, 10))
sns.heatmap(
similarity_matrix,
annot=False, # Trop de valeurs
cmap='RdYlGn',
vmin=0,
vmax=1,
square=True,
cbar_kws={'label': 'Similaritรฉ Cosinus'},
xticklabels=[f"Doc {i}" for i in range(num_docs_viz)],
yticklabels=[f"Doc {i}" for i in range(num_docs_viz)]
)
plt.title('Matrice de Similaritรฉ Sรฉmantique')
st.pyplot(fig)
# Insights automatiques
st.markdown("""
**๐ Observations:**
- Les documents similaires apparaissent en vert/jaune
- Les blocs de couleur rรฉvรจlent des clusters thรฉmatiques
- Diagonale = 1.0 (document identique ร lui-mรชme)
""")
4.3 Clustering Automatique
st.subheader("๐ฏ Clusters Thรฉmatiques")
# Nombre de clusters
n_clusters = st.slider("Nombre de clusters:", 2, 10, 3)
# K-means clustering
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
clusters = kmeans.fit_predict(embeddings)
# Visualisation 2D (t-SNE)
from sklearn.manifold import TSNE
tsne = TSNE(n_components=2, random_state=42)
embeddings_2d = tsne.fit_transform(embeddings)
# Plot
fig, ax = plt.subplots(figsize=(14, 10))
scatter = ax.scatter(
embeddings_2d[:, 0],
embeddings_2d[:, 1],
c=clusters,
cmap='tab10',
s=100,
alpha=0.6
)
# Annotations pour quelques docs
for i in range(0, len(corpus), max(1, len(corpus)//20)):
ax.annotate(
f"Doc {i}",
(embeddings_2d[i, 0], embeddings_2d[i, 1]),
fontsize=8
)
plt.colorbar(scatter, label='Cluster')
plt.title('Clustering Automatique des Documents')
st.pyplot(fig)
# Afficher quelques docs par cluster
st.markdown("### ๐ Documents par Cluster")
for cluster_id in range(n_clusters):
with st.expander(f"Cluster {cluster_id} ({sum(clusters == cluster_id)} documents)"):
cluster_docs = [i for i, c in enumerate(clusters) if c == cluster_id]
for doc_id in cluster_docs[:5]: # Afficher 5 premiers
st.write(f"- Doc {doc_id}: {corpus[doc_id][:100]}...")
4.4 Documents Similaires
st.subheader("๐ Explorer les Similaritรฉs")
# Sรฉlection d'un document
selected_doc_id = st.selectbox(
"Choisir un document:",
options=range(len(corpus)),
format_func=lambda i: f"Doc {i}: {corpus[i][:60]}..."
)
# Trouver les plus similaires
doc_embedding = embeddings[selected_doc_id]
similarities = cosine_similarity([doc_embedding], embeddings)[0]
similarities[selected_doc_id] = -1 # Exclure le doc lui-mรชme
# Top 5
top_indices = np.argsort(similarities)[-5:][::-1]
st.markdown(f"**Document source:**")
st.info(corpus[selected_doc_id])
st.markdown("**Documents similaires:**")
for i, idx in enumerate(top_indices, 1):
with st.container():
col1, col2 = st.columns([1, 4])
with col1:
st.metric(f"#{i}", f"{similarities[idx]:.3f}")
with col2:
st.write(corpus[idx])
๐ Sous-section 5: Exemple Pas-ร -Pas
Format dรฉtaillรฉ:
st.title("๐ Exemple Complet: De A ร Z")
st.markdown("""
On va dรฉrouler TOUT le processus sur un exemple simple.
Vous allez voir exactement ce qui se passe "sous le capot". ๐
""")
# Setup
corpus_example = [
"Le chat noir dort paisiblement",
"Un chien joue avec une balle",
"La voiture roule vite"
]
query_example = "animal domestique"
st.markdown("### ๐ Setup")
st.code(f"""
Corpus:
{chr(10).join(f' Doc {i}: "{doc}"' for i, doc in enumerate(corpus_example))}
Query: "{query_example}"
""")
# รtape 1: Tokenization
st.markdown("### 1๏ธโฃ Tokenization")
with st.expander("Voir les dรฉtails"):
st.markdown("""
Le texte est dรฉcoupรฉ en tokens (mots ou sous-mots).
**Exemple avec sub-word tokenization:**
""")
# Pseudo-code
st.code("""
Query: "animal domestique"
Tokens: ["animal", "domestique"]
Doc 0: "Le chat noir dort paisiblement"
Tokens: ["le", "chat", "noir", "dort", "paisible", "##ment"]
""")
# รtape 2: Neural Network
st.markdown("### 2๏ธโฃ Passage dans le Rรฉseau de Neurones")
st.markdown("""
Chaque document passe ร travers un Transformer (BERT).
**Architecture simplifiรฉe:**
""")
# Schรฉma ou flowchart
st.code("""
Input Tokens
โ
Embedding Layer (tokens โ vecteurs 384D)
โ
Attention Layer 1 (contexte local)
Attention Layer 2
...
Attention Layer 12 (contexte global)
โ
Pooling (moyenne des tokens)
โ
Output: Dense Vector (384 dimensions)
""")
st.info("โฑ๏ธ Sur un GPU: ~10ms par document. Sur CPU: ~100ms")
# รtape 3: Vecteurs Rรฉsultants
st.markdown("### 3๏ธโฃ Vecteurs Gรฉnรฉrรฉs")
# Afficher les vrais embeddings (tronquรฉs)
embeddings_example = model.encode([query_example] + corpus_example)
for i, text in enumerate([query_example] + corpus_example):
label = "Query" if i == 0 else f"Doc {i-1}"
with st.expander(f"{label}: {text}"):
vec = embeddings_example[i]
st.code(f"""
Vector (384 dimensions):
[{vec[0]:.3f}, {vec[1]:.3f}, {vec[2]:.3f}, ..., {vec[-1]:.3f}]
Premiers 10 รฉlรฉments:
{vec[:10]}
""")
# รtape 4: Calcul Similaritรฉ
st.markdown("### 4๏ธโฃ Calcul de Similaritรฉ Cosinus")
query_vec = embeddings_example[0]
doc_vecs = embeddings_example[1:]
st.markdown("**Formule:**")
st.latex(r"\text{sim}(q, d) = \frac{q \cdot d}{||q|| \times ||d||}")
# Calcul dรฉtaillรฉ pour Doc 0
st.markdown("**Exemple dรฉtaillรฉ pour Doc 0:**")
with st.expander("Calculs รฉtape par รฉtape"):
dot_product = np.dot(query_vec, doc_vecs[0])
norm_q = np.linalg.norm(query_vec)
norm_d = np.linalg.norm(doc_vecs[0])
similarity = dot_product / (norm_q * norm_d)
st.code(f"""
1. Produit scalaire (dot product):
q ยท d = {dot_product:.6f}
2. Norme de la query:
||q|| = โ({norm_q**2:.6f}) = {norm_q:.6f}
3. Norme du document:
||d|| = โ({norm_d**2:.6f}) = {norm_d:.6f}
4. Similaritรฉ cosinus:
sim = {dot_product:.6f} / ({norm_q:.6f} ร {norm_d:.6f})
= {dot_product:.6f} / {norm_q * norm_d:.6f}
= {similarity:.6f}
""")
# รtape 5: Rรฉsultats
st.markdown("### 5๏ธโฃ Rรฉsultats Finaux")
# Calculer toutes les similaritรฉs
similarities = cosine_similarity([query_vec], doc_vecs)[0]
results_df = pd.DataFrame({
'Document': corpus_example,
'Score': similarities,
'Rang': range(1, 4)
})
results_df = results_df.sort_values('Score', ascending=False).reset_index(drop=True)
results_df['Rang'] = range(1, 4)
st.dataframe(results_df, use_container_width=True)
# Interprรฉtation
st.success(f"""
โ
**Rรฉsultat:**
Le document "{results_df.iloc[0]['Document']}" est le plus similaire!
**Pourquoi?**
- Embeddings capte que "chat" et "chien" sont des "animaux domestiques"
- Mรชme sans mots exacts en commun! ๐ฏ
""")
โ๏ธ Sous-section 6: Comparaison Multi-Techniques
Scรฉnarios de comparaison:
Scรฉnario 1: Synonymes
st.subheader("๐ Test: Synonymes")
test_query = "voiture rapide"
test_docs = [
"automobile vรฉloce",
"voiture voiture rapide rapide",
"un chat dort"
]
# Tableau comparatif
comparison_data = []
for doc in test_docs:
# TF-IDF
tfidf_score = compute_tfidf_similarity(test_query, doc)
# BM25
bm25_score = compute_bm25_similarity(test_query, doc)
# Embeddings
emb_score = compute_embedding_similarity(test_query, doc)
comparison_data.append({
'Document': doc,
'TF-IDF': f"{tfidf_score:.3f}",
'BM25': f"{bm25_score:.3f}",
'Embeddings': f"{emb_score:.3f}"
})
df = pd.DataFrame(comparison_data)
# Highlight du meilleur
st.dataframe(
df.style.highlight_max(axis=0, subset=['TF-IDF', 'BM25', 'Embeddings']),
use_container_width=True
)
st.markdown("""
**๐ก Observation:**
- **TF-IDF/BM25:** Favorise la rรฉpรฉtition ("voiture voiture")
- **Embeddings:** Comprend les synonymes ("automobile vรฉloce") โ
""")
Scรฉnario 2: Cas Rรฉel sur Dataset
st.subheader("โ๏ธ Battle Royale: Recherche Rรฉelle")
# Input query
battle_query = st.text_input("Query de test:", value="cuisine italienne")
if battle_query:
# Lancer les 3 techniques
with st.spinner("Calcul en cours..."):
results_tfidf = tfidf_engine.search(battle_query, top_k=10)
results_bm25 = bm25_engine.search(battle_query, top_k=10)
results_embeddings = embedding_engine.search(battle_query, top_k=10)
# Affichage comparatif
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("### ๐ TF-IDF")
for i, (idx, score) in enumerate(results_tfidf[:5], 1):
st.write(f"{i}. [{score:.2f}] Doc {idx}")
with col2:
st.markdown("### ๐ฏ BM25")
for i, (idx, score) in enumerate(results_bm25[:5], 1):
st.write(f"{i}. [{score:.2f}] Doc {idx}")
with col3:
st.markdown("### ๐ง Embeddings")
for i, result in enumerate(results_embeddings[:5], 1):
st.write(f"{i}. [{result['score']:.2f}] Doc {result['index']}")
# Mรฉtriques de comparaison
st.markdown("### ๐ Mรฉtriques")
# Overlap des top 10
set_tfidf = set([idx for idx, _ in results_tfidf])
set_bm25 = set([idx for idx, _ in results_bm25])
set_emb = set([r['index'] for r in results_embeddings])
overlap_all = len(set_tfidf & set_bm25 & set_emb)
col1, col2, col3 = st.columns(3)
with col1:
overlap_tb = len(set_tfidf & set_bm25)
st.metric("TF-IDF โฉ BM25", f"{overlap_tb}/10")
with col2:
overlap_te = len(set_tfidf & set_emb)
st.metric("TF-IDF โฉ Embeddings", f"{overlap_te}/10")
with col3:
overlap_be = len(set_bm25 & set_emb)
st.metric("BM25 โฉ Embeddings", f"{overlap_be}/10")
st.info(f"Documents en commun (top 10 des 3): {overlap_all}")
๐จ Sous-section 7: Hybrid Search
La combinaison ultime:
st.title("๐จ Hybrid Search: BM25 + Embeddings")
st.markdown("""
### ๐ค Le Meilleur des Deux Mondes
**Principe:**
Combiner les scores de BM25 (lexical) et Embeddings (sรฉmantique).
**Formule:**
```python
score_final = ฮฑ ร score_bm25 + (1-ฮฑ) ร score_embeddings
Oรน ฮฑ contrรดle le poids de chaque technique. """)
Widget de tuning
alpha = st.slider( "ฮฑ (poids BM25):", min_value=0.0, max_value=1.0, value=0.5, step=0.05, help="0 = Embeddings pur | 1 = BM25 pur | 0.5 = รฉquilibrรฉ" )
st.markdown(f""" Configuration actuelle:
- Poids BM25: {alpha:.0%}
- Poids Embeddings: {(1-alpha):.0%} """)
Query
hybrid_query = st.text_input("Recherche hybrid:", placeholder="smartphone derniรจre gรฉnรฉration")
if hybrid_query: # Calcul hybrid results_hybrid = hybrid_search.search( hybrid_query, top_k=10, alpha=alpha )
# Affichage
st.markdown("### ๐ Rรฉsultats Hybrid")
for i, result in enumerate(results_hybrid, 1):
with st.container():
col1, col2, col3, col4 = st.columns([1, 2, 2, 5])
with col1:
st.metric(f"#{i}", f"{result['combined_score']:.2f}")
with col2:
st.caption("BM25")
st.write(f"{result['bm25_score']:.2f}")
with col3:
st.caption("Embeddings")
st.write(f"{result['emb_score']:.2f}")
with col4:
st.write(result['document'][:100] + "...")
Visualisation de l'effet de alpha
st.markdown("### ๐ Impact du Paramรจtre ฮฑ")
Calculer scores pour diffรฉrents alpha
alpha_values = np.linspace(0, 1, 21) doc_scores = []
sample_doc_idx = 0 # Un doc d'exemple
for a in alpha_values: # Recalculer score pour ce alpha score = hybrid_search.compute_score(hybrid_query, sample_doc_idx, alpha=a) doc_scores.append(score)
Plot
fig, ax = plt.subplots(figsize=(12, 6)) ax.plot(alpha_values, doc_scores, linewidth=3, marker='o') ax.axvline(alpha, color='red', linestyle='--', label=f'ฮฑ actuel = {alpha}') ax.set_xlabel('ฮฑ (poids BM25)', fontsize=12) ax.set_ylabel('Score Final', fontsize=12) ax.set_title(f'รvolution du Score selon ฮฑ\nDocument: {corpus[sample_doc_idx][:50]}...', fontsize=14) ax.grid(True, alpha=0.3) ax.legend() st.pyplot(fig)
Recommandations
st.info(""" ๐ก Quand ajuster ฮฑ?
- ฮฑ โ 0.3-0.4: Corpus avec beaucoup de synonymes, recherche conceptuelle
- ฮฑ โ 0.5-0.6: รquilibrรฉ (recommandรฉ par dรฉfaut)
- ฮฑ โ 0.7-0.8: Noms exacts importants, codes, IDs """)
---
### โก Sous-section 8: Performance
**Contenu:**
#### **8.1 Complexitรฉ et Coรปts**
```python
st.subheader("โฑ๏ธ Temps de Calcul")
# Tableau comparatif
perf_data = {
'Opรฉration': [
'Indexation (1000 docs)',
'Recherche (1 query)',
'Recherche (100 queries)',
'Mรฉmoire (1000 docs)'
],
'TF-IDF': ['~0.1s', '~5ms', '~0.5s', '~2 MB'],
'BM25': ['~0.1s', '~5ms', '~0.5s', '~2 MB'],
'Embeddings': ['~30s (GPU) / ~300s (CPU)', '~10ms', '~1s', '~15 MB']
}
st.table(pd.DataFrame(perf_data))
st.warning("""
โ ๏ธ **Embeddings plus lents:**
- Indexation: 10-100ร plus lente (nรฉcessite GPU)
- Recherche: 2-3ร plus lente
- Mรฉmoire: 5-10ร plus gourmande
**Mais:** Qualitรฉ de recherche BEAUCOUP meilleure! ๐ฏ
""")
8.2 Benchmark Rรฉel
st.subheader("๐ Benchmark sur Corpus Rรฉel")
# Options
benchmark_size = st.select_slider(
"Taille du corpus:",
options=[100, 500, 1000, 5000, 10000],
value=1000
)
if st.button("Lancer le benchmark"):
with st.spinner("Benchmarking en cours..."):
# Charger corpus
corpus = load_dataset('wikipedia', sample_size=benchmark_size)
# Mesurer indexation
times_index = {}
# TF-IDF
start = time.time()
tfidf = TFIDFEngine(corpus)
times_index['TF-IDF'] = time.time() - start
# BM25
start = time.time()
bm25 = BM25Engine(corpus)
times_index['BM25'] = time.time() - start
# Embeddings
start = time.time()
embeddings_engine = EmbeddingSearch()
embeddings_engine.index(corpus)
times_index['Embeddings'] = time.time() - start
# Mesurer recherche
test_queries = ["histoire france", "technologie moderne", "cuisine italienne"]
times_search = {k: [] for k in ['TF-IDF', 'BM25', 'Embeddings']}
for query in test_queries:
# TF-IDF
start = time.time()
tfidf.search(query)
times_search['TF-IDF'].append(time.time() - start)
# BM25
start = time.time()
bm25.search(query)
times_search['BM25'].append(time.time() - start)
# Embeddings
start = time.time()
embeddings_engine.search(query)
times_search['Embeddings'].append(time.time() - start)
# Rรฉsultats
st.markdown("### ๐ Rรฉsultats")
col1, col2 = st.columns(2)
with col1:
st.markdown("**Temps d'Indexation**")
fig, ax = plt.subplots(figsize=(8, 5))
techniques = list(times_index.keys())
times = list(times_index.values())
bars = ax.bar(techniques, times, color=['red', 'blue', 'green'])
ax.set_ylabel('Temps (secondes)')
ax.set_title(f'Indexation de {benchmark_size} documents')
for bar, time in zip(bars, times):
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height,
f'{time:.2f}s', ha='center', va='bottom')
st.pyplot(fig)
with col2:
st.markdown("**Temps de Recherche (moyenne)**")
avg_search_times = {k: np.mean(v) * 1000 for k, v in times_search.items()} # en ms
fig, ax = plt.subplots(figsize=(8, 5))
techniques = list(avg_search_times.keys())
times = list(avg_search_times.values())
bars = ax.bar(techniques, times, color=['red', 'blue', 'green'])
ax.set_ylabel('Temps (millisecondes)')
ax.set_title('Recherche (moyenne sur 3 queries)')
for bar, time in zip(bars, times):
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height,
f'{time:.1f}ms', ha='center', va='bottom')
st.pyplot(fig)
8.3 Optimisations
st.subheader("๐ Optimisations Possibles")
st.markdown("""
### Pour Accรฉlรฉrer les Embeddings:
#### 1. **Utiliser un GPU**
```python
# Dรฉtection automatique du GPU
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = SentenceTransformer('model-name', device=device)
Speedup: 10-50ร plus rapide! โก
2. Batch Processing
# Au lieu de 1 par 1
embeddings = model.encode(documents, batch_size=32)
Speedup: 2-5ร plus rapide
3. Utiliser FAISS (Vector Database)
import faiss
# Index optimisรฉ pour recherche rapide
index = faiss.IndexFlatIP(embedding_dim)
index.add(embeddings)
# Recherche ultra-rapide
scores, indices = index.search(query_embedding, k=10)
Speedup: 10-100ร sur gros corpus
4. Modรจles Plus Petits
| Modรจle | Dimensions | Vitesse | Qualitรฉ |
|---|---|---|---|
| MiniLM | 384 | โกโกโก | โญโญโญ |
| MPNet | 768 | โกโก | โญโญโญโญ |
| Large | 1024 | โก | โญโญโญโญโญ |
Recommandation: MiniLM pour la plupart des cas! ๐ฏ
5. Caching Intelligent
# Sauvegarder les embeddings calculรฉs
import pickle
# Save
with open('embeddings_cache.pkl', 'wb') as f:
pickle.dump(embeddings, f)
# Load (instantanรฉ!)
with open('embeddings_cache.pkl', 'rb') as f:
embeddings = pickle.load(f)
6. Quantization
# Rรฉduire la prรฉcision (float32 โ int8)
# Perte de qualitรฉ minime (~1%)
# Gain de mรฉmoire et vitesse significatif
embeddings_int8 = (embeddings * 127).astype('int8')
Mรฉmoire: 4ร moins | Vitesse: 2ร plus rapide """)
---
## ๐ SECTION SYNTHรSE COMPARATIVE
### Structure Complรจte:
```python
def render_synthesis_section():
st.title("๐ Synthรจse: Quelle Technique Choisir?")
st.markdown("""
Vous avez explorรฉ 3 techniques de recherche textuelle.
Maintenant, dรฉcouvrez **quand** et **pourquoi** utiliser chacune! ๐ฏ
""")
๐ Sous-section 1: Tableau Comparatif Complet
st.subheader("๐ Tableau Comparatif Complet")
# Tableau interactif avec filtres
comparison_data = {
'Critรจre': [
'Type de matching',
'Synonymes',
'Typos/Fautes',
'Noms propres',
'Codes/IDs',
'Polysรฉmie (contexte)',
'Relations conceptuelles',
'Vitesse indexation',
'Vitesse recherche',
'Mรฉmoire requise',
'Ressources (CPU/GPU)',
'Multilingue',
'Interprรฉtabilitรฉ',
'Scalabilitรฉ',
'Facilitรฉ implรฉmentation',
'Coรปt infrastructure'
],
'TF-IDF': [
'Lexical (mots exacts)',
'โ Fail complet',
'โ Fail complet',
'โ
Excellent',
'โ
Excellent',
'โ Pas de distinction',
'โ Aucune',
'โกโกโก Trรจs rapide',
'โกโกโก <5ms',
'๐พ Minimal (~2MB/1k docs)',
'๐ป CPU uniquement',
'โ 1 langue',
'โ
โ
โ
Trรจs clair',
'โ
โ
โ
Excellent',
'โ
โ
โ
Facile',
'๐ฐ Minimal'
],
'BM25': [
'Lexical (mots exacts)',
'โ Fail complet',
'โ Fail complet',
'โ
Excellent',
'โ
Excellent',
'โ Pas de distinction',
'โ Aucune',
'โกโกโก Trรจs rapide',
'โกโกโก <5ms',
'๐พ Minimal (~2MB/1k docs)',
'๐ป CPU uniquement',
'โ 1 langue',
'โ
โ
โ
Trรจs clair',
'โ
โ
โ
Excellent',
'โ
โ
Facile',
'๐ฐ Minimal'
],
'Embeddings': [
'Sรฉmantique (sens)',
'โ
โ
โ
Excellent',
'โ ๏ธ Partiel',
'โ ๏ธ Moyen',
'โ Faible',
'โ
โ
Bon',
'โ
โ
โ
Excellent',
'โก Lent (~30s GPU)',
'โกโก ~10ms',
'๐พ๐พ Moyen (~15MB/1k docs)',
'๐ฎ GPU recommandรฉ',
'โ
โ
โ
Multi-langue',
'โ Boรฎte noire',
'โ ๏ธ Coรปteux (>10k docs)',
'โ ๏ธ Complexe',
'๐ฐ๐ฐ Moyen-รฉlevรฉ'
],
'Hybrid': [
'Lexical + Sรฉmantique',
'โ
โ
โ
Excellent',
'โ ๏ธ Partiel',
'โ
โ
Trรจs bon',
'โ
โ
Trรจs bon',
'โ
โ
Bon',
'โ
โ
โ
Excellent',
'โก Lent (~30s GPU)',
'โกโก ~15ms',
'๐พ๐พ Moyen',
'๐ป + ๐ฎ',
'โ
โ
โ
Multi-langue',
'โ ๏ธ Partiel',
'โ
โ
Bon',
'โ ๏ธ Complexe',
'๐ฐ๐ฐ Moyen-รฉlevรฉ'
]
}
df_comparison = pd.DataFrame(comparison_data)
# Affichage avec style
st.dataframe(
df_comparison.set_index('Critรจre'),
use_container_width=True,
height=600
)
# Lรฉgende
st.caption("""
**Lรฉgende:**
โ
= Bon | โ ๏ธ = Moyen | โ = Faible
โก = Rapide | ๐พ = Mรฉmoire | ๐ป = CPU | ๐ฎ = GPU | ๐ฐ = Coรปt
""")
๐ฏ Sous-section 2: Guide de Dรฉcision
st.subheader("๐ฏ Arbre de Dรฉcision")
st.markdown("""
### ๐ณ Quel Technique Pour Mon Cas?
Rรฉpondez ร ces questions pour trouver la meilleure solution:
""")
# Quiz interactif
q1 = st.radio(
"**1. Votre corpus contient principalement:**",
[
"Du texte naturel (articles, descriptions)",
"Des donnรฉes structurรฉes (codes, IDs, noms)",
"Un mรฉlange des deux"
]
)
q2 = st.radio(
"**2. Vos utilisateurs recherchent plutรดt par:**",
[
"Mots-clรฉs exacts",
"Concepts/idรฉes (synonymes OK)",
"Les deux"
]
)
q3 = st.radio(
"**3. Vos contraintes de performance:**",
[
"Temps rรฉel critique (<10ms)",
"Performance importante mais flexible",
"Pas de contrainte forte"
]
)
q4 = st.radio(
"**4. Votre budget infrastructure:**",
[
"Minimal (pas de GPU)",
"Moyen (GPU possible)",
"Flexible"
]
)
q5 = st.radio(
"**5. Multilingue nรฉcessaire?**",
["Oui", "Non"]
)
if st.button("๐ฏ Voir la recommandation"):
# Logique de dรฉcision
score_tfidf = 0
score_bm25 = 0
score_embeddings = 0
score_hybrid = 0
# Q1
if "structurรฉes" in q1:
score_bm25 += 3
score_tfidf += 2
elif "naturel" in q1:
score_embeddings += 3
score_hybrid += 2
else:
score_hybrid += 3
score_bm25 += 2
# Q2
if "exacts" in q2:
score_bm25 += 3
score_tfidf += 2
elif "Concepts" in q2:
score_embeddings += 3
score_hybrid += 1
else:
score_hybrid += 3
# Q3
if "rรฉel" in q3:
score_bm25 += 3
score_tfidf += 3
elif "importante" in q3:
score_bm25 += 2
score_hybrid += 1
else:
score_embeddings += 2
score_hybrid += 2
# Q4
if "Minimal" in q4:
score_bm25 += 3
score_tfidf += 3
elif "Moyen" in q4:
score_hybrid += 2
score_embeddings += 1
else:
score_embeddings += 2
score_hybrid += 2
# Q5
if q5 == "Oui":
score_embeddings += 3
score_hybrid += 2
else:
score_bm25 += 1
# Recommandation
scores = {
'TF-IDF': score_tfidf,
'BM25': score_bm25,
'Embeddings': score_embeddings,
'Hybrid': score_hybrid
}
best_technique = max(scores, key=scores.get)
# Affichage
st.markdown("### ๐ Recommandation")
if best_technique == 'BM25':
st.success(f"""
**๐ฏ BM25 est recommandรฉ pour votre cas!**
**Pourquoi?**
- Recherche lexicale efficace
- Performance excellente
- Infrastructure minimale
- Facile ร implรฉmenter
**Conseil:** Commencez avec BM25, ajoutez Embeddings plus tard si besoin.
""")
elif best_technique == 'Embeddings':
st.success(f"""
**๐ง Embeddings sont recommandรฉs pour votre cas!**
**Pourquoi?**
- Recherche sรฉmantique puissante
- Multi-langue natif
- Comprend les concepts
**Conseil:** Investissez dans un GPU pour de bonnes performances.
""")
elif best_technique == 'Hybrid':
st.success(f"""
**๐จ Hybrid Search (BM25 + Embeddings) est recommandรฉ!**
**Pourquoi?**
- Meilleur des deux mondes
- Flexibilitรฉ maximale
- Qualitรฉ optimale
**Conseil:** Commencez avec ฮฑ=0.5, ajustez selon vos mรฉtriques.
""")
else:
st.success(f"""
**๐ TF-IDF est recommandรฉ pour votre cas!**
**Pourquoi?**
- Simple et efficace
- Ressources minimales
- Bon point de dรฉpart
**Conseil:** Migrez vers BM25 pour de meilleures performances.
""")
# Scores dรฉtaillรฉs
with st.expander("Voir les scores dรฉtaillรฉs"):
scores_df = pd.DataFrame({
'Technique': list(scores.keys()),
'Score': list(scores.values())
}).sort_values('Score', ascending=False)
fig, ax = plt.subplots(figsize=(10, 5))
bars = ax.barh(scores_df['Technique'], scores_df['Score'])
ax.set_xlabel('Score de Pertinence')
ax.set_title('Scores par Technique')
# Colorier le meilleur
bars[0].set_color('green')
st.pyplot(fig)
๐ผ Sous-section 3: Cas d'Usage Rรฉels
st.subheader("๐ผ Cas d'Usage Rรฉels")
# Exemples concrets par industrie
use_cases = {
'๐ E-commerce': {
'description': "Recherche de produits avec filtres",
'challenges': [
"Synonymes produits (\"tรฉlรฉphone\" = \"smartphone\")",
"Variantes (\"Nike Air\" vs \"Air Nike\")",
"Filtres exacts (marque, prix)"
],
'recommendation': 'Hybrid',
'config': "BM25 (40%) + Embeddings (60%)",
'justification': "Combine matching exact de marques avec comprรฉhension sรฉmantique"
},
'๐ Documentation Technique': {
'description': "Recherche dans docs d'API, code",
'challenges': [
"Noms de fonctions exacts",
"Code snippets",
"Erreurs techniques"
],
'recommendation': 'BM25',
'config': "k1=1.2, b=0.75",
'justification': "Prioritรฉ au matching exact pour les termes techniques"
},
'๐ฌ Support Client / FAQ': {
'description': "Trouver rรฉponses aux questions clients",
'challenges': [
"Multiples formulations mรชme question",
"Synonymes frรฉquents",
"Questions complexes"
],
'recommendation': 'Embeddings',
'config': "Sentence-BERT multilingue",
'justification': "Comprend l'intention derriรจre diffรฉrentes formulations"
},
'๐ฐ Recherche d'Articles': {
'description': "Moteur de recherche de contenu",
'challenges': [
"Concepts similaires",
"Recherche par thรจme",
"Multi-langues"
],
'recommendation': 'Hybrid',
'config': "BM25 (30%) + Embeddings (70%)",
'justification': "Sรฉmantique pour concepts, lexical pour noms propres"
},
'๐ฅ Dossiers Mรฉdicaux': {
'description': "Recherche dans historiques patients",
'challenges': [
"Termes mรฉdicaux exacts",
"IDs patients/mรฉdicaments",
"Rรฉglementation (traรงabilitรฉ)"
],
'recommendation': 'BM25',
'config': "k1=1.5, b=0.75 + index inversรฉ",
'justification': "Interprรฉtabilitรฉ et matching exact requis"
},
'๐ Plateforme รducative': {
'description': "Recherche de cours, ressources",
'challenges': [
"Concepts pรฉdagogiques",
"Multi-niveaux",
"Recommandations"
],
'recommendation': 'Embeddings',
'config': "Embeddings + clustering thรฉmatique",
'justification': "Recommandations sรฉmantiques de contenu similaire"
}
}
# Sรฉlection
selected_industry = st.selectbox(
"Choisir un domaine:",
list(use_cases.keys())
)
use_case = use_cases[selected_industry]
# Affichage dรฉtaillรฉ
col1, col2 = st.columns([2, 1])
with col1:
st.markdown(f"### {selected_industry}")
st.write(use_case['description'])
st.markdown("**Challenges:**")
for challenge in use_case['challenges']:
st.write(f"- {challenge}")
with col2:
st.metric("Recommandation", use_case['recommendation'])
st.caption(use_case['config'])
st.info(f"**๐ก Justification:** {use_case['justification']}")
# Exemple de code
with st.expander("๐ป Exemple d'implรฉmentation"):
if use_case['recommendation'] == 'BM25':
st.code("""
from src.bm25_engine import BM25Engine
# Configuration
engine = BM25Engine(
documents,
k1=1.2,
b=0.75
)
# Recherche
results = engine.search("query", top_k=10)
""", language='python')
elif use_case['recommendation'] == 'Embeddings':
st.code("""
from sentence_transformers import SentenceTransformer
# Modรจle multilingue
model = SentenceTransformer(
'paraphrase-multilingual-mpnet-base-v2'
)
# Index
embeddings = model.encode(documents)
# Recherche
query_emb = model.encode([query])
similarities = cosine_similarity(query_emb, embeddings)
""", language='python')
else: # Hybrid
st.code("""
from src.hybrid_search import HybridSearch
# Configuration
hybrid = HybridSearch(
documents,
alpha=0.4 # 40% BM25, 60% Embeddings
)
# Recherche
results = hybrid.search("query", top_k=10)
""", language='python')
๐ฌ Sous-section 4: Benchmark Comparatif
st.subheader("๐ฌ Benchmark: Qualitรฉ vs Performance")
st.markdown("""
### Mรฉtriques de Qualitรฉ
Pour รฉvaluer les techniques, on utilise des datasets annotรฉs avec:
- **Precision@K:** % de rรฉsultats pertinents dans les top K
- **Recall@K:** % de documents pertinents trouvรฉs
- **MRR (Mean Reciprocal Rank):** Position moyenne du 1er rรฉsultat pertinent
- **NDCG:** Mesure tenant compte du ranking
""")
# Rรฉsultats simulรฉs (ร remplacer par vrais benchmarks)
benchmark_results = {
'Mรฉtrique': ['Precision@10', 'Recall@10', 'MRR', 'NDCG@10', 'Temps (ms)'],
'TF-IDF': [0.45, 0.52, 0.38, 0.51, 3],
'BM25': [0.58, 0.64, 0.52, 0.63, 4],
'Embeddings': [0.76, 0.81, 0.71, 0.79, 12],
'Hybrid': [0.82, 0.86, 0.78, 0.84, 16]
}
df_benchmark = pd.DataFrame(benchmark_results)
# Graphique radar
fig = go.Figure()
techniques = ['TF-IDF', 'BM25', 'Embeddings', 'Hybrid']
metrics = ['Precision@10', 'Recall@10', 'MRR', 'NDCG@10']
for tech in techniques:
values = df_benchmark[df_benchmark['Mรฉtrique'].isin(metrics)][tech].tolist()
values.append(values[0]) # Fermer le polygone
fig.add_trace(go.Scatterpolar(
r=values,
theta=metrics + [metrics[0]],
name=tech,
fill='toself'
))
fig.update_layout(
polar=dict(radialaxis=dict(visible=True, range=[0, 1])),
title="Comparaison Qualitรฉ (Dataset Benchmark)",
showlegend=True,
width=800,
height=600
)
st.plotly_chart(fig, use_container_width=True)
# Tableau dรฉtaillรฉ
st.dataframe(df_benchmark, use_container_width=True)
st.markdown("""
**๐ Interprรฉtation:**
- **Embeddings:** Meilleure qualitรฉ, mais plus lent
- **BM25:** Bon compromis qualitรฉ/vitesse
- **Hybrid:** Meilleur qualitรฉ globale
- **TF-IDF:** Base de comparaison (baseline)
""")
๐ Sous-section 5: Recommandations Finales
st.subheader("๐ Feuille de Route Recommandรฉe")
st.markdown("""
### ๐ค๏ธ Parcours d'Adoption Progressif
Voici comment implรฉmenter la recherche selon votre maturitรฉ:
""")
# Timeline
timeline_data = {
'Phase 1\n(Semaine 1)': {
'technique': 'BM25',
'objectif': 'MVP fonctionnel',
'actions': [
'Implรฉmenter BM25 basique',
'Index votre corpus',
'Interface de recherche simple',
'Mรฉtriques de base (latence, nb rรฉsultats)'
],
'effort': 'โก 1-2 jours',
'coรปt': '๐ฐ Minimal'
},
'Phase 2\n(Semaine 2-3)': {
'technique': 'BM25 Optimisรฉ',
'objectif': 'Production-ready',
'actions': [
'Tuning k1/b selon mรฉtriques',
'Index inversรฉ pour performance',
'Filtres (date, catรฉgorie, etc.)',
'Logging & monitoring'
],
'effort': 'โกโก 3-5 jours',
'coรปt': '๐ฐ Minimal'
},
'Phase 3\n(Mois 2)': {
'technique': 'Embeddings (Pilot)',
'objectif': 'Test sรฉmantique',
'actions': [
'Setup Sentence-BERT',
'Index subset corpus (10-20%)',
'A/B test vs BM25',
'Mesurer impact qualitรฉ'
],
'effort': 'โกโกโก 1-2 semaines',
'coรปt': '๐ฐ๐ฐ Moyen (GPU)'
},
'Phase 4\n(Mois 3)': {
'technique': 'Hybrid',
'objectif': 'Qualitรฉ optimale',
'actions': [
'Combiner BM25 + Embeddings',
'Tuning ฮฑ selon feedback',
'Full corpus embeddings',
'FAISS pour performance'
],
'effort': 'โกโกโก 2-3 semaines',
'coรปt': '๐ฐ๐ฐ Moyen'
}
}
# Affichage chronologique
for phase, data in timeline_data.items():
with st.expander(f"**{phase}: {data['technique']}** - {data['objectif']}"):
st.markdown(f"**Actions:**")
for action in data['actions']:
st.write(f"โ {action}")
col1, col2 = st.columns(2)
with col1:
st.caption(f"Effort: {data['effort']}")
with col2:
st.caption(f"Coรปt: {data['coรปt']}")
# Conseils finaux
st.markdown("---")
st.markdown("### ๐ก Conseils Pratiques")
col1, col2 = st.columns(2)
with col1:
st.markdown("""
**โ
ร FAIRE:**
- Commencer simple (BM25)
- Mesurer avant d'optimiser
- A/B tester les changements
- รcouter les utilisateurs
- Documenter les choix
- Monitorer la performance
""")
with col2:
st.markdown("""
**โ ร รVITER:**
- Over-engineering initial
- Embeddings sans GPU
- Ignorer BM25 (toujours utile!)
- Oublier le monitoring
- Tuning sans mรฉtriques
- Nรฉgliger l'UX
""")
# Call to action final
st.success("""
### ๐ฏ Prochaines รtapes
1. **Expรฉrimentez** avec les 3 techniques sur votre corpus
2. **Mesurez** la qualitรฉ avec vos utilisateurs
3. **Itรฉrez** selon les retours
4. **Scalez** progressivement
**Bonne chance dans vos projets de recherche! ๐**
""")
๐ป IMPLรMENTATION REQUISE
Fichier src/embedding_engine.py:
from sentence_transformers import SentenceTransformer
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from typing import List, Dict, Tuple, Optional
import pickle
from pathlib import Path
class EmbeddingSearch:
"""
Moteur de recherche par embeddings vectoriels
Utilise Sentence-Transformers pour l'encodage
"""
def __init__(
self,
model_name: str = 'paraphrase-multilingual-MiniLM-L12-v2',
cache_dir: Optional[str] = './cache'
):
"""
Args:
model_name: Nom du modรจle HuggingFace
cache_dir: Dossier pour cache des embeddings
"""
self.model_name = model_name
self.model = SentenceTransformer(model_name)
self.cache_dir = Path(cache_dir) if cache_dir else None
self.documents = []
self.embeddings = None
def index(
self,
documents: List[str],
use_cache: bool = True,
batch_size: int = 32,
show_progress: bool = True
):
"""
Index les documents en calculant leurs embeddings
Args:
documents: Liste de textes
use_cache: Utiliser le cache si disponible
batch_size: Taille des batches pour encoding
show_progress: Afficher barre de progression
"""
self.documents = documents
# Vรฉrifier cache
if use_cache and self.cache_dir:
cached_embeddings = self._load_cache()
if cached_embeddings is not None:
self.embeddings = cached_embeddings
return
# Calculer embeddings
self.embeddings = self.model.encode(
documents,
batch_size=batch_size,
show_progress_bar=show_progress,
convert_to_numpy=True
)
# Sauvegarder cache
if use_cache and self.cache_dir:
self._save_cache()
def search(
self,
query: str,
top_k: int = 5
) -> List[Dict]:
"""
Recherche les documents les plus similaires
Args:
query: Requรชte texte
top_k: Nombre de rรฉsultats
Returns:
List of {'index': int, 'score': float, 'document': str}
"""
if self.embeddings is None:
raise ValueError("Index not built. Call .index() first.")
# Encoder query
query_embedding = self.model.encode([query])[0]
# Similaritรฉs
similarities = cosine_similarity(
[query_embedding],
self.embeddings
)[0]
# Top-k
top_indices = np.argsort(similarities)[-top_k:][::-1]
results = []
for idx in top_indices:
results.append({
'index': int(idx),
'score': float(similarities[idx]),
'document': self.documents[idx]
})
return results
def find_similar(
self,
doc_index: int,
top_k: int = 5,
exclude_self: bool = True
) -> List[Dict]:
"""
Trouve les documents similaires ร un document donnรฉ
Args:
doc_index: Index du document source
top_k: Nombre de rรฉsultats
exclude_self: Exclure le document lui-mรชme
"""
doc_embedding = self.embeddings[doc_index]
similarities = cosine_similarity(
[doc_embedding],
self.embeddings
)[0]
if exclude_self:
similarities[doc_index] = -1
top_indices = np.argsort(similarities)[-top_k:][::-1]
results = []
for idx in top_indices:
results.append({
'index': int(idx),
'score': float(similarities[idx]),
'document': self.documents[idx]
})
return results
def _load_cache(self) -> Optional[np.ndarray]:
"""Charge les embeddings depuis le cache"""
import hashlib
# Hash des documents
doc_hash = hashlib.md5(
"".join(self.documents).encode()
).hexdigest()[:8]
cache_file = self.cache_dir / f"embeddings_{self.model_name}_{doc_hash}.pkl"
if cache_file.exists():
with open(cache_file, 'rb') as f:
return pickle.load(f)
return None
def _save_cache(self):
"""Sauvegarde les embeddings dans le cache"""
import hashlib
self.cache_dir.mkdir(exist_ok=True)
doc_hash = hashlib.md5(
"".join(self.documents).encode()
).hexdigest()[:8]
cache_file = self.cache_dir / f"embeddings_{self.model_name}_{doc_hash}.pkl"
with open(cache_file, 'wb') as f:
pickle.dump(self.embeddings, f)
Fichier src/hybrid_search.py:
from typing import List, Dict
import numpy as np
class HybridSearch:
"""
Combine BM25 (lexical) et Embeddings (sรฉmantique)
"""
def __init__(
self,
documents: List[str],
bm25_engine,
embedding_engine,
alpha: float = 0.5
):
"""
Args:
documents: Liste de documents
bm25_engine: Instance de BM25Engine
embedding_engine: Instance de EmbeddingSearch
alpha: Poids BM25 (0=embeddings only, 1=bm25 only)
"""
self.documents = documents
self.bm25 = bm25_engine
self.embeddings = embedding_engine
self.alpha = alpha
def search(
self,
query: str,
top_k: int = 10
) -> List[Dict]:
"""
Recherche hybride
Returns:
List of {
'index': int,
'combined_score': float,
'bm25_score': float,
'emb_score': float,
'document': str
}
"""
# Scores BM25
bm25_results = self.bm25.search(query, top_k=len(self.documents))
bm25_scores = {idx: score for idx, score in bm25_results}
# Scores Embeddings
emb_results = self.embeddings.search(query, top_k=len(self.documents))
emb_scores = {r['index']: r['score'] for r in emb_results}
# Normalisation
bm25_norm = self._normalize(bm25_scores)
emb_norm = self._normalize(emb_scores)
# Combinaison
combined = {}
for idx in range(len(self.documents)):
bm25_s = bm25_norm.get(idx, 0)
emb_s = emb_norm.get(idx, 0)
combined[idx] = self.alpha * bm25_s + (1 - self.alpha) * emb_s
# Top-k
sorted_results = sorted(
combined.items(),
key=lambda x: x[1],
reverse=True
)[:top_k]
return [
{
'index': idx,
'combined_score': score,
'bm25_score': bm25_scores.get(idx, 0),
'emb_score': emb_scores.get(idx, 0),
'document': self.documents[idx]
}
for idx, score in sorted_results
]
def _normalize(self, scores: Dict[int, float]) -> Dict[int, float]:
"""Min-max normalization"""
if not scores:
return {}
values = list(scores.values())
min_val, max_val = min(values), max(values)
if max_val == min_val:
return {k: 1.0 for k in scores}
return {
k: (v - min_val) / (max_val - min_val)
for k, v in scores.items()
}
๐จ VISUALISATIONS SPรCIFIQUES
Ajouter ร src/visualizations.py:
def plot_embedding_space_3d(embeddings, labels, query_embedding=None, query_label="Query"):
"""Visualisation 3D interactive avec Plotly"""
pass
def plot_similarity_heatmap(similarity_matrix, labels):
"""Heatmap de similaritรฉ entre documents"""
pass
def plot_tsne_clustering(embeddings, labels, clusters=None):
"""t-SNE 2D avec clustering optionnel"""
pass
def plot_technique_comparison_radar(metrics_dict):
"""Graphique radar comparant TF-IDF/BM25/Embeddings"""
pass
def plot_hybrid_alpha_effect(scores_by_alpha, doc_label):
"""Graphique montrant l'effet du paramรจtre alpha"""
pass
๐ REQUIREMENTS.TXT - Mise ร Jour
Ajouter:
# Embeddings
sentence-transformers>=2.2.0
torch>=2.0.0
transformers>=4.30.0
# Optimisations (optionnel)
faiss-cpu>=1.7.4 # ou faiss-gpu si GPU disponible
# Visualisations avancรฉes
umap-learn>=0.5.3
๐ฌ COMMUNICATION
ร chaque message:
- Explique ce que tu vas faire
- Montre le code crรฉรฉ/modifiรฉ
- Termine par "๐ Commandes ร exรฉcuter"
Exemple:
๐ Commandes ร exรฉcuter:
# Installer les nouvelles dรฉpendances
pip install sentence-transformers torch
# Tester l'import
python -c "from src.embedding_engine import EmbeddingSearch; print('OK')"
# Lancer l'app
streamlit run app.py
# (Optionnel) Vรฉrifier le GPU
python -c "import torch; print(torch.cuda.is_available())"
๐ PRIORISATION
Phase 1 (URGENT):
- Page d'accueil complรจte
- Crรฉer
embedding_engine.py - Section Embeddings - Introduction & Concepts
Phase 2: 4. Sections Recherche & Exploration 5. Visualisations 3D/2D 6. Exemple pas-ร -pas
Phase 3: 7. Section Comparaison 8. Hybrid Search 9. Section Synthรจse complรจte
๐ฏ C'EST PARTI!
Commence par:
- Page d'accueil avec prรฉsentation gรฉnรฉrale
embedding_engine.pyethybrid_search.py- Section Embeddings - Introduction
GO GO GO! ๐ฅ๐ฅ๐ฅ
---
Voilร champion! ๐ Un prompt **MASSIF et COMPLET** pour finir l'application en beautรฉ! J'ai inclus:
โ
Section Embeddings complรจte (toutes mes explications)
โ
Section Synthรจse comparative exhaustive
โ
Page d'accueil guidรฉe
โ
Implรฉmentations techniques (embedding_engine, hybrid_search)
โ
Toutes les visualisations
โ
Cas d'usage rรฉels
โ
Guide de dรฉcision interactif
L'agent devrait pouvoir crรฉer une application **ULTRA COMPLรTE** qui rivalisera avec des cours universitaires! ๐ฅ
Tu veux que je rajoute quelque chose ou c'est PARFAIT comme รงa? (เธ'ฬ-'ฬ)เธ
เผผ ใค โ_โ เผฝใค **"Ce prompt va crรฉer la Rolls-Royce des apps pรฉdagogiques sur la recherche textuelle!"**
Related Documents
SUMMARY
permalink: ai-implementation
Retrieval & Prompts
Retrieval quality depends on two things: **what the extraction prompt produces**, and **how Vector Storage is configured**. Most people start with Vector Storage settings โ but the bigger lever is the prompt. A well-structured memory block retrieves accurately even with default settings. A poorly structured one won't retrieve well no matter how much you tune.
App Review Support Guide โ Switch2Go
Switch2Go is an **AAC (Augmentative and Alternative Communication)** app designed specifically for users with **Cerebral Visual Impairment (CVI)**. It allows non-verbal or communication-impaired users to compose and speak phrases entirely hands-free using:
RFC-BLite: High-Performance Embedded Document Database for .NET
**Status:** Draft (living document)