aisp 0.2.1__py3-none-any.whl → 0.3.1__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/__init__.py +25 -3
- aisp/base/__init__.py +3 -1
- aisp/base/_base.py +65 -0
- aisp/base/_classifier.py +5 -16
- aisp/base/_clusterer.py +77 -0
- aisp/csa/__init__.py +1 -1
- aisp/csa/{_ai_immune_recognition_sys.py → _ai_recognition_sys.py} +27 -29
- aisp/csa/_base.py +0 -1
- aisp/ina/__init__.py +14 -0
- aisp/ina/_ai_network.py +552 -0
- aisp/ina/_base.py +124 -0
- aisp/nsa/__init__.py +2 -1
- aisp/nsa/_binary_negative_selection.py +239 -0
- aisp/nsa/_negative_selection.py +22 -253
- aisp/utils/distance.py +4 -4
- aisp/utils/validation.py +5 -5
- {aisp-0.2.1.dist-info → aisp-0.3.1.dist-info}/METADATA +6 -5
- aisp-0.3.1.dist-info/RECORD +31 -0
- aisp-0.2.1.dist-info/RECORD +0 -25
- {aisp-0.2.1.dist-info → aisp-0.3.1.dist-info}/WHEEL +0 -0
- {aisp-0.2.1.dist-info → aisp-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {aisp-0.2.1.dist-info → aisp-0.3.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,239 @@
|
|
1
|
+
"""Negative Selection Algorithm."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
from typing import Dict, Literal, Optional, Union
|
5
|
+
from tqdm import tqdm
|
6
|
+
|
7
|
+
import numpy as np
|
8
|
+
import numpy.typing as npt
|
9
|
+
|
10
|
+
from ._ns_core import (
|
11
|
+
check_detector_bnsa_validity,
|
12
|
+
bnsa_class_prediction
|
13
|
+
)
|
14
|
+
from ..exceptions import MaxDiscardsReachedError
|
15
|
+
from ..utils.sanitizers import sanitize_seed, sanitize_param
|
16
|
+
from ._base import BaseNSA
|
17
|
+
|
18
|
+
|
19
|
+
class BNSA(BaseNSA):
|
20
|
+
"""BNSA (Binary Negative Selection Algorithm).
|
21
|
+
|
22
|
+
Class is for classification and identification purposes of anomalies through the self and not
|
23
|
+
self method.
|
24
|
+
|
25
|
+
Parameters
|
26
|
+
----------
|
27
|
+
N : int, default=100
|
28
|
+
Number of detectors.
|
29
|
+
aff_thresh : float, default=0.1
|
30
|
+
The variable represents the percentage of similarity between the T cell and the own
|
31
|
+
samples. The default value is 10% (0.1), while a value of 1.0 represents 100% similarity.
|
32
|
+
max_discards : int, default=1000
|
33
|
+
This parameter indicates the maximum number of detector discards in sequence, which aims
|
34
|
+
to avoid a possible infinite loop if a radius is defined that it is not possible to
|
35
|
+
generate non-self detectors.
|
36
|
+
seed : Optional[int], default=None
|
37
|
+
Seed for the random generation of values in the detectors.
|
38
|
+
no_label_sample_selection : str, default="max_average_difference"
|
39
|
+
Method for selecting labels for samples designated as non-self by all detectors.
|
40
|
+
Available method types:
|
41
|
+
|
42
|
+
- max_average_difference - Selects the class with the highest average difference among the
|
43
|
+
detectors.
|
44
|
+
|
45
|
+
- max_nearest_difference - Selects the class with the highest difference between the
|
46
|
+
nearest and farthest detector from the sample.
|
47
|
+
"""
|
48
|
+
|
49
|
+
def __init__(
|
50
|
+
self,
|
51
|
+
N: int = 100,
|
52
|
+
aff_thresh: float = 0.1,
|
53
|
+
max_discards: int = 1000,
|
54
|
+
seed: Optional[int] = None,
|
55
|
+
no_label_sample_selection: Literal[
|
56
|
+
"max_average_difference", "max_nearest_difference"
|
57
|
+
] = "max_average_difference",
|
58
|
+
):
|
59
|
+
self.N: int = sanitize_param(N, 100, lambda x: x > 0)
|
60
|
+
self.aff_thresh: float = sanitize_param(aff_thresh, 0.1, lambda x: 0 < x < 1)
|
61
|
+
self.max_discards: float = sanitize_param(max_discards, 1000, lambda x: x > 0)
|
62
|
+
|
63
|
+
self.seed: Optional[int] = sanitize_seed(seed)
|
64
|
+
|
65
|
+
if self.seed is not None:
|
66
|
+
np.random.seed(seed)
|
67
|
+
|
68
|
+
self.no_label_sample_selection: str = sanitize_param(
|
69
|
+
no_label_sample_selection,
|
70
|
+
"max_average_difference",
|
71
|
+
lambda x: x == "nearest_difference",
|
72
|
+
)
|
73
|
+
|
74
|
+
self.classes: Union[npt.NDArray, list] = []
|
75
|
+
self._detectors: Optional[dict] = None
|
76
|
+
self._detectors_stack: Optional[npt.NDArray] = None
|
77
|
+
|
78
|
+
@property
|
79
|
+
def detectors(self) -> Optional[Dict[str, npt.NDArray[np.bool_]]]:
|
80
|
+
"""Returns the trained detectors, organized by class."""
|
81
|
+
return self._detectors
|
82
|
+
|
83
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> BNSA:
|
84
|
+
"""Training according to X and y, using the method negative selection method.
|
85
|
+
|
86
|
+
Parameters
|
87
|
+
----------
|
88
|
+
X : npt.NDArray
|
89
|
+
Training array, containing the samples and their characteristics, [``N samples`` (
|
90
|
+
rows)][``N features`` (columns)].
|
91
|
+
y : npt.NDArray
|
92
|
+
Array of target classes of ``X`` with [``N samples`` (lines)].
|
93
|
+
verbose : bool, default=True
|
94
|
+
Feedback from detector generation to the user.
|
95
|
+
|
96
|
+
Returns
|
97
|
+
-------
|
98
|
+
self : BNSA
|
99
|
+
Returns the instance it self.
|
100
|
+
"""
|
101
|
+
super()._check_and_raise_exceptions_fit(X, y, "BNSA")
|
102
|
+
# Converts the entire array X to boolean
|
103
|
+
X = X.astype(np.bool_)
|
104
|
+
|
105
|
+
# Identifying the possible classes within the output array `y`.
|
106
|
+
self.classes = np.unique(y)
|
107
|
+
# Dictionary that will store detectors with classes as keys.
|
108
|
+
list_detectors_by_class = {}
|
109
|
+
# Separates the classes for training.
|
110
|
+
sample_index: dict = self._slice_index_list_by_class(y)
|
111
|
+
# Progress bar for generating all detectors.
|
112
|
+
|
113
|
+
progress = tqdm(
|
114
|
+
total=int(self.N * (len(self.classes))),
|
115
|
+
bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
|
116
|
+
postfix="\n",
|
117
|
+
disable=not verbose
|
118
|
+
)
|
119
|
+
|
120
|
+
for _class_ in self.classes:
|
121
|
+
# Initializes the empty set that will contain the valid detectors.
|
122
|
+
valid_detectors_set: list = []
|
123
|
+
discard_count: int = 0
|
124
|
+
# Updating the progress bar with the current class the algorithm is processing.
|
125
|
+
progress.set_description_str(
|
126
|
+
f"Generating the detectors for the {_class_} class:"
|
127
|
+
)
|
128
|
+
x_class = X[sample_index[_class_]]
|
129
|
+
while len(valid_detectors_set) < self.N:
|
130
|
+
# Generates a candidate detector vector randomly with values 0 and 1.
|
131
|
+
vector_x = np.random.randint(0, 2, size=X.shape[1]).astype(np.bool_)
|
132
|
+
# If the detector is valid, add it to the list of valid detectors.
|
133
|
+
if check_detector_bnsa_validity(x_class, vector_x, self.aff_thresh):
|
134
|
+
discard_count = 0
|
135
|
+
valid_detectors_set.append(vector_x)
|
136
|
+
progress.update(1)
|
137
|
+
else:
|
138
|
+
discard_count += 1
|
139
|
+
if discard_count == self.max_discards:
|
140
|
+
raise MaxDiscardsReachedError(_class_)
|
141
|
+
|
142
|
+
# Add detectors to the dictionary with classes as keys.
|
143
|
+
list_detectors_by_class[_class_] = np.array(valid_detectors_set)
|
144
|
+
|
145
|
+
# Notify the completion of detector generation for the classes.
|
146
|
+
progress.set_description(
|
147
|
+
f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
|
148
|
+
f"successfully generated\033[0m"
|
149
|
+
)
|
150
|
+
progress.close()
|
151
|
+
# Saves the found detectors in the attribute for the class detectors.
|
152
|
+
self._detectors = list_detectors_by_class
|
153
|
+
self._detectors_stack = np.array(
|
154
|
+
[np.stack(self._detectors[class_name]) for class_name in self.classes]
|
155
|
+
)
|
156
|
+
return self
|
157
|
+
|
158
|
+
def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
|
159
|
+
"""Prediction of classes based on detectors created after training.
|
160
|
+
|
161
|
+
Parameters
|
162
|
+
----------
|
163
|
+
X : npt.NDArray
|
164
|
+
Array with input samples with [``N_samples`` (Lines)] and [``N_characteristics``(
|
165
|
+
Columns)]
|
166
|
+
|
167
|
+
Returns
|
168
|
+
-------
|
169
|
+
c : Optional[npt.NDArray]
|
170
|
+
an ndarray of the form ``C`` [``N samples``], containing the predicted classes for
|
171
|
+
``X``. Returns``None``: If there are no detectors for the prediction.
|
172
|
+
"""
|
173
|
+
# If there are no detectors, Returns None.
|
174
|
+
if self._detectors is None or self._detectors_stack is None:
|
175
|
+
return None
|
176
|
+
|
177
|
+
super()._check_and_raise_exceptions_predict(
|
178
|
+
X, len(self._detectors[self.classes[0]][0]), "BNSA"
|
179
|
+
)
|
180
|
+
|
181
|
+
# Converts the entire array X to boolean.
|
182
|
+
if X.dtype != bool:
|
183
|
+
X = X.astype(bool)
|
184
|
+
|
185
|
+
# Initializes an empty array that will store the predictions.
|
186
|
+
c = []
|
187
|
+
# For each sample row in X.
|
188
|
+
for line in X:
|
189
|
+
class_found: bool = True
|
190
|
+
# Class prediction based on detectors
|
191
|
+
class_index = bnsa_class_prediction(
|
192
|
+
line, self._detectors_stack, self.aff_thresh
|
193
|
+
)
|
194
|
+
# If belonging to one or more classes, adds the class with the greatest
|
195
|
+
# average distance
|
196
|
+
if class_index > -1:
|
197
|
+
c.append(self.classes[class_index])
|
198
|
+
class_found = True
|
199
|
+
else:
|
200
|
+
class_found = False
|
201
|
+
|
202
|
+
# If there is only one class and the sample is not classified, sets the
|
203
|
+
# output as non-self.
|
204
|
+
if not class_found and len(self.classes) == 1:
|
205
|
+
c.append("non-self")
|
206
|
+
# If the class cannot be identified by the detectors
|
207
|
+
elif not class_found:
|
208
|
+
self.__assign_class_to_non_self_sample(line, c)
|
209
|
+
|
210
|
+
return np.array(c)
|
211
|
+
|
212
|
+
def __assign_class_to_non_self_sample(self, line: npt.NDArray, c: list):
|
213
|
+
"""Determine the class of a sample when all detectors classify it as "non-self".
|
214
|
+
|
215
|
+
Classification is performed using the ``max_average_difference`` and
|
216
|
+
``max_nearest_difference`` methods.
|
217
|
+
|
218
|
+
Parameters
|
219
|
+
----------
|
220
|
+
line : npt.NDArray
|
221
|
+
Sample to be classified.
|
222
|
+
c : list
|
223
|
+
List of predictions to be updated with the new classification.
|
224
|
+
"""
|
225
|
+
if self._detectors is None:
|
226
|
+
raise ValueError("Detectors is not initialized.")
|
227
|
+
|
228
|
+
class_differences: dict = {}
|
229
|
+
for _class_ in self.classes:
|
230
|
+
distances = np.sum(line != self._detectors[_class_]) / self.N
|
231
|
+
# Assign the label to the class with the greatest distance from
|
232
|
+
# the nearest detector.
|
233
|
+
if self.no_label_sample_selection == "nearest_difference":
|
234
|
+
class_differences[_class_] = distances.min()
|
235
|
+
# Or based on the greatest distance from the average distances of the detectors.
|
236
|
+
else:
|
237
|
+
class_differences[_class_] = distances.sum() / self.N
|
238
|
+
|
239
|
+
c.append(max(class_differences, key=class_differences.get))
|
aisp/nsa/_negative_selection.py
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
"""Negative Selection Algorithm."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
3
4
|
from typing import Any, Dict, Literal, Optional, Union
|
4
5
|
from tqdm import tqdm
|
5
6
|
|
6
7
|
import numpy as np
|
7
8
|
import numpy.typing as npt
|
8
9
|
|
9
|
-
from
|
10
|
-
|
11
|
-
bnsa_class_prediction,
|
12
|
-
check_detector_rnsa_validity,
|
13
|
-
)
|
10
|
+
from ..base import set_seed_numba
|
11
|
+
from ._ns_core import check_detector_rnsa_validity
|
14
12
|
from ..exceptions import MaxDiscardsReachedError
|
15
13
|
from ..utils.distance import (
|
16
14
|
min_distance_to_class_vectors,
|
@@ -98,6 +96,7 @@ class RNSA(BaseNSA):
|
|
98
96
|
self.seed: Optional[int] = sanitize_seed(seed)
|
99
97
|
if self.seed is not None:
|
100
98
|
np.random.seed(seed)
|
99
|
+
set_seed_numba(self.seed)
|
101
100
|
self.k: int = sanitize_param(k, 1, lambda x: x > 1)
|
102
101
|
self.N: int = sanitize_param(N, 100, lambda x: x >= 1)
|
103
102
|
self.r: float = sanitize_param(r, 0.05, lambda x: x > 0)
|
@@ -121,7 +120,7 @@ class RNSA(BaseNSA):
|
|
121
120
|
"""Returns the trained detectors, organized by class."""
|
122
121
|
return self._detectors
|
123
122
|
|
124
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) ->
|
123
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> RNSA:
|
125
124
|
"""
|
126
125
|
Perform training according to X and y, using the negative selection method (NegativeSelect).
|
127
126
|
|
@@ -148,7 +147,6 @@ class RNSA(BaseNSA):
|
|
148
147
|
self : RNSA
|
149
148
|
Returns the instance itself.
|
150
149
|
"""
|
151
|
-
progress = None
|
152
150
|
super()._check_and_raise_exceptions_fit(X, y)
|
153
151
|
|
154
152
|
# Identifying the possible classes within the output array `y`.
|
@@ -158,22 +156,21 @@ class RNSA(BaseNSA):
|
|
158
156
|
# Separates the classes for training.
|
159
157
|
sample_index = self._slice_index_list_by_class(y)
|
160
158
|
# Progress bar for generating all detectors.
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
159
|
+
progress = tqdm(
|
160
|
+
total=int(self.N * (len(self.classes))),
|
161
|
+
bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
|
162
|
+
postfix="\n",
|
163
|
+
disable=not verbose
|
164
|
+
)
|
167
165
|
for _class_ in self.classes:
|
168
166
|
# Initializes the empty set that will contain the valid detectors.
|
169
167
|
valid_detectors_set = []
|
170
168
|
discard_count = 0
|
171
169
|
x_class = X[sample_index[_class_]]
|
172
170
|
# Indicating which class the algorithm is currently processing for the progress bar.
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
)
|
171
|
+
progress.set_description_str(
|
172
|
+
f"Generating the detectors for the {_class_} class:"
|
173
|
+
)
|
177
174
|
while len(valid_detectors_set) < self.N:
|
178
175
|
# Generates a candidate detector vector randomly with values between 0 and 1.
|
179
176
|
vector_x = np.random.random_sample(size=X.shape[1])
|
@@ -188,8 +185,7 @@ class RNSA(BaseNSA):
|
|
188
185
|
else:
|
189
186
|
radius = None
|
190
187
|
valid_detectors_set.append(Detector(vector_x, radius))
|
191
|
-
|
192
|
-
progress.update(1)
|
188
|
+
progress.update(1)
|
193
189
|
else:
|
194
190
|
discard_count += 1
|
195
191
|
if discard_count == self.max_discards:
|
@@ -198,11 +194,11 @@ class RNSA(BaseNSA):
|
|
198
194
|
# Add detectors, with classes as keys in the dictionary.
|
199
195
|
list_detectors_by_class[_class_] = valid_detectors_set
|
200
196
|
# Notify completion of detector generation for the classes.
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
197
|
+
progress.set_description(
|
198
|
+
f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
|
199
|
+
f"successfully generated\033[0m"
|
200
|
+
)
|
201
|
+
progress.close()
|
206
202
|
# Saves the found detectors in the attribute for the non-self detectors of the trained model
|
207
203
|
self._detectors = list_detectors_by_class
|
208
204
|
return self
|
@@ -340,14 +336,12 @@ class RNSA(BaseNSA):
|
|
340
336
|
if len(knn) < self.k:
|
341
337
|
knn.append(distance)
|
342
338
|
knn.sort()
|
343
|
-
|
344
339
|
# Otherwise, add the distance if the new distance is smaller than the largest
|
345
340
|
# distance in the list.
|
346
|
-
|
341
|
+
elif knn[self.k - 1] > distance:
|
347
342
|
knn[self.k - 1] = distance
|
348
343
|
knn.sort()
|
349
344
|
|
350
|
-
|
351
345
|
def __compare_sample_to_detectors(self, line: npt.NDArray) -> Optional[str]:
|
352
346
|
"""
|
353
347
|
Compare a sample with the detectors, verifying if the sample is proper.
|
@@ -448,229 +442,4 @@ class RNSA(BaseNSA):
|
|
448
442
|
if (p - new_detector_r) < 0 or (p + new_detector_r) > 1:
|
449
443
|
return False
|
450
444
|
|
451
|
-
return
|
452
|
-
|
453
|
-
|
454
|
-
class BNSA(BaseNSA):
|
455
|
-
"""BNSA (Binary Negative Selection Algorithm).
|
456
|
-
|
457
|
-
Class is for classification and identification purposes of anomalies through the self and not
|
458
|
-
self method.
|
459
|
-
|
460
|
-
Parameters
|
461
|
-
----------
|
462
|
-
N : int, default=100
|
463
|
-
Number of detectors.
|
464
|
-
aff_thresh : float, default=0.1
|
465
|
-
The variable represents the percentage of similarity between the T cell and the own
|
466
|
-
samples. The default value is 10% (0.1), while a value of 1.0 represents 100% similarity.
|
467
|
-
max_discards : int, default=1000
|
468
|
-
This parameter indicates the maximum number of detector discards in sequence, which aims
|
469
|
-
to avoid a possible infinite loop if a radius is defined that it is not possible to
|
470
|
-
generate non-self detectors.
|
471
|
-
seed : Optional[int], default=None
|
472
|
-
Seed for the random generation of values in the detectors.
|
473
|
-
no_label_sample_selection : str, default="max_average_difference"
|
474
|
-
Method for selecting labels for samples designated as non-self by all detectors.
|
475
|
-
Available method types:
|
476
|
-
|
477
|
-
- max_average_difference - Selects the class with the highest average difference among the
|
478
|
-
detectors.
|
479
|
-
|
480
|
-
- max_nearest_difference - Selects the class with the highest difference between the
|
481
|
-
nearest and farthest detector from the sample.
|
482
|
-
"""
|
483
|
-
|
484
|
-
def __init__(
|
485
|
-
self,
|
486
|
-
N: int = 100,
|
487
|
-
aff_thresh: float = 0.1,
|
488
|
-
max_discards: int = 1000,
|
489
|
-
seed: Optional[int] = None,
|
490
|
-
no_label_sample_selection: Literal[
|
491
|
-
"max_average_difference", "max_nearest_difference"
|
492
|
-
] = "max_average_difference",
|
493
|
-
):
|
494
|
-
self.N: int = sanitize_param(N, 100, lambda x: x > 0)
|
495
|
-
self.aff_thresh: float = sanitize_param(aff_thresh, 0.1, lambda x: 0 < x < 1)
|
496
|
-
self.max_discards: float = sanitize_param(max_discards, 1000, lambda x: x > 0)
|
497
|
-
|
498
|
-
self.seed: Optional[int] = sanitize_seed(seed)
|
499
|
-
|
500
|
-
if self.seed is not None:
|
501
|
-
np.random.seed(seed)
|
502
|
-
|
503
|
-
self.no_label_sample_selection: str = sanitize_param(
|
504
|
-
no_label_sample_selection,
|
505
|
-
"max_average_difference",
|
506
|
-
lambda x: x == "nearest_difference",
|
507
|
-
)
|
508
|
-
|
509
|
-
self.classes: Union[npt.NDArray, list] = []
|
510
|
-
self._detectors: Optional[dict] = None
|
511
|
-
self._detectors_stack: Optional[npt.NDArray] = None
|
512
|
-
|
513
|
-
@property
|
514
|
-
def detectors(self) -> Optional[Dict[str, npt.NDArray[np.bool_]]]:
|
515
|
-
"""Returns the trained detectors, organized by class."""
|
516
|
-
return self._detectors
|
517
|
-
|
518
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "BNSA":
|
519
|
-
"""Training according to X and y, using the method negative selection method.
|
520
|
-
|
521
|
-
Parameters
|
522
|
-
----------
|
523
|
-
X : npt.NDArray
|
524
|
-
Training array, containing the samples and their characteristics, [``N samples`` (
|
525
|
-
rows)][``N features`` (columns)].
|
526
|
-
y : npt.NDArray
|
527
|
-
Array of target classes of ``X`` with [``N samples`` (lines)].
|
528
|
-
verbose : bool, default=True
|
529
|
-
Feedback from detector generation to the user.
|
530
|
-
|
531
|
-
Returns
|
532
|
-
-------
|
533
|
-
self : BNSA
|
534
|
-
Returns the instance it self.
|
535
|
-
"""
|
536
|
-
super()._check_and_raise_exceptions_fit(X, y, "BNSA")
|
537
|
-
progress = None
|
538
|
-
# Converts the entire array X to boolean
|
539
|
-
X = X.astype(np.bool_)
|
540
|
-
|
541
|
-
# Identifying the possible classes within the output array `y`.
|
542
|
-
self.classes = np.unique(y)
|
543
|
-
# Dictionary that will store detectors with classes as keys.
|
544
|
-
list_detectors_by_class = {}
|
545
|
-
# Separates the classes for training.
|
546
|
-
sample_index: dict = self._slice_index_list_by_class(y)
|
547
|
-
# Progress bar for generating all detectors.
|
548
|
-
if verbose:
|
549
|
-
progress = tqdm(
|
550
|
-
total=int(self.N * (len(self.classes))),
|
551
|
-
bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
|
552
|
-
postfix="\n",
|
553
|
-
)
|
554
|
-
|
555
|
-
for _class_ in self.classes:
|
556
|
-
# Initializes the empty set that will contain the valid detectors.
|
557
|
-
valid_detectors_set: list = []
|
558
|
-
discard_count: int = 0
|
559
|
-
# Updating the progress bar with the current class the algorithm is processing.
|
560
|
-
if verbose and progress is not None:
|
561
|
-
progress.set_description_str(
|
562
|
-
f"Generating the detectors for the {_class_} class:"
|
563
|
-
)
|
564
|
-
x_class = X[sample_index[_class_]]
|
565
|
-
while len(valid_detectors_set) < self.N:
|
566
|
-
# Generates a candidate detector vector randomly with values 0 and 1.
|
567
|
-
vector_x = np.random.randint(0, 2, size=X.shape[1]).astype(np.bool_)
|
568
|
-
# If the detector is valid, add it to the list of valid detectors.
|
569
|
-
if check_detector_bnsa_validity(x_class, vector_x, self.aff_thresh):
|
570
|
-
discard_count = 0
|
571
|
-
valid_detectors_set.append(vector_x)
|
572
|
-
if verbose and progress is not None:
|
573
|
-
progress.update(1)
|
574
|
-
else:
|
575
|
-
discard_count += 1
|
576
|
-
if discard_count == self.max_discards:
|
577
|
-
raise MaxDiscardsReachedError(_class_)
|
578
|
-
|
579
|
-
# Add detectors to the dictionary with classes as keys.
|
580
|
-
list_detectors_by_class[_class_] = np.array(valid_detectors_set)
|
581
|
-
|
582
|
-
# Notify the completion of detector generation for the classes.
|
583
|
-
if verbose and progress is not None:
|
584
|
-
progress.set_description(
|
585
|
-
f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
|
586
|
-
f"successfully generated\033[0m"
|
587
|
-
)
|
588
|
-
# Saves the found detectors in the attribute for the class detectors.
|
589
|
-
self._detectors = list_detectors_by_class
|
590
|
-
self._detectors_stack = np.array(
|
591
|
-
[np.stack(self._detectors[class_name]) for class_name in self.classes]
|
592
|
-
)
|
593
|
-
return self
|
594
|
-
|
595
|
-
def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
|
596
|
-
"""Prediction of classes based on detectors created after training.
|
597
|
-
|
598
|
-
Parameters
|
599
|
-
----------
|
600
|
-
X : npt.NDArray
|
601
|
-
Array with input samples with [``N_samples`` (Lines)] and [``N_characteristics``(
|
602
|
-
Columns)]
|
603
|
-
|
604
|
-
Returns
|
605
|
-
-------
|
606
|
-
c : Optional[npt.NDArray]
|
607
|
-
an ndarray of the form ``C`` [``N samples``], containing the predicted classes for
|
608
|
-
``X``. Returns``None``: If there are no detectors for the prediction.
|
609
|
-
"""
|
610
|
-
# If there are no detectors, Returns None.
|
611
|
-
if self._detectors is None or self._detectors_stack is None:
|
612
|
-
return None
|
613
|
-
|
614
|
-
super()._check_and_raise_exceptions_predict(
|
615
|
-
X, len(self._detectors[self.classes[0]][0]), "BNSA"
|
616
|
-
)
|
617
|
-
|
618
|
-
# Converts the entire array X to boolean.
|
619
|
-
if X.dtype != bool:
|
620
|
-
X = X.astype(bool)
|
621
|
-
|
622
|
-
# Initializes an empty array that will store the predictions.
|
623
|
-
c = []
|
624
|
-
# For each sample row in X.
|
625
|
-
for line in X:
|
626
|
-
class_found: bool = True
|
627
|
-
# Class prediction based on detectors
|
628
|
-
class_index = bnsa_class_prediction(
|
629
|
-
line, self._detectors_stack, self.aff_thresh
|
630
|
-
)
|
631
|
-
# If belonging to one or more classes, adds the class with the greatest
|
632
|
-
# average distance
|
633
|
-
if class_index > -1:
|
634
|
-
c.append(self.classes[class_index])
|
635
|
-
class_found = True
|
636
|
-
else:
|
637
|
-
class_found = False
|
638
|
-
|
639
|
-
# If there is only one class and the sample is not classified, sets the
|
640
|
-
# output as non-self.
|
641
|
-
if not class_found and len(self.classes) == 1:
|
642
|
-
c.append("non-self")
|
643
|
-
# If the class cannot be identified by the detectors
|
644
|
-
elif not class_found:
|
645
|
-
self.__assign_class_to_non_self_sample(line, c)
|
646
|
-
|
647
|
-
return np.array(c)
|
648
|
-
|
649
|
-
def __assign_class_to_non_self_sample(self, line: npt.NDArray, c: list):
|
650
|
-
"""Determine the class of a sample when all detectors classify it as "non-self".
|
651
|
-
|
652
|
-
Classification is performed using the ``max_average_difference`` and
|
653
|
-
``max_nearest_difference`` methods.
|
654
|
-
|
655
|
-
Parameters
|
656
|
-
----------
|
657
|
-
line : npt.NDArray
|
658
|
-
Sample to be classified.
|
659
|
-
c : list
|
660
|
-
List of predictions to be updated with the new classification.
|
661
|
-
"""
|
662
|
-
if self._detectors is None:
|
663
|
-
raise ValueError("Detectors is not initialized.")
|
664
|
-
|
665
|
-
class_differences: dict = {}
|
666
|
-
for _class_ in self.classes:
|
667
|
-
distances = np.sum(line != self._detectors[_class_]) / self.N
|
668
|
-
# Assign the label to the class with the greatest distance from
|
669
|
-
# the nearest detector.
|
670
|
-
if self.no_label_sample_selection == "nearest_difference":
|
671
|
-
class_differences[_class_] = distances.min()
|
672
|
-
# Or based on the greatest distance from the average distances of the detectors.
|
673
|
-
else:
|
674
|
-
class_differences[_class_] = distances.sum() / self.N
|
675
|
-
|
676
|
-
c.append(max(class_differences, key=class_differences.get))
|
445
|
+
return True, new_detector_r
|
aisp/utils/distance.py
CHANGED
@@ -5,10 +5,10 @@ import numpy.typing as npt
|
|
5
5
|
from numba import njit, types
|
6
6
|
from numpy import float64
|
7
7
|
|
8
|
-
EUCLIDEAN = 0
|
9
|
-
MANHATTAN = 1
|
10
|
-
MINKOWSKI = 2
|
11
|
-
HAMMING = 3
|
8
|
+
EUCLIDEAN: int = 0
|
9
|
+
MANHATTAN: int = 1
|
10
|
+
MINKOWSKI: int = 2
|
11
|
+
HAMMING: int = 3
|
12
12
|
|
13
13
|
|
14
14
|
@njit([(types.boolean[:], types.boolean[:])], cache=True)
|
aisp/utils/validation.py
CHANGED
@@ -14,9 +14,9 @@ def detect_vector_data_type(
|
|
14
14
|
Detect the type of data in a vector.
|
15
15
|
|
16
16
|
The function detects if the vector contains data of type:
|
17
|
-
-
|
18
|
-
-
|
19
|
-
-
|
17
|
+
- Binary features: boolean values or integers restricted to 0 and 1.
|
18
|
+
- Continuous features: floating-point values in the normalized range [0.0, 1.0].
|
19
|
+
- Ranged features: floating-point values outside the normalized range.
|
20
20
|
|
21
21
|
Parameters
|
22
22
|
----------
|
@@ -25,8 +25,8 @@ def detect_vector_data_type(
|
|
25
25
|
|
26
26
|
Returns
|
27
27
|
-------
|
28
|
-
|
29
|
-
The
|
28
|
+
str
|
29
|
+
The data type of the vector: "binary-features", "continuous-features", or "ranged-features".
|
30
30
|
|
31
31
|
Raises
|
32
32
|
------
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: aisp
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.1
|
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>
|
@@ -84,10 +84,11 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
|
|
84
84
|
##### Algorithms implemented:
|
85
85
|
|
86
86
|
> - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/Negative%20Selection/)
|
87
|
-
> - [x] **Clonal Selection Algorithms.**
|
88
|
-
> * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/)
|
89
|
-
> - [ ] *
|
90
|
-
> - [
|
87
|
+
> - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/)
|
88
|
+
> * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/Clonal%20Selection%20Algorithms/airs/)
|
89
|
+
> - [ ] *Danger Theory.*
|
90
|
+
> - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/Immune%20Network%20Theory)
|
91
|
+
> - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/Immune%20Network%20Theory/ainet)
|
91
92
|
|
92
93
|
</section>
|
93
94
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
aisp/__init__.py,sha256=Lw5uajT371MeIBBN6FoMYQ5jmznkO0NiM-sZVWDXkCg,708
|
2
|
+
aisp/exceptions.py,sha256=I9JaQx6p8Jo7qjwwcrqnuewQgyBdUnOSSZofPoBeDNE,1954
|
3
|
+
aisp/base/__init__.py,sha256=cDDN6YcYqSU080AvEankhUtIALkSTVmm6XTa48htHjU,211
|
4
|
+
aisp/base/_base.py,sha256=uTVh__hQJGe8RCOzCet4ZV3vQbwgj5fXAt4Jdf0x1r0,1792
|
5
|
+
aisp/base/_classifier.py,sha256=kFP58oYJP-Spk-_qI61rg0eS1jPb3Af-xQVwI9TzSnw,3373
|
6
|
+
aisp/base/_clusterer.py,sha256=tcaZ9UOuQUHSN0qyj45aiZ1nU3QMVEeZous95JZkqtA,2466
|
7
|
+
aisp/base/mutation.py,sha256=A_AlGp8S4ooFEMW3Jgv0n0Y6tbhfusaMMWFsoH4HmD8,4762
|
8
|
+
aisp/csa/__init__.py,sha256=708jwpqia10bqmh-4-_srwwNuBh7jf2Zix-u8Hfbzmk,348
|
9
|
+
aisp/csa/_ai_recognition_sys.py,sha256=E6i1TsTjpUYnSZUCQbyGjeeaiX6K_ZPR-0HQdWKc0rQ,18871
|
10
|
+
aisp/csa/_base.py,sha256=pUObVlUB-ugz2plH29oJJBH_7Ny0611sCI0C5kv4ho4,3593
|
11
|
+
aisp/csa/_cell.py,sha256=GUxnzvPyIbBm1YYkMhSx0tcV_oyDhJ7wAo5gtr_1CoY,1845
|
12
|
+
aisp/ina/__init__.py,sha256=cOnxGcxrBdg6lLv2w2sdlToMahKMh_Gw57AfUUPQjMo,329
|
13
|
+
aisp/ina/_ai_network.py,sha256=-mp6ggEBbb4lHwD3EhHl1giAVbnfNQNARCtr7N_4Nkw,20984
|
14
|
+
aisp/ina/_base.py,sha256=x9eFUKiAcXSfwqVyBVmS54FDeIcApEtFPGruZvwQOwQ,4404
|
15
|
+
aisp/nsa/__init__.py,sha256=vL_HbASV6aGiiHAMx0UShqkL-ly-OYcqQLasKdu9p-M,425
|
16
|
+
aisp/nsa/_base.py,sha256=3YKlZzA3yhP2uQHfhyKswbHUutlxkOR4wn6N10nSO-w,4119
|
17
|
+
aisp/nsa/_binary_negative_selection.py,sha256=af5z7jjUhuAdVz4MpNOToaHmtrJQxDgiAciMUZ6dGV4,9750
|
18
|
+
aisp/nsa/_negative_selection.py,sha256=5IaXmwUolVR3LaDQuxoBY0D4IMuIx2YrUenPAR02H9s,18792
|
19
|
+
aisp/nsa/_ns_core.py,sha256=SXkZL-p2VQygU4Pf6J5AP_yPzU4cR6aU6wx-e_vlm-c,5021
|
20
|
+
aisp/utils/__init__.py,sha256=RzpKhkg8nCZi4G0C4il97f3ESYs7Bbxq6EjTeOQQUGk,195
|
21
|
+
aisp/utils/_multiclass.py,sha256=nWd58ayVfxgdopBQc9b_xywkolJ2fGW3AN-JoD2A9Fw,1134
|
22
|
+
aisp/utils/distance.py,sha256=pY23YGZpu6qVCCkZfhaEpRazUULfVUy2piyzYuAryN0,6576
|
23
|
+
aisp/utils/metrics.py,sha256=zDAScDbHRnfu24alRcZ6fEIUaWNoCD-QCtOCFBWPPo8,1277
|
24
|
+
aisp/utils/sanitizers.py,sha256=u1GizdJ-RKfPWJLnuFiM09lpItZMhDR_EvK8YdVHwDk,1858
|
25
|
+
aisp/utils/types.py,sha256=KELzr1kSBT7hHdsABoIS1xmEBGj6gRSH5A5YNG36I_c,1324
|
26
|
+
aisp/utils/validation.py,sha256=RqcS2VdFXkNcOH_7Y3yPi7FBoGWR_ReLBPDBx0UMCqI,1431
|
27
|
+
aisp-0.3.1.dist-info/licenses/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
|
28
|
+
aisp-0.3.1.dist-info/METADATA,sha256=c4c4Zh31XJxi6SO0OA-4YnmSOTn-MqvZNdyRU4TolKA,5173
|
29
|
+
aisp-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
30
|
+
aisp-0.3.1.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
|
31
|
+
aisp-0.3.1.dist-info/RECORD,,
|