valor-lite 0.37.1__tar.gz → 0.37.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {valor_lite-0.37.1 → valor_lite-0.37.2}/PKG-INFO +1 -1
  2. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/object_detection/computation.py +116 -97
  3. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/object_detection/evaluator.py +9 -10
  4. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/object_detection/shared.py +25 -4
  5. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite.egg-info/PKG-INFO +1 -1
  6. {valor_lite-0.37.1 → valor_lite-0.37.2}/README.md +0 -0
  7. {valor_lite-0.37.1 → valor_lite-0.37.2}/pyproject.toml +0 -0
  8. {valor_lite-0.37.1 → valor_lite-0.37.2}/setup.cfg +0 -0
  9. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/LICENSE +0 -0
  10. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/__init__.py +0 -0
  11. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/cache/__init__.py +0 -0
  12. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/cache/compute.py +0 -0
  13. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/cache/ephemeral.py +0 -0
  14. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/cache/persistent.py +0 -0
  15. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/__init__.py +0 -0
  16. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/annotation.py +0 -0
  17. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/computation.py +0 -0
  18. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/evaluator.py +0 -0
  19. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/loader.py +0 -0
  20. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/metric.py +0 -0
  21. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/numpy_compatibility.py +0 -0
  22. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/shared.py +0 -0
  23. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/classification/utilities.py +0 -0
  24. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/exceptions.py +0 -0
  25. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/object_detection/__init__.py +0 -0
  26. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/object_detection/annotation.py +0 -0
  27. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/object_detection/loader.py +0 -0
  28. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/object_detection/metric.py +0 -0
  29. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/object_detection/utilities.py +0 -0
  30. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/schemas.py +0 -0
  31. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/semantic_segmentation/__init__.py +0 -0
  32. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/semantic_segmentation/annotation.py +0 -0
  33. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/semantic_segmentation/computation.py +0 -0
  34. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/semantic_segmentation/evaluator.py +0 -0
  35. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/semantic_segmentation/loader.py +0 -0
  36. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/semantic_segmentation/metric.py +0 -0
  37. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/semantic_segmentation/shared.py +0 -0
  38. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/semantic_segmentation/utilities.py +0 -0
  39. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/__init__.py +0 -0
  40. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/annotation.py +0 -0
  41. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/computation.py +0 -0
  42. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/llm/__init__.py +0 -0
  43. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/llm/exceptions.py +0 -0
  44. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/llm/generation.py +0 -0
  45. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/llm/instructions.py +0 -0
  46. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/llm/integrations.py +0 -0
  47. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/llm/utilities.py +0 -0
  48. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/llm/validators.py +0 -0
  49. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/manager.py +0 -0
  50. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite/text_generation/metric.py +0 -0
  51. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite.egg-info/SOURCES.txt +0 -0
  52. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite.egg-info/dependency_links.txt +0 -0
  53. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite.egg-info/requires.txt +0 -0
  54. {valor_lite-0.37.1 → valor_lite-0.37.2}/valor_lite.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: valor-lite
3
- Version: 0.37.1
3
+ Version: 0.37.2
4
4
  Summary: Evaluate machine learning models.
5
5
  Project-URL: homepage, https://www.striveworks.com
6
6
  Requires-Python: >=3.10
@@ -176,11 +176,32 @@ def compute_polygon_iou(
176
176
  return ious
177
177
 
178
178
 
179
- def rank_pairs(sorted_pairs: NDArray[np.float64]):
179
+ def rank_pairs(
180
+ sorted_pairs: NDArray[np.float64],
181
+ ) -> tuple[NDArray[np.float64], NDArray[np.intp]]:
180
182
  """
181
183
  Prunes and ranks prediction pairs.
182
184
 
183
185
  Should result in a single pair per prediction annotation.
186
+
187
+ Parameters
188
+ ----------
189
+ sorted_pairs : NDArray[np.float64]
190
+ Ranked annotation pairs.
191
+ Index 0 - Datum Index
192
+ Index 1 - GroundTruth Index
193
+ Index 2 - Prediction Index
194
+ Index 3 - GroundTruth Label Index
195
+ Index 4 - Prediction Label Index
196
+ Index 5 - IOU
197
+ Index 6 - Score
198
+
199
+ Returns
200
+ -------
201
+ NDArray[float64]
202
+ Ranked prediction pairs.
203
+ NDArray[intp]
204
+ Indices of ranked prediction pairs.
184
205
  """
185
206
 
186
207
  # remove unmatched ground truths
@@ -197,8 +218,10 @@ def rank_pairs(sorted_pairs: NDArray[np.float64]):
197
218
  pairs = pairs[mask_label_match | mask_unmatched_predictions]
198
219
  indices = indices[mask_label_match | mask_unmatched_predictions]
199
220
 
200
- # only keep the highest ranked pair
201
- _, unique_indices = np.unique(pairs[:, [0, 2]], axis=0, return_index=True)
221
+ # only keep the highest ranked prediction (datum_id, prediction_id, predicted_label_id)
222
+ _, unique_indices = np.unique(
223
+ pairs[:, [0, 2, 4]], axis=0, return_index=True
224
+ )
202
225
  pairs = pairs[unique_indices]
203
226
  indices = indices[unique_indices]
204
227
 
@@ -216,55 +239,57 @@ def rank_pairs(sorted_pairs: NDArray[np.float64]):
216
239
 
217
240
 
218
241
  def calculate_ranking_boundaries(
219
- ranked_pairs: NDArray[np.float64], number_of_labels: int
220
- ):
221
- dt_gt_ids = ranked_pairs[:, (0, 1)].astype(np.int64)
222
- gt_ids = dt_gt_ids[:, 1]
223
- ious = ranked_pairs[:, 5]
242
+ ranked_pairs: NDArray[np.float64],
243
+ ) -> NDArray[np.float64]:
244
+ """
245
+ Determine IOU boundaries for computing AP across chunks.
224
246
 
225
- unique_gts, gt_counts = np.unique(
226
- dt_gt_ids,
227
- return_counts=True,
228
- axis=0,
229
- )
230
- unique_gts = unique_gts[gt_counts > 1] # select gts with many pairs
231
- unique_gts = unique_gts[unique_gts[:, 1] >= 0] # remove null
247
+ Parameters
248
+ ----------
249
+ ranked_pairs : NDArray[np.float64]
250
+ Ranked annotation pairs.
251
+ Index 0 - Datum Index
252
+ Index 1 - GroundTruth Index
253
+ Index 2 - Prediction Index
254
+ Index 3 - GroundTruth Label Index
255
+ Index 4 - Prediction Label Index
256
+ Index 5 - IOU
257
+ Index 6 - Score
258
+
259
+ Returns
260
+ -------
261
+ NDArray[np.float64]
262
+ A 1-D array containing the lower IOU boundary for classifying pairs as true-positive across chunks.
263
+ """
264
+ # groundtruths defined as (datum_id, groundtruth_id, groundtruth_label_id)
265
+ gts = ranked_pairs[:, (0, 1, 3)].astype(np.int64)
266
+ ious = ranked_pairs[:, 5]
232
267
 
233
- winning_predictions = np.ones_like(ious, dtype=np.bool_)
234
- winning_predictions[gt_ids < 0] = False # null gts cannot be won
235
- iou_boundary = np.zeros_like(ious)
268
+ iou_boundary = np.ones_like(ious) * 2 # impossible bound
236
269
 
270
+ mask_valid_gts = gts[:, 1] >= 0
271
+ unique_gts = np.unique(gts[mask_valid_gts], axis=0)
237
272
  for gt in unique_gts:
238
- mask_gts = (
239
- ranked_pairs[:, (0, 1)].astype(np.int64) == (gt[0], gt[1])
240
- ).all(axis=1)
241
- for label in range(number_of_labels):
242
- mask_plabel = (ranked_pairs[:, 4] == label) & mask_gts
243
- if mask_plabel.sum() <= 1:
244
- continue
273
+ mask_gt = (gts == gt).all(axis=1)
274
+ if mask_gt.sum() <= 1:
275
+ iou_boundary[mask_gt] = 0.0
276
+ continue
245
277
 
246
- # mark sequence of increasing IOUs starting from index 0
247
- labeled_ious = ranked_pairs[mask_plabel, 5]
248
- mask_increasing_iou = np.ones_like(labeled_ious, dtype=np.bool_)
249
- mask_increasing_iou[1:] = labeled_ious[1:] > labeled_ious[:-1]
250
- idx_dec = np.where(~mask_increasing_iou)[0]
251
- if idx_dec.size == 1:
252
- mask_increasing_iou[idx_dec[0] :] = False
278
+ running_max = np.maximum.accumulate(ious[mask_gt])
279
+ mask_rmax = np.isclose(running_max, ious[mask_gt])
280
+ mask_rmax[1:] &= running_max[1:] > running_max[:-1]
281
+ mask_gt[mask_gt] &= mask_rmax
253
282
 
254
- # define IOU lower bound
255
- iou_boundary[mask_plabel][1:] = labeled_ious[:-1]
256
- iou_boundary[mask_plabel][
257
- ~mask_increasing_iou
258
- ] = 2.0 # arbitrary >1.0 value
283
+ indices = np.where(mask_gt)[0]
259
284
 
260
- # mark first element (highest score)
261
- indices = np.where(mask_gts)[0][1:]
262
- winning_predictions[indices] = False
285
+ iou_boundary[indices[0]] = 0.0
286
+ iou_boundary[indices[1:]] = ious[indices[:-1]]
263
287
 
264
- return iou_boundary, winning_predictions
288
+ return iou_boundary
265
289
 
266
290
 
267
- def rank_table(tbl: pa.Table, number_of_labels: int) -> pa.Table:
291
+ def rank_table(tbl: pa.Table) -> pa.Table:
292
+ """Rank table for AP computation."""
268
293
  numeric_columns = [
269
294
  "datum_id",
270
295
  "gt_id",
@@ -278,24 +303,24 @@ def rank_table(tbl: pa.Table, number_of_labels: int) -> pa.Table:
278
303
  ("pd_score", "descending"),
279
304
  ("iou", "descending"),
280
305
  ]
306
+
307
+ # initial sort
281
308
  sorted_tbl = tbl.sort_by(sorting_args)
282
309
  pairs = np.column_stack(
283
310
  [sorted_tbl[col].to_numpy() for col in numeric_columns]
284
311
  )
285
- pairs, indices = rank_pairs(pairs)
312
+
313
+ # rank pairs
314
+ ranked_pairs, indices = rank_pairs(pairs)
286
315
  ranked_tbl = sorted_tbl.take(indices)
287
- lower_iou_bound, winning_predictions = calculate_ranking_boundaries(
288
- pairs, number_of_labels=number_of_labels
289
- )
290
- ranked_tbl = ranked_tbl.append_column(
291
- pa.field("high_score", pa.bool_()),
292
- pa.array(winning_predictions, type=pa.bool_()),
293
- )
316
+
317
+ # find boundaries
318
+ lower_iou_bound = calculate_ranking_boundaries(ranked_pairs)
294
319
  ranked_tbl = ranked_tbl.append_column(
295
320
  pa.field("iou_prev", pa.float64()),
296
321
  pa.array(lower_iou_bound, type=pa.float64()),
297
322
  )
298
- ranked_tbl = ranked_tbl.sort_by(sorting_args)
323
+
299
324
  return ranked_tbl
300
325
 
301
326
 
@@ -306,41 +331,42 @@ def compute_counts(
306
331
  number_of_groundtruths_per_label: NDArray[np.uint64],
307
332
  number_of_labels: int,
308
333
  running_counts: NDArray[np.uint64],
309
- ) -> tuple:
334
+ pr_curve: NDArray[np.float64],
335
+ ) -> NDArray[np.uint64]:
310
336
  """
311
337
  Computes Object Detection metrics.
312
338
 
313
- Takes data with shape (N, 7):
314
-
315
- Index 0 - Datum Index
316
- Index 1 - GroundTruth Index
317
- Index 2 - Prediction Index
318
- Index 3 - GroundTruth Label Index
319
- Index 4 - Prediction Label Index
320
- Index 5 - IOU
321
- Index 6 - Score
322
- Index 7 - IOU Lower Boundary
323
- Index 8 - Winning Prediction
339
+ Precision-recall curve and running counts are updated in-place.
324
340
 
325
341
  Parameters
326
342
  ----------
327
343
  ranked_pairs : NDArray[np.float64]
328
344
  A ranked array summarizing the IOU calculations of one or more pairs.
345
+ Index 0 - Datum Index
346
+ Index 1 - GroundTruth Index
347
+ Index 2 - Prediction Index
348
+ Index 3 - GroundTruth Label Index
349
+ Index 4 - Prediction Label Index
350
+ Index 5 - IOU
351
+ Index 6 - Score
352
+ Index 7 - IOU Lower Boundary
329
353
  iou_thresholds : NDArray[np.float64]
330
354
  A 1-D array containing IOU thresholds.
331
355
  score_thresholds : NDArray[np.float64]
332
356
  A 1-D array containing score thresholds.
357
+ number_of_groundtruths_per_label : NDArray[np.uint64]
358
+ A 1-D array containing total number of ground truths per label.
359
+ number_of_labels : int
360
+ Total number of unique labels.
361
+ running_counts : NDArray[np.uint64]
362
+ A 2-D array containing running counts of total predictions and true-positive. This array is mutated.
363
+ pr_curve : NDArray[np.float64]
364
+ A 2-D array containing 101-point binning of precision and score over a fixed recall interval. This array is mutated.
333
365
 
334
366
  Returns
335
367
  -------
336
- tuple[NDArray[np.float64], NDArray[np.float64]]
337
- Average Precision results (AP, mAP).
338
- tuple[NDArray[np.float64], NDArray[np.float64]]
339
- Average Recall results (AR, mAR).
340
- NDArray[np.float64]
341
- Precision, Recall, TP, FP, FN, F1 Score.
342
- NDArray[np.float64]
343
- Interpolated Precision-Recall Curves.
368
+ NDArray[uint64]
369
+ Batched counts of TP, FP, FN.
344
370
  """
345
371
  n_rows = ranked_pairs.shape[0]
346
372
  n_labels = number_of_labels
@@ -349,7 +375,6 @@ def compute_counts(
349
375
 
350
376
  # initialize result arrays
351
377
  counts = np.zeros((n_ious, n_scores, 3, n_labels), dtype=np.uint64)
352
- pr_curve = np.zeros((n_ious, n_labels, 101, 2))
353
378
 
354
379
  # start computation
355
380
  ids = ranked_pairs[:, :5].astype(np.int64)
@@ -359,7 +384,6 @@ def compute_counts(
359
384
  ious = ranked_pairs[:, 5]
360
385
  scores = ranked_pairs[:, 6]
361
386
  prev_ious = ranked_pairs[:, 7]
362
- winners = ranked_pairs[:, 8].astype(np.bool_)
363
387
 
364
388
  unique_pd_labels, _ = np.unique(pd_labels, return_index=True)
365
389
 
@@ -384,9 +408,9 @@ def compute_counts(
384
408
  mask_iou_prev = prev_ious < iou_thresholds[iou_idx]
385
409
  mask_iou = mask_iou_curr & mask_iou_prev
386
410
 
387
- mask_tp_outer = mask_tp & mask_iou & winners
411
+ mask_tp_outer = mask_tp & mask_iou
388
412
  mask_fp_outer = mask_fp & (
389
- (~mask_gt_exists_labels_match & mask_iou) | ~mask_iou | ~winners
413
+ (~mask_gt_exists_labels_match & mask_iou) | ~mask_iou
390
414
  )
391
415
 
392
416
  for score_idx in range(n_scores):
@@ -421,33 +445,29 @@ def compute_counts(
421
445
  )
422
446
 
423
447
  # create true-positive mask score threshold
424
- tp_candidates = ids[mask_tp_outer]
425
- _, indices_gt_unique = np.unique(
426
- tp_candidates[:, [0, 1, 3]], axis=0, return_index=True
427
- )
428
- mask_gt_unique = np.zeros(tp_candidates.shape[0], dtype=np.bool_)
429
- mask_gt_unique[indices_gt_unique] = True
430
- true_positives_mask = np.zeros(n_rows, dtype=np.bool_)
431
- true_positives_mask[mask_tp_outer] = mask_gt_unique
448
+ mask_tps = mask_tp_outer
449
+ true_positives_mask = mask_tps & mask_iou_prev
432
450
 
433
451
  # count running tp and total for AP
434
452
  for pd_label in unique_pd_labels:
435
453
  mask_pd_label = pd_labels == pd_label
454
+ total_count = mask_pd_label.sum()
455
+ if total_count == 0:
456
+ continue
436
457
 
437
458
  # running total prediction count
438
- total_count = mask_pd_label.sum()
439
- running_total_count[iou_idx][mask_pd_label] = np.arange(
440
- running_counts[iou_idx, pd_label, 0],
441
- running_counts[iou_idx, pd_label, 0] + total_count,
459
+ running_total_count[iou_idx, mask_pd_label] = np.arange(
460
+ running_counts[iou_idx, pd_label, 0] + 1,
461
+ running_counts[iou_idx, pd_label, 0] + total_count + 1,
442
462
  )
443
463
  running_counts[iou_idx, pd_label, 0] += total_count
444
464
 
445
465
  # running true-positive count
446
466
  mask_tp_for_counting = mask_pd_label & true_positives_mask
447
467
  tp_count = mask_tp_for_counting.sum()
448
- running_tp_count[iou_idx][mask_tp_for_counting] = np.arange(
449
- running_counts[iou_idx, pd_label, 1],
450
- running_counts[iou_idx, pd_label, 1] + tp_count,
468
+ running_tp_count[iou_idx, mask_tp_for_counting] = np.arange(
469
+ running_counts[iou_idx, pd_label, 1] + 1,
470
+ running_counts[iou_idx, pd_label, 1] + tp_count + 1,
451
471
  )
452
472
  running_counts[iou_idx, pd_label, 1] += tp_count
453
473
 
@@ -474,15 +494,14 @@ def compute_counts(
474
494
  pr_curve[iou_idx, pd_labels, recall_index[iou_idx], 0],
475
495
  precision[iou_idx],
476
496
  )
477
- pr_curve[iou_idx, pd_labels, recall_index[iou_idx], 1] = np.maximum(
478
- pr_curve[iou_idx, pd_labels, recall_index[iou_idx], 1],
479
- scores,
497
+ pr_curve[
498
+ iou_idx, pd_labels[::-1], recall_index[iou_idx][::-1], 1
499
+ ] = np.maximum(
500
+ pr_curve[iou_idx, pd_labels[::-1], recall_index[iou_idx][::-1], 1],
501
+ scores[::-1],
480
502
  )
481
503
 
482
- return (
483
- counts,
484
- pr_curve,
485
- )
504
+ return counts
486
505
 
487
506
 
488
507
  def compute_precision_recall_f1(
@@ -79,7 +79,7 @@ class Builder:
79
79
  batch_size=batch_size,
80
80
  )
81
81
  ranked_writer = MemoryCacheWriter.create(
82
- schema=generate_ranked_schema(),
82
+ schema=generate_ranked_schema(metadata_fields),
83
83
  batch_size=batch_size,
84
84
  )
85
85
 
@@ -126,7 +126,7 @@ class Builder:
126
126
  )
127
127
  ranked_writer = FileCacheWriter.create(
128
128
  path=generate_ranked_cache_path(path),
129
- schema=generate_ranked_schema(),
129
+ schema=generate_ranked_schema(metadata_fields),
130
130
  batch_size=batch_size,
131
131
  rows_per_file=rows_per_file,
132
132
  compression=compression,
@@ -163,11 +163,9 @@ class Builder:
163
163
  columns=[
164
164
  field.name
165
165
  for field in self._ranked_writer.schema
166
- if field.name not in {"high_score", "iou_prev"}
166
+ if field.name != "iou_prev"
167
167
  ],
168
- table_sort_override=lambda tbl: rank_table(
169
- tbl, number_of_labels=n_labels
170
- ),
168
+ table_sort_override=rank_table,
171
169
  )
172
170
  self._ranked_writer.flush()
173
171
 
@@ -454,7 +452,8 @@ class Evaluator:
454
452
 
455
453
  counts = np.zeros((n_ious, n_scores, 3, n_labels), dtype=np.uint64)
456
454
  pr_curve = np.zeros((n_ious, n_labels, 101, 2), dtype=np.float64)
457
- running_counts = np.ones((n_ious, n_labels, 2), dtype=np.uint64)
455
+ running_counts = np.zeros((n_ious, n_labels, 2), dtype=np.uint64)
456
+
458
457
  for pairs in self._ranked_reader.iterate_arrays(
459
458
  numeric_columns=[
460
459
  "datum_id",
@@ -465,23 +464,22 @@ class Evaluator:
465
464
  "iou",
466
465
  "pd_score",
467
466
  "iou_prev",
468
- "high_score",
469
467
  ],
470
468
  filter=datums,
471
469
  ):
472
470
  if pairs.size == 0:
473
471
  continue
474
472
 
475
- (batch_counts, batch_pr_curve) = compute_counts(
473
+ batch_counts = compute_counts(
476
474
  ranked_pairs=pairs,
477
475
  iou_thresholds=np.array(iou_thresholds),
478
476
  score_thresholds=np.array(score_thresholds),
479
477
  number_of_groundtruths_per_label=n_gts_per_lbl,
480
478
  number_of_labels=len(self._index_to_label),
481
479
  running_counts=running_counts,
480
+ pr_curve=pr_curve,
482
481
  )
483
482
  counts += batch_counts
484
- pr_curve = np.maximum(batch_pr_curve, pr_curve)
485
483
 
486
484
  # fn count
487
485
  counts[:, :, 2, :] = n_gts_per_lbl - counts[:, :, 0, :]
@@ -551,6 +549,7 @@ class Evaluator:
551
549
  (n_ious, n_scores, n_labels), dtype=np.uint64
552
550
  )
553
551
  unmatched_predictions = np.zeros_like(unmatched_groundtruths)
552
+
554
553
  for pairs in self._detailed_reader.iterate_arrays(
555
554
  numeric_columns=[
556
555
  "datum_id",
@@ -68,8 +68,10 @@ def generate_detailed_schema(
68
68
  return pa.schema(reserved_fields + metadata_fields)
69
69
 
70
70
 
71
- def generate_ranked_schema() -> pa.Schema:
72
- reserved_fields = [
71
+ def generate_ranked_schema(
72
+ metadata_fields: list[tuple[str, str | pa.DataType]] | None
73
+ ) -> pa.Schema:
74
+ reserved_detailed_fields = [
73
75
  ("datum_uid", pa.string()),
74
76
  ("datum_id", pa.int64()),
75
77
  # groundtruth
@@ -81,10 +83,29 @@ def generate_ranked_schema() -> pa.Schema:
81
83
  ("pd_score", pa.float64()),
82
84
  # pair
83
85
  ("iou", pa.float64()),
84
- ("high_score", pa.bool_()),
86
+ ]
87
+ reserved_ranking_fields = [
85
88
  ("iou_prev", pa.float64()),
86
89
  ]
87
- return pa.schema(reserved_fields)
90
+ metadata_fields = metadata_fields if metadata_fields else []
91
+
92
+ # validate
93
+ reserved_field_names = {
94
+ f[0] for f in reserved_detailed_fields + reserved_ranking_fields
95
+ }
96
+ metadata_field_names = {f[0] for f in metadata_fields}
97
+ if conflicting := reserved_field_names & metadata_field_names:
98
+ raise ValueError(
99
+ f"metadata fields {conflicting} conflict with reserved fields"
100
+ )
101
+
102
+ return pa.schema(
103
+ [
104
+ *reserved_detailed_fields,
105
+ *metadata_fields,
106
+ *reserved_ranking_fields,
107
+ ]
108
+ )
88
109
 
89
110
 
90
111
  def encode_metadata_fields(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: valor-lite
3
- Version: 0.37.1
3
+ Version: 0.37.2
4
4
  Summary: Evaluate machine learning models.
5
5
  Project-URL: homepage, https://www.striveworks.com
6
6
  Requires-Python: >=3.10
File without changes
File without changes
File without changes