NeuralNetworks 0.1.11__py3-none-any.whl → 0.2.0__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/Dependances.py +2 -14
- NeuralNetworks/Latent.py +51 -0
- NeuralNetworks/MLP.py +171 -161
- NeuralNetworks/__init__.py +7 -8
- NeuralNetworks/tools/AirfRANS.py +36 -0
- NeuralNetworks/tools/MNIST.py +118 -0
- NeuralNetworks/tools/VKI-LS59.py +7 -0
- NeuralNetworks/tools/image.py +249 -0
- neuralnetworks-0.2.0.dist-info/METADATA +190 -0
- neuralnetworks-0.2.0.dist-info/RECORD +13 -0
- NeuralNetworks/Image.py +0 -105
- NeuralNetworks/Plot.py +0 -324
- neuralnetworks-0.1.11.dist-info/METADATA +0 -146
- neuralnetworks-0.1.11.dist-info/RECORD +0 -10
- {neuralnetworks-0.1.11.dist-info → neuralnetworks-0.2.0.dist-info}/WHEEL +0 -0
- {neuralnetworks-0.1.11.dist-info → neuralnetworks-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {neuralnetworks-0.1.11.dist-info → neuralnetworks-0.2.0.dist-info}/top_level.txt +0 -0
NeuralNetworks/Dependances.py
CHANGED
|
@@ -83,7 +83,7 @@ def get_best_device():
|
|
|
83
83
|
if os_name == "darwin":
|
|
84
84
|
if torch.backends.mps.is_available():
|
|
85
85
|
return torch.device("mps")
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
# =========== WINDOWS ===========
|
|
88
88
|
if os_name == "windows":
|
|
89
89
|
# 1) CUDA
|
|
@@ -236,19 +236,7 @@ def optim_list(self, learning_rate):
|
|
|
236
236
|
"Rprop": optim.Rprop(self.model.parameters(), lr=learning_rate),
|
|
237
237
|
"SGD": optim.SGD(self.model.parameters(), lr=learning_rate)
|
|
238
238
|
}
|
|
239
|
-
|
|
240
|
-
"Adadelta",
|
|
241
|
-
"Adafactor",
|
|
242
|
-
"Adam",
|
|
243
|
-
"AdamW",
|
|
244
|
-
"Adamax",
|
|
245
|
-
"ASGD",
|
|
246
|
-
"NAdam",
|
|
247
|
-
"RAdam",
|
|
248
|
-
"RMSprop",
|
|
249
|
-
"Rprop",
|
|
250
|
-
"SGD"
|
|
251
|
-
]
|
|
239
|
+
|
|
252
240
|
optims = lambda: print("""
|
|
253
241
|
"Adadelta"
|
|
254
242
|
"Adafactor"
|
NeuralNetworks/Latent.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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 Latent(nn.Module):
|
|
11
|
+
def __init__(self, insize, outsize):
|
|
12
|
+
super().__init__()
|
|
13
|
+
|
|
14
|
+
self.insize = insize
|
|
15
|
+
self.outsize = outsize
|
|
16
|
+
|
|
17
|
+
# Start latent conv channels
|
|
18
|
+
channels = 128
|
|
19
|
+
|
|
20
|
+
# ----- Encoder -----
|
|
21
|
+
layers = []
|
|
22
|
+
for k in range(5):
|
|
23
|
+
layers.append(nn.Conv2d(channels, channels // 2, kernel_size=3, padding=1))
|
|
24
|
+
channels = channels // 2
|
|
25
|
+
layers.append(nn.BatchNorm2d(channels))
|
|
26
|
+
layers.append(nn.ReLU(inplace=True))
|
|
27
|
+
layers.append(nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
|
|
28
|
+
|
|
29
|
+
layers.append(nn.Flatten())
|
|
30
|
+
layers.append(nn.Linear(channels * 1 * 1, outsize)) # adjust if spatial dims not 1x1
|
|
31
|
+
|
|
32
|
+
self.Emodel = nn.Sequential(*layers).to(device)
|
|
33
|
+
|
|
34
|
+
# ----- Decoder -----
|
|
35
|
+
layers = []
|
|
36
|
+
layers.append(nn.Linear(outsize, channels)) # output same number of channels
|
|
37
|
+
layers.append(nn.Unflatten(1, (channels, 1, 1)))
|
|
38
|
+
|
|
39
|
+
for k in range(5):
|
|
40
|
+
layers.append(nn.ConvTranspose2d(channels, channels * 2, kernel_size=3, stride=2, padding=1, output_padding=1))
|
|
41
|
+
channels = channels * 2
|
|
42
|
+
layers.append(nn.BatchNorm2d(channels))
|
|
43
|
+
layers.append(nn.ReLU(inplace=True))
|
|
44
|
+
|
|
45
|
+
self.Dmodel = nn.Sequential(*layers).to(device)
|
|
46
|
+
|
|
47
|
+
def encode(self, image):
|
|
48
|
+
return self.Emodel(image)
|
|
49
|
+
|
|
50
|
+
def decode(self, vector):
|
|
51
|
+
return self.Dmodel(vector)
|
NeuralNetworks/MLP.py
CHANGED
|
@@ -28,12 +28,14 @@ class MLP():
|
|
|
28
28
|
Dimensions successives du réseau (entrée → couches cachées → sortie).
|
|
29
29
|
Exemple : [in_features, hidden1, hidden2, ..., out_features].
|
|
30
30
|
Default: [1, 1, 1]
|
|
31
|
-
|
|
32
|
-
Taux d’apprentissage pour l’optimiseur.
|
|
31
|
+
init_lr : float, optional
|
|
32
|
+
Taux d’apprentissage initial pour l’optimiseur.
|
|
33
33
|
Default: 1e-3
|
|
34
|
-
Fourier :
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
Fourier : list[float] or None, optional
|
|
35
|
+
Liste de valeurs sigma pour appliquer plusieurs encodages RFF
|
|
36
|
+
(Random Fourier Features).
|
|
37
|
+
Si None : aucune transformation, l’entrée est passée via nn.Identity().
|
|
38
|
+
Chaque sigma ajoute un encodage distinct et double la dimension d’entrée.
|
|
37
39
|
optim : str, optional
|
|
38
40
|
Nom de l’optimiseur à utiliser (doit exister dans `optim_list`).
|
|
39
41
|
Default: "ADAM"
|
|
@@ -56,8 +58,9 @@ class MLP():
|
|
|
56
58
|
Historique des pertes cumulées lors de l'entraînement.
|
|
57
59
|
layers : list[int]
|
|
58
60
|
Dimensions du réseau, ajustées si encodage Fourier actif.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
encodings : list[nn.Module]
|
|
62
|
+
Liste de modules RFF GaussianEncoding ou Identity appliqués aux entrées.
|
|
63
|
+
Un module par valeur sigma dans `Fourier`.
|
|
61
64
|
norm : nn.Module
|
|
62
65
|
Normalisation ou activation utilisée dans les couches cachées.
|
|
63
66
|
crit : nn.Module
|
|
@@ -98,57 +101,69 @@ class MLP():
|
|
|
98
101
|
- Le suivi des pertes permet d’afficher l’évolution du training loss.
|
|
99
102
|
"""
|
|
100
103
|
|
|
101
|
-
def __init__(self, layers=[1,1,1],
|
|
104
|
+
def __init__(self, layers=[1,1,1], init_lr=1e-3, Fourier=None,
|
|
102
105
|
optim="Adam", crit="MSE", norm="Relu",
|
|
103
106
|
name="Net", Iscompiled=False):
|
|
104
107
|
"""
|
|
105
|
-
Initialise un réseau MLP avec
|
|
106
|
-
|
|
108
|
+
Initialise un réseau MLP flexible avec support multi-encodage Fourier,
|
|
109
|
+
choix d’activation, perte, optimiseur, et compilation optionnelle.
|
|
107
110
|
|
|
108
111
|
Parameters
|
|
109
112
|
----------
|
|
110
113
|
layers : list[int], optional
|
|
111
114
|
Dimensions successives du réseau (entrée → couches cachées → sortie).
|
|
115
|
+
Le premier élément est utilisé comme input_size avant encodage Fourier.
|
|
112
116
|
Default: [1, 1, 1]
|
|
113
|
-
|
|
114
|
-
Taux d’apprentissage pour l’optimiseur
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
init_lr : float, optional
|
|
118
|
+
Taux d’apprentissage initial pour l’optimiseur.
|
|
119
|
+
Default: 1e-3
|
|
120
|
+
Fourier : list[float] or None, optional
|
|
121
|
+
Liste de valeurs sigma pour appliquer plusieurs encodages RFF
|
|
122
|
+
(Random Fourier Features).
|
|
123
|
+
Si None : aucune transformation, l’entrée est passée via nn.Identity().
|
|
124
|
+
Chaque sigma ajoute un encodage distinct et double la dimension d’entrée.
|
|
118
125
|
optim : str, optional
|
|
119
|
-
Nom de l’optimiseur
|
|
126
|
+
Nom de l’optimiseur (doit appartenir à `optim_list(self, init_lr)`).
|
|
120
127
|
Default: "Adam"
|
|
121
128
|
crit : str, optional
|
|
122
|
-
Nom de la fonction de perte
|
|
129
|
+
Nom de la fonction de perte (doit appartenir à `crit_list`).
|
|
123
130
|
Default: "MSE"
|
|
124
131
|
norm : str, optional
|
|
125
|
-
|
|
132
|
+
Nom de la fonction d’activation entre couches cachées
|
|
133
|
+
(doit appartenir à `norm_list`).
|
|
126
134
|
Default: "Relu"
|
|
127
135
|
name : str, optional
|
|
128
|
-
Nom du
|
|
136
|
+
Nom du modèle, utilisé pour l’identification.
|
|
129
137
|
Default: "Net"
|
|
130
138
|
Iscompiled : bool, optional
|
|
131
|
-
Si True, compile le
|
|
132
|
-
|
|
139
|
+
Si True, compile le MLP avec `torch.compile` pour accélérer l’inférence.
|
|
140
|
+
Si GCC est absent, la compilation est automatiquement désactivée.
|
|
141
|
+
Default: False
|
|
133
142
|
|
|
134
143
|
Attributes
|
|
135
144
|
----------
|
|
136
145
|
losses : list
|
|
137
|
-
Historique des pertes
|
|
146
|
+
Historique des pertes pendant l’entraînement.
|
|
138
147
|
layers : list[int]
|
|
139
|
-
Dimensions du réseau
|
|
140
|
-
|
|
141
|
-
|
|
148
|
+
Dimensions du réseau, modifiées si un encodage Fourier est appliqué
|
|
149
|
+
(l’entrée devient 2 * encoded_size).
|
|
150
|
+
encodings : list[nn.Module]
|
|
151
|
+
Liste de modules RFF GaussianEncoding ou Identity appliqués aux entrées.
|
|
152
|
+
Un module par valeur sigma dans `Fourier`.
|
|
142
153
|
norm : nn.Module
|
|
143
|
-
|
|
154
|
+
Fonction d’activation utilisée dans les couches cachées.
|
|
144
155
|
crit : nn.Module
|
|
145
|
-
Fonction de perte PyTorch
|
|
156
|
+
Fonction de perte PyTorch.
|
|
146
157
|
model : nn.Sequential
|
|
147
|
-
MLP
|
|
158
|
+
Réseau MLP construit dynamiquement via `Create_MLP()`.
|
|
148
159
|
optim : torch.optim.Optimizer
|
|
149
160
|
Optimiseur associé au MLP.
|
|
150
161
|
name : str
|
|
151
162
|
Nom du réseau.
|
|
163
|
+
f : nn.Linear
|
|
164
|
+
Couche linéaire appliquée après le MLP, prenant en entrée
|
|
165
|
+
(nb_encodings * layers[-1]) et renvoyant layers[-1].
|
|
166
|
+
Utilisée pour agréger les sorties des différents encodages.
|
|
152
167
|
"""
|
|
153
168
|
|
|
154
169
|
super().__init__()
|
|
@@ -158,16 +173,18 @@ class MLP():
|
|
|
158
173
|
self.name = name
|
|
159
174
|
|
|
160
175
|
# --- Encodage Fourier (RFF) ou passthrough ---
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
|
176
|
+
self.encodings = []
|
|
177
|
+
if self.Fourier is None:
|
|
178
|
+
self.encodings.append(nn.Identity().to(device)) # passthrough si pas de Fourier
|
|
168
179
|
else:
|
|
169
|
-
|
|
170
|
-
|
|
180
|
+
for sigma_val in Fourier:
|
|
181
|
+
self.encodings.append(rff.layers.GaussianEncoding(
|
|
182
|
+
sigma=sigma_val,
|
|
183
|
+
input_size=self.layers[0],
|
|
184
|
+
encoded_size=self.layers[1]
|
|
185
|
+
).to(device))
|
|
186
|
+
self.layers[0] = self.layers[1] * 2 # chaque entrée est doublée après encodage
|
|
187
|
+
|
|
171
188
|
# --- Sélection du normalisateur / activation ---
|
|
172
189
|
self.norm = norm_list.get(norm)
|
|
173
190
|
if self.norm is None:
|
|
@@ -187,11 +204,11 @@ class MLP():
|
|
|
187
204
|
self.model = self.Create_MLP(self.layers)
|
|
188
205
|
|
|
189
206
|
# --- Sélection de l’optimiseur ---
|
|
190
|
-
self.optim = optim_list(self,
|
|
207
|
+
self.optim = optim_list(self, init_lr).get(optim)
|
|
191
208
|
if self.optim is None:
|
|
192
209
|
print("")
|
|
193
210
|
print (f"{optim} n'est pas reconnu")
|
|
194
|
-
self.optim = optim_list(self,
|
|
211
|
+
self.optim = optim_list(self, init_lr).get("Adam")
|
|
195
212
|
print (f"Retour au paramètre par défaut: 'Adam'")
|
|
196
213
|
|
|
197
214
|
# --- Compilation optionnelle du modèle pour accélérer l’inférence ---
|
|
@@ -208,6 +225,7 @@ class MLP():
|
|
|
208
225
|
|
|
209
226
|
# --- Envoi du modèle sur le device GPU / CPU ---
|
|
210
227
|
self.model.to(device)
|
|
228
|
+
self.f = nn.Linear(len(self.encodings) * self.layers[-1], self.layers[-1]).to(device)
|
|
211
229
|
|
|
212
230
|
def __repr__(self):
|
|
213
231
|
"""
|
|
@@ -267,37 +285,45 @@ class MLP():
|
|
|
267
285
|
background_fill=(0, 0, 0, 0),
|
|
268
286
|
opacity=255
|
|
269
287
|
)
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
plt.show()
|
|
273
|
-
return ""
|
|
288
|
+
); plt.show(); return ""
|
|
274
289
|
|
|
275
290
|
def __call__(self, x):
|
|
276
291
|
"""
|
|
277
|
-
Effectue une inférence complète
|
|
278
|
-
|
|
292
|
+
Effectue une inférence complète en appliquant :
|
|
293
|
+
- chaque encodage d'entrée défini dans `self.encodings`,
|
|
294
|
+
- un passage dans le MLP (`self.model`) pour chaque encodage,
|
|
295
|
+
- une concaténation des sorties,
|
|
296
|
+
- une couche linéaire finale (`self.f`) pour produire la prédiction.
|
|
279
297
|
|
|
280
|
-
Cette méthode permet d’utiliser l’objet comme une fonction
|
|
281
|
-
|
|
298
|
+
Cette méthode permet d’utiliser l’objet comme une fonction :
|
|
299
|
+
y = net(x)
|
|
282
300
|
|
|
283
|
-
|
|
301
|
+
Parameters
|
|
284
302
|
----------
|
|
285
303
|
x : array-like
|
|
286
|
-
Entrée(s) à prédire. Peut être un tableau
|
|
287
|
-
ou
|
|
304
|
+
Entrée(s) à prédire. Peut être un tableau NumPy, une liste Python
|
|
305
|
+
ou un tenseur PyTorch convertible par `tensorise()`.
|
|
288
306
|
|
|
289
307
|
Returns
|
|
290
308
|
-------
|
|
291
309
|
np.ndarray
|
|
292
|
-
Sortie du
|
|
293
|
-
|
|
310
|
+
Sortie finale du modèle après :
|
|
311
|
+
encodage(s) → MLP → concaténation → couche finale.
|
|
312
|
+
Résultat renvoyé sous forme de tableau NumPy CPU aplati.
|
|
294
313
|
"""
|
|
295
314
|
|
|
296
315
|
# Inférence sans calcul de gradient (plus rapide et évite la construction du graphe)
|
|
297
316
|
with torch.no_grad():
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
317
|
+
inputs = tensorise(x)
|
|
318
|
+
if inputs.dim() == 1:
|
|
319
|
+
inputs = inputs.unsqueeze(0)
|
|
320
|
+
|
|
321
|
+
# --- Initialisation du scaler pour l'entraînement en précision mixte ---
|
|
322
|
+
|
|
323
|
+
results_list = []
|
|
324
|
+
for encoding in self.encodings:
|
|
325
|
+
results_list.append(self.model(encoding(inputs)))
|
|
326
|
+
return self.f(torch.cat(results_list, dim=1)).cpu().numpy().flatten()
|
|
301
327
|
|
|
302
328
|
def params(self):
|
|
303
329
|
"""
|
|
@@ -430,154 +456,140 @@ class MLP():
|
|
|
430
456
|
self.norm
|
|
431
457
|
])
|
|
432
458
|
|
|
433
|
-
# Ajout de la couche finale : Linear
|
|
459
|
+
# Ajout de la couche finale : Linear
|
|
434
460
|
layer_list.extend([
|
|
435
|
-
nn.Linear(layers[-2], layers[-1])
|
|
436
|
-
nn.Sigmoid()
|
|
461
|
+
nn.Linear(layers[-2], layers[-1])
|
|
437
462
|
])
|
|
438
463
|
|
|
439
464
|
return nn.Sequential(*layer_list)
|
|
440
465
|
|
|
441
|
-
def
|
|
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`.
|
|
466
|
+
def train(self, inputs, outputs, num_epochs=1500, batch_size=1024):
|
|
462
467
|
"""
|
|
463
|
-
|
|
464
|
-
|
|
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)
|
|
468
|
+
Entraîne le modèle en utilisant un schéma mini-batch et la précision
|
|
469
|
+
mixte (AMP). Chaque batch subit :
|
|
507
470
|
|
|
508
|
-
|
|
509
|
-
maxarray = [0.00000001] + [loss.item() for loss in self.losses]
|
|
510
|
-
los.set_ylim(0, max(maxarray))
|
|
471
|
+
encodages multiples → MLP → concaténation → couche finale → perte
|
|
511
472
|
|
|
512
|
-
|
|
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).
|
|
473
|
+
Ce training met également à jour dynamiquement le learning rate.
|
|
522
474
|
|
|
523
475
|
Parameters
|
|
524
476
|
----------
|
|
525
|
-
inputs : array-like or
|
|
526
|
-
Données d'entrée
|
|
527
|
-
|
|
528
|
-
|
|
477
|
+
inputs : array-like or torch.Tensor
|
|
478
|
+
Données d'entrée, shape (N, input_dim). Sont converties en
|
|
479
|
+
tenseur PyTorch et envoyées sur `device`.
|
|
480
|
+
outputs : array-like or torch.Tensor
|
|
481
|
+
Cibles correspondantes, shape (N, output_dim).
|
|
529
482
|
num_epochs : int, optional
|
|
530
|
-
Nombre total d'époques d'entraînement
|
|
483
|
+
Nombre total d'époques d'entraînement.
|
|
484
|
+
Default: 1500.
|
|
531
485
|
batch_size : int, optional
|
|
532
|
-
Taille des mini-batchs
|
|
486
|
+
Taille des mini-batchs. Default: 1024.
|
|
533
487
|
|
|
534
488
|
Notes
|
|
535
489
|
-----
|
|
536
|
-
- Utilise torch.amp.autocast
|
|
537
|
-
|
|
538
|
-
-
|
|
539
|
-
* self.
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
* self.
|
|
490
|
+
- Utilise `torch.amp.autocast` et `GradScaler` pour un entraînement
|
|
491
|
+
accéléré et stable en précision mixte (FP16/FP32).
|
|
492
|
+
- Pour chaque mini-batch :
|
|
493
|
+
* chaque module dans `self.encodings` est appliqué aux entrées,
|
|
494
|
+
* chaque encodage passe dans `self.model`,
|
|
495
|
+
* les sorties sont concaténées,
|
|
496
|
+
* la couche finale `self.f` produit la prédiction,
|
|
497
|
+
* la perte est évaluée via `self.crit`.
|
|
498
|
+
- Le learning rate est ajusté après chaque époque.
|
|
499
|
+
- Les pertes d'époques sont enregistrées dans `self.losses`.
|
|
543
500
|
"""
|
|
544
501
|
|
|
545
502
|
# --- Conversion en tensors et récupération du nombre d'échantillons ---
|
|
546
|
-
inputs, outputs
|
|
503
|
+
inputs, outputs = tensorise(inputs).to(device), tensorise(outputs).to(device)
|
|
547
504
|
self.model = self.model.to(device)
|
|
548
|
-
|
|
505
|
+
n_samples = inputs.size(0)
|
|
506
|
+
|
|
549
507
|
# --- Initialisation du scaler pour l'entraînement en précision mixte ---
|
|
550
508
|
dev = str(device)
|
|
551
509
|
scaler = GradScaler(dev)
|
|
552
|
-
|
|
510
|
+
|
|
511
|
+
def update_lr(optimizer, loss):
|
|
512
|
+
for param_group in optimizer.param_groups:
|
|
513
|
+
param_group['lr'] = 0.005*np.where(loss <=0, 0,
|
|
514
|
+
np.where(loss >=1, 1,
|
|
515
|
+
np.sqrt(loss)/(2 - loss**2)))
|
|
516
|
+
|
|
553
517
|
# --- Boucle principale sur les époques ---
|
|
554
518
|
for epoch in tqdm(range(num_epochs), desc="train epoch"):
|
|
555
519
|
# Génération d'un ordre aléatoire des indices
|
|
556
520
|
perm = torch.randperm(n_samples, device=device)
|
|
557
521
|
epoch_loss = 0.0
|
|
558
|
-
|
|
522
|
+
|
|
559
523
|
# --- Parcours des mini-batchs ---
|
|
560
524
|
for i in range(0, n_samples, batch_size):
|
|
561
525
|
idx = perm[i:i+batch_size]
|
|
562
|
-
|
|
526
|
+
|
|
563
527
|
# Fonction interne calculant la perte et les gradients
|
|
564
528
|
def closure():
|
|
565
529
|
self.optim.zero_grad(set_to_none=True)
|
|
566
530
|
with autocast(dev): # AMP
|
|
567
|
-
|
|
531
|
+
results_list = []
|
|
532
|
+
for encoding in self.encodings:
|
|
533
|
+
results_list.append(self.model(encoding(inputs[idx])))
|
|
534
|
+
loss = self.crit(self.f(torch.cat(results_list, dim=1)),outputs[idx])
|
|
568
535
|
scaler.scale(loss).backward()
|
|
569
536
|
return loss
|
|
570
|
-
|
|
537
|
+
|
|
571
538
|
# Calcul de la perte et mise à jour des poids
|
|
572
539
|
loss = closure()
|
|
573
540
|
scaler.step(self.optim)
|
|
574
541
|
scaler.update()
|
|
575
|
-
|
|
542
|
+
|
|
576
543
|
# Accumulation de la perte pour l'époque
|
|
577
|
-
epoch_loss += loss
|
|
578
|
-
|
|
544
|
+
epoch_loss += loss.item()
|
|
545
|
+
|
|
579
546
|
# --- Stockage de la perte de l'époque ---
|
|
580
547
|
self.losses.append(epoch_loss)
|
|
548
|
+
update_lr(self.optim,self.losses[-1])
|
|
549
|
+
|
|
550
|
+
def losses(*nets):
|
|
551
|
+
"""
|
|
552
|
+
Affiche les courbes de pertes (training loss) de plusieurs réseaux MLP.
|
|
553
|
+
|
|
554
|
+
Parameters
|
|
555
|
+
----------
|
|
556
|
+
nets : MLP
|
|
557
|
+
Un ou plusieurs réseaux possédant un attribut `.losses`
|
|
558
|
+
contenant l'historique des pertes (liste de float).
|
|
559
|
+
|
|
560
|
+
Notes
|
|
561
|
+
-----
|
|
562
|
+
- L’axe X correspond aux itérations (epochs ou steps).
|
|
563
|
+
- L’axe Y correspond à la valeur de la perte.
|
|
564
|
+
- La fonction utilise matplotlib en mode interactif pour affichage dynamique.
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
# --- Initialisation de la figure ---
|
|
568
|
+
fig = plt.figure(figsize=(5, 5))
|
|
569
|
+
|
|
570
|
+
# --- Définition des limites des axes ---
|
|
571
|
+
all_losses = [[loss for loss in net.losses] for net in nets]
|
|
572
|
+
if max(len(lst) for lst in all_losses) == 1:
|
|
573
|
+
lenlosses = 2
|
|
574
|
+
else:
|
|
575
|
+
lenlosses = max(len(lst) for lst in all_losses)
|
|
576
|
+
plt.xlim(1, lenlosses)
|
|
577
|
+
|
|
578
|
+
# --- Tracé des courbes de pertes pour chaque réseau ---
|
|
579
|
+
for k, net in enumerate(nets):
|
|
580
|
+
steps = np.linspace(1, len(net.losses), len(net.losses)) # epochs
|
|
581
|
+
plt.plot(np.arange(1, len(all_losses[k])+1), all_losses[k],label = net.name)
|
|
582
|
+
plt.yscale('log', nonpositive='mask')
|
|
583
|
+
# --- Affichage ---
|
|
584
|
+
plt.legend()
|
|
585
|
+
plt.xlabel("Epoch")
|
|
586
|
+
plt.ylabel("Résidus")
|
|
587
|
+
fig.canvas.draw_idle()
|
|
588
|
+
plt.tight_layout()
|
|
589
|
+
plt.ion() # mode interactif
|
|
590
|
+
plt.show()
|
|
591
|
+
|
|
592
|
+
losses.help = fPrintDoc(losses)
|
|
581
593
|
|
|
582
594
|
MLP.__init__.help = fPrintDoc(MLP.__init__)
|
|
583
595
|
MLP.__repr__.help = fPrintDoc(MLP.__repr__)
|
|
@@ -586,6 +598,4 @@ MLP.help = fPrintDoc(MLP)
|
|
|
586
598
|
MLP.params.help = fPrintDoc(MLP.params)
|
|
587
599
|
MLP.nb_params.help = fPrintDoc(MLP.nb_params)
|
|
588
600
|
MLP.neurons.help = fPrintDoc(MLP.neurons)
|
|
589
|
-
MLP.Create_MLP.help = fPrintDoc(MLP.Create_MLP)
|
|
590
|
-
MLP.plot.help = fPrintDoc(MLP.plot)
|
|
591
601
|
MLP.train.help = fPrintDoc(MLP.train)
|
NeuralNetworks/__init__.py
CHANGED
|
@@ -114,15 +114,14 @@ Notes générales
|
|
|
114
114
|
"""
|
|
115
115
|
|
|
116
116
|
# Import des dépendances et utilitaires globaux (device, settings, tensorise, etc.)
|
|
117
|
-
from .Dependances import norms, crits, optims, rglen, device, pi, e, tensorise
|
|
117
|
+
from .Dependances import norms, crits, optims, rglen, device, pi, e, tensorise
|
|
118
118
|
|
|
119
|
-
#
|
|
120
|
-
from .
|
|
119
|
+
# Modèle MLP principal + fonction d'entraînement associée
|
|
120
|
+
from .MLP import MLP, losses
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
from .Plot import compare, plot, losses, train
|
|
122
|
+
from .Latent import Latent
|
|
124
123
|
|
|
125
|
-
#
|
|
126
|
-
from .
|
|
124
|
+
# Fonctions de chargement/preprocessing des images
|
|
125
|
+
from .tools import image, MNIST, AirfRANS
|
|
127
126
|
|
|
128
|
-
__version__ = "0.
|
|
127
|
+
__version__ = "0.2.0"
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
from airfrans import *
|
|
10
|
+
|
|
11
|
+
def download(path,unzip = True, OpenFOAM = False):
|
|
12
|
+
"""
|
|
13
|
+
Télécharge le dataset AirfRANS dans le dossier spécifié.
|
|
14
|
+
|
|
15
|
+
Cette fonction est un simple wrapper autour :
|
|
16
|
+
dataset.download(root=path, file_name='AirfRANS', unzip=True, OpenFOAM=True)
|
|
17
|
+
|
|
18
|
+
Les arguments `unzip` et `OpenFOAM` sont actuellement ignorés par la fonction
|
|
19
|
+
et forcés à True dans l’appel interne.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
path : str
|
|
24
|
+
Chemin du dossier dans lequel le dataset doit être téléchargé.
|
|
25
|
+
unzip : bool, optional
|
|
26
|
+
Paramètre non utilisé. Le téléchargement interne force `unzip=True`.
|
|
27
|
+
OpenFOAM : bool, optional
|
|
28
|
+
Paramètre non utilisé. Le téléchargement interne force `OpenFOAM=True`.
|
|
29
|
+
|
|
30
|
+
Notes
|
|
31
|
+
-----
|
|
32
|
+
- Le fichier téléchargé s’appelle `'AirfRANS'`.
|
|
33
|
+
- Le dataset est automatiquement décompressé.
|
|
34
|
+
- Le format OpenFOAM est toujours inclus.
|
|
35
|
+
"""
|
|
36
|
+
dataset.download(root = path, file_name = 'AirfRANS', unzip = True, OpenFOAM = True)
|