mojo-tabular / train /tabular_tune.py
lee101's picture
mojo-tabular: tiny CPU tabular classifiers
d5ef81d verified
Raw History Blame Contribute Delete
4.7 kB
import argparse, hashlib, json, time
from pathlib import Path
import joblib, numpy as np
from sklearn.ensemble import HistGradientBoostingClassifier, ExtraTreesClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from tabular_bench import DATA, SEED, load, prep
TRIALS = 16
GB_TRIALS = 6
def gbdt(rng):
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier
for _ in range(GB_TRIALS):
lr = float(10 ** rng.uniform(-2, -0.7)); n = int(rng.integers(150, 600)); d = int(rng.integers(3, 9))
yield 'xgb', dict(learning_rate=lr, n_estimators=n, max_depth=d), XGBClassifier(
learning_rate=lr, n_estimators=n, max_depth=d, subsample=0.8, colsample_bytree=0.8, n_jobs=2, random_state=SEED, verbosity=0)
yield 'lgbm', dict(learning_rate=lr, n_estimators=n, num_leaves=2 ** d), LGBMClassifier(
learning_rate=lr, n_estimators=n, num_leaves=2 ** d, subsample=0.8, subsample_freq=1, colsample_bytree=0.8, n_jobs=2, random_state=SEED, verbose=-1)
def hgb(rng):
return dict(learning_rate=float(10 ** rng.uniform(-2, -0.7)), max_iter=int(rng.integers(100, 600)),
max_leaf_nodes=int(rng.integers(8, 64)), min_samples_leaf=int(rng.integers(5, 60)),
l2_regularization=float(10 ** rng.uniform(-3, 1)), max_features=float(rng.uniform(0.5, 1.0)))
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--output', type=Path, required=True)
ap.add_argument('--datasets', nargs='*')
ap.add_argument('--gbdt', action='store_true')
ap.add_argument('--artifacts', type=Path, required=True)
a = ap.parse_args()
man = json.loads((DATA / 'manifest.json').read_text())
out = dict(kind='tuned_ensemble', seed=SEED, metric='roc_auc', trials=TRIALS,
source_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), datasets={})
a.artifacts.mkdir(parents=True, exist_ok=True)
for name, m in man.items():
if a.datasets and name not in a.datasets:
continue
X, y = load(name, m['target'])
if y is None:
continue
idx = np.arange(len(y))
tr, rest = train_test_split(idx, test_size=0.4, stratify=y, random_state=SEED)
va, te = train_test_split(rest, test_size=0.5, stratify=y[rest], random_state=SEED)
rng = np.random.default_rng(SEED)
t0 = time.monotonic()
cands = []
for i in range(TRIALS):
p = hgb(rng)
mdl = make_pipeline(prep(X), HistGradientBoostingClassifier(early_stopping=False, random_state=SEED, **p)).fit(X.iloc[tr], y[tr])
cands.append(('hgb', p, mdl, mdl.predict_proba(X.iloc[va])[:, 1]))
for c in (0.1, 1.0):
mdl = make_pipeline(prep(X), LogisticRegression(C=c, max_iter=500)).fit(X.iloc[tr], y[tr])
cands.append(('logreg', dict(C=c), mdl, mdl.predict_proba(X.iloc[va])[:, 1]))
mdl = make_pipeline(prep(X), ExtraTreesClassifier(300, min_samples_leaf=2, n_jobs=2, random_state=SEED)).fit(X.iloc[tr], y[tr])
cands.append(('et', {}, mdl, mdl.predict_proba(X.iloc[va])[:, 1]))
if a.gbdt:
for kind, p, est in gbdt(np.random.default_rng(SEED + 1)):
mdl = make_pipeline(prep(X), est).fit(X.iloc[tr], y[tr])
cands.append((kind, p, mdl, mdl.predict_proba(X.iloc[va])[:, 1]))
aucs = [roc_auc_score(y[va], c[3]) for c in cands]
order = np.argsort(aucs)[::-1]
top = [cands[i] for i in order[:3]]
ens_va = np.mean([c[3] for c in top], axis=0)
ens_auc = roc_auc_score(y[va], ens_va)
single = cands[order[0]]
use_ens = ens_auc > aucs[order[0]]
members = top if use_ens else [single]
pred = lambda i: np.mean([c[2].predict_proba(X.iloc[i])[:, 1] for c in members], axis=0)
path = a.artifacts / f'{name}.joblib'
joblib.dump(dict(members=[c[2] for c in members], columns=list(X.columns)), path, compress=3)
out['datasets'][name] = dict(
val_auc=round(max(ens_auc, aucs[order[0]]), 4), test_auc=round(roc_auc_score(y[te], pred(te)), 4),
ensemble=use_ens, best_single_kind=single[0], members=[dict(kind=c[0], params=c[1]) for c in members],
artifact_bytes=path.stat().st_size, artifact_sha256=hashlib.sha256(path.read_bytes()).hexdigest(),
search_s=round(time.monotonic() - t0, 1))
print(name, out['datasets'][name], flush=True)
a.output.write_text(json.dumps(out, indent=2) + '\n')
if __name__ == '__main__':
main()