dataeval 0.69.4__py3-none-any.whl → 0.70.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dataeval/__init__.py +8 -8
- dataeval/_internal/datasets.py +235 -131
- dataeval/_internal/detectors/clusterer.py +2 -0
- dataeval/_internal/detectors/drift/base.py +7 -8
- dataeval/_internal/detectors/drift/mmd.py +4 -4
- dataeval/_internal/detectors/duplicates.py +64 -45
- dataeval/_internal/detectors/merged_stats.py +23 -54
- dataeval/_internal/detectors/ood/ae.py +8 -6
- dataeval/_internal/detectors/ood/aegmm.py +6 -4
- dataeval/_internal/detectors/ood/base.py +12 -7
- dataeval/_internal/detectors/ood/llr.py +6 -4
- dataeval/_internal/detectors/ood/vae.py +5 -3
- dataeval/_internal/detectors/ood/vaegmm.py +6 -4
- dataeval/_internal/detectors/outliers.py +137 -63
- dataeval/_internal/interop.py +11 -7
- dataeval/_internal/metrics/balance.py +13 -11
- dataeval/_internal/metrics/ber.py +5 -3
- dataeval/_internal/metrics/coverage.py +4 -0
- dataeval/_internal/metrics/divergence.py +9 -5
- dataeval/_internal/metrics/diversity.py +14 -12
- dataeval/_internal/metrics/parity.py +32 -22
- dataeval/_internal/metrics/stats/base.py +231 -0
- dataeval/_internal/metrics/stats/boxratiostats.py +159 -0
- dataeval/_internal/metrics/stats/datasetstats.py +99 -0
- dataeval/_internal/metrics/stats/dimensionstats.py +113 -0
- dataeval/_internal/metrics/stats/hashstats.py +75 -0
- dataeval/_internal/metrics/stats/labelstats.py +125 -0
- dataeval/_internal/metrics/stats/pixelstats.py +119 -0
- dataeval/_internal/metrics/stats/visualstats.py +124 -0
- dataeval/_internal/metrics/uap.py +8 -4
- dataeval/_internal/metrics/utils.py +30 -15
- dataeval/_internal/models/pytorch/autoencoder.py +5 -5
- dataeval/_internal/models/tensorflow/pixelcnn.py +1 -4
- dataeval/_internal/output.py +3 -18
- dataeval/_internal/utils.py +11 -16
- dataeval/_internal/workflows/sufficiency.py +152 -151
- dataeval/detectors/__init__.py +4 -0
- dataeval/detectors/drift/__init__.py +8 -3
- dataeval/detectors/drift/kernels/__init__.py +4 -0
- dataeval/detectors/drift/updates/__init__.py +4 -0
- dataeval/detectors/linters/__init__.py +15 -4
- dataeval/detectors/ood/__init__.py +14 -2
- dataeval/metrics/__init__.py +5 -0
- dataeval/metrics/bias/__init__.py +13 -4
- dataeval/metrics/estimators/__init__.py +8 -8
- dataeval/metrics/stats/__init__.py +25 -3
- dataeval/utils/__init__.py +16 -3
- dataeval/utils/tensorflow/__init__.py +11 -0
- dataeval/utils/torch/__init__.py +12 -0
- dataeval/utils/torch/datasets/__init__.py +7 -0
- dataeval/workflows/__init__.py +6 -2
- {dataeval-0.69.4.dist-info → dataeval-0.70.1.dist-info}/METADATA +12 -4
- dataeval-0.70.1.dist-info/RECORD +80 -0
- {dataeval-0.69.4.dist-info → dataeval-0.70.1.dist-info}/WHEEL +1 -1
- dataeval/_internal/flags.py +0 -77
- dataeval/_internal/metrics/stats.py +0 -397
- dataeval/flags/__init__.py +0 -3
- dataeval/tensorflow/__init__.py +0 -3
- dataeval/torch/__init__.py +0 -3
- dataeval-0.69.4.dist-info/RECORD +0 -74
- /dataeval/{tensorflow → utils/tensorflow}/loss/__init__.py +0 -0
- /dataeval/{tensorflow → utils/tensorflow}/models/__init__.py +0 -0
- /dataeval/{tensorflow → utils/tensorflow}/recon/__init__.py +0 -0
- /dataeval/{torch → utils/torch}/models/__init__.py +0 -0
- /dataeval/{torch → utils/torch}/trainer/__init__.py +0 -0
- {dataeval-0.69.4.dist-info → dataeval-0.70.1.dist-info}/LICENSE.txt +0 -0
@@ -61,9 +61,9 @@ class AETrainer:
|
|
61
61
|
List[float]
|
62
62
|
A list of average loss values for each epoch.
|
63
63
|
|
64
|
-
|
64
|
+
Note
|
65
65
|
----
|
66
|
-
To replace this function with a custom function, do
|
66
|
+
To replace this function with a custom function, do:
|
67
67
|
AETrainer.train = custom_function
|
68
68
|
"""
|
69
69
|
# Setup training
|
@@ -120,7 +120,7 @@ class AETrainer:
|
|
120
120
|
|
121
121
|
Note
|
122
122
|
----
|
123
|
-
To replace this function with a custom function, do
|
123
|
+
To replace this function with a custom function, do:
|
124
124
|
AETrainer.eval = custom_function
|
125
125
|
"""
|
126
126
|
self.model.eval()
|
@@ -155,8 +155,8 @@ class AETrainer:
|
|
155
155
|
torch.Tensor
|
156
156
|
Data encoded by the model
|
157
157
|
|
158
|
-
|
159
|
-
|
158
|
+
Note
|
159
|
+
----
|
160
160
|
This function should be run after the model has been trained and evaluated.
|
161
161
|
"""
|
162
162
|
self.model.eval()
|
@@ -272,8 +272,6 @@ class PixelCNN(distribution.Distribution):
|
|
272
272
|
The minimum value of the input data.
|
273
273
|
dtype : tensorflow dtype, default tf.float32
|
274
274
|
Data type of the `Distribution`.
|
275
|
-
name : str, default "PixelCNN"
|
276
|
-
The name of the `Distribution`.
|
277
275
|
"""
|
278
276
|
|
279
277
|
def __init__(
|
@@ -293,10 +291,9 @@ class PixelCNN(distribution.Distribution):
|
|
293
291
|
high: int = 255,
|
294
292
|
low: int = 0,
|
295
293
|
dtype=tf.float32,
|
296
|
-
name: str = "PixelCNN",
|
297
294
|
) -> None:
|
298
295
|
parameters = dict(locals())
|
299
|
-
with tf.name_scope(
|
296
|
+
with tf.name_scope("PixelCNN") as name:
|
300
297
|
super().__init__(
|
301
298
|
dtype=dtype,
|
302
299
|
reparameterization_type=reparameterization.NOT_REPARAMETERIZED,
|
dataeval/_internal/output.py
CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3
3
|
import inspect
|
4
4
|
from datetime import datetime, timezone
|
5
5
|
from functools import wraps
|
6
|
+
from typing import Any
|
6
7
|
|
7
8
|
import numpy as np
|
8
9
|
|
@@ -17,10 +18,10 @@ class OutputMetadata:
|
|
17
18
|
_state: dict[str, str]
|
18
19
|
_version: str
|
19
20
|
|
20
|
-
def dict(self) -> dict:
|
21
|
+
def dict(self) -> dict[str, Any]:
|
21
22
|
return {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
|
22
23
|
|
23
|
-
def meta(self) -> dict:
|
24
|
+
def meta(self) -> dict[str, Any]:
|
24
25
|
return {k.removeprefix("_"): v for k, v in self.__dict__.items() if k.startswith("_")}
|
25
26
|
|
26
27
|
|
@@ -67,19 +68,3 @@ def set_metadata(module_name: str = "", state_attr: list[str] | None = None):
|
|
67
68
|
return wrapper
|
68
69
|
|
69
70
|
return decorator
|
70
|
-
|
71
|
-
|
72
|
-
def populate_defaults(d: dict, c: type) -> dict:
|
73
|
-
def default(t):
|
74
|
-
t = (
|
75
|
-
t if isinstance(t, str) else t._name if hasattr(t, "_name") else t.__name__
|
76
|
-
).lower() # py3.9 : _name, py3.10 : __name__
|
77
|
-
if t.startswith("dict"):
|
78
|
-
return {}
|
79
|
-
if t.startswith("list"):
|
80
|
-
return []
|
81
|
-
if t.startswith("ndarray"):
|
82
|
-
return np.array([])
|
83
|
-
raise TypeError("Unrecognized annotation type")
|
84
|
-
|
85
|
-
return {k: d[k] if k in d else default(t) for k, t in c.__annotations__.items()}
|
dataeval/_internal/utils.py
CHANGED
@@ -8,7 +8,7 @@ from torch.utils.data import Dataset
|
|
8
8
|
|
9
9
|
def read_dataset(dataset: Dataset) -> list[list[Any]]:
|
10
10
|
"""
|
11
|
-
Extract information from a dataset at each index into
|
11
|
+
Extract information from a dataset at each index into individual lists of each information position
|
12
12
|
|
13
13
|
Parameters
|
14
14
|
----------
|
@@ -31,36 +31,31 @@ def read_dataset(dataset: Dataset) -> list[list[Any]]:
|
|
31
31
|
Examples
|
32
32
|
--------
|
33
33
|
>>> import numpy as np
|
34
|
-
|
35
|
-
>>> data = np.ones((10, 3, 3))
|
34
|
+
>>> data = np.ones((10, 1, 3, 3))
|
36
35
|
>>> labels = np.ones((10,))
|
37
36
|
>>> class ICDataset:
|
38
37
|
... def __init__(self, data, labels):
|
39
38
|
... self.data = data
|
40
39
|
... self.labels = labels
|
41
|
-
|
40
|
+
...
|
42
41
|
... def __getitem__(self, idx):
|
43
42
|
... return self.data[idx], self.labels[idx]
|
44
43
|
|
45
44
|
>>> ds = ICDataset(data, labels)
|
46
45
|
|
47
46
|
>>> result = read_dataset(ds)
|
48
|
-
>>>
|
49
|
-
|
50
|
-
>>>
|
51
|
-
|
52
|
-
>>>
|
53
|
-
|
47
|
+
>>> len(result) # images and labels
|
48
|
+
2
|
49
|
+
>>> np.asarray(result[0]).shape # images
|
50
|
+
(10, 1, 3, 3)
|
51
|
+
>>> np.asarray(result[1]).shape # labels
|
52
|
+
(10,)
|
54
53
|
"""
|
55
54
|
|
56
|
-
ddict: dict[int, list] = defaultdict(list)
|
55
|
+
ddict: dict[int, list[Any]] = defaultdict(list[Any])
|
57
56
|
|
58
57
|
for data in dataset:
|
59
|
-
|
60
|
-
if not isinstance(data, tuple):
|
61
|
-
data = (data,)
|
62
|
-
|
63
|
-
for i, d in enumerate(data):
|
58
|
+
for i, d in enumerate(data if isinstance(data, tuple) else (data,)):
|
64
59
|
ddict[i].append(d)
|
65
60
|
|
66
61
|
return list(ddict.values())
|
@@ -2,23 +2,26 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import warnings
|
4
4
|
from dataclasses import dataclass
|
5
|
-
from typing import Any, Callable, Sequence, cast
|
5
|
+
from typing import Any, Callable, Iterable, Mapping, Sequence, cast
|
6
6
|
|
7
7
|
import matplotlib.pyplot as plt
|
8
8
|
import numpy as np
|
9
9
|
import torch
|
10
10
|
import torch.nn as nn
|
11
11
|
from matplotlib.figure import Figure
|
12
|
-
from numpy.typing import NDArray
|
12
|
+
from numpy.typing import ArrayLike, NDArray
|
13
13
|
from scipy.optimize import basinhopping
|
14
14
|
from torch.utils.data import Dataset
|
15
15
|
|
16
|
+
from dataeval._internal.interop import as_numpy
|
16
17
|
from dataeval._internal.output import OutputMetadata, set_metadata
|
17
18
|
|
18
19
|
|
19
20
|
@dataclass(frozen=True)
|
20
21
|
class SufficiencyOutput(OutputMetadata):
|
21
22
|
"""
|
23
|
+
Output class for :class:`Sufficiency` workflow
|
24
|
+
|
22
25
|
Attributes
|
23
26
|
----------
|
24
27
|
steps : NDArray
|
@@ -42,6 +45,130 @@ class SufficiencyOutput(OutputMetadata):
|
|
42
45
|
if c != c_v:
|
43
46
|
raise ValueError(f"{m} does not contain the expected number ({c}) of data points.")
|
44
47
|
|
48
|
+
@set_metadata("dataeval.workflows.SufficiencyOutput")
|
49
|
+
def project(
|
50
|
+
self,
|
51
|
+
projection: int | Iterable[int],
|
52
|
+
) -> SufficiencyOutput:
|
53
|
+
"""Projects the measures for each value of X
|
54
|
+
|
55
|
+
Parameters
|
56
|
+
----------
|
57
|
+
projection : int | Iterable[int]
|
58
|
+
Step or steps to project
|
59
|
+
|
60
|
+
Returns
|
61
|
+
-------
|
62
|
+
SufficiencyOutput
|
63
|
+
Dataclass containing the projected measures per projection
|
64
|
+
|
65
|
+
Raises
|
66
|
+
------
|
67
|
+
ValueError
|
68
|
+
If the length of data points in the measures do not match
|
69
|
+
If `projection` is not numerical
|
70
|
+
"""
|
71
|
+
projection = np.asarray(list(projection) if isinstance(projection, Iterable) else [projection])
|
72
|
+
|
73
|
+
if not np.issubdtype(projection.dtype, np.number):
|
74
|
+
raise ValueError("'projection' must consist of numerical values")
|
75
|
+
|
76
|
+
output = {}
|
77
|
+
for name, measures in self.measures.items():
|
78
|
+
if measures.ndim > 1:
|
79
|
+
result = []
|
80
|
+
for i in range(len(measures)):
|
81
|
+
projected = project_steps(self.params[name][i], projection)
|
82
|
+
result.append(projected)
|
83
|
+
output[name] = np.array(result)
|
84
|
+
else:
|
85
|
+
output[name] = project_steps(self.params[name], projection)
|
86
|
+
return SufficiencyOutput(projection, self.params, output)
|
87
|
+
|
88
|
+
def plot(self, class_names: Sequence[str] | None = None) -> list[Figure]:
|
89
|
+
"""Plotting function for data sufficiency tasks
|
90
|
+
|
91
|
+
Parameters
|
92
|
+
----------
|
93
|
+
class_names : Sequence[str] | None, default None
|
94
|
+
List of class names
|
95
|
+
|
96
|
+
Returns
|
97
|
+
-------
|
98
|
+
list[plt.Figure]
|
99
|
+
List of Figures for each measure
|
100
|
+
|
101
|
+
Raises
|
102
|
+
------
|
103
|
+
ValueError
|
104
|
+
If the length of data points in the measures do not match
|
105
|
+
"""
|
106
|
+
# Extrapolation parameters
|
107
|
+
last_X = self.steps[-1]
|
108
|
+
geomshape = (0.01 * last_X, last_X * 4, len(self.steps))
|
109
|
+
extrapolated = np.geomspace(*geomshape).astype(np.int64)
|
110
|
+
|
111
|
+
# Stores all plots
|
112
|
+
plots = []
|
113
|
+
|
114
|
+
# Create a plot for each measure on one figure
|
115
|
+
for name, measures in self.measures.items():
|
116
|
+
if measures.ndim > 1:
|
117
|
+
if class_names is not None and len(measures) != len(class_names):
|
118
|
+
raise IndexError("Class name count does not align with measures")
|
119
|
+
for i, measure in enumerate(measures):
|
120
|
+
class_name = str(i) if class_names is None else class_names[i]
|
121
|
+
fig = plot_measure(
|
122
|
+
f"{name}_{class_name}",
|
123
|
+
self.steps,
|
124
|
+
measure,
|
125
|
+
self.params[name][i],
|
126
|
+
extrapolated,
|
127
|
+
)
|
128
|
+
plots.append(fig)
|
129
|
+
|
130
|
+
else:
|
131
|
+
fig = plot_measure(name, self.steps, measures, self.params[name], extrapolated)
|
132
|
+
plots.append(fig)
|
133
|
+
|
134
|
+
return plots
|
135
|
+
|
136
|
+
def inv_project(self, targets: Mapping[str, ArrayLike]) -> dict[str, NDArray[np.float64]]:
|
137
|
+
"""
|
138
|
+
Calculate training samples needed to achieve target model metric values.
|
139
|
+
|
140
|
+
Parameters
|
141
|
+
----------
|
142
|
+
targets : Mapping[str, ArrayLike]
|
143
|
+
Mapping of target metric scores (from 0.0 to 1.0) that we want
|
144
|
+
to achieve, where the key is the name of the metric.
|
145
|
+
|
146
|
+
Returns
|
147
|
+
-------
|
148
|
+
dict[str, NDArray]
|
149
|
+
List of the number of training samples needed to achieve each
|
150
|
+
corresponding entry in targets
|
151
|
+
"""
|
152
|
+
|
153
|
+
projection = {}
|
154
|
+
|
155
|
+
for name, target in targets.items():
|
156
|
+
tarray = as_numpy(target)
|
157
|
+
if name not in self.measures:
|
158
|
+
continue
|
159
|
+
|
160
|
+
measure = self.measures[name]
|
161
|
+
if measure.ndim > 1:
|
162
|
+
projection[name] = np.zeros((len(measure), len(tarray)))
|
163
|
+
for i in range(len(measure)):
|
164
|
+
projection[name][i] = inv_project_steps(
|
165
|
+
self.params[name][i], tarray[i] if tarray.ndim == measure.ndim else tarray
|
166
|
+
)
|
167
|
+
else:
|
168
|
+
projection[name] = inv_project_steps(self.params[name], tarray)
|
169
|
+
|
170
|
+
return projection
|
171
|
+
|
45
172
|
|
46
173
|
def f_out(n_i: NDArray, x: NDArray) -> NDArray:
|
47
174
|
"""
|
@@ -256,18 +383,18 @@ class Sufficiency:
|
|
256
383
|
Function which takes a model (torch.nn.Module), a dataset
|
257
384
|
(torch.utils.data.Dataset), indices to train on and executes model
|
258
385
|
training against the data.
|
259
|
-
eval_fn : Callable[[nn.Module, Dataset],
|
386
|
+
eval_fn : Callable[[nn.Module, Dataset], Mapping[str, float | ArrayLike]]
|
260
387
|
Function which takes a model (torch.nn.Module), a dataset
|
261
388
|
(torch.utils.data.Dataset) and returns a dictionary of metric
|
262
|
-
values (
|
389
|
+
values (Mapping[str, float]) which is used to assess model performance
|
263
390
|
given the model and data.
|
264
391
|
runs : int, default 1
|
265
392
|
Number of models to run over all subsets
|
266
393
|
substeps : int, default 5
|
267
394
|
Total number of dataset partitions that each model will train on
|
268
|
-
train_kwargs :
|
395
|
+
train_kwargs : Mapping | None, default None
|
269
396
|
Additional arguments required for custom training function
|
270
|
-
eval_kwargs :
|
397
|
+
eval_kwargs : Mapping | None, default None
|
271
398
|
Additional arguments required for custom evaluation function
|
272
399
|
"""
|
273
400
|
|
@@ -277,11 +404,11 @@ class Sufficiency:
|
|
277
404
|
train_ds: Dataset,
|
278
405
|
test_ds: Dataset,
|
279
406
|
train_fn: Callable[[nn.Module, Dataset, Sequence[int]], None],
|
280
|
-
eval_fn: Callable[[nn.Module, Dataset],
|
407
|
+
eval_fn: Callable[[nn.Module, Dataset], Mapping[str, float] | Mapping[str, ArrayLike]],
|
281
408
|
runs: int = 1,
|
282
409
|
substeps: int = 5,
|
283
|
-
train_kwargs:
|
284
|
-
eval_kwargs:
|
410
|
+
train_kwargs: Mapping[str, Any] | None = None,
|
411
|
+
eval_kwargs: Mapping[str, Any] | None = None,
|
285
412
|
):
|
286
413
|
self.model = model
|
287
414
|
self.train_ds = train_ds
|
@@ -324,42 +451,42 @@ class Sufficiency:
|
|
324
451
|
@property
|
325
452
|
def eval_fn(
|
326
453
|
self,
|
327
|
-
) -> Callable[[nn.Module, Dataset], dict[str, float] |
|
454
|
+
) -> Callable[[nn.Module, Dataset], dict[str, float] | Mapping[str, ArrayLike]]:
|
328
455
|
return self._eval_fn
|
329
456
|
|
330
457
|
@eval_fn.setter
|
331
458
|
def eval_fn(
|
332
459
|
self,
|
333
|
-
value: Callable[[nn.Module, Dataset], dict[str, float] |
|
460
|
+
value: Callable[[nn.Module, Dataset], dict[str, float] | Mapping[str, ArrayLike]],
|
334
461
|
):
|
335
462
|
if not callable(value):
|
336
463
|
raise TypeError("Must provide a callable for eval_fn.")
|
337
464
|
self._eval_fn = value
|
338
465
|
|
339
466
|
@property
|
340
|
-
def train_kwargs(self) ->
|
467
|
+
def train_kwargs(self) -> Mapping[str, Any]:
|
341
468
|
return self._train_kwargs
|
342
469
|
|
343
470
|
@train_kwargs.setter
|
344
|
-
def train_kwargs(self, value:
|
471
|
+
def train_kwargs(self, value: Mapping[str, Any] | None):
|
345
472
|
self._train_kwargs = {} if value is None else value
|
346
473
|
|
347
474
|
@property
|
348
|
-
def eval_kwargs(self) ->
|
475
|
+
def eval_kwargs(self) -> Mapping[str, Any]:
|
349
476
|
return self._eval_kwargs
|
350
477
|
|
351
478
|
@eval_kwargs.setter
|
352
|
-
def eval_kwargs(self, value:
|
479
|
+
def eval_kwargs(self, value: Mapping[str, Any] | None):
|
353
480
|
self._eval_kwargs = {} if value is None else value
|
354
481
|
|
355
482
|
@set_metadata("dataeval.workflows", ["runs", "substeps"])
|
356
|
-
def evaluate(self, eval_at:
|
483
|
+
def evaluate(self, eval_at: int | Iterable[int] | None = None, niter: int = 1000) -> SufficiencyOutput:
|
357
484
|
"""
|
358
485
|
Creates data indices, trains models, and returns plotting data
|
359
486
|
|
360
487
|
Parameters
|
361
488
|
----------
|
362
|
-
eval_at :
|
489
|
+
eval_at : int | Iterable[int] | None, default None
|
363
490
|
Specify this to collect accuracies over a specific set of dataset lengths, rather
|
364
491
|
than letting Sufficiency internally create the lengths to evaluate at.
|
365
492
|
niter : int, default 1000
|
@@ -370,6 +497,11 @@ class Sufficiency:
|
|
370
497
|
SufficiencyOutput
|
371
498
|
Dataclass containing the average of each measure per substep
|
372
499
|
|
500
|
+
Raises
|
501
|
+
------
|
502
|
+
ValueError
|
503
|
+
If `eval_at` is not numerical
|
504
|
+
|
373
505
|
Examples
|
374
506
|
--------
|
375
507
|
>>> suff = Sufficiency(
|
@@ -379,7 +511,9 @@ class Sufficiency:
|
|
379
511
|
SufficiencyOutput(steps=array([ 1, 3, 10, 31, 100], dtype=uint32), params={'test': array([ 0., 42., 0.])}, measures={'test': array([1., 1., 1., 1., 1.])})
|
380
512
|
""" # noqa: E501
|
381
513
|
if eval_at is not None:
|
382
|
-
ranges = eval_at
|
514
|
+
ranges = np.asarray(list(eval_at) if isinstance(eval_at, Iterable) else [eval_at])
|
515
|
+
if not np.issubdtype(ranges.dtype, np.number):
|
516
|
+
raise ValueError("'eval_at' must consist of numerical values")
|
383
517
|
else:
|
384
518
|
geomshape = (
|
385
519
|
0.01 * self._length,
|
@@ -421,136 +555,3 @@ class Sufficiency:
|
|
421
555
|
measures = {k: (v / self.runs).T for k, v in measures.items()}
|
422
556
|
params_output = get_curve_params(measures, ranges, niter)
|
423
557
|
return SufficiencyOutput(ranges, params_output, measures)
|
424
|
-
|
425
|
-
@classmethod
|
426
|
-
def project(
|
427
|
-
cls,
|
428
|
-
data: SufficiencyOutput,
|
429
|
-
projection: int | Sequence[int] | NDArray[np.uint],
|
430
|
-
) -> SufficiencyOutput:
|
431
|
-
"""Projects the measures for each value of X
|
432
|
-
|
433
|
-
Parameters
|
434
|
-
----------
|
435
|
-
data : SufficiencyOutput
|
436
|
-
Dataclass containing the average of each measure per substep
|
437
|
-
projection : int | Sequence[int] | NDArray[np.uint]
|
438
|
-
Step or steps to project
|
439
|
-
|
440
|
-
Returns
|
441
|
-
-------
|
442
|
-
SufficiencyOutput
|
443
|
-
Dataclass containing the projected measures per projection
|
444
|
-
|
445
|
-
Raises
|
446
|
-
------
|
447
|
-
ValueError
|
448
|
-
If the length of data points in the measures do not match
|
449
|
-
If the steps are not int, Sequence[int] or an ndarray
|
450
|
-
"""
|
451
|
-
projection = [projection] if isinstance(projection, int) else projection
|
452
|
-
projection = np.array(projection) if isinstance(projection, Sequence) else projection
|
453
|
-
if not isinstance(projection, np.ndarray):
|
454
|
-
raise ValueError("'steps' must be an int, Sequence[int] or ndarray")
|
455
|
-
|
456
|
-
output = {}
|
457
|
-
for name, measures in data.measures.items():
|
458
|
-
if measures.ndim > 1:
|
459
|
-
result = []
|
460
|
-
for i in range(len(measures)):
|
461
|
-
projected = project_steps(data.params[name][i], projection)
|
462
|
-
result.append(projected)
|
463
|
-
output[name] = np.array(result)
|
464
|
-
else:
|
465
|
-
output[name] = project_steps(data.params[name], projection)
|
466
|
-
return SufficiencyOutput(projection, data.params, output)
|
467
|
-
|
468
|
-
@classmethod
|
469
|
-
def plot(cls, data: SufficiencyOutput, class_names: Sequence[str] | None = None) -> list[Figure]:
|
470
|
-
"""Plotting function for data sufficiency tasks
|
471
|
-
|
472
|
-
Parameters
|
473
|
-
----------
|
474
|
-
data : SufficiencyOutput
|
475
|
-
Dataclass containing the average of each measure per substep
|
476
|
-
class_names : Sequence[str] | None, default None
|
477
|
-
List of class names
|
478
|
-
|
479
|
-
Returns
|
480
|
-
-------
|
481
|
-
List[plt.Figure]
|
482
|
-
List of Figures for each measure
|
483
|
-
|
484
|
-
Raises
|
485
|
-
------
|
486
|
-
ValueError
|
487
|
-
If the length of data points in the measures do not match
|
488
|
-
"""
|
489
|
-
# Extrapolation parameters
|
490
|
-
last_X = data.steps[-1]
|
491
|
-
geomshape = (0.01 * last_X, last_X * 4, len(data.steps))
|
492
|
-
extrapolated = np.geomspace(*geomshape).astype(np.int64)
|
493
|
-
|
494
|
-
# Stores all plots
|
495
|
-
plots = []
|
496
|
-
|
497
|
-
# Create a plot for each measure on one figure
|
498
|
-
for name, measures in data.measures.items():
|
499
|
-
if measures.ndim > 1:
|
500
|
-
if class_names is not None and len(measures) != len(class_names):
|
501
|
-
raise IndexError("Class name count does not align with measures")
|
502
|
-
for i, measure in enumerate(measures):
|
503
|
-
class_name = str(i) if class_names is None else class_names[i]
|
504
|
-
fig = plot_measure(
|
505
|
-
f"{name}_{class_name}",
|
506
|
-
data.steps,
|
507
|
-
measure,
|
508
|
-
data.params[name][i],
|
509
|
-
extrapolated,
|
510
|
-
)
|
511
|
-
plots.append(fig)
|
512
|
-
|
513
|
-
else:
|
514
|
-
fig = plot_measure(name, data.steps, measures, data.params[name], extrapolated)
|
515
|
-
plots.append(fig)
|
516
|
-
|
517
|
-
return plots
|
518
|
-
|
519
|
-
@classmethod
|
520
|
-
def inv_project(cls, targets: dict[str, NDArray], data: SufficiencyOutput) -> dict[str, NDArray]:
|
521
|
-
"""
|
522
|
-
Calculate training samples needed to achieve target model metric values.
|
523
|
-
|
524
|
-
Parameters
|
525
|
-
----------
|
526
|
-
targets : Dict[str, NDArray]
|
527
|
-
Dictionary of target metric scores (from 0.0 to 1.0) that we want
|
528
|
-
to achieve, where the key is the name of the metric.
|
529
|
-
|
530
|
-
data : SufficiencyOutput
|
531
|
-
Dataclass containing the average of each measure per substep
|
532
|
-
|
533
|
-
Returns
|
534
|
-
-------
|
535
|
-
Dict[str, NDArray]
|
536
|
-
List of the number of training samples needed to achieve each
|
537
|
-
corresponding entry in targets
|
538
|
-
"""
|
539
|
-
|
540
|
-
projection = {}
|
541
|
-
|
542
|
-
for name, target in targets.items():
|
543
|
-
if name not in data.measures:
|
544
|
-
continue
|
545
|
-
|
546
|
-
measure = data.measures[name]
|
547
|
-
if measure.ndim > 1:
|
548
|
-
projection[name] = np.zeros((len(measure), len(target)))
|
549
|
-
for i in range(len(measure)):
|
550
|
-
projection[name][i] = inv_project_steps(
|
551
|
-
data.params[name][i], target[i] if target.ndim == measure.ndim else target
|
552
|
-
)
|
553
|
-
else:
|
554
|
-
projection[name] = inv_project_steps(data.params[name], target)
|
555
|
-
|
556
|
-
return projection
|
dataeval/detectors/__init__.py
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
+
"""
|
2
|
+
Drift detectors identify if the statistical properties of the data has changed.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from dataeval import _IS_TORCH_AVAILABLE
|
6
|
+
from dataeval._internal.detectors.drift.base import DriftOutput
|
2
7
|
from dataeval._internal.detectors.drift.cvm import DriftCVM
|
3
8
|
from dataeval._internal.detectors.drift.ks import DriftKS
|
4
9
|
|
5
10
|
from . import updates
|
6
11
|
|
7
|
-
__all__ = ["DriftCVM", "DriftKS", "updates"]
|
12
|
+
__all__ = ["DriftCVM", "DriftKS", "DriftOutput", "updates"]
|
8
13
|
|
9
14
|
if _IS_TORCH_AVAILABLE: # pragma: no cover
|
10
|
-
from dataeval._internal.detectors.drift.mmd import DriftMMD
|
15
|
+
from dataeval._internal.detectors.drift.mmd import DriftMMD, DriftMMDOutput
|
11
16
|
from dataeval._internal.detectors.drift.torch import preprocess_drift
|
12
17
|
from dataeval._internal.detectors.drift.uncertainty import DriftUncertainty
|
13
18
|
|
14
19
|
from . import kernels
|
15
20
|
|
16
|
-
__all__ += ["DriftMMD", "DriftUncertainty", "kernels", "preprocess_drift"]
|
21
|
+
__all__ += ["DriftMMD", "DriftMMDOutput", "DriftUncertainty", "kernels", "preprocess_drift"]
|
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
Update strategies inform how the drift detector classes update the reference data when monitoring for drift.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from dataeval._internal.detectors.drift.base import LastSeenUpdate, ReservoirSamplingUpdate
|
2
6
|
|
3
7
|
__all__ = ["LastSeenUpdate", "ReservoirSamplingUpdate"]
|
@@ -1,5 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
"""
|
2
|
+
Linters help identify potential issues in training and test data and are an important aspect of data cleaning.
|
3
|
+
"""
|
4
4
|
|
5
|
-
|
5
|
+
from dataeval._internal.detectors.clusterer import Clusterer, ClustererOutput
|
6
|
+
from dataeval._internal.detectors.duplicates import Duplicates, DuplicatesOutput
|
7
|
+
from dataeval._internal.detectors.outliers import Outliers, OutliersOutput
|
8
|
+
|
9
|
+
__all__ = [
|
10
|
+
"Clusterer",
|
11
|
+
"ClustererOutput",
|
12
|
+
"Duplicates",
|
13
|
+
"DuplicatesOutput",
|
14
|
+
"Outliers",
|
15
|
+
"OutliersOutput",
|
16
|
+
]
|
@@ -1,11 +1,23 @@
|
|
1
|
+
"""
|
2
|
+
Out-of-distribution detectors identify data that is different from the data used to train a particular model.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from dataeval import _IS_TENSORFLOW_AVAILABLE
|
2
6
|
|
3
7
|
if _IS_TENSORFLOW_AVAILABLE: # pragma: no cover
|
4
8
|
from dataeval._internal.detectors.ood.ae import OOD_AE
|
5
9
|
from dataeval._internal.detectors.ood.aegmm import OOD_AEGMM
|
6
|
-
from dataeval._internal.detectors.ood.base import OODOutput,
|
10
|
+
from dataeval._internal.detectors.ood.base import OODOutput, OODScoreOutput
|
7
11
|
from dataeval._internal.detectors.ood.llr import OOD_LLR
|
8
12
|
from dataeval._internal.detectors.ood.vae import OOD_VAE
|
9
13
|
from dataeval._internal.detectors.ood.vaegmm import OOD_VAEGMM
|
10
14
|
|
11
|
-
__all__ = [
|
15
|
+
__all__ = [
|
16
|
+
"OOD_AE",
|
17
|
+
"OOD_AEGMM",
|
18
|
+
"OOD_LLR",
|
19
|
+
"OOD_VAE",
|
20
|
+
"OOD_VAEGMM",
|
21
|
+
"OODOutput",
|
22
|
+
"OODScoreOutput",
|
23
|
+
]
|
dataeval/metrics/__init__.py
CHANGED