Skip to content

Commit 9b8e4c7

Browse files
authored
Merge pull request #12 from InseeFrLab/maj-25
Maj 25
2 parents b364353 + ffb55d1 commit 9b8e4c7

File tree

3 files changed

+90
-35
lines changed

3 files changed

+90
-35
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ jobs:
1212
uses: actions/checkout@v4
1313
- name: Install system dependencies
1414
run: |
15-
sudo apt-get install libudunits2-dev \
15+
sudo apt-get update
16+
sudo apt-get install -y --fix-missing \
17+
libudunits2-dev \
1618
libgdal-dev \
1719
libgeos-dev \
1820
libproj-dev
@@ -24,7 +26,7 @@ jobs:
2426
- name: Install python
2527
uses: actions/setup-python@v5
2628
with:
27-
python-version: '3.10'
29+
python-version: '3.13'
2830
cache: 'pip' # caching pip dependencies
2931

3032
- name: Install python dependencies

applications/ape.qmd

Lines changed: 80 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ filters:
77
format:
88
html:
99
df-print: paged
10+
execute:
11+
daemon: false
1012
---
1113

1214
Cette 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
5255
import os
5356
import pandas as pd
@@ -173,6 +176,7 @@ Dans une démarche exploratoire, le plus simple est de commencer par compter les
173176
Par 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
176180
train_data=train.copy()
177181
178182
def 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
190195
filter_train_data(train, "data science").head(5)
191196
```
192197
```{python}
198+
#| label: use-filter_train_data2
193199
#| echo: true
194200
filter_train_data(train, "boulanger").head(5)
195201
```
196202

197203
```{python}
198-
204+
#| label: def-graph_wordcloud
199205
import matplotlib.pyplot as plt
200206
from wordcloud import WordCloud
201207
@@ -220,6 +226,7 @@ Les _wordclouds_ peuvent servir à rapidement visualiser la structure d'un corpu
220226
On 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
223230
wordcloud_corpus = graph_wordcloud(train.sample(10000))
224231
plt.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
229236
proches de celles que nous allons mettre en oeuvre dans notre algorithme de classification.
230237

231238
```{python}
239+
#| label: use-graph_wordcloud2
232240
wordcloud_corpus = graph_wordcloud(train, naf = "1071C")
233241
plt.imshow(wordcloud_corpus, interpolation="bilinear")
234242
```
235243

236244
```{python}
245+
#| label: use-graph_wordcloud3
237246
wordcloud_corpus = graph_wordcloud(train, naf = "4942Z")
238247
plt.imshow(wordcloud_corpus, interpolation="bilinear")
239248
```
@@ -243,6 +252,7 @@ La première étape classique est de retirer les _stop words_ et éventuellement
243252
Par 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
246256
from nltk.tokenize import word_tokenize
247257
import spacy
248258
@@ -271,6 +281,7 @@ train['text_clean'] = (train['text']
271281
Voici 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
274285
wordcloud_corpus_cleaned = graph_wordcloud(train.sample(10000), "text_clean")
275286
plt.imshow(wordcloud_corpus_cleaned, interpolation="bilinear")
276287
```
@@ -288,6 +299,7 @@ Pour cela, il suffit de charger le module `processor.py` mis à disposition dans
288299
Le code de nettoyage est directement fourni:
289300

290301
```{python}
302+
#| label: data-processessing
291303
#| echo: true
292304
293305
from processor import Preprocessor
@@ -308,6 +320,7 @@ Pour développer votre code, utilisez un échantillon des données pour éviter
308320
Récupérons les features et les labels.
309321

310322
```{python}
323+
#| label: get-features
311324
#| echo: true
312325
313326
df = 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
437464
classifier = 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
454482
from sklearn.preprocessing import LabelEncoder
455483
le = LabelEncoder()
456484
y_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%)
462492
from 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

561577
Evaluation 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
567609
predictions = 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()
571617
print(f"Test accuracy: {accuracy:.3f}")
572618
573619
```
574620

575621
Testons le modèle sur quelques libellés d'activité :
576622

577623
```{python}
624+
#| label: test-inference
578625
import numpy as np
579626
580627
searched_professions = np.array(["Conseil datascience", "Concésion dans l'automobile", "Concession automobile", "peintre"])

download_nlp_reqs.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ mkdir applications/data
1111

1212
curl https://minio.lab.sspcloud.fr/projet-formation/diffusion/mlops/data/firm_activity_data.parquet --output applications/data/data.parquet
1313
curl https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/data/naf2008_liste_n5.xls --output applications/data/naf.parquet
14+
15+
mkdir applications/model_ape
16+
17+
curl https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/metadata.pkl --output applications/model_ape/metadata.pkl
18+
curl https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/model_checkpoint.ckpt --output applications/model_ape/model_checkpoint.ckpt
19+
curl https://minio.lab.sspcloud.fr/projet-formation/nouvelles-sources/model_ape/tokenizer.pkl --output applications/model_ape/tokenizer.pkl

0 commit comments

Comments
 (0)