Really updated to add code for new experiments
This commit is contained in:
194
src/experiments_unimodal_scalarized/cmaes_tc.py
Normal file
194
src/experiments_unimodal_scalarized/cmaes_tc.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import argparse
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import mlflow
|
||||
|
||||
from src.data_openml import load_dataset
|
||||
from .search_space_rf import (
|
||||
PARAM_NAMES,
|
||||
decode_rf,
|
||||
clip_to_bounds,
|
||||
sample_uniform,
|
||||
)
|
||||
from .scalarized_evaluate import evaluate_scalarized
|
||||
|
||||
|
||||
def run_cmaes_tc(
|
||||
dataset,
|
||||
alpha,
|
||||
seed=0,
|
||||
pop_size=20,
|
||||
generations=50,
|
||||
elite_frac=0.5,
|
||||
n_folds=3,
|
||||
sigma0=0.2,
|
||||
tc_k=5,
|
||||
eps_scalar=1e-4,
|
||||
reinject_factor=1.5,
|
||||
experiment_name="cmaes_tc_unimodal",
|
||||
outdir="runs/unimodal_cmaes_tc",
|
||||
):
|
||||
"""Simplified CMA-ES-TC.
|
||||
|
||||
We keep a diagonal covariance adaptation via elite std.
|
||||
Label this as simplified CMA-ES for teaching unless you later add full cov updates.
|
||||
"""
|
||||
|
||||
X, y, task = load_dataset(dataset, random_state=seed)
|
||||
|
||||
pre_cfg = {
|
||||
"num_impute_strategy": "median",
|
||||
"cat_impute_strategy": "most_frequent",
|
||||
"scaler": "standard",
|
||||
"poly_degree": 1,
|
||||
"select_k": None,
|
||||
}
|
||||
|
||||
rng = np.random.RandomState(seed)
|
||||
dim = len(PARAM_NAMES)
|
||||
n_elite = max(1, int(pop_size * elite_frac))
|
||||
|
||||
outdir = Path(outdir) / f"{dataset}_alpha{alpha}_seed{seed}"
|
||||
outdir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mlflow.set_experiment(experiment_name)
|
||||
with mlflow.start_run(run_name=f"{dataset}_alpha{alpha}_seed{seed}"):
|
||||
mlflow.log_params({
|
||||
"dataset": dataset,
|
||||
"alpha": alpha,
|
||||
"seed": seed,
|
||||
"pop_size": pop_size,
|
||||
"generations": generations,
|
||||
"elite_frac": elite_frac,
|
||||
"n_folds": n_folds,
|
||||
"sigma0": sigma0,
|
||||
"tc_k": tc_k,
|
||||
"eps_scalar": eps_scalar,
|
||||
})
|
||||
|
||||
# Initialize mean from random uniform sample
|
||||
init_pop = np.stack([sample_uniform(rng) for _ in range(pop_size)])
|
||||
mean = np.mean(init_pop, axis=0)
|
||||
sigma = np.ones(dim) * sigma0
|
||||
|
||||
log = []
|
||||
std_log = []
|
||||
best_vec = None
|
||||
best_scalar = float("inf")
|
||||
best_meta = None
|
||||
|
||||
start = time.time()
|
||||
|
||||
for gen in range(generations):
|
||||
samples = rng.normal(loc=mean, scale=sigma, size=(pop_size, dim))
|
||||
samples = [clip_to_bounds(s) for s in samples]
|
||||
|
||||
fits = []
|
||||
for s in samples:
|
||||
mp = decode_rf(s)
|
||||
scalar, meta = evaluate_scalarized(
|
||||
X=X,
|
||||
y=y,
|
||||
task=task,
|
||||
algo="rf",
|
||||
model_params=mp,
|
||||
pre_cfg=pre_cfg,
|
||||
alpha=alpha,
|
||||
seed=seed,
|
||||
n_folds=n_folds,
|
||||
)
|
||||
fits.append((scalar, s, meta))
|
||||
|
||||
fits.sort(key=lambda x: x[0])
|
||||
elites = [f[1] for f in fits[:n_elite]]
|
||||
elites = np.stack(elites)
|
||||
|
||||
mean = np.mean(elites, axis=0)
|
||||
sigma = np.std(elites, axis=0) + 1e-6
|
||||
|
||||
scalars = [f[0] for f in fits]
|
||||
pop_std = float(np.std(scalars))
|
||||
std_log.append(pop_std)
|
||||
|
||||
best_scalar_gen, best_vec_gen, best_meta_gen = fits[0]
|
||||
if best_scalar_gen < best_scalar:
|
||||
best_scalar = float(best_scalar_gen)
|
||||
best_vec = best_vec_gen.copy()
|
||||
best_meta = best_meta_gen.copy()
|
||||
|
||||
row = {
|
||||
"gen": gen,
|
||||
"best_scalar": float(best_scalar_gen),
|
||||
"best_mse_like": best_meta_gen["mse_like"],
|
||||
"best_shap_std": best_meta_gen["shap_std"],
|
||||
"best_stability_score": best_meta_gen["stability_score"],
|
||||
"pop_std_scalar": pop_std,
|
||||
"params": decode_rf(best_vec_gen),
|
||||
"elapsed_s": time.time() - start,
|
||||
}
|
||||
log.append(row)
|
||||
|
||||
mlflow.log_metrics({
|
||||
"best_scalar": row["best_scalar"],
|
||||
"best_mse_like": row["best_mse_like"],
|
||||
"best_shap_std": row["best_shap_std"],
|
||||
"best_stability_score": row["best_stability_score"],
|
||||
"pop_std_scalar": row["pop_std_scalar"],
|
||||
}, step=gen)
|
||||
|
||||
if gen >= tc_k and all(s < eps_scalar for s in std_log[-tc_k:]):
|
||||
sigma *= reinject_factor
|
||||
mlflow.log_metric("tc_reinject", 1.0, step=gen)
|
||||
|
||||
hall = {
|
||||
"best_params": decode_rf(best_vec),
|
||||
"best_scalar": best_scalar,
|
||||
**best_meta,
|
||||
}
|
||||
|
||||
(outdir / "log.json").write_text(json.dumps(log, indent=2))
|
||||
(outdir / "hall_of_fame.json").write_text(json.dumps(hall, indent=2))
|
||||
|
||||
mlflow.log_dict(log, "log.json")
|
||||
mlflow.log_dict(hall, "hall_of_fame.json")
|
||||
|
||||
return hall
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--dataset", required=True, choices=["adult", "cal_housing"])
|
||||
ap.add_argument("--alpha", type=float, required=True)
|
||||
ap.add_argument("--seed", type=int, default=0)
|
||||
ap.add_argument("--pop-size", type=int, default=20)
|
||||
ap.add_argument("--generations", type=int, default=50)
|
||||
ap.add_argument("--elite-frac", type=float, default=0.5)
|
||||
ap.add_argument("--n-folds", type=int, default=3)
|
||||
ap.add_argument("--sigma0", type=float, default=0.2)
|
||||
ap.add_argument("--tc-k", type=int, default=5)
|
||||
ap.add_argument("--eps-scalar", type=float, default=1e-4)
|
||||
ap.add_argument("--experiment-name", default="cmaes_tc_unimodal")
|
||||
ap.add_argument("--outdir", default="runs/unimodal_cmaes_tc")
|
||||
args = ap.parse_args()
|
||||
|
||||
run_cmaes_tc(
|
||||
dataset=args.dataset,
|
||||
alpha=args.alpha,
|
||||
seed=args.seed,
|
||||
pop_size=args.pop_size,
|
||||
generations=args.generations,
|
||||
elite_frac=args.elite_frac,
|
||||
n_folds=args.n_folds,
|
||||
sigma0=args.sigma0,
|
||||
tc_k=args.tc_k,
|
||||
eps_scalar=args.eps_scalar,
|
||||
experiment_name=args.experiment_name,
|
||||
outdir=args.outdir,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
183
src/experiments_unimodal_scalarized/emna_tc.py
Normal file
183
src/experiments_unimodal_scalarized/emna_tc.py
Normal file
@@ -0,0 +1,183 @@
|
||||
import argparse
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import mlflow
|
||||
|
||||
from src.data_openml import load_dataset
|
||||
from .search_space_rf import (
|
||||
PARAM_NAMES,
|
||||
sample_uniform,
|
||||
decode_rf,
|
||||
clip_to_bounds,
|
||||
)
|
||||
from .scalarized_evaluate import evaluate_scalarized
|
||||
|
||||
|
||||
def run_emna_tc(
|
||||
dataset,
|
||||
alpha,
|
||||
seed=0,
|
||||
pop_size=20,
|
||||
generations=50,
|
||||
elite_frac=0.5,
|
||||
n_folds=3,
|
||||
tc_k=5,
|
||||
eps_scalar=1e-4,
|
||||
reinject_factor=1.5,
|
||||
experiment_name="emna_tc_unimodal",
|
||||
outdir="runs/unimodal_emna_tc",
|
||||
):
|
||||
X, y, task = load_dataset(dataset, random_state=seed)
|
||||
|
||||
pre_cfg = {
|
||||
"num_impute_strategy": "median",
|
||||
"cat_impute_strategy": "most_frequent",
|
||||
"scaler": "standard",
|
||||
"poly_degree": 1,
|
||||
"select_k": None,
|
||||
}
|
||||
|
||||
rng = np.random.RandomState(seed)
|
||||
dim = len(PARAM_NAMES)
|
||||
n_elite = max(1, int(pop_size * elite_frac))
|
||||
|
||||
outdir = Path(outdir) / f"{dataset}_alpha{alpha}_seed{seed}"
|
||||
outdir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mlflow.set_experiment(experiment_name)
|
||||
with mlflow.start_run(run_name=f"{dataset}_alpha{alpha}_seed{seed}"):
|
||||
mlflow.log_params({
|
||||
"dataset": dataset,
|
||||
"alpha": alpha,
|
||||
"seed": seed,
|
||||
"pop_size": pop_size,
|
||||
"generations": generations,
|
||||
"elite_frac": elite_frac,
|
||||
"n_folds": n_folds,
|
||||
"tc_k": tc_k,
|
||||
"eps_scalar": eps_scalar,
|
||||
})
|
||||
|
||||
pop = [sample_uniform(rng) for _ in range(pop_size)]
|
||||
mean = np.mean(pop, axis=0)
|
||||
cov = np.cov(np.stack(pop).T) + 1e-6 * np.eye(dim)
|
||||
|
||||
log = []
|
||||
std_log = []
|
||||
best_vec = None
|
||||
best_scalar = float("inf")
|
||||
best_meta = None
|
||||
|
||||
start = time.time()
|
||||
|
||||
for gen in range(generations):
|
||||
samples = rng.multivariate_normal(mean, cov, size=pop_size)
|
||||
samples = [clip_to_bounds(s) for s in samples]
|
||||
|
||||
fits = []
|
||||
for s in samples:
|
||||
mp = decode_rf(s)
|
||||
scalar, meta = evaluate_scalarized(
|
||||
X=X,
|
||||
y=y,
|
||||
task=task,
|
||||
algo="rf",
|
||||
model_params=mp,
|
||||
pre_cfg=pre_cfg,
|
||||
alpha=alpha,
|
||||
seed=seed,
|
||||
n_folds=n_folds,
|
||||
)
|
||||
fits.append((scalar, s, meta))
|
||||
|
||||
fits.sort(key=lambda x: x[0])
|
||||
elites = [f[1] for f in fits[:n_elite]]
|
||||
|
||||
mean = np.mean(elites, axis=0)
|
||||
cov = np.cov(np.stack(elites).T) + 1e-6 * np.eye(dim)
|
||||
|
||||
scalars = [f[0] for f in fits]
|
||||
pop_std = float(np.std(scalars))
|
||||
std_log.append(pop_std)
|
||||
|
||||
best_scalar_gen, best_vec_gen, best_meta_gen = fits[0]
|
||||
if best_scalar_gen < best_scalar:
|
||||
best_scalar = float(best_scalar_gen)
|
||||
best_vec = best_vec_gen.copy()
|
||||
best_meta = best_meta_gen.copy()
|
||||
|
||||
row = {
|
||||
"gen": gen,
|
||||
"best_scalar": float(best_scalar_gen),
|
||||
"best_mse_like": best_meta_gen["mse_like"],
|
||||
"best_shap_std": best_meta_gen["shap_std"],
|
||||
"best_stability_score": best_meta_gen["stability_score"],
|
||||
"pop_std_scalar": pop_std,
|
||||
"params": decode_rf(best_vec_gen),
|
||||
"elapsed_s": time.time() - start,
|
||||
}
|
||||
log.append(row)
|
||||
|
||||
mlflow.log_metrics({
|
||||
"best_scalar": row["best_scalar"],
|
||||
"best_mse_like": row["best_mse_like"],
|
||||
"best_shap_std": row["best_shap_std"],
|
||||
"best_stability_score": row["best_stability_score"],
|
||||
"pop_std_scalar": row["pop_std_scalar"],
|
||||
}, step=gen)
|
||||
|
||||
# Threshold convergence reinjection
|
||||
if gen >= tc_k and all(s < eps_scalar for s in std_log[-tc_k:]):
|
||||
cov *= reinject_factor
|
||||
mlflow.log_metric("tc_reinject", 1.0, step=gen)
|
||||
|
||||
hall = {
|
||||
"best_params": decode_rf(best_vec),
|
||||
"best_scalar": best_scalar,
|
||||
**best_meta,
|
||||
}
|
||||
|
||||
(outdir / "log.json").write_text(json.dumps(log, indent=2))
|
||||
(outdir / "hall_of_fame.json").write_text(json.dumps(hall, indent=2))
|
||||
|
||||
mlflow.log_dict(log, "log.json")
|
||||
mlflow.log_dict(hall, "hall_of_fame.json")
|
||||
|
||||
return hall
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--dataset", required=True, choices=["adult", "cal_housing"])
|
||||
ap.add_argument("--alpha", type=float, required=True)
|
||||
ap.add_argument("--seed", type=int, default=0)
|
||||
ap.add_argument("--pop-size", type=int, default=20)
|
||||
ap.add_argument("--generations", type=int, default=50)
|
||||
ap.add_argument("--elite-frac", type=float, default=0.5)
|
||||
ap.add_argument("--n-folds", type=int, default=3)
|
||||
ap.add_argument("--tc-k", type=int, default=5)
|
||||
ap.add_argument("--eps-scalar", type=float, default=1e-4)
|
||||
ap.add_argument("--experiment-name", default="emna_tc_unimodal")
|
||||
ap.add_argument("--outdir", default="runs/unimodal_emna_tc")
|
||||
args = ap.parse_args()
|
||||
|
||||
run_emna_tc(
|
||||
dataset=args.dataset,
|
||||
alpha=args.alpha,
|
||||
seed=args.seed,
|
||||
pop_size=args.pop_size,
|
||||
generations=args.generations,
|
||||
elite_frac=args.elite_frac,
|
||||
n_folds=args.n_folds,
|
||||
tc_k=args.tc_k,
|
||||
eps_scalar=args.eps_scalar,
|
||||
experiment_name=args.experiment_name,
|
||||
outdir=args.outdir,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
196
src/experiments_unimodal_scalarized/pso_tc.py
Normal file
196
src/experiments_unimodal_scalarized/pso_tc.py
Normal file
@@ -0,0 +1,196 @@
|
||||
import argparse
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import mlflow
|
||||
|
||||
from src.data_openml import load_dataset
|
||||
from .search_space_rf import (
|
||||
PARAM_NAMES,
|
||||
sample_uniform,
|
||||
decode_rf,
|
||||
clip_to_bounds,
|
||||
)
|
||||
from .scalarized_evaluate import evaluate_scalarized
|
||||
|
||||
|
||||
def run_pso_tc(
|
||||
dataset,
|
||||
alpha,
|
||||
seed=0,
|
||||
swarm_size=20,
|
||||
iterations=50,
|
||||
n_folds=3,
|
||||
w=0.7,
|
||||
c1=1.4,
|
||||
c2=1.4,
|
||||
tc_k=5,
|
||||
eps_scalar=1e-4,
|
||||
reinject_factor=1.5,
|
||||
experiment_name="pso_tc_unimodal",
|
||||
outdir="runs/unimodal_pso_tc",
|
||||
):
|
||||
X, y, task = load_dataset(dataset, random_state=seed)
|
||||
|
||||
pre_cfg = {
|
||||
"num_impute_strategy": "median",
|
||||
"cat_impute_strategy": "most_frequent",
|
||||
"scaler": "standard",
|
||||
"poly_degree": 1,
|
||||
"select_k": None,
|
||||
}
|
||||
|
||||
rng = np.random.RandomState(seed)
|
||||
dim = len(PARAM_NAMES)
|
||||
|
||||
outdir = Path(outdir) / f"{dataset}_alpha{alpha}_seed{seed}"
|
||||
outdir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mlflow.set_experiment(experiment_name)
|
||||
with mlflow.start_run(run_name=f"{dataset}_alpha{alpha}_seed{seed}"):
|
||||
mlflow.log_params({
|
||||
"dataset": dataset,
|
||||
"alpha": alpha,
|
||||
"seed": seed,
|
||||
"swarm_size": swarm_size,
|
||||
"iterations": iterations,
|
||||
"n_folds": n_folds,
|
||||
"w": w,
|
||||
"c1": c1,
|
||||
"c2": c2,
|
||||
"tc_k": tc_k,
|
||||
"eps_scalar": eps_scalar,
|
||||
})
|
||||
|
||||
pos = np.array([sample_uniform(rng) for _ in range(swarm_size)])
|
||||
vel = rng.normal(0, 1, size=(swarm_size, dim)) * 0.1
|
||||
|
||||
pbest_pos = pos.copy()
|
||||
pbest_fit = np.full(swarm_size, np.inf)
|
||||
|
||||
gbest_pos = None
|
||||
gbest_fit = np.inf
|
||||
gbest_meta = None
|
||||
|
||||
log = []
|
||||
std_log = []
|
||||
start = time.time()
|
||||
|
||||
for it in range(iterations):
|
||||
fits = []
|
||||
for i in range(swarm_size):
|
||||
mp = decode_rf(pos[i])
|
||||
scalar, meta = evaluate_scalarized(
|
||||
X=X,
|
||||
y=y,
|
||||
task=task,
|
||||
algo="rf",
|
||||
model_params=mp,
|
||||
pre_cfg=pre_cfg,
|
||||
alpha=alpha,
|
||||
seed=seed,
|
||||
n_folds=n_folds,
|
||||
)
|
||||
fits.append((scalar, meta))
|
||||
|
||||
if scalar < pbest_fit[i]:
|
||||
pbest_fit[i] = scalar
|
||||
pbest_pos[i] = pos[i].copy()
|
||||
|
||||
if scalar < gbest_fit:
|
||||
gbest_fit = scalar
|
||||
gbest_pos = pos[i].copy()
|
||||
gbest_meta = meta.copy()
|
||||
|
||||
scalars = [f[0] for f in fits]
|
||||
pop_std = float(np.std(scalars))
|
||||
std_log.append(pop_std)
|
||||
|
||||
row = {
|
||||
"iter": it,
|
||||
"best_scalar": float(gbest_fit),
|
||||
"best_mse_like": gbest_meta["mse_like"],
|
||||
"best_shap_std": gbest_meta["shap_std"],
|
||||
"best_stability_score": gbest_meta["stability_score"],
|
||||
"pop_std_scalar": pop_std,
|
||||
"params": decode_rf(gbest_pos),
|
||||
"elapsed_s": time.time() - start,
|
||||
}
|
||||
log.append(row)
|
||||
|
||||
mlflow.log_metrics({
|
||||
"best_scalar": row["best_scalar"],
|
||||
"best_mse_like": row["best_mse_like"],
|
||||
"best_shap_std": row["best_shap_std"],
|
||||
"best_stability_score": row["best_stability_score"],
|
||||
"pop_std_scalar": row["pop_std_scalar"],
|
||||
}, step=it)
|
||||
|
||||
r1 = rng.rand(swarm_size, dim)
|
||||
r2 = rng.rand(swarm_size, dim)
|
||||
|
||||
vel = (
|
||||
w * vel
|
||||
+ c1 * r1 * (pbest_pos - pos)
|
||||
+ c2 * r2 * (gbest_pos - pos)
|
||||
)
|
||||
pos = pos + vel
|
||||
pos = np.array([clip_to_bounds(p) for p in pos])
|
||||
|
||||
if it >= tc_k and all(s < eps_scalar for s in std_log[-tc_k:]):
|
||||
vel *= reinject_factor
|
||||
mlflow.log_metric("tc_reinject", 1.0, step=it)
|
||||
|
||||
hall = {
|
||||
"best_params": decode_rf(gbest_pos),
|
||||
"best_scalar": float(gbest_fit),
|
||||
**gbest_meta,
|
||||
}
|
||||
|
||||
(outdir / "log.json").write_text(json.dumps(log, indent=2))
|
||||
(outdir / "hall_of_fame.json").write_text(json.dumps(hall, indent=2))
|
||||
|
||||
mlflow.log_dict(log, "log.json")
|
||||
mlflow.log_dict(hall, "hall_of_fame.json")
|
||||
|
||||
return hall
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--dataset", required=True, choices=["adult", "cal_housing"])
|
||||
ap.add_argument("--alpha", type=float, required=True)
|
||||
ap.add_argument("--seed", type=int, default=0)
|
||||
ap.add_argument("--swarm-size", type=int, default=20)
|
||||
ap.add_argument("--iterations", type=int, default=50)
|
||||
ap.add_argument("--n-folds", type=int, default=3)
|
||||
ap.add_argument("--w", type=float, default=0.7)
|
||||
ap.add_argument("--c1", type=float, default=1.4)
|
||||
ap.add_argument("--c2", type=float, default=1.4)
|
||||
ap.add_argument("--tc-k", type=int, default=5)
|
||||
ap.add_argument("--eps-scalar", type=float, default=1e-4)
|
||||
ap.add_argument("--experiment-name", default="pso_tc_unimodal")
|
||||
ap.add_argument("--outdir", default="runs/unimodal_pso_tc")
|
||||
args = ap.parse_args()
|
||||
|
||||
run_pso_tc(
|
||||
dataset=args.dataset,
|
||||
alpha=args.alpha,
|
||||
seed=args.seed,
|
||||
swarm_size=args.swarm_size,
|
||||
iterations=args.iterations,
|
||||
n_folds=args.n_folds,
|
||||
w=args.w,
|
||||
c1=args.c1,
|
||||
c2=args.c2,
|
||||
tc_k=args.tc_k,
|
||||
eps_scalar=args.eps_scalar,
|
||||
experiment_name=args.experiment_name,
|
||||
outdir=args.outdir,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
55
src/experiments_unimodal_scalarized/run_all_alphas.py
Normal file
55
src/experiments_unimodal_scalarized/run_all_alphas.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ALPHAS = [round(0.1 * i, 1) for i in range(1, 10)]
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--dataset", required=True, choices=["adult", "cal_housing"])
|
||||
ap.add_argument("--seed", type=int, default=0)
|
||||
|
||||
ap.add_argument("--emna-pop", type=int, default=20)
|
||||
ap.add_argument("--emna-gens", type=int, default=50)
|
||||
|
||||
ap.add_argument("--cmaes-pop", type=int, default=20)
|
||||
ap.add_argument("--cmaes-gens", type=int, default=50)
|
||||
|
||||
ap.add_argument("--pso-swarm", type=int, default=20)
|
||||
ap.add_argument("--pso-iters", type=int, default=50)
|
||||
args = ap.parse_args()
|
||||
|
||||
py = sys.executable # uses your current venv python
|
||||
|
||||
for alpha in ALPHAS:
|
||||
subprocess.run([
|
||||
py, "-m", "src.experiments_unimodal_scalarized.emna_tc",
|
||||
"--dataset", args.dataset,
|
||||
"--alpha", str(alpha),
|
||||
"--seed", str(args.seed),
|
||||
"--pop-size", str(args.emna_pop),
|
||||
"--generations", str(args.emna_gens),
|
||||
], check=True)
|
||||
|
||||
subprocess.run([
|
||||
py, "-m", "src.experiments_unimodal_scalarized.cmaes_tc",
|
||||
"--dataset", args.dataset,
|
||||
"--alpha", str(alpha),
|
||||
"--seed", str(args.seed),
|
||||
"--pop-size", str(args.cmaes_pop),
|
||||
"--generations", str(args.cmaes_gens),
|
||||
], check=True)
|
||||
|
||||
subprocess.run([
|
||||
py, "-m", "src.experiments_unimodal_scalarized.pso_tc",
|
||||
"--dataset", args.dataset,
|
||||
"--alpha", str(alpha),
|
||||
"--seed", str(args.seed),
|
||||
"--swarm-size", str(args.pso_swarm),
|
||||
"--iterations", str(args.pso_iters),
|
||||
], check=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
102
src/experiments_unimodal_scalarized/scalarized_evaluate.py
Normal file
102
src/experiments_unimodal_scalarized/scalarized_evaluate.py
Normal file
@@ -0,0 +1,102 @@
|
||||
import numpy as np
|
||||
from sklearn.pipeline import Pipeline
|
||||
from sklearn.metrics import mean_squared_error, brier_score_loss
|
||||
|
||||
from src.preprocessing import build_preprocessor
|
||||
from src.models import make_model
|
||||
from src.stability import compute_shap_matrix, shap_stability_from_matrices
|
||||
from src.protocols_methodology.automl_protocol_adapters import cv_protocol
|
||||
|
||||
|
||||
def evaluate_scalarized(
|
||||
X,
|
||||
y,
|
||||
task,
|
||||
algo,
|
||||
model_params,
|
||||
pre_cfg,
|
||||
alpha,
|
||||
seed=0,
|
||||
n_folds=3,
|
||||
max_eval_rows=512,
|
||||
bg_size=128,
|
||||
):
|
||||
"""Single objective scalarization.
|
||||
|
||||
Returns:
|
||||
scalar: alpha * loss_mean + (1-alpha) * shap_std_mean (minimize)
|
||||
meta: dict with mse_like, shap_std, stability_score
|
||||
"""
|
||||
|
||||
rng = np.random.RandomState(seed)
|
||||
|
||||
# Fixed SHAP evaluation pool for this evaluation.
|
||||
eval_size = min(max_eval_rows, len(X))
|
||||
eval_idx = rng.choice(len(X), size=eval_size, replace=False)
|
||||
X_eval_fixed = X.iloc[eval_idx]
|
||||
|
||||
# Freeze preprocessing dimensionality.
|
||||
fixed_poly_degree = pre_cfg.get("poly_degree", 1)
|
||||
probe_pre = build_preprocessor(
|
||||
X, task, pre_cfg, fixed_k=None, fixed_poly_degree=fixed_poly_degree
|
||||
)
|
||||
Xp = probe_pre.fit_transform(X, y)
|
||||
n_after_prep = Xp.shape[1]
|
||||
desired_k = pre_cfg.get("select_k", None)
|
||||
fixed_k = None if desired_k is None else int(min(max(1, desired_k), n_after_prep))
|
||||
|
||||
shap_mats_with_names = []
|
||||
losses = []
|
||||
|
||||
reps = cv_protocol(X, y, n_folds=n_folds, seed=seed)
|
||||
|
||||
for rep_id, rep in enumerate(reps):
|
||||
tr, te = rep["train_idx"], rep["test_idx"]
|
||||
X_fit, y_fit = X.iloc[tr], y.iloc[tr]
|
||||
X_test, y_test = X.iloc[te], y.iloc[te]
|
||||
|
||||
preproc = build_preprocessor(
|
||||
X, task, pre_cfg, fixed_k=fixed_k, fixed_poly_degree=fixed_poly_degree
|
||||
)
|
||||
model = make_model(task, algo, model_params, random_state=seed + rep_id)
|
||||
pipe = Pipeline([("pre", preproc), ("model", model)])
|
||||
|
||||
shap_vals, _, _, feat_names = compute_shap_matrix(
|
||||
pipe,
|
||||
X_fit=X_fit,
|
||||
y_fit=y_fit,
|
||||
X_eval=X_eval_fixed,
|
||||
task_type=task,
|
||||
bg_size=bg_size,
|
||||
max_eval_rows=max_eval_rows,
|
||||
rng_seed=seed,
|
||||
)
|
||||
shap_mats_with_names.append((shap_vals, feat_names))
|
||||
|
||||
if task == "regression":
|
||||
y_pred = pipe.predict(X_test)
|
||||
loss = float(mean_squared_error(y_test, y_pred))
|
||||
else:
|
||||
if hasattr(pipe.named_steps["model"], "predict_proba"):
|
||||
y_prob = pipe.predict_proba(X_test)[:, 1]
|
||||
else:
|
||||
scores = pipe.decision_function(X_test)
|
||||
scores = (scores - scores.min()) / (scores.max() - scores.min() + 1e-8)
|
||||
y_prob = scores
|
||||
loss = float(brier_score_loss(y_test, y_prob))
|
||||
|
||||
losses.append(loss)
|
||||
|
||||
agg_std, stability_score, _, _ = shap_stability_from_matrices(shap_mats_with_names)
|
||||
|
||||
mse_like = float(np.mean(losses))
|
||||
shap_std = float(agg_std)
|
||||
|
||||
scalar = float(alpha * mse_like + (1.0 - alpha) * shap_std)
|
||||
|
||||
meta = {
|
||||
"mse_like": mse_like,
|
||||
"shap_std": shap_std,
|
||||
"stability_score": float(stability_score),
|
||||
}
|
||||
return scalar, meta
|
||||
48
src/experiments_unimodal_scalarized/search_space_rf.py
Normal file
48
src/experiments_unimodal_scalarized/search_space_rf.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import numpy as np
|
||||
|
||||
# Continuous-ish search space for RandomForest.
|
||||
# We fix model family to keep the landscape close to unimodal once scalarized.
|
||||
BOUNDS = {
|
||||
"n_estimators": (50, 400), # int
|
||||
"max_depth": (2, 15), # int
|
||||
"max_features": (0.2, 1.0), # float (fraction of features)
|
||||
"min_samples_split": (2, 20), # int
|
||||
"min_samples_leaf": (1, 10), # int
|
||||
}
|
||||
|
||||
PARAM_NAMES = list(BOUNDS.keys())
|
||||
|
||||
|
||||
def sample_uniform(rng: np.random.RandomState):
|
||||
vec = []
|
||||
for k in PARAM_NAMES:
|
||||
low, high = BOUNDS[k]
|
||||
if k == "max_features":
|
||||
vec.append(rng.uniform(low, high))
|
||||
else:
|
||||
vec.append(rng.randint(low, high + 1))
|
||||
return np.array(vec, dtype=float)
|
||||
|
||||
|
||||
def clip_to_bounds(vec):
|
||||
out = []
|
||||
for i, k in enumerate(PARAM_NAMES):
|
||||
low, high = BOUNDS[k]
|
||||
v = float(vec[i])
|
||||
if k == "max_features":
|
||||
v = float(np.clip(v, low, high))
|
||||
else:
|
||||
v = int(np.clip(round(v), low, high))
|
||||
out.append(v)
|
||||
return np.array(out, dtype=float)
|
||||
|
||||
|
||||
def decode_rf(vec):
|
||||
v = clip_to_bounds(vec)
|
||||
return {
|
||||
"n_estimators": int(v[0]),
|
||||
"max_depth": int(v[1]),
|
||||
"max_features": float(v[2]),
|
||||
"min_samples_split": int(v[3]),
|
||||
"min_samples_leaf": int(v[4]),
|
||||
}
|
||||
Reference in New Issue
Block a user