aisp 0.3.0__tar.gz → 0.3.2__tar.gz
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-0.3.0 → aisp-0.3.2}/PKG-INFO +6 -6
- {aisp-0.3.0 → aisp-0.3.2}/README.md +5 -5
- aisp-0.3.2/aisp/__init__.py +26 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/base/__init__.py +1 -1
- {aisp-0.3.0 → aisp-0.3.2}/aisp/base/_classifier.py +3 -1
- {aisp-0.3.0 → aisp-0.3.2}/aisp/base/_clusterer.py +3 -1
- {aisp-0.3.0 → aisp-0.3.2}/aisp/csa/_ai_recognition_sys.py +14 -12
- {aisp-0.3.0 → aisp-0.3.2}/aisp/csa/_base.py +1 -2
- {aisp-0.3.0 → aisp-0.3.2}/aisp/ina/_ai_network.py +9 -9
- {aisp-0.3.0 → aisp-0.3.2}/aisp/ina/_base.py +1 -2
- {aisp-0.3.0 → aisp-0.3.2}/aisp/nsa/__init__.py +2 -1
- aisp-0.3.2/aisp/nsa/_binary_negative_selection.py +240 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/nsa/_negative_selection.py +7 -233
- {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/_multiclass.py +1 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/PKG-INFO +6 -6
- {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/SOURCES.txt +1 -0
- {aisp-0.3.0 → aisp-0.3.2}/pyproject.toml +1 -1
- aisp-0.3.0/aisp/__init__.py +0 -4
- {aisp-0.3.0 → aisp-0.3.2}/LICENSE +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/base/_base.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/base/mutation.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/csa/__init__.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/csa/_cell.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/exceptions.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/ina/__init__.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/nsa/_base.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/nsa/_ns_core.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/__init__.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/distance.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/metrics.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/sanitizers.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/types.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp/utils/validation.py +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/dependency_links.txt +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/requires.txt +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/aisp.egg-info/top_level.txt +0 -0
- {aisp-0.3.0 → aisp-0.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: aisp
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.2
|
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>
|
@@ -83,12 +83,12 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
|
|
83
83
|
|
84
84
|
##### Algorithms implemented:
|
85
85
|
|
86
|
-
> - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/
|
87
|
-
> - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/
|
88
|
-
> * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/
|
86
|
+
> - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
|
87
|
+
> - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
|
88
|
+
> * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
|
89
89
|
> - [ ] *Danger Theory.*
|
90
|
-
> - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/
|
91
|
-
> - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/
|
90
|
+
> - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
|
91
|
+
> - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
|
92
92
|
|
93
93
|
</section>
|
94
94
|
|
@@ -48,12 +48,12 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
|
|
48
48
|
|
49
49
|
##### Algorithms implemented:
|
50
50
|
|
51
|
-
> - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/
|
52
|
-
> - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/
|
53
|
-
> * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/
|
51
|
+
> - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
|
52
|
+
> - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
|
53
|
+
> * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
|
54
54
|
> - [ ] *Danger Theory.*
|
55
|
-
> - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/
|
56
|
-
> - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/
|
55
|
+
> - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
|
56
|
+
> - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
|
57
57
|
|
58
58
|
</section>
|
59
59
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"""AISP - Artificial Immune Systems Package.
|
2
|
+
|
3
|
+
AISP is a Python package of immunoinspired techniques that apply metaphors from the vertebrate
|
4
|
+
immune system to pattern recognition and optimization tasks.
|
5
|
+
|
6
|
+
The package is organized into specialized modules, each dedicated to a family of Artificial
|
7
|
+
Immune Systems algorithms:
|
8
|
+
- csa: Clonal Selection Algorithms
|
9
|
+
- nsa: Negative Selection Algorithms
|
10
|
+
- ina: Immune Network Algorithms
|
11
|
+
|
12
|
+
For detailed documentation and examples, visit:
|
13
|
+
https://ais-package.github.io/docs/intro
|
14
|
+
"""
|
15
|
+
|
16
|
+
from . import csa
|
17
|
+
from . import ina
|
18
|
+
from . import nsa
|
19
|
+
|
20
|
+
__author__ = "AISP Development Team"
|
21
|
+
__version__ = "0.3.2"
|
22
|
+
__all__ = [
|
23
|
+
'csa',
|
24
|
+
'nsa',
|
25
|
+
'ina'
|
26
|
+
]
|
@@ -1,5 +1,7 @@
|
|
1
1
|
"""Base class for classification algorithm."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
from abc import ABC, abstractmethod
|
4
6
|
from typing import Optional, Union
|
5
7
|
|
@@ -19,7 +21,7 @@ class BaseClassifier(ABC, Base):
|
|
19
21
|
classes: Union[npt.NDArray, list] = []
|
20
22
|
|
21
23
|
@abstractmethod
|
22
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) ->
|
24
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> BaseClassifier:
|
23
25
|
"""
|
24
26
|
Train the model using the input data X and corresponding labels y.
|
25
27
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
"""Base class for clustering algorithms."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
from abc import ABC, abstractmethod
|
4
6
|
from typing import Optional
|
5
7
|
|
@@ -17,7 +19,7 @@ class BaseClusterer(ABC, Base):
|
|
17
19
|
"""
|
18
20
|
|
19
21
|
@abstractmethod
|
20
|
-
def fit(self, X: npt.NDArray, verbose: bool = True) ->
|
22
|
+
def fit(self, X: npt.NDArray, verbose: bool = True) -> BaseClusterer:
|
21
23
|
"""
|
22
24
|
Train the model using the input data X.
|
23
25
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
"""Artificial Immune Recognition System (AIRS)."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
import random
|
4
6
|
from collections import Counter
|
5
7
|
from heapq import nlargest
|
@@ -11,13 +13,13 @@ import numpy.typing as npt
|
|
11
13
|
from scipy.spatial.distance import pdist
|
12
14
|
from tqdm import tqdm
|
13
15
|
|
14
|
-
from
|
16
|
+
from ._base import BaseAIRS
|
15
17
|
from ._cell import Cell
|
16
|
-
from ..
|
18
|
+
from ..base import set_seed_numba
|
17
19
|
from ..utils.distance import hamming, compute_metric_distance, get_metric_code
|
20
|
+
from ..utils.sanitizers import sanitize_param, sanitize_seed, sanitize_choice
|
18
21
|
from ..utils.types import FeatureType, MetricType
|
19
22
|
from ..utils.validation import detect_vector_data_type
|
20
|
-
from ._base import BaseAIRS
|
21
23
|
|
22
24
|
|
23
25
|
class _ARB(Cell):
|
@@ -189,6 +191,7 @@ class AIRS(BaseAIRS):
|
|
189
191
|
self.p: np.float64 = np.float64(kwargs.get("p", 2.0))
|
190
192
|
|
191
193
|
self._cells_memory = None
|
194
|
+
self._all_class_cell_vectors = None
|
192
195
|
self.affinity_threshold = 0.0
|
193
196
|
self.classes = []
|
194
197
|
self._bounds: Optional[npt.NDArray[np.float64]] = None
|
@@ -198,7 +201,7 @@ class AIRS(BaseAIRS):
|
|
198
201
|
"""Returns the trained cells memory, organized by class."""
|
199
202
|
return self._cells_memory
|
200
203
|
|
201
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) ->
|
204
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> AIRS:
|
202
205
|
"""
|
203
206
|
Fit the model to the training data using the AIRS.
|
204
207
|
|
@@ -301,6 +304,11 @@ class AIRS(BaseAIRS):
|
|
301
304
|
)
|
302
305
|
progress.close()
|
303
306
|
self._cells_memory = pool_cells_classes
|
307
|
+
self._all_class_cell_vectors = [
|
308
|
+
(class_name, cell.vector)
|
309
|
+
for class_name in self.classes
|
310
|
+
for cell in self._cells_memory[class_name]
|
311
|
+
]
|
304
312
|
return self
|
305
313
|
|
306
314
|
def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
|
@@ -322,7 +330,7 @@ class AIRS(BaseAIRS):
|
|
322
330
|
An ndarray of the form ``C`` [``N samples``], containing the predicted classes for
|
323
331
|
``X``. or ``None``: If there are no detectors for the prediction.
|
324
332
|
"""
|
325
|
-
if self.
|
333
|
+
if self._all_class_cell_vectors is None:
|
326
334
|
return None
|
327
335
|
|
328
336
|
super()._check_and_raise_exceptions_predict(
|
@@ -331,16 +339,10 @@ class AIRS(BaseAIRS):
|
|
331
339
|
|
332
340
|
c: list = []
|
333
341
|
|
334
|
-
all_cells_memory = [
|
335
|
-
(class_name, cell.vector)
|
336
|
-
for class_name in self.classes
|
337
|
-
for cell in self._cells_memory[class_name]
|
338
|
-
]
|
339
|
-
|
340
342
|
for line in X:
|
341
343
|
label_stim_list = [
|
342
344
|
(class_name, self._affinity(memory, line))
|
343
|
-
for class_name, memory in
|
345
|
+
for class_name, memory in self._all_class_cell_vectors
|
344
346
|
]
|
345
347
|
# Create the list with the k nearest neighbors and select the class with the most votes
|
346
348
|
k_nearest = nlargest(self.k, label_stim_list, key=lambda x: x[1])
|
@@ -5,9 +5,9 @@ from abc import ABC
|
|
5
5
|
import numpy as np
|
6
6
|
import numpy.typing as npt
|
7
7
|
|
8
|
+
from ..base import BaseClassifier
|
8
9
|
from ..exceptions import FeatureDimensionMismatch
|
9
10
|
from ..utils.types import FeatureType
|
10
|
-
from ..base import BaseClassifier
|
11
11
|
|
12
12
|
|
13
13
|
class BaseAIRS(BaseClassifier, ABC):
|
@@ -54,7 +54,6 @@ class BaseAIRS(BaseClassifier, ABC):
|
|
54
54
|
"X does not have the same amount of sample for the output classes in y."
|
55
55
|
)
|
56
56
|
|
57
|
-
|
58
57
|
@staticmethod
|
59
58
|
def _check_and_raise_exceptions_predict(
|
60
59
|
X: npt.NDArray,
|
@@ -1,5 +1,7 @@
|
|
1
1
|
"""Artificial Immune Network (AiNet)."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
from collections import Counter
|
4
6
|
from heapq import nlargest
|
5
7
|
from typing import Optional
|
@@ -14,14 +16,14 @@ from ._base import BaseAiNet
|
|
14
16
|
from ..base import set_seed_numba
|
15
17
|
from ..base.mutation import clone_and_mutate_binary, clone_and_mutate_continuous, \
|
16
18
|
clone_and_mutate_ranged
|
17
|
-
from ..utils.sanitizers import sanitize_choice, sanitize_param, sanitize_seed
|
18
19
|
from ..utils.distance import hamming, compute_metric_distance, get_metric_code
|
20
|
+
from ..utils.sanitizers import sanitize_choice, sanitize_param, sanitize_seed
|
19
21
|
from ..utils.types import FeatureType, MetricType
|
20
22
|
from ..utils.validation import detect_vector_data_type
|
21
23
|
|
22
24
|
|
23
25
|
class AiNet(BaseAiNet):
|
24
|
-
"""Artificial Immune Network for Compression and Clustering
|
26
|
+
"""Artificial Immune Network for Compression and Clustering.
|
25
27
|
|
26
28
|
This class implements the aiNet algorithm, an artificial immune network model designed for
|
27
29
|
clustering and data compression tasks. The aiNet algorithm uses principles from immune
|
@@ -77,13 +79,11 @@ class AiNet(BaseAiNet):
|
|
77
79
|
|
78
80
|
References
|
79
81
|
----------
|
80
|
-
.. [1]
|
81
|
-
|
82
|
-
Draft Chapter XII of the book *Data Mining: A Heuristic Approach*.
|
83
|
-
Department of Computer and Automation Engineering, University of Campinas.
|
82
|
+
.. [1] De Castro, Leandro & José, Fernando & von Zuben, Antonio Augusto. (2001). aiNet: An
|
83
|
+
Artificial Immune Network for Data Analysis.
|
84
84
|
Available at:
|
85
|
-
https://www.
|
86
|
-
|
85
|
+
https://www.researchgate.net/publication/
|
86
|
+
228378350_aiNet_An_Artificial_Immune_Network_for_Data_Analysis
|
87
87
|
.. [2] SciPy Documentation. *Minimum Spanning Tree*.
|
88
88
|
https://docs.scipy.org/doc/scipy/reference/generated/
|
89
89
|
scipy.sparse.csgraph.minimum_spanning_tree
|
@@ -175,7 +175,7 @@ class AiNet(BaseAiNet):
|
|
175
175
|
'std_distance': self._mst_std_distance
|
176
176
|
}
|
177
177
|
|
178
|
-
def fit(self, X: npt.NDArray, verbose: bool = True):
|
178
|
+
def fit(self, X: npt.NDArray, verbose: bool = True) -> AiNet:
|
179
179
|
"""
|
180
180
|
Train the AiNet model on input data.
|
181
181
|
|
@@ -5,7 +5,8 @@ distinguish between self and non-self. Only T-cells capable of recognizing non-s
|
|
5
5
|
preserved.
|
6
6
|
"""
|
7
7
|
|
8
|
-
from .
|
8
|
+
from ._binary_negative_selection import BNSA
|
9
|
+
from ._negative_selection import RNSA
|
9
10
|
|
10
11
|
__author__ = "João Paulo da Silva Barros"
|
11
12
|
__all__ = ["RNSA", "BNSA"]
|
@@ -0,0 +1,240 @@
|
|
1
|
+
"""Negative Selection Algorithm."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import Dict, Literal, Optional, Union
|
6
|
+
|
7
|
+
import numpy as np
|
8
|
+
import numpy.typing as npt
|
9
|
+
from tqdm import tqdm
|
10
|
+
|
11
|
+
from ._base import BaseNSA
|
12
|
+
from ._ns_core import (
|
13
|
+
check_detector_bnsa_validity,
|
14
|
+
bnsa_class_prediction
|
15
|
+
)
|
16
|
+
from ..exceptions import MaxDiscardsReachedError
|
17
|
+
from ..utils.sanitizers import sanitize_seed, sanitize_param
|
18
|
+
|
19
|
+
|
20
|
+
class BNSA(BaseNSA):
|
21
|
+
"""BNSA (Binary Negative Selection Algorithm).
|
22
|
+
|
23
|
+
Class is for classification and identification purposes of anomalies through the self and not
|
24
|
+
self method.
|
25
|
+
|
26
|
+
Parameters
|
27
|
+
----------
|
28
|
+
N : int, default=100
|
29
|
+
Number of detectors.
|
30
|
+
aff_thresh : float, default=0.1
|
31
|
+
The variable represents the percentage of similarity between the T cell and the own
|
32
|
+
samples. The default value is 10% (0.1), while a value of 1.0 represents 100% similarity.
|
33
|
+
max_discards : int, default=1000
|
34
|
+
This parameter indicates the maximum number of detector discards in sequence, which aims
|
35
|
+
to avoid a possible infinite loop if a radius is defined that it is not possible to
|
36
|
+
generate non-self detectors.
|
37
|
+
seed : Optional[int], default=None
|
38
|
+
Seed for the random generation of values in the detectors.
|
39
|
+
no_label_sample_selection : str, default="max_average_difference"
|
40
|
+
Method for selecting labels for samples designated as non-self by all detectors.
|
41
|
+
Available method types:
|
42
|
+
|
43
|
+
- max_average_difference - Selects the class with the highest average difference among the
|
44
|
+
detectors.
|
45
|
+
|
46
|
+
- max_nearest_difference - Selects the class with the highest difference between the
|
47
|
+
nearest and farthest detector from the sample.
|
48
|
+
"""
|
49
|
+
|
50
|
+
def __init__(
|
51
|
+
self,
|
52
|
+
N: int = 100,
|
53
|
+
aff_thresh: float = 0.1,
|
54
|
+
max_discards: int = 1000,
|
55
|
+
seed: Optional[int] = None,
|
56
|
+
no_label_sample_selection: Literal[
|
57
|
+
"max_average_difference", "max_nearest_difference"
|
58
|
+
] = "max_average_difference",
|
59
|
+
):
|
60
|
+
self.N: int = sanitize_param(N, 100, lambda x: x > 0)
|
61
|
+
self.aff_thresh: float = sanitize_param(aff_thresh, 0.1, lambda x: 0 < x < 1)
|
62
|
+
self.max_discards: float = sanitize_param(max_discards, 1000, lambda x: x > 0)
|
63
|
+
|
64
|
+
self.seed: Optional[int] = sanitize_seed(seed)
|
65
|
+
|
66
|
+
if self.seed is not None:
|
67
|
+
np.random.seed(seed)
|
68
|
+
|
69
|
+
self.no_label_sample_selection: str = sanitize_param(
|
70
|
+
no_label_sample_selection,
|
71
|
+
"max_average_difference",
|
72
|
+
lambda x: x == "nearest_difference",
|
73
|
+
)
|
74
|
+
|
75
|
+
self.classes: Union[npt.NDArray, list] = []
|
76
|
+
self._detectors: Optional[dict] = None
|
77
|
+
self._detectors_stack: Optional[npt.NDArray] = None
|
78
|
+
|
79
|
+
@property
|
80
|
+
def detectors(self) -> Optional[Dict[str, npt.NDArray[np.bool_]]]:
|
81
|
+
"""Returns the trained detectors, organized by class."""
|
82
|
+
return self._detectors
|
83
|
+
|
84
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> BNSA:
|
85
|
+
"""Training according to X and y, using the method negative selection method.
|
86
|
+
|
87
|
+
Parameters
|
88
|
+
----------
|
89
|
+
X : npt.NDArray
|
90
|
+
Training array, containing the samples and their characteristics, [``N samples`` (
|
91
|
+
rows)][``N features`` (columns)].
|
92
|
+
y : npt.NDArray
|
93
|
+
Array of target classes of ``X`` with [``N samples`` (lines)].
|
94
|
+
verbose : bool, default=True
|
95
|
+
Feedback from detector generation to the user.
|
96
|
+
|
97
|
+
Returns
|
98
|
+
-------
|
99
|
+
self : BNSA
|
100
|
+
Returns the instance it self.
|
101
|
+
"""
|
102
|
+
super()._check_and_raise_exceptions_fit(X, y, "BNSA")
|
103
|
+
# Converts the entire array X to boolean
|
104
|
+
X = X.astype(np.bool_)
|
105
|
+
|
106
|
+
# Identifying the possible classes within the output array `y`.
|
107
|
+
self.classes = np.unique(y)
|
108
|
+
# Dictionary that will store detectors with classes as keys.
|
109
|
+
list_detectors_by_class = {}
|
110
|
+
# Separates the classes for training.
|
111
|
+
sample_index: dict = self._slice_index_list_by_class(y)
|
112
|
+
# Progress bar for generating all detectors.
|
113
|
+
|
114
|
+
progress = tqdm(
|
115
|
+
total=int(self.N * (len(self.classes))),
|
116
|
+
bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
|
117
|
+
postfix="\n",
|
118
|
+
disable=not verbose
|
119
|
+
)
|
120
|
+
|
121
|
+
for _class_ in self.classes:
|
122
|
+
# Initializes the empty set that will contain the valid detectors.
|
123
|
+
valid_detectors_set: list = []
|
124
|
+
discard_count: int = 0
|
125
|
+
# Updating the progress bar with the current class the algorithm is processing.
|
126
|
+
progress.set_description_str(
|
127
|
+
f"Generating the detectors for the {_class_} class:"
|
128
|
+
)
|
129
|
+
x_class = X[sample_index[_class_]]
|
130
|
+
while len(valid_detectors_set) < self.N:
|
131
|
+
# Generates a candidate detector vector randomly with values 0 and 1.
|
132
|
+
vector_x = np.random.randint(0, 2, size=X.shape[1]).astype(np.bool_)
|
133
|
+
# If the detector is valid, add it to the list of valid detectors.
|
134
|
+
if check_detector_bnsa_validity(x_class, vector_x, self.aff_thresh):
|
135
|
+
discard_count = 0
|
136
|
+
valid_detectors_set.append(vector_x)
|
137
|
+
progress.update(1)
|
138
|
+
else:
|
139
|
+
discard_count += 1
|
140
|
+
if discard_count == self.max_discards:
|
141
|
+
raise MaxDiscardsReachedError(_class_)
|
142
|
+
|
143
|
+
# Add detectors to the dictionary with classes as keys.
|
144
|
+
list_detectors_by_class[_class_] = np.array(valid_detectors_set)
|
145
|
+
|
146
|
+
# Notify the completion of detector generation for the classes.
|
147
|
+
progress.set_description(
|
148
|
+
f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
|
149
|
+
f"successfully generated\033[0m"
|
150
|
+
)
|
151
|
+
progress.close()
|
152
|
+
# Saves the found detectors in the attribute for the class detectors.
|
153
|
+
self._detectors = list_detectors_by_class
|
154
|
+
self._detectors_stack = np.array(
|
155
|
+
[np.stack(self._detectors[class_name]) for class_name in self.classes]
|
156
|
+
)
|
157
|
+
return self
|
158
|
+
|
159
|
+
def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
|
160
|
+
"""Prediction of classes based on detectors created after training.
|
161
|
+
|
162
|
+
Parameters
|
163
|
+
----------
|
164
|
+
X : npt.NDArray
|
165
|
+
Array with input samples with [``N_samples`` (Lines)] and [``N_characteristics``(
|
166
|
+
Columns)]
|
167
|
+
|
168
|
+
Returns
|
169
|
+
-------
|
170
|
+
c : Optional[npt.NDArray]
|
171
|
+
an ndarray of the form ``C`` [``N samples``], containing the predicted classes for
|
172
|
+
``X``. Returns``None``: If there are no detectors for the prediction.
|
173
|
+
"""
|
174
|
+
# If there are no detectors, Returns None.
|
175
|
+
if self._detectors is None or self._detectors_stack is None:
|
176
|
+
return None
|
177
|
+
|
178
|
+
super()._check_and_raise_exceptions_predict(
|
179
|
+
X, len(self._detectors[self.classes[0]][0]), "BNSA"
|
180
|
+
)
|
181
|
+
|
182
|
+
# Converts the entire array X to boolean.
|
183
|
+
if X.dtype != bool:
|
184
|
+
X = X.astype(bool)
|
185
|
+
|
186
|
+
# Initializes an empty array that will store the predictions.
|
187
|
+
c = []
|
188
|
+
# For each sample row in X.
|
189
|
+
for line in X:
|
190
|
+
class_found: bool = True
|
191
|
+
# Class prediction based on detectors
|
192
|
+
class_index = bnsa_class_prediction(
|
193
|
+
line, self._detectors_stack, self.aff_thresh
|
194
|
+
)
|
195
|
+
# If belonging to one or more classes, adds the class with the greatest
|
196
|
+
# average distance
|
197
|
+
if class_index > -1:
|
198
|
+
c.append(self.classes[class_index])
|
199
|
+
class_found = True
|
200
|
+
else:
|
201
|
+
class_found = False
|
202
|
+
|
203
|
+
# If there is only one class and the sample is not classified, sets the
|
204
|
+
# output as non-self.
|
205
|
+
if not class_found and len(self.classes) == 1:
|
206
|
+
c.append("non-self")
|
207
|
+
# If the class cannot be identified by the detectors
|
208
|
+
elif not class_found:
|
209
|
+
self.__assign_class_to_non_self_sample(line, c)
|
210
|
+
|
211
|
+
return np.array(c)
|
212
|
+
|
213
|
+
def __assign_class_to_non_self_sample(self, line: npt.NDArray, c: list):
|
214
|
+
"""Determine the class of a sample when all detectors classify it as "non-self".
|
215
|
+
|
216
|
+
Classification is performed using the ``max_average_difference`` and
|
217
|
+
``max_nearest_difference`` methods.
|
218
|
+
|
219
|
+
Parameters
|
220
|
+
----------
|
221
|
+
line : npt.NDArray
|
222
|
+
Sample to be classified.
|
223
|
+
c : list
|
224
|
+
List of predictions to be updated with the new classification.
|
225
|
+
"""
|
226
|
+
if self._detectors is None:
|
227
|
+
raise ValueError("Detectors is not initialized.")
|
228
|
+
|
229
|
+
class_differences: dict = {}
|
230
|
+
for _class_ in self.classes:
|
231
|
+
distances = np.sum(line != self._detectors[_class_]) / self.N
|
232
|
+
# Assign the label to the class with the greatest distance from
|
233
|
+
# the nearest detector.
|
234
|
+
if self.no_label_sample_selection == "nearest_difference":
|
235
|
+
class_differences[_class_] = distances.min()
|
236
|
+
# Or based on the greatest distance from the average distances of the detectors.
|
237
|
+
else:
|
238
|
+
class_differences[_class_] = distances.sum() / self.N
|
239
|
+
|
240
|
+
c.append(max(class_differences, key=class_differences.get))
|
@@ -1,17 +1,16 @@
|
|
1
1
|
"""Negative Selection Algorithm."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
from typing import Any, Dict, Literal, Optional, Union
|
4
|
-
from tqdm import tqdm
|
5
6
|
|
6
7
|
import numpy as np
|
7
8
|
import numpy.typing as npt
|
9
|
+
from tqdm import tqdm
|
8
10
|
|
11
|
+
from ._base import BaseNSA, Detector
|
12
|
+
from ._ns_core import check_detector_rnsa_validity
|
9
13
|
from ..base import set_seed_numba
|
10
|
-
from ._ns_core import (
|
11
|
-
check_detector_bnsa_validity,
|
12
|
-
bnsa_class_prediction,
|
13
|
-
check_detector_rnsa_validity,
|
14
|
-
)
|
15
14
|
from ..exceptions import MaxDiscardsReachedError
|
16
15
|
from ..utils.distance import (
|
17
16
|
min_distance_to_class_vectors,
|
@@ -19,7 +18,6 @@ from ..utils.distance import (
|
|
19
18
|
compute_metric_distance,
|
20
19
|
)
|
21
20
|
from ..utils.sanitizers import sanitize_seed, sanitize_choice, sanitize_param
|
22
|
-
from ._base import BaseNSA, Detector
|
23
21
|
|
24
22
|
|
25
23
|
class RNSA(BaseNSA):
|
@@ -123,7 +121,7 @@ class RNSA(BaseNSA):
|
|
123
121
|
"""Returns the trained detectors, organized by class."""
|
124
122
|
return self._detectors
|
125
123
|
|
126
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) ->
|
124
|
+
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> RNSA:
|
127
125
|
"""
|
128
126
|
Perform training according to X and y, using the negative selection method (NegativeSelect).
|
129
127
|
|
@@ -339,10 +337,9 @@ class RNSA(BaseNSA):
|
|
339
337
|
if len(knn) < self.k:
|
340
338
|
knn.append(distance)
|
341
339
|
knn.sort()
|
342
|
-
|
343
340
|
# Otherwise, add the distance if the new distance is smaller than the largest
|
344
341
|
# distance in the list.
|
345
|
-
|
342
|
+
elif knn[self.k - 1] > distance:
|
346
343
|
knn[self.k - 1] = distance
|
347
344
|
knn.sort()
|
348
345
|
|
@@ -447,226 +444,3 @@ class RNSA(BaseNSA):
|
|
447
444
|
return False
|
448
445
|
|
449
446
|
return True, new_detector_r
|
450
|
-
|
451
|
-
|
452
|
-
class BNSA(BaseNSA):
|
453
|
-
"""BNSA (Binary Negative Selection Algorithm).
|
454
|
-
|
455
|
-
Class is for classification and identification purposes of anomalies through the self and not
|
456
|
-
self method.
|
457
|
-
|
458
|
-
Parameters
|
459
|
-
----------
|
460
|
-
N : int, default=100
|
461
|
-
Number of detectors.
|
462
|
-
aff_thresh : float, default=0.1
|
463
|
-
The variable represents the percentage of similarity between the T cell and the own
|
464
|
-
samples. The default value is 10% (0.1), while a value of 1.0 represents 100% similarity.
|
465
|
-
max_discards : int, default=1000
|
466
|
-
This parameter indicates the maximum number of detector discards in sequence, which aims
|
467
|
-
to avoid a possible infinite loop if a radius is defined that it is not possible to
|
468
|
-
generate non-self detectors.
|
469
|
-
seed : Optional[int], default=None
|
470
|
-
Seed for the random generation of values in the detectors.
|
471
|
-
no_label_sample_selection : str, default="max_average_difference"
|
472
|
-
Method for selecting labels for samples designated as non-self by all detectors.
|
473
|
-
Available method types:
|
474
|
-
|
475
|
-
- max_average_difference - Selects the class with the highest average difference among the
|
476
|
-
detectors.
|
477
|
-
|
478
|
-
- max_nearest_difference - Selects the class with the highest difference between the
|
479
|
-
nearest and farthest detector from the sample.
|
480
|
-
"""
|
481
|
-
|
482
|
-
def __init__(
|
483
|
-
self,
|
484
|
-
N: int = 100,
|
485
|
-
aff_thresh: float = 0.1,
|
486
|
-
max_discards: int = 1000,
|
487
|
-
seed: Optional[int] = None,
|
488
|
-
no_label_sample_selection: Literal[
|
489
|
-
"max_average_difference", "max_nearest_difference"
|
490
|
-
] = "max_average_difference",
|
491
|
-
):
|
492
|
-
self.N: int = sanitize_param(N, 100, lambda x: x > 0)
|
493
|
-
self.aff_thresh: float = sanitize_param(aff_thresh, 0.1, lambda x: 0 < x < 1)
|
494
|
-
self.max_discards: float = sanitize_param(max_discards, 1000, lambda x: x > 0)
|
495
|
-
|
496
|
-
self.seed: Optional[int] = sanitize_seed(seed)
|
497
|
-
|
498
|
-
if self.seed is not None:
|
499
|
-
np.random.seed(seed)
|
500
|
-
|
501
|
-
self.no_label_sample_selection: str = sanitize_param(
|
502
|
-
no_label_sample_selection,
|
503
|
-
"max_average_difference",
|
504
|
-
lambda x: x == "nearest_difference",
|
505
|
-
)
|
506
|
-
|
507
|
-
self.classes: Union[npt.NDArray, list] = []
|
508
|
-
self._detectors: Optional[dict] = None
|
509
|
-
self._detectors_stack: Optional[npt.NDArray] = None
|
510
|
-
|
511
|
-
@property
|
512
|
-
def detectors(self) -> Optional[Dict[str, npt.NDArray[np.bool_]]]:
|
513
|
-
"""Returns the trained detectors, organized by class."""
|
514
|
-
return self._detectors
|
515
|
-
|
516
|
-
def fit(self, X: npt.NDArray, y: npt.NDArray, verbose: bool = True) -> "BNSA":
|
517
|
-
"""Training according to X and y, using the method negative selection method.
|
518
|
-
|
519
|
-
Parameters
|
520
|
-
----------
|
521
|
-
X : npt.NDArray
|
522
|
-
Training array, containing the samples and their characteristics, [``N samples`` (
|
523
|
-
rows)][``N features`` (columns)].
|
524
|
-
y : npt.NDArray
|
525
|
-
Array of target classes of ``X`` with [``N samples`` (lines)].
|
526
|
-
verbose : bool, default=True
|
527
|
-
Feedback from detector generation to the user.
|
528
|
-
|
529
|
-
Returns
|
530
|
-
-------
|
531
|
-
self : BNSA
|
532
|
-
Returns the instance it self.
|
533
|
-
"""
|
534
|
-
super()._check_and_raise_exceptions_fit(X, y, "BNSA")
|
535
|
-
# Converts the entire array X to boolean
|
536
|
-
X = X.astype(np.bool_)
|
537
|
-
|
538
|
-
# Identifying the possible classes within the output array `y`.
|
539
|
-
self.classes = np.unique(y)
|
540
|
-
# Dictionary that will store detectors with classes as keys.
|
541
|
-
list_detectors_by_class = {}
|
542
|
-
# Separates the classes for training.
|
543
|
-
sample_index: dict = self._slice_index_list_by_class(y)
|
544
|
-
# Progress bar for generating all detectors.
|
545
|
-
|
546
|
-
progress = tqdm(
|
547
|
-
total=int(self.N * (len(self.classes))),
|
548
|
-
bar_format="{desc} ┇{bar}┇ {n}/{total} detectors",
|
549
|
-
postfix="\n",
|
550
|
-
disable=not verbose
|
551
|
-
)
|
552
|
-
|
553
|
-
for _class_ in self.classes:
|
554
|
-
# Initializes the empty set that will contain the valid detectors.
|
555
|
-
valid_detectors_set: list = []
|
556
|
-
discard_count: int = 0
|
557
|
-
# Updating the progress bar with the current class the algorithm is processing.
|
558
|
-
progress.set_description_str(
|
559
|
-
f"Generating the detectors for the {_class_} class:"
|
560
|
-
)
|
561
|
-
x_class = X[sample_index[_class_]]
|
562
|
-
while len(valid_detectors_set) < self.N:
|
563
|
-
# Generates a candidate detector vector randomly with values 0 and 1.
|
564
|
-
vector_x = np.random.randint(0, 2, size=X.shape[1]).astype(np.bool_)
|
565
|
-
# If the detector is valid, add it to the list of valid detectors.
|
566
|
-
if check_detector_bnsa_validity(x_class, vector_x, self.aff_thresh):
|
567
|
-
discard_count = 0
|
568
|
-
valid_detectors_set.append(vector_x)
|
569
|
-
progress.update(1)
|
570
|
-
else:
|
571
|
-
discard_count += 1
|
572
|
-
if discard_count == self.max_discards:
|
573
|
-
raise MaxDiscardsReachedError(_class_)
|
574
|
-
|
575
|
-
# Add detectors to the dictionary with classes as keys.
|
576
|
-
list_detectors_by_class[_class_] = np.array(valid_detectors_set)
|
577
|
-
|
578
|
-
# Notify the completion of detector generation for the classes.
|
579
|
-
progress.set_description(
|
580
|
-
f"\033[92m✔ Non-self detectors for classes ({', '.join(map(str, self.classes))}) "
|
581
|
-
f"successfully generated\033[0m"
|
582
|
-
)
|
583
|
-
progress.close()
|
584
|
-
# Saves the found detectors in the attribute for the class detectors.
|
585
|
-
self._detectors = list_detectors_by_class
|
586
|
-
self._detectors_stack = np.array(
|
587
|
-
[np.stack(self._detectors[class_name]) for class_name in self.classes]
|
588
|
-
)
|
589
|
-
return self
|
590
|
-
|
591
|
-
def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
|
592
|
-
"""Prediction of classes based on detectors created after training.
|
593
|
-
|
594
|
-
Parameters
|
595
|
-
----------
|
596
|
-
X : npt.NDArray
|
597
|
-
Array with input samples with [``N_samples`` (Lines)] and [``N_characteristics``(
|
598
|
-
Columns)]
|
599
|
-
|
600
|
-
Returns
|
601
|
-
-------
|
602
|
-
c : Optional[npt.NDArray]
|
603
|
-
an ndarray of the form ``C`` [``N samples``], containing the predicted classes for
|
604
|
-
``X``. Returns``None``: If there are no detectors for the prediction.
|
605
|
-
"""
|
606
|
-
# If there are no detectors, Returns None.
|
607
|
-
if self._detectors is None or self._detectors_stack is None:
|
608
|
-
return None
|
609
|
-
|
610
|
-
super()._check_and_raise_exceptions_predict(
|
611
|
-
X, len(self._detectors[self.classes[0]][0]), "BNSA"
|
612
|
-
)
|
613
|
-
|
614
|
-
# Converts the entire array X to boolean.
|
615
|
-
if X.dtype != bool:
|
616
|
-
X = X.astype(bool)
|
617
|
-
|
618
|
-
# Initializes an empty array that will store the predictions.
|
619
|
-
c = []
|
620
|
-
# For each sample row in X.
|
621
|
-
for line in X:
|
622
|
-
class_found: bool = True
|
623
|
-
# Class prediction based on detectors
|
624
|
-
class_index = bnsa_class_prediction(
|
625
|
-
line, self._detectors_stack, self.aff_thresh
|
626
|
-
)
|
627
|
-
# If belonging to one or more classes, adds the class with the greatest
|
628
|
-
# average distance
|
629
|
-
if class_index > -1:
|
630
|
-
c.append(self.classes[class_index])
|
631
|
-
class_found = True
|
632
|
-
else:
|
633
|
-
class_found = False
|
634
|
-
|
635
|
-
# If there is only one class and the sample is not classified, sets the
|
636
|
-
# output as non-self.
|
637
|
-
if not class_found and len(self.classes) == 1:
|
638
|
-
c.append("non-self")
|
639
|
-
# If the class cannot be identified by the detectors
|
640
|
-
elif not class_found:
|
641
|
-
self.__assign_class_to_non_self_sample(line, c)
|
642
|
-
|
643
|
-
return np.array(c)
|
644
|
-
|
645
|
-
def __assign_class_to_non_self_sample(self, line: npt.NDArray, c: list):
|
646
|
-
"""Determine the class of a sample when all detectors classify it as "non-self".
|
647
|
-
|
648
|
-
Classification is performed using the ``max_average_difference`` and
|
649
|
-
``max_nearest_difference`` methods.
|
650
|
-
|
651
|
-
Parameters
|
652
|
-
----------
|
653
|
-
line : npt.NDArray
|
654
|
-
Sample to be classified.
|
655
|
-
c : list
|
656
|
-
List of predictions to be updated with the new classification.
|
657
|
-
"""
|
658
|
-
if self._detectors is None:
|
659
|
-
raise ValueError("Detectors is not initialized.")
|
660
|
-
|
661
|
-
class_differences: dict = {}
|
662
|
-
for _class_ in self.classes:
|
663
|
-
distances = np.sum(line != self._detectors[_class_]) / self.N
|
664
|
-
# Assign the label to the class with the greatest distance from
|
665
|
-
# the nearest detector.
|
666
|
-
if self.no_label_sample_selection == "nearest_difference":
|
667
|
-
class_differences[_class_] = distances.min()
|
668
|
-
# Or based on the greatest distance from the average distances of the detectors.
|
669
|
-
else:
|
670
|
-
class_differences[_class_] = distances.sum() / self.N
|
671
|
-
|
672
|
-
c.append(max(class_differences, key=class_differences.get))
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: aisp
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.2
|
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>
|
@@ -83,12 +83,12 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
|
|
83
83
|
|
84
84
|
##### Algorithms implemented:
|
85
85
|
|
86
|
-
> - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/
|
87
|
-
> - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/
|
88
|
-
> * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/
|
86
|
+
> - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
|
87
|
+
> - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
|
88
|
+
> * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
|
89
89
|
> - [ ] *Danger Theory.*
|
90
|
-
> - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/
|
91
|
-
> - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/
|
90
|
+
> - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
|
91
|
+
> - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
|
92
92
|
|
93
93
|
</section>
|
94
94
|
|
aisp-0.3.0/aisp/__init__.py
DELETED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|