Skip to content

Commit

Permalink
feat: add optimisation for hyperparams to the contrast limit class w …
Browse files Browse the repository at this point in the history
…test
  • Loading branch information
seankmartin committed Oct 14, 2024
1 parent 05ab24d commit 17a311b
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
62 changes: 62 additions & 0 deletions cryoet_data_portal_neuroglancer/precompute/contrast_limits.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from sklearn.cluster import KMeans
from sklearn.mixture import GaussianMixture

from cryoet_data_portal_neuroglancer.utils import ParameterOptimizer

LOGGER = logging.getLogger(__name__)


Expand All @@ -27,6 +29,10 @@ def wrapper(*args, **kwargs):
return wrapper


def euclidean_distance(x: tuple[float, float], y: tuple[float, float]) -> float:
return np.sqrt(((x[0] - y[0]) ** 2) + ((x[1] - y[1]) ** 2))


# TODO fix this to work with dask data, see the mesh changes for reference
def _restrict_volume_around_central_z_slice(
volume: "np.ndarray",
Expand Down Expand Up @@ -177,6 +183,62 @@ def compute_contrast_limit(
except AttributeError:
return low_value, high_value

def optimize(self, real_limits, max_evals=100, loss_threshold=None, **kwargs) -> dict:
"""Hyper-parameter optimisation.
Sub-classes should implement the
_objective_function and _define_parameter_space methods.
Parameters
----------
params: dict
The parameters for the optimisation.
Keys are "low_percentile" and "high_percentile".
real_limits: tuple[float, float]
The real contrast limits.
max_evals: int, optional.
The maximum number of evaluations.
By default 100.
loss_threshold: float, optional.
The loss threshold.
By default None.
**kwargs
Additional keyword arguments - passed to hyperopt fmin.
Returns
-------
tuple[float, float]
The best parameters found.
"""

def _objective(params):
return self._objective_function(params, real_limits)

parameter_optimizer = ParameterOptimizer(_objective)
self._define_parameter_space(parameter_optimizer)
return parameter_optimizer.optimize(
max_evals=max_evals,
loss_threshold=loss_threshold,
**kwargs,
)

def _objective_function(self, params, real_limits):
return euclidean_distance(
self.compute_contrast_limit(
params["low_percentile"],
params["high_percentile"],
),
real_limits,
)

def _define_parameter_space(self, parameter_optimizer: ParameterOptimizer):
parameter_optimizer.space_creator(
{
"low_percentile": {"type": "randint", "args": [0, 50]},
"high_percentile": {"type": "randint", "args": [51, 100]},
},
)

@compute_with_timer
def contrast_limits_from_mean(
self,
Expand Down
13 changes: 10 additions & 3 deletions cryoet_data_portal_neuroglancer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,17 @@ def params_to_hyperopt(key, params):

space = {key: params_to_hyperopt(key, value) for key, value in parameter_dict.items()}
self.set_space(space)

def set_space(self, space):
self.space = space

def optimize(self, max_evals=100):
best = fmin(self.objective, self.space, algo=tpe.suggest, max_evals=max_evals)
def optimize(self, max_evals=100, loss_threshold=None, **kwargs) -> dict:
best = fmin(
self.objective,
self.space,
algo=tpe.suggest,
max_evals=max_evals,
loss_threshold=loss_threshold,
**kwargs,
)
return best
10 changes: 10 additions & 0 deletions tests/test_contrast_limits.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ def test_percentile_contrast_limits():
assert limits == (0.0, 99.0)


def test_hyperparameter_optimization():
data = np.arange(100).reshape(10, 5, 2)
calculator = ContrastLimitCalculator(data)
real_limits = (5, 95)

result = calculator.optimize(real_limits, max_evals=2500, loss_threshold=0.1)
assert result["low_percentile"] == 5
assert result["high_percentile"] == 96


def test_mean_rms_contrast_limits():
data = np.arange(100).reshape(10, 5, 2)

Expand Down

0 comments on commit 17a311b

Please sign in to comment.