NeuralNetworks 0.1.12__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
NeuralNetworks/MLP.py DELETED
@@ -1,591 +0,0 @@
1
- # NeuralNetworksBeta - Multi-Layer Perceptrons avec encodage Fourier
2
- # Copyright (C) 2025 Alexandre Brun
3
- # This program is free software: you can redistribute it and/or modify
4
- # it under the terms of the GNU General Public License as published by
5
- # the Free Software Foundation, either version 3 of the License, or
6
- # (at your option) any later version.
7
-
8
- from .Dependances import *
9
-
10
- class MLP():
11
- """
12
- Multi-Layer Perceptron (MLP) avec encodage optionnel Fourier (RFF),
13
- suivi automatique des pertes, visualisation et compilation PyTorch.
14
-
15
- Cette classe fournit :
16
- - Un MLP entièrement configurable (dimensions, normalisation, activation),
17
- - Option d'encodage Fourier (Random Fourier Features) sur les entrées,
18
- - Méthodes pour entraîner le réseau avec mini-batchs et AMP (Automatic Mixed Precision),
19
- - Visualisation de l'architecture via visualtorch,
20
- - Suivi et affichage de la perte d'entraînement,
21
- - Accès aux poids, biais et nombre de paramètres,
22
- - Compilation du modèle via `torch.compile` pour accélérer l'inférence,
23
- - Méthode `__call__` permettant l'utilisation directe comme une fonction (`y = net(x)`).
24
-
25
- Parameters
26
- ----------
27
- layers : list[int], optional
28
- Dimensions successives du réseau (entrée → couches cachées → sortie).
29
- Exemple : [in_features, hidden1, hidden2, ..., out_features].
30
- Default: [1, 1, 1]
31
- learning_rate : float, optional
32
- Taux d’apprentissage pour l’optimiseur.
33
- Default: 1e-3
34
- Fourier : bool, optional
35
- Si True, applique un encodage Fourier gaussien (RFF) sur les entrées.
36
- Default: True
37
- optim : str, optional
38
- Nom de l’optimiseur à utiliser (doit exister dans `optim_list`).
39
- Default: "ADAM"
40
- crit : str, optional
41
- Fonction de perte à utiliser (doit exister dans `crit_list`).
42
- Default: "MSE"
43
- norm : str, optional
44
- Type de normalisation / activation pour les couches cachées (ex: "Relu").
45
- Default: "Relu"
46
- name : str, optional
47
- Nom du réseau pour identification ou affichage.
48
- Default: "Net"
49
- Iscompiled : bool, optional
50
- Si True, compile le modèle via `torch.compile` pour accélérer l’inférence.
51
- Default: True
52
-
53
- Attributes
54
- ----------
55
- losses : list[torch.Tensor]
56
- Historique des pertes cumulées lors de l'entraînement.
57
- layers : list[int]
58
- Dimensions du réseau, ajustées si encodage Fourier actif.
59
- encoding : nn.Module
60
- Module appliquant l'encodage des entrées (RFF ou identity).
61
- norm : nn.Module
62
- Normalisation ou activation utilisée dans les couches cachées.
63
- crit : nn.Module
64
- Fonction de perte PyTorch sur le device spécifié.
65
- model : nn.Sequential
66
- MLP complet construit dynamiquement.
67
- optim : torch.optim.Optimizer
68
- Optimiseur associé au MLP.
69
- name : str
70
- Nom du réseau.
71
-
72
- Methods
73
- -------
74
- __init__(...)
75
- Initialise le réseau, configure l’encodage, la fonction de perte et l’optimiseur.
76
- __repr__()
77
- Affiche un schéma visuel du MLP et ses dimensions (avec compression si nécessaire).
78
- __call__(x)
79
- Applique l’encodage et le MLP sur un input x, retourne la prédiction en ndarray.
80
- Create_MLP(layers)
81
- Construit un nn.Sequential avec les couches linéaires, activations et normalisations.
82
- plot(inputs, img_array)
83
- Affiche l’image originale, l’image prédite et la courbe des pertes.
84
- train(inputs, outputs, num_epochs=1500, batch_size=1024)
85
- Entraîne le MLP avec mini-batchs et AMP, stocke les pertes.
86
- params()
87
- Retourne tous les poids du MLP sous forme de liste d’ndarray.
88
- neurons()
89
- Retourne tous les biais du MLP sous forme de liste d’ndarray.
90
- nb_params()
91
- Calcule le nombre total de paramètres (poids uniquement) du réseau.
92
-
93
- Notes
94
- -----
95
- - La classe supporte un entraînement sur GPU via `device`.
96
- - Les fonctions de visualisation utilisent matplotlib et visualtorch.
97
- - Les sorties sont compatibles avec des images normalisées entre 0 et 1.
98
- - Le suivi des pertes permet d’afficher l’évolution du training loss.
99
- """
100
-
101
- def __init__(self, layers=[1,1,1], learning_rate=1e-3, Fourier=True,
102
- optim="Adam", crit="MSE", norm="Relu",
103
- name="Net", Iscompiled=False):
104
- """
105
- Initialise un réseau MLP avec options avancées : encodage Fourier,
106
- normalisation, choix d’optimiseur et de fonction de perte, et compilation.
107
-
108
- Parameters
109
- ----------
110
- layers : list[int], optional
111
- Dimensions successives du réseau (entrée → couches cachées → sortie).
112
- Default: [1, 1, 1]
113
- learning_rate : float, optional
114
- Taux d’apprentissage pour l’optimiseur (default: 1e-3).
115
- Fourier : bool, optional
116
- Si True, applique un encodage RFF (Random Fourier Features) sur les entrées.
117
- Default: True
118
- optim : str, optional
119
- Nom de l’optimiseur à utiliser (doit être présent dans `optim_list`).
120
- Default: "Adam"
121
- crit : str, optional
122
- Nom de la fonction de perte à utiliser (doit être présent dans `crit_list`).
123
- Default: "MSE"
124
- norm : str, optional
125
- Type de normalisation / activation à appliquer entre les couches cachées.
126
- Default: "Relu"
127
- name : str, optional
128
- Nom du réseau (pour identification et affichage).
129
- Default: "Net"
130
- Iscompiled : bool, optional
131
- Si True, compile le modèle avec `torch.compile` pour accélérer l’inférence.
132
- Default: True
133
-
134
- Attributes
135
- ----------
136
- losses : list
137
- Historique des pertes durant l’entraînement.
138
- layers : list[int]
139
- Dimensions du réseau après ajustement pour encodage Fourier.
140
- encoding : nn.Module
141
- Module appliquant l’encodage des entrées (RFF ou identité).
142
- norm : nn.Module
143
- Normalisation / activation utilisée entre les couches cachées.
144
- crit : nn.Module
145
- Fonction de perte PyTorch sur GPU.
146
- model : nn.Sequential
147
- MLP complet construit dynamiquement.
148
- optim : torch.optim.Optimizer
149
- Optimiseur associé au MLP.
150
- name : str
151
- Nom du réseau.
152
- """
153
-
154
- super().__init__()
155
-
156
- # --- Initialisation des attributs de base ---
157
- self.losses, self.layers, self.Fourier = [], layers.copy(), Fourier
158
- self.name = name
159
-
160
- # --- Encodage Fourier (RFF) ou passthrough ---
161
- if self.Fourier:
162
- self.encoding = rff.layers.GaussianEncoding(
163
- sigma=10.0,
164
- input_size=self.layers[0],
165
- encoded_size=self.layers[1]
166
- ).to(device)
167
- self.layers[0] = self.layers[1] * 2 # chaque entrée est doublée après encodage
168
- else:
169
- self.encoding = nn.Identity().to(device) # passthrough si pas de Fourier
170
-
171
- # --- Sélection du normalisateur / activation ---
172
- self.norm = norm_list.get(norm)
173
- if self.norm is None:
174
- print("")
175
- print (f"{norm} n'est pas reconnu")
176
- self.norm = norm_list.get("Relu")
177
- print (f"Retour au paramètre par défaut: 'Relu'")
178
-
179
- # --- Fonction de perte ---
180
- self.crit = crit_list.get(crit)
181
- if self.crit is None:
182
- print("")
183
- print (f"{crit} n'est pas reconnu")
184
- self.crit = crit_list.get("MSE")
185
- print (f"Retour au paramètre par défaut: 'MSE'")
186
- # --- Construction du MLP ---
187
- self.model = self.Create_MLP(self.layers)
188
-
189
- # --- Sélection de l’optimiseur ---
190
- self.optim = optim_list(self, learning_rate).get(optim)
191
- if self.optim is None:
192
- print("")
193
- print (f"{optim} n'est pas reconnu")
194
- self.optim = optim_list(self, learning_rate).get("Adam")
195
- print (f"Retour au paramètre par défaut: 'Adam'")
196
-
197
- # --- Compilation optionnelle du modèle pour accélérer l’inférence ---
198
- if not has_gcc():
199
- Iscompiled = False
200
-
201
- if Iscompiled:
202
- self.model = torch.compile(
203
- self.model,
204
- mode="max-autotune",
205
- fullgraph=True,
206
- dynamic=True
207
- )
208
-
209
- # --- Envoi du modèle sur le device GPU / CPU ---
210
- self.model.to(device)
211
-
212
- def __repr__(self):
213
- """
214
- Génère un aperçu visuel du MLP et affiche ses dimensions.
215
-
216
- Cette méthode :
217
- - crée une version éventuellement "compressée" des dimensions du réseau
218
- (utile lorsque certaines couches dépassent 30 neurones, afin de
219
- conserver une visualisation lisible),
220
- - utilise `visualtorch.graph_view` pour afficher un schéma du MLP,
221
- - imprime la liste réelle des dimensions du réseau,
222
- - retourne une chaîne indiquant si un redimensionnement a été appliqué.
223
-
224
- Notes
225
- -----
226
- - Le redimensionnement ne modifie pas le MLP réel. Il ne sert qu'à
227
- améliorer la lisibilité du graphe affiché.
228
- - Si Fourier Features sont activées, seule la première dimension est
229
- recalculée en conséquence.
230
- """
231
-
232
- # Si les couches sont trop grandes, on crée une version réduite
233
- if max(self.layers) > 30:
234
- # Mise à l’échelle proportionnelle sur une base max 32
235
- fakelayers = [int(32 * layer / max(self.layers)) for layer in self.layers]
236
-
237
- # Ajustement de la couche d’entrée si encodage Fourier
238
- fakelayers[0] = (
239
- int(32 * self.layers[0] / max(self.layers))
240
- if self.Fourier else self.layers[0]
241
- )
242
-
243
- # La couche de sortie reste intacte
244
- fakelayers[-1] = self.layers[-1]
245
-
246
- else:
247
- # Sinon, on garde les dimensions réelles
248
- fakelayers = self.layers
249
-
250
- # Affichage console des dimensions réelles
251
- print("Tailles réelles :")
252
- print(str(self.layers))
253
- print("")
254
- print("Tailles affichées :")
255
- print(str(fakelayers))
256
-
257
- # --- Visualisation du MLP ---
258
- fig, ax = plt.subplots(figsize=(12, 6))
259
- ax.axis("off")
260
-
261
- # Utilisation de visualtorch pour tracer l’architecture
262
- ax.imshow(
263
- visualtorch.graph_view(
264
- self.Create_MLP(fakelayers),
265
- (1, fakelayers[0]),
266
- ellipsize_after=34,
267
- background_fill=(0, 0, 0, 0),
268
- opacity=255
269
- )
270
- )
271
-
272
- plt.show()
273
- return ""
274
-
275
- def __call__(self, x):
276
- """
277
- Effectue une inférence complète : encodage éventuel, passage
278
- dans le MLP, puis retour en numpy.
279
-
280
- Cette méthode permet d’utiliser l’objet comme une fonction
281
- directement : `y = net(x)`.
282
-
283
- Paramètres
284
- ----------
285
- x : array-like
286
- Entrée(s) à prédire. Peut être un tableau numpy, une liste,
287
- ou déjà un tenseur compatible.
288
-
289
- Returns
290
- -------
291
- np.ndarray
292
- Sortie du MLP après encodage et propagation avant,
293
- convertie en tableau numpy sur CPU.
294
- """
295
-
296
- # Inférence sans calcul de gradient (plus rapide et évite la construction du graphe)
297
- with torch.no_grad():
298
- return self.model(
299
- self.encoding(tensorise(x))
300
- ).cpu().numpy()
301
-
302
- def params(self):
303
- """
304
- Retourne la liste de tous les poids (weights) du MLP.
305
-
306
- Cette fonction extrait uniquement les matrices de poids des couches
307
- linéaires, en ignorant les biais.
308
- Dans un `nn.Linear`, `parameters()` renvoie dans l'ordre :
309
- - les poids (indice pair),
310
- - les biais (indice impair).
311
-
312
- La méthode :
313
- - parcourt les paramètres du réseau,
314
- - sélectionne uniquement ceux correspondant aux poids,
315
- - sépare chaque ligne de la matrice de poids,
316
- - convertit chaque ligne en ndarray CPU détaché.
317
-
318
- Returns
319
- -------
320
- list[np.ndarray]
321
- Liste contenant chaque ligne des matrices de poids
322
- (chaque élément est un vecteur numpy).
323
- """
324
-
325
- list_weights = []
326
- params = list(self.model.parameters())
327
-
328
- # Indices pairs → matrices de poids (indices impairs = biais)
329
- for i in rglen(params):
330
- if i % 2 == 0:
331
- weights = list(params[i]) # Chaque élément = ligne de la matrice W
332
- for j in rglen(weights):
333
- # On convertit ligne par ligne en numpy pour inspection externe
334
- list_weights.append(weights[j].detach().cpu().numpy())
335
-
336
- return list_weights
337
-
338
- def nb_params(self):
339
- """
340
- Calcule le nombre total de paramètres (poids) du MLP.
341
-
342
- Cette méthode parcourt les paramètres du modèle et ne compte
343
- que les **poids** des couches linéaires.
344
- Dans un nn.Linear, `parameters()` renvoie dans l'ordre :
345
- - les poids (indice pair),
346
- - puis les biais (indice impair).
347
- On ignore donc les biais dans ce comptage.
348
-
349
- Returns
350
- -------
351
- int
352
- Nombre total de paramètres pondérés (weights) dans le MLP.
353
- """
354
-
355
- sum = 0
356
- params = list(self.model.parameters())
357
-
358
- # Les indices pairs correspondent aux matrices de poids
359
- for i in rglen(params):
360
- if i % 2 == 0:
361
- weights = list(params[i])
362
- # On additionne la longueur de chaque ligne de la matrice de poids
363
- for j in rglen(weights):
364
- sum += len(weights[j])
365
-
366
- return sum
367
-
368
- def neurons(self):
369
- """
370
- Extrait l'ensemble des neurones (poids) du MLP couche par couche.
371
-
372
- Cette méthode parcourt les paramètres du modèle et récupère
373
- uniquement les poids associés aux biais des couches linéaires.
374
- Dans un nn.Linear, `parameters()` renvoie successivement :
375
- - les poids (weights),
376
- - puis les biais (bias).
377
- Les indices impairs correspondent donc aux biais.
378
-
379
- Returns
380
- -------
381
- list of ndarray
382
- Liste contenant chaque neurone (chaque valeur de biais),
383
- converti en array NumPy sur CPU.
384
- """
385
-
386
- list_neurons = []
387
- params = list(self.model.parameters())
388
-
389
- # Parcours des paramètres et sélection des biais (indices impairs)
390
- for i in rglen(params):
391
- if i % 2 == 1: # Les biais des nn.Linear
392
- neurons = list(params[i])
393
- # Extraction individuelle de chaque neurone
394
- for j in rglen(neurons):
395
- list_neurons.append(neurons[j].detach().cpu().numpy())
396
-
397
- return list_neurons
398
-
399
- def Create_MLP(self, layers):
400
- """
401
- Construit un Multi-Layer Perceptron (MLP) standard composé :
402
- - d'une succession de couches Linéaires,
403
- - suivies d'une normalisation (self.norm) après chaque couche cachée,
404
- - et d'une activation Sigmoid sur la couche de sortie.
405
-
406
- Parameters
407
- ----------
408
- layers : list[int]
409
- Liste des dimensions successives du réseau.
410
- Exemple : [in_features, hidden1, hidden2, ..., out_features]
411
-
412
- Returns
413
- -------
414
- nn.Sequential
415
- Le MLP complet sous forme de séquence PyTorch.
416
-
417
- Notes
418
- -----
419
- - La couche finale applique systématiquement une Sigmoid, adaptée à des
420
- sorties dans [0, 1].
421
- """
422
-
423
- layer_list = []
424
-
425
- # Ajout des couches cachées : Linear → Normalisation
426
- # (pour chaque couple consecutive layers[k] → layers[k+1], sauf la dernière)
427
- for k in range(len(layers) - 2):
428
- layer_list.extend([
429
- nn.Linear(layers[k], layers[k+1]),
430
- self.norm
431
- ])
432
-
433
- # Ajout de la couche finale : Linear → Sigmoid
434
- layer_list.extend([
435
- nn.Linear(layers[-2], layers[-1]),
436
- nn.Sigmoid()
437
- ])
438
-
439
- return nn.Sequential(*layer_list)
440
-
441
- def plot(self, img_array, inputs):
442
- """
443
- Affiche côte à côte :
444
- - l’image originale,
445
- - l’image prédite par le MLP,
446
- - l’évolution de la fonction de perte (loss) au cours de l’entraînement.
447
-
448
- Parameters
449
- ----------
450
- img_array : np.ndarray
451
- Image originale sous forme de tableau (H, W, 3) utilisée comme référence.
452
- inputs : array-like or torch.Tensor
453
- Tableau des coordonnées (ou features) servant d’entrée au réseau.
454
- Doit correspondre à la grille permettant de reconstruire l’image.
455
-
456
- Notes
457
- -----
458
- Cette méthode :
459
- - tensorise les entrées puis les encode avant passage dans le MLP,
460
- - reshape la sortie du modèle pour retrouver la forme (H, W, 3),
461
- - trace également la courbe de pertes stockée dans `self.losses`.
462
- """
463
-
464
- # Conversion des inputs en tenseur + récupération du nombre d'échantillons
465
- inputs, n_samples = tensorise(inputs), inputs.size(0)
466
-
467
- # Dimensions de l'image originale
468
- h, w = img_array.shape[:2]
469
-
470
- # Figure principale avec 3 panneaux : original / prédiction / loss
471
- fig = plt.figure(figsize=(15, 5))
472
- gs = GridSpec(1, 3, figure=fig)
473
-
474
- # --- Image originale ---
475
- ax_orig = fig.add_subplot(gs[0,0])
476
- ax_orig.axis('off')
477
- ax_orig.set_title("Original Image")
478
- ax_orig.imshow(img_array)
479
-
480
- # --- Image prédite ---
481
- ax = fig.add_subplot(gs[0,1])
482
- ax.axis('off')
483
- ax.set_title("Predicted Image")
484
- # Prédiction → CPU → numpy → reshape en (H, W, 3)
485
- ax.imshow(
486
- self.model(self.encoding(inputs))
487
- .cpu()
488
- .detach()
489
- .numpy()
490
- .reshape(h, w, 3)
491
- )
492
-
493
- # --- Courbe de loss ---
494
- los = fig.add_subplot(gs[0,2])
495
- los.set_title("Loss")
496
-
497
- # Axe X = 1..N
498
- los.plot(
499
- np.linspace(1, len(self.losses), len(self.losses)),
500
- [loss.item() for loss in self.losses]
501
- )
502
- if len(self.losses) ==1:
503
- lenlosses = 2
504
- else:
505
- lenlosses = len(self.losses)
506
- los.set_xlim(1, lenlosses)
507
-
508
- # Évite un ylim min = 0 pile si les pertes sont trop faibles
509
- maxarray = [0.00000001] + [loss.item() for loss in self.losses]
510
- los.set_ylim(0, max(maxarray))
511
-
512
- # Rafraîchissement non bloquant
513
- fig.canvas.draw_idle()
514
- plt.tight_layout()
515
- plt.ion()
516
- plt.show()
517
-
518
- def train(self, inputs, outputs, num_epochs=1500, batch_size=1024):
519
- """
520
- Entraîne le MLP sur des paires (inputs → outputs) en utilisant un
521
- schéma de mini-batchs et l'AMP (Automatic Mixed Precision).
522
-
523
- Parameters
524
- ----------
525
- inputs : array-like or tensor
526
- Données d'entrée du réseau, de shape (N, input_dim).
527
- outputs : array-like or tensor
528
- Cibles associées, de shape (N, output_dim).
529
- num_epochs : int, optional
530
- Nombre total d'époques d'entraînement (default: 1500).
531
- batch_size : int, optional
532
- Taille des mini-batchs utilisés à chaque itération (default: 1024).
533
-
534
- Notes
535
- -----
536
- - Utilise torch.amp.autocast + GradScaler pour un entraînement accéléré en FP16.
537
- - Les pertes par époque sont stockées dans `self.losses`.
538
- - Le réseau doit posséder :
539
- * self.model : module PyTorch (MLP)
540
- * self.encoding() : encodage éventuel (Fourier features)
541
- * self.crit : fonction de perte
542
- * self.optim : optimiseur
543
- """
544
-
545
- # --- Conversion en tensors et récupération du nombre d'échantillons ---
546
- inputs, outputs, n_samples = tensorise(inputs).to(device), tensorise(outputs).to(device), inputs.size(0)
547
- self.model = self.model.to(device)
548
-
549
- # --- Initialisation du scaler pour l'entraînement en précision mixte ---
550
- dev = str(device)
551
- scaler = GradScaler(dev)
552
-
553
- # --- Boucle principale sur les époques ---
554
- for epoch in tqdm(range(num_epochs), desc="train epoch"):
555
- # Génération d'un ordre aléatoire des indices
556
- perm = torch.randperm(n_samples, device=device)
557
- epoch_loss = 0.0
558
-
559
- # --- Parcours des mini-batchs ---
560
- for i in range(0, n_samples, batch_size):
561
- idx = perm[i:i+batch_size]
562
-
563
- # Fonction interne calculant la perte et les gradients
564
- def closure():
565
- self.optim.zero_grad(set_to_none=True)
566
- with autocast(dev): # AMP
567
- loss = self.crit(self.model(self.encoding(inputs[idx])),outputs[idx])
568
- scaler.scale(loss).backward()
569
- return loss
570
-
571
- # Calcul de la perte et mise à jour des poids
572
- loss = closure()
573
- scaler.step(self.optim)
574
- scaler.update()
575
-
576
- # Accumulation de la perte pour l'époque
577
- epoch_loss += loss
578
-
579
- # --- Stockage de la perte de l'époque ---
580
- self.losses.append(epoch_loss)
581
-
582
- MLP.__init__.help = fPrintDoc(MLP.__init__)
583
- MLP.__repr__.help = fPrintDoc(MLP.__repr__)
584
- MLP.__call__.help = fPrintDoc(MLP.__call__)
585
- MLP.help = fPrintDoc(MLP)
586
- MLP.params.help = fPrintDoc(MLP.params)
587
- MLP.nb_params.help = fPrintDoc(MLP.nb_params)
588
- MLP.neurons.help = fPrintDoc(MLP.neurons)
589
- MLP.Create_MLP.help = fPrintDoc(MLP.Create_MLP)
590
- MLP.plot.help = fPrintDoc(MLP.plot)
591
- MLP.train.help = fPrintDoc(MLP.train)