Skip to content

Commit beab613

Browse files
committed
wip
1 parent 634320d commit beab613

6 files changed

Lines changed: 69 additions & 71 deletions

File tree

src/valor_lite/cache/compute.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import heapq
22
import tempfile
33
from pathlib import Path
4-
from typing import Callable
4+
from typing import Callable, Any
55

66
import pyarrow as pa
77

@@ -16,12 +16,13 @@ def _merge(
1616
batch_size: int,
1717
sorting: list[tuple[str, str]],
1818
columns: list[str] | None = None,
19-
table_sort_override: Callable[[pa.Table], pa.Table] | None = None,
19+
sort_override: Callable[[pa.Table], pa.Table] | None = None,
20+
merge_override: Callable[[pa.RecordBatch, Any], tuple[pa.RecordBatch | None, Any]] | None = None,
2021
):
2122
"""Merge locally sorted cache fragments."""
2223
for tbl in source.iterate_tables(columns=columns):
23-
if table_sort_override is not None:
24-
sorted_tbl = table_sort_override(tbl)
24+
if sort_override is not None:
25+
sorted_tbl = sort_override(tbl)
2526
else:
2627
sorted_tbl = tbl.sort_by(sorting)
2728
intermediate_sink.write_table(sorted_tbl)
@@ -57,12 +58,18 @@ def create_sort_key(
5758
if batches[batch_idx] is not None and len(batches[batch_idx]) > 0:
5859
heapq.heappush(heap, create_sort_key(batches, batch_idx, 0))
5960

61+
prev_state = None
6062
while heap:
6163
row = heapq.heappop(heap)
6264
batch_idx = row[-2]
6365
row_idx = row[-1]
6466
row_table = batches[batch_idx].slice(row_idx, 1)
65-
sink.write_batch(row_table)
67+
if merge_override is not None:
68+
batch = merge_override(row_table, prev_state)
69+
if batch is not None:
70+
sink.write_batch(row_table)
71+
else:
72+
sink.write_batch(row_table)
6673
row_idx += 1
6774
if row_idx < len(batches[batch_idx]):
6875
heapq.heappush(

src/valor_lite/classification/computation.py

Lines changed: 40 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ def compute_rocauc(
1414
n_labels: int,
1515
accumulated_tp: NDArray[np.uint64],
1616
accumulated_fp: NDArray[np.uint64],
17-
prev_fpr: NDArray[np.float64],
18-
prev_tpr: NDArray[np.float64],
1917
) -> tuple[NDArray[np.float64], NDArray[np.uint64], NDArray[np.uint64]]:
2018
"""
2119
Compute ROCAUC.
@@ -54,98 +52,81 @@ def compute_rocauc(
5452
positive_count = gt_count_per_label
5553
negative_count = pd_count_per_label - gt_count_per_label
5654

55+
print()
5756
for label_idx in range(n_labels):
5857
mask_pds = pd_labels == label_idx
5958
n_masked_pds = mask_pds.sum()
6059
if pd_count_per_label[label_idx] == 0 or n_masked_pds == 0:
6160
continue
6261

6362
true_positives = mask_matching_labels[mask_pds]
64-
false_positives = ~mask_matching_labels[mask_pds]
6563
tp_scores = scores[mask_pds]
6664

67-
cumulative_fp = np.cumsum(false_positives) + accumulated_fp[label_idx]
68-
cumulative_tp = np.cumsum(true_positives) + accumulated_tp[label_idx]
65+
distinct_score_indices = np.where(np.diff(tp_scores))[0]
66+
indices = np.r_[distinct_score_indices, n_masked_pds - 1]
67+
cumulative_tp = np.cumsum(true_positives, dtype=np.uint64)[indices]
68+
cumulative_fp = indices + 1 - cumulative_tp
69+
70+
cumulative_tp += accumulated_tp[label_idx]
71+
cumulative_fp += accumulated_fp[label_idx]
72+
73+
cumulative_tp = np.concatenate([accumulated_tp[label_idx:label_idx+1], cumulative_tp])
74+
cumulative_fp = np.concatenate([accumulated_fp[label_idx:label_idx+1], cumulative_fp])
6975

70-
accumulated_fp[label_idx] = cumulative_fp[-1]
7176
accumulated_tp[label_idx] = cumulative_tp[-1]
77+
accumulated_fp[label_idx] = cumulative_fp[-1]
7278

73-
fpr = np.zeros(n_masked_pds, dtype=np.float64)
79+
fpr = np.zeros_like(cumulative_fp, dtype=np.float64)
7480
np.divide(
7581
cumulative_fp,
7682
negative_count[label_idx],
7783
where=negative_count[label_idx] > 0,
7884
out=fpr,
7985
)
80-
tpr = np.zeros(n_masked_pds, dtype=np.float64)
86+
tpr = np.zeros_like(cumulative_tp, dtype=np.float64)
8187
np.divide(
8288
cumulative_tp,
8389
positive_count[label_idx],
8490
where=positive_count[label_idx] > 0,
8591
out=tpr,
8692
)
8793

88-
if prev_fpr[label_idx] > -0.5 and prev_tpr[label_idx] > -0.5:
89-
fpr = np.concatenate([prev_fpr[label_idx:label_idx+1], fpr])
90-
tpr = np.concatenate([prev_tpr[label_idx:label_idx+1], tpr])
91-
92-
# sort by -tpr, -score
93-
indices = np.lexsort((-tpr, -tp_scores))
94-
fpr = fpr[indices]
95-
tpr = tpr[indices]
96-
97-
sfpr = fpr.copy()
98-
stpr = tpr.copy()
94+
# # sort by -tpr, -score
95+
# indices = np.lexsort((-tpr, -tp_scores))
96+
# fpr = fpr[indices]
97+
# tpr = tpr[indices]
9998

10099
# running max of tpr
101100
np.maximum.accumulate(tpr, out=tpr)
102101

103-
104-
prev_fpr[label_idx] = fpr[-1]
105-
prev_tpr[label_idx] = tpr[-1]
106-
107102
# compute rocauc
108103
rocauc[label_idx] += npc.trapezoid(x=fpr, y=tpr, axis=0)
109104

110-
print()
111-
print(label_idx, rocauc[label_idx])
112-
print("====")
113-
print(
114-
f"{'FP':4}",
115-
f"{'TP':4}",
116-
f"{'CFP':4}",
117-
f"{'CTP':4}",
118-
f"{'FPR':4}",
119-
f"{'TPR':4}",
120-
f"{'SFPR':4}",
121-
f"{'STPR':4}",
122-
f"{'SCO':4}",
123-
)
124-
for f, t, af, at, fr, tr, sf, st, s in zip(
125-
false_positives,
126-
true_positives,
127-
cumulative_fp,
128-
cumulative_tp,
129-
fpr,
130-
tpr,
131-
sfpr,
132-
stpr,
133-
tp_scores,
134-
):
105+
if label_idx == 3:
106+
print(rocauc[label_idx])
135107
print(
136-
f"{f:.2f}",
137-
f"{t:.2f}",
138-
f"{af:.2f}",
139-
f"{at:.2f}",
140-
f"{fr:.2f}",
141-
f"{tr:.2f}",
142-
f"{sf:.2f}",
143-
f"{st:.2f}",
144-
f"{s:.2f}",
108+
f"{'CFP':4}",
109+
f"{'CTP':4}",
110+
f"{'FPR':4}",
111+
f"{'TPR':4}",
112+
# f"{'SCO':4}",
145113
)
114+
for af, at, fr, tr in zip(
115+
cumulative_fp,
116+
cumulative_tp,
117+
fpr,
118+
tpr,
119+
# tp_scores,
120+
):
121+
print(
122+
f"{af:.2f}",
123+
f"{at:.2f}",
124+
f"{fr:.2f}",
125+
f"{tr:.2f}",
126+
# f"{s:.2f}",
127+
)
146128

147-
148-
return rocauc, accumulated_fp, accumulated_tp, prev_fpr, prev_tpr
129+
return rocauc, accumulated_fp, accumulated_tp
149130

150131

151132
def compute_counts(

src/valor_lite/classification/evaluator.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,17 +294,14 @@ def compute_rocauc(self) -> dict[MetricType, list[Metric]]:
294294
# tpr[plabel]
295295
# else:
296296
# cumulative_fp[plabel] += 1
297-
print("loop", loopid)
298-
rocauc, accumulated_fp, accumulated_tp, prev_fpr, prev_tpr = compute_rocauc(
297+
rocauc, accumulated_fp, accumulated_tp = compute_rocauc(
299298
rocauc=rocauc,
300299
array=array,
301300
gt_count_per_label=self._label_counts[:, 0],
302301
pd_count_per_label=self._label_counts[:, 1],
303302
n_labels=self.info.number_of_labels,
304303
accumulated_fp=accumulated_fp,
305304
accumulated_tp=accumulated_tp,
306-
prev_fpr=prev_fpr,
307-
prev_tpr=prev_tpr,
308305
)
309306

310307
mean_rocauc = rocauc.mean()

src/valor_lite/classification/loader.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
from pathlib import Path
33

4+
import pyarrow as pa
45
import numpy as np
56
from pyarrow import DataType
67
from tqdm import tqdm
@@ -229,14 +230,24 @@ def finalize(
229230

230231
# post-process into sorted writer
231232
reader = self._writer.to_reader()
233+
234+
n_labels = len(self._index_to_label)
235+
236+
def accumulate(batch: pa.RecordBatch, prev: np.ndarray | None) -> pa.RecordBatch:
237+
pd_label_id = batch["pd_label_id"].as_py()
238+
matched = batch["match"][0].as_py()
239+
if prev is None:
240+
prev = np.zeros(n_labels, dtype=np.uint64)
241+
return None, ()
242+
232243
sort(
233244
source=reader,
234245
sink=self._rocauc_writer,
235246
batch_size=batch_size,
236247
sorting=[
237248
("score", "descending"),
238-
("match", "descending"),
239-
("pd_label_id", "ascending"),
249+
# ("match", "descending"),
250+
# ("pd_label_id", "ascending"),
240251
],
241252
columns=[
242253
"pd_label_id",

tests/classification/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
@pytest.fixture(
99
params=[
1010
("persistent", 10_000, 100_000),
11-
("persistent", 1, 1),
11+
("persistent", 2, 2),
1212
("memory", 10_000, 0),
1313
("memory", 1, 0),
1414
],

tests/classification/test_rocauc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ def test_rocauc_with_color_example(
6060

6161
loader.add_data(classifications_color_example)
6262
evaluator = loader.finalize()
63+
print(evaluator._index_to_label)
6364

6465
metrics = evaluator.compute_rocauc()
6566

67+
6668
# test ROCAUC
6769
actual_metrics = [m.to_dict() for m in metrics[MetricType.ROCAUC]]
6870
expected_metrics = [

0 commit comments

Comments
 (0)