Skip to content

About

Active Learning Regressor repo

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

267 Commits

Folders and files

Repository files navigation

ActiveLearningRegressor

activereg — An active learning framework for supervised regression problems.

Active learning reduces labeling cost by iteratively selecting the most informative candidates from an unlabeled pool, training on them, and refining the model. This repository provides the full infrastructure: acquisition functions, sampling strategies, ML model backends, evaluation metrics, and benchmark scripts.


Table of Contents


Overview

The active learning loop in activereg follows a standard pool-based protocol:

Initial labeled set
        │
        ▼
   Train ML model
        │
        ▼
  Predict on pool  ──►  Acquisition landscape
        │
        ▼
  Select batch  ──►  FPS / Voronoi / random
        │
        ▼
   Label batch  ──►  add to training set
        │
        └──── repeat ◄────────────────────┘

Each iteration expands the training set with the most informative points according to the chosen acquisition function, converging toward an accurate model with fewer labels than random or grid sampling.


Package Structure

activereg/
├── mlmodel/                # ML model backends (subpackage)
│   ├── _base.py            # MLModel protocol definition
│   ├── _gpr.py             # Gaussian Process Regressor
│   ├── _knn.py             # k-Nearest Neighbours regressor
│   ├── _mlp.py             # MLP and Anchored Ensemble MLP        [extra: nn]
│   ├── _bnn.py             # Bayesian Neural Network (PyTorch + Pyro)  [extra: nn]
│   ├── _kernels.py         # Kernel factory for GPs
│   └── _multi_property.py  # Multi-property model wrappers
├── acquisition.py          # Acquisition functions and batch selection
├── adaptiveRefinement.py   # Adaptive spatial refinement strategies
├── benchmarkFunctions.py   # Benchmark test functions (Hartmann, Ackley, …)  [extra: benchmarks]
├── beauty.py               # Visualization utilities
├── data.py                 # Dataset generation (LHS, Sobol, random)
├── experiment.py           # Experiment setup, model factory, and AL cycle core
├── hyperparams.py          # Hyperparameter grids and grid search utilities
├── metrics.py              # Evaluation metrics (RMSE, NLL, PICP, MPIW, …)
├── sampling.py             # Sampling methods (FPS, Voronoi, random)
├── utils.py                # Miscellaneous utilities
└── format.py               # Repository path constants

All ML models expose the same three-method protocol (see MLModel in activereg/mlmodel/_base.py):

Method Signature Description
train (X, y) → None Fit model to labeled data
predict (X) → (ŷ, mean, uncertainty) Predict with uncertainty estimate
__repr__ () → str Human-readable model description

Installation

Requirements

  • Python 3.10+

The core install depends only on the scientific Python stack (NumPy, pandas, SciPy, scikit-learn, matplotlib, seaborn, joblib, PyYAML, tqdm). PyTorch is not required — the core covers the entire active learning loop: GPR and kNN backends, acquisition functions, batch selection, metrics, and plotting.

Install from source

git clone https://github.com/AGardinon/ActiveLearningRegressor.git
cd ActiveLearningRegressor
pip install -e .

Optional extras

Deep-learning and benchmark-generation dependencies are opt-in:

Extra Installs Needed for
nn torch, pyro-ppl MLP, AnchoredEnsembleMLP, BayesianNN
benchmarks botorch (pulls in torch) synthetic test functions and benchmark dataset generation
all both of the above everything
pip install -e '.[nn]'           # neural-network backends
pip install -e '.[benchmarks]'   # synthetic benchmark functions
pip install -e '.[all]'          # everything

Optional components are imported lazily, so a missing extra surfaces only when you actually use it — and says which extra to install:

>>> from activereg.mlmodel import BayesianNN
ImportError: 'BayesianNN' requires the optional 'nn' dependencies.
             Install them with: pip install 'activereg[nn]'

Which extra do I need?

Task Install
Run AL with GPR or kNN over an existing pool CSV core
Regenerate figures from stored benchmark results core
scripts/run_lab_cycle.py, scripts/insilico_lab_al_simulation.py core
scripts/benchmark_gtlandscape.py — runs against a precomputed ground-truth landscape core
scripts/benchmark_functions.py — generates data from analytic test functions benchmarks
Neural-network model backends nn

Optional: GPU-accelerated PyTorch

Relevant only when installing the nn or benchmarks extras, which pull the CPU-only PyTorch wheel by default. For a CUDA build, install PyTorch separately before installing the extra — follow the official PyTorch install guide.


Quick Start

import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from activereg.experiment import (
    setup_multi_property_ml_model,
    setup_data_pool,
    run_single_al_cycle,
)

# --- data ---
# pool_df: search space (features only)
# evidence_df: initial labeled samples (features + targets)
pool_df     = pd.read_csv("datasets/my_pool.csv")
evidence_df = pd.read_csv("datasets/my_evidence.csv")

search_vars = ["x1", "x2", "x3"]
target_vars = ["y"]

# --- scaler: fit once on the full pool, reuse every cycle ---
scaler = StandardScaler()
_, scaler = setup_data_pool(df=pool_df, search_var=search_vars, scaler=scaler)

# --- model config ---
config = {
    "ml_model": "GPR",
    "model_parameters": {"kernel": "MATERN_W", "n_restarts_optimizer": 10, "alpha": 1e-10},
}
acquisition_params = [{"acquisition_mode": "expected_improvement", "n_points": 5, "xi": 0.01}]

# --- active learning cycle ---
for cycle in range(10):
    model = setup_multi_property_ml_model(config, target_vars)
    result = run_single_al_cycle(
        pool_df=pool_df,
        evidence_df=evidence_df,
        scaler=scaler,
        ml_model=model,
        search_vars=search_vars,
        target_vars=target_vars,
        acquisition_params=acquisition_params,
        batch_selection_method="highest_landscape",
        batch_selection_params={"percentile": 95, "sampling_method": "voronoi"},
    )
    # next_batch_df contains the selected candidates with NaN targets
    # measure them and add to evidence_df for the next cycle
    new_points = result["next_batch_df"]
    print(f"Cycle {cycle+1}: selected {len(new_points)} candidates")
    # evidence_df = pd.concat([evidence_df, measured_points])

Lab Use

activereg provides two additional workflows for experiments that cannot be run as a single automated benchmark.

Real-lab single-cycle workflow

One AL step at a time, with physical measurement between cycles:

Cycle 0: python scripts/run_lab_cycle.py -c config.yaml
         → writes cycle_0/output_sampled.csv
              (fill in measured values → cycle_0/validated.csv)
Cycle 1: python scripts/run_lab_cycle.py -c config.yaml
         → reads validated.csv, updates evidence, writes cycle_1/output_sampled.csv
         ...

Folder layout created automatically on the first run:

lab_al_experiments/{experiment_name}/
  dataset/
    POOL.csv          # immutable: full search space (features only)
    EVIDENCE.csv      # grows each cycle (features + targets)
    CANDIDATES.csv    # shrinks each cycle (features only)
    scaler.joblib     # fit once on POOL
  cycle_0/
    output_sampled.csv   # points to measure (targets = NaN)
    predictions.csv      # model predictions over pool
    landscapes.csv       # acquisition landscape per entry
    model_snapshot.pkl
    log.json
    validated.csv        # fill in measurements, then re-run
  cycle_1/
    ...

Copy and edit scripts/lab_cycle_config_template.yaml to configure your experiment.

In-silico simulation

Set ground_truth_file in the config to run the full multi-cycle loop automatically — the script looks up target values from the CSV after each cycle, so no manual measurement step is needed:

ground_truth_file: "ackley3d_10000pts.csv"   # in datasets/
python scripts/insilico_lab_al_simulation.py -c scripts/insilico_lab_al_simulation_config.yaml

ML Models

Model Class Backend Uncertainty source Requires
Gaussian Process Regressor GPR scikit-learn Posterior variance core
k-Nearest Neighbours kNNRegressorAL scikit-learn Neighbourhood spread core
Multi-Layer Perceptron MLP PyTorch Dropout / ensemble nn extra
Anchored Ensemble MLP AnchoredEnsembleMLP PyTorch Ensemble disagreement nn extra
Bayesian Neural Network BayesianNN PyTorch + Pyro Variational posterior nn extra

Models are configured via YAML files in scripts/mlmodel_config/ and instantiated through the factory:

from activereg.experiment import setup_ml_model

model = setup_ml_model(
    ml_model_type="GPR",
    ml_model_params={"kernel": "matern", "nu": 2.5},
)

GPR kernels

The KernelFactory in activereg/mlmodel/_kernels.py supports composing standard scikit-learn kernels (RBF, Matérn, WhiteKernel, ConstantKernel). Pre-defined kernel recipes are available in hyperparams.py.


Acquisition Functions

Acquisition functions map the model's predictions over the unlabeled pool to a scalar informativeness score for each candidate point.

Convention: all acquisition functions are defined for maximisation — a higher predicted mean or acquisition score is always better. Minimisation objectives must be negated upstream.

Mode Key parameters Description
upper_confidence_bound kappa (default 2.0) μ + κ·σ — tunable exploration/exploitation balance
expected_improvement xi (default 0.01) Expected improvement over the best observation
target_expected_improvement y_target, and exactly one of dist or epsilon Steers toward a target value rather than the maximum
percentage_target_expected_improvement percentage As above, with the target set as a percentage of the observed range
maximum_predicted_value — Pure exploitation: rank by predicted mean
uncertainty_landscape — Pure exploration: rank by predicted uncertainty
exploration_mutual_info noise variance Mutual-information exploration; needs a meaningful noise estimate

Numbered variants. Any mode may carry a numeric suffix — expected_improvement_1, target_expected_improvement_2 — which selects the same formula but lets one acquisition protocol declare the same mode several times with different parameters. The suffix is stripped internally (acquisition.py:291).

from activereg.acquisition import AcquisitionFunction

acq = AcquisitionFunction(
    acquisition_mode="upper_confidence_bound",
    y_best=float(y_train.max()),
    kappa=2.0,
)
landscape = acq.landscape_acquisition(X_candidates, ml_model)

Batch selection

Given a landscape, a batch of q points is chosen by one of:

Strategy Function Description
Highest landscape batch_highest_landscape Draws spatially from the top percentile of the landscape
Constant liar batch_constant_liar Imputes a fixed value for pending points, then re-acquires
Kriging believer batch_kriging_believer Imputes the model's own prediction for pending points
Local penalization batch_local_penalization Suppresses the landscape around already-selected points

highest_landscape is the strategy in practical use. penalize_landscape_fast applies the Gaussian suppression used for within-batch diversity.


Multi-Property Optimization

⚠️ Under development — documentation to be added.

The implementation is merged and usable, but the API and configuration schema may still change. Treat this section as a pointer, not a spec.

activereg supports active learning campaigns that optimize several target properties jointly, rather than one at a time.

What exists today:

  • IndependentMultiPropertyModel (activereg/mlmodel/_multi_property.py) — a dict of independent single-property models sharing one interface, plus wrap_single_property for backwards compatibility.
  • ParEGO-style scalarization in activereg/acquisition.py — scalarize(), WeightSampler, and compute_per_property_stats(). Random per-cycle weight sampling turns the multi-property problem into a sequence of scalar ones, tracing out the Pareto front across cycles.
  • augmented_chebyshev scalarization (default) applied in quality space, where each property is normalised so 0 = worst and 1 = best, consistent with the maximisation convention above.
  • Multi-property config variants alongside their single-property counterparts: scripts/general_config/*_multiprop.yaml.

Not yet documented here: the joint acquisition entry schema, weight-sampling options, per-property vs joint dispatch, and worked examples.

Backwards compatibility is preserved throughout — single-property campaigns are unaffected, and all existing acquisition formulas and batch selectors operate unchanged on the scalarized 1-D landscape.


Sampling Strategies

Once the acquisition landscape is computed, a batch of candidates is drawn using one of three strategies:

Strategy Mode (sampling_method in configs) Function Description
Farthest Point Sampling fps (default) fps Maximises pairwise distance in feature space
Voronoi voronoi voronoi Cluster-aware spatial coverage
Random random rnd Uniform random draw from top percentile
from activereg.sampling import sample_landscape

# X_top: coordinates of the candidates surviving the landscape filter.
# Returns the indices of the selected points within X_top.
selected_idx = sample_landscape(X_top, n_points=5, sampling_mode="fps")

Configuration

Experiments are controlled by YAML configuration files under scripts/:

scripts/
├── general_config/                          # benchmark scripts
│   ├── benchmark_config.yaml                # experiment, data, cycles, batch selection, protocol, refinement
│   ├── acquisition_mode_settings.yaml       # acquisition function parameters
│   ├── target_function_config.yaml          # test function and pool generation (benchmark_functions.py only)
│   └── *_multiprop.yaml                     # multi-property variants of the three files above
├── mlmodel_config/                          # benchmark scripts
│   ├── gpr_config.yaml                      # Gaussian Process Regressor
│   ├── knnregressor_config.yaml             # k-Nearest Neighbours
│   └── mlpanchored_config.yaml              # Anchored Ensemble MLP  [extra: nn]
├── lab_cycle_config_template.yaml           # run_lab_cycle.py (single file)
└── insilico_lab_al_simulation_config.yaml   # insilico_lab_al_simulation.py (single file)

The benchmark and lab scripts share the same building blocks but lay out the configuration differently.

Benchmark scripts split it over several files. The model file holds ml_model and model_parameters (plus optional grid_search). acquisition_mode_settings.yaml lists the modes and their parameters, and the benchmark config says how many points each mode acquires, stage by stage:

# mlmodel_config/gpr_config.yaml
ml_model: "GPR"
model_parameters:
  kernel: "MATERN_W"          # kernel recipe; see hyperparams.py
  n_restarts_optimizer: 50
  alpha: 1e-10
  normalize_y: true

# general_config/acquisition_mode_settings.yaml
acquisition_parameters:
  - acquisition_mode: "expected_improvement"
  - acquisition_mode: "uncertainty_landscape"

# general_config/benchmark_config.yaml
batch_selection:
  method: "highest_landscape"   # highest_landscape | constant_liar | kriging_believer | local_penalization
  method_params:                # null uses the defaults
    percentile: 95
    sampling_method: "fps"      # fps (default) | voronoi | random

acquisition_protocol:
  stage_1:
    cycles: 40
    acquisition_modes: ['uncertainty_landscape', 'expected_improvement']
    n_points: [2, 3]            # points per mode in each cycle

adaptive_refinement: null       # see "Adaptive refinement" below

Lab scripts (run_lab_cycle.py, insilico_lab_al_simulation.py) use one flat file, with n_points given in each acquisition entry:

ml_model: "GPR"
model_parameters:
  kernel: "MATERN_W"
  n_restarts_optimizer: 20
  alpha: 1.0e-10
  normalize_y: true

batch_selection_method: "highest_landscape"
batch_selection_params:
  percentile: 95
  sampling_method: "fps"        # fps (default) | voronoi | random

acquisition_parameters:
  - acquisition_mode: "expected_improvement"
    n_points: 5
    xi: 0.01

With highest_landscape, fps starts from the best point of the acquisition landscape and spreads the rest of the batch over its top percentile; with one point per mode it returns the argmax.

Adaptive refinement

A pool-based campaign cannot do better than the best point already in its pool. Adaptive refinement lifts that limit: every refinement_step acquired points it builds a hypercube around a few of the acquired points (the centroids), fills it with new points, labels them with the test function, and adds them to the pool. It is a local search: it adds points only around acquired points, so a region the starting pool never touches stays out of reach.

It runs only in benchmark_functions.py, because the new points need the function to be labelled. benchmark_gtlandscape.py stops with an error if adaptive_refinement is not null. It is off in the template; to enable it, replace adaptive_refinement: null in benchmark_config.yaml with the commented block below it. The main keys:

Key Default Meaning
refinement_step 20 refine after this many points were acquired since the last refinement
refinement_centroids 5 hypercubes per refinement step
refinement_batch_size 500 points sampled in each hypercube
refinement_centroid_window null pick centroids among the last N acquired points (null = all of them)
refinement_centroid_selection maximin maximin: spread-out centroids; best_first: best acquired points first (single target only)
refinement_centroid_top_fraction 0.2 best_first only: fraction of the window, ranked by target value, to pick from
half_side_length_strategy density hypercube size: density (from the local pool density) or fixed (base_fraction), in scaled units

best_first converges faster when the starting pool covers the space (e.g. a large LHS pool); maximin is the default. Each repetition writes collective_refinement_points_rep*.csv (the points added) and refinement_cubes_rep*.csv (one row per hypercube) to the benchmark folder. Results and design notes: proof_of_concepts/pool_refinement/.


Running Benchmarks

Benchmark experiments compare acquisition strategies on synthetic test functions (Hartmann3, Hartmann6, Ackley, Styblinski-Tang) across dimensions. Two entry points are available:

benchmark_functions.py — evaluates the analytic test function directly to generate the pool and label points. Requires the benchmarks extra (botorch).

bash scripts/run_benchmark_funcs.sh

# Or directly with Python
python scripts/benchmark_functions.py \
    -bc        scripts/general_config/benchmark_config.yaml \
    -mc        scripts/mlmodel_config/gpr_config.yaml \
    -acqmodes  scripts/general_config/acquisition_mode_settings.yaml \
    -tfc       scripts/general_config/target_function_config.yaml \
    -r         5

benchmark_gtlandscape.py — runs against a precomputed ground-truth landscape CSV named by ground_truth_file in the benchmark config, so no analytic function is evaluated. Runs on the core install (no -tfc argument, since the target function is already baked into the ground-truth file).

bash scripts/run_benchmark_gtlandsc.sh

python scripts/benchmark_gtlandscape.py \
    -bc        scripts/general_config/benchmark_config.yaml \
    -mc        scripts/mlmodel_config/gpr_config.yaml \
    -acqmodes  scripts/general_config/acquisition_mode_settings.yaml \
    -r         5

Both accept --rerun to overwrite an existing benchmark folder. Results (training CSVs, metric logs, per-run config/ snapshots) are written to benchmarks/.


License

MIT License. See LICENSE for details.

About

Active Learning Regressor repo

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages