bourdoiscatie commited on
Commit
8177aa9
·
verified ·
1 Parent(s): 0452c49

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -30
app.py CHANGED
@@ -8,8 +8,13 @@ import faiss
8
  from usearch.index import Index
9
 
10
  # Load titles and texts
11
- title_text_dataset = load_dataset("bourdoiscatie/wikipedia_fr_2022_250K", split="train", num_proc=4).select_columns(["title", "text"])
12
 
 
 
 
 
 
13
  # Load the int8 and binary indices. Int8 is loaded as a view to save memory, as we never actually perform search with it.
14
  int8_view = Index.restore("wikipedia_fr_2022_250K_int8_usearch.index", view=True)
15
  binary_index: faiss.IndexBinaryFlat = faiss.read_index_binary("wikipedia_fr_2022_250K_ubinary_faiss.index")
@@ -52,8 +57,15 @@ def search(query, top_k: int = 20, rescore_multiplier: int = 1, use_approx: bool
52
  indices = scores.argsort()[::-1][:top_k]
53
  top_k_indices = binary_ids[indices]
54
  top_k_scores = scores[indices]
55
- top_k_titles, top_k_texts = zip(*[(title_text_dataset[idx]["title"], title_text_dataset[idx]["text"]) for idx in top_k_indices.tolist()])
56
- df = pd.DataFrame({"Score": [round(value, 2) for value in top_k_scores], "Titre": top_k_titles, "Texte": top_k_texts})
 
 
 
 
 
 
 
57
  sort_time = time.time() - start_time
58
 
59
  return df, {
@@ -67,24 +79,34 @@ def search(query, top_k: int = 20, rescore_multiplier: int = 1, use_approx: bool
67
  }
68
 
69
 
70
- with gr.Blocks(title="Requêter Wikipedia en temps réel") as demo:
71
  gr.Markdown(
72
  """
73
- ## Requêter Wikipedia en temps réel
74
-
75
- Effectuer une requête dans un corpus composé de 250K paragraphes tirés d'articles de Wikipédia.
76
- Les résultats sont renvoyés en temps réel via une architecture tournant sur un CPU 🚀
77
-
78
-
79
- <details><summary>Détails du processus</summary>
 
 
 
 
 
 
 
 
 
 
80
 
81
- Détails :
82
- 1. La requête est enchâssée en float32 à l'aide du modèle [`OrdalieTech/Solon-embeddings-large-0.1`](https://hf.co/OrdalieTech/Solon-embeddings-large-0.1).
83
  2. La requête est quantizée en binaire à l'aide de la fonction `quantize_embeddings` de la bibliothèque [SentenceTransformers](https://sbert.net/).
84
- 3. Un index binaire (XXM d'enchâssements binaires pesant XXGB de mémoire/espace disque) est requêté (en binaire si l'option approximatie sélectionnée, en int8 si l'option exacte est sélectionnée).
85
- 4. Les *n* textes demandés par l'utilisateur jugés les plus pertinents sont chargés à la volée à partir d'un index int8 sur disque (XXM d'enchâssements int8 ; 0 bytes de mémoire, XXGB d'espace disque).
86
  5. Les *n* textes sont rescorés en utilisant la requête en float32 et les enchâssements en int8.
87
- 6. Les *n* premiers textes sont triés par score et affichés.
88
 
89
  Ce processus est conçu pour être rapide et efficace en termes de mémoire : l'index binaire étant suffisamment petit pour tenir dans la mémoire et l'index int8 étant chargé en tant que vue pour économiser de la mémoire.
90
  Au total, ce processus nécessite de conserver 1) le modèle en mémoire, 2) l'index binaire en mémoire et 3) l'index int8 sur le disque.
@@ -93,30 +115,29 @@ Avec une dimension de 1024, nous avons besoin de `1024 / 8 * num_docs` octets po
93
  C'est nettement moins cher que de faire le même processus avec des enchâssements en float32 qui nécessiterait `4 * 1024 * num_docs` octets de mémoire/espace disque pour l'index float32, soit 32x plus de mémoire et 4x plus d'espace disque.
94
  De plus, l'index binaire est beaucoup plus rapide (jusqu'à 32x) à rechercher que l'index float32, tandis que le rescorage est également extrêmement efficace.
95
  En conclusion, ce processus permet une recherche rapide, évolutive, peu coûteuse et efficace en termes de mémoire.
96
-
97
  </details>
98
  """
99
  )
100
  with gr.Row():
101
  with gr.Column(scale=75):
102
  query = gr.Textbox(
103
- label="Recherche d'articles dans le Wikipédia francophone",
104
  placeholder="Saisissez une requête pour rechercher des textes pertinents dans Wikipédia.",
105
  )
106
  with gr.Column(scale=25):
107
  use_approx = gr.Radio(
108
- choices=[("Recherche exacte", False), ("Recherche approximative", True)],
109
  value=True,
110
- label="Index de recherche",
111
  )
112
 
113
  with gr.Row():
114
  with gr.Column(scale=2):
115
  top_k = gr.Slider(
116
- minimum=10,
117
- maximum=200,
118
- step=5,
119
- value=20,
120
  label="Nombre de documents à rechercher",
121
  info="Recherche effectué via un bi-encodeur binaire",
122
  )
@@ -127,16 +148,13 @@ En conclusion, ce processus permet une recherche rapide, évolutive, peu coûteu
127
  step=1,
128
  value=1,
129
  label="Coefficient de rescorage",
130
- info="Reranking via le coefficient`",
131
  )
132
 
133
  search_button = gr.Button(value="Search")
134
 
135
- with gr.Row():
136
- with gr.Column(scale=4):
137
- output = gr.Dataframe(headers=["Score", "Titre", "Texte"])
138
- with gr.Column(scale=1):
139
- json = gr.JSON()
140
 
141
  query.submit(search, inputs=[query, top_k, rescore_multiplier, use_approx], outputs=[output, json])
142
  search_button.click(search, inputs=[query, top_k, rescore_multiplier, use_approx], outputs=[output, json])
 
8
  from usearch.index import Index
9
 
10
  # Load titles and texts
11
+ wikipedia_dataset = load_dataset("bourdoiscatie/wikipedia_fr_2022_250K", split="train", num_proc=4).select_columns(["title", "text", "wiki_id"])
12
 
13
+ def add_link(example):
14
+ example["title"] = '['+example["title"]+']('+'https://fr.wikipedia.org/wiki?curid='+str(example["wiki_id"])+')'
15
+ return example
16
+ wikipedia_dataset = wikipedia_dataset.map(add_link)
17
+
18
  # Load the int8 and binary indices. Int8 is loaded as a view to save memory, as we never actually perform search with it.
19
  int8_view = Index.restore("wikipedia_fr_2022_250K_int8_usearch.index", view=True)
20
  binary_index: faiss.IndexBinaryFlat = faiss.read_index_binary("wikipedia_fr_2022_250K_ubinary_faiss.index")
 
57
  indices = scores.argsort()[::-1][:top_k]
58
  top_k_indices = binary_ids[indices]
59
  top_k_scores = scores[indices]
60
+ top_k_titles, top_k_texts = zip(*[(wikipedia_dataset[idx]["title"], wikipedia_dataset[idx]["text"]) for idx in top_k_indices.tolist()])
61
+ df = pd.DataFrame({"Score_ind": [round(value, 2) for value in top_k_scores], "Titre": top_k_titles, "Texte": top_k_texts})
62
+ score_sum = df.groupby('Titre')['Score_ind'].sum().reset_index()
63
+ df = pd.merge(df, score_sum, on='Titre', how='left')
64
+ df.rename(columns={'Score_ind_y': 'Score_sum'}, inplace=True)
65
+ df.rename(columns={'Score_ind_x': 'Score_ind'}, inplace=True)
66
+ df = df[["Score_sum", "Score_ind", "Titre", "Texte"]]
67
+ df = df.sort_values('Score_sum', ascending=False)
68
+ # df = df.groupby('Titre')[['Score', 'Texte']].agg({'Score': 'sum', 'Texte': '\n\n'.join}).reset_index().sort_values('Score', ascending=False)
69
  sort_time = time.time() - start_time
70
 
71
  return df, {
 
79
  }
80
 
81
 
82
+ with gr.Blocks(title="Requêter Wikipedia en temps réel 🔍") as demo:
83
  gr.Markdown(
84
  """
85
+ ## Requêter Wikipedia en temps réel 🔍
86
+
87
+ Ce démonstrateur permet de requêter un corpus composé des 250K paragraphes les plus consultés du Wikipédia francophone.
88
+ Les résultats sont renvoyés en temps réel via un pipeline tournant sur un CPU 🚀
89
+ Nous nous sommes grandement inspirés du Space [quantized-retrieval](https://huggingface.co/spaces/sentence-transformers/quantized-retrieval) conçu par [Tom Aarsen](https://huggingface.co/tomaarsen) 🤗.
90
+ Si vous voulez en savoir plus sur le processus complet derrière ce démonstrateur, n'hésitez pas à déplier les liens ci-dessous.
91
+
92
+ <details><summary>1. Détails sur les données</summary>
93
+ Le corpus utilisé correspond au 250 000 premières lignes du jeu de données [wikipedia-22-12-fr-embeddings](https://huggingface.co/datasets/Cohere/wikipedia-22-12-fr-embeddings) mis en ligne par Cohere.
94
+ Comme son nom l'indique il s'agit d'un jeu de données datant de décembre 2022. Cette information est à prendre en compte lorsque vous effectuez votre requête.
95
+ De même il s'agit ici d'un sous-ensemble du jeu de données total, à savoir les 250 000 paragraphes les plus consultés à cette date-là.
96
+ Ainsi, si vous effectuez une recherche pointue sur un sujet peu consulté, ce démonstrateur ne reverra probablement rien de pertinent.
97
+ A noter également que Cohere a effectué un prétraitement sur les données ce qui a conduit à la suppression de dates par exemple.
98
+ Ce jeu de données n'est donc pas optimal. L'idée était de pouvoir proposer quelque chose en peu de temps.
99
+ Dans un deuxième temps, ce démonstrateur sera étendu à l'ensemble du jeu de données *wikipedia-22-12-fr-embeddings* (soit 13M de paragraphes).
100
+ Il n'est pas exclus d'ensuite utiliser une version plus récente de Wikipedia (on peut penser par exemple à [wikimedia/wikipedia](https://huggingface.co/datasets/wikimedia/wikipedia) de Wikimedia) mais qui demandera d'effectuer plusieurs nettoyages.
101
+ </details>
102
 
103
+ <details><summary>2. Détails le pipeline</summary>
104
+ 1. La requête est enchâssée en float32 à l'aide du modèle [`OrdalieTech/Solon-embeddings-large-0.1`](https://hf.co/OrdalieTech/Solon-embeddings-large-0.1) d'Ordalie.
105
  2. La requête est quantizée en binaire à l'aide de la fonction `quantize_embeddings` de la bibliothèque [SentenceTransformers](https://sbert.net/).
106
+ 3. Un index binaire (250K *embeddings* binaires pesant 32MB de mémoire/espace disque) est requêté (en binaire si l'option approximative est sélectionnée, en int8 si l'option exacte est sélectionnée).
107
+ 4. Les *n* textes demandés par l'utilisateur jugés les plus pertinents sont chargés à la volée à partir d'un index int8 sur disque (250K *embeddings* int8 ; 0 bytes de mémoire, 293MB d'espace disque).
108
  5. Les *n* textes sont rescorés en utilisant la requête en float32 et les enchâssements en int8.
109
+ 6. Les *n* premiers textes sont triés par score et affichés. Le "Score_ind" correspond au score individuel de chaque paragraphe d'être pertinant vis-à-vis de la requête. Le "Score_sum" correspond à la somme de tous les scores individuels des paragraphes issus d'un même article Wikipedia. L'objectif est alors de mettre en avant l'article source plutôt qu'un bout de texte le composant.
110
 
111
  Ce processus est conçu pour être rapide et efficace en termes de mémoire : l'index binaire étant suffisamment petit pour tenir dans la mémoire et l'index int8 étant chargé en tant que vue pour économiser de la mémoire.
112
  Au total, ce processus nécessite de conserver 1) le modèle en mémoire, 2) l'index binaire en mémoire et 3) l'index int8 sur le disque.
 
115
  C'est nettement moins cher que de faire le même processus avec des enchâssements en float32 qui nécessiterait `4 * 1024 * num_docs` octets de mémoire/espace disque pour l'index float32, soit 32x plus de mémoire et 4x plus d'espace disque.
116
  De plus, l'index binaire est beaucoup plus rapide (jusqu'à 32x) à rechercher que l'index float32, tandis que le rescorage est également extrêmement efficace.
117
  En conclusion, ce processus permet une recherche rapide, évolutive, peu coûteuse et efficace en termes de mémoire.
 
118
  </details>
119
  """
120
  )
121
  with gr.Row():
122
  with gr.Column(scale=75):
123
  query = gr.Textbox(
124
+ label="Requêter le Wikipédia francophone",
125
  placeholder="Saisissez une requête pour rechercher des textes pertinents dans Wikipédia.",
126
  )
127
  with gr.Column(scale=25):
128
  use_approx = gr.Radio(
129
+ choices=[("Exacte", False), ("Approximative", True)],
130
  value=True,
131
+ label="Type de recherche",
132
  )
133
 
134
  with gr.Row():
135
  with gr.Column(scale=2):
136
  top_k = gr.Slider(
137
+ minimum=3,
138
+ maximum=40,
139
+ step=1,
140
+ value=15,
141
  label="Nombre de documents à rechercher",
142
  info="Recherche effectué via un bi-encodeur binaire",
143
  )
 
148
  step=1,
149
  value=1,
150
  label="Coefficient de rescorage",
151
+ info="Reranking via le coefficient",
152
  )
153
 
154
  search_button = gr.Button(value="Search")
155
 
156
+ output = gr.Dataframe(headers=["Score_sum", "Score_ind", "Titre", "Texte"], datatype="markdown")
157
+ json = gr.JSON()
 
 
 
158
 
159
  query.submit(search, inputs=[query, top_k, rescore_multiplier, use_approx], outputs=[output, json])
160
  search_button.click(search, inputs=[query, top_k, rescore_multiplier, use_approx], outputs=[output, json])