fioriclass commited on
Commit
bf5fb5f
·
1 Parent(s): b002f5a

initialisation

Browse files
.gitattributes CHANGED
@@ -25,7 +25,6 @@
25
  *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
  *.tflite filter=lfs diff=lfs merge=lfs -text
30
  *.tgz filter=lfs diff=lfs merge=lfs -text
31
  *.wasm filter=lfs diff=lfs merge=lfs -text
@@ -33,3 +32,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
25
  *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
 
28
  *.tflite filter=lfs diff=lfs merge=lfs -text
29
  *.tgz filter=lfs diff=lfs merge=lfs -text
30
  *.wasm filter=lfs diff=lfs merge=lfs -text
 
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ aim_repo.tar.gz filter=lfs diff=lfs merge=lfs -text
36
+ *.csv filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
175
+
176
+ chunks/
177
+
178
+ check_ins
179
+
180
+ meta/
181
+
182
+ seqs/
183
+ locks/
Dockerfile ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04 AS runtime
2
+
3
+ # Désactiver l'interactivité
4
+ ENV DEBIAN_FRONTEND=noninteractive
5
+
6
+ # Installer les outils de base nécessaires
7
+ RUN apt-get update && apt-get install -y \
8
+ bash \
9
+ curl \
10
+ git \
11
+ git-lfs \
12
+ wget \
13
+ procps \
14
+ build-essential \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ ENV XDG_BIN_HOME=/usr/local/bin
18
+
19
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
20
+
21
+ # Créer l'utilisateur non privilégié avec UID 1000 et préparer le répertoire de l'application
22
+ RUN useradd -m -u 1000 aim_user && \
23
+ mkdir -p /app && \
24
+ chown -R 1000:1000 /app
25
+
26
+ RUN echo "alias venv='source .venv/bin/activate'" >> /etc/bash.bashrc
27
+
28
+ WORKDIR /app
29
+
30
+ USER 1000
31
+
32
+ COPY --chown=1000:1000 requirements.txt /app/
33
+
34
+ # Initialiser UV, créer l'environnement virtuel, installer les dépendances et purger le cache pip
35
+ RUN uv init --python 3.11 --no-managed-python --no-workspace && \
36
+ uv venv && \
37
+ . /app/.venv/bin/activate && \
38
+ uv add -r requirements.txt && \
39
+ uv run --with pip --with spacy -- spacy download en_core_web_sm && \
40
+ uv cache prune
41
+
42
+ # Copier le reste du code source dans /app
43
+ COPY --chown=1000:1000 src/ /app/
44
+
45
+ ENV PATH="/app/.venv/bin:${PATH}"
46
+
47
+ CMD ["mlflow", "ui", "--backend-store-uri", "file:///data/mlruns", "--host", "0.0.0.0", "--port", "7860"]
requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ hydra-core
2
+ omegaconf
3
+ mlflow
4
+ pydantic
5
+ cupy
6
+ cudf
7
+ scikit-learn
8
+ scipy
9
+ numpy
10
+ pandas
11
+ optuna
12
+ ray[tune]
13
+ transformers
14
+ torch
15
+ datasets
16
+ seaborn
17
+ matplotlib
18
+ cupy-cuda12x
19
+ cuml-cu12==25.2.*
20
+ cudf-cu12==25.2.*
21
+ torchvision
22
+ accelerate
23
+ transformers
src/base_trainer.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =========================
2
+ # Fichier: base_trainer.py
3
+ # =========================
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Union, Optional
7
+ import cupy as cp
8
+ from scipy.sparse import csr_matrix
9
+
10
+ from config import Config
11
+ from interfaces.metrics_calculator import MetricsCalculator
12
+
13
+
14
+ class BaseTrainer(ABC):
15
+ """
16
+ Classe de base abstraite représentant un entraîneur (trainer) générique,
17
+ tel que défini dans le diagramme UML.
18
+
19
+ Attributs:
20
+ config (Config): Configuration globale du système (modèle, data, etc.).
21
+ classifier (object): Référence vers le classifieur ou le modèle entraîné.
22
+ metrics_calculator (MetricsCalculator): Outil de calcul et de logging des métriques.
23
+ data_path (str): Chemin vers les données.
24
+ target_column (str): Nom de la colonne cible dans les données.
25
+ """
26
+
27
+ def __init__(self, config: Config, data_path: str,
28
+ target_column: str) -> None:
29
+ """
30
+ Initialise un trainer générique avec la configuration et les informations de chemin de données.
31
+
32
+ :param config: Objet de configuration global.
33
+ :param data_path: Chemin vers le fichier de données.
34
+ :param target_column: Nom de la colonne cible pour l'entraînement/prédiction.
35
+ """
36
+ self.config: Config = config
37
+ self.data_path: str = data_path
38
+ self.target_column: str = target_column
39
+ self.classifier: object = None
40
+ self.metrics_calculator: MetricsCalculator = None
41
+
42
+ @abstractmethod
43
+ def build_components(self) -> None:
44
+ """
45
+ Méthode abstraite. Instancie les composants nécessaires
46
+ (e.g. le classifieur, éventuellement le vectorizer) selon la config.
47
+ """
48
+ pass
49
+
50
+ @abstractmethod
51
+ def train(self) -> None:
52
+ """
53
+ Méthode abstraite. Lance la procédure d'entraînement.
54
+ """
55
+ pass
56
+
57
+ @abstractmethod
58
+ def evaluate(self) -> None:
59
+ """
60
+ Méthode abstraite. Évalue le modèle entraîné, par exemple
61
+ sur un jeu de validation ou de test, et calcule les métriques.
62
+ """
63
+ pass
64
+
65
+ def optimize_if_needed(self) -> None:
66
+ """
67
+ Vérifie la configuration pour déterminer si l'optimisation des hyperparamètres
68
+ est nécessaire. Si oui, instancie l'optimiseur approprié et lance
69
+ le processus d'optimisation. Met ensuite à jour la configuration du modèle
70
+ avec les meilleurs paramètres trouvés et reconstruit les composants.
71
+ """
72
+ import logging
73
+ logger = logging.getLogger(__name__)
74
+
75
+ # Vérifier si l'optimisation est configurée
76
+ if (self.config.hyperparameters.optimizer and
77
+ self.config.hyperparameters.param_grid and
78
+ self.config.hyperparameters.n_trials > 0):
79
+
80
+ logger.info("Démarrage de l'optimisation des hyperparamètres")
81
+
82
+ # Importation et instanciation de l'optimiseur
83
+ optimizer_type = self.config.hyperparameters.optimizer.lower()
84
+ if optimizer_type == "optuna":
85
+ from optimizers.optuna_optimizer import OptunaOptimizer
86
+ optimizer = OptunaOptimizer()
87
+ elif optimizer_type == "raytune":
88
+ from optimizers.ray_tune_optimizer import RayTuneOptimizer
89
+ optimizer = RayTuneOptimizer()
90
+ else:
91
+ raise ValueError(f"Type d'optimizer non supporté: {optimizer_type}")
92
+
93
+ # Lancement de l'optimisation
94
+ best_params = optimizer.optimize(
95
+ trainer=self, # Passe l'instance actuelle du trainer
96
+ param_grid=self.config.hyperparameters.param_grid
97
+ )
98
+
99
+ logger.info(f"Meilleurs hyperparamètres trouvés: {best_params}")
100
+
101
+ # Mise à jour de la configuration du modèle avec les meilleurs paramètres
102
+ self.config.model.params.update(best_params)
103
+
104
+ # Reconstruire les composants avec les nouveaux paramètres
105
+ logger.info("Reconstruction des composants avec les hyperparamètres optimisés.")
106
+ self.build_components()
107
+ else:
108
+ logger.info("Aucune optimisation des hyperparamètres configurée.")
109
+
110
+ def log_parameters_to_mlflow(self) -> None:
111
+ """
112
+ Appelle une fonction singledispatch (get_relevant_params_for_logging(trainer)) pour récupérer
113
+ les paramètres pertinents et les logguer, par exemple via MLflow.
114
+ Implementé ici en tant que méthode non-abstraite, mais la logique de logging
115
+ devrait être assurée dans l'environnement MLflow approprié.
116
+ """
117
+ pass
118
+
119
+ def _prepare_input_for_fit(
120
+ self, X: Union[cp.ndarray,
121
+ csr_matrix]) -> Union[cp.ndarray, csr_matrix]:
122
+ """
123
+ Méthode utilitaire pour préparer les données d'entraînement avant
124
+ l'ajustement du modèle.
125
+
126
+ :param X: Matrice (cupy.ndarray ou scipy.sparse.csr_matrix) représentant les données.
127
+ :return: Matrice transformée ou identique, prête pour l'entraînement.
128
+ """
129
+ return X
130
+
131
+ def _prepare_input_for_predict(
132
+ self, X: Union[cp.ndarray,
133
+ csr_matrix]) -> Union[cp.ndarray, csr_matrix]:
134
+ """
135
+ Méthode utilitaire pour préparer les données de prédiction avant
136
+ l'appel à la méthode `predict` du modèle.
137
+
138
+ :param X: Matrice (cupy.ndarray ou scipy.sparse.csr_matrix) représentant les données.
139
+ :return: Matrice transformée ou identique, prête pour la prédiction.
140
+ """
141
+ return X
142
+
143
+ def _get_binary_predictions(self, X: cp.ndarray) -> cp.ndarray:
144
+ """
145
+ Retourne un vecteur de prédictions binaires (0/1).
146
+
147
+ :param X: Matrice de données de dimension (n_samples, n_features),
148
+ déjà sous forme cupy.ndarray.
149
+ :return: Vecteur de prédictions binaires (cupy.ndarray).
150
+ """
151
+ # Ici, la logique de conversion en 0/1 n'est pas spécifiée dans l'UML,
152
+ # donc on la laisse minimale (raise NotImplementedError si nécessaire).
153
+ raise NotImplementedError(
154
+ "La méthode '_get_binary_predictions' doit être implémentée dans une sous-classe."
155
+ )
156
+
157
+ def _get_positive_probabilities(self,
158
+ X: cp.ndarray) -> Optional[cp.ndarray]:
159
+ """
160
+ Retourne la probabilité d'appartenir à la classe positive pour chaque échantillon,
161
+ si le modèle le permet. Sinon, retourne None.
162
+
163
+ :param X: Matrice de données en cupy.ndarray.
164
+ :return: Vecteur de probabilités (cupy.ndarray) ou None si non applicable.
165
+ """
166
+ return None
167
+
168
+ def _get_label_dtype(self) -> cp.dtype:
169
+ """
170
+ Retourne le type cupy.dtype approprié pour les labels.
171
+
172
+ :return: Par exemple, cp.int32.
173
+ """
174
+ return cp.int32
src/conf/config.yaml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # conf/config.yaml
2
+
3
+ defaults:
4
+ - models: models
5
+ - _self_
6
+
7
+ model:
8
+ type: "svm"
9
+
10
+ data:
11
+ path: "dataset/dataset.csv"
12
+ target_column: "label"
13
+
14
+ vectorization:
15
+ method: "tfidf"
16
+
17
+ mlflow:
18
+ experiment_name: "experiment-${model.type}"
19
+ tracking_uri: "file:///data/mlruns"
20
+
21
+ hyperparameters:
22
+ optimizer: "optuna"
23
+ param_grid:
24
+ C: [0.1, 1, 10, 100]
25
+ kernel:
26
+ - "linear"
27
+ - "rbf"
28
+ gamma:
29
+ low: 0.001
30
+ high: 0.1
31
+ log: true
32
+ n_trials: 50
src/conf/model.yaml ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # conf/models.yaml
2
+
3
+ ###############################################################################
4
+ # Ce fichier rassemble TOUTES les config de modèles dans un seul YAML.
5
+ #
6
+ # L'idée : on centralise ici les paramètres, et dans le "main" ou dans
7
+ # un autre fichier config.yaml, on choisit un "type" => "svm", "random_forest",
8
+ # "huggingface_transformer"..., puis on récupère le bloc correspondant.
9
+ ###############################################################################
10
+
11
+ models:
12
+ huggingface_transformer:
13
+ # Paramètres pour HuggingFaceTransformerTrainer
14
+ model_name: "bert-base-uncased"
15
+ num_labels: 2
16
+ learning_rate: 5e-5
17
+ epochs: 3
18
+ batch_size: 8
19
+ warmup_steps: 0
20
+ weight_decay: 0.0
21
+ adam_epsilon: 1e-8
22
+
23
+ svm:
24
+ # Paramètres pour SvmTrainer
25
+ kernel: "rbf"
26
+ C: 1.0
27
+ gamma: "scale"
28
+ degree: 3
29
+ tol: 1e-3
30
+ max_iter: 1000
31
+ probability: True
32
+ decision_function_shape: "ovr"
33
+
34
+ random_forest:
35
+ # Paramètres pour RandomForestTrainer
36
+ n_estimators: 100
37
+ max_depth: null
38
+ max_features: "auto"
39
+ min_samples_split: 2
40
+ min_samples_leaf: 1
41
+ bootstrap: true
42
+ criterion: "gini"
43
+
44
+ logistic_regression:
45
+ # Paramètres pour LogisticRegressionTrainer
46
+ penalty: "l2"
47
+ C: 1.0
48
+ solver: "qn"
49
+ max_iter: 100
50
+ fit_intercept: true
51
+ linesearch_max_iter: 50
52
+
53
+ linear_regression:
54
+ # Paramètres pour LinearRegressionTrainer
55
+ fit_intercept: true
56
+ algorithm: "eig"
src/conf/vectorization.yaml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # conf/vectorization.yaml
2
+ vectorization :
3
+ tfidf:
4
+ lowercase: false # Géré par cuML
5
+ stop_words: null # Géré par cuML (None, 'english', ou une liste)
6
+ ngram_range: [1, 1] # Géré par cuML
7
+ max_df: 1.0 # Géré par cuML
8
+ min_df: 1 # Géré par cuML
9
+ max_features: 5000 # Géré par cuML
10
+ norm: l2 # Spécifique TF-IDF
11
+ use_idf: true # Spécifique TF-IDF
12
+ smooth_idf: true # Spécifique TF-IDF
13
+ sublinear_tf: false # Spécifique TF-IDF
14
+ # vocabulary: null # Optionnel, gardé si besoin
15
+ # binary: false # Optionnel, gardé si besoin
16
+
17
+ bow:
18
+ lowercase: false # Géré par cuML
19
+ stop_words: null # Géré par cuML
20
+ token_pattern: "(?u)\\b\\w\\w+\\b" # Géré par CountVectorizer cuML
21
+ ngram_range: [1, 1] # Géré par cuML
22
+ max_df: 1.0 # Géré par cuML
23
+ min_df: 1 # Géré par cuML
24
+ max_features: null # Géré par cuML
25
+ # vocabulary: null # Optionnel, gardé si besoin
26
+ # binary: false # Optionnel, gardé si besoin
src/config.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ======================
2
+ # Fichier: config.py
3
+ # ======================
4
+
5
+ from pydantic import BaseModel, Field
6
+ from typing import Dict, Any
7
+
8
+
9
+ class ModelConfig(BaseModel):
10
+ """
11
+ Représente la configuration du modèle, incluant le type de modèle
12
+ et les paramètres associés.
13
+ """
14
+ type: str = Field(
15
+ ...,
16
+ description=
17
+ "Le type de modèle à entraîner (ex. 'svm', 'random_forest', 'logistic_regression', etc.)."
18
+ )
19
+ params: Dict[str, Any] = Field(
20
+ default_factory=dict,
21
+ description="Dictionnaire des paramètres propres au modèle choisi.")
22
+
23
+
24
+ class VectorizationConfig(BaseModel):
25
+ """
26
+ Représente la configuration de la vectorisation, incluant la méthode
27
+ et les paramètres éventuels.
28
+ """
29
+ method: str = Field(
30
+ ...,
31
+ description="Le type de vectorisation à utiliser (ex. 'tfidf', 'bow')."
32
+ )
33
+ tfidf: Dict[str, Any] = Field(
34
+ default_factory=dict,
35
+ description="Paramètres spécifiques à une vectorisation TF-IDF.")
36
+ bow: Dict[str, Any] = Field(
37
+ default_factory=dict,
38
+ description="Paramètres spécifiques à une vectorisation bag-of-words.")
39
+
40
+
41
+ class DataConfig(BaseModel):
42
+ """
43
+ Représente la configuration liée aux données, incluant
44
+ le chemin vers les données et le nom de la colonne cible.
45
+ """
46
+ path: str = Field(...,
47
+ description="Chemin d'accès vers la source de données.")
48
+ target_column: str = Field(
49
+ ..., description="Nom de la colonne contenant la variable cible.")
50
+
51
+
52
+ class HyperparameterConfig(BaseModel):
53
+ """
54
+ Représente la configuration pour l'optimisation des hyperparamètres,
55
+ incluant le nom de l'optimiseur, la grille de paramètres et
56
+ le nombre d'itérations d'entraînement.
57
+ """
58
+ optimizer: str = Field(
59
+ ...,
60
+ description=
61
+ "Nom de l'optimiseur d'hyperparamètres (ex. 'optuna', 'raytune').")
62
+ param_grid: Dict[str, Any] = Field(
63
+ default_factory=dict,
64
+ description=
65
+ "Grille définissant l'espace de recherche pour chaque hyperparamètre.")
66
+ n_trials: int = Field(
67
+ default=1,
68
+ description="Nombre d'essais pour la recherche d'hyperparamètres.")
69
+
70
+
71
+ class Config(BaseModel):
72
+ """
73
+ Objet de configuration global combinant la section modèle, vectorisation,
74
+ données et hyperparamètres.
75
+ """
76
+ model: ModelConfig
77
+ vectorization: VectorizationConfig
78
+ data: DataConfig
79
+ hyperparameters: HyperparameterConfig
src/cuml_trainer.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ===========================
2
+ # Fichier: cuml_trainer.py
3
+ # ===========================
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Union
7
+ import cupy as cp
8
+ from scipy.sparse import csr_matrix
9
+
10
+ from config import Config
11
+ from base_trainer import BaseTrainer
12
+ from interfaces.vectorizer import Vectorizer
13
+
14
+
15
+ class CuMLTrainer(BaseTrainer, ABC):
16
+ """
17
+ Classe abstraite, héritée de BaseTrainer, représentant un entraîneur
18
+ basé sur la librairie cuML. Elle ajoute notamment le concept de vectoriseur
19
+ et force le passage de la matrice d'entrée en cupy.ndarray pour la plupart
20
+ des opérations.
21
+
22
+ Attributs:
23
+ vectorizer (Vectorizer): Objet responsable de la vectorisation du texte.
24
+ """
25
+
26
+ def __init__(self, config: Config, data_path: str,
27
+ target_column: str) -> None:
28
+ """
29
+ Initialise un CuMLTrainer avec la configuration.
30
+ Appelle également le constructeur de BaseTrainer.
31
+
32
+ :param config: Configuration globale du système.
33
+ :param data_path: Chemin vers le fichier de données.
34
+ :param target_column: Nom de la colonne cible dans les données.
35
+ """
36
+ super().__init__(config, data_path, target_column)
37
+ self.vectorizer: Vectorizer = None
38
+ # self.classifier est déjà défini dans BaseTrainer.
39
+ # On suppose que 'classifier' sera un modèle cuML (cuml.Base).
40
+
41
+ @abstractmethod
42
+ def build_components(self) -> None:
43
+ """
44
+ Méthode abstraite. Instancie concrètement le vectorizer et le classifieur,
45
+ selon la configuration (ex. 'svm', 'random_forest', etc.).
46
+ """
47
+ pass
48
+
49
+ def train(self) -> None:
50
+ """
51
+ Entraîne le classifieur sur les données vectorisées.
52
+ Cette implémentation générique fonctionne pour tous les trainers cuML.
53
+ """
54
+ # Chargement des données
55
+ import cudf
56
+ data = cudf.read_csv(self.data_path)
57
+
58
+ # Séparation des textes et des étiquettes
59
+ texts = data.drop(columns=[self.target_column])
60
+ labels = data[self.target_column].values
61
+
62
+ # Vectorisation des textes
63
+ X = self.vectorizer.fit_transform(texts)
64
+ X_prepared = self._prepare_input_for_fit(X)
65
+
66
+ # Entraînement du modèle
67
+ self.classifier.fit(X_prepared, labels)
68
+
69
+ def evaluate(self) -> None:
70
+ """
71
+ Évalue le classifieur et calcule les métriques.
72
+ Cette implémentation générique fonctionne pour tous les trainers cuML.
73
+ """
74
+ # Chargement des données (idéalement un jeu de test séparé)
75
+ import cudf
76
+ data = cudf.read_csv(self.data_path)
77
+
78
+ # Séparation des textes et des étiquettes
79
+ texts = data.drop(columns=[self.target_column])
80
+ y_true = data[self.target_column].values
81
+
82
+ # Vectorisation et prédiction
83
+ X = self.vectorizer.transform(texts)
84
+ X_prepared = self._prepare_input_for_predict(X)
85
+ y_pred = self.classifier.predict(X_prepared)
86
+
87
+ # Calcul et logging des métriques
88
+ prefix = self.config.model.type.lower()
89
+ metrics = self.metrics_calculator.calculate_and_log(
90
+ y_true=y_true,
91
+ y_pred=y_pred,
92
+ prefix=prefix
93
+ )
94
+
95
+ # Afficher les résultats
96
+ print(f"Métriques d'évaluation {prefix}: {metrics}")
97
+
98
+ def _prepare_input_for_fit(self, X: Union[cp.ndarray,
99
+ csr_matrix]) -> cp.ndarray:
100
+ """
101
+ Convertit, si nécessaire, la matrice en cupy.ndarray pour l'entraînement.
102
+
103
+ :param X: Données d'entraînement (cupy.ndarray ou scipy.sparse.csr_matrix).
104
+ :return: Données converties en cupy.ndarray, pour compatibilité cuML.
105
+ """
106
+ if isinstance(X, csr_matrix):
107
+ return cp.asarray(X.toarray())
108
+ return X # c'est déjà cupy.ndarray
109
+
110
+ def _prepare_input_for_predict(
111
+ self, X: Union[cp.ndarray, csr_matrix]) -> cp.ndarray:
112
+ """
113
+ Convertit, si nécessaire, la matrice en cupy.ndarray pour la prédiction.
114
+
115
+ :param X: Données de prédiction (cupy.ndarray ou scipy.sparse.csr_matrix).
116
+ :return: Données converties en cupy.ndarray, pour compatibilité cuML.
117
+ """
118
+ if isinstance(X, csr_matrix):
119
+ return cp.asarray(X.toarray())
120
+ return X
src/dataset/dataset.csv ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a4c582a26c9391ad2e190298ad0a0d0951f2ee3c71e8183b461a4d765856d4ee
3
+ size 421000
src/interfaces/cuml_tfidf_vectorizer.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ====
2
+ # Fichier: interfaces/cuml_tfidf_vectorizer.py
3
+ # ====
4
+
5
+ from typing import Union
6
+ import cupy as cp
7
+ import cudf
8
+ from scipy.sparse import csr_matrix
9
+ from cuml.feature_extraction.text import TfidfVectorizer as CuMLTfidf
10
+ from interfaces.vectorizer import Vectorizer
11
+
12
+
13
+ class CuMLTfidfVectorizer(Vectorizer):
14
+ """
15
+ Implémentation concrète d'une vectorisation TF-IDF avec cuML.
16
+ Les paramètres se basent sur un dict (e.g. venant de config.vectorization.tfidf).
17
+ """
18
+ def __init__(self, **kwargs):
19
+ self._vectorizer = CuMLTfidf(**kwargs)
20
+ self._fitted = False
21
+
22
+ def fit_transform(self, texts: cudf.Series) -> Union[cp.ndarray, csr_matrix]:
23
+ """
24
+ Ajuste la TF-IDF sur les textes et retourne la matrice resultante.
25
+ """
26
+ X = self._vectorizer.fit_transform(texts)
27
+ self._fitted = True
28
+ return X
29
+
30
+ def transform(self, texts: cudf.Series) -> Union[cp.ndarray, csr_matrix]:
31
+ """
32
+ Applique la TF-IDF déjà apprise et retourne la matrice resultante.
33
+ """
34
+ if not self._fitted:
35
+ raise ValueError("Vectorizer not yet fitted. Call fit_transform first.")
36
+ X = self._vectorizer.transform(texts)
37
+ return X
src/interfaces/hyperparameter_optimizer.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================
2
+ # Fichier: interfaces/hyperparameter_optimizer.py
3
+ # ============================================
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Dict, Any
7
+ from base_trainer import BaseTrainer
8
+
9
+
10
+ class HyperparameterOptimizer(ABC):
11
+ """
12
+ Interface pour la classe responsable de l'optimisation
13
+ des hyperparamètres.
14
+ """
15
+
16
+ @abstractmethod
17
+ def optimize(self, trainer: BaseTrainer,
18
+ param_grid: Dict[str, Any]) -> Dict[str, Any]:
19
+ """
20
+ Recherche les meilleurs hyperparamètres pour un 'trainer' donné,
21
+ selon la grille 'param_grid'.
22
+
23
+ :param trainer: Instance d'une classe implémentant BaseTrainer.
24
+ :param param_grid: Dictionnaire définissant l'espace de recherche
25
+ pour chaque hyperparamètre.
26
+ :return: Un dictionnaire contenant les hyperparamètres optimaux trouvés.
27
+ """
28
+ pass
src/interfaces/metrics_calculator.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cupy as cp
2
+ import numpy as np
3
+ from typing import Dict
4
+ import logging
5
+ from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
6
+ from interfaces.metrics_calculator import MetricsCalculator
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ class DefaultMetricsCalculator(MetricsCalculator):
11
+ """
12
+ Implémentation concrète de MetricsCalculator qui calcule
13
+ accuracy, f1, precision, recall, et auc-roc.
14
+ Fonctionne pour binaire ou multiclasses (avec 'ovr' ou 'macro').
15
+ """
16
+
17
+ def calculate_and_log(
18
+ self,
19
+ y_true: cp.ndarray,
20
+ y_pred: cp.ndarray,
21
+ prefix: str
22
+ ) -> Dict[str, float]:
23
+ """
24
+ Calcule et log les métriques pour un problème binaire
25
+ en supposant y_pred est dans {0,1} ou {True,False}.
26
+ """
27
+ y_true_np = cp.asnumpy(y_true)
28
+ y_pred_np = cp.asnumpy(y_pred)
29
+
30
+ acc = accuracy_score(y_true_np, y_pred_np)
31
+ prec = precision_score(y_true_np, y_pred_np, zero_division=0)
32
+ rec = recall_score(y_true_np, y_pred_np, zero_division=0)
33
+ f1 = f1_score(y_true_np, y_pred_np, zero_division=0)
34
+
35
+ # Calcul AUC pour un problème binaire (si y_pred est 0/1)
36
+ # On treat y_pred_np as our "probabilities" only if truly 0/1.
37
+ # In a real pipeline you might store probabilities separately.
38
+ try:
39
+ auc = roc_auc_score(y_true_np, y_pred_np)
40
+ except ValueError:
41
+ auc = 0.0
42
+
43
+ metrics = {
44
+ f"{prefix}_accuracy" : acc,
45
+ f"{prefix}_precision" : prec,
46
+ f"{prefix}_recall" : rec,
47
+ f"{prefix}_f1" : f1,
48
+ f"{prefix}_auc_roc" : auc
49
+ }
50
+ logger.info(f"[{prefix}] Metrics: {metrics}")
51
+ return metrics
52
+
53
+ def calculate_and_log_multiclass(
54
+ self,
55
+ y_true: cp.ndarray,
56
+ y_pred: cp.ndarray,
57
+ prefix: str
58
+ ) -> Dict[str, float]:
59
+ """
60
+ Calcule et log les métriques pour un problème multiclasses.
61
+ AUC-ROC en mode 'macro' si possible.
62
+ """
63
+ y_true_np = cp.asnumpy(y_true)
64
+ y_pred_np = cp.asnumpy(y_pred)
65
+
66
+ acc = accuracy_score(y_true_np, y_pred_np)
67
+ prec = precision_score(y_true_np, y_pred_np, average="macro", zero_division=0)
68
+ rec = recall_score(y_true_np, y_pred_np, average="macro", zero_division=0)
69
+ f1 = f1_score(y_true_np, y_pred_np, average="macro", zero_division=0)
70
+
71
+ # Pour le multiclasses, la roc_auc_score nécessite des scores proba
72
+ # ou "decision_function" => vous ajusterez selon votre cas.
73
+ # Ici, on met 0.0 en fallback.
74
+ auc = 0.0
75
+
76
+ metrics = {
77
+ f"{prefix}_accuracy" : acc,
78
+ f"{prefix}_precision" : prec,
79
+ f"{prefix}_recall" : rec,
80
+ f"{prefix}_f1" : f1,
81
+ f"{prefix}_auc_roc" : auc
82
+ }
83
+ logger.info(f"[{prefix}] Multiclass metrics: {metrics}")
84
+ return metrics
src/interfaces/vectorizer.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =================================
2
+ # Fichier: interfaces/vectorizer.py
3
+ # =================================
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Union
7
+ import cupy as cp
8
+ import cudf
9
+ from scipy.sparse import csr_matrix
10
+
11
+
12
+ class Vectorizer(ABC):
13
+ """
14
+ Interface pour la classe responsable de la vectorisation du texte.
15
+ Permet de convertir une série de textes en représentations
16
+ numériques (matrice sparse ou dense).
17
+ """
18
+
19
+ @abstractmethod
20
+ def fit_transform(self,
21
+ texts: cudf.Series) -> Union[cp.ndarray, csr_matrix]:
22
+ """
23
+ Ajuste la vectorisation sur les textes de la série 'texts'
24
+ puis renvoie la matrice vectorisée associée.
25
+
26
+ :param texts: Série cudf contenant des chaînes de caractères.
27
+ :return: Matrice vectorisée (cupy.ndarray ou scipy.sparse.csr_matrix).
28
+ """
29
+ pass
30
+
31
+ @abstractmethod
32
+ def transform(self, texts: cudf.Series) -> Union[cp.ndarray, csr_matrix]:
33
+ """
34
+ Applique la vectorisation déjà apprise à une nouvelle série de textes
35
+ pour produire la matrice vectorisée.
36
+
37
+ :param texts: Série cudf contenant des chaînes de caractères.
38
+ :return: Matrice vectorisée (cupy.ndarray ou scipy.sparse.csr_matrix).
39
+ """
40
+ pass
src/main.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # =========================
3
+ # Fichier: main.py
4
+ # =========================
5
+
6
+ import os
7
+ import logging
8
+ import hydra
9
+ from omegaconf import DictConfig, OmegaConf
10
+ import mlflow
11
+
12
+ # Import des trainers
13
+ from trainers.cuml.svm_trainer import SvmTrainer
14
+ from trainers.cuml.random_forest_trainer import RandomForestTrainer
15
+ from trainers.cuml.logistic_regression_trainer import LogisticRegressionTrainer
16
+ from trainers.cuml.linear_regression_trainer import LinearRegressionTrainer
17
+ from trainers.huggingface.huggingface_transformer_trainer import HuggingFaceTransformerTrainer
18
+
19
+ # Import des optimizers
20
+ from optimizers.optuna_optimizer import OptunaOptimizer
21
+ from optimizers.ray_tune_optimizer import RayTuneOptimizer
22
+
23
+ # Import du décorateur MLflow
24
+ from mlflow_integration.mlflow_decorator import MLflowDecorator
25
+
26
+ # Import de la configuration
27
+ from config import Config
28
+
29
+ # Configuration du logging
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ def get_trainer(config: Config):
34
+ """
35
+ Crée et retourne l'instance du trainer approprié en fonction de la configuration.
36
+
37
+ Args:
38
+ config: Objet de configuration
39
+
40
+ Returns:
41
+ Une instance concrète de BaseTrainer
42
+ """
43
+ model_type = config.model.type.lower()
44
+
45
+ # Mapping des types de modèles vers leurs trainers
46
+ trainer_map = {
47
+ "svm": SvmTrainer,
48
+ "random_forest": RandomForestTrainer,
49
+ "logistic_regression": LogisticRegressionTrainer,
50
+ "linear_regression": LinearRegressionTrainer,
51
+ "transformer": HuggingFaceTransformerTrainer,
52
+ }
53
+
54
+ if model_type not in trainer_map:
55
+ raise ValueError(f"Type de modèle non supporté: {model_type}")
56
+
57
+ # Création de l'instance du trainer avec la configuration
58
+ trainer_class = trainer_map[model_type]
59
+ return trainer_class(
60
+ config=config,
61
+ data_path=config.data.path,
62
+ target_column=config.data.target_column
63
+ )
64
+
65
+
66
+ def get_optimizer(config: Config):
67
+ """
68
+ Crée et retourne l'instance d'optimizer appropriée en fonction de la configuration.
69
+
70
+ Args:
71
+ config: Objet de configuration
72
+
73
+ Returns:
74
+ Une instance concrète de HyperparameterOptimizer
75
+ """
76
+ optimizer_type = config.hyperparameters.optimizer.lower()
77
+
78
+ # Mapping des types d'optimizers
79
+ optimizer_map = {
80
+ "optuna": OptunaOptimizer,
81
+ "raytune": RayTuneOptimizer,
82
+ }
83
+
84
+ if optimizer_type not in optimizer_map:
85
+ raise ValueError(f"Type d'optimizer non supporté: {optimizer_type}")
86
+
87
+ # Création de l'instance de l'optimizer
88
+ optimizer_class = optimizer_map[optimizer_type]
89
+ return optimizer_class()
90
+
91
+
92
+ @hydra.main(config_path="conf", config_name="config")
93
+ def main(cfg: DictConfig) -> None:
94
+ """
95
+ Point d'entrée principal de l'application.
96
+
97
+ Args:
98
+ cfg: Configuration Hydra sous forme de DictConfig
99
+ """
100
+ # Conversion de la configuration Hydra en configuration Pydantic
101
+ config_dict = OmegaConf.to_container(cfg, resolve=True)
102
+ config = Config(**config_dict)
103
+
104
+ logger.info(f"Configuration chargée: {config}")
105
+
106
+ # Création du trainer approprié
107
+ trainer = get_trainer(config)
108
+
109
+ # Construction des composants (vectorizer, classifier, etc.)
110
+ trainer.build_components()
111
+
112
+
113
+ mlflow_decorator = MLflowDecorator(
114
+ experiment_name=config.mlflow.experiment_name,
115
+ tracking_uri=config.mlflow.tracking_uri
116
+ )
117
+ # Appliquer le décorateur aux méthodes clés
118
+ train_with_mlflow = mlflow_decorator(trainer.train)
119
+ evaluate_with_mlflow = mlflow_decorator(trainer.evaluate)
120
+ log_params_with_mlflow = mlflow_decorator(trainer.log_parameters_to_mlflow)
121
+ optimize_if_needed_with_mlflow = mlflow_decorator(trainer.optimize_if_needed) # Décorer aussi l'optimisation
122
+
123
+ logger.info("Vérification et lancement de l'optimisation des hyperparamètres si nécessaire (avec MLflow)...")
124
+ optimize_if_needed_with_mlflow()
125
+
126
+ # Exécuter l'entraînement (toujours avec MLflow)
127
+ logger.info("Lancement de l'entraînement avec MLflow...")
128
+ train_with_mlflow()
129
+
130
+ # Exécuter l'évaluation (toujours avec MLflow)
131
+ logger.info("Lancement de l'évaluation avec MLflow...")
132
+ evaluate_with_mlflow()
133
+
134
+ # Logger les paramètres (toujours avec MLflow)
135
+ logger.info("Logging des paramètres avec MLflow...")
136
+ log_params_with_mlflow()
137
+
138
+ logger.info("Entraînement, évaluation et logging des paramètres terminés avec succès via MLflow.")
139
+
140
+
141
+ if __name__ == "__main__":
142
+ main()
src/mlflow_integration/mlflow_decorator.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =======================================
2
+ # Fichier: mlflow_integration/mlflow_decorator.py
3
+ # =======================================
4
+
5
+ from typing import Callable, Dict, Any
6
+ import mlflow
7
+
8
+
9
+ class MLflowDecorator:
10
+ """
11
+ Décorateur dédié à l'intégration MLflow. Permet d'initialiser un 'experiment_name',
12
+ un 'tracking_uri', et de gérer la logique de logging de paramètres, métriques,
13
+ et artefacts pour MLflow.
14
+ """
15
+
16
+ def __init__(self, experiment_name: str, tracking_uri: str) -> None:
17
+ """
18
+ Initialise le décorateur MLflow avec le nom d'expérience et l'URI du serveur de tracking.
19
+
20
+ :param experiment_name: Nom de l'expérience MLflow à utiliser.
21
+ :param tracking_uri: URI du serveur MLflow (peut être un chemin local,
22
+ ou une URL vers un serveur distant).
23
+ """
24
+ self.experiment_name: str = experiment_name
25
+ self.tracking_uri: str = tracking_uri
26
+
27
+ def __call__(self, func: Callable) -> Callable:
28
+ """
29
+ Rend la classe MLflowDecorator utilisable comme décorateur.
30
+ Lance la session MLflow avant d'appeler 'func', et la termine ensuite.
31
+
32
+ :param func: La fonction ou méthode à décorer.
33
+ :return: Une fonction enveloppée qui gère le cycle de vie d'une run MLflow.
34
+ """
35
+
36
+ def wrapper(*args, **kwargs):
37
+ mlflow.set_tracking_uri(self.tracking_uri)
38
+ mlflow.set_experiment(self.experiment_name)
39
+ with mlflow.start_run():
40
+ result = func(*args, **kwargs)
41
+ return result
42
+
43
+ return wrapper
44
+
45
+ def _start_run(self) -> None:
46
+ """
47
+ Démarre explicitement un run MLflow.
48
+ """
49
+ mlflow.start_run()
50
+
51
+ def _log_params(self, params: Dict[str, Any]) -> None:
52
+ """
53
+ Logue dans MLflow les paramètres passés en dictionnaire.
54
+
55
+ :param params: Dictionnaire de paramètres (clef : valeur).
56
+ """
57
+ mlflow.log_params(params)
58
+
59
+ def _log_metrics(self, metrics: Dict[str, float]) -> None:
60
+ """
61
+ Logue dans MLflow les métriques passées en dictionnaire.
62
+
63
+ :param metrics: Dictionnaire de métriques (clef : valeur).
64
+ """
65
+ mlflow.log_metrics(metrics)
66
+
67
+ def _log_artifacts(self, artifacts: Dict[str, Any]) -> None:
68
+ """
69
+ Logue dans MLflow différents artefacts (fichiers, images, etc.).
70
+ L'UML mentionne la méthode, mais pas la logique interne.
71
+
72
+ :param artifacts: Dictionnaire décrivant les artefacts à logguer.
73
+ """
74
+ pass
75
+
76
+ def _end_run(self, status: str) -> None:
77
+ """
78
+ Termine un run MLflow en spécifiant un statut (ex: 'FINISHED', 'FAILED').
79
+
80
+ :param status: Statut final à associer au run MLflow.
81
+ """
82
+ mlflow.end_run(status=status)
src/optimizers/optuna_optimizer.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =====================================
2
+ # Fichier: optimizers/optuna_optimizer.py
3
+ # =====================================
4
+
5
+ from typing import Dict, Any
6
+ from interfaces.hyperparameter_optimizer import HyperparameterOptimizer
7
+ from base_trainer import BaseTrainer
8
+
9
+
10
+ class OptunaOptimizer(HyperparameterOptimizer):
11
+ """
12
+ Optimiseur d'hyperparamètres basé sur la librairie Optuna.
13
+ Implémente l'interface HyperparameterOptimizer.
14
+ """
15
+
16
+ def optimize(self, trainer: BaseTrainer,
17
+ param_grid: Dict[str, Any]) -> Dict[str, Any]:
18
+ """
19
+ Recherche les meilleurs hyperparamètres pour un 'trainer' donné,
20
+ selon la grille 'param_grid', en utilisant Optuna.
21
+
22
+ :param trainer: Instance d'une classe implémentant BaseTrainer.
23
+ :param param_grid: Dictionnaire définissant l'espace de recherche
24
+ pour chaque hyperparamètre.
25
+ :return: Un dictionnaire contenant les hyperparamètres optimaux trouvés.
26
+ """
27
+ # L'implémentation réelle d'Optuna ne figure pas dans l'UML,
28
+ # donc nous restons minimalistes et précises,
29
+ # sans ajouter d'hypothèses externes.
30
+ # L'UML mentionne juste + optimize(trainer: BaseTrainer, param_grid: dict): dict
31
+ import optuna
32
+ from functools import partial
33
+
34
+ class OptunaOptimizer(HyperparameterOptimizer):
35
+ """
36
+ Optimiseur d'hyperparamètres basé sur la librairie Optuna.
37
+ Implémente l'interface HyperparameterOptimizer.
38
+ """
39
+
40
+ def optimize(self, trainer: BaseTrainer, param_grid: Dict[str, Any]) -> Dict[str, Any]:
41
+ """
42
+ Recherche les meilleurs hyperparamètres pour un 'trainer' donné,
43
+ selon la grille 'param_grid', en utilisant Optuna.
44
+
45
+ :param trainer: Instance d'une classe implémentant BaseTrainer.
46
+ :param param_grid: Dictionnaire définissant l'espace de recherche
47
+ pour chaque hyperparamètre.
48
+ :return: Un dictionnaire contenant les hyperparamètres optimaux trouvés.
49
+ """
50
+ def suggest_param(trial, param, values):
51
+ return (
52
+ trial.suggest_categorical(param, values) if isinstance(values, list) else
53
+ trial.suggest_float(param, values['low'], values['high'], log=values.get('log', False)) if isinstance(values, dict) and 'low' in values and 'high' in values else
54
+ trial.suggest_int(param, values['low'], values.get('high', 10)) if isinstance(values, dict) else
55
+ trial.suggest_float(param, 0.0, 1.0)
56
+ )
57
+
58
+ def objective(trial):
59
+ params = {param: suggest_param(trial, param, vals) for param, vals in param_grid.items()}
60
+ trainer.config.hyperparameters = params
61
+ trainer.train()
62
+ results = trainer.evaluate()
63
+ return results.get('validation_loss', float('inf')) # Supposant que le loss est à minimiser
64
+
65
+ study = optuna.create_study(direction='minimize')
66
+ n_trials = trainer.config.hyperparameters.get('n_trials', 100)
67
+ study.optimize(objective, n_trials=n_trials)
68
+
69
+ return study.best_params
src/optimizers/ray_tune_optimizer.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =======================================
2
+ # Fichier: optimizers/ray_tune_optimizer.py
3
+ # =======================================
4
+
5
+ from typing import Dict, Any
6
+ from interfaces.hyperparameter_optimizer import HyperparameterOptimizer
7
+ from base_trainer import BaseTrainer
8
+ import ray
9
+ from ray import tune
10
+ from ray.tune import CLIReporter
11
+ from ray.tune.schedulers import ASHAScheduler
12
+ from trainers.cuml.svm_trainer import SvmTrainer
13
+ from trainers.cuml.random_forest_trainer import RandomForestTrainer
14
+ from trainers.cuml.logistic_regression_trainer import LogisticRegressionTrainer
15
+ from trainers.cuml.linear_regression_trainer import LinearRegressionTrainer
16
+
17
+
18
+ class RayTuneOptimizer(HyperparameterOptimizer):
19
+ """
20
+ Optimiseur d'hyperparamètres basé sur Ray Tune.
21
+ Implémente l'interface HyperparameterOptimizer.
22
+ """
23
+
24
+ def optimize(self, trainer: BaseTrainer, param_grid: Dict[str, Any]) -> Dict[str, Any]:
25
+ """
26
+ Recherche les meilleurs hyperparamètres pour un 'trainer' donné,
27
+ selon la grille 'param_grid', en utilisant Ray Tune.
28
+
29
+ :param trainer: Instance d'une classe implémentant BaseTrainer.
30
+ :param param_grid: Dictionnaire définissant l'espace de recherche
31
+ pour chaque hyperparamètre.
32
+ :return: Un dictionnaire contenant les hyperparamètres optimaux trouvés.
33
+ """
34
+ full_config = trainer.config.dict() if hasattr(trainer.config, 'dict') else trainer.config
35
+ config = self._create_config(param_grid)
36
+ scheduler = self._create_scheduler()
37
+ reporter = self._create_reporter()
38
+
39
+ analysis = tune.run(
40
+ self._train_model,
41
+ config={"hyperparameters": config, **full_config},
42
+ num_samples=full_config['hyperparameters'].get('n_trials', 100),
43
+ scheduler=scheduler,
44
+ progress_reporter=reporter,
45
+ resources_per_trial={"cpu": 1, "gpu": 0}
46
+ )
47
+
48
+ return analysis.best_config
49
+
50
+ def _create_config(self, param_grid: Dict[str, Any]) -> Dict[str, Any]:
51
+ """
52
+ Crée la configuration pour Ray Tune à partir du param_grid.
53
+
54
+ :param param_grid: Dictionnaire définissant l'espace de recherche
55
+ pour chaque hyperparamètre.
56
+ :return: Dictionnaire de configuration pour Ray Tune.
57
+ """
58
+ return {param: self._define_search_space(param, vals) for param, vals in param_grid.items()}
59
+
60
+ def _define_search_space(self, param: str, vals: Any) -> Any:
61
+ """
62
+ Définit l'espace de recherche pour un hyperparamètre donné.
63
+
64
+ :param param: Nom de l'hyperparamètre.
65
+ :param vals: Valeurs possibles ou dictionnaire définissant l'espace.
66
+ :return: Espace de recherche Ray Tune.
67
+ """
68
+ if isinstance(vals, list):
69
+ return tune.choice(vals)
70
+ elif isinstance(vals, dict):
71
+ low = vals.get('low', 0)
72
+ high = vals.get('high', 10)
73
+ log = vals.get('log', False)
74
+ if 'low' in vals and 'high' in vals:
75
+ return tune.uniform(param, low, high) if log else tune.randint(param, low, high)
76
+ return tune.uniform(param, 0.0, 1.0)
77
+
78
+ def _create_scheduler(self) -> ASHAScheduler:
79
+ """
80
+ Crée un scheduler ASHAScheduler pour Ray Tune.
81
+
82
+ :return: Instance d'ASHAScheduler.
83
+ """
84
+ return ASHAScheduler(
85
+ max_t=100,
86
+ grace_period=10,
87
+ reduction_factor=2
88
+ )
89
+
90
+ def _create_reporter(self) -> CLIReporter:
91
+ """
92
+ Crée un reporter CLIReporter pour Ray Tune.
93
+
94
+ :return: Instance de CLIReporter.
95
+ """
96
+ return CLIReporter(
97
+ metric_columns=["validation_loss", "training_iteration"]
98
+ )
99
+
100
+ def _train_model(self, config: Dict[str, Any]):
101
+ """
102
+ Fonction d'entraînement pour Ray Tune.
103
+
104
+ :param config: Configuration des hyperparamètres.
105
+ """
106
+ merged_config = config.copy()
107
+ hyperparams = merged_config.pop('hyperparameters', {})
108
+ trainer_instance = self._get_trainer_instance(merged_config)
109
+ trainer_instance.config.hyperparameters = hyperparams
110
+ trainer_instance.train()
111
+ results = trainer_instance.evaluate()
112
+ tune.report(validation_loss=results.get('validation_loss', float('inf')))
113
+
114
+ def _get_trainer_instance(self, config: Dict[str, Any]) -> BaseTrainer:
115
+ """
116
+ Obtient une instance de BaseTrainer basée sur la configuration.
117
+
118
+ :param config: Configuration globale incluant hyperparamètres.
119
+ :return: Instance de BaseTrainer.
120
+ """
121
+ model_type = config['model']['type'].lower()
122
+ trainer_mapping = {
123
+ 'svm': SvmTrainer,
124
+ 'random_forest': RandomForestTrainer,
125
+ 'logistic_regression': LogisticRegressionTrainer,
126
+ 'linear_regression': LinearRegressionTrainer,
127
+ # Ajouter d'autres mappings ici si nécessaire
128
+ }
129
+
130
+ trainer_class = trainer_mapping.get(model_type)
131
+ if not trainer_class:
132
+ raise ValueError(f"Type de modèle non supporté : {model_type}")
133
+
134
+ return trainer_class(
135
+ config=config,
136
+ data_path=config['data']['path'],
137
+ target_column=config['data']['target_column']
138
+ )
src/parameter_logging.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==================================
2
+ # Fichier: parameter_logging.py
3
+ # ==================================
4
+
5
+ from functools import singledispatch
6
+ from typing import Dict, Any
7
+ from base_trainer import BaseTrainer
8
+ from trainers.huggingface.huggingface_transformer_trainer import HuggingFaceTransformerTrainer
9
+ from trainers.cuml.svm_trainer import SvmTrainer
10
+ from trainers.cuml.random_forest_trainer import RandomForestTrainer
11
+ from trainers.cuml.logistic_regression_trainer import LogisticRegressionTrainer
12
+ from trainers.cuml.linear_regression_trainer import LinearRegressionTrainer
13
+
14
+
15
+ @singledispatch
16
+ def get_relevant_params_for_logging(trainer: BaseTrainer) -> Dict[str, Any]:
17
+ """
18
+ Méthode générique, par défaut, pour récupérer les paramètres
19
+ à logger dans MLflow ou ailleurs.
20
+
21
+ :param trainer: Trainer dont on veut extraire les paramètres.
22
+ :return: Un dictionnaire de paramètres pertinents.
23
+ """
24
+ # Par défaut, on récupère la section 'params' du config.model,
25
+ # mais l'UML ne précise pas la logique interne.
26
+ return trainer.config.model.params
27
+
28
+
29
+ @get_relevant_params_for_logging.register
30
+ def _(trainer: HuggingFaceTransformerTrainer) -> Dict[str, Any]:
31
+ """
32
+ Cas particulier pour un HuggingFaceTransformerTrainer.
33
+ """
34
+ # Extrait les paramètres spécifiques HuggingFace indiqués dans trainer.config.model.params.
35
+ return trainer.config.model.params
36
+
37
+
38
+ @get_relevant_params_for_logging.register
39
+ def _(trainer: SvmTrainer) -> Dict[str, Any]:
40
+ """
41
+ Cas particulier pour un SvmTrainer.
42
+ """
43
+ return trainer.config.model.params
44
+
45
+
46
+ @get_relevant_params_for_logging.register
47
+ def _(trainer: RandomForestTrainer) -> Dict[str, Any]:
48
+ """
49
+ Cas particulier pour un RandomForestTrainer.
50
+ """
51
+ return trainer.config.model.params
52
+
53
+
54
+ @get_relevant_params_for_logging.register
55
+ def _(trainer: LogisticRegressionTrainer) -> Dict[str, Any]:
56
+ """
57
+ Cas particulier pour un LogisticRegressionTrainer.
58
+ """
59
+ return trainer.config.model.params
60
+
61
+
62
+ @get_relevant_params_for_logging.register
63
+ def _(trainer: LinearRegressionTrainer) -> Dict[str, Any]:
64
+ """
65
+ Cas particulier pour un LinearRegressionTrainer.
66
+ """
67
+ return trainer.config.model.params
src/trainers/cuml/linear_regression_trainer.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================
2
+ # Fichier: trainers/cuml/linear_regression_trainer.py
3
+ # ============================================
4
+
5
+ from cuml.linear_model import LinearRegression
6
+
7
+ from cuml_trainer import CuMLTrainer
8
+ from config import Config
9
+ from interfaces.cuml_tfidf_vectorizer import CuMLTfidfVectorizer
10
+ from interfaces.metrics_calculator import DefaultMetricsCalculator
11
+
12
+ class LinearRegressionTrainer(CuMLTrainer):
13
+ """
14
+ Entraîneur spécifique utilisant un classifieur LinearRegression de la librairie cuML.
15
+ Hérite de CuMLTrainer, qui hérite lui-même de BaseTrainer.
16
+
17
+ Cette classe implémente les méthodes spécifiques à l'utilisation d'un modèle
18
+ de régression linéaire pour la prédiction de valeurs continues.
19
+ """
20
+
21
+ def __init__(self, config: Config, data_path: str, target_column: str) -> None:
22
+ """
23
+ Initialise un LinearRegressionTrainer avec la configuration et les paramètres
24
+ du parent CuMLTrainer.
25
+
26
+ :param config: Configuration globale du système.
27
+ :param data_path: Chemin vers le fichier de données.
28
+ :param target_column: Nom de la colonne cible.
29
+ """
30
+ super().__init__(config, data_path, target_column)
31
+
32
+ def _build_classifier(self) -> None:
33
+ """
34
+ Crée et configure un classifieur LinearRegression selon les paramètres
35
+ spécifiés dans la configuration.
36
+
37
+ Utilise directement les paramètres de la configuration pour initialiser
38
+ le modèle avec les hyperparamètres appropriés.
39
+ """
40
+ params = self.config.model.params or {}
41
+ self.classifier = LinearRegression(**params)
42
+
43
+ def build_components(self) -> None:
44
+ """
45
+ Instancie le vectorizer, le classifieur LinearRegression et le calculateur de métriques.
46
+
47
+ Cette méthode configure tous les composants nécessaires au fonctionnement
48
+ du trainer, en se basant sur les paramètres spécifiés dans la configuration.
49
+ """
50
+ # Récupération de la méthode et des paramètres de vectorisation
51
+ vector_method = self.config.vectorization.method
52
+ vector_params = self.config.vectorization.dict().get(vector_method, {})
53
+ self.vectorizer = CuMLTfidfVectorizer(**vector_params)
54
+
55
+ # Construction du classifieur
56
+ self._build_classifier()
57
+
58
+ # Initialisation du calculateur de métriques
59
+ self.metrics_calculator = DefaultMetricsCalculator()
60
+
61
+ def train(self) -> None:
62
+ """
63
+ Entraîne le classifieur LinearRegression sur les données vectorisées.
64
+
65
+ Utilise l'implémentation fournie par la classe parente CuMLTrainer,
66
+ qui s'occupe du chargement des données, de la vectorisation et de l'entraînement.
67
+ """
68
+ # Inheriting from CuMLTrainer => loads data, vectorizes, fits
69
+ super().train()
70
+
71
+ def evaluate(self) -> None:
72
+ """
73
+ Évalue le classifieur LinearRegression et calcule les métriques appropriées.
74
+
75
+ Utilise l'implémentation fournie par la classe parente CuMLTrainer,
76
+ qui s'occupe du chargement des données, de la vectorisation, de la prédiction
77
+ et du calcul des métriques via le metrics_calculator.
78
+ """
79
+ # Inheriting from CuMLTrainer => loads data, vectorizes, calls the metrics calculator
80
+ super().evaluate()
src/trainers/cuml/logistic_regression_trainer.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================
2
+ # Fichier: trainers/cuml/logistic_regression_trainer.py
3
+ # ============================================
4
+
5
+ from cuml.linear_model import LogisticRegression
6
+
7
+ from cuml_trainer import CuMLTrainer
8
+ from config import Config
9
+ from interfaces.cuml_tfidf_vectorizer import CuMLTfidfVectorizer
10
+ from interfaces.metrics_calculator import DefaultMetricsCalculator
11
+
12
+ class LogisticRegressionTrainer(CuMLTrainer):
13
+ """
14
+ Entraîneur spécifique utilisant un classifieur LogisticRegression de la librairie cuML.
15
+ Hérite de CuMLTrainer, qui hérite lui-même de BaseTrainer.
16
+
17
+ Cette classe implémente les méthodes spécifiques à l'utilisation d'un modèle
18
+ de régression logistique pour la classification binaire ou multi-classes.
19
+ """
20
+
21
+ def __init__(self, config: Config, data_path: str, target_column: str) -> None:
22
+ """
23
+ Initialise un LogisticRegressionTrainer avec la configuration et les paramètres
24
+ du parent CuMLTrainer.
25
+
26
+ :param config: Configuration globale du système.
27
+ :param data_path: Chemin vers le fichier de données.
28
+ :param target_column: Nom de la colonne cible.
29
+ """
30
+ super().__init__(config, data_path, target_column)
31
+
32
+ def _build_classifier(self) -> None:
33
+ """
34
+ Crée et configure un classifieur LogisticRegression selon les paramètres
35
+ spécifiés dans la configuration.
36
+
37
+ Utilise directement les paramètres de la configuration pour initialiser
38
+ le modèle avec les hyperparamètres appropriés.
39
+ """
40
+ params = self.config.model.params or {}
41
+ self.classifier = LogisticRegression(**params)
42
+
43
+ def build_components(self) -> None:
44
+ """
45
+ Instancie le vectorizer, le classifieur LogisticRegression et le calculateur de métriques.
46
+
47
+ Cette méthode configure tous les composants nécessaires au fonctionnement
48
+ du trainer, en se basant sur les paramètres spécifiés dans la configuration.
49
+ """
50
+ # Récupération de la méthode et des paramètres de vectorisation
51
+ vector_method = self.config.vectorization.method
52
+ vector_params = self.config.vectorization.dict().get(vector_method, {})
53
+ self.vectorizer = CuMLTfidfVectorizer(**vector_params)
54
+
55
+ # Construction du classifieur
56
+ self._build_classifier()
57
+
58
+ # Initialisation du calculateur de métriques
59
+ self.metrics_calculator = DefaultMetricsCalculator()
60
+
61
+ def train(self) -> None:
62
+ """
63
+ Entraîne le classifieur LogisticRegression sur les données vectorisées.
64
+
65
+ Utilise l'implémentation fournie par la classe parente CuMLTrainer,
66
+ qui s'occupe du chargement des données, de la vectorisation et de l'entraînement.
67
+ """
68
+ super().train()
69
+
70
+ def evaluate(self) -> None:
71
+ """
72
+ Évalue le classifieur LogisticRegression et calcule les métriques appropriées.
73
+
74
+ Utilise l'implémentation fournie par la classe parente CuMLTrainer,
75
+ qui s'occupe du chargement des données, de la vectorisation, de la prédiction
76
+ et du calcul des métriques via le metrics_calculator.
77
+ """
78
+ super().evaluate()
src/trainers/cuml/random_forest_trainer.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================
2
+ # Fichier: trainers/cuml/random_forest_trainer.py
3
+ # ============================================
4
+
5
+ from typing import Optional
6
+ from cuml.ensemble import RandomForestClassifier
7
+ import cupy as cp
8
+
9
+ from cuml_trainer import CuMLTrainer
10
+ from config import Config
11
+ from interfaces.cuml_tfidf_vectorizer import CuMLTfidfVectorizer
12
+ from interfaces.metrics_calculator import DefaultMetricsCalculator
13
+
14
+ class RandomForestTrainer(CuMLTrainer):
15
+ """
16
+ Entraîneur spécifique utilisant un classifieur RandomForestClassifier de la librairie cuML.
17
+ Hérite de CuMLTrainer, qui hérite lui-même de BaseTrainer.
18
+
19
+ Cette classe implémente les méthodes spécifiques à l'utilisation d'un modèle
20
+ de forêt aléatoire pour la classification, en exploitant les capacités GPU
21
+ offertes par la librairie cuML.
22
+ """
23
+
24
+ def __init__(self, config: Config, data_path: str, target_column: str) -> None:
25
+ """
26
+ Initialise un RandomForestTrainer avec la configuration et les paramètres
27
+ du parent CuMLTrainer.
28
+
29
+ :param config: Configuration globale du système.
30
+ :param data_path: Chemin vers le fichier de données.
31
+ :param target_column: Nom de la colonne cible.
32
+ """
33
+ super().__init__(config, data_path, target_column)
34
+
35
+ def _build_classifier(self) -> None:
36
+ """
37
+ Crée et configure un classifieur RandomForestClassifier selon les paramètres
38
+ spécifiés dans la configuration.
39
+
40
+ Utilise directement les paramètres de la configuration pour initialiser
41
+ le modèle avec les hyperparamètres appropriés (ex: n_estimators, max_depth, etc.).
42
+ """
43
+ params = self.config.model.params or {}
44
+ self.classifier = RandomForestClassifier(**params)
45
+
46
+ def build_components(self) -> None:
47
+ """
48
+ Instancie le vectorizer, le classifieur RandomForest et le calculateur de métriques.
49
+
50
+ Cette méthode configure tous les composants nécessaires au fonctionnement
51
+ du trainer, en se basant sur les paramètres spécifiés dans la configuration.
52
+ """
53
+ # Récupération de la méthode et des paramètres de vectorisation
54
+ vector_method = self.config.vectorization.method # e.g. "tfidf"
55
+ vector_params = self.config.vectorization.dict().get(vector_method, {})
56
+ self.vectorizer = CuMLTfidfVectorizer(**vector_params)
57
+
58
+ # Construction du classifieur
59
+ self._build_classifier()
60
+
61
+ # Initialisation du calculateur de métriques
62
+ self.metrics_calculator = DefaultMetricsCalculator()
63
+
64
+ def train(self) -> None:
65
+ """
66
+ Entraîne le classifieur RandomForest sur les données vectorisées.
67
+
68
+ Utilise l'implémentation fournie par la classe parente CuMLTrainer,
69
+ qui s'occupe du chargement des données, de la vectorisation et de l'entraînement.
70
+ """
71
+ super().train()
72
+
73
+ def evaluate(self) -> None:
74
+ """
75
+ Évalue le classifieur RandomForest et calcule les métriques appropriées.
76
+
77
+ Utilise l'implémentation fournie par la classe parente CuMLTrainer,
78
+ qui s'occupe du chargement des données, de la vectorisation, de la prédiction
79
+ et du calcul des métriques via le metrics_calculator.
80
+ """
81
+ super().evaluate()
82
+
83
+ def _get_binary_predictions(self, X: cp.ndarray) -> cp.ndarray:
84
+ """
85
+ Retourne les prédictions binaires du modèle RandomForest.
86
+
87
+ :param X: Matrice de caractéristiques au format cupy.ndarray.
88
+ :return: Vecteur de prédictions binaires.
89
+ """
90
+ X_prepared = self._prepare_input_for_predict(X)
91
+ return self.classifier.predict(X_prepared)
92
+
93
+ def _get_positive_probabilities(self, X: cp.ndarray) -> Optional[cp.ndarray]:
94
+ """
95
+ Retourne les probabilités de la classe positive.
96
+ Les forêts aléatoires peuvent toujours fournir des probabilités,
97
+ ce qui est l'un de leurs avantages.
98
+
99
+ :param X: Matrice de caractéristiques au format cupy.ndarray.
100
+ :return: Vecteur de probabilités pour la classe positive.
101
+ """
102
+ X_prepared = self._prepare_input_for_predict(X)
103
+ return self.classifier.predict_proba(X_prepared)[:, 1]
src/trainers/cuml/svm_trainer.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================
2
+ # Fichier: trainers/cuml/svm_trainer.py
3
+ # ============================================
4
+
5
+ from cuml.svm import SVC
6
+ import cupy as cp
7
+ from typing import Optional
8
+
9
+ from cuml_trainer import CuMLTrainer
10
+ from config import Config
11
+ from interfaces.cuml_tfidf_vectorizer import CuMLTfidfVectorizer
12
+ from interfaces.metrics_calculator import DefaultMetricsCalculator
13
+
14
+ class SvmTrainer(CuMLTrainer):
15
+ """
16
+ Entraîneur spécifique utilisant un classifieur SVC (Support Vector Classifier) de la librairie cuML.
17
+ Hérite de CuMLTrainer, qui hérite lui-même de BaseTrainer.
18
+
19
+ Cette classe implémente les méthodes spécifiques à l'utilisation d'un modèle SVM
20
+ pour la classification binaire ou multi-classes, en exploitant les capacités GPU
21
+ offertes par la librairie cuML.
22
+ """
23
+
24
+ def __init__(self, config: Config, data_path: str, target_column: str) -> None:
25
+ """
26
+ Initialise un SvmTrainer avec la configuration et les paramètres
27
+ du parent CuMLTrainer.
28
+
29
+ :param config: Configuration globale du système.
30
+ :param data_path: Chemin vers le fichier de données.
31
+ :param target_column: Nom de la colonne cible.
32
+ """
33
+ super().__init__(config, data_path, target_column)
34
+
35
+ def _build_classifier(self) -> None:
36
+ """
37
+ Crée et configure un classifieur SVC selon les paramètres
38
+ spécifiés dans la configuration.
39
+
40
+ Utilise directement les paramètres de la configuration pour initialiser
41
+ le modèle avec les hyperparamètres appropriés (ex: kernel, C, gamma, etc.).
42
+ """
43
+ params = self.config.model.params or {}
44
+ self.classifier = SVC(**params)
45
+
46
+ def build_components(self) -> None:
47
+ """
48
+ Instancie le vectorizer, le classifieur SVC et le calculateur de métriques.
49
+
50
+ Cette méthode configure tous les composants nécessaires au fonctionnement
51
+ du trainer, en se basant sur les paramètres spécifiés dans la configuration.
52
+ """
53
+ # Récupération de la méthode et des paramètres de vectorisation
54
+ vector_method = self.config.vectorization.method
55
+ vector_params = self.config.vectorization.dict().get(vector_method, {})
56
+ self.vectorizer = CuMLTfidfVectorizer(**vector_params)
57
+
58
+ # Construction du classifieur
59
+ self._build_classifier()
60
+
61
+ # Initialisation du calculateur de métriques
62
+ self.metrics_calculator = DefaultMetricsCalculator()
63
+
64
+ def train(self) -> None:
65
+ """
66
+ Entraîne le classifieur SVC sur les données vectorisées.
67
+
68
+ Utilise l'implémentation fournie par la classe parente CuMLTrainer,
69
+ qui s'occupe du chargement des données, de la vectorisation et de l'entraînement.
70
+ """
71
+ super().train()
72
+
73
+ def evaluate(self) -> None:
74
+ """
75
+ Évalue le classifieur SVC et calcule les métriques appropriées.
76
+
77
+ Utilise l'implémentation fournie par la classe parente CuMLTrainer,
78
+ qui s'occupe du chargement des données, de la vectorisation, de la prédiction
79
+ et du calcul des métriques via le metrics_calculator.
80
+ """
81
+ super().evaluate()
82
+
83
+ def _get_binary_predictions(self, X: cp.ndarray) -> cp.ndarray:
84
+ """
85
+ Retourne les prédictions binaires du modèle SVC.
86
+
87
+ Cette méthode est spécifiquement utilisée pour obtenir des prédictions
88
+ binaires (0/1) à partir du modèle entraîné.
89
+
90
+ :param X: Matrice de caractéristiques au format cupy.ndarray.
91
+ :return: Vecteur de prédictions binaires.
92
+ """
93
+ X_prepared = self._prepare_input_for_predict(X)
94
+ return self.classifier.predict(X_prepared)
95
+
96
+ def _get_positive_probabilities(self, X: cp.ndarray) -> Optional[cp.ndarray]:
97
+ """
98
+ Retourne les probabilités de la classe positive si disponible.
99
+
100
+ Cette méthode est utilisée pour obtenir les probabilités d'appartenance
101
+ à la classe positive, utile notamment pour calculer l'AUC-ROC ou
102
+ pour des seuils de décision personnalisés.
103
+
104
+ :param X: Matrice de caractéristiques au format cupy.ndarray.
105
+ :return: Vecteur de probabilités ou None si non disponible.
106
+ """
107
+ if hasattr(self.classifier, 'predict_proba'):
108
+ X_prepared = self._prepare_input_for_predict(X)
109
+ return self.classifier.predict_proba(X_prepared)[:, 1]
110
+ return None
src/trainers/huggingface/huggingface_transformer_trainer.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # Fichier: trainers/huggingface/huggingface_transformer_trainer.py
3
+ # ============================================================
4
+
5
+ from typing import Optional
6
+ import cupy as cp
7
+ import cudf
8
+ import torch
9
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments
10
+
11
+ from base_trainer import BaseTrainer
12
+ from config import Config
13
+
14
+
15
+ class HuggingFaceTransformerTrainer(BaseTrainer):
16
+ """
17
+ Entraîneur spécifique Hugging Face, utilisant un tokenizer,
18
+ un modèle AutoModelForSequenceClassification et un HF Trainer.
19
+ Ne dépend pas d'un vectorizer cuML d'après l'UML.
20
+ """
21
+
22
+ def __init__(self, config: Config, data_path: str,
23
+ target_column: str) -> None:
24
+ """
25
+ Initialise un HuggingFaceTransformerTrainer avec la configuration
26
+ et les paramètres du parent BaseTrainer.
27
+
28
+ :param config: Configuration globale du système.
29
+ (La config.vectorizer n'est pas utilisée ici.)
30
+ :param data_path: Chemin vers le fichier de données.
31
+ :param target_column: Nom de la colonne cible dans vos données.
32
+ """
33
+ super().__init__(config, data_path, target_column)
34
+ self.tokenizer: Optional[AutoTokenizer] = None
35
+ self.model: Optional[AutoModelForSequenceClassification] = None
36
+ self.hf_trainer: Optional[Trainer] = None
37
+
38
+ def build_components(self) -> None:
39
+ """
40
+ Instancie le tokenizer et le modèle Hugging Face
41
+ AutoModelForSequenceClassification, puis crée un Trainer
42
+ avec des TrainingArguments par défaut.
43
+ """
44
+ model_name = self.config.model.params.get("pretrained_model_name",
45
+ "bert-base-uncased")
46
+ self.tokenizer = AutoTokenizer.from_pretrained(model_name)
47
+ self.model = AutoModelForSequenceClassification.from_pretrained(
48
+ model_name)
49
+ training_args = self._prepare_training_args()
50
+
51
+ # Le HF Trainer a besoin de datasets, qui sont construits
52
+ # dans le code de train/evaluate ou un data loader.
53
+ self.hf_trainer = Trainer(model=self.model,
54
+ args=training_args,
55
+ train_dataset=None,
56
+ eval_dataset=None)
57
+
58
+ def train(self) -> None:
59
+ """
60
+ Entraîne le modèle Hugging Face sur le jeu de données
61
+ (les datasets sont assignés au HF Trainer).
62
+ """
63
+ pass
64
+
65
+ def evaluate(self) -> None:
66
+ """
67
+ Évalue le modèle Hugging Face; la logique de calcul
68
+ des métriques est en partie assurée par le HF Trainer.
69
+ """
70
+ pass
71
+
72
+ def _create_torch_dataset(self, texts: cudf.Series,
73
+ labels: cp.ndarray) -> torch.utils.data.Dataset:
74
+ """
75
+ Convertit un cudf.Series de textes et un tableau cupy de labels
76
+ en un Dataset PyTorch.
77
+
78
+ :param texts: Série cudf contenant les textes.
79
+ :param labels: Vecteur cupy des labels (ex. classification binaire ou multiclasses).
80
+ :return: Un Dataset PyTorch utilisable par Trainer.
81
+ """
82
+ # Implémentation possible : tokenization + construction d'un dataset custom.
83
+ raise NotImplementedError(
84
+ "La méthode _create_torch_dataset est à implémenter selon vos besoins."
85
+ )
86
+
87
+ def _prepare_training_args(self) -> TrainingArguments:
88
+ """
89
+ Construit un objet TrainingArguments Hugging Face,
90
+ par exemple pour définir l'output_dir, le batch_size, etc.
91
+
92
+ :return: Instance de TrainingArguments configurée.
93
+ """
94
+ return TrainingArguments(output_dir="./results",
95
+ num_train_epochs=1,
96
+ per_device_train_batch_size=8,
97
+ per_device_eval_batch_size=8)
src/utilities/cuml_pyfunc_wrapper.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ===========================================
2
+ # Fichier: utilities/cuml_pyfunc_wrapper.py
3
+ # ===========================================
4
+
5
+ from typing import Any, Dict, Optional
6
+ import pandas as pd
7
+ import numpy as np
8
+
9
+ from interfaces.vectorizer import Vectorizer
10
+
11
+
12
+ class CuMLPyFuncWrapper:
13
+ """
14
+ Classe wrapper pour intégration de modèles cuML dans MLflow PyFunc,
15
+ permettant le chargement et l'inférence.
16
+ """
17
+
18
+ def __init__(self, vectorizer: Vectorizer, classifier: object) -> None:
19
+ """
20
+ Initialise le wrapper avec un vectorizer cuDF/cupy et le classifieur cuML.
21
+
22
+ :param vectorizer: Instance implémentant l'interface Vectorizer.
23
+ :param classifier: Modèle cuML déjà entraîné (ex. SVC, RandomForest, etc.).
24
+ """
25
+ self.vectorizer: Vectorizer = vectorizer
26
+ self.classifier: object = classifier
27
+
28
+ def load_context(self, context: Dict[str, Any]) -> None:
29
+ """
30
+ Chargé par MLflow PyFunc lors du rechargement du modèle,
31
+ par exemple pour initialiser l'environnement.
32
+
33
+ :param context: Contexte de chargement contenant d'éventuelles informations
34
+ sur l'environnement ou l'emplacement d'artefacts.
35
+ """
36
+ # Le diagramme UML n'indique pas de logique interne, donc on reste concis.
37
+ pass
38
+
39
+ def predict(self, context: Dict[str, Any],
40
+ model_input: pd.DataFrame) -> np.ndarray:
41
+ """
42
+ Fonction de prédiction, appelée par MLflow PyFunc.
43
+ Convertit éventuellement model_input en cudf, vectorise,
44
+ puis appelle le modèle cuML.
45
+
46
+ :param context: Contexte d'exécution éventuel (non défini par l'UML).
47
+ :param model_input: Données d'entrée sous forme de DataFrame pandas.
48
+ :return: Un vecteur numpy des prédictions.
49
+ """
50
+ # On ne fait aucune hypothèse supplémentaire sur la forme de model_input.
51
+ # On se contente de renvoyer un np.ndarray.
52
+ # Logique d'exemple : conversion en array et appel classifier.predict(...)
53
+ # A adapter selon la forme réelle du classifier.
54
+ raise NotImplementedError(
55
+ "La méthode 'predict' doit intégrer la logique de conversion et d'appel cuML."
56
+ )
uml.plantuml ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @startuml
2
+ !theme plain
3
+
4
+ skinparam classAttributeIconSize 0
5
+ skinparam defaultFontName Arial
6
+ skinparam class {
7
+ BackgroundColor PaleTurquoise
8
+ BorderColor DarkSlateGray
9
+ }
10
+ skinparam abstractClass {
11
+ BackgroundColor LightYellow
12
+ BorderColor DarkSlateGray
13
+ }
14
+ skinparam interface {
15
+ BackgroundColor White
16
+ BorderColor Black
17
+ }
18
+
19
+ ' ----------- Type ParamGrid -----------
20
+ class ParamGrid {
21
+ note as N1
22
+ Exemples de clés et valeurs :
23
+ - "C": [0.1, 1, 10, 100]
24
+ - "kernel": ["linear", "rbf"]
25
+ - "gamma":
26
+ low: 0.001
27
+ high: 0.1
28
+ log: true
29
+
30
+ Ce type sert à décrire la structure attendue pour le param_grid
31
+ dans les Optimizers.
32
+ end note
33
+ }
34
+
35
+ ' ----------- Interfaces -----------
36
+ package "Interfaces" {
37
+ interface MetricsCalculator {
38
+ + calculate_and_log(y_true: cp.ndarray, y_pred: cp.ndarray, prefix: str): dict
39
+ + calculate_and_log_multiclass(y_true: cp.ndarray, y_pred: cp.ndarray, prefix: str): dict
40
+ --
41
+ note bottom
42
+ Génère toujours : accuracy, f1, precision, recall, auc-roc
43
+ end note
44
+ }
45
+
46
+ interface Vectorizer {
47
+ + fit_transform(texts: cudf.Series): cp.ndarray | csr_matrix
48
+ + transform(texts: cudf.Series): cp.ndarray | csr_matrix
49
+ }
50
+
51
+ interface HyperparameterOptimizer {
52
+ + optimize(trainer: BaseTrainer, param_grid: ParamGrid): dict
53
+ --
54
+ note bottom
55
+ Renvoie un dict correspondant \nà la best config trouvée
56
+ ex: {"C": 1, "kernel": "linear", "gamma": 0.01}
57
+ end note
58
+ }
59
+ }
60
+
61
+
62
+ ' ----------- Base Classes -----------
63
+ package "Base Classes" {
64
+ abstract class BaseTrainer {
65
+ - config: Config
66
+ - classifier: object
67
+ - metrics_calculator: MetricsCalculator
68
+ --
69
+ + __init__(config: Config, data_path: str, target_column: str)
70
+ + build_components(): void
71
+ + train(): void
72
+ + evaluate(): void
73
+ + log_parameters_to_mlflow(): void
74
+ + optimize_if_needed(): void
75
+ -
76
+ _prepare_input_for_fit(X: cp.ndarray | csr_matrix): cp.ndarray | csr_matrix
77
+ -
78
+ _prepare_input_for_predict(X: cp.ndarray | csr_matrix): cp.ndarray | csr_matrix
79
+ -
80
+ _get_binary_predictions(X: cp.ndarray): cp.ndarray
81
+ -
82
+ _get_positive_probabilities(X: cp.ndarray): cp.ndarray | None
83
+ -
84
+ _get_label_dtype(): cp.dtype
85
+
86
+ note right
87
+ log_parameters_to_mlflow():
88
+ appelle la fonction singledispatch
89
+ get_relevant_params_for_logging(self).
90
+
91
+ optimize_if_needed():
92
+ Vérifie dans self.config \nsi un optimizer est défini, \npuis appelle optimize() \nsur ce dernier si besoin.
93
+ end note
94
+ }
95
+
96
+ abstract class CuMLTrainer extends BaseTrainer {
97
+ - vectorizer: Vectorizer
98
+ - classifier: cuML.Base
99
+ --
100
+ + build_components(): void
101
+ + train(): void
102
+ + evaluate(): void
103
+ -
104
+ _prepare_input_for_fit(X: cp.ndarray | csr_matrix): cp.ndarray
105
+ -
106
+ _prepare_input_for_predict(X: cp.ndarray | csr_matrix): cp.ndarray
107
+ }
108
+ }
109
+
110
+ ' ----------- Concrete Trainers (cuML) -----------
111
+ package "Concrete Trainers (cuML)" {
112
+ class SvmTrainer extends CuMLTrainer {
113
+ - classifier: SVC
114
+ --
115
+ + _build_classifier(): void
116
+ note bottom
117
+ SvmTrainer est affecté \npar les paramètres C, kernel, \net gamma (pour RBF).
118
+ end note
119
+ }
120
+
121
+ class RandomForestTrainer extends CuMLTrainer {
122
+ - classifier: RandomForestClassifier
123
+ --
124
+ + _build_classifier(): void
125
+ }
126
+
127
+ class LogisticRegressionTrainer extends CuMLTrainer {
128
+ - classifier: LogisticRegression
129
+ --
130
+ + _build_classifier(): void
131
+ }
132
+
133
+ class LinearRegressionTrainer extends CuMLTrainer {
134
+ - classifier: LinearRegression
135
+ --
136
+ + _build_classifier(): void
137
+ }
138
+ }
139
+
140
+ ' ----------- Concrete Trainers (Hugging Face) -----------
141
+ package "Concrete Trainers (Hugging Face)" {
142
+ class HuggingFaceTransformerTrainer extends BaseTrainer {
143
+ - tokenizer: AutoTokenizer
144
+ - model: AutoModelForSequenceClassification
145
+ - hf_trainer: Trainer
146
+ --
147
+ + build_components(): void
148
+ + train(): void
149
+ + evaluate(): void
150
+ -
151
+ _create_torch_dataset(texts: cudf.Series, labels: cp.ndarray): torch.utils.data.Dataset
152
+ -
153
+ _prepare_training_args(): TrainingArguments
154
+
155
+ note right
156
+ Ce trainer n'utilise pas
157
+ la config vectorizer
158
+ end note
159
+ }
160
+ }
161
+
162
+ ' ----------- Hyperparameter Optimizers -----------
163
+ package "Hyperparameter Optimizers" {
164
+ class OptunaOptimizer {
165
+ - study: optuna.study.Study
166
+ - objective: function
167
+ --
168
+ + optimize(trainer: BaseTrainer, param_grid: ParamGrid): dict
169
+ --
170
+ note bottom
171
+ Implementation:
172
+ 1) Crée/récupère un study Optuna.
173
+ 2) Définit l'objective (fonction de coût).
174
+ Ex: Utilise param_grid["C"] \npour suggérer \ntrial.suggest_float("C",...)
175
+ 3) Applique les hyperparams au trainer \n(e.g. trainer.classifier = SVC(**params)).
176
+ 4) study.optimize(..., n_trials=...)
177
+ 5) Retourne la meilleure config sous forme d'un dict
178
+ end note
179
+ }
180
+
181
+ class RayTuneOptimizer {
182
+ - param_space: dict
183
+ - search_alg: object
184
+ - scheduler: object
185
+ --
186
+ + optimize(trainer: BaseTrainer, param_grid: ParamGrid): dict
187
+ --
188
+ note bottom
189
+ Implementation:
190
+ 1) Convertit param_grid \nen param_space pour Ray Tune.
191
+ (ex: "C": tune.grid_search([...]))
192
+ 2) Lance tune.run(...).
193
+ 3) Utilise search_alg/scheduler.
194
+ 4) Retourne la meilleure config \nsous forme d'un dict
195
+ end note
196
+ }
197
+
198
+ OptunaOptimizer ..> HyperparameterOptimizer : «implements»
199
+ RayTuneOptimizer ..> HyperparameterOptimizer : «implements»
200
+ }
201
+
202
+ ' ----------- MLflow Integration -----------
203
+ package "MLflow Integration" {
204
+ class MLflowDecorator {
205
+ - experiment_name: str
206
+ - tracking_uri: str
207
+ --
208
+ + __init__(experiment_name: str, tracking_uri: str): void
209
+ + __call__(func: function): function
210
+ + _start_run(): void
211
+ + _log_params(params: dict): void
212
+ + _log_metrics(metrics: dict): void
213
+ + _log_artifacts(artifacts: dict): void
214
+ + _end_run(status: str): void
215
+ }
216
+ }
217
+
218
+ ' ----------- Utilities -----------
219
+ package "Utilities" {
220
+ class CuMLPyFuncWrapper {
221
+ - vectorizer: Vectorizer
222
+ - classifier: object
223
+ --
224
+ + load_context(context): void
225
+ + predict(context, model_input: pd.DataFrame): np.ndarray
226
+ }
227
+ }
228
+
229
+ ' ----------- Configuration -----------
230
+ package "Configuration" {
231
+ class Config <<PydanticModel>> {
232
+ + model: ModelConfig
233
+ + vectorization: VectorizationConfig
234
+ + data: DataConfig
235
+ + hyperparameters: HyperparameterConfig
236
+ }
237
+
238
+ class ModelConfig <<PydanticModel>> {
239
+ + type: str
240
+ + params: dict
241
+ }
242
+
243
+ class VectorizationConfig <<PydanticModel>> {
244
+ + method: str
245
+ + tfidf: dict
246
+ + bow: dict
247
+ }
248
+
249
+ class DataConfig <<PydanticModel>> {
250
+ + path: str
251
+ + target_column: str
252
+ }
253
+
254
+ class HyperparameterConfig <<PydanticModel>> {
255
+ + optimizer: str
256
+ + param_grid: dict
257
+ + n_trials: int
258
+ --
259
+ note bottom
260
+ Exemple de param_grid pour SVM:
261
+ \{
262
+ "C": [0.1, 1, 10, 100],
263
+ "kernel": ["linear", "rbf"],
264
+ "gamma": \{
265
+ "low": 0.001,
266
+ "high": 0.1,
267
+ "log": true
268
+ \}
269
+ \}
270
+ n_trials: 50
271
+ end note
272
+ }
273
+ Config <|-- ModelConfig
274
+ Config <|-- VectorizationConfig
275
+ Config <|-- DataConfig
276
+ Config <|-- HyperparameterConfig
277
+
278
+ note left of Config
279
+ Extrait YAML:
280
+ hyperparameters:
281
+ optimizer: "optuna"
282
+ param_grid:
283
+ C: [0.1, 1, 10, 100]
284
+ kernel:
285
+ - "linear"
286
+ - "rbf"
287
+ gamma:
288
+ low: 0.001
289
+ high: 0.1
290
+ log: true
291
+ n_trials: 50
292
+ =
293
+ Hydra -> DictConfig -> Config(Pydantic)
294
+ end note
295
+ }
296
+
297
+ ' ----------- singledispatch function -----------
298
+ package "Parameter Logging (singledispatch)" {
299
+ object get_relevant_params_for_logging <<Function>>
300
+ note bottom
301
+ @singledispatch
302
+ def get_relevant_params_for_logging(trainer: BaseTrainer) -> dict:
303
+ ...
304
+
305
+ @get_relevant_params_for_logging.register
306
+ def _(trainer: HuggingFaceTransformerTrainer) -> dict:
307
+ ...
308
+
309
+ @get_relevant_params_for_logging.register
310
+ def _(trainer: SvmTrainer) -> dict:
311
+ ...
312
+
313
+ etc.
314
+ end note
315
+ }
316
+
317
+ ' ----------- Relations -----------
318
+ BaseTrainer ..> MetricsCalculator : «uses»
319
+ BaseTrainer ..> HyperparameterOptimizer : «may use»
320
+ BaseTrainer ..> MLflowDecorator : «may be decorated by»
321
+ BaseTrainer ..> get_relevant_params_for_logging : «calls singledispatch function»
322
+
323
+ CuMLTrainer ..> cuML.Base : «uses»
324
+ CuMLTrainer ..> CuMLPyFuncWrapper : «for saving model»
325
+
326
+ HuggingFaceTransformerTrainer ..> AutoTokenizer : «uses»
327
+ HuggingFaceTransformerTrainer ..> AutoModelForSequenceClassification : «uses»
328
+ HuggingFaceTransformerTrainer ..> Trainer : «uses»
329
+ HuggingFaceTransformerTrainer ..> TrainingArguments : «uses»
330
+
331
+ MLflowDecorator ..> mlflow : «uses»
332
+
333
+ @enduml