Backends
A backend is a small class in quibble/backends.py with two methods: fit(Xtr, ytr, task, seed, cfg, device) and predict(X). predict returns class probabilities (n, k) for classification and a
vector (n,) for regression. The table below lists registered backends. --backends all
selects all public backends for a sweep; it is not a list-only command and excludes weak.
When CUDA is selected or Auto resolves to CUDA, incompatible methods are visibly
excluded. CPU runs keep all selected methods. No algorithm is silently substituted.
| Name | Library | Tasks | GPU | Native categoricals | Tested |
|---|---|---|---|---|---|
histgb |
scikit-learn HistGradientBoosting | both | no | yes | yes (CPU) |
lightgbm |
LightGBM | both | no (CPU adapter) | yes | yes (CPU) |
catboost |
CatBoost | both | yes | yes | CPU both tasks; local CUDA classification smoke |
tabpfn |
TabPFN | both | yes | no (ordinal) | refusal logic only |
tabfm |
Google Research TabFM (PyTorch) | both | yes | yes | CPU both tasks; local CUDA Wine classification fits |
kumo_tabular |
NVIDIA structured-data-models | both | yes | yes | CPU both tasks; local CUDA Wine classification smoke fits |
autogluon |
AutoGluon Tabular | both | excluded from GPU-only runs | own preprocessing | CPU, one dataset per task |
weak |
HistGB, handicapped | both | no | yes | yes (internal) |
All backends use library defaults except where noted. quibble is not a tuner.
Install requirements.txt into this repository's .venv using the
local setup. It includes the
libraries below; separate backend installations are not needed.
histgb
HistGradientBoostingClassifier / Regressor with random_state=seed. In native mode it uses
categorical_features="from_dtype". On training sets under about 10,000 rows the defaults involve no
randomness, so different model seeds give identical predictions; above that, scikit-learn's automatic
early stopping uses a random validation split and the seed starts to matter.
lightgbm
LGBMClassifier / Regressor with random_state=seed, verbose=-1, n_jobs from the thread
setting. With default settings (no bagging or feature subsampling) predictions do not depend on the
seed. This adapter uses LightGBM's CPU backend and is excluded from CUDA runs.
LightGBM has GPU backends, but they need a compatible build and adapter configuration;
its CUDA backend is not supported on Windows. The model is not silently replaced.
catboost
CatBoostClassifier / Regressor with random_seed=seed, verbose=0, allow_writing_files=False,
thread_count from the thread setting, and task_type="GPU" when the device is cuda. In native
mode categorical columns are passed as strings (missing values become the string __nan__). The GPU
path has passed a small local classification smoke run. This does not establish
GPU regression, large-data support, or general device coverage.
tabpfn
TabPFNClassifier / Regressor (a pretrained tabular foundation model) with device and
random_state=seed. quibble refuses (records a refusal, not an error) when:
- training rows exceed the limit:
QUIBBLE_TABPFN_MAX_ROWS, default 10,000 oncudaand 1,000 oncpu(Quibble's conservative policy limits, not verified maximum capabilities of the installed TabPFN release), - there are more than 500 features,
- a classification target has more than 10 classes.
Notes:
tabpfnis included in the requirements. Model weights are downloaded on first use and may need network access and a Hugging Face account or token, depending on the TabPFN release.- It always receives ordinal-encoded categoricals.
- TabPFN predicts by conditioning on the training set, so single-row latency is expected to be much higher than for tree models. That is a genuine cost of the approach and the benchmark will show it.
- Only the refusal logic is covered by tests; the fit/predict path has not been run.
tabfm
The requirements select tabfm[pytorch] on Python 3.11+, not the JAX backend.
The PyTorch loader receives the resolved cpu or
cuda device explicitly; the estimator receives the model seed as random_state.
QUIBBLE_TABFM_N_ESTIMATORS controls ensemble size (default 4).
Like the tree adapters, TabFM receives ordinal codes by default and pandas categoricals
when QUIBBLE_PREPROCESS=native. It then applies its own model preprocessing.
Ordinary Quibble runs do not downsample the training split; explicit smoke checks do.
Large datasets and repeated single-row inference can therefore be slow on CPU.
Pretrained weights download from Hugging Face on first use and are subject to their own
terms. Quibble sets the local Hugging Face cache automatically; use QUIBBLE_HF_CACHE_DIR
to override it rather than an inherited HF_HOME. See sources and destinations.
The loader's in-process model cache is disabled; sweep processes remain isolated.
kumo_tabular
Included in the requirements on Python 3.11+. The distribution is
structured-data-models; its import is sdm. sdm.models.KumoTabular receives the
requested device and QUIBBLE_KUMO_SIZE (small, medium, or large; default small).
QUIBBLE_KUMO_N_ESTIMATORS controls ensemble size (default 4). A device-local PyTorch
generator seeded with the model seed controls the fit's stochastic preprocessing.
Features use Quibble's training-fitted ordinal or native preprocessing, then convert to
sdm.TableTensor. The training feature schema is reused for every prediction. Classification
targets are explicitly categorical; regression targets are numerical. Class probability
columns are reordered to the encoded target's class order, including multiclass targets.
Missing, duplicate, nonfinite, or invalid probability outputs raise an error.
Regression returns the mean of all 999 equally spaced predicted quantiles, q001 through
q999, as an approximation to the predictive mean. It does not use the first quantile or
the median. Missing, duplicate, unexpected, or nonfinite quantiles raise an error.
The model's inference cache is cleared after each run, including failed fits.
Weights download on first use and reuse the configured local Hugging Face cache. Full training splits are retained outside explicit smoke checks. Local CUDA Wine classification smoke records are available; GPU regression and large-dataset behavior remain unverified.
Example comparison after installing the requirements:
quibble run --sweep --backends lightgbm,tabfm,kumo_tabular --datasets breast_cancer --cv 3x2 --model-seeds 0 --device cpu --jobs 1 --out results_cpu
quibble report --out results_cpu --swap lightgbm:tabfm --swap lightgbm:kumo_tabular
Changing the TabFM ensemble count or Kumo size/ensemble count warns before reusing saved
results. Use --force or a different --out for each configuration.
Missing optional packages are recorded as run errors; they do not break the core demo.
autogluon
TabularPredictor with preset QUIBBLE_AG_PRESET (default medium_quality) and time limit
QUIBBLE_AG_TIME_LIMIT (default 120 s, held fixed across datasets), num_gpus=0 on CPU, and
num_cpus from the thread setting. Regression uses problem_type="regression"; classification is
inferred. It does its own preprocessing, so QUIBBLE_PREPROCESS does not change it (recorded as
preprocess_effective: native).
Notes:
- The seed is not controlled, so repeated runs can differ.
- Training uses up to the time limit, so
fit_sis dominated by that budget. A sweep of 12 datasets x 10 splits at 120 s is about 4 hours for a single model seed. - Each run trains into a temporary directory that is deleted afterwards. With
--keep-models, its contents are first copied to a unique retained-fit directory. The internal validation leaderboard is saved even without retention; see reports. - AutoGluon and its model families constrain their dependencies. Install the complete
requirements together, then run
python -m pip checkandpython -m quibble doctor. - GPU-only runs exclude AutoGluon because a GPU budget does not prevent CPU model families.
- It is tested on CPU on one classification and one regression dataset.
weak (internal)
HistGB limited to max_iter=2, learning_rate=0.05. It exists so that a swap to a clearly worse model
must fail; the test suite and the demo rely on that. It is hidden from --backends all.
Adding a backend
Add a class to quibble/backends.py and include it in the class tuple used to construct
the BACKENDS dictionary at the bottom of the file. The shortest path subclasses _SK,
which handles categorical preprocessing for you:
class MyModel(_SK):
name, native_ok, gpu = "mymodel", False, False # native_ok: supports QUIBBLE_PREPROCESS=native
def make(self, task, seed, cfg, device, cat_idx):
from mylib import Clf, Reg # import inside make(): optional dependency
return (Clf if task == "classification" else Reg)(random_state=seed)
Rules:
- Import optional libraries inside
make()/fit(). A missing package is recorded as an error result (ModuleNotFoundError) rather than breaking everything else. - Raise
Refused("reason")for unsupported sizes or tasks. Refusals appear in the report's failure matrix. - Set
gpu = Trueonly if the backend really uses the device argument; this controls scheduling. - For full control (as
AutoGluondoes), subclassBackendand implementfit,predict, and optionallyclose()for cleanup. Put effective settings you want recorded inself.info. - Add a test in
tests/test_backends_run.pyand a row to the table above.
Hosted services
A backend that sends data to a third-party API is not implemented. If you add one, make it
refuse non-public datasets by default, never log credentials, and record the service version in
self.info.