uniovi-simur-wearablepermed-ml 1.1.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.

Potentially problematic release.


This version of uniovi-simur-wearablepermed-ml might be problematic. Click here for more details.

@@ -0,0 +1,671 @@
1
+ import math
2
+ import os
3
+ import joblib # Librería empleada para guardar y cargar los modelos Random Forests
4
+ import numpy as np
5
+ from sklearn.metrics import accuracy_score, f1_score
6
+ import tensorflow as tf
7
+ import keras
8
+ # from sklearn.ensemble import RandomForestClassifier
9
+ from imblearn.ensemble import BalancedRandomForestClassifier
10
+ import xgboost as xgb
11
+
12
+ from tensorflow.keras import layers, models, optimizers
13
+
14
+
15
+ #import _spectral_features_calculator
16
+
17
+ # Librerías necesarias para implementar el algoritmo de fusión sensorial ahsr
18
+ # import ahrs
19
+ # from ahrs.filters import Madgwick
20
+
21
+ class SiMuRModel_ESANN(object):
22
+ def __init__(self, data, params: dict, **kwargs) -> None:
23
+
24
+ #############################################################################
25
+ # Aquí se tratan los parámetros del modelo. Esto es necesario porque estos modelos contienen muchos hiperparámetros
26
+
27
+ # - Hiperparámetros asociados a las opciones de entrenamiento de la CNN
28
+ self.optimizador = params.get("optimizador", "adam") # especifica el optimizador a utilizar durante el entrenamiento
29
+ self.tamanho_minilote = params.get("tamanho_minilote", 10) # especifica el tamaño del mini-lote
30
+ self.tasa_aprendizaje = params.get("tasa_aprendizaje", 0.01) # especifica el learning-rate empleado durante el entrenamiento
31
+
32
+ # - Hiperparámetros asociados a la arquitectura de la red CNN
33
+ self.N_capas = params.get("N_capas", 2) # especifica el número de capas ocultas de la red
34
+ self.activacion_capas_ocultas = params.get("funcion_activacion", "relu") # especifica la función de activación asociada las neuronas de las capas ocultas
35
+ self.numero_filtros = params.get("numero_filtros", 12) # especifica el número de filtros utilizados en las capas ocultas de la red
36
+ self.tamanho_filtro = params.get("tamanho_filtro", 7) # especifica el tamaño de los filtros de las capas ocultas
37
+
38
+ self.testMetrics = []
39
+ self.metrics = [accuracy_score, f1_score]
40
+ #############################################################################
41
+ # Los datos de entrenamiento vienen en el parametro data:
42
+ # - Vienen pre-procesados.
43
+ # - data suele ser un objeto o diccionario con:
44
+ # data.X_Train
45
+ # data.Y_Train
46
+ # data.X_Test
47
+ # data.Y_Test
48
+ # El formato del objeto Data puede variar de aplicación en aplicación
49
+
50
+ self.X_train = data.X_train
51
+
52
+ try:
53
+ self.X_validation = data.X_validation
54
+ except:
55
+ print("Not enough data for validation.")
56
+ self.X_validation = None
57
+
58
+ try:
59
+ self.X_test = data.X_test
60
+ except:
61
+ print("Not enough data for test.")
62
+ self.X_test = None
63
+
64
+ self.y_train = data.y_train
65
+
66
+ try:
67
+ self.y_validation = data.y_validation
68
+ except:
69
+ print("Not enough data for validation.")
70
+ self.y_validation = None
71
+
72
+ try:
73
+ self.y_test = data.y_test
74
+ except:
75
+ print("Not enough data for test.")
76
+ self.y_test = None
77
+
78
+ #############################################################################
79
+ # También se crea el modelo. Si es una red aquí se define el grafo.
80
+ # La creación del modelo se encapsula en la función "create_model"
81
+ # Ejemplo de lectura de parámetros:
82
+ # param1 = params.get("N_capas", 3)
83
+
84
+ self.model = self.create_model()
85
+
86
+ #############################################################################
87
+
88
+ def create_model(self):
89
+ # Aquí se define la red, SVC, árbol, etc.
90
+ self.numClasses = int((max(self.y_train)+1)[0]) # especifica el número de clases
91
+
92
+ # if (self.X_train).shape[1]==12:
93
+ # dimension_de_entrada = (12, 250)
94
+ # elif (self.X_train).shape[1]==6:
95
+ dimension_de_entrada = (6, 250)
96
+
97
+ model = models.Sequential()
98
+ model.add(layers.InputLayer(input_shape=dimension_de_entrada))
99
+
100
+ # Añadir capas convolucionales según N_capas
101
+ for i in range(self.N_capas):
102
+ filtros = self.numero_filtros
103
+ model.add(layers.Conv1D(filtros, self.tamanho_filtro, padding="causal", activation=self.activacion_capas_ocultas))
104
+ model.add(layers.LayerNormalization())
105
+
106
+ # Capas finales fijas
107
+ model.add(layers.GlobalAveragePooling1D())
108
+ model.add(layers.Dropout(0.2))
109
+ model.add(layers.Dense(self.numClasses, activation='softmax'))
110
+
111
+ # Optimizadores
112
+ if self.optimizador == "adam":
113
+ optimizer_hyperparameter = tf.keras.optimizers.Adam(learning_rate=self.tasa_aprendizaje)
114
+ elif self.optimizador == 'rmsprop':
115
+ optimizer_hyperparameter = tf.keras.optimizers.RMSprop(learning_rate=self.tasa_aprendizaje)
116
+ elif self.optimizador == 'SGD':
117
+ optimizer_hyperparameter = tf.keras.optimizers.SGD(learning_rate=self.tasa_aprendizaje)
118
+ else:
119
+ raise
120
+
121
+ model.compile(optimizer=optimizer_hyperparameter,
122
+ loss='sparse_categorical_crossentropy',
123
+ metrics=['accuracy'])
124
+ model.summary()
125
+
126
+ return model
127
+
128
+ def train(self, epochs):
129
+ # Se lanza el entrenamiento de los modelos. El código para lanzar el entrenamiento depende mucho del modelo.
130
+ callbacks = [
131
+ keras.callbacks.EarlyStopping(monitor='val_loss', patience=5)
132
+ ]
133
+
134
+ # Verifica si X_validation e y_validation existen
135
+ if self.X_validation is not None and self.y_validation is not None:
136
+ history = self.model.fit(
137
+ self.X_train,
138
+ self.y_train,
139
+ validation_data=(self.X_validation, self.y_validation),
140
+ batch_size=self.tamanho_minilote,
141
+ epochs=epochs,
142
+ verbose=1,
143
+ callbacks=callbacks
144
+ )
145
+ else:
146
+ history = self.model.fit(
147
+ self.X_train,
148
+ self.y_train,
149
+ batch_size=self.tamanho_minilote,
150
+ epochs=epochs,
151
+ verbose=1,
152
+ callbacks=callbacks
153
+ )
154
+
155
+ if self.X_test is not None and self.y_test is not None:
156
+ # Cuando acaba el entrenamiento y obtenemos los pesos óptimos, las métricas de error para los datos de test son calculadas.
157
+ self.y_test_est = self.predict(self.X_test)
158
+ self.y_test_est = np.argmax(self.y_test_est, axis=1) # Trabajamos con clasificación multicategoría
159
+
160
+ # y_test_est_float_round = np.around(self.y_test_est) # Redondear vector de tipo float (codificado en one_hot)
161
+ # y_test_est_int_round = y_test_est_float_round.astype(int) # Obtención de vector de tipo int
162
+ # self.y_test_est = y_test_est_int_round # Asignación del atributo y_test_est
163
+
164
+ self.testMetrics = [accuracy_score(self.y_test, self.y_test_est),
165
+ f1_score(self.y_test, self.y_test_est, average='micro')] # REVISAR la opción 'average'
166
+
167
+ def predict(self,X):
168
+ # Método para predecir una o varias muestras.
169
+ # El código puede variar dependiendo del modelo
170
+ return self.model.predict(X)
171
+
172
+ def store(self, model_id, path):
173
+ # Método para guardar los pesos en path
174
+ # Serialize weights to HDF5
175
+ path = os.path.join(path, model_id + ".weights.h5")
176
+
177
+ self.model.save_weights(path)
178
+ print("Saved model to disk.")
179
+
180
+ return None
181
+
182
+ def load(self, model_id, path):
183
+ # Método para cargar los pesos desde el path indicado
184
+ path = os.path.join(path, model_id + ".weights.h5")
185
+
186
+ self.model.load_weights(path)
187
+ print("Loaded model from disk.")
188
+
189
+ # Evaluate loaded model on test data
190
+ self.model.compile(loss='sparse_categorical_crossentropy',
191
+ optimizer=self.optimizador,
192
+ metrics=['accuracy'])
193
+
194
+ return None
195
+
196
+ ########## MÉTODOS DE LAS CLASES ##########
197
+ # Estos métodos se pueden llamar sin instar un objeto de la clase
198
+ # Ej.: import model; model.get_model_type()
199
+
200
+ @classmethod
201
+ def get_model_type(cls):
202
+ return "CNN" # Aquí se puede indicar qué tipo de modelo es: RRNN, keras, scikit-learn, etc.
203
+
204
+ @classmethod
205
+ def get_model_name(cls):
206
+ return "SiMuR" # Aquí se puede indicar un ID que identifique el modelo
207
+
208
+ @classmethod
209
+ def get_model_Obj(cls):
210
+ return SiMuRModel_ESANN
211
+
212
+ class SiMuRModel_CAPTURE24(object):
213
+ def __init__(self, data, params: dict):
214
+ # -----------------------------
215
+ # Hiperparámetros de entrenamiento
216
+ self.optimizador = params.get("optimizador", "adam")
217
+ self.tasa_aprendizaje = params.get("tasa_aprendizaje", 5e-3)
218
+ self.tamanho_minilote = params.get("tamanho_minilote", 4) # batch pequeño
219
+
220
+ # -----------------------------
221
+ # Arquitectura CNN
222
+ self.N_capas = params.get("N_capas", 3) # usar 3–4 etapas
223
+ self.funcion_activacion = params.get("funcion_activacion", "relu")
224
+ self.numero_filtros = params.get("numero_filtros", 64)
225
+ self.tamanho_filtro = params.get("tamanho_filtro", 3)
226
+ self.num_resblocks = params.get("num_resblocks", 1)
227
+
228
+ # -----------------------------
229
+ # Datos
230
+ self.X_train = data.X_train
231
+ self.y_train = data.y_train
232
+ self.X_validation = getattr(data, "X_validation", None)
233
+ self.y_validation = getattr(data, "y_validation", None)
234
+ self.X_test = getattr(data, "X_test", None)
235
+ self.y_test = getattr(data, "y_test", None)
236
+
237
+ # -----------------------------
238
+ # Input shape y clases
239
+ self.input_shape = (6,250) # (6, 250)
240
+ self.num_classes = int(np.max(self.y_train) + 1)
241
+
242
+ # -----------------------------
243
+ # Crear modelo
244
+ self.model = self.create_model()
245
+
246
+ # -----------------------------
247
+ # Bloque residual
248
+ def ResBlock(self, x, filtros, kernel_size, activation='relu'):
249
+ shortcut = x
250
+ x = layers.Conv1D(filtros, kernel_size, padding='same', activation=activation)(x)
251
+ x = layers.Conv1D(filtros, kernel_size, padding='same')(x)
252
+ x = layers.Add()([x, shortcut])
253
+ x = layers.Activation(activation)(x)
254
+ x = layers.BatchNormalization()(x)
255
+ return x
256
+
257
+ # -----------------------------
258
+ # Crear modelo
259
+ def create_model(self):
260
+ inputs = layers.Input(shape=self.input_shape)
261
+ x = inputs
262
+
263
+ filtros = self.numero_filtros # inicializamos filtros
264
+ for i in range(self.N_capas):
265
+ # Reducir dimensionalidad temporal en cada etapa excepto la última
266
+ stride = 2 if i < self.N_capas - 1 else 1
267
+ x = layers.Conv1D(filtros, self.tamanho_filtro, strides=stride,
268
+ padding='same', activation=self.funcion_activacion)(x)
269
+
270
+ # 1 ResBlock por etapa para no saturar memoria
271
+ for _ in range(self.num_resblocks):
272
+ x = self.ResBlock(x, filtros, self.tamanho_filtro, self.funcion_activacion)
273
+
274
+ # Solo duplicar filtros si no es la última capa
275
+ if i < self.N_capas - 1:
276
+ filtros *= 2
277
+
278
+ # Global average pooling para reducir dimensionalidad
279
+ x = layers.GlobalAveragePooling1D()(x)
280
+
281
+ # Dropout para regularización
282
+ x = layers.Dropout(0.3)(x) # menos agresivo que 0.5 para batch pequeño
283
+
284
+ # Dense intermedio más pequeño para memoria limitada
285
+ x = layers.Dense(64, activation='relu')(x)
286
+ x = layers.BatchNormalization()(x)
287
+
288
+ # Capa de salida
289
+ outputs = layers.Dense(self.num_classes, activation='softmax')(x)
290
+
291
+ # Optimizer
292
+ if self.optimizador.lower() == "adam":
293
+ optimizer = tf.keras.optimizers.Adam(learning_rate=self.tasa_aprendizaje)
294
+ elif self.optimizador.lower() == "rmsprop":
295
+ optimizer = tf.keras.optimizers.RMSprop(learning_rate=self.tasa_aprendizaje)
296
+ elif self.optimizador.lower() == "sgd":
297
+ optimizer = tf.keras.optimizers.SGD(learning_rate=self.tasa_aprendizaje)
298
+ else:
299
+ raise ValueError(f"Optimizador {self.optimizador} no soportado")
300
+
301
+ model = models.Model(inputs, outputs)
302
+ model.compile(optimizer=optimizer,
303
+ loss='sparse_categorical_crossentropy',
304
+ metrics=['accuracy'])
305
+
306
+ return model
307
+
308
+ def train(self, epochs):
309
+ # Se lanza el entrenamiento de los modelos. El código para lanzar el entrenamiento depende mucho del modelo.
310
+ callbacks = [
311
+ keras.callbacks.EarlyStopping(monitor='val_loss', patience=5)
312
+ ]
313
+
314
+ # Verifica si X_validation e y_validation existen
315
+ if self.X_validation is not None and self.y_validation is not None:
316
+ history = self.model.fit(
317
+ self.X_train,
318
+ self.y_train,
319
+ validation_data=(self.X_validation, self.y_validation),
320
+ batch_size=self.tamanho_minilote,
321
+ epochs=epochs,
322
+ verbose=1,
323
+ callbacks=callbacks
324
+ )
325
+ else:
326
+ history = self.model.fit(
327
+ self.X_train,
328
+ self.y_train,
329
+ batch_size=self.tamanho_minilote,
330
+ epochs=epochs,
331
+ verbose=1,
332
+ callbacks=callbacks
333
+ )
334
+
335
+ if self.X_test is not None and self.y_test is not None:
336
+ # Cuando acaba el entrenamiento y obtenemos los pesos óptimos, las métricas de error para los datos de test son calculadas.
337
+ self.y_test_est = self.predict(self.X_test)
338
+ self.y_test_est = np.argmax(self.y_test_est, axis=1) # Trabajamos con clasificación multicategoría
339
+
340
+ # y_test_est_float_round = np.around(self.y_test_est) # Redondear vector de tipo float (codificado en one_hot)
341
+ # y_test_est_int_round = y_test_est_float_round.astype(int) # Obtención de vector de tipo int
342
+ # self.y_test_est = y_test_est_int_round # Asignación del atributo y_test_est
343
+
344
+ self.testMetrics = [accuracy_score(self.y_test, self.y_test_est),
345
+ f1_score(self.y_test, self.y_test_est, average='micro')] # REVISAR la opción 'average'
346
+
347
+
348
+ def predict(self, X):
349
+ # Método para predecir una o varias muestras.
350
+ # El código puede variar dependiendo del modelo
351
+ return self.model.predict(X)
352
+
353
+ def store(self, model_id, path):
354
+ # Método para guardar los pesos en path
355
+ # Serialize weights to HDF5
356
+ path = os.path.join(path, model_id + ".weights.h5")
357
+
358
+ self.model.save_weights(path)
359
+ print("Saved model to disk.")
360
+
361
+ return None
362
+
363
+ def load(self, model_id, path):
364
+ # Método para cargar los pesos desde el path indicado
365
+ path = os.path.join(path, model_id + ".weights.h5")
366
+
367
+ self.model.load_weights(path)
368
+ print("Loaded model from disk.")
369
+ # Evaluate loaded model on test data
370
+ self.model.compile(loss='sparse_categorical_crossentropy',
371
+ optimizer=self.optimizador,
372
+ metrics=['accuracy'])
373
+
374
+ return None
375
+
376
+ ########## MÉTODOS DE LAS CLASES ##########
377
+ # Estos métodos se pueden llamar sin instar un objeto de la clase
378
+ # Ej.: import model; model.get_model_type()
379
+
380
+ @classmethod
381
+ def get_model_type(cls):
382
+ return "CNN" # Aquí se puede indicar qué tipo de modelo es: RRNN, keras, scikit-learn, etc.
383
+
384
+ @classmethod
385
+ def get_model_name(cls):
386
+ return "SiMuR" # Aquí se puede indicar un ID que identifique el modelo
387
+
388
+ @classmethod
389
+ def get_model_Obj(cls):
390
+ return SiMuRModel_CAPTURE24
391
+
392
+
393
+ class SiMuRModel_RandomForest(object):
394
+ def __init__(self, data, params: dict, **kwargs) -> None:
395
+
396
+ #############################################################################
397
+ # Aquí se tratan los parámetros del modelo. Esto es necesario porque estos modelos contienen muchos hiperparámetros
398
+ self.n_estimators = params.get("n_estimators", 1000)
399
+ self.max_depth = params.get("max_depth", 10)
400
+ self.min_samples_split = params.get("min_samples_split", 3)
401
+ self.min_samples_leaf = params.get("min_samples_leaf", 2)
402
+ self.max_features = params.get("max_features", "auto")
403
+
404
+
405
+ self.testMetrics = []
406
+ self.metrics = [accuracy_score, f1_score]
407
+ #############################################################################
408
+ # Los datos de entrenamiento vienen en el parametro data:
409
+ # - Vienen pre-procesados.
410
+ # - d".h5"ain = data.y_train
411
+ self.X_train = data.X_train
412
+ self.X_test = data.X_test
413
+
414
+ self.y_train = data.y_train
415
+ self.y_test = data.y_test
416
+
417
+ #############################################################################
418
+
419
+ # También se crea el modelo. Si es una red aquí se define el grafo.
420
+ # La creación del modelo se encapsula en la función "create_model"
421
+ # Ejemplo de lectura de parámetros:
422
+ # param1 = params.get("N_capas", 3)
423
+
424
+ self.model = self.create_model()
425
+
426
+ #############################################################################
427
+
428
+ def create_model(self):
429
+ # Creamos el modelo de Random Forest con 3000 árboles
430
+ model = BalancedRandomForestClassifier(n_estimators=self.n_estimators,
431
+ n_jobs=-1, # n_jobs=-1 utiliza todos los núcleos disponibles para acelerar el entrenamiento
432
+ verbose=1,
433
+ max_features=self.max_features,
434
+ max_depth=self.max_depth,
435
+ min_samples_split=self.min_samples_split,
436
+ min_samples_leaf=self.min_samples_leaf)
437
+
438
+ return model
439
+
440
+ def train(self):
441
+
442
+ # Se lanza el entrenamiento de los modelos. El código para lanzar el entrenamiento depende mucho del modelo.
443
+ history = self.model.fit(self.X_train, self.y_train)
444
+
445
+ # Cuando acaba el entrenamiento y obtenemos los pesos óptimos, las métricas de error para los datos de test son calculadas.
446
+ self.y_test_est = self.predict(self.X_test)
447
+
448
+ y_test_est_float_round = np.around(self.y_test_est) # Redondear vector de tipo float (codificado en one_hot)
449
+ y_test_est_int_round = y_test_est_float_round.astype(int) # Obtención de vector de tipo int
450
+ self.y_test_est = y_test_est_int_round # Asignación del atributo y_test_est
451
+
452
+ self.testMetrics = [accuracy_score(self.y_test, self.y_test_est),
453
+ f1_score(self.y_test, self.y_test_est, average='micro')] # REVISAR la opción 'average'
454
+
455
+ def predict(self, X):
456
+ # Método para predecir una o varias muestras.
457
+ # El código puede variar dependiendo del modelo
458
+
459
+ return self.model.predict(X)
460
+
461
+ def store(self, model_id, path):
462
+ path = os.path.join(path, model_id + ".pkl")
463
+
464
+ # Método para guardar el modelo Random Forest en formato '.pkl'
465
+ joblib.dump(self.model, path)
466
+ print("Saved model to disk")
467
+
468
+ return None
469
+
470
+ def load(self, model_id, path):
471
+ path = os.path.join(path, model_id + ".pkl")
472
+
473
+ # Método para cargar el modelo Random Forest desde el path indicado
474
+ self.model = joblib.load(path)
475
+ print("Loaded model from disk")
476
+
477
+ return None
478
+
479
+ ########## MÉTODOS DE LAS CLASES ##########
480
+ # Estos métodos se pueden llamar sin instar un objeto de la clase
481
+ # Ej.: import model; model.get_model_type()
482
+
483
+ @classmethod
484
+ def get_model_type(cls):
485
+ return "Balanced Random Forest" # Aquí se puede indicar qué tipo de modelo es: RRNN, keras, scikit-learn, etc.
486
+
487
+ @classmethod
488
+ def get_model_name(cls):
489
+ return "SiMuR" # Aquí se puede indicar un ID que identifique el modelo
490
+
491
+ @classmethod
492
+ def get_model_Obj(cls):
493
+ return SiMuRModel_RandomForest
494
+
495
+
496
+ # Implementación del modelo XGBoost
497
+ class SiMuRModel_XGBoost(object):
498
+ def __init__(self, data, params: dict, **kwargs) -> None:
499
+
500
+ #############################################################################
501
+ # Aquí se tratan los parámetros del modelo. Esto es necesario porque estos modelos contienen muchos hiperparámetros
502
+ self.num_boost_round = params.get("num_boost_round", 1000) # Número de estimadores
503
+ self.max_depth = params.get("max_depth", 10) # Profundidad máxima
504
+ self.learning_rate = params.get("learning_rate", 0.05) # Tasa de aprendizaje
505
+ self.subsample = params.get("subsample", 0.70) # Fracción de muestras por árbol
506
+ self.colsample_bytree = params.get("colsample_bytree", 0.80) # Fracción de columnas por árbol
507
+ self.gamma = params.get("gamma", 4.2) # Regularización mínima de pérdida
508
+ self.min_child_weight = params.get("min_child_weight", 2) # Peso mínimo de hijos
509
+ self.reg_alpha = params.get("reg_alpha", 0.6) # L1 regularization
510
+ self.reg_lambda = params.get("reg_lambda", 0.04) # L2 regularization
511
+
512
+ self.testMetrics = []
513
+ self.metrics = [accuracy_score, f1_score]
514
+ #############################################################################
515
+ # Los datos de entrenamiento vienen en el parametro data:
516
+ # - Vienen pre-procesados.
517
+ # - data suele ser un objeto o diccionario con:
518
+ # data.X_Train
519
+ # data.Y_Train
520
+ # data.X_Test
521
+ # data.Y_Test
522
+
523
+ self.X_train = data.X_train
524
+
525
+ try:
526
+ self.X_validation = data.X_validation
527
+ except:
528
+ print("Not enough data for validation.")
529
+ self.X_validation = None
530
+
531
+ try:
532
+ self.X_test = data.X_test
533
+ except:
534
+ print("Not enough data for test.")
535
+ self.X_test = None
536
+
537
+ self.y_train = data.y_train
538
+
539
+ try:
540
+ self.y_validation = data.y_validation
541
+ except:
542
+ print("Not enough data for validation.")
543
+ self.y_validation = None
544
+
545
+ try:
546
+ self.y_test = data.y_test
547
+ except:
548
+ print("Not enough data for test.")
549
+ self.y_test = None
550
+
551
+ #############################################################################
552
+
553
+ # También se crea el modelo. Si es una red aquí se define el grafo.
554
+ # La creación del modelo se encapsula en la función "create_model"
555
+ # Ejemplo de lectura de parámetros:
556
+ # param1 = params.get("N_capas", 3)
557
+
558
+ self.model = self.create_model()
559
+
560
+ #############################################################################
561
+
562
+ def create_model(self):
563
+ # Creamos el modelo de XGBoost, el cual se entrenará con las mismas características definidas para el Random Forest
564
+ model = xgb.XGBClassifier(use_label_encoder=False,
565
+ num_boost_round=self.num_boost_round, # Como parámetros indicamos: el número de estimadores
566
+ max_depth=self.max_depth, # Profundidad máxima
567
+ learning_rate=self.learning_rate, # Tasa de aprendizaje
568
+ subsample=self.subsample, # Fracción de muestras por árbol
569
+ colsample_bytree=self.colsample_bytree, # Fracción de columnas por árbol
570
+ gamma=self.gamma, # Regularización mínima de pérdida
571
+ min_child_weight=self.min_child_weight, # Peso mínimo de hijos
572
+ reg_alpha=self.reg_alpha, # L1 regularization
573
+ reg_lambda=self.reg_lambda, # L2 regularization
574
+ eval_metric='mlogloss',
575
+ tree_method="gpu_hist", # GPU
576
+ predictor="gpu_predictor", # fuerza GPU
577
+ random_state=None # o simplemente no fijar semilla
578
+ )
579
+ return model
580
+
581
+ def train(self):
582
+ # --- Conversión a DMatrix ---
583
+ dtrain = xgb.DMatrix(self.X_train, label=self.y_train) # DMatrix de entrenamiento
584
+ dval = xgb.DMatrix(self.X_validation, label=self.y_validation) # DMatrix de validación
585
+ # --- Parámetros del modelo ---
586
+ params = {
587
+ "objective": "multi:softprob", # Salida probabilidades multiclase
588
+ "num_class": len(np.unique(self.y_train)), # Número de clases
589
+ "eval_metric": "mlogloss", # Métrica log-loss multiclase
590
+ "max_depth": self.max_depth, # Profundidad máxima
591
+ "learning_rate":self.learning_rate, # Tasa de aprendizaje
592
+ "subsample": self.subsample, # Fracción de muestras por árbol
593
+ "colsample_bytree": self.colsample_bytree, # Fracción de columnas por árbol
594
+ "gamma": self.gamma, # Regularización mínima de pérdida
595
+ "min_child_weight": self.min_child_weight, # Peso mínimo de hijos
596
+ "reg_alpha": self.reg_alpha, # L1 regularization
597
+ "reg_lambda": self.reg_lambda, # L2 regularization
598
+ "seed": np.random.randint(0, 1000000) # Semilla aleatoria en cada run
599
+ }
600
+ # --- Lista de evaluaciones ---
601
+ evals = [(dtrain, "train"), (dval, "validation")] # Conjuntos de evaluación
602
+ # --- Entrenamiento con early stopping ---
603
+ self.model = xgb.train(
604
+ params=params, # Parámetros
605
+ dtrain=dtrain, # Datos de entrenamiento
606
+ num_boost_round=self.num_boost_round, # Número de estimadores (rondas) en XGBoost
607
+ evals=evals, # Conjuntos de validación
608
+ early_stopping_rounds=20 # Parada temprana (early-stopping)
609
+ )
610
+ # --- Predicciones ---
611
+ y_predicted_probability = self.model.predict(dval) # Probabilidades predichas
612
+ self.y_validation_est = np.argmax(y_predicted_probability, axis=1) # Clase con mayor probabilidad
613
+ # --- Métricas ---
614
+ self.validationMetrics = [
615
+ accuracy_score(self.y_validation, self.y_validation_est), # Exactitud
616
+ f1_score(self.y_validation, self.y_validation_est, average='micro') # F1 micro
617
+ ]
618
+
619
+
620
+ def predict(self, X):
621
+ dmatrix = xgb.DMatrix(X) # Convierte numpy.ndarray a DMatrix
622
+ y_pred_proba = self.model.predict(dmatrix) # Obtiene probabilidades o scores
623
+
624
+ # Para clasificación multiclase (softprob), devuelve la clase con mayor probabilidad
625
+ return np.argmax(y_pred_proba, axis=1)
626
+
627
+
628
+ def store(self, model_id, path):
629
+ path = os.path.join(path, model_id + ".pkl")
630
+ # Método para guardar el modelo Random Forest en formato '.pkl'
631
+ joblib.dump(self.model, path)
632
+ print("Saved model to disk.")
633
+
634
+ return None
635
+
636
+
637
+ def load(self, model_id, path):
638
+ path = os.path.join(path, model_id + ".pkl")
639
+ # Método para cargar el modelo Random Forest desde el path indicado
640
+ self.model = joblib.load(path)
641
+ print("Loaded model from disk.")
642
+
643
+ return None
644
+
645
+
646
+ ########## MÉTODOS DE LAS CLASES ##########
647
+ # Estos métodos se pueden llamar sin instar un objeto de la clase
648
+ # Ej.: import model; model.get_model_type()
649
+
650
+ @classmethod
651
+ def get_model_type(cls):
652
+ return "XGBoost" # Aquí se puede indicar qué tipo de modelo es: RRNN, keras, scikit-learn, etc.
653
+
654
+ @classmethod
655
+ def get_model_name(cls):
656
+ return "SiMuR" # Aquí se puede indicar un ID que identifique el modelo
657
+
658
+ @classmethod
659
+ def get_model_Obj(cls):
660
+ return SiMuRModel_XGBoost
661
+
662
+
663
+ ##########################################
664
+ # Unit testing
665
+ ##########################################
666
+ if __name__ == "__main__":
667
+ # Este código solo se ejecuta si el script de ejecución principal es BaseModel.py:
668
+ # run BaseModel.py
669
+
670
+ # Aquí se puede escribir un código de prueba para probar por separado
671
+ pass
@@ -0,0 +1 @@
1
+ from .SiMuR_Model import SiMuRModel_ESANN, SiMuRModel_CAPTURE24, SiMuRModel_RandomForest, SiMuRModel_XGBoost