aisp 0.1.31__py3-none-any.whl → 0.1.33__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.
- aisp/NSA/__init__.py +1 -1
- aisp/NSA/_base.py +82 -41
- aisp/NSA/_negative_selection.py +132 -133
- aisp/utils/__init__.py +5 -0
- aisp/utils/_multiclass.py +43 -0
- aisp/utils/metrics.py +61 -0
- {aisp-0.1.31.dist-info → aisp-0.1.33.dist-info}/METADATA +3 -26
- aisp-0.1.33.dist-info/RECORD +11 -0
- {aisp-0.1.31.dist-info → aisp-0.1.33.dist-info}/WHEEL +1 -1
- aisp-0.1.31.dist-info/RECORD +0 -8
- {aisp-0.1.31.dist-info → aisp-0.1.33.dist-info}/LICENSE +0 -0
- {aisp-0.1.31.dist-info → aisp-0.1.33.dist-info}/top_level.txt +0 -0
aisp/NSA/__init__.py
CHANGED
aisp/NSA/_base.py
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
|
1
3
|
import numpy as np
|
2
4
|
import numpy.typing as npt
|
3
|
-
from typing import Literal
|
5
|
+
from typing import Literal, Optional
|
4
6
|
from scipy.spatial.distance import euclidean, cityblock, minkowski
|
5
7
|
|
8
|
+
from ..utils.metrics import accuracy_score
|
9
|
+
|
6
10
|
|
7
11
|
class Base:
|
8
12
|
"""
|
@@ -93,8 +97,11 @@ class Base:
|
|
93
97
|
else:
|
94
98
|
return euclidean(u, v)
|
95
99
|
|
96
|
-
|
97
|
-
|
100
|
+
@staticmethod
|
101
|
+
def _check_and_raise_exceptions_fit(
|
102
|
+
X: npt.NDArray = None,
|
103
|
+
y: npt.NDArray = None,
|
104
|
+
_class_: Literal["RNSA", "BNSA"] = "RNSA",
|
98
105
|
):
|
99
106
|
"""
|
100
107
|
Function responsible for verifying fit function parameters and throwing exceptions if the \
|
@@ -149,43 +156,6 @@ class Base:
|
|
149
156
|
"The array X contains values that are not composed only of 0 and 1."
|
150
157
|
)
|
151
158
|
|
152
|
-
def _slice_index_list_by_class(self, y: npt.NDArray) -> dict:
|
153
|
-
"""
|
154
|
-
The function ``__slice_index_list_by_class(...)``, separates the indices of the lines \
|
155
|
-
according to the output class, to loop through the sample array, only in positions where \
|
156
|
-
the output is the class being trained.
|
157
|
-
|
158
|
-
Parameters:
|
159
|
-
---
|
160
|
-
* y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
|
161
|
-
``X`` sample array.
|
162
|
-
|
163
|
-
returns:
|
164
|
-
---
|
165
|
-
* dict: A dictionary with the list of array positions(``y``), with the classes as key.
|
166
|
-
|
167
|
-
---
|
168
|
-
|
169
|
-
A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a \
|
170
|
-
classe de saída, para percorrer o array de amostra, apenas nas posições que a saída for \
|
171
|
-
a classe que está sendo treinada.
|
172
|
-
|
173
|
-
Parameters:
|
174
|
-
---
|
175
|
-
* y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do \
|
176
|
-
array de amostra ``X``.
|
177
|
-
|
178
|
-
Returns:
|
179
|
-
---
|
180
|
-
* dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
|
181
|
-
"""
|
182
|
-
position_samples = dict()
|
183
|
-
for _class_ in self.classes:
|
184
|
-
# Pega as posições das amostras por classes a partir do y.
|
185
|
-
position_samples[_class_] = list(np.where(y == _class_)[0])
|
186
|
-
|
187
|
-
return position_samples
|
188
|
-
|
189
159
|
def _score(self, X: npt.NDArray, y: list) -> float:
|
190
160
|
"""
|
191
161
|
Score function calculates forecast accuracy.
|
@@ -237,4 +207,75 @@ class Base:
|
|
237
207
|
if len(y) == 0:
|
238
208
|
return 0
|
239
209
|
y_pred = self.predict(X)
|
240
|
-
return
|
210
|
+
return accuracy_score(y, y_pred)
|
211
|
+
|
212
|
+
@abstractmethod
|
213
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True):
|
214
|
+
"""
|
215
|
+
Function to train the model using the input data ``X`` and corresponding labels ``y``.
|
216
|
+
|
217
|
+
This abstract method is implemented by the class that inherits it.
|
218
|
+
|
219
|
+
Parameters:
|
220
|
+
---
|
221
|
+
* X (``npt.NDArray``): Input data used for training the model, previously normalized to the range [0, 1].
|
222
|
+
* y (``npt.NDArray``): Corresponding labels or target values for the input data.
|
223
|
+
* verbose (``bool``, optional): Flag to enable or disable detailed output during \
|
224
|
+
training. Default is ``True``.
|
225
|
+
|
226
|
+
Returns:
|
227
|
+
---
|
228
|
+
* self: Returns the instance of the class that implements this method.
|
229
|
+
|
230
|
+
---
|
231
|
+
|
232
|
+
Função para treinar o modelo usando os dados de entrada ``X`` e os classes correspondentes ``y``.
|
233
|
+
|
234
|
+
Este método abstrato é implementado pela classe que o herdar.
|
235
|
+
|
236
|
+
Parâmetros:
|
237
|
+
---
|
238
|
+
* X (``npt.NDArray``): Dados de entrada utilizados para o treinamento do modelo, previamente \
|
239
|
+
normalizados no intervalo [0, 1].
|
240
|
+
* y (``npt.NDArray``): Rótulos ou valores-alvo correspondentes aos dados de entrada.
|
241
|
+
* verbose (``bool``, opcional): Flag para ativar ou desativar a saída detalhada durante o \
|
242
|
+
treinamento. O padrão é ``True``.
|
243
|
+
|
244
|
+
Retornos:
|
245
|
+
---
|
246
|
+
* self: Retorna a instância da classe que implementa este método.
|
247
|
+
"""
|
248
|
+
pass
|
249
|
+
|
250
|
+
@abstractmethod
|
251
|
+
def predict(self, X) -> Optional[npt.NDArray]:
|
252
|
+
"""
|
253
|
+
Function to generate predictions based on the input data ``X``.
|
254
|
+
|
255
|
+
This abstract method is implemented by the class that inherits it.
|
256
|
+
|
257
|
+
Parameters:
|
258
|
+
---
|
259
|
+
* X (``npt.NDArray``): Input data for which predictions will be generated.
|
260
|
+
|
261
|
+
Returns:
|
262
|
+
---
|
263
|
+
* Predictions (``Optional[npt.NDArray]``): Predicted values for each input sample, or ``None``
|
264
|
+
if the prediction fails.
|
265
|
+
|
266
|
+
---
|
267
|
+
|
268
|
+
Função para gerar previsões com base nos dados de entrada ``X``.
|
269
|
+
|
270
|
+
Este método abstrato é implementado pela classe que o herdar.
|
271
|
+
|
272
|
+
Parâmetros:
|
273
|
+
---
|
274
|
+
* X (``npt.NDArray``): Dados de entrada para os quais as previsões serão geradas.
|
275
|
+
|
276
|
+
Retornos:
|
277
|
+
---
|
278
|
+
* Previsões (``Optional[npt.NDArray]``): Valores previstos para cada amostra de entrada,
|
279
|
+
ou ``None`` se a previsão falhar.
|
280
|
+
"""
|
281
|
+
pass
|
aisp/NSA/_negative_selection.py
CHANGED
@@ -3,9 +3,10 @@ import numpy.typing as npt
|
|
3
3
|
from tqdm import tqdm
|
4
4
|
from typing import Dict, Literal, Optional, Union
|
5
5
|
from collections import namedtuple
|
6
|
-
from scipy.spatial.distance import
|
6
|
+
from scipy.spatial.distance import cdist
|
7
7
|
|
8
8
|
from ._base import Base
|
9
|
+
from ..utils import slice_index_list_by_class
|
9
10
|
|
10
11
|
|
11
12
|
class RNSA(Base):
|
@@ -236,7 +237,7 @@ class RNSA(Base):
|
|
236
237
|
self.r_s: float = 0
|
237
238
|
|
238
239
|
if algorithm == "V-detector":
|
239
|
-
self._Detector = namedtuple("Detector", "position radius")
|
240
|
+
self._Detector = namedtuple("Detector", ["position", "radius"])
|
240
241
|
self._algorithm: str = algorithm
|
241
242
|
else:
|
242
243
|
self._Detector = namedtuple("Detector", "position")
|
@@ -247,12 +248,12 @@ class RNSA(Base):
|
|
247
248
|
else:
|
248
249
|
self.max_discards: int = 1000
|
249
250
|
|
250
|
-
#
|
251
|
+
# Retrieves the variables from kwargs.
|
251
252
|
self.p: float = kwargs.get("p", 2)
|
252
253
|
self._cell_bounds: bool = kwargs.get("cell_bounds", False)
|
253
254
|
self.non_self_label: str = kwargs.get("non_self_label", "non-self")
|
254
255
|
|
255
|
-
#
|
256
|
+
# Initializes the other class variables as None.
|
256
257
|
self.detectors: Union[dict, None] = None
|
257
258
|
self.classes: npt.NDArray = None
|
258
259
|
|
@@ -286,36 +287,37 @@ class RNSA(Base):
|
|
286
287
|
---
|
287
288
|
(``self``): Retorna a própria instância.
|
288
289
|
"""
|
290
|
+
progress = None
|
289
291
|
super()._check_and_raise_exceptions_fit(X, y)
|
290
292
|
|
291
|
-
#
|
293
|
+
# Identifying the possible classes within the output array `y`.
|
292
294
|
self.classes = np.unique(y)
|
293
|
-
#
|
295
|
+
# Dictionary that will store detectors with classes as keys.
|
294
296
|
list_detectors_by_class = dict()
|
295
|
-
#
|
297
|
+
# Separates the classes for training.
|
296
298
|
sample_index = self.__slice_index_list_by_class(y)
|
297
|
-
#
|
299
|
+
# Progress bar for generating all detectors.
|
298
300
|
if verbose:
|
299
301
|
progress = tqdm(total=int(self.N * (len(self.classes))),
|
300
|
-
bar_format="{desc} ┇{bar}┇ {n}/{total} detectors", postfix="\n",)
|
302
|
+
bar_format="{desc} ┇{bar}┇ {n}/{total} detectors", postfix="\n", )
|
301
303
|
for _class_ in self.classes:
|
302
|
-
#
|
304
|
+
# Initializes the empty set that will contain the valid detectors.
|
303
305
|
valid_detectors_set = []
|
304
306
|
discard_count = 0
|
305
|
-
#
|
307
|
+
# Indicating which class the algorithm is currently processing for the progress bar.
|
306
308
|
if verbose:
|
307
309
|
progress.set_description_str(
|
308
310
|
f"Generating the detectors for the {_class_} class:"
|
309
311
|
)
|
310
312
|
while len(valid_detectors_set) < self.N:
|
311
|
-
#
|
313
|
+
# Generates a candidate detector vector randomly with values between 0 and 1.
|
312
314
|
vector_x = np.random.random_sample(size=X.shape[1])
|
313
|
-
#
|
315
|
+
# Checks the validity of the detector for non-self with respect to the class samples.
|
314
316
|
valid_detector = self.__checks_valid_detector(
|
315
317
|
X=X, vector_x=vector_x, samples_index_class=sample_index[_class_]
|
316
318
|
)
|
317
319
|
|
318
|
-
#
|
320
|
+
# If the detector is valid, add it to the list of valid detectors.
|
319
321
|
if self._algorithm == "V-detector" and valid_detector is not False:
|
320
322
|
discard_count = 0
|
321
323
|
valid_detectors_set.append(
|
@@ -338,15 +340,15 @@ class RNSA(Base):
|
|
338
340
|
"consider reducing its value."
|
339
341
|
)
|
340
342
|
|
341
|
-
#
|
343
|
+
# Add detectors, with classes as keys in the dictionary.
|
342
344
|
list_detectors_by_class[_class_] = valid_detectors_set
|
343
|
-
#
|
345
|
+
# Notify completion of detector generation for the classes.
|
344
346
|
if verbose:
|
345
347
|
progress.set_description(
|
346
348
|
f'\033[92m✔ Non-self detectors for classes ({", ".join(map(str, self.classes))}) '
|
347
349
|
f'successfully generated\033[0m'
|
348
350
|
)
|
349
|
-
#
|
351
|
+
# Saves the found detectors in the attribute for the non-self detectors of the trained model.
|
350
352
|
self.detectors = list_detectors_by_class
|
351
353
|
return self
|
352
354
|
|
@@ -382,7 +384,7 @@ class RNSA(Base):
|
|
382
384
|
contendo as classes previstas para ``X``.
|
383
385
|
* ``None``: Se não existir detectores para a previsão.
|
384
386
|
"""
|
385
|
-
#
|
387
|
+
# If there are no detectors, returns None.
|
386
388
|
if self.detectors is None:
|
387
389
|
return None
|
388
390
|
elif not isinstance(X, (np.ndarray, list)):
|
@@ -394,9 +396,9 @@ class RNSA(Base):
|
|
394
396
|
)
|
395
397
|
)
|
396
398
|
|
397
|
-
#
|
399
|
+
# Initializes an empty array that will store the predictions.
|
398
400
|
C = np.empty(shape=0)
|
399
|
-
#
|
401
|
+
# For each sample row in X.
|
400
402
|
for line in X:
|
401
403
|
class_found: bool
|
402
404
|
_class_ = self.__compare_sample_to_detectors(line)
|
@@ -406,11 +408,11 @@ class RNSA(Base):
|
|
406
408
|
C = np.append(C, [_class_])
|
407
409
|
class_found = True
|
408
410
|
|
409
|
-
#
|
411
|
+
# If there is only one class and the sample is not classified, set the output as non-self.
|
410
412
|
if not class_found and len(self.classes) == 1:
|
411
413
|
C = np.append(C, [self.non_self_label])
|
412
|
-
#
|
413
|
-
#
|
414
|
+
# If the class is not identified with the detectors, assign the class with the greatest distance
|
415
|
+
# from the mean of its detectors.
|
414
416
|
elif not class_found:
|
415
417
|
average_distance: dict = {}
|
416
418
|
for _class_ in self.classes:
|
@@ -421,12 +423,43 @@ class RNSA(Base):
|
|
421
423
|
[self.__distance(detector, line)
|
422
424
|
for detector in detectores]
|
423
425
|
)
|
424
|
-
C = np.append(
|
425
|
-
C, [max(average_distance, key=average_distance.get)])
|
426
|
+
C = np.append(C, [max(average_distance, key=average_distance.get)])
|
426
427
|
return C
|
427
428
|
|
429
|
+
def __slice_index_list_by_class(self, y: npt.NDArray) -> dict:
|
430
|
+
"""
|
431
|
+
The function ``__slice_index_list_by_class(...)``, separates the indices of the lines according \
|
432
|
+
to the output class, to loop through the sample array, only in positions where the output is \
|
433
|
+
the class being trained.
|
434
|
+
|
435
|
+
Parameters:
|
436
|
+
---
|
437
|
+
* y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
|
438
|
+
``X`` sample array.
|
439
|
+
|
440
|
+
returns:
|
441
|
+
---
|
442
|
+
* dict: A dictionary with the list of array positions(``y``), with the classes as key.
|
443
|
+
|
444
|
+
---
|
445
|
+
|
446
|
+
A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe \
|
447
|
+
de saída, para percorrer o array de amostra, apenas nas posições que a saída for a classe que \
|
448
|
+
está sendo treinada.
|
449
|
+
|
450
|
+
Parameters:
|
451
|
+
---
|
452
|
+
* y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do array \
|
453
|
+
de amostra ``X``.
|
454
|
+
|
455
|
+
Returns:
|
456
|
+
---
|
457
|
+
* dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
|
458
|
+
"""
|
459
|
+
return slice_index_list_by_class(self.classes, y)
|
460
|
+
|
428
461
|
def __checks_valid_detector(self, X: npt.NDArray = None, vector_x: npt.NDArray = None,
|
429
|
-
|
462
|
+
samples_index_class: npt.NDArray = None):
|
430
463
|
"""
|
431
464
|
Function to check if the detector has a valid non-proper ``r`` radius for the class.
|
432
465
|
|
@@ -457,26 +490,25 @@ class RNSA(Base):
|
|
457
490
|
* Validade (``bool``): Retorna se o detector é válido ou não.
|
458
491
|
|
459
492
|
"""
|
460
|
-
#
|
493
|
+
# If any of the input arrays have zero size, returns false.
|
461
494
|
if (np.size(samples_index_class) == 0 or np.size(X) == 0 or np.size(vector_x) == 0):
|
462
495
|
return False
|
463
|
-
#
|
464
|
-
#
|
496
|
+
# If self.k > 1, uses the k nearest neighbors (kNN); otherwise, checks the detector
|
497
|
+
# without considering kNN.
|
465
498
|
if self.k > 1:
|
466
|
-
# Iniciar a lista dos knn vazia.
|
467
499
|
knn_list = np.empty(shape=0)
|
468
500
|
for i in samples_index_class:
|
469
|
-
#
|
470
|
-
#
|
501
|
+
# Calculates the distance between the two vectors and adds it to the kNN list if the
|
502
|
+
# distance is smaller than the largest distance in the list.
|
471
503
|
knn_list = self.__compare_KnearestNeighbors_List(
|
472
504
|
knn_list, self.__distance(X[i], vector_x)
|
473
505
|
)
|
474
|
-
#
|
506
|
+
# If the average of the distances in the kNN list is less than the radius, returns true.
|
475
507
|
distance_mean = np.mean(knn_list)
|
476
508
|
if self._algorithm == "V-detector":
|
477
509
|
return self.__detector_is_valid_to_Vdetector(distance_mean, vector_x)
|
478
510
|
elif distance_mean > (self.r + self.r_s):
|
479
|
-
return True
|
511
|
+
return True
|
480
512
|
else:
|
481
513
|
distance: Union[float, None] = None
|
482
514
|
for i in samples_index_class:
|
@@ -487,16 +519,16 @@ class RNSA(Base):
|
|
487
519
|
elif distance > new_distance:
|
488
520
|
distance = new_distance
|
489
521
|
else:
|
490
|
-
#
|
491
|
-
#
|
522
|
+
# Calculates the distance between the vectors; if it is less than or equal to the radius
|
523
|
+
# plus the sample's radius, sets the validity of the detector to false.
|
492
524
|
if (self.r + self.r_s) >= self.__distance(X[i], vector_x):
|
493
525
|
return False # Detector não é valido!
|
494
526
|
|
495
527
|
if self._algorithm == "V-detector":
|
496
528
|
return self.__detector_is_valid_to_Vdetector(distance, vector_x)
|
497
|
-
return True
|
529
|
+
return True
|
498
530
|
|
499
|
-
return False # Detector
|
531
|
+
return False # Detector is not valid!
|
500
532
|
|
501
533
|
def __compare_KnearestNeighbors_List(self, knn: npt.NDArray, distance: float) -> npt.NDArray:
|
502
534
|
"""
|
@@ -528,12 +560,12 @@ class RNSA(Base):
|
|
528
560
|
---
|
529
561
|
npt.NDArray: Lista de vizinhos mais próximos atualizada e ordenada.
|
530
562
|
"""
|
531
|
-
#
|
563
|
+
# If the number of distances in kNN is less than k, adds the distance.
|
532
564
|
if len(knn) < self.k:
|
533
565
|
knn = np.append(knn, distance)
|
534
566
|
knn.sort()
|
535
567
|
else:
|
536
|
-
#
|
568
|
+
# Otherwise, add the distance if the new distance is smaller than the largest distance in the list.
|
537
569
|
if knn[self.k - 1] > distance:
|
538
570
|
knn[self.k - 1] = distance
|
539
571
|
knn.sort()
|
@@ -577,16 +609,14 @@ class RNSA(Base):
|
|
577
609
|
a nenhuma classe.
|
578
610
|
"""
|
579
611
|
|
580
|
-
#
|
612
|
+
# List to store the classes and the average distance between the detectors and the sample.
|
581
613
|
possible_classes = []
|
582
614
|
for _class_ in self.classes:
|
583
|
-
#
|
615
|
+
# Variable to indicate if the class was found with the detectors.
|
584
616
|
class_found: bool = True
|
585
|
-
sum_distance = 0
|
617
|
+
sum_distance = 0
|
586
618
|
for detector in self.detectors[_class_]:
|
587
|
-
# Calcula a distância entre a amostra e os detectores.
|
588
619
|
distance = self.__distance(detector.position, line)
|
589
|
-
# Soma as distâncias para calcular a média.
|
590
620
|
sum_distance += distance
|
591
621
|
if self._algorithm == "V-detector":
|
592
622
|
if distance <= detector.radius:
|
@@ -596,17 +626,16 @@ class RNSA(Base):
|
|
596
626
|
class_found = False
|
597
627
|
break
|
598
628
|
|
599
|
-
#
|
600
|
-
# possível previsão.
|
629
|
+
# If the sample passes through all the detectors of a class, adds the class as a possible prediction.
|
601
630
|
if class_found:
|
602
631
|
possible_classes.append([_class_, sum_distance / self.N])
|
603
|
-
#
|
632
|
+
# If classified as belonging to only one class, returns the class.
|
604
633
|
if len(possible_classes) == 1:
|
605
634
|
return possible_classes[0][0]
|
606
|
-
#
|
635
|
+
# If belonging to more than one class, returns the class with the greatest average distance.
|
607
636
|
elif len(possible_classes) > 1:
|
608
637
|
return max(possible_classes, key=lambda x: x[1])[0]
|
609
|
-
else:
|
638
|
+
else:
|
610
639
|
return None
|
611
640
|
|
612
641
|
def __distance(self, u: npt.NDArray, v: npt.NDArray):
|
@@ -674,47 +703,15 @@ class RNSA(Base):
|
|
674
703
|
"""
|
675
704
|
new_detector_r = float(distance - self.r_s)
|
676
705
|
if self.r >= new_detector_r:
|
677
|
-
return False
|
706
|
+
return False
|
678
707
|
else:
|
679
|
-
#
|
708
|
+
# If _cell_bounds is True, considers the detector to be within the plane bounds.
|
680
709
|
if self._cell_bounds:
|
681
710
|
for p in vector_x:
|
682
711
|
if (p - new_detector_r) < 0 or (p + new_detector_r) > 1:
|
683
712
|
return False
|
684
713
|
return True, new_detector_r
|
685
714
|
|
686
|
-
def __slice_index_list_by_class(self, y: npt.NDArray) -> dict:
|
687
|
-
"""
|
688
|
-
The function ``__slice_index_list_by_class(...)``, separates the indices of the lines \
|
689
|
-
according to the output class, to loop through the sample array, only in positions where \
|
690
|
-
the output is the class being trained.
|
691
|
-
|
692
|
-
Parameters:
|
693
|
-
---
|
694
|
-
* y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
|
695
|
-
``X`` sample array.
|
696
|
-
|
697
|
-
returns:
|
698
|
-
---
|
699
|
-
* dict: A dictionary with the list of array positions(``y``), with the classes as key.
|
700
|
-
|
701
|
-
---
|
702
|
-
|
703
|
-
A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe \
|
704
|
-
de saída, para percorrer o array de amostra, apenas nas posições que a saída for a classe que \
|
705
|
-
está sendo treinada.
|
706
|
-
|
707
|
-
Parameters:
|
708
|
-
---
|
709
|
-
* y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do array \
|
710
|
-
de amostra ``X``.
|
711
|
-
|
712
|
-
Returns:
|
713
|
-
---
|
714
|
-
* dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
|
715
|
-
"""
|
716
|
-
return super()._slice_index_list_by_class(y)
|
717
|
-
|
718
715
|
def score(self, X: npt.NDArray, y: list) -> float:
|
719
716
|
"""
|
720
717
|
Score function calculates forecast accuracy.
|
@@ -932,40 +929,41 @@ class BNSA(Base):
|
|
932
929
|
"""
|
933
930
|
super()._check_and_raise_exceptions_fit(X, y, "BNSA")
|
934
931
|
|
935
|
-
#
|
932
|
+
# Converts the entire array X to boolean
|
936
933
|
if X.dtype != bool:
|
937
934
|
X = X.astype(bool)
|
938
935
|
|
939
|
-
#
|
936
|
+
# Identifying the possible classes within the output array `y`.
|
940
937
|
self.classes = np.unique(y)
|
941
|
-
#
|
938
|
+
# Dictionary that will store detectors with classes as keys.
|
942
939
|
list_detectors_by_class = dict()
|
943
|
-
#
|
940
|
+
# Separates the classes for training.
|
944
941
|
sample_index: dict = self.__slice_index_list_by_class(y)
|
945
|
-
#
|
942
|
+
# Progress bar for generating all detectors.
|
946
943
|
if verbose:
|
947
944
|
progress = tqdm(total=int(self.N * (len(self.classes))),
|
948
945
|
bar_format='{desc} ┇{bar}┇ {n}/{total} detectors', postfix='\n')
|
949
946
|
|
950
947
|
for _class_ in self.classes:
|
951
|
-
#
|
948
|
+
# Initializes the empty set that will contain the valid detectors.
|
952
949
|
valid_detectors_set: list = []
|
953
950
|
discard_count: int = 0
|
954
|
-
#
|
951
|
+
# Updating the progress bar with the current class the algorithm is processing.
|
955
952
|
if verbose:
|
956
953
|
progress.set_description_str(
|
957
954
|
f"Generating the detectors for the {_class_} class:")
|
958
955
|
while len(valid_detectors_set) < self.N:
|
959
956
|
|
960
957
|
is_valid_detector: bool = True
|
961
|
-
#
|
958
|
+
# Generates a candidate detector vector randomly with values 0 and 1.
|
962
959
|
vector_x = np.random.choice([False, True], size=X.shape[1])
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
960
|
+
# Calculates the distance between the candidate and the class samples.
|
961
|
+
distances = cdist(np.expand_dims(vector_x, axis=0),
|
962
|
+
X[sample_index[_class_]], metric='hamming')
|
963
|
+
# Checks if any of the distances is below or equal to the threshold.
|
964
|
+
is_valid_detector = not np.any(distances <= self.aff_thresh)
|
965
|
+
|
966
|
+
# If the detector is valid, add it to the list of valid detectors.
|
969
967
|
if is_valid_detector:
|
970
968
|
discard_count = 0
|
971
969
|
valid_detectors_set.append(vector_x)
|
@@ -981,15 +979,15 @@ class BNSA(Base):
|
|
981
979
|
"radius and consider reducing its value."
|
982
980
|
)
|
983
981
|
|
984
|
-
#
|
982
|
+
# Add detectors to the dictionary with classes as keys.
|
985
983
|
list_detectors_by_class[_class_] = valid_detectors_set
|
986
984
|
|
987
|
-
#
|
985
|
+
# Notify the completion of detector generation for the classes.
|
988
986
|
if verbose:
|
989
987
|
progress.set_description(
|
990
988
|
f'\033[92m✔ Non-self detectors for classes ({", ".join(map(str, self.classes))}) '
|
991
989
|
f'successfully generated\033[0m')
|
992
|
-
#
|
990
|
+
# Saves the found detectors in the attribute for the class detectors.
|
993
991
|
self.detectors = list_detectors_by_class
|
994
992
|
return self
|
995
993
|
|
@@ -1025,7 +1023,7 @@ class BNSA(Base):
|
|
1025
1023
|
contendo as classes previstas para ``X``.
|
1026
1024
|
* ``None``: Se não existir detectores para a previsão.
|
1027
1025
|
"""
|
1028
|
-
#
|
1026
|
+
# If there are no detectors, returns None.
|
1029
1027
|
if self.detectors is None:
|
1030
1028
|
return None
|
1031
1029
|
elif not isinstance(X, (np.ndarray, list)):
|
@@ -1036,40 +1034,42 @@ class BNSA(Base):
|
|
1036
1034
|
len(self.detectors[self.classes[0]][0])
|
1037
1035
|
)
|
1038
1036
|
)
|
1039
|
-
#
|
1037
|
+
# Checks if matrix X contains only binary samples. Otherwise, raises an exception.
|
1040
1038
|
if not np.isin(X, [0, 1]).all():
|
1041
1039
|
raise ValueError(
|
1042
1040
|
"The array X contains values that are not composed only of 0 and 1."
|
1043
1041
|
)
|
1044
1042
|
|
1045
|
-
#
|
1043
|
+
# Converts the entire array X to boolean.
|
1046
1044
|
if X.dtype != bool:
|
1047
1045
|
X = X.astype(bool)
|
1048
1046
|
|
1049
|
-
#
|
1047
|
+
# Initializes an empty array that will store the predictions.
|
1050
1048
|
C = np.empty(shape=0)
|
1051
|
-
#
|
1049
|
+
# For each sample row in X.
|
1052
1050
|
for line in X:
|
1053
1051
|
class_found: bool = True
|
1054
|
-
#
|
1055
|
-
#
|
1052
|
+
# List to store the possible classes to which the sample matches with self
|
1053
|
+
# when compared to the non-self detectors.
|
1056
1054
|
possible_classes: list = []
|
1057
1055
|
for _class_ in self.classes:
|
1058
|
-
# Lista para armazenar as taxas de similaridade entre a amostra e os detectores.
|
1059
1056
|
similarity_sum: float = 0
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1057
|
+
# Calculates the Hamming distance between the row and all detectors.
|
1058
|
+
distances = cdist(np.expand_dims(line, axis=0),
|
1059
|
+
self.detectors[_class_], metric='hamming')
|
1060
|
+
|
1061
|
+
# Check if any distance is below or equal to the threshold.
|
1062
|
+
if np.any(distances <= self.aff_thresh):
|
1063
|
+
class_found = False
|
1064
|
+
else:
|
1065
|
+
similarity_sum = np.sum(distances)
|
1066
|
+
|
1067
|
+
# If the sample passes through all detectors of a class, adds the class as a possible prediction
|
1068
|
+
# and its average similarity.
|
1069
1069
|
if class_found:
|
1070
1070
|
possible_classes.append([_class_, similarity_sum / self.N])
|
1071
1071
|
|
1072
|
-
#
|
1072
|
+
# If belonging to one or more classes, adds the class with the greatest average distance.
|
1073
1073
|
if len(possible_classes) > 0:
|
1074
1074
|
C = np.append(
|
1075
1075
|
C, [max(possible_classes, key=lambda x: x[1])[0]])
|
@@ -1077,28 +1077,27 @@ class BNSA(Base):
|
|
1077
1077
|
else:
|
1078
1078
|
class_found = False
|
1079
1079
|
|
1080
|
-
#
|
1080
|
+
# If there is only one class and the sample is not classified, sets the output as non-self.
|
1081
1081
|
if not class_found and len(self.classes) == 1:
|
1082
1082
|
C = np.append(C, ["non-self"])
|
1083
|
-
#
|
1083
|
+
# If the class cannot be identified by the detectors
|
1084
1084
|
elif not class_found:
|
1085
1085
|
class_differences: dict = {}
|
1086
1086
|
for _class_ in self.classes:
|
1087
|
-
#
|
1087
|
+
# Assign the label to the class with the greatest distance from the nearest detector.
|
1088
1088
|
if self.no_label_sample_selection == 'nearest_difference':
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
class_differences[_class_] =
|
1093
|
-
#
|
1089
|
+
difference_min: float = cdist(np.expand_dims(line, axis=0),
|
1090
|
+
self.detectors[_class_], metric='hamming'
|
1091
|
+
).min()
|
1092
|
+
class_differences[_class_] = difference_min
|
1093
|
+
# Or based on the greatest distance from the average distances of the detectors.
|
1094
1094
|
else:
|
1095
|
-
difference_sum: float = 0
|
1096
|
-
|
1097
|
-
|
1095
|
+
difference_sum: float = cdist(np.expand_dims(line, axis=0),
|
1096
|
+
self.detectors[_class_], metric='hamming'
|
1097
|
+
).sum()
|
1098
1098
|
class_differences[_class_] = difference_sum / self.N
|
1099
1099
|
|
1100
|
-
C = np.append(
|
1101
|
-
C, [max(class_differences, key=class_differences.get)])
|
1100
|
+
C = np.append(C, [max(class_differences, key=class_differences.get)])
|
1102
1101
|
|
1103
1102
|
return C
|
1104
1103
|
|
@@ -1132,7 +1131,7 @@ class BNSA(Base):
|
|
1132
1131
|
---
|
1133
1132
|
* dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
|
1134
1133
|
"""
|
1135
|
-
return
|
1134
|
+
return slice_index_list_by_class(self.classes, y)
|
1136
1135
|
|
1137
1136
|
def score(self, X: npt.NDArray, y: list) -> float:
|
1138
1137
|
"""
|
aisp/utils/__init__.py
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
from typing import Union
|
2
|
+
import numpy as np
|
3
|
+
import numpy.typing as npt
|
4
|
+
|
5
|
+
|
6
|
+
def slice_index_list_by_class(classes: Union[npt.NDArray, list], y: npt.NDArray) -> dict:
|
7
|
+
"""
|
8
|
+
The function ``__slice_index_list_by_class(...)``, separates the indices of the lines \
|
9
|
+
according to the output class, to loop through the sample array, only in positions where \
|
10
|
+
the output is the class being trained.
|
11
|
+
|
12
|
+
Parameters:
|
13
|
+
---
|
14
|
+
* classes (``list or npt.NDArray``): list with unique classes.
|
15
|
+
* y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
|
16
|
+
``X`` sample array.
|
17
|
+
|
18
|
+
returns:
|
19
|
+
---
|
20
|
+
* dict: A dictionary with the list of array positions(``y``), with the classes as key.
|
21
|
+
|
22
|
+
---
|
23
|
+
|
24
|
+
A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a \
|
25
|
+
classe de saída, para percorrer o array de amostra, apenas nas posições que a saída for \
|
26
|
+
a classe que está sendo treinada.
|
27
|
+
|
28
|
+
Parameters:
|
29
|
+
---
|
30
|
+
* classes (``list or npt.NDArray``): lista com classes únicas.
|
31
|
+
* y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do \
|
32
|
+
array de amostra ``X``.
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
---
|
36
|
+
* dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
|
37
|
+
"""
|
38
|
+
position_samples = dict()
|
39
|
+
for _class_ in classes:
|
40
|
+
# Gets the sample positions by class from y.
|
41
|
+
position_samples[_class_] = list(np.nonzero(y == _class_)[0])
|
42
|
+
|
43
|
+
return position_samples
|
aisp/utils/metrics.py
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
from typing import Union
|
2
|
+
import numpy as np
|
3
|
+
import numpy.typing as npt
|
4
|
+
|
5
|
+
|
6
|
+
def accuracy_score(
|
7
|
+
y_true: Union[npt.NDArray, list],
|
8
|
+
y_pred: Union[npt.NDArray, list]
|
9
|
+
) -> float:
|
10
|
+
"""
|
11
|
+
Function to calculate precision accuracy based on lists of true labels and
|
12
|
+
predicted labels.
|
13
|
+
|
14
|
+
Parameters:
|
15
|
+
---
|
16
|
+
|
17
|
+
* y_true (``Union[npt.NDArray, list]``): Ground truth (correct) labels. \
|
18
|
+
Expected to be of the same length as `y_pred`.
|
19
|
+
* y_pred (``Union[npt.NDArray, list]``): Predicted labels. Expected to \
|
20
|
+
be of the same length as `y_true`.
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
---
|
24
|
+
* Accuracy (``float``): The ratio of correct predictions to the total \
|
25
|
+
number of predictions.
|
26
|
+
|
27
|
+
Raises:
|
28
|
+
---
|
29
|
+
* ValueError: If `y_true` or `y_pred` are empty or if they do not have the same length.
|
30
|
+
|
31
|
+
---
|
32
|
+
|
33
|
+
Função para calcular a acurácia de precisão com base em listas de rótulos
|
34
|
+
verdadeiros e nos rótulos previstos.
|
35
|
+
|
36
|
+
Parâmetros:
|
37
|
+
---
|
38
|
+
* y_true (``Union[npt.NDArray, list]``): Rótulos verdadeiros (corretos)..
|
39
|
+
* y_pred (``Union[npt.NDArray, list]``): Rótulos previstos.
|
40
|
+
|
41
|
+
Retornos:
|
42
|
+
---
|
43
|
+
* Precisão (``float``): A proporção de previsões corretas em relação
|
44
|
+
ao número total de previsões.
|
45
|
+
|
46
|
+
Lança:
|
47
|
+
---
|
48
|
+
* ValueError: Se `y_true` ou `y_pred` estiverem vazios ou se não
|
49
|
+
tiverem o mesmo tamanho.
|
50
|
+
"""
|
51
|
+
n = len(y_true)
|
52
|
+
if n == 0:
|
53
|
+
raise ValueError(
|
54
|
+
"Division by zero: y_true cannot be an empty list or array."
|
55
|
+
)
|
56
|
+
elif n != len(y_pred):
|
57
|
+
raise ValueError(
|
58
|
+
f"Error: The arrays must have the same size. Size of y_true: "
|
59
|
+
f"{len(y_true)}, Size of y_pred: {len(y_pred)}"
|
60
|
+
)
|
61
|
+
return np.sum(np.sum(np.array(y_true) == np.array(y_pred))) / n
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: aisp
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.33
|
4
4
|
Summary: Package with techniques of artificial immune systems.
|
5
5
|
Author-email: João Paulo da Silva Barros <jpsilvabarr@gmail.com>
|
6
6
|
Maintainer-email: Alison Zille Lopes <alisonzille@gmail.com>
|
@@ -63,7 +63,6 @@ Requires-Dist: tqdm >=4.64.1
|
|
63
63
|
> 2. [Installation.](#installation)
|
64
64
|
> 1. [Dependencies](#dependencies)
|
65
65
|
> 2. [User installation](#user-installation)
|
66
|
-
> 3. [How to import the Techniques](#how-to-import-the-techniques)
|
67
66
|
> 3. [Examples.](#examples)
|
68
67
|
|
69
68
|
---
|
@@ -81,8 +80,8 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
|
|
81
80
|
##### Algorithms implemented:
|
82
81
|
|
83
82
|
> - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
|
83
|
+
> - [ ] *Clonal Selection Algorithms.*
|
84
84
|
> - [ ] *Dendritic Cells.*
|
85
|
-
> - [ ] *Clonalg.*
|
86
85
|
> - [ ] *Immune Network Theory.*
|
87
86
|
|
88
87
|
</section>
|
@@ -119,17 +118,7 @@ pip install aisp
|
|
119
118
|
```
|
120
119
|
|
121
120
|
</section>
|
122
|
-
<section id='how-to-import-the-techniques'>
|
123
121
|
|
124
|
-
##### **How to import the Techniques**
|
125
|
-
|
126
|
-
``` Python
|
127
|
-
from aisp.NSA import RNSA
|
128
|
-
|
129
|
-
nsa = RNSA(N=300, r=0.05)
|
130
|
-
```
|
131
|
-
|
132
|
-
</section>
|
133
122
|
</section>
|
134
123
|
<section id='examples'>
|
135
124
|
|
@@ -173,7 +162,6 @@ Below are some examples that use a database for classification with the [Jupyter
|
|
173
162
|
> 2. [Instalação.](#instalação)
|
174
163
|
> 1. [Dependências](#dependências)
|
175
164
|
> 2. [Instalação do usuário](#instalação-do-usuário)
|
176
|
-
> 3. [Como importar as Tecnicas](#como-importar-as-tecnicas)
|
177
165
|
> 3. [Exemplos.](#exemplos)
|
178
166
|
|
179
167
|
---
|
@@ -190,8 +178,8 @@ Os sistemas imunológicos artificiais (SIA) inspiram-se no sistema imunológico
|
|
190
178
|
##### Algoritmos implementados:
|
191
179
|
|
192
180
|
> - [x] [**Seleção Negativa.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
|
181
|
+
> - [ ] *Algoritmos de Seleção Clonal.*
|
193
182
|
> - [ ] *Células Dendríticas.*
|
194
|
-
> - [ ] *Clonalg.*
|
195
183
|
> - [ ] *Teoria da Rede Imune.*
|
196
184
|
|
197
185
|
</section>
|
@@ -229,17 +217,6 @@ pip install aisp
|
|
229
217
|
|
230
218
|
</section>
|
231
219
|
|
232
|
-
<section id='como-importar-as-tecnicas'>
|
233
|
-
|
234
|
-
##### **Como importar as Tecnicas**
|
235
|
-
|
236
|
-
``` Python
|
237
|
-
from aisp.NSA import RNSA
|
238
|
-
|
239
|
-
nsa = RNSA(N=300, r=0.05)
|
240
|
-
```
|
241
|
-
|
242
|
-
</section>
|
243
220
|
</section>
|
244
221
|
<section id='exemplos'>
|
245
222
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
aisp/NSA/__init__.py,sha256=b0KSfocc9p4s4AQatyEBDfQuVmMqZ18rCieTUpWnQ7c,143
|
2
|
+
aisp/NSA/_base.py,sha256=iqmsLoM7yBC_EPFZXvdsQjI1bidz1A87ZFyht0P--fI,10861
|
3
|
+
aisp/NSA/_negative_selection.py,sha256=AqsOJYkZlb2kP48b35FFIwU_fGedLpZw5JY9hpnzkXM,54897
|
4
|
+
aisp/utils/__init__.py,sha256=c8OCaR9IAQ21vpxWqtLplB84W_UKdWceI--yw7sBMi0,163
|
5
|
+
aisp/utils/_multiclass.py,sha256=aoxSqnemNjs5uJAk7a1GumNDa-q-LOw6TWluwQ10tR4,1597
|
6
|
+
aisp/utils/metrics.py,sha256=xp9A52-QsP0cJGIlYprrI7BrVwQN6gqCLINVJumlIDI,1908
|
7
|
+
aisp-0.1.33.dist-info/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
|
8
|
+
aisp-0.1.33.dist-info/METADATA,sha256=M2czdHGPXbXXVOKUckfYMwwPpnj_Lt49dU5PRfLd_jw,7562
|
9
|
+
aisp-0.1.33.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
10
|
+
aisp-0.1.33.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
|
11
|
+
aisp-0.1.33.dist-info/RECORD,,
|
aisp-0.1.31.dist-info/RECORD
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
aisp/NSA/__init__.py,sha256=cmHx6ydTfhFAOfgCjDcKDJRfH5Sz41LHqVRlDZgVL58,141
|
2
|
-
aisp/NSA/_base.py,sha256=HjdF4ZnophMY_Ooj2ERjhVoWwqe8Q4rPQ-UxHlfIeNY,9557
|
3
|
-
aisp/NSA/_negative_selection.py,sha256=pWYgiyd0Jr0w1FIt6StQ_HEQr5-UjF4mVI0QJIcXeHM,54974
|
4
|
-
aisp-0.1.31.dist-info/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
|
5
|
-
aisp-0.1.31.dist-info/METADATA,sha256=hKXEal6xERzXfr3b7HX1VP43xq73f3tR1A1YAsIRics,7999
|
6
|
-
aisp-0.1.31.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
7
|
-
aisp-0.1.31.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
|
8
|
-
aisp-0.1.31.dist-info/RECORD,,
|
File without changes
|
File without changes
|