datapruning 2.1.4__tar.gz → 2.1.6__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.
- {datapruning-2.1.4/datapruning.egg-info → datapruning-2.1.6}/PKG-INFO +1 -1
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/base.py +59 -35
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/margin_scorer.py +2 -2
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/sliding_window.py +35 -20
- {datapruning-2.1.4 → datapruning-2.1.6/datapruning.egg-info}/PKG-INFO +1 -1
- {datapruning-2.1.4 → datapruning-2.1.6}/pyproject.toml +1 -1
- {datapruning-2.1.4 → datapruning-2.1.6}/LICENSE +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/MANIFEST.in +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/README.md +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/build_src/_core.c +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/el2n_scorer.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/fusion_pipeline.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/kcenter_select.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/oof_utils.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/registry.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/benchmark.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/explainability.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/intelligence.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/optimizer.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/scanner.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/strategy_display.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/strategy_selector.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/multimodal/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/multimodal/clip_encoder.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/pipeline.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/reports/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/reports/exporter.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/compare.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/evaluation.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/synthetic.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/difficulty.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/embeddings/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/embeddings/torch_utils.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/fusion.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/noise.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/normalize.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/pipeline.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/redundancy.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/aum.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/el2n.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/forgetting.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/grand.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/margin.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selection.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/__init__.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/facility_location.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/kcenter.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/out_of_fold.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/strategy.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/sdk.py +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning.egg-info/SOURCES.txt +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning.egg-info/dependency_links.txt +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning.egg-info/requires.txt +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/datapruning.egg-info/top_level.txt +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/setup.cfg +0 -0
- {datapruning-2.1.4 → datapruning-2.1.6}/setup.py +0 -0
|
@@ -54,53 +54,59 @@ class BaseOptimizer(ABC):
|
|
|
54
54
|
X: pd.DataFrame,
|
|
55
55
|
y: pd.Series,
|
|
56
56
|
keep_ratio: float,
|
|
57
|
-
n_random: int =
|
|
58
|
-
|
|
57
|
+
n_random: int = 5,
|
|
58
|
+
n_splits: int = 3,
|
|
59
59
|
seed: int = 0,
|
|
60
60
|
) -> bool:
|
|
61
|
-
"""Check whether this strategy beats random on
|
|
61
|
+
"""Check whether this strategy beats random on internal holdouts.
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
Runs n_splits stratified train/val splits, each with n_random
|
|
64
|
+
random baseline subsets. The strategy is beneficial only if its
|
|
65
|
+
mean accuracy exceeds random mean by at least half the random
|
|
66
|
+
standard deviation — removing noisy single-split false positives.
|
|
66
67
|
"""
|
|
67
|
-
from sklearn.model_selection import
|
|
68
|
+
from sklearn.model_selection import StratifiedKFold
|
|
68
69
|
from sklearn.linear_model import LogisticRegression
|
|
69
70
|
|
|
70
71
|
if len(y) < 50:
|
|
71
72
|
return True
|
|
72
|
-
y_vals = y.values if hasattr(y,
|
|
73
|
+
y_vals = y.values if hasattr(y, "values") else np.asarray(y)
|
|
73
74
|
n_unique = len(np.unique(y_vals))
|
|
74
75
|
if n_unique < 2 or n_unique >= len(y_vals) * 0.8:
|
|
75
76
|
return True
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
random_state=seed, stratify=y.values,
|
|
80
|
-
)
|
|
81
|
-
X_tr, y_tr = X.iloc[tr_idx], y.iloc[tr_idx]
|
|
82
|
-
X_va, y_va = X.iloc[va_idx], y.iloc[va_idx]
|
|
83
|
-
n_keep = max(1, int(len(tr_idx) * keep_ratio))
|
|
78
|
+
strat_accs_all = []
|
|
79
|
+
random_accs_all = []
|
|
84
80
|
|
|
85
81
|
try:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
82
|
+
skf = StratifiedKFold(
|
|
83
|
+
n_splits=n_splits, shuffle=True, random_state=seed
|
|
84
|
+
)
|
|
85
|
+
for tr_idx, va_idx in skf.split(np.arange(len(y)), y.values):
|
|
86
|
+
X_tr, y_tr = X.iloc[tr_idx], y.iloc[tr_idx]
|
|
87
|
+
X_va, y_va = X.iloc[va_idx], y.iloc[va_idx]
|
|
88
|
+
n_keep = max(1, int(len(tr_idx) * keep_ratio))
|
|
89
|
+
|
|
90
|
+
self.fit(X_tr, y_tr)
|
|
91
|
+
s_keep = self._select_rows(X_tr, y_tr, keep_ratio)
|
|
92
|
+
m = LogisticRegression(max_iter=300, C=0.1, random_state=seed)
|
|
93
|
+
m.fit(X_tr.loc[s_keep], y_tr.loc[s_keep])
|
|
94
|
+
strat_accs_all.append(m.score(X_va, y_va))
|
|
95
|
+
|
|
96
|
+
rng = np.random.RandomState(seed + len(strat_accs_all))
|
|
97
|
+
for _ in range(n_random):
|
|
98
|
+
r_idx = rng.choice(len(tr_idx), n_keep, replace=False)
|
|
99
|
+
r_keep = X_tr.index[r_idx]
|
|
100
|
+
m2 = LogisticRegression(max_iter=300, C=0.1, random_state=seed)
|
|
101
|
+
m2.fit(X_tr.loc[r_keep], y_tr.loc[r_keep])
|
|
102
|
+
random_accs_all.append(m2.score(X_va, y_va))
|
|
103
|
+
|
|
104
|
+
mean_strat = np.mean(strat_accs_all)
|
|
105
|
+
mean_random = np.mean(random_accs_all)
|
|
106
|
+
std_random = np.std(random_accs_all)
|
|
107
|
+
return mean_strat > mean_random + std_random * 0.5
|
|
102
108
|
except Exception:
|
|
103
|
-
return
|
|
109
|
+
return False
|
|
104
110
|
|
|
105
111
|
def _random_select(self, X: pd.DataFrame, y: pd.Series, keep_ratio: float, seed: int = 42) -> pd.Index:
|
|
106
112
|
n_target = max(1, int(len(y) * keep_ratio))
|
|
@@ -131,9 +137,27 @@ class BaseOptimizer(ABC):
|
|
|
131
137
|
|
|
132
138
|
beneficial = self._beat_random_gate(X, y, keep_ratio)
|
|
133
139
|
if not beneficial:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
140
|
+
if keep_ratio < 0.25 and self.key != "kcenter":
|
|
141
|
+
try:
|
|
142
|
+
from datapruning.algorithms.kcenter_select import KCenterSelectOptimizer
|
|
143
|
+
kcenter = KCenterSelectOptimizer()
|
|
144
|
+
kcenter_beneficial = kcenter._beat_random_gate(X, y, keep_ratio)
|
|
145
|
+
if kcenter_beneficial:
|
|
146
|
+
kcenter.fit(X, y)
|
|
147
|
+
X_res, y_res = kcenter.transform(X, y, keep_ratio)
|
|
148
|
+
strategy_method = "Diversity Selection (auto)"
|
|
149
|
+
else:
|
|
150
|
+
keep_idx = self._random_select(X, y, keep_ratio)
|
|
151
|
+
X_res, y_res = X.loc[keep_idx], y.loc[keep_idx]
|
|
152
|
+
strategy_method = "random (fallback)"
|
|
153
|
+
except Exception:
|
|
154
|
+
keep_idx = self._random_select(X, y, keep_ratio)
|
|
155
|
+
X_res, y_res = X.loc[keep_idx], y.loc[keep_idx]
|
|
156
|
+
strategy_method = "random (fallback)"
|
|
157
|
+
else:
|
|
158
|
+
keep_idx = self._random_select(X, y, keep_ratio)
|
|
159
|
+
X_res, y_res = X.loc[keep_idx], y.loc[keep_idx]
|
|
160
|
+
strategy_method = "random (fallback)"
|
|
137
161
|
else:
|
|
138
162
|
strategy_method = self.display_name
|
|
139
163
|
|
|
@@ -43,9 +43,9 @@ class MarginScorerOptimizer(BaseOptimizer):
|
|
|
43
43
|
n_total_target = max(1, int(len(y) * keep_ratio))
|
|
44
44
|
n_minority = min(len(minority_idx), int(n_total_target * 0.8))
|
|
45
45
|
n_majority_target = max(n_total_target - n_minority, 0)
|
|
46
|
-
majority_scores = self._scores.loc[majority_idx].sort_values(ascending=
|
|
46
|
+
majority_scores = self._scores.loc[majority_idx].sort_values(ascending=False)
|
|
47
47
|
kept_majority_idx = majority_scores.index[:n_majority_target]
|
|
48
|
-
kept_minority_idx = minority_idx[:n_minority]
|
|
48
|
+
kept_minority_idx = self._scores.loc[minority_idx].sort_values(ascending=False).index[:n_minority]
|
|
49
49
|
return kept_minority_idx.append(kept_majority_idx)
|
|
50
50
|
|
|
51
51
|
def why_selected(self) -> str:
|
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import pandas as pd
|
|
4
4
|
from datapruning.algorithms.oof_utils import compute_oof_logits, logits_to_el2n
|
|
5
|
-
from datapruning.research_pruning.selection import sliding_window_select
|
|
6
5
|
from datapruning.algorithms.base import BaseOptimizer
|
|
7
6
|
from datapruning.algorithms.registry import register
|
|
8
7
|
|
|
@@ -32,29 +31,45 @@ class SlidingWindowOptimizer(BaseOptimizer):
|
|
|
32
31
|
def _select_rows(self, X: pd.DataFrame, y: pd.Series, keep_ratio: float) -> pd.Index:
|
|
33
32
|
if self._scores is None:
|
|
34
33
|
self.fit(X, y)
|
|
34
|
+
|
|
35
35
|
y_arr = y.values
|
|
36
36
|
classes = np.unique(y_arr)
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
n = len(y_arr)
|
|
38
|
+
n_target = max(1, int(n * keep_ratio))
|
|
39
|
+
|
|
39
40
|
if len(classes) < 2:
|
|
40
|
-
n_target = max(1, int(len(y) * keep_ratio))
|
|
41
41
|
return y.sample(n=n_target, random_state=42).index
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
|
|
43
|
+
scores = self._scores
|
|
44
|
+
order = np.argsort(scores)
|
|
45
|
+
drop_n = max(1, int(n * 0.10))
|
|
46
|
+
pool = order[:n - drop_n]
|
|
47
|
+
|
|
48
|
+
floor_per_class = max(1, int(n_target * 0.5 / len(classes)))
|
|
49
|
+
selected = set()
|
|
50
|
+
|
|
51
|
+
for c in classes:
|
|
52
|
+
c_mask = y_arr[pool] == c
|
|
53
|
+
c_indices = pool[c_mask]
|
|
54
|
+
if len(c_indices) == 0:
|
|
55
|
+
c_all = np.where(y_arr == c)[0]
|
|
56
|
+
n_pick = min(floor_per_class, len(c_all))
|
|
57
|
+
picks = c_all[np.argsort(scores[c_all])[-n_pick:]]
|
|
58
|
+
for idx in picks:
|
|
59
|
+
selected.add(int(idx))
|
|
60
|
+
else:
|
|
61
|
+
sorted_c = c_indices[np.argsort(scores[c_indices])]
|
|
62
|
+
n_pick = min(floor_per_class, len(sorted_c))
|
|
63
|
+
for idx in sorted_c[-n_pick:]:
|
|
64
|
+
selected.add(int(idx))
|
|
65
|
+
|
|
66
|
+
for idx in order[::-1]:
|
|
67
|
+
if len(selected) >= n_target:
|
|
68
|
+
break
|
|
69
|
+
selected.add(int(idx))
|
|
70
|
+
|
|
71
|
+
selected_arr = np.array(sorted(selected)[:n_target], dtype=int)
|
|
72
|
+
return X.index[selected_arr]
|
|
58
73
|
|
|
59
74
|
def why_selected(self) -> str:
|
|
60
75
|
return ("Filters out both the easiest (redundant) and hardest (potentially mislabeled) "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/evaluation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/embeddings/torch_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/facility_location.py
RENAMED
|
File without changes
|
|
File without changes
|
{datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/out_of_fold.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|