Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions detection/active_learning/query_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,14 @@ def select(self, pool: pd.DataFrame, n_query: int, model=None) -> list[str]:
return cast(list[str], pool.iloc[selected_idx]["wallet"].tolist())


def _kmeans_pp_indices(X: np.ndarray, k: int) -> list[int]:
"""k-means++ seeding — returns k indices."""
rng = np.random.default_rng(42)
def _kmeans_pp_indices(X: np.ndarray, k: int, seed: int | None = None) -> list[int]:
"""k-means++ seeding — returns k indices.

When *seed* is provided the seeding is deterministic, enabling exact
reproduction of a batch selection. Defaults to a fixed seed of 42 for
backward compatibility.
"""
rng = np.random.default_rng(42 if seed is None else seed)
idx = [int(rng.integers(len(X)))]
for _ in range(k - 1):
dists = _min_dist_to_set(X, X[idx])
Expand Down
32 changes: 32 additions & 0 deletions scripts/run_active_learning.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
# Select wallets using default strategy and push to queue:
python -m scripts.run_active_learning --pool data/unscored_wallets.parquet

# Select exactly reproducibly for a given seed:
python -m scripts.run_active_learning \\
--pool data/unscored_wallets.parquet \\
--strategy badge \\
--seed 42

# Specify strategy and batch size:
python -m scripts.run_active_learning \\
--pool data/unscored_wallets.parquet \\
Expand All @@ -27,6 +33,7 @@
from __future__ import annotations

import argparse
import inspect
import os

import pandas as pd
Expand Down Expand Up @@ -77,9 +84,14 @@ def run_active_learning(
queue_path: str,
model_dir: str,
asset_pair: str = "",
seed: int | None = None,
) -> list[str]:
"""Select *batch_size* wallets from *pool_path* and push to *queue_path*.

*seed*, when provided, is forwarded to the query strategy so that the
random components of batch selection (e.g. BADGE k-means++ seeding) can
be reproduced exactly.

Returns the list of selected wallet IDs.
"""
pool = load_pool(pool_path)
Expand All @@ -99,6 +111,15 @@ def run_active_learning(
elif primary_model is not None:
kwargs["model"] = primary_model

if seed is not None:
select_sig = inspect.signature(strategy.select)
if "seed" in select_sig.parameters:
kwargs["seed"] = seed
else:
logger.warning(
"Strategy '%s' does not accept a seed; ignoring --seed", strategy_name
)

selected = strategy.select(pool, n_query=batch_size, **kwargs)
logger.info(
"Strategy '%s' selected %d wallets from pool of %d",
Expand All @@ -121,6 +142,16 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--queue", default=config.AL_QUEUE_PATH)
parser.add_argument("--model-dir", default=config.MODEL_DIR)
parser.add_argument("--asset-pair", default="")
parser.add_argument(
"--seed",
type=int,
default=None,
help=(
"Random seed for reproducible batch selection (e.g. BADGE "
"k-means++ seeding / committee tie-breaking). When omitted, "
"selections use the strategy's default behaviour."
),
)
# Incremental update flags
parser.add_argument(
"--update",
Expand Down Expand Up @@ -173,6 +204,7 @@ def main() -> None:
queue_path=args.queue,
model_dir=args.model_dir,
asset_pair=args.asset_pair,
seed=args.seed,
)
print(f"Selected {len(selected)} wallets → {args.queue}")

Expand Down
Loading