"""
File: qumphy/uq.py
Project: 22HLT01 QUMPHY
Contact: oskar.pfeffer@ptb.de
Gitlab: https://gitlab.com/qumphy
Description: Uncertainty quantification utilities.
"""
from __future__ import annotations
import numpy as np
import typing
[docs]
def deep_ensemble(
models: typing.List,
data: np.ndarray,
weights: np.ndarray | None = None,
) -> np.ndarray:
"""Compute deep ensemble of the data using the given models.
Parameters
----------
models : list
List of callable models.
Expected to return type ``np.ndarray``.
data : np.ndarray
Input data.
weights : np.ndarray, optional
Weights for each model.
Returns
-------
np.ndarray
Weighted model output predictions.
Examples
--------
Compute unweighted deep ensemble prediction of two models on given data.
>>> model0 = lambda x : np.dot(np.zeros((1, 2)), x.T).reshape(-1, 1)
>>> model1 = lambda x : np.dot(np.ones((1, 2)), x.T).reshape(-1, 1)
>>> data = np.array([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
>>> deep_ensemble([model0, model1], data)
[[0. ]
[0.5]
[0.5]
[1. ]]
Compute weighted deep ensemble prediction of two models on given data.
>>> model0 = lambda x : np.dot(np.zeros((1, 2)), x.T).reshape(-1, 1)
>>> model1 = lambda x : np.dot(np.ones((1, 2)), x.T).reshape(-1, 1)
>>> data = np.array([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
>>> weights=np.array([0.0, 1.0])
>>> deep_ensemble([model0, model1], data)
[[0.]
[1.]
[1.]
[2.]]
"""
models_output = [model(data) for model in models]
assert np.all([isinstance(m, np.ndarray) for m in models_output])
assert np.all(models_output[0].shape == v.shape for v in models_output)
return np.average(np.array(models_output), axis=0, weights=weights)
[docs]
def deep_ensemble_gaussian(
prediction_mean: np.ndarray,
prediction_var: np.ndarray,
weights: np.ndarray | None = None,
) -> typing.Tuple[np.ndarray, np.ndarray]:
"""Compute deep ensemble using the given predictions.
Parameters
----------
prediction_mean: np.ndarray
Mean of the predicted gaussian distribution.
prediction_std: np.ndarray
Variance of the predicted gaussian distribution.
weights : np.ndarray, optional
Weights for each model.
Returns
-------
Tuple[np.ndarray, np.ndarray]
Weighted ensemble prediction mean and variance.
Examples
--------
Compute deep ensemble using the given predictions.
Shape of `prediction_mean` and `prediction_var` should be:
`[#models, #samples, #outputs]`
>>> prediction_mean_1 = np.array([[[0.0], [0.5]], [[0.5], [1.0]]])
>>> prediction_mean_2 = np.array([[[1.0], [0.5]], [[0.5], [1.0]]])
>>> prediction_var_1 = np.array([[[0.0], [1.0]], [[0.5], [1.0]]])
>>> prediction_var_2 = np.array([[[0.0], [0.0]], [[0.5], [1.0]]])
>>> prediction_mean = np.array([prediction_mean_1, prediction_mean_2])
>>> prediction_var = np.array([prediction_var_1, prediction_var_2])
>>> weights = np.array([0.25, 0.75])
>>> deep_ensemble_gaussian(prediction_mean, prediction_var, weights)
"""
if weights is None:
weights = np.ones(len(prediction_mean)) / len(prediction_mean)
ensemble_mean = np.average(prediction_mean, axis=0, weights=weights)
ensemble_var = (
np.average(prediction_var + prediction_mean**2, axis=0, weights=weights)
- ensemble_mean**2
)
ensemble_var_aleatoric = np.average(prediction_var, axis=0, weights=weights)
ensemble_var_epistemic = np.std(prediction_mean, axis=0)
return ensemble_mean, ensemble_var, ensemble_var_aleatoric, ensemble_var_epistemic