valor-lite 0.33.13__py3-none-any.whl → 0.33.14__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.

Potentially problematic release.


This version of valor-lite might be problematic. Click here for more details.

@@ -0,0 +1,505 @@
1
+ from collections import defaultdict
2
+
3
+ import numpy as np
4
+ from numpy.typing import NDArray
5
+ from valor_lite.object_detection.metric import Metric, MetricType
6
+
7
+
8
+ def unpack_precision_recall_into_metric_lists(
9
+ results: tuple[
10
+ tuple[
11
+ NDArray[np.float64],
12
+ NDArray[np.float64],
13
+ NDArray[np.float64],
14
+ float,
15
+ ],
16
+ tuple[
17
+ NDArray[np.float64],
18
+ NDArray[np.float64],
19
+ NDArray[np.float64],
20
+ float,
21
+ ],
22
+ NDArray[np.float64],
23
+ NDArray[np.float64],
24
+ NDArray[np.float64],
25
+ ],
26
+ iou_thresholds: list[float],
27
+ score_thresholds: list[float],
28
+ index_to_label: dict[int, str],
29
+ label_metadata: NDArray[np.int32],
30
+ ):
31
+ (
32
+ (
33
+ average_precision,
34
+ mean_average_precision,
35
+ average_precision_average_over_ious,
36
+ mean_average_precision_average_over_ious,
37
+ ),
38
+ (
39
+ average_recall,
40
+ mean_average_recall,
41
+ average_recall_averaged_over_scores,
42
+ mean_average_recall_averaged_over_scores,
43
+ ),
44
+ accuracy,
45
+ precision_recall,
46
+ pr_curves,
47
+ ) = results
48
+
49
+ metrics = defaultdict(list)
50
+
51
+ metrics[MetricType.AP] = [
52
+ Metric.average_precision(
53
+ value=float(average_precision[iou_idx][label_idx]),
54
+ iou_threshold=iou_threshold,
55
+ label=label,
56
+ )
57
+ for iou_idx, iou_threshold in enumerate(iou_thresholds)
58
+ for label_idx, label in index_to_label.items()
59
+ if int(label_metadata[label_idx, 0]) > 0
60
+ ]
61
+
62
+ metrics[MetricType.mAP] = [
63
+ Metric.mean_average_precision(
64
+ value=float(mean_average_precision[iou_idx]),
65
+ iou_threshold=iou_threshold,
66
+ )
67
+ for iou_idx, iou_threshold in enumerate(iou_thresholds)
68
+ ]
69
+
70
+ metrics[MetricType.APAveragedOverIOUs] = [
71
+ Metric.average_precision_averaged_over_IOUs(
72
+ value=float(average_precision_average_over_ious[label_idx]),
73
+ iou_thresholds=iou_thresholds,
74
+ label=label,
75
+ )
76
+ for label_idx, label in index_to_label.items()
77
+ if int(label_metadata[label_idx, 0]) > 0
78
+ ]
79
+
80
+ metrics[MetricType.mAPAveragedOverIOUs] = [
81
+ Metric.mean_average_precision_averaged_over_IOUs(
82
+ value=float(mean_average_precision_average_over_ious),
83
+ iou_thresholds=iou_thresholds,
84
+ )
85
+ ]
86
+
87
+ metrics[MetricType.AR] = [
88
+ Metric.average_recall(
89
+ value=float(average_recall[score_idx, label_idx]),
90
+ iou_thresholds=iou_thresholds,
91
+ score_threshold=score_threshold,
92
+ label=label,
93
+ )
94
+ for score_idx, score_threshold in enumerate(score_thresholds)
95
+ for label_idx, label in index_to_label.items()
96
+ if int(label_metadata[label_idx, 0]) > 0
97
+ ]
98
+
99
+ metrics[MetricType.mAR] = [
100
+ Metric.mean_average_recall(
101
+ value=float(mean_average_recall[score_idx]),
102
+ iou_thresholds=iou_thresholds,
103
+ score_threshold=score_threshold,
104
+ )
105
+ for score_idx, score_threshold in enumerate(score_thresholds)
106
+ ]
107
+
108
+ metrics[MetricType.ARAveragedOverScores] = [
109
+ Metric.average_recall_averaged_over_scores(
110
+ value=float(average_recall_averaged_over_scores[label_idx]),
111
+ score_thresholds=score_thresholds,
112
+ iou_thresholds=iou_thresholds,
113
+ label=label,
114
+ )
115
+ for label_idx, label in index_to_label.items()
116
+ if int(label_metadata[label_idx, 0]) > 0
117
+ ]
118
+
119
+ metrics[MetricType.mARAveragedOverScores] = [
120
+ Metric.mean_average_recall_averaged_over_scores(
121
+ value=float(mean_average_recall_averaged_over_scores),
122
+ score_thresholds=score_thresholds,
123
+ iou_thresholds=iou_thresholds,
124
+ )
125
+ ]
126
+
127
+ metrics[MetricType.Accuracy] = [
128
+ Metric.accuracy(
129
+ value=float(accuracy[iou_idx, score_idx]),
130
+ iou_threshold=iou_threshold,
131
+ score_threshold=score_threshold,
132
+ )
133
+ for iou_idx, iou_threshold in enumerate(iou_thresholds)
134
+ for score_idx, score_threshold in enumerate(score_thresholds)
135
+ ]
136
+
137
+ metrics[MetricType.PrecisionRecallCurve] = [
138
+ Metric.precision_recall_curve(
139
+ precisions=pr_curves[iou_idx, label_idx, :, 0]
140
+ .astype(float)
141
+ .tolist(),
142
+ scores=pr_curves[iou_idx, label_idx, :, 1].astype(float).tolist(),
143
+ iou_threshold=iou_threshold,
144
+ label=label,
145
+ )
146
+ for iou_idx, iou_threshold in enumerate(iou_thresholds)
147
+ for label_idx, label in index_to_label.items()
148
+ if label_metadata[label_idx, 0] > 0
149
+ ]
150
+
151
+ for label_idx, label in index_to_label.items():
152
+
153
+ if label_metadata[label_idx, 0] == 0:
154
+ continue
155
+
156
+ for score_idx, score_threshold in enumerate(score_thresholds):
157
+ for iou_idx, iou_threshold in enumerate(iou_thresholds):
158
+
159
+ row = precision_recall[iou_idx, score_idx, label_idx, :]
160
+ kwargs = {
161
+ "label": label,
162
+ "iou_threshold": iou_threshold,
163
+ "score_threshold": score_threshold,
164
+ }
165
+ metrics[MetricType.Counts].append(
166
+ Metric.counts(
167
+ tp=int(row[0]),
168
+ fp=int(row[1]),
169
+ fn=int(row[2]),
170
+ **kwargs,
171
+ )
172
+ )
173
+
174
+ metrics[MetricType.Precision].append(
175
+ Metric.precision(
176
+ value=float(row[3]),
177
+ **kwargs,
178
+ )
179
+ )
180
+ metrics[MetricType.Recall].append(
181
+ Metric.recall(
182
+ value=float(row[4]),
183
+ **kwargs,
184
+ )
185
+ )
186
+ metrics[MetricType.F1].append(
187
+ Metric.f1_score(
188
+ value=float(row[5]),
189
+ **kwargs,
190
+ )
191
+ )
192
+
193
+ return metrics
194
+
195
+
196
+ def _convert_example_to_dict(box: NDArray[np.float16]) -> dict[str, float]:
197
+ """
198
+ Converts a cached bounding box example to dictionary format.
199
+ """
200
+ return {
201
+ "xmin": float(box[0]),
202
+ "xmax": float(box[1]),
203
+ "ymin": float(box[2]),
204
+ "ymax": float(box[3]),
205
+ }
206
+
207
+
208
+ def _unpack_confusion_matrix_value(
209
+ confusion_matrix: NDArray[np.float64],
210
+ number_of_labels: int,
211
+ number_of_examples: int,
212
+ index_to_uid: dict[int, str],
213
+ index_to_label: dict[int, str],
214
+ groundtruth_examples: dict[int, NDArray[np.float16]],
215
+ prediction_examples: dict[int, NDArray[np.float16]],
216
+ ) -> dict[
217
+ str,
218
+ dict[
219
+ str,
220
+ dict[
221
+ str,
222
+ int
223
+ | list[
224
+ dict[
225
+ str,
226
+ str | dict[str, float] | float,
227
+ ]
228
+ ],
229
+ ],
230
+ ],
231
+ ]:
232
+ """
233
+ Unpacks a numpy array of confusion matrix counts and examples.
234
+ """
235
+
236
+ datum_idx = lambda gt_label_idx, pd_label_idx, example_idx: int( # noqa: E731 - lambda fn
237
+ confusion_matrix[
238
+ gt_label_idx,
239
+ pd_label_idx,
240
+ example_idx * 4 + 1,
241
+ ]
242
+ )
243
+
244
+ groundtruth_idx = lambda gt_label_idx, pd_label_idx, example_idx: int( # noqa: E731 - lambda fn
245
+ confusion_matrix[
246
+ gt_label_idx,
247
+ pd_label_idx,
248
+ example_idx * 4 + 2,
249
+ ]
250
+ )
251
+
252
+ prediction_idx = lambda gt_label_idx, pd_label_idx, example_idx: int( # noqa: E731 - lambda fn
253
+ confusion_matrix[
254
+ gt_label_idx,
255
+ pd_label_idx,
256
+ example_idx * 4 + 3,
257
+ ]
258
+ )
259
+
260
+ score_idx = lambda gt_label_idx, pd_label_idx, example_idx: float( # noqa: E731 - lambda fn
261
+ confusion_matrix[
262
+ gt_label_idx,
263
+ pd_label_idx,
264
+ example_idx * 4 + 4,
265
+ ]
266
+ )
267
+
268
+ return {
269
+ index_to_label[gt_label_idx]: {
270
+ index_to_label[pd_label_idx]: {
271
+ "count": max(
272
+ int(confusion_matrix[gt_label_idx, pd_label_idx, 0]),
273
+ 0,
274
+ ),
275
+ "examples": [
276
+ {
277
+ "datum": index_to_uid[
278
+ datum_idx(gt_label_idx, pd_label_idx, example_idx)
279
+ ],
280
+ "groundtruth": _convert_example_to_dict(
281
+ groundtruth_examples[
282
+ datum_idx(
283
+ gt_label_idx,
284
+ pd_label_idx,
285
+ example_idx,
286
+ )
287
+ ][
288
+ groundtruth_idx(
289
+ gt_label_idx,
290
+ pd_label_idx,
291
+ example_idx,
292
+ )
293
+ ]
294
+ ),
295
+ "prediction": _convert_example_to_dict(
296
+ prediction_examples[
297
+ datum_idx(
298
+ gt_label_idx,
299
+ pd_label_idx,
300
+ example_idx,
301
+ )
302
+ ][
303
+ prediction_idx(
304
+ gt_label_idx,
305
+ pd_label_idx,
306
+ example_idx,
307
+ )
308
+ ]
309
+ ),
310
+ "score": score_idx(
311
+ gt_label_idx, pd_label_idx, example_idx
312
+ ),
313
+ }
314
+ for example_idx in range(number_of_examples)
315
+ if datum_idx(gt_label_idx, pd_label_idx, example_idx) >= 0
316
+ ],
317
+ }
318
+ for pd_label_idx in range(number_of_labels)
319
+ }
320
+ for gt_label_idx in range(number_of_labels)
321
+ }
322
+
323
+
324
+ def _unpack_hallucinations_value(
325
+ hallucinations: NDArray[np.float64],
326
+ number_of_labels: int,
327
+ number_of_examples: int,
328
+ index_to_uid: dict[int, str],
329
+ index_to_label: dict[int, str],
330
+ prediction_examples: dict[int, NDArray[np.float16]],
331
+ ) -> dict[
332
+ str,
333
+ dict[
334
+ str,
335
+ int | list[dict[str, str | float | dict[str, float]]],
336
+ ],
337
+ ]:
338
+ """
339
+ Unpacks a numpy array of hallucination counts and examples.
340
+ """
341
+
342
+ datum_idx = (
343
+ lambda pd_label_idx, example_idx: int( # noqa: E731 - lambda fn
344
+ hallucinations[
345
+ pd_label_idx,
346
+ example_idx * 3 + 1,
347
+ ]
348
+ )
349
+ )
350
+
351
+ prediction_idx = (
352
+ lambda pd_label_idx, example_idx: int( # noqa: E731 - lambda fn
353
+ hallucinations[
354
+ pd_label_idx,
355
+ example_idx * 3 + 2,
356
+ ]
357
+ )
358
+ )
359
+
360
+ score_idx = (
361
+ lambda pd_label_idx, example_idx: float( # noqa: E731 - lambda fn
362
+ hallucinations[
363
+ pd_label_idx,
364
+ example_idx * 3 + 3,
365
+ ]
366
+ )
367
+ )
368
+
369
+ return {
370
+ index_to_label[pd_label_idx]: {
371
+ "count": max(
372
+ int(hallucinations[pd_label_idx, 0]),
373
+ 0,
374
+ ),
375
+ "examples": [
376
+ {
377
+ "datum": index_to_uid[
378
+ datum_idx(pd_label_idx, example_idx)
379
+ ],
380
+ "prediction": _convert_example_to_dict(
381
+ prediction_examples[
382
+ datum_idx(pd_label_idx, example_idx)
383
+ ][prediction_idx(pd_label_idx, example_idx)]
384
+ ),
385
+ "score": score_idx(pd_label_idx, example_idx),
386
+ }
387
+ for example_idx in range(number_of_examples)
388
+ if datum_idx(pd_label_idx, example_idx) >= 0
389
+ ],
390
+ }
391
+ for pd_label_idx in range(number_of_labels)
392
+ }
393
+
394
+
395
+ def _unpack_missing_predictions_value(
396
+ missing_predictions: NDArray[np.int32],
397
+ number_of_labels: int,
398
+ number_of_examples: int,
399
+ index_to_uid: dict[int, str],
400
+ index_to_label: dict[int, str],
401
+ groundtruth_examples: dict[int, NDArray[np.float16]],
402
+ ) -> dict[str, dict[str, int | list[dict[str, str | dict[str, float]]]]]:
403
+ """
404
+ Unpacks a numpy array of missing prediction counts and examples.
405
+ """
406
+
407
+ datum_idx = (
408
+ lambda gt_label_idx, example_idx: int( # noqa: E731 - lambda fn
409
+ missing_predictions[
410
+ gt_label_idx,
411
+ example_idx * 2 + 1,
412
+ ]
413
+ )
414
+ )
415
+
416
+ groundtruth_idx = (
417
+ lambda gt_label_idx, example_idx: int( # noqa: E731 - lambda fn
418
+ missing_predictions[
419
+ gt_label_idx,
420
+ example_idx * 2 + 2,
421
+ ]
422
+ )
423
+ )
424
+
425
+ return {
426
+ index_to_label[gt_label_idx]: {
427
+ "count": max(
428
+ int(missing_predictions[gt_label_idx, 0]),
429
+ 0,
430
+ ),
431
+ "examples": [
432
+ {
433
+ "datum": index_to_uid[
434
+ datum_idx(gt_label_idx, example_idx)
435
+ ],
436
+ "groundtruth": _convert_example_to_dict(
437
+ groundtruth_examples[
438
+ datum_idx(gt_label_idx, example_idx)
439
+ ][groundtruth_idx(gt_label_idx, example_idx)]
440
+ ),
441
+ }
442
+ for example_idx in range(number_of_examples)
443
+ if datum_idx(gt_label_idx, example_idx) >= 0
444
+ ],
445
+ }
446
+ for gt_label_idx in range(number_of_labels)
447
+ }
448
+
449
+
450
+ def unpack_confusion_matrix_into_metric_list(
451
+ results: tuple[
452
+ NDArray[np.float64],
453
+ NDArray[np.float64],
454
+ NDArray[np.int32],
455
+ ],
456
+ iou_thresholds: list[float],
457
+ score_thresholds: list[float],
458
+ number_of_examples: int,
459
+ index_to_label: dict[int, str],
460
+ index_to_uid: dict[int, str],
461
+ groundtruth_examples: dict[int, NDArray[np.float16]],
462
+ prediction_examples: dict[int, NDArray[np.float16]],
463
+ ) -> list[Metric]:
464
+ (
465
+ confusion_matrix,
466
+ hallucinations,
467
+ missing_predictions,
468
+ ) = results
469
+ n_labels = len(index_to_label)
470
+ return [
471
+ Metric.confusion_matrix(
472
+ iou_threshold=iou_threshold,
473
+ score_threshold=score_threshold,
474
+ maximum_number_of_examples=number_of_examples,
475
+ confusion_matrix=_unpack_confusion_matrix_value(
476
+ confusion_matrix=confusion_matrix[iou_idx, score_idx, :, :, :],
477
+ number_of_labels=n_labels,
478
+ number_of_examples=number_of_examples,
479
+ index_to_label=index_to_label,
480
+ index_to_uid=index_to_uid,
481
+ groundtruth_examples=groundtruth_examples,
482
+ prediction_examples=prediction_examples,
483
+ ),
484
+ hallucinations=_unpack_hallucinations_value(
485
+ hallucinations=hallucinations[iou_idx, score_idx, :, :],
486
+ number_of_labels=n_labels,
487
+ number_of_examples=number_of_examples,
488
+ index_to_label=index_to_label,
489
+ index_to_uid=index_to_uid,
490
+ prediction_examples=prediction_examples,
491
+ ),
492
+ missing_predictions=_unpack_missing_predictions_value(
493
+ missing_predictions=missing_predictions[
494
+ iou_idx, score_idx, :, :
495
+ ],
496
+ number_of_labels=n_labels,
497
+ number_of_examples=number_of_examples,
498
+ index_to_label=index_to_label,
499
+ index_to_uid=index_to_uid,
500
+ groundtruth_examples=groundtruth_examples,
501
+ ),
502
+ )
503
+ for iou_idx, iou_threshold in enumerate(iou_thresholds)
504
+ for score_idx, score_threshold in enumerate(score_thresholds)
505
+ ]
valor_lite/schemas.py CHANGED
@@ -1,15 +1,17 @@
1
- from dataclasses import dataclass
1
+ from dataclasses import asdict, dataclass
2
2
 
3
3
 
4
4
  @dataclass
5
- class Metric:
5
+ class BaseMetric:
6
6
  type: str
7
- value: float | dict | list
7
+ value: int | float | dict
8
8
  parameters: dict
9
9
 
10
+ def __post_init__(self):
11
+ if not isinstance(self.value, (int, float, dict)):
12
+ raise TypeError(
13
+ "Metric value must be of type `int`, `float` or `dict`."
14
+ )
15
+
10
16
  def to_dict(self) -> dict:
11
- return {
12
- "type": self.type,
13
- "value": self.value,
14
- "parameters": self.parameters,
15
- }
17
+ return asdict(self)
@@ -1,27 +1,12 @@
1
1
  from .annotation import Bitmask, Segmentation
2
2
  from .manager import DataLoader, Evaluator
3
- from .metric import (
4
- F1,
5
- Accuracy,
6
- ConfusionMatrix,
7
- IoU,
8
- MetricType,
9
- Precision,
10
- Recall,
11
- mIoU,
12
- )
3
+ from .metric import Metric, MetricType
13
4
 
14
5
  __all__ = [
15
6
  "DataLoader",
16
7
  "Evaluator",
17
8
  "Segmentation",
18
9
  "Bitmask",
10
+ "Metric",
19
11
  "MetricType",
20
- "Precision",
21
- "Recall",
22
- "Accuracy",
23
- "F1",
24
- "IoU",
25
- "mIoU",
26
- "ConfusionMatrix",
27
12
  ]
@@ -109,7 +109,7 @@ def compute_metrics(
109
109
  float
110
110
  Accuracy
111
111
  NDArray[np.float64]
112
- Confusion matrix containing IoU values.
112
+ Confusion matrix containing IOU values.
113
113
  NDArray[np.float64]
114
114
  Hallucination ratios.
115
115
  NDArray[np.float64]