DeepEnsemble

Defined in fynance.models.uncertainty

class DeepEnsemble(factory, n_members=5, seed=0)[source]

Bases: object

Epistemic-uncertainty proxy from an ensemble of independent nets.

Trains n_members independent copies of a SignalModel-conforming net (built fresh by factory for each member) and aggregates their predictions. The member-to-member spreadpredict_std — is a proxy for epistemic uncertainty (how much the fit itself could have differed had training gone slightly differently), as distinct from the aleatoric noise in the data itself. See Lakshminarayanan, Pritzel & Blundell (2017), “Simple and Scalable Predictive Uncertainty Estimation using Deep Ensembles”.

Conforms to the SignalModel protocol (fit/predict): it drops into the harness like any other model, with predict_std / predict_members as the extra uncertainty API.

Parameters:
factorycallable

No-argument callable returning a fresh, unfit SignalModel-conforming net (e.g. a closure building a MultiLayerPerceptron and calling set_optimizer on it, or any object with fit(X, y) / predict(X)). Called once per member, per fit call.

n_membersint

Number of independently trained members. Default 5.

seedint

Base seed. Member i is built and trained under torch.manual_seed(seed + i) (and np.random.seed(seed + i)), so the ensemble as a whole is reproducible while members differ from one another.

See also

fynance.models.uncertainty.MCDropout
fynance.models.ensemble.StackingEnsemble

Notes

Determinism limits. fit reseeds the global PyTorch and NumPy generators immediately before building and training each member. This reproducibly diversifies members whose randomness (parameter initialization, dropout masks, data shuffling) is drawn from those global generators — which covers MultiLayerPerceptron and the other BaseNeuralNet subclasses. A member that instead draws from its own private torch.Generator (for example ObjectiveModel’s mini-batch shuffling, seeded from its own constructor seed argument rather than the global RNG) is unaffected by this reseed step: give such a factory a distinct explicit per-member seed if independent draws from that private generator are also required. Two full fit runs with the same DeepEnsemble.seed (and the same factory/data) are otherwise reproducible.

Examples

>>> import numpy as np
>>> import torch
>>> from fynance.models.mlp import MultiLayerPerceptron
>>> from fynance.models.uncertainty import DeepEnsemble
>>> rng = np.random.default_rng(0)
>>> X = rng.standard_normal((40, 2)).astype(np.float32)
>>> y = np.sin(X[:, :1]).astype(np.float32)
>>> def factory():
...     net = MultiLayerPerceptron(2, 1, layers=[8], activation=torch.nn.Tanh)
...     net.set_optimizer(torch.nn.MSELoss, torch.optim.Adam, lr=1e-2)
...     return net
>>> ens = DeepEnsemble(factory, n_members=3, seed=0).fit(X, y)
>>> ens.predict(X).shape
(40,)
>>> ens.predict_members(X).shape
(3, 40)
fit(X, y)[source]

Build and train n_members independent copies of factory().

Parameters:
X, yarray-like

Training data forwarded verbatim to each member’s fit(X, y).

Returns:
DeepEnsemble

self.

predict(X)[source]

Ensemble mean prediction, shape (T,).

predict_members(X)[source]

Per-member predictions, shape (n_members, T).

Parameters:
Xarray-like

Inputs, shape (T, N).

Returns:
numpy.ndarray

Stacked per-member predictions, shape (n_members, T).

Raises:
RuntimeError

If called before fit.

predict_std(X)[source]

Cross-member standard deviation, shape (T,) — epistemic proxy.