|
6 | 6 | User Guide |
7 | 7 | ========== |
8 | 8 |
|
9 | | -Estimator |
10 | | ---------- |
11 | | - |
12 | | -The central piece of transformer, regressor, and classifier is |
13 | | -:class:`sklearn.base.BaseEstimator`. All estimators in scikit-learn are derived |
14 | | -from this class. In more details, this base class enables to set and get |
15 | | -parameters of the estimator. It can be imported as:: |
16 | | - |
17 | | - >>> from sklearn.base import BaseEstimator |
18 | | - |
19 | | -Once imported, you can create a class which inherate from this base class:: |
20 | | - |
21 | | - >>> class MyOwnEstimator(BaseEstimator): |
22 | | - ... pass |
23 | | - |
24 | | -Transformer |
25 | | ------------ |
26 | | - |
27 | | -Transformers are scikit-learn estimators which implement a ``transform`` method. |
28 | | -The use case is the following: |
29 | | - |
30 | | -* at ``fit``, some parameters can be learned from ``X`` and ``y``; |
31 | | -* at ``transform``, `X` will be transformed, using the parameters learned |
32 | | - during ``fit``. |
33 | | - |
34 | | -.. _mixin: https://en.wikipedia.org/wiki/Mixin |
35 | | - |
36 | | -In addition, scikit-learn provides a |
37 | | -mixin_, i.e. :class:`sklearn.base.TransformerMixin`, which |
38 | | -implement the combination of ``fit`` and ``transform`` called ``fit_transform``. |
39 | | - |
40 | | -One can import the mixin class as:: |
41 | | - |
42 | | - >>> from sklearn.base import TransformerMixin |
43 | | - |
44 | | -Therefore, when creating a transformer, you need to create a class which |
45 | | -inherits from both :class:`sklearn.base.BaseEstimator` and |
46 | | -:class:`sklearn.base.TransformerMixin`. The scikit-learn API imposed ``fit`` to |
47 | | -**return ``self``**. The reason is that it allows to pipeline ``fit`` and |
48 | | -``transform`` imposed by the :class:`sklearn.base.TransformerMixin`. The |
49 | | -``fit`` method is expected to have ``X`` and ``y`` as inputs. Note that |
50 | | -``transform`` takes only ``X`` as input and is expected to return the |
51 | | -transformed version of ``X``:: |
52 | | - |
53 | | - >>> class MyOwnTransformer(TransformerMixin, BaseEstimator): |
54 | | - ... def fit(self, X, y=None): |
55 | | - ... return self |
56 | | - ... def transform(self, X): |
57 | | - ... return X |
58 | | - |
59 | | -We build a basic example to show that our :class:`MyOwnTransformer` is working |
60 | | -within a scikit-learn ``pipeline``:: |
61 | | - |
62 | | - >>> from sklearn.datasets import load_iris |
63 | | - >>> from sklearn.pipeline import make_pipeline |
64 | | - >>> from sklearn.linear_model import LogisticRegression |
65 | | - >>> X, y = load_iris(return_X_y=True) |
66 | | - >>> pipe = make_pipeline(MyOwnTransformer(), |
67 | | - ... LogisticRegression(random_state=10, |
68 | | - ... solver='lbfgs')) |
69 | | - >>> pipe.fit(X, y) # doctest: +ELLIPSIS |
70 | | - Pipeline(...) |
71 | | - >>> pipe.predict(X) # doctest: +ELLIPSIS |
72 | | - array([...]) |
73 | | - |
74 | | -Predictor |
75 | | ---------- |
76 | | - |
77 | | -Regressor |
78 | | -~~~~~~~~~ |
79 | | - |
80 | | -Similarly, regressors are scikit-learn estimators which implement a ``predict`` |
81 | | -method. The use case is the following: |
82 | | - |
83 | | -* at ``fit``, some parameters can be learned from ``X`` and ``y``; |
84 | | -* at ``predict``, predictions will be computed using ``X`` using the parameters |
85 | | - learned during ``fit``. |
86 | | - |
87 | | -In addition, scikit-learn provides a mixin_, i.e. |
88 | | -:class:`sklearn.base.RegressorMixin`, which implements the ``score`` method |
89 | | -which computes the :math:`R^2` score of the predictions. |
90 | | - |
91 | | -One can import the mixin as:: |
92 | | - |
93 | | - >>> from sklearn.base import RegressorMixin |
94 | | - |
95 | | -Therefore, we create a regressor, :class:`MyOwnRegressor` which inherits from |
96 | | -both :class:`sklearn.base.BaseEstimator` and |
97 | | -:class:`sklearn.base.RegressorMixin`. The method ``fit`` gets ``X`` and ``y`` |
98 | | -as input and should return ``self``. It should implement the ``predict`` |
99 | | -function which should output the predictions of your regressor:: |
100 | | - |
101 | | - >>> import numpy as np |
102 | | - >>> class MyOwnRegressor(RegressorMixin, BaseEstimator): |
103 | | - ... def fit(self, X, y): |
104 | | - ... return self |
105 | | - ... def predict(self, X): |
106 | | - ... return np.mean(X, axis=1) |
107 | | - |
108 | | -We illustrate that this regressor is working within a scikit-learn pipeline:: |
109 | | - |
110 | | - >>> from sklearn.datasets import load_diabetes |
111 | | - >>> X, y = load_diabetes(return_X_y=True) |
112 | | - >>> pipe = make_pipeline(MyOwnTransformer(), MyOwnRegressor()) |
113 | | - >>> pipe.fit(X, y) # doctest: +ELLIPSIS |
114 | | - Pipeline(...) |
115 | | - >>> pipe.predict(X) # doctest: +ELLIPSIS |
116 | | - array([...]) |
117 | | - |
118 | | -Since we inherit from the :class:`sklearn.base.RegressorMixin`, we can call |
119 | | -the ``score`` method which will return the :math:`R^2` score:: |
120 | | - |
121 | | - >>> pipe.score(X, y) # doctest: +ELLIPSIS |
122 | | - -3.9... |
123 | | - |
124 | | -Classifier |
125 | | -~~~~~~~~~~ |
126 | | - |
127 | | -Similarly to regressors, classifiers implement ``predict``. In addition, they |
128 | | -output the probabilities of the prediction using the ``predict_proba`` method: |
129 | | - |
130 | | -* at ``fit``, some parameters can be learned from ``X`` and ``y``; |
131 | | -* at ``predict``, predictions will be computed using ``X`` using the parameters |
132 | | - learned during ``fit``. The output corresponds to the predicted class for each sample; |
133 | | -* ``predict_proba`` will give a 2D matrix where each column corresponds to the |
134 | | - class and each entry will be the probability of the associated class. |
135 | | - |
136 | | -In addition, scikit-learn provides a mixin, i.e. |
137 | | -:class:`sklearn.base.ClassifierMixin`, which implements the ``score`` method |
138 | | -which computes the accuracy score of the predictions. |
139 | | - |
140 | | -One can import this mixin as:: |
141 | | - |
142 | | - >>> from sklearn.base import ClassifierMixin |
143 | | - |
144 | | -Therefore, we create a classifier, :class:`MyOwnClassifier` which inherits |
145 | | -from both :class:`slearn.base.BaseEstimator` and |
146 | | -:class:`sklearn.base.ClassifierMixin`. The method ``fit`` gets ``X`` and ``y`` |
147 | | -as input and should return ``self``. It should implement the ``predict`` |
148 | | -function which should output the class inferred by the classifier. |
149 | | -``predict_proba`` will output some probabilities instead:: |
150 | | - |
151 | | - >>> class MyOwnClassifier(ClassifierMixin, BaseEstimator): |
152 | | - ... def fit(self, X, y): |
153 | | - ... self.classes_ = np.unique(y) |
154 | | - ... return self |
155 | | - ... def predict(self, X): |
156 | | - ... return np.random.randint(0, self.classes_.size, |
157 | | - ... size=X.shape[0]) |
158 | | - ... def predict_proba(self, X): |
159 | | - ... pred = np.random.rand(X.shape[0], self.classes_.size) |
160 | | - ... return pred / np.sum(pred, axis=1)[:, np.newaxis] |
161 | | - |
162 | | -We illustrate that this regressor is working within a scikit-learn pipeline:: |
163 | | - |
164 | | - >>> X, y = load_iris(return_X_y=True) |
165 | | - >>> pipe = make_pipeline(MyOwnTransformer(), MyOwnClassifier()) |
166 | | - >>> pipe.fit(X, y) # doctest: +ELLIPSIS |
167 | | - Pipeline(...) |
168 | | - |
169 | | -Then, you can call ``predict`` and ``predict_proba``:: |
170 | | - |
171 | | - >>> pipe.predict(X) # doctest: +ELLIPSIS |
172 | | - array([...]) |
173 | | - >>> pipe.predict_proba(X) # doctest: +ELLIPSIS |
174 | | - array([...]) |
175 | | - |
176 | | -Since our classifier inherits from :class:`sklearn.base.ClassifierMixin`, we |
177 | | -can compute the accuracy by calling the ``score`` method:: |
178 | | - |
179 | | - >>> pipe.score(X, y) # doctest: +ELLIPSIS |
180 | | - 0... |
| 9 | +This guide focuses on the pieces that are specific to ``bde``. If you are new to |
| 10 | +scikit-learn's estimator API, refer to the official `developer guide |
| 11 | +<https://scikit-learn.org/stable/developers/develop.html>`__ for the foundational |
| 12 | +concepts. The sections below assume that background and concentrate on how |
| 13 | +``BdeRegressor`` and ``BdeClassifier`` behave, how they integrate with JAX, and how |
| 14 | +you should prepare data to get reliable results. |
| 15 | + |
| 16 | +Estimator overview |
| 17 | +------------------ |
| 18 | + |
| 19 | +``bde`` exposes two scikit-learn compatible estimators: |
| 20 | + |
| 21 | +* :class:`bde.BdeRegressor` for continuous targets. |
| 22 | +* :class:`bde.BdeClassifier` for categorical targets. |
| 23 | + |
| 24 | +Both inherit :class:`sklearn.base.BaseEstimator` and the relevant mixins, so they |
| 25 | +support the familiar ``fit``/``predict``/``score`` methods, accept keyword |
| 26 | +hyperparameters in ``__init__``, and can be dropped into a |
| 27 | +:class:`sklearn.pipeline.Pipeline`. Under the hood they train a fully connected |
| 28 | +ensemble in JAX and then run an MCMC sampler to draw posterior weight samples. At |
| 29 | +prediction time the estimator combines those samples to provide means, standard |
| 30 | +deviations, credible intervals, probability vectors, or the raw ensemble outputs. |
| 31 | + |
| 32 | +Data preparation |
| 33 | +---------------- |
| 34 | + |
| 35 | +Bayesian deep ensembles are sensitive to feature and target scale because the |
| 36 | +networks are initialised with zero-mean weights and the prior assumes unit-scale |
| 37 | +activations. Large raw targets (for instance the default output of |
| 38 | +:func:`sklearn.datasets.make_regression`) can lead to very poor fits if left |
| 39 | +unscaled. Always apply basic preprocessing before calling ``fit``: |
| 40 | + |
| 41 | +Understanding the outputs |
| 42 | +------------------------- |
| 43 | + |
| 44 | +The estimators expose several prediction modes: |
| 45 | + |
| 46 | +``predict(X)`` |
| 47 | + Returns the mean prediction (regression) or hard labels (classification). |
| 48 | +``predict(X, mean_and_std=True)`` |
| 49 | + Regression only; returns a tuple ``(mean, std)`` where ``std`` combines |
| 50 | + aleatoric and epistemic components. |
| 51 | +``predict(X, credible_intervals=[0.9, 0.95])`` |
| 52 | + Regression only; returns ``(mean, intervals)`` with quantiles over posterior |
| 53 | + samples. |
| 54 | +``predict(X, raw=True)`` |
| 55 | + Returns the raw tensor with leading axes ``(ensemble_members, samples, n, |
| 56 | + output_dims)``. Useful for custom diagnostics. |
| 57 | +``predict_proba(X)`` |
| 58 | + Classification only; returns class probability vectors. |
| 59 | + |
| 60 | + |
| 61 | +Key hyperparameters |
| 62 | +------------------- |
| 63 | + |
| 64 | +``n_members`` |
| 65 | + Number of deterministic networks in the ensemble. Increasing members improves |
| 66 | + epistemic uncertainty estimation but raises computational cost. |
| 67 | +``hidden_layers`` |
| 68 | + Widths of hidden layers. Defaults internally to ``[4, 4]`` if ``None``. |
| 69 | +``epochs`` / ``patience`` |
| 70 | + Control training duration. ``epochs`` sets the maximum number of epochs, |
| 71 | + while ``patience`` enables early stopping. When ``patience`` is ``None``, |
| 72 | + training always runs for all epochs. |
| 73 | +``lr`` |
| 74 | + Learning rate for the Adam optimiser during pre-sampling training. |
| 75 | +``warmup_steps`` / ``n_samples`` / ``n_thinning`` |
| 76 | + Control the MCMC sampling stage. ``warmup_steps`` adjusts the step size, |
| 77 | + ``n_samples`` defines the number of retained posterior draws, and |
| 78 | + ``n_thinning`` specifies the interval between saved samples. |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | +Sampler and builder internals |
| 83 | +----------------------------- |
| 84 | + |
| 85 | +After the deterministic training phase ``BdeRegressor`` and ``BdeClassifier`` |
| 86 | +construct a :class:`bde.bde_builder.BdeBuilder` instance. This helper manages the |
| 87 | +ensemble members, coordinates parallel training across devices, and hands off to |
| 88 | +``bde.sampler`` utilities for warmup and sampling. Advanced users can interact |
| 89 | +with these pieces directly: |
| 90 | + |
| 91 | +* ``estimator._bde`` references the builder after ``fit`` and exposes the |
| 92 | + deterministic members and training history. |
| 93 | +* ``estimator.positions_eT_`` stores the weight samples with shape ``(E, T, ...)``. |
| 94 | +* Warmup behaviour can be tuned via ``desired_energy_var_start`` and |
| 95 | + ``desired_energy_var_end``. |
| 96 | + |
| 97 | +Generally you should rely on the high-level estimator API, but the internals are |
| 98 | +accessible for custom diagnostics or research experiments. |
| 99 | + |
| 100 | +Accelerators and environment |
| 101 | +---------------------------- |
| 102 | + |
| 103 | +The estimators run on whatever backend JAX initialises. On CPU-only machines you |
| 104 | +must set ``XLA_FLAGS="--xla_force_host_platform_device_count=<n>"`` to allocate |
| 105 | +several virtual devices for the ensemble. On GPU- or TPU-enabled hardware JAX |
| 106 | +picks up devices automatically; ensure your environment includes the matching |
| 107 | +``jaxlib`` build. The repository ships with a ``pixi.toml`` that pins compatible |
| 108 | +versions and provides tasks such as ``pixi run test`` and ``pixi run build-doc``. |
| 109 | + |
| 110 | +Where to next |
| 111 | +------------- |
| 112 | + |
| 113 | +* The :ref:`quick_start` page shows condensed scripts you can run end to end. |
| 114 | +* :ref:`api` documents every public class and helper in the package. |
| 115 | +* :ref:`general_examples` renders notebooks and plots that mirror the examples |
| 116 | + in the ``examples/`` directory. |
0 commit comments