aisp 0.1.30__py3-none-any.whl → 0.1.32__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 +4 -4
- aisp/{_base.py → NSA/_base.py} +93 -61
- aisp/NSA/{_negativeSelection.py → _negative_selection.py} +407 -292
- {aisp-0.1.30.dist-info → aisp-0.1.32.dist-info}/METADATA +4 -4
- aisp-0.1.32.dist-info/RECORD +8 -0
- {aisp-0.1.30.dist-info → aisp-0.1.32.dist-info}/WHEEL +1 -1
- aisp-0.1.30.dist-info/RECORD +0 -8
- {aisp-0.1.30.dist-info → aisp-0.1.32.dist-info}/LICENSE +0 -0
- {aisp-0.1.30.dist-info → aisp-0.1.32.dist-info}/top_level.txt +0 -0
@@ -1,52 +1,57 @@
|
|
1
1
|
import numpy as np
|
2
2
|
import numpy.typing as npt
|
3
3
|
from tqdm import tqdm
|
4
|
-
from typing import Dict, Literal, Union
|
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
|
-
from
|
8
|
+
from ._base import Base
|
9
9
|
|
10
10
|
|
11
11
|
class RNSA(Base):
|
12
12
|
"""
|
13
|
-
The ``RNSA`` (Real-Valued Negative Selection Algorithm) class is for classification and
|
14
|
-
of anomalies through the self and not self method.
|
13
|
+
The ``RNSA`` (Real-Valued Negative Selection Algorithm) class is for classification and \
|
14
|
+
identification purposes. of anomalies through the self and not self method.
|
15
15
|
|
16
16
|
Attributes:
|
17
17
|
---
|
18
18
|
* N (``int``): Number of detectors.
|
19
19
|
* r (``float``): Radius of the detector.
|
20
20
|
* r_s (``float``): rₛ Radius of the ``X`` own samples.
|
21
|
-
* k (``int``): K number of near neighbors to calculate the average
|
21
|
+
* k (``int``): K number of near neighbors to calculate the average
|
22
|
+
distance of the detectors.
|
22
23
|
* metric (``str``): Way to calculate the distance: ``'euclidean', 'minkowski', or 'manhattan'``.
|
23
|
-
* max_discards (``int``): This parameter indicates the maximum number of consecutive
|
24
|
-
aimed at preventing a possible infinite loop in case a radius is
|
25
|
-
detectors.
|
24
|
+
* max_discards (``int``): This parameter indicates the maximum number of consecutive \
|
25
|
+
detector discards, aimed at preventing a possible infinite loop in case a radius is \
|
26
|
+
defined that cannot generate non-self detectors.
|
26
27
|
* seed (``int``): Seed for the random generation of detector values.
|
27
28
|
* algorithm(``str``), Set the algorithm version:
|
28
29
|
|
29
30
|
* ``'default-NSA'``: Default algorithm with fixed radius.
|
30
|
-
* ``'V-detector'``: This algorithm uses a variable radius for anomaly detection
|
31
|
+
* ``'V-detector'``: This algorithm uses a variable radius for anomaly detection \
|
32
|
+
in feature spaces.
|
31
33
|
|
32
34
|
Defaults to ``'default-NSA'``.
|
33
35
|
|
34
|
-
* non_self_label (``str``): This variable stores the label that will be
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
36
|
+
* non_self_label (``str``): This variable stores the label that will be when the data has \
|
37
|
+
only one output class, and the sample is classified as not belonging to that class. \
|
38
|
+
Defaults to ``'non-self'``.
|
39
|
+
* cell_bounds (``bool``): If set to ``True``, this option limits the generation of \
|
40
|
+
detectors to the space within the plane between 0 and 1. This means that any detector \
|
41
|
+
whose radius exceeds this limit is discarded, this variable is only used in the \
|
42
|
+
``V-detector`` algorithm. Defaults to ``False``.
|
43
|
+
* p (``float``): This parameter stores the value of ``p`` used in the Minkowski distance. \
|
44
|
+
The default is ``2``, which represents normalized Euclidean distance. Different values \
|
45
|
+
of p lead to different variants of the Minkowski distance \
|
46
|
+
[learn more](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.minkowski.html).
|
47
|
+
|
43
48
|
* detectors (``dict``): This variable stores a list of detectors by class.
|
44
49
|
* classes (``npt.NDArray``): list of output classes.
|
45
|
-
|
50
|
+
|
46
51
|
---
|
47
52
|
|
48
|
-
A classe ``RNSA`` (Algoritmo de Seleção Negativa de Valor Real) tem a finalidade de classificação
|
49
|
-
de anomalias através do método self e not self .
|
53
|
+
A classe ``RNSA`` (Algoritmo de Seleção Negativa de Valor Real) tem a finalidade de classificação \
|
54
|
+
e identificação de anomalias através do método self e not self .
|
50
55
|
|
51
56
|
Attributes:
|
52
57
|
---
|
@@ -55,134 +60,154 @@ class RNSA(Base):
|
|
55
60
|
* r_s (``float``): O valor de ``rₛ`` é o raio das amostras próprias da matriz ``X``.
|
56
61
|
* k (``int``): K quantidade de vizinhos próximos para calcular a média da distância dos detectores.
|
57
62
|
* metric (``str``): Forma de calcular a distância: ``'euclidiana', 'minkowski', or 'manhattan'``.
|
58
|
-
* max_discards (``int``): Este parâmetro indica o número máximo de descartes de detectores
|
59
|
-
que tem como objetivo evitar um possível loop infinito caso seja definido
|
60
|
-
gerar detectores do não-próprio.
|
63
|
+
* max_discards (``int``): Este parâmetro indica o número máximo de descartes de detectores \
|
64
|
+
em sequência, que tem como objetivo evitar um possível loop infinito caso seja definido \
|
65
|
+
um raio que não seja possível gerar detectores do não-próprio.
|
61
66
|
* seed (``int``): Semente para a geração randômica dos valores dos detectores.
|
62
67
|
* algorithm (``str``), Definir a versão do algoritmo:
|
63
68
|
|
64
69
|
* ``'default-NSA'``: Algoritmo padrão com raio fixo.
|
65
|
-
* ``'V-detector'``: Este algoritmo utiliza um raio variável para a detecção de
|
66
|
-
de características.
|
70
|
+
* ``'V-detector'``: Este algoritmo utiliza um raio variável para a detecção de \
|
71
|
+
anomalias em espaços de características.
|
67
72
|
|
68
73
|
Defaults to ``'default-NSA'``.
|
69
74
|
|
70
|
-
* non_self_label (``str``): Esta variável armazena o rótulo que será atribuído quando
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
* non_self_label (``str``): Esta variável armazena o rótulo que será atribuído quando \
|
76
|
+
os dados possuírem apenas uma classe de saída, e a amostra for classificada como não \
|
77
|
+
pertencente a essa classe. Defaults to ``'non-self'``.
|
78
|
+
* cell_bounds (``bool``): Se definido como ``True``, esta opção limita a geração dos \
|
79
|
+
detectores ao espaço do plano compreendido entre 0 e 1. Isso significa que qualquer \
|
80
|
+
detector cujo raio ultrapasse esse limite é descartado, e esta variável é usada \
|
81
|
+
exclusivamente no algoritmo ``V-detector``.
|
82
|
+
* p (``float``): Este parâmetro armazena o valor de ``p`` utilizada na distância de \
|
83
|
+
Minkowski. O padrão é ``2``, o que significa distância euclidiana normalizada. \
|
84
|
+
Diferentes valores de p levam a diferentes variantes da distância de Minkowski \
|
85
|
+
[saiba mais](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.minkowski.html).
|
86
|
+
|
79
87
|
* detectors (``dict``): Essa variável armazena uma lista com detectores por classes.
|
80
88
|
* classes (``npt.NDArray``): lista com as classes de saída.
|
81
89
|
"""
|
82
|
-
|
83
|
-
def __init__(
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
r_s: float = 0.0001,
|
88
|
-
k: int = 1,
|
89
|
-
metric: Literal['manhattan', 'minkowski', 'euclidean'] = 'euclidean',
|
90
|
-
max_discards: int = 1000,
|
91
|
-
seed: int = None,
|
92
|
-
algorithm: Literal['default-NSA', 'V-detector'] ='default-NSA',
|
93
|
-
**kwargs: Dict[str, Union[bool, str, float]]
|
94
|
-
):
|
90
|
+
|
91
|
+
def __init__(self, N: int = 100, r: float = 0.05, r_s: float = 0.0001, k: int = 1,
|
92
|
+
metric: Literal["manhattan", "minkowski", "euclidean"] = "euclidean", max_discards: int = 1000,
|
93
|
+
seed: int = None, algorithm: Literal["default-NSA", "V-detector"] = "default-NSA",
|
94
|
+
**kwargs: Dict[str, Union[bool, str, float]]):
|
95
95
|
"""
|
96
96
|
Negative Selection class constructor (``RNSA``).
|
97
97
|
|
98
98
|
Details:
|
99
99
|
---
|
100
|
-
This method initializes the ``detectors``, ``classes``, ``k``, ``metric``, ``N``, ``r``,
|
101
|
-
``max_discards``, ``seed`` and ``algorithm`` attributes.
|
100
|
+
This method initializes the ``detectors``, ``classes``, ``k``, ``metric``, ``N``, ``r``, \
|
101
|
+
``r_S``, ``max_discards``, ``seed`` and ``algorithm`` attributes.
|
102
102
|
|
103
103
|
Parameters:
|
104
104
|
---
|
105
105
|
* N (``int``): Number of detectors. Defaults to ``100``.
|
106
106
|
* r (``float``): Radius of the detector. Defaults to ``0.05``.
|
107
107
|
* r_s (``float``): rₛ Radius of the ``X`` own samples. Defaults to ``0.0001``.
|
108
|
-
* k (``int``): Number of neighbors near the randomly generated detectors to perform the
|
109
|
-
|
108
|
+
* k (``int``): Number of neighbors near the randomly generated detectors to perform the \
|
109
|
+
distance average calculation. Defaults to ``1``.
|
110
110
|
* metric (``str``): Way to calculate the distance between the detector and the sample:
|
111
111
|
|
112
|
-
* ``'Euclidean'`` ➜ The calculation of the distance is given by the expression:
|
113
|
-
|
114
|
-
* ``'
|
112
|
+
* ``'Euclidean'`` ➜ The calculation of the distance is given by the expression: \
|
113
|
+
√( (x₁ – x₂)² + (y₁ – y₂)² + ... + (yn – yn)²).
|
114
|
+
* ``'minkowski'`` ➜ The calculation of the distance is given by the expression: \
|
115
|
+
( |X₁ – Y₁|p + |X₂ – Y₂|p + ... + |Xn – Yn|p) ¹/ₚ.
|
116
|
+
* ``'manhattan'`` ➜ The calculation of the distance is given by the expression: \
|
117
|
+
( |x₁ – x₂| + |y₁ – y₂| + ... + |yn – yn|) .
|
115
118
|
|
116
119
|
Defaults to ``'euclidean'``.
|
117
120
|
|
118
|
-
* max_discards (``int``): This parameter indicates the maximum number of consecutive
|
119
|
-
|
120
|
-
|
121
|
+
* max_discards (``int``): This parameter indicates the maximum number of consecutive \
|
122
|
+
detector discards, aimed at preventing a possible infinite loop in case a radius is \
|
123
|
+
defined that cannot generate non-self detectors. Defaults to ``1000``.
|
124
|
+
* seed (``int``): Seed for the random generation of values in the detectors. Defaults \
|
125
|
+
to ``None``.
|
121
126
|
|
122
127
|
* algorithm(``str``), Set the algorithm version:
|
123
128
|
|
124
129
|
* ``'default-NSA'``: Default algorithm with fixed radius.
|
125
|
-
* ``'V-detector'``: This algorithm is based on the article
|
130
|
+
* ``'V-detector'``: This algorithm is based on the article \
|
131
|
+
"[Real-Valued Negative Selection Algorithm with Variable-Sized Detectors](https://doi.org/10.1007/978-3-540-24854-5_30)", \
|
132
|
+
by Ji, Z., Dasgupta, D. (2004), and uses a variable radius for anomaly \
|
133
|
+
detection in feature spaces.
|
126
134
|
|
127
135
|
Defaults to ``'default-NSA'``.
|
128
136
|
|
129
137
|
- ``**kwargs``:
|
130
|
-
- non_self_label (``str``): This variable stores the label that will be assigned
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
+
- non_self_label (``str``): This variable stores the label that will be assigned \
|
139
|
+
when the data has only one output class, and the sample is classified as not \
|
140
|
+
belonging to that class. Defaults to ``'non-self'``.
|
141
|
+
- cell_bounds (``bool``): If set to ``True``, this option limits the generation \
|
142
|
+
of detectors to the space within the plane between 0 and 1. This means that \
|
143
|
+
any detector whose radius exceeds this limit is discarded, this variable is \
|
144
|
+
only used in the ``V-detector`` algorithm. Defaults to ``False``.
|
145
|
+
- p (``float``): This parameter stores the value of ``p`` used in the Minkowski \
|
146
|
+
distance. The default is ``2``, which represents normalized Euclidean distance. \
|
147
|
+
Different values of p lead to different variants of the Minkowski distance \
|
148
|
+
[learn more](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.minkowski.html).
|
138
149
|
---
|
139
150
|
|
140
151
|
Construtor da classe de Seleção negativa (``RNSA``).
|
141
152
|
|
142
153
|
Details:
|
143
154
|
---
|
144
|
-
Este método inicializa os atributos ``detectors``, ``classes``, ``k``, ``metric``, ``N``,
|
155
|
+
Este método inicializa os atributos ``detectors``, ``classes``, ``k``, ``metric``, ``N``, \
|
156
|
+
``r`` e ``seed``.
|
145
157
|
|
146
158
|
Parameters:
|
147
159
|
---
|
148
160
|
* N (``int``): Quantidade de detectores. Defaults to ``100``.
|
149
161
|
* r (``float``): Raio do detector. Defaults to ``0.05``.
|
150
|
-
* r_s (``float``): O valor de ``rₛ`` é o raio das amostras próprias da matriz ``X``.
|
151
|
-
|
152
|
-
|
162
|
+
* r_s (``float``): O valor de ``rₛ`` é o raio das amostras próprias da matriz ``X``. \
|
163
|
+
Defaults to ``0.0001``.
|
164
|
+
* k (``int``): Quantidade de vizinhos próximos dos detectores gerados aleatoriamente \
|
165
|
+
para efetuar o cálculo da média da distância. Defaults to ``1``.
|
153
166
|
* metric (``str``): Forma para se calcular a distância entre o detector e a amostra:
|
154
167
|
|
155
|
-
* ``'euclidiana'`` ➜ O cálculo da distância dá-se pela expressão:
|
156
|
-
|
157
|
-
* ``'
|
168
|
+
* ``'euclidiana'`` ➜ O cálculo da distância dá-se pela expressão: \
|
169
|
+
√( (x₁ – x₂)² + (y₁ – y₂)² + ... + (yn – yn)²).
|
170
|
+
* ``'minkowski'`` ➜ O cálculo da distância dá-se pela expressão: \
|
171
|
+
( |X₁ – Y₁|p + |X₂ – Y₂|p + ... + |Xn – Yn|p) ¹/ₚ.
|
172
|
+
* ``'manhattan'`` ➜ O cálculo da distância dá-se pela expressão: \
|
173
|
+
( |x₁ – x₂| + |y₁ – y₂| + ... + |yn – yn|).
|
158
174
|
|
159
175
|
Defaults to ``'euclidean'``.
|
160
176
|
|
161
|
-
* max_discards (``int``): Este parâmetro indica o número máximo de descartes de detectores
|
162
|
-
|
163
|
-
|
177
|
+
* max_discards (``int``): Este parâmetro indica o número máximo de descartes de detectores \
|
178
|
+
em sequência, que tem como objetivo evitar um possível loop infinito caso seja definido \
|
179
|
+
um raio que não seja possível gerar detectores do não-próprio. Defaults to ``1000``.
|
180
|
+
* seed (``int``): Semente para a geração randômica dos valores nos detectores. \
|
181
|
+
Defaults to ``None``.
|
164
182
|
* algorithm (``str``), Definir a versão do algoritmo:
|
165
183
|
|
166
184
|
* ``'default-NSA'``: Algoritmo padrão com raio fixo.
|
167
|
-
* ``'V-detector'``: Este algoritmo é baseado no artigo
|
185
|
+
* ``'V-detector'``: Este algoritmo é baseado no artigo \
|
186
|
+
"[Real-Valued Negative Selection Algorithm with Variable-Sized Detectors](https://doi.org/10.1007/978-3-540-24854-5_30)", \
|
187
|
+
de autoria de Ji, Z., Dasgupta, D. (2004), e utiliza um raio variável para a \
|
188
|
+
detecção de anomalias em espaços de características.
|
168
189
|
|
169
190
|
Defaults to ``'default-NSA'``.
|
170
191
|
|
171
192
|
- ``**kwargs``:
|
172
|
-
- non_self_label (``str``): Esta variável armazena o rótulo que será atribuído
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
193
|
+
- non_self_label (``str``): Esta variável armazena o rótulo que será atribuído \
|
194
|
+
quando os dados possuírem apenas uma classe de saída, e a amostra for \
|
195
|
+
classificada como não pertencente a essa classe. Defaults to ``'non-self'``.
|
196
|
+
- cell_bounds (``bool``): Se definido como ``True``, esta opção limita a \
|
197
|
+
geração dos detectores ao espaço do plano compreendido entre 0 e 1. Isso \
|
198
|
+
significa que qualquer detector cujo raio ultrapasse esse limite é descartado, \
|
199
|
+
e esta variável é usada exclusivamente no algoritmo ``V-detector``.
|
200
|
+
- p (``float``): Este parâmetro armazena o valor de ``p`` utilizada na distância \
|
201
|
+
de Minkowski. O padrão é ``2``, o que significa distância euclidiana normalizada. \
|
202
|
+
Diferentes valores de p levam a diferentes variantes da distância de Minkowski \
|
203
|
+
[saiba mais](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.minkowski.html).
|
180
204
|
"""
|
181
|
-
|
182
|
-
|
205
|
+
|
206
|
+
super().__init__(metric)
|
207
|
+
if metric == "manhattan" or metric == "minkowski":
|
183
208
|
self.metric = metric
|
184
209
|
else:
|
185
|
-
self.metric =
|
210
|
+
self.metric = "euclidean"
|
186
211
|
|
187
212
|
if seed is not None and isinstance(seed, int):
|
188
213
|
np.random.seed(seed)
|
@@ -204,18 +229,18 @@ class RNSA(Base):
|
|
204
229
|
self.r: float = 0.05
|
205
230
|
else:
|
206
231
|
self.r: float = r
|
207
|
-
|
232
|
+
|
208
233
|
if r_s > 0:
|
209
234
|
self.r_s: float = r_s
|
210
235
|
else:
|
211
236
|
self.r_s: float = 0
|
212
237
|
|
213
|
-
if algorithm ==
|
238
|
+
if algorithm == "V-detector":
|
214
239
|
self._Detector = namedtuple("Detector", "position radius")
|
215
240
|
self._algorithm: str = algorithm
|
216
241
|
else:
|
217
242
|
self._Detector = namedtuple("Detector", "position")
|
218
|
-
self._algorithm: str =
|
243
|
+
self._algorithm: str = "default-NSA"
|
219
244
|
|
220
245
|
if max_discards > 0:
|
221
246
|
self.max_discards: int = max_discards
|
@@ -223,10 +248,10 @@ class RNSA(Base):
|
|
223
248
|
self.max_discards: int = 1000
|
224
249
|
|
225
250
|
# Obtém as variáveis do kwargs.
|
226
|
-
self.p: float = kwargs.get(
|
227
|
-
self._cell_bounds: bool = kwargs.get(
|
228
|
-
self.non_self_label: str = kwargs.get(
|
229
|
-
|
251
|
+
self.p: float = kwargs.get("p", 2)
|
252
|
+
self._cell_bounds: bool = kwargs.get("cell_bounds", False)
|
253
|
+
self.non_self_label: str = kwargs.get("non_self_label", "non-self")
|
254
|
+
|
230
255
|
# Inicializa as demais variáveis da classe como None
|
231
256
|
self.detectors: Union[dict, None] = None
|
232
257
|
self.classes: npt.NDArray = None
|
@@ -238,8 +263,8 @@ class RNSA(Base):
|
|
238
263
|
|
239
264
|
Parameters:
|
240
265
|
---
|
241
|
-
* X (``npt.NDArray``): Training array, containing the samples and their characteristics,
|
242
|
-
|
266
|
+
* X (``npt.NDArray``): Training array, containing the samples and their characteristics, \
|
267
|
+
[``N samples`` (rows)][``N features`` (columns)].
|
243
268
|
* y (``npt.NDArray``): Array of target classes of ``X`` with [``N samples`` (lines)].
|
244
269
|
* verbose (``bool``): Feedback from detector generation to the user.
|
245
270
|
returns:
|
@@ -248,13 +273,13 @@ class RNSA(Base):
|
|
248
273
|
|
249
274
|
----
|
250
275
|
|
251
|
-
A função ``fit(...)``, realiza o treinamento de acordo com ``X`` e ``y``, usando o método
|
276
|
+
A função ``fit(...)``, realiza o treinamento de acordo com ``X`` e ``y``, usando o método
|
252
277
|
de seleção negativa(``NegativeSelect``).
|
253
278
|
|
254
279
|
Parameters:
|
255
280
|
---
|
256
|
-
* X (``npt.NDArray``): Array de treinamento, contendo as amostras é suas características,
|
257
|
-
|
281
|
+
* X (``npt.NDArray``): Array de treinamento, contendo as amostras é suas características, \
|
282
|
+
[``N amostras`` (linhas)][``N características`` (colunas)].
|
258
283
|
* y (``npt.NDArray``): Array com as classes alvos de ``X`` com [``N amostras`` (linhas)].
|
259
284
|
* verbose (``bool``): Feedback da geração de detectores para o usuário.
|
260
285
|
Returns:
|
@@ -262,7 +287,7 @@ class RNSA(Base):
|
|
262
287
|
(``self``): Retorna a própria instância.
|
263
288
|
"""
|
264
289
|
super()._check_and_raise_exceptions_fit(X, y)
|
265
|
-
|
290
|
+
|
266
291
|
# Identificando as classes possíveis, dentro do array de saídas ``y``.
|
267
292
|
self.classes = np.unique(y)
|
268
293
|
# Dict que armazenará os detectores com as classes como key.
|
@@ -272,7 +297,7 @@ class RNSA(Base):
|
|
272
297
|
# Barra de progresso para a geração de todos os detectores.
|
273
298
|
if verbose:
|
274
299
|
progress = tqdm(total=int(self.N * (len(self.classes))),
|
275
|
-
bar_format=
|
300
|
+
bar_format="{desc} ┇{bar}┇ {n}/{total} detectors", postfix="\n",)
|
276
301
|
for _class_ in self.classes:
|
277
302
|
# Inicia o conjunto vazio que conterá os detectores válidos.
|
278
303
|
valid_detectors_set = []
|
@@ -280,19 +305,22 @@ class RNSA(Base):
|
|
280
305
|
# Informando em qual classe o algoritmo está para a barra de progresso.
|
281
306
|
if verbose:
|
282
307
|
progress.set_description_str(
|
283
|
-
f
|
308
|
+
f"Generating the detectors for the {_class_} class:"
|
309
|
+
)
|
284
310
|
while len(valid_detectors_set) < self.N:
|
285
311
|
# Gera um vetor candidato a detector aleatoriamente com valores entre 0 e 1.
|
286
312
|
vector_x = np.random.random_sample(size=X.shape[1])
|
287
313
|
# Verifica a validade do detector para o não-próprio com relação às amostras da classe.
|
288
314
|
valid_detector = self.__checks_valid_detector(
|
289
|
-
X=X, vector_x=vector_x, samples_index_class=sample_index[_class_]
|
290
|
-
|
315
|
+
X=X, vector_x=vector_x, samples_index_class=sample_index[_class_]
|
316
|
+
)
|
317
|
+
|
291
318
|
# Se o detector for válido, adicione a lista dos válidos.
|
292
|
-
if self._algorithm ==
|
319
|
+
if self._algorithm == "V-detector" and valid_detector is not False:
|
293
320
|
discard_count = 0
|
294
321
|
valid_detectors_set.append(
|
295
|
-
self._Detector(vector_x, valid_detector[1])
|
322
|
+
self._Detector(vector_x, valid_detector[1])
|
323
|
+
)
|
296
324
|
if verbose:
|
297
325
|
progress.update(1)
|
298
326
|
elif valid_detector:
|
@@ -303,21 +331,26 @@ class RNSA(Base):
|
|
303
331
|
else:
|
304
332
|
discard_count += 1
|
305
333
|
if discard_count == self.max_discards:
|
306
|
-
raise Exception(
|
307
|
-
|
308
|
-
|
309
|
-
|
334
|
+
raise Exception(
|
335
|
+
"An error has been identified:\n"
|
336
|
+
f"the maximum number of discards of detectors for the {_class_} class "
|
337
|
+
"has been reached.\nIt is recommended to check the defined radius and "
|
338
|
+
"consider reducing its value."
|
339
|
+
)
|
340
|
+
|
310
341
|
# Adicionar detectores, com as classes como chave na dict.
|
311
342
|
list_detectors_by_class[_class_] = valid_detectors_set
|
312
343
|
# Informar a finalização da geração dos detectores para as classes.
|
313
344
|
if verbose:
|
314
345
|
progress.set_description(
|
315
|
-
f'\033[92m✔ Non-self detectors for classes ({", ".join(map(str, self.classes))})
|
346
|
+
f'\033[92m✔ Non-self detectors for classes ({", ".join(map(str, self.classes))}) '
|
347
|
+
f'successfully generated\033[0m'
|
348
|
+
)
|
316
349
|
# Armazena os detectores encontrados no atributo, para os detectores da classe.
|
317
350
|
self.detectors = list_detectors_by_class
|
318
351
|
return self
|
319
352
|
|
320
|
-
def predict(self, X: npt.NDArray) -> npt.NDArray:
|
353
|
+
def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
|
321
354
|
"""
|
322
355
|
Function to perform the prediction of classes based on detectors
|
323
356
|
created after training.
|
@@ -335,31 +368,34 @@ class RNSA(Base):
|
|
335
368
|
|
336
369
|
---
|
337
370
|
|
338
|
-
Função para efetuar a previsão das classes com base nos detectores
|
371
|
+
Função para efetuar a previsão das classes com base nos detectores
|
339
372
|
criados após o treinamento.
|
340
373
|
|
341
374
|
Parameters:
|
342
375
|
---
|
343
|
-
* X (``npt.NDArray``): Array com as amostras de entradas com [``N amostras`` (Linhas)] e
|
376
|
+
* X (``npt.NDArray``): Array com as amostras de entradas com [``N amostras`` (Linhas)] e
|
344
377
|
[``N características``(Colunas)]
|
345
378
|
|
346
379
|
Returns:
|
347
380
|
---
|
348
|
-
* C – (``npt.NDArray``): um ndarray de forma ``C`` [``N amostras``],
|
381
|
+
* C – (``npt.NDArray``): um ndarray de forma ``C`` [``N amostras``],
|
349
382
|
contendo as classes previstas para ``X``.
|
350
383
|
* ``None``: Se não existir detectores para a previsão.
|
351
384
|
"""
|
352
385
|
# se não houver detectores retorna None.
|
353
386
|
if self.detectors is None:
|
354
387
|
return None
|
355
|
-
elif not isinstance(X,
|
388
|
+
elif not isinstance(X, (np.ndarray, list)):
|
356
389
|
raise TypeError("X is not an ndarray or list")
|
357
390
|
elif len(self.detectors[self.classes[0]][0].position) != len(X[0]):
|
358
|
-
raise Exception(
|
359
|
-
|
391
|
+
raise Exception(
|
392
|
+
"X does not have {} features to make the prediction".format(
|
393
|
+
len(self.detectors[self.classes[0]][0])
|
394
|
+
)
|
395
|
+
)
|
360
396
|
|
361
397
|
# Inicia um array vazio.
|
362
|
-
C = np.empty(shape=
|
398
|
+
C = np.empty(shape=0)
|
363
399
|
# Para cada linha de amostra em X.
|
364
400
|
for line in X:
|
365
401
|
class_found: bool
|
@@ -373,25 +409,31 @@ class RNSA(Base):
|
|
373
409
|
# Se possuir apenas uma classe e não classificar a amostra define a saída como não-própria.
|
374
410
|
if not class_found and len(self.classes) == 1:
|
375
411
|
C = np.append(C, [self.non_self_label])
|
376
|
-
# Se não identificar a classe com os detectores, coloca a classe com a maior distância
|
412
|
+
# Se não identificar a classe com os detectores, coloca a classe com a maior distância
|
413
|
+
# da média dos seus detectores.
|
377
414
|
elif not class_found:
|
378
415
|
average_distance: dict = {}
|
379
416
|
for _class_ in self.classes:
|
380
417
|
detectores = list(
|
381
|
-
map(lambda x: x.position, self.detectors[_class_])
|
382
|
-
|
383
|
-
|
384
|
-
|
418
|
+
map(lambda x: x.position, self.detectors[_class_])
|
419
|
+
)
|
420
|
+
average_distance[_class_] = np.average(
|
421
|
+
[self.__distance(detector, line)
|
422
|
+
for detector in detectores]
|
423
|
+
)
|
424
|
+
C = np.append(C, [max(average_distance, key=average_distance.get)])
|
385
425
|
return C
|
386
426
|
|
387
|
-
def __checks_valid_detector(self, X: npt.NDArray = None, vector_x: npt.NDArray = None,
|
427
|
+
def __checks_valid_detector(self, X: npt.NDArray = None, vector_x: npt.NDArray = None,
|
428
|
+
samples_index_class: npt.NDArray = None):
|
388
429
|
"""
|
389
430
|
Function to check if the detector has a valid non-proper ``r`` radius for the class.
|
390
431
|
|
391
432
|
Parameters:
|
392
433
|
---
|
393
434
|
* X (``npt.NDArray``): Array ``X`` with the samples.
|
394
|
-
* vector_x (``npt.NDArray``): Randomly generated vector x candidate detector with values
|
435
|
+
* vector_x (``npt.NDArray``): Randomly generated vector x candidate detector with values \
|
436
|
+
between [0, 1].
|
395
437
|
* samples_index_class (``npt.NDArray``): Sample positions of a class in ``X``.
|
396
438
|
|
397
439
|
returns:
|
@@ -405,7 +447,8 @@ class RNSA(Base):
|
|
405
447
|
Parameters:
|
406
448
|
---
|
407
449
|
* X (``npt.NDArray``): Array ``X`` com as amostras.
|
408
|
-
* vector_x (``npt.NDArray``): Vetor x candidato a detector gerado aleatoriamente com valores
|
450
|
+
* vector_x (``npt.NDArray``): Vetor x candidato a detector gerado aleatoriamente com valores \
|
451
|
+
entre [0, 1].
|
409
452
|
* samples_index_class (``npt.NDArray``): Posições das amostras de uma classe em ``X``.
|
410
453
|
|
411
454
|
Returns:
|
@@ -414,37 +457,41 @@ class RNSA(Base):
|
|
414
457
|
|
415
458
|
"""
|
416
459
|
# Se um ou mais array de entrada possuir zero dados, retorna falso.
|
417
|
-
if np.size(samples_index_class) == 0 or np.size(X) == 0 or np.size(vector_x) == 0:
|
460
|
+
if (np.size(samples_index_class) == 0 or np.size(X) == 0 or np.size(vector_x) == 0):
|
418
461
|
return False
|
419
|
-
# se self.k > 1 utiliza os k vizinhos mais próximos (knn), se não verifica o detector sem
|
462
|
+
# se self.k > 1 utiliza os k vizinhos mais próximos (knn), se não verifica o detector sem
|
463
|
+
# considerar os knn.
|
420
464
|
if self.k > 1:
|
421
465
|
# Iniciar a lista dos knn vazia.
|
422
|
-
knn_list = np.empty(shape=
|
466
|
+
knn_list = np.empty(shape=0)
|
423
467
|
for i in samples_index_class:
|
424
|
-
# Calcula a distância entre os dois vetores e adiciona a lista dos knn, se a
|
468
|
+
# Calcula a distância entre os dois vetores e adiciona a lista dos knn, se a
|
469
|
+
# distância for menor que a maior da lista.
|
425
470
|
knn_list = self.__compare_KnearestNeighbors_List(
|
426
|
-
knn_list, self.__distance(X[i], vector_x)
|
471
|
+
knn_list, self.__distance(X[i], vector_x)
|
472
|
+
)
|
427
473
|
# Se a média das distâncias na lista dos knn, for menor que o raio, retorna verdadeiro.
|
428
474
|
distance_mean = np.mean(knn_list)
|
429
|
-
if self._algorithm ==
|
475
|
+
if self._algorithm == "V-detector":
|
430
476
|
return self.__detector_is_valid_to_Vdetector(distance_mean, vector_x)
|
431
477
|
elif distance_mean > (self.r + self.r_s):
|
432
478
|
return True # Detector é valido!
|
433
479
|
else:
|
434
480
|
distance: Union[float, None] = None
|
435
481
|
for i in samples_index_class:
|
436
|
-
if self._algorithm ==
|
482
|
+
if self._algorithm == "V-detector":
|
437
483
|
new_distance = self.__distance(X[i], vector_x)
|
438
484
|
if distance is None:
|
439
485
|
distance = new_distance
|
440
486
|
elif distance > new_distance:
|
441
487
|
distance = new_distance
|
442
488
|
else:
|
443
|
-
# Calcula a distância entre os vetores, se menor ou igual ao raio + raio da
|
489
|
+
# Calcula a distância entre os vetores, se menor ou igual ao raio + raio da
|
490
|
+
# amostra define a validade do detector como falso.
|
444
491
|
if (self.r + self.r_s) >= self.__distance(X[i], vector_x):
|
445
492
|
return False # Detector não é valido!
|
446
|
-
|
447
|
-
if self._algorithm ==
|
493
|
+
|
494
|
+
if self._algorithm == "V-detector":
|
448
495
|
return self.__detector_is_valid_to_Vdetector(distance, vector_x)
|
449
496
|
return True # Detector é valido!
|
450
497
|
|
@@ -467,26 +514,26 @@ class RNSA(Base):
|
|
467
514
|
|
468
515
|
---
|
469
516
|
|
470
|
-
Compara a distância do k-vizinho mais próximo na posição ``k-1``da lista ``knn``,
|
517
|
+
Compara a distância do k-vizinho mais próximo na posição ``k-1``da lista ``knn``,
|
471
518
|
se a distância da nova amostra for menor, substitui ela e ordena em ordem crescente.
|
472
519
|
|
473
520
|
|
474
521
|
Parameters:
|
475
522
|
---
|
476
523
|
knn (npt.NDArray): Lista de distâncias dos k-vizinhos mais próximos.
|
477
|
-
distance (float): Distância a ser verificada.
|
524
|
+
distance (float): Distância a ser verificada.
|
478
525
|
|
479
526
|
Returns:
|
480
527
|
---
|
481
528
|
npt.NDArray: Lista de vizinhos mais próximos atualizada e ordenada.
|
482
529
|
"""
|
483
530
|
# Se a quantidade de distâncias em knn, for menor que k, adiciona a distância.
|
484
|
-
if
|
531
|
+
if len(knn) < self.k:
|
485
532
|
knn = np.append(knn, distance)
|
486
533
|
knn.sort()
|
487
534
|
else:
|
488
535
|
# Se não, adicione a distância, se a nova distancia for menor que a maior distância da lista.
|
489
|
-
if
|
536
|
+
if knn[self.k - 1] > distance:
|
490
537
|
knn[self.k - 1] = distance
|
491
538
|
knn.sort()
|
492
539
|
|
@@ -498,7 +545,7 @@ class RNSA(Base):
|
|
498
545
|
|
499
546
|
Details:
|
500
547
|
---
|
501
|
-
In this function, when there is class ambiguity, it returns the class that has the greatest
|
548
|
+
In this function, when there is class ambiguity, it returns the class that has the greatest
|
502
549
|
average distance between the detectors.
|
503
550
|
|
504
551
|
Parameters:
|
@@ -507,7 +554,8 @@ class RNSA(Base):
|
|
507
554
|
|
508
555
|
returns:
|
509
556
|
---
|
510
|
-
* Returns the predicted class with the detectors or None if the sample does not qualify
|
557
|
+
* Returns the predicted class with the detectors or None if the sample does not qualify \
|
558
|
+
for any class.
|
511
559
|
|
512
560
|
---
|
513
561
|
|
@@ -515,8 +563,8 @@ class RNSA(Base):
|
|
515
563
|
|
516
564
|
Details:
|
517
565
|
---
|
518
|
-
Nesta função, quando possui ambiguidade de classes, retorna a classe que possuir a média de
|
519
|
-
maior entre os detectores.
|
566
|
+
Nesta função, quando possui ambiguidade de classes, retorna a classe que possuir a média de \
|
567
|
+
distância maior entre os detectores.
|
520
568
|
|
521
569
|
Parameters:
|
522
570
|
---
|
@@ -524,7 +572,8 @@ class RNSA(Base):
|
|
524
572
|
|
525
573
|
Returns:
|
526
574
|
---
|
527
|
-
* Retorna a classe prevista com os detectores ou None se a amostra não se qualificar
|
575
|
+
* Retorna a classe prevista com os detectores ou None se a amostra não se qualificar \
|
576
|
+
a nenhuma classe.
|
528
577
|
"""
|
529
578
|
|
530
579
|
# Lista para armazenar as classes e a distância média entre os detectores e a amostra.
|
@@ -538,7 +587,7 @@ class RNSA(Base):
|
|
538
587
|
distance = self.__distance(detector.position, line)
|
539
588
|
# Soma as distâncias para calcular a média.
|
540
589
|
sum_distance += distance
|
541
|
-
if self._algorithm ==
|
590
|
+
if self._algorithm == "V-detector":
|
542
591
|
if distance <= detector.radius:
|
543
592
|
class_found = False
|
544
593
|
break
|
@@ -546,9 +595,10 @@ class RNSA(Base):
|
|
546
595
|
class_found = False
|
547
596
|
break
|
548
597
|
|
549
|
-
# Se a amostra passar por todos os detectores de uma classe, adiciona a classe como
|
598
|
+
# Se a amostra passar por todos os detectores de uma classe, adiciona a classe como
|
599
|
+
# possível previsão.
|
550
600
|
if class_found:
|
551
|
-
possible_classes.append([_class_, sum_distance/self.N])
|
601
|
+
possible_classes.append([_class_, sum_distance / self.N])
|
552
602
|
# Se classificar como pertencentes a apenas uma classe, retorna a classe.
|
553
603
|
if len(possible_classes) == 1:
|
554
604
|
return possible_classes[0][0]
|
@@ -588,33 +638,37 @@ class RNSA(Base):
|
|
588
638
|
|
589
639
|
def __detector_is_valid_to_Vdetector(self, distance: float, vector_x: npt.NDArray):
|
590
640
|
"""
|
591
|
-
Check if the distance between the detector and the samples, minus the radius of the samples,
|
641
|
+
Check if the distance between the detector and the samples, minus the radius of the samples,
|
592
642
|
is greater than the minimum radius.
|
593
643
|
|
594
644
|
Parameters:
|
595
645
|
---
|
596
646
|
distance (``float``): minimum distance calculated between all samples.
|
597
|
-
vector_x (``numpy.ndarray``): randomly generated candidate detector vector x with values
|
647
|
+
vector_x (``numpy.ndarray``): randomly generated candidate detector vector x with values \
|
648
|
+
between 0 and 1.
|
649
|
+
|
598
650
|
Returns:
|
599
651
|
---
|
600
|
-
* ``False``: if the calculated radius is smaller than the minimum distance or exceeds the
|
601
|
-
|
652
|
+
* ``False``: if the calculated radius is smaller than the minimum distance or exceeds the \
|
653
|
+
edge of the space, if this option is enabled.
|
602
654
|
* ``True`` and the distance minus the radius of the samples, if the radius is valid.`
|
603
655
|
|
604
656
|
----
|
605
657
|
|
606
|
-
Verifique se a distância entre o detector e as amostras, descontando o raio das amostras,
|
658
|
+
Verifique se a distância entre o detector e as amostras, descontando o raio das amostras, \
|
659
|
+
é maior do que o raio mínimo.
|
607
660
|
|
608
661
|
Parameters:
|
609
662
|
---
|
610
663
|
distance (``float``): distância mínima calculada entre todas as amostras.
|
611
|
-
vector_x (``numpy.ndarray``): vetor x candidato do detector gerado aleatoriamente, com
|
612
|
-
|
664
|
+
vector_x (``numpy.ndarray``): vetor x candidato do detector gerado aleatoriamente, com \
|
665
|
+
valores entre 0 e 1.
|
666
|
+
|
613
667
|
Returns:
|
614
668
|
---
|
615
669
|
|
616
|
-
* ``False``: caso o raio calculado seja menor do que a distância mínima ou ultrapasse a
|
617
|
-
|
670
|
+
* ``False``: caso o raio calculado seja menor do que a distância mínima ou ultrapasse a \
|
671
|
+
borda do espaço, caso essa opção esteja habilitada.
|
618
672
|
* ``True`` e a distância menos o raio das amostras, caso o raio seja válido.
|
619
673
|
"""
|
620
674
|
new_detector_r = float(distance - self.r_s)
|
@@ -630,12 +684,14 @@ class RNSA(Base):
|
|
630
684
|
|
631
685
|
def __slice_index_list_by_class(self, y: npt.NDArray) -> dict:
|
632
686
|
"""
|
633
|
-
The function ``__slice_index_list_by_class(...)``, separates the indices of the lines
|
634
|
-
to loop through the sample array, only in positions where
|
687
|
+
The function ``__slice_index_list_by_class(...)``, separates the indices of the lines \
|
688
|
+
according to the output class, to loop through the sample array, only in positions where \
|
689
|
+
the output is the class being trained.
|
635
690
|
|
636
691
|
Parameters:
|
637
692
|
---
|
638
|
-
* y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the
|
693
|
+
* y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
|
694
|
+
``X`` sample array.
|
639
695
|
|
640
696
|
returns:
|
641
697
|
---
|
@@ -643,35 +699,36 @@ class RNSA(Base):
|
|
643
699
|
|
644
700
|
---
|
645
701
|
|
646
|
-
A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe
|
647
|
-
para percorrer o array de amostra, apenas nas posições que a saída for a classe que
|
702
|
+
A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe \
|
703
|
+
de saída, para percorrer o array de amostra, apenas nas posições que a saída for a classe que \
|
704
|
+
está sendo treinada.
|
648
705
|
|
649
706
|
Parameters:
|
650
707
|
---
|
651
|
-
* y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de
|
708
|
+
* y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do array \
|
709
|
+
de amostra ``X``.
|
652
710
|
|
653
711
|
Returns:
|
654
712
|
---
|
655
713
|
* dict: Um dicionário com a lista de posições do array(``y``), com as classes como chave.
|
656
714
|
"""
|
657
715
|
return super()._slice_index_list_by_class(y)
|
658
|
-
|
716
|
+
|
659
717
|
def score(self, X: npt.NDArray, y: list) -> float:
|
660
718
|
"""
|
661
719
|
Score function calculates forecast accuracy.
|
662
720
|
|
663
721
|
Details:
|
664
722
|
---
|
665
|
-
This function performs the prediction of X and checks how many elements are equal between
|
666
|
-
This function was added for compatibility with some scikit-learn
|
723
|
+
This function performs the prediction of X and checks how many elements are equal between \
|
724
|
+
vector y and y_predicted. This function was added for compatibility with some scikit-learn \
|
725
|
+
functions.
|
667
726
|
|
668
727
|
Parameters:
|
669
728
|
-----------
|
670
729
|
|
671
|
-
X
|
672
|
-
|
673
|
-
y: np.ndarray
|
674
|
-
True values with shape (n_samples,).
|
730
|
+
* X (np.ndarray): Feature set with shape (n_samples, n_features).
|
731
|
+
* y (np.ndarray): True values with shape (n_samples,).
|
675
732
|
|
676
733
|
Returns:
|
677
734
|
-------
|
@@ -685,84 +742,88 @@ class RNSA(Base):
|
|
685
742
|
|
686
743
|
Details:
|
687
744
|
---
|
688
|
-
Esta função realiza a previsão de X e verifica quantos elementos são iguais entre o vetor
|
689
|
-
Essa função foi adicionada para oferecer compatibilidade com algumas
|
745
|
+
Esta função realiza a previsão de X e verifica quantos elementos são iguais entre o vetor \
|
746
|
+
y e y_previsto. Essa função foi adicionada para oferecer compatibilidade com algumas \
|
747
|
+
funções do scikit-learn.
|
690
748
|
|
691
749
|
Parameters:
|
692
750
|
---
|
693
751
|
|
694
|
-
* X :
|
695
|
-
|
696
|
-
* y : np.ndarray
|
697
|
-
Valores verdadeiros com shape (n_samples,).
|
752
|
+
* X (np.ndarray): Conjunto de características com shape (n_samples, n_features).
|
753
|
+
* y (np.ndarray): Valores verdadeiros com shape (n_samples,).
|
698
754
|
|
699
755
|
returns:
|
700
756
|
---
|
701
757
|
|
702
|
-
accuracy :
|
703
|
-
|
758
|
+
* accuracy (float): A acurácia do modelo.
|
759
|
+
|
704
760
|
"""
|
705
761
|
return super()._score(X, y)
|
706
762
|
|
707
763
|
def get_params(self, deep: bool = True) -> dict:
|
708
764
|
return {
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
765
|
+
"N": self.N,
|
766
|
+
"r": self.r,
|
767
|
+
"k": self.k,
|
768
|
+
"metric": self.metric,
|
769
|
+
"seed": self.seed,
|
770
|
+
"algorithm": self._algorithm,
|
771
|
+
"r_s": self.r_s,
|
772
|
+
"cell_bounds": self._cell_bounds,
|
773
|
+
"p": self.p,
|
718
774
|
}
|
719
775
|
|
776
|
+
|
720
777
|
class BNSA(Base):
|
721
778
|
"""
|
722
|
-
The ``BNSA`` (Binary Negative Selection Algorithm) class is for classification and identification
|
723
|
-
of anomalies through the self and not self method.
|
779
|
+
The ``BNSA`` (Binary Negative Selection Algorithm) class is for classification and identification \
|
780
|
+
purposes of anomalies through the self and not self method.
|
724
781
|
|
725
782
|
Attributes:
|
726
783
|
---
|
727
|
-
|
784
|
+
|
728
785
|
* N (``int``): Number of detectors.
|
729
|
-
* aff_thresh (``float``): The variable represents the percentage of similarity between the
|
730
|
-
|
731
|
-
|
786
|
+
* aff_thresh (``float``): The variable represents the percentage of similarity between the \
|
787
|
+
T cell and the own samples.
|
788
|
+
* max_discards (``int``): This parameter indicates the maximum number of detector discards \
|
789
|
+
in sequence, which aims to avoid a possible infinite loop if a radius is defined that \
|
790
|
+
it is not possible to generate non-self detectors.
|
732
791
|
* seed (``int``): Seed for the random generation of values in the detectors.
|
733
|
-
|
792
|
+
|
734
793
|
* detectors (``dict``): This variable stores a list of detectors by class.
|
735
794
|
* classes (``npt.NDArray``): list of output classes.
|
736
|
-
|
795
|
+
|
737
796
|
|
738
797
|
---
|
739
798
|
|
740
|
-
A classe ``BNSA`` (Algoritmo de Seleção Negativa Binária) tem a finalidade de classificação e
|
741
|
-
de anomalias através do método self e not self .
|
799
|
+
A classe ``BNSA`` (Algoritmo de Seleção Negativa Binária) tem a finalidade de classificação e \
|
800
|
+
identificação de anomalias através do método self e not self .
|
742
801
|
|
743
802
|
Attributes:
|
744
803
|
---
|
745
804
|
* N (``int``): Quantidade de detectores. Defaults to ``100``.
|
746
|
-
* aff_thresh (``float``): A variável representa a porcentagem de similaridade entre a célula
|
747
|
-
|
748
|
-
|
749
|
-
|
805
|
+
* aff_thresh (``float``): A variável representa a porcentagem de similaridade entre a célula \
|
806
|
+
T e as amostras próprias. O valor padrão é de 10% (0,1), enquanto que o valor de 1,0 \
|
807
|
+
representa 100% de similaridade.
|
808
|
+
* max_discards (``int``): Este parâmetro indica o número máximo de descartes de detectores \
|
809
|
+
em sequência, que tem como objetivo evitar um possível loop infinito caso seja definido \
|
810
|
+
um raio que não seja possível gerar detectores do não-próprio. Defaults to ``100``.
|
750
811
|
* seed (``int``): Semente para a geração randômica dos valores nos detectores. Defaults to ``None``.
|
812
|
+
* no_label_sample_selection (``str``): Method for selecting labels for samples designated as \
|
813
|
+
non-members by all non-member detectors. Defaults to ``max_average_difference``.
|
814
|
+
|
751
815
|
|
752
816
|
* detectors (``dict``): Essa variável armazena uma lista com detectores por classes.
|
753
817
|
* classes (``npt.NDArray``): lista com as classes de saída.
|
754
818
|
|
755
819
|
"""
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
max_discards: int = 1000,
|
761
|
-
seed: int = None
|
762
|
-
):
|
820
|
+
|
821
|
+
def __init__(self, N: int = 100, aff_thresh: float = 0.1, max_discards: int = 1000, seed: int = None,
|
822
|
+
no_label_sample_selection: Literal["max_average_difference", "max_nearest_difference"] =
|
823
|
+
"max_average_difference"):
|
763
824
|
"""
|
764
825
|
Constructor of the Negative Selection class (``BNSA``).
|
765
|
-
|
826
|
+
|
766
827
|
Details:
|
767
828
|
---
|
768
829
|
This method initializes the ``detectors``, ``classes``, ``N``, ``t`` and ``seed`` attributes.
|
@@ -770,16 +831,23 @@ class BNSA(Base):
|
|
770
831
|
Parameters:
|
771
832
|
---
|
772
833
|
* N (``int``): Number of detectors. Defaults to ``100``.
|
773
|
-
* aff_thresh (``float``): The variable represents the percentage of similarity between
|
774
|
-
|
775
|
-
|
776
|
-
|
834
|
+
* aff_thresh (``float``): The variable represents the percentage of similarity between \
|
835
|
+
the T cell and the own samples. The default value is 10% (0.1), while a value of 1.0 \
|
836
|
+
represents 100% similarity.
|
837
|
+
* max_discards (``int``): This parameter indicates the maximum number of detector \
|
838
|
+
discards in sequence, which aims to avoid a possible infinite loop if a radius is \
|
839
|
+
defined that it is not possible to generate non-self detectors. Defaults to ``1000``.
|
777
840
|
* seed (``int``): Seed for the random generation of values in the detectors. Defaults to ``None``.
|
778
|
-
|
841
|
+
* no_label_sample_selection (``str``): Method for selecting labels for samples designated as \
|
842
|
+
non-members by all non-member detectors. Available method types:
|
843
|
+
- (``max_average_difference``): Selects the class with the highest average difference \
|
844
|
+
among the detectors.
|
845
|
+
- (``max_nearest_difference``): Selects the class with the highest difference between \
|
846
|
+
the nearest and farthest detector from the sample.
|
779
847
|
---
|
780
848
|
|
781
849
|
Construtor da classe de Seleção negativa (``BNSA``).
|
782
|
-
|
850
|
+
|
783
851
|
Details:
|
784
852
|
---
|
785
853
|
Este método inicializa os atributos ``detectors``, ``classes``, ``N``, ``t`` e ``seed``.
|
@@ -787,31 +855,47 @@ class BNSA(Base):
|
|
787
855
|
Parameters:
|
788
856
|
---
|
789
857
|
* N (``int``): Quantidade de detectores. Defaults to ``100``.
|
790
|
-
* aff_thresh (``float``): A variável representa a porcentagem de similaridade entre a
|
791
|
-
|
792
|
-
|
793
|
-
|
858
|
+
* aff_thresh (``float``): A variável representa a porcentagem de similaridade entre a \
|
859
|
+
célula T e as amostras próprias. O valor padrão é de 10% (0,1), enquanto que o valor \
|
860
|
+
de 1,0 representa 100% de similaridade.
|
861
|
+
* max_discards (``int``): Este parâmetro indica o número máximo de descartes de detectores \
|
862
|
+
em sequência, que tem como objetivo evitar um possível loop infinito caso seja definido \
|
863
|
+
um raio que não seja possível gerar detectores do não-próprio. Defaults to ``1000``.
|
794
864
|
* seed (``int``): Semente para a geração randômica dos valores nos detectores. Defaults to ``None``.
|
865
|
+
* no_label_sample_selection (``str``): Método para a seleção de rótulos para amostras designadas \
|
866
|
+
como não pertencentes por todos os detectores não pertencentes. Tipos de métodos disponíveis:
|
867
|
+
- (``max_average_difference``): Seleciona a classe com a maior diferença média entre os \
|
868
|
+
detectores.
|
869
|
+
- (``max_nearest_difference``): Seleciona a classe com a maior diferença entre o detector \
|
870
|
+
mais próximo e mais distante da amostra.
|
871
|
+
|
795
872
|
"""
|
873
|
+
super().__init__()
|
796
874
|
if N > 0:
|
797
875
|
self.N: int = N
|
798
876
|
else:
|
799
877
|
self.N: int = 100
|
800
878
|
|
801
|
-
if
|
879
|
+
if 0 < aff_thresh < 1:
|
802
880
|
self.aff_thresh: float = aff_thresh
|
803
881
|
else:
|
804
882
|
self.aff_thresh: float = 0.1
|
805
883
|
if max_discards > 0:
|
806
884
|
self.max_discards: int = max_discards
|
807
885
|
else:
|
808
|
-
self.max_discards: int =
|
809
|
-
|
886
|
+
self.max_discards: int = 1000
|
887
|
+
|
810
888
|
if seed is not None and isinstance(seed, int):
|
811
889
|
np.random.seed(seed)
|
812
890
|
self.seed: int = seed
|
813
891
|
else:
|
814
892
|
self.seed = None
|
893
|
+
|
894
|
+
if no_label_sample_selection == 'nearest_difference':
|
895
|
+
self.no_label_sample_selection = 'nearest_difference'
|
896
|
+
else:
|
897
|
+
self.no_label_sample_selection = 'max_average_difference'
|
898
|
+
|
815
899
|
self.classes: npt.NDArray = None
|
816
900
|
self.detectors: npt.NDArray = None
|
817
901
|
|
@@ -832,22 +916,22 @@ class BNSA(Base):
|
|
832
916
|
|
833
917
|
----
|
834
918
|
|
835
|
-
A função ``fit(...)``, realiza o treinamento de acordo com ``X`` e ``y``, usando o método
|
919
|
+
A função ``fit(...)``, realiza o treinamento de acordo com ``X`` e ``y``, usando o método
|
836
920
|
de seleção negativa(``NegativeSelect``).
|
837
921
|
|
838
922
|
Parameters:
|
839
923
|
---
|
840
|
-
* X (``npt.NDArray``): Array de treinamento, contendo as amostras é suas características,
|
841
|
-
|
924
|
+
* X (``npt.NDArray``): Array de treinamento, contendo as amostras é suas características, \
|
925
|
+
[``N amostras`` (linhas)][``N características`` (colunas)].
|
842
926
|
* y (``npt.NDArray``): Array com as classes alvos de ``X`` com [``N amostras`` (linhas)].
|
843
927
|
* verbose (``bool``): Feedback da geração de detectores para o usuário.
|
844
928
|
Returns:
|
845
929
|
---
|
846
930
|
(``self``): Retorna a própria instância.
|
847
931
|
"""
|
848
|
-
super()._check_and_raise_exceptions_fit(X, y,
|
849
|
-
|
850
|
-
# Converte todo o array X para
|
932
|
+
super()._check_and_raise_exceptions_fit(X, y, "BNSA")
|
933
|
+
|
934
|
+
# Converte todo o array X para boolean
|
851
935
|
if X.dtype != bool:
|
852
936
|
X = X.astype(bool)
|
853
937
|
|
@@ -861,7 +945,7 @@ class BNSA(Base):
|
|
861
945
|
if verbose:
|
862
946
|
progress = tqdm(total=int(self.N * (len(self.classes))),
|
863
947
|
bar_format='{desc} ┇{bar}┇ {n}/{total} detectors', postfix='\n')
|
864
|
-
|
948
|
+
|
865
949
|
for _class_ in self.classes:
|
866
950
|
# Inicia o conjunto vazio que conterá os detectores válidos.
|
867
951
|
valid_detectors_set: list = []
|
@@ -869,17 +953,18 @@ class BNSA(Base):
|
|
869
953
|
# Informando em qual classe o algoritmo está para a barra de progresso.
|
870
954
|
if verbose:
|
871
955
|
progress.set_description_str(
|
872
|
-
f
|
956
|
+
f"Generating the detectors for the {_class_} class:")
|
873
957
|
while len(valid_detectors_set) < self.N:
|
874
|
-
|
958
|
+
|
875
959
|
is_valid_detector: bool = True
|
876
960
|
# Gera um vetor candidato a detector aleatoriamente com valores 0 e 1.
|
877
961
|
vector_x = np.random.choice([False, True], size=X.shape[1])
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
962
|
+
# Calcula a distância entre o candidato e as amostras da classe.
|
963
|
+
distances = cdist(np.expand_dims(vector_x, axis=0),
|
964
|
+
X[sample_index[_class_]], metric='hamming')
|
965
|
+
# Verifica se alguma das distâncias está abaixo ou igual ao limiar
|
966
|
+
is_valid_detector = not np.any(distances <= self.aff_thresh)
|
967
|
+
|
883
968
|
# Se o detector for válido, adicione a lista dos válidos.
|
884
969
|
if is_valid_detector:
|
885
970
|
discard_count = 0
|
@@ -889,22 +974,26 @@ class BNSA(Base):
|
|
889
974
|
else:
|
890
975
|
discard_count += 1
|
891
976
|
if discard_count == self.max_discards:
|
892
|
-
raise Exception(
|
893
|
-
|
894
|
-
|
895
|
-
|
977
|
+
raise Exception(
|
978
|
+
"An error has been identified:\n"
|
979
|
+
f"the maximum number of discards of detectors for the {_class_} "
|
980
|
+
"class has been reached.\nIt is recommended to check the defined "
|
981
|
+
"radius and consider reducing its value."
|
982
|
+
)
|
983
|
+
|
896
984
|
# Adicionar detectores, com as classes como chave na dict.
|
897
985
|
list_detectors_by_class[_class_] = valid_detectors_set
|
898
986
|
|
899
987
|
# Informar a finalização da geração dos detectores para as classes.
|
900
988
|
if verbose:
|
901
989
|
progress.set_description(
|
902
|
-
f'\033[92m✔ Non-self detectors for classes ({", ".join(map(str, self.classes))})
|
990
|
+
f'\033[92m✔ Non-self detectors for classes ({", ".join(map(str, self.classes))}) '
|
991
|
+
f'successfully generated\033[0m')
|
903
992
|
# Armazena os detectores encontrados no atributo, para os detectores da classe.
|
904
993
|
self.detectors = list_detectors_by_class
|
905
994
|
return self
|
906
995
|
|
907
|
-
def predict(self, X: npt.NDArray) -> npt.NDArray:
|
996
|
+
def predict(self, X: npt.NDArray) -> Optional[npt.NDArray]:
|
908
997
|
"""
|
909
998
|
Function to perform the prediction of classes based on detectors
|
910
999
|
created after training.
|
@@ -922,88 +1011,111 @@ class BNSA(Base):
|
|
922
1011
|
|
923
1012
|
---
|
924
1013
|
|
925
|
-
Função para efetuar a previsão das classes com base nos detectores
|
1014
|
+
Função para efetuar a previsão das classes com base nos detectores
|
926
1015
|
criados após o treinamento.
|
927
1016
|
|
928
1017
|
Parameters:
|
929
1018
|
---
|
930
|
-
* X (``npt.NDArray``): Array com as amostras de entradas com [``N amostras`` (Linhas)] e
|
1019
|
+
* X (``npt.NDArray``): Array com as amostras de entradas com [``N amostras`` (Linhas)] e
|
931
1020
|
[``N características``(Colunas)]
|
932
1021
|
|
933
1022
|
Returns:
|
934
1023
|
---
|
935
|
-
* C – (``npt.NDArray``): um ndarray de forma ``C`` [``N amostras``],
|
1024
|
+
* C – (``npt.NDArray``): um ndarray de forma ``C`` [``N amostras``],
|
936
1025
|
contendo as classes previstas para ``X``.
|
937
1026
|
* ``None``: Se não existir detectores para a previsão.
|
938
1027
|
"""
|
939
1028
|
# se não houver detectores retorna None.
|
940
1029
|
if self.detectors is None:
|
941
1030
|
return None
|
942
|
-
elif not isinstance(X,
|
1031
|
+
elif not isinstance(X, (np.ndarray, list)):
|
943
1032
|
raise TypeError("X is not an ndarray or list")
|
944
1033
|
elif len(self.detectors[self.classes[0]][0]) != len(X[0]):
|
945
|
-
raise Exception(
|
946
|
-
|
1034
|
+
raise Exception(
|
1035
|
+
"X does not have {} features to make the prediction".format(
|
1036
|
+
len(self.detectors[self.classes[0]][0])
|
1037
|
+
)
|
1038
|
+
)
|
947
1039
|
# Verifica se a matriz X contém apenas amostras binárias. Caso contrário, lança uma exceção.
|
948
1040
|
if not np.isin(X, [0, 1]).all():
|
949
1041
|
raise ValueError(
|
950
|
-
"The array X contains values that are not composed only of 0 and 1."
|
951
|
-
|
952
|
-
|
1042
|
+
"The array X contains values that are not composed only of 0 and 1."
|
1043
|
+
)
|
1044
|
+
|
1045
|
+
# Converte todo o array X para boolean
|
953
1046
|
if X.dtype != bool:
|
954
1047
|
X = X.astype(bool)
|
955
|
-
|
1048
|
+
|
956
1049
|
# Inicia um array vazio.
|
957
|
-
C = np.empty(shape=
|
1050
|
+
C = np.empty(shape=0)
|
958
1051
|
# Para cada linha de amostra em X.
|
959
1052
|
for line in X:
|
960
1053
|
class_found: bool = True
|
961
|
-
# Lista para armazenar as possíveis classes às quais a amostra se adequou ao self na
|
962
|
-
|
1054
|
+
# Lista para armazenar as possíveis classes às quais a amostra se adequou ao self na
|
1055
|
+
# comparação com os detectores non-self.
|
1056
|
+
possible_classes: list = []
|
963
1057
|
for _class_ in self.classes:
|
964
|
-
# Lista para
|
1058
|
+
# Lista para armazenar as taxas de similaridade entre a amostra e os detectores.
|
965
1059
|
similarity_sum: float = 0
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
1060
|
+
|
1061
|
+
# Calcula a distância de Hamming entre a linha e todos os detectores
|
1062
|
+
distances = cdist(np.expand_dims(line, axis=0),
|
1063
|
+
self.detectors[_class_], metric='hamming')
|
1064
|
+
|
1065
|
+
# Verificar se alguma distância está abaixo ou igual ao limiar
|
1066
|
+
if np.any(distances <= self.aff_thresh):
|
1067
|
+
class_found = False
|
1068
|
+
else:
|
1069
|
+
# Somar todas as distâncias
|
1070
|
+
similarity_sum = np.sum(distances)
|
1071
|
+
|
1072
|
+
# Se a amostra passar por todos os detectores de uma classe, adiciona a classe como
|
1073
|
+
# possível previsão e sua media de similaridade.
|
974
1074
|
if class_found:
|
975
1075
|
possible_classes.append([_class_, similarity_sum / self.N])
|
976
|
-
|
1076
|
+
|
977
1077
|
# Se, pertencer a uma ou mais classes, adiciona a classe com a distância média mais distante.
|
978
|
-
if len(possible_classes) > 0
|
979
|
-
C = np.append(
|
1078
|
+
if len(possible_classes) > 0:
|
1079
|
+
C = np.append(
|
1080
|
+
C, [max(possible_classes, key=lambda x: x[1])[0]])
|
980
1081
|
class_found = True
|
981
1082
|
else:
|
982
1083
|
class_found = False
|
983
1084
|
|
984
1085
|
# Se possuir apenas uma classe e não classificar a amostra define a saída como não-própria.
|
985
1086
|
if not class_found and len(self.classes) == 1:
|
986
|
-
C = np.append(C, [
|
987
|
-
# Se
|
1087
|
+
C = np.append(C, ["non-self"])
|
1088
|
+
# Se a classe não puder ser identificada pelos detectores
|
988
1089
|
elif not class_found:
|
989
1090
|
class_differences: dict = {}
|
990
1091
|
for _class_ in self.classes:
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
1092
|
+
# Atribua-a o rotulo a classe com à maior distância em relação ao detector mais próximo.
|
1093
|
+
if self.no_label_sample_selection == 'nearest_difference':
|
1094
|
+
difference_min: float = cdist( np.expand_dims(line, axis=0),
|
1095
|
+
self.detectors[_class_], metric='hamming'
|
1096
|
+
).min()
|
1097
|
+
class_differences[_class_] = difference_min
|
1098
|
+
# Ou com base na maior distância com relação à média da distancias dos detectores
|
1099
|
+
else:
|
1100
|
+
difference_sum: float = cdist( np.expand_dims(line, axis=0),
|
1101
|
+
self.detectors[_class_], metric='hamming'
|
1102
|
+
).sum()
|
1103
|
+
class_differences[_class_] = difference_sum / self.N
|
1104
|
+
|
995
1105
|
C = np.append(C, [max(class_differences, key=class_differences.get)])
|
996
|
-
|
1106
|
+
|
997
1107
|
return C
|
998
1108
|
|
999
1109
|
def __slice_index_list_by_class(self, y: npt.NDArray) -> dict:
|
1000
1110
|
"""
|
1001
|
-
The function ``__slice_index_list_by_class(...)``, separates the indices of the lines according
|
1002
|
-
to loop through the sample array, only in positions where the output is
|
1111
|
+
The function ``__slice_index_list_by_class(...)``, separates the indices of the lines according \
|
1112
|
+
to the output class, to loop through the sample array, only in positions where the output is \
|
1113
|
+
the class being trained.
|
1003
1114
|
|
1004
1115
|
Parameters:
|
1005
1116
|
---
|
1006
|
-
* y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the
|
1117
|
+
* y (npt.NDArray): Receives a ``y``[``N sample``] array with the output classes of the \
|
1118
|
+
``X`` sample array.
|
1007
1119
|
|
1008
1120
|
returns:
|
1009
1121
|
---
|
@@ -1011,12 +1123,14 @@ class BNSA(Base):
|
|
1011
1123
|
|
1012
1124
|
---
|
1013
1125
|
|
1014
|
-
A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe
|
1015
|
-
para percorrer o array de amostra, apenas nas posições que a saída for a classe que
|
1126
|
+
A função ``__slice_index_list_by_class(...)``, separa os índices das linhas conforme a classe \
|
1127
|
+
de saída, para percorrer o array de amostra, apenas nas posições que a saída for a classe que \
|
1128
|
+
está sendo treinada.
|
1016
1129
|
|
1017
1130
|
Parameters:
|
1018
1131
|
---
|
1019
|
-
* y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de
|
1132
|
+
* y (npt.NDArray): Recebe um array ``y``[``N amostra``] com as classes de saída do array \
|
1133
|
+
de amostra ``X``.
|
1020
1134
|
|
1021
1135
|
Returns:
|
1022
1136
|
---
|
@@ -1030,8 +1144,8 @@ class BNSA(Base):
|
|
1030
1144
|
|
1031
1145
|
Details:
|
1032
1146
|
---
|
1033
|
-
This function performs the prediction of X and checks how many elements are equal between vector
|
1034
|
-
This function was added for compatibility with some scikit-learn functions.
|
1147
|
+
This function performs the prediction of X and checks how many elements are equal between vector \
|
1148
|
+
y and y_predicted. This function was added for compatibility with some scikit-learn functions.
|
1035
1149
|
|
1036
1150
|
Parameters:
|
1037
1151
|
-----------
|
@@ -1053,7 +1167,8 @@ class BNSA(Base):
|
|
1053
1167
|
|
1054
1168
|
Details:
|
1055
1169
|
---
|
1056
|
-
Esta função realiza a previsão de X e verifica quantos elementos são iguais entre o vetor y
|
1170
|
+
Esta função realiza a previsão de X e verifica quantos elementos são iguais entre o vetor y \
|
1171
|
+
e y_previsto.
|
1057
1172
|
Essa função foi adicionada para oferecer compatibilidade com algumas funções do scikit-learn.
|
1058
1173
|
|
1059
1174
|
Parameters:
|
@@ -1074,8 +1189,8 @@ class BNSA(Base):
|
|
1074
1189
|
|
1075
1190
|
def get_params(self, deep: bool = True) -> dict:
|
1076
1191
|
return {
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
}
|
1192
|
+
"N": self.N,
|
1193
|
+
"aff_thresh": self.aff_thresh,
|
1194
|
+
"max_discards": self.max_discards,
|
1195
|
+
"seed": self.seed,
|
1196
|
+
}
|