77format :
88 html :
99 df-print : paged
10+ execute :
11+ daemon : false
1012---
1113
1214Cette application illustrera certains apports des outils du
@@ -48,6 +50,7 @@ Le code pour lire les données est directement fourni :
4850
4951``` {python}
5052#| echo: true
53+ #| label: download-data
5154
5255import os
5356import pandas as pd
@@ -173,6 +176,7 @@ Dans une démarche exploratoire, le plus simple est de commencer par compter les
173176Par exemple, de manière naturelle, nous avons beaucoup plus de déclarations liées à la boulangerie que liées à la _ data science_ :
174177
175178``` {python}
179+ #| label: filter_train_data
176180train_data=train.copy()
177181
178182def filter_train_data(train_data, sequence):
@@ -186,16 +190,18 @@ def filter_train_data(train_data, sequence):
186190```
187191
188192``` {python}
193+ #| label: use-filter_train_data
189194#| echo: true
190195filter_train_data(train, "data science").head(5)
191196```
192197``` {python}
198+ #| label: use-filter_train_data2
193199#| echo: true
194200filter_train_data(train, "boulanger").head(5)
195201```
196202
197203``` {python}
198-
204+ #| label: def-graph_wordcloud
199205import matplotlib.pyplot as plt
200206from wordcloud import WordCloud
201207
@@ -220,6 +226,7 @@ Les _wordclouds_ peuvent servir à rapidement visualiser la structure d'un corpu
220226On voit ici que notre corpus est très bruité car nous n'avons pas nettoyé celui-ci:
221227
222228``` {python}
229+ #| label: use-graph_wordcloud
223230wordcloud_corpus = graph_wordcloud(train.sample(10000))
224231plt.imshow(wordcloud_corpus, interpolation="bilinear")
225232```
@@ -229,11 +236,13 @@ Arrivez-vous à inférer la catégorie de la NAF en question ? Si oui, vous util
229236proches de celles que nous allons mettre en oeuvre dans notre algorithme de classification.
230237
231238``` {python}
239+ #| label: use-graph_wordcloud2
232240wordcloud_corpus = graph_wordcloud(train, naf = "1071C")
233241plt.imshow(wordcloud_corpus, interpolation="bilinear")
234242```
235243
236244``` {python}
245+ #| label: use-graph_wordcloud3
237246wordcloud_corpus = graph_wordcloud(train, naf = "4942Z")
238247plt.imshow(wordcloud_corpus, interpolation="bilinear")
239248```
@@ -243,6 +252,7 @@ La première étape classique est de retirer les _stop words_ et éventuellement
243252Par exemple, pour des données de caisse, on retirera les bruits, les abréviations, etc. qui peuvent bruiter notre corpus.
244253
245254``` {python}
255+ #| label: data-cleaning
246256from nltk.tokenize import word_tokenize
247257import spacy
248258
@@ -271,6 +281,7 @@ train['text_clean'] = (train['text']
271281Voici le wordcloud de notre corpus tout entier une fois cette première étape de nettoyage achevée :
272282
273283``` {python}
284+ #| label: use-graph_wordcloud4
274285wordcloud_corpus_cleaned = graph_wordcloud(train.sample(10000), "text_clean")
275286plt.imshow(wordcloud_corpus_cleaned, interpolation="bilinear")
276287```
@@ -288,6 +299,7 @@ Pour cela, il suffit de charger le module `processor.py` mis à disposition dans
288299Le code de nettoyage est directement fourni:
289300
290301``` {python}
302+ #| label: data-processessing
291303#| echo: true
292304
293305from processor import Preprocessor
@@ -308,6 +320,7 @@ Pour développer votre code, utilisez un échantillon des données pour éviter
308320Récupérons les features et les labels.
309321
310322``` {python}
323+ #| label: get-features
311324#| echo: true
312325
313326df = df.dropna(subset = [Y, TEXT_FEATURE])
@@ -429,9 +442,23 @@ training_config = TrainingConfig(
429442# )
430443
431444# Download a pre-trained instead to make it faster :
432- os.system("curl -I https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/metadata.pkl")
433- os.system("curl -I https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/model_checkpoint.ckpt")
434- os.system("curl -I https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/tokenizer.pkl")
445+
446+ # Download the model
447+ base_url = "https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape"
448+ files = ["metadata.pkl", "model_checkpoint.ckpt", "tokenizer.pkl"]
449+
450+ import subprocess
451+
452+ for file in files:
453+ subprocess.run([
454+ "curl",
455+ f"{base_url}/{file}",
456+ "--output", f"model_ape/{file}",
457+ "--silent",
458+ "--fail", # Fail on HTTP errors
459+ "--location" # Follow redirects
460+ ], check=True)
461+
435462
436463# Load it
437464classifier = torchTextClassifiers.load("model_ape")
@@ -451,13 +478,16 @@ searched_professions = np.array(["Conseil datascience", "Concésion dans l'autom
451478
452479
453480``` {python}
481+ #| label: label-encoding
454482from sklearn.preprocessing import LabelEncoder
455483le = LabelEncoder()
456484y_encoded = le.fit_transform(y) # Convertit ["cat", "dog"] → [0, 1]
457485```
458486
459487
460488``` {python}
489+ #| label: split-data
490+
461491# Première division : train (80 %) + test (20%)
462492from sklearn.model_selection import train_test_split
463493
@@ -481,21 +511,21 @@ X_train, X_val, y_train, y_val = train_test_split(
481511
482512
483513``` {python}
514+ #| label: useless-chunk
515+
484516# from torchTextClassifiers.tokenizers.ngram import NGramTokenizer
485517
486518# tokenizer = NGramTokenizer(
487519# min_count=2, # On considère un mot s'il est trouvé au moins 2 fois dans le corpus
488520# min_n=2,
489521# max_n=4, # On fait des 2grams, 3grams et 4grams de caractères
490522# len_word_ngrams=2, # On fait des 2grams de mots
491- # num_tokens=10000, # Nombre max de tokens considérés
492- # training_text=X,
523+ # num_tokens=10000, # Nombre max de tokens considérés dans le vocable
524+ # training_text=X, # Jeu d'entraînement du tokenizer
493525# )
494- ```
495526
496- Configuration du modèle :
527+ # # Set model configs ---------------
497528
498- ``` {python}
499529# from torchTextClassifiers import ModelConfig
500530# import numpy as np
501531
@@ -510,20 +540,17 @@ Configuration du modèle :
510540# embedding_dim=embedding_dim,
511541# num_classes=num_unique
512542# )
513- ```
514543
544+ # # Instanciate a ttc model (nammed "classifier") ---------------
515545
516- ``` {python}
517546# from torchTextClassifiers import torchTextClassifiers
518547
519548# classifier = torchTextClassifiers(
520549# tokenizer=tokenizer,
521550# model_config=model_config
522551# )
523- ```
524-
525552
526- ``` {python}
553+ # # Set the training configs ---------------
527554
528555# from torchTextClassifiers import TrainingConfig
529556
@@ -534,47 +561,67 @@ Configuration du modèle :
534561# lr=1e-3,
535562# patience_early_stopping=7,
536563# num_workers=0,
537- # trainer_params={'deterministic': True},
538- # save_path="model_ape"
564+ # trainer_params={'deterministic': True}
539565# )
540566
541567```
542568
543- Téléchargement du modèle :
569+ Chargement du modèle :
544570
545571``` {python}
546- #| warning: false
547- #| message: false
548-
549- # Download the model
550- # In terminal :
551- os.makedirs("model_ape/", exist_ok=True)
552- os.system("curl https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/metadata.pkl --output model_ape/metadata.pkl --silent")
553- os.system("curl https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/model_checkpoint.ckpt --output model_ape/model_checkpoint.ckpt --silent")
554- os.system("curl https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/tokenizer.pkl --output model_ape/tokenizer.pkl --silent")
555-
556- # Load it
557- from torchTextClassifiers import torchTextClassifiers
558- classifier = torchTextClassifiers.load("model_ape")
572+ #| label: load-model
573+ from torchTextClassifiers import torchTextClassifiers as ttc
574+ classifier = ttc.load("model_ape")
559575```
560576
561577Evaluation du modèle :
562578
563- ``` {python}
579+ ``` {python }
580+ #| label: get-accuracy
581+
582+ # Force single-threaded execution (for gh actions)
583+ import torch.multiprocessing as mp
584+ mp.set_start_method("spawn", force=True)
585+ import torch
586+ import os
587+ torch.set_num_threads(1)
588+ torch.set_num_interop_threads(1)
589+ os.environ["OMP_NUM_THREADS"] = "1"
590+ os.environ["MKL_NUM_THREADS"] = "1"
591+
592+ import numpy as np
593+ n = X_test.shape[0]
594+ sample_size = min(1000, n)
595+
596+ rng = np.random.default_rng(seed=42)
597+ idx = rng.choice(n, size=sample_size, replace=False)
598+
599+ X_sample = X_test[idx]
600+ y_sample = y_test[idx]
564601
565602# Inference on testset
566- result = classifier.predict(X_test)
603+ result = classifier.predict(X_sample)
604+
605+ ```
606+
607+ ``` {python }
608+ #| label: get-accuracy2
567609predictions = result["prediction"].squeeze().numpy()
610+ ```
568611
612+ ``` {python }
613+ #| label: get-accuracy3
614+
569615# Step 8: Evaluate
570- accuracy = (predictions == y_test ).mean()
616+ accuracy = (predictions == y_sample ).mean()
571617print(f"Test accuracy: {accuracy:.3f}")
572618
573619```
574620
575621Testons le modèle sur quelques libellés d'activité :
576622
577623``` {python}
624+ #| label: test-inference
578625import numpy as np
579626
580627searched_professions = np.array(["Conseil datascience", "Concésion dans l'automobile", "Concession automobile", "peintre"])
0 commit comments