diff --git a/examples/solver_tutorial.py b/examples/solver_tutorial.py deleted file mode 100644 index cac378d..0000000 --- a/examples/solver_tutorial.py +++ /dev/null @@ -1,124 +0,0 @@ -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -import os -import sys -from hyppopy.HyppopyProject import HyppopyProject -from hyppopy.solver.HyperoptSolver import HyperoptSolver -from hyppopy.solver.OptunitySolver import OptunitySolver -from hyppopy.solver.RandomsearchSolver import RandomsearchSolver -from hyppopy.solver.GridsearchSolver import GridsearchSolver -from hyppopy.BlackboxFunction import BlackboxFunction - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) - -from sklearn.svm import SVC -from sklearn.datasets import load_iris -from sklearn.model_selection import cross_val_score - - -config = { -"hyperparameter": { - "C": { - "domain": "uniform", - "data": [0.0001, 20], - "type": "float" - }, - "gamma": { - "domain": "uniform", - "data": [0.0001, 20.0], - "type": "float" - }, - "kernel": { - "domain": "categorical", - "data": ["linear", "sigmoid", "poly", "rbf"], - "type": "str" - }, - "decision_function_shape": { - "domain": "categorical", - "data": ["ovo", "ovr"], - "type": "str" - } -}, -"settings": { - "solver": { - "max_iterations": 300 - }, - "custom": { - "use_solver": "hyperopt" - } -}} - -project = HyppopyProject(config=config) - -print("--------------------------------------------------------------") -print("max_iterations:\t{}".format(project.solver_max_iterations)) -print("plugin:\t{}".format(project.custom_use_solver)) - - -def my_loss_function(data, params): - clf = SVC(**params) - return -cross_val_score(estimator=clf, X=data[0], y=data[1], cv=3).mean() - - -def my_dataloader_function(**kwargs): - print("Dataloading...") - # kwargs['params'] allows accessing additional parameter passed, see below my_preproc_param, my_dataloader_input. - print("my loading argument: {}".format(kwargs['params']['my_dataloader_input'])) - iris_data = load_iris() - return [iris_data.data, iris_data.target] - - -def my_preprocess_function(**kwargs): - print("Preprocessing...") - # kwargs['data'] allows accessing the input data - print("data:", kwargs['data'][0].shape, kwargs['data'][1].shape) - # kwargs['params'] allows accessing additional parameter passed, see below my_preproc_param, my_dataloader_input. - print("kwargs['params']['my_preproc_param']={}".format(kwargs['params']['my_preproc_param']), "\n") - # if the preprocessing function returns something, - # the input data will be replaced with the data returned by this function. - x = kwargs['data'][0] - y = kwargs['data'][1] - for i in range(x.shape[0]): - x[i, :] += kwargs['params']['my_preproc_param'] - return [x, y] - - -def my_callback_function(**kwargs): - print("\r{}".format(kwargs), end="") - - -blackbox = BlackboxFunction(blackbox_func=my_loss_function, - dataloader_func=my_dataloader_function, - preprocess_func=my_preprocess_function, - callback_func=my_callback_function, - #data=input_data, # data can be set directly or via a dataloader function - my_preproc_param=1, - my_dataloader_input='could/be/a/path') - - -if project.custom_use_solver == "hyperopt": - solver = HyperoptSolver(project) -elif project.custom_use_solver == "optunity": - solver = OptunitySolver(project) -elif project.custom_use_solver == "randomsearch": - solver = RandomsearchSolver(project) -elif project.custom_use_solver == "gridsearch": - solver = GridsearchSolver(project) - -if solver is not None: - solver.blackbox = blackbox -solver.run() -df, best = solver.get_results() \ No newline at end of file diff --git a/examples/tutorial_gridsearch.py b/examples/tutorial_gridsearch.py new file mode 100644 index 0000000..bf55ad4 --- /dev/null +++ b/examples/tutorial_gridsearch.py @@ -0,0 +1,116 @@ +# In this tutorial we solve an optimization problem using the GridsearchSolver +# Gridsearch is very inefficient a Randomsearch might most of the time be the +# better choice. + +# import the HyppopyProject class keeping track of inputs +from hyppopy.HyppopyProject import HyppopyProject + +# import the GridsearchSolver classes +from hyppopy.solver.GridsearchSolver import GridsearchSolver + +# import the Blackboxfunction class wrapping your problem for Hyppopy +from hyppopy.BlackboxFunction import BlackboxFunction + +# To configure the GridsearchSolver we only need the hyperparameter section. Another +# difference to the other solvers is that we need to define a gridsampling in addition +# to the range: 'data': [0, 1, 100] which means sampling the space from 0 to 1 in 100 +# intervals. Gridsearch also supports categorical, uniform, normal and lognormal sampling +config = { +"hyperparameter": { + "C": { + "domain": "uniform", + "data": [0.0001, 20, 20], + "type": "float" + }, + "gamma": { + "domain": "uniform", + "data": [0.0001, 20.0, 20], + "type": "float" + }, + "kernel": { + "domain": "categorical", + "data": ["linear", "sigmoid", "poly", "rbf"], + "type": "str" + } +}, +"settings": { + "solver": {}, + "custom": {} +}} + +# When creating a HyppopyProject instance we +# pass the config dictionary to the constructor. +project = HyppopyProject(config=config) + +# Hyppopy offers a class called BlackboxFunction to wrap your problem for Hyppopy. +# The function signature is as follows: +# BlackboxFunction(blackbox_func=None, +# dataloader_func=None, +# preprocess_func=None, +# callback_func=None, +# data=None, +# **kwargs) +# +# Means we can set a couple of function pointers, a data object and an arbitrary number of custom parameter via kwargs. +# +# - blackbox_func: a function pointer to the actual, user defined, blackbox function that is computing our loss +# - dataloader_func: a function pointer to a function handling the dataloading +# - preprocess_func: a function pointer to a function automatically executed before starting the optimization process +# - callback_func: a function pointer to a function that is called after each iteration with the trail object as input +# - data: setting data can be done via dataloader_func or directly +# - kwargs are passed to all functions above and thus can be used for parameter sharing between the functions +# +# (more details see in the documentation) +# +# Below we demonstrate the usage of all the above by defining a my_dataloader_function which in fact only grabs the +# iris dataset from sklearn and returns it. A my_preprocess_function which also does nothing useful here but +# demonstrating that a custom parameter can be set via kwargs and used in all of our functions when called within +# Hyppopy. The my_callback_function gets as input the dictionary containing the state of the iteration and thus can be +# used to access the current state of each solver iteration. Finally we define the actual loss_function +# my_loss_function, which gets as input a data object and params. Both parameter are fixed, the first is defined by +# the user depending on what is dataloader returns or the data object set in the constructor, the second is a dictionary +# with a sample of your hyperparameter space which content is in the choice of the solver. + +from sklearn.svm import SVC +from sklearn.datasets import load_iris +from sklearn.model_selection import cross_val_score + + +def my_dataloader_function(**kwargs): + print("Dataloading...") + iris_data = load_iris() + return [iris_data.data, iris_data.target] + + +def my_callback_function(**kwargs): + print("\r{}".format(kwargs), end="") + + +def my_loss_function(data, params): + clf = SVC(**params) + return -cross_val_score(estimator=clf, X=data[0], y=data[1], cv=3).mean() + + +# We now create the BlackboxFunction object and pass all function pointers defined above, +# as well as 2 dummy parameter (my_preproc_param, my_dataloader_input) for demonstration purposes. +blackbox = BlackboxFunction(blackbox_func=my_loss_function, + dataloader_func=my_dataloader_function, + callback_func=my_callback_function) + + +# create a solver instance +solver = GridsearchSolver(project) +# pass the loss function to the solver +solver.blackbox = blackbox +# run the solver +solver.run() +# get the result via get_result() which returns a pandas dataframe +# containing the complete history and a dict best containing the +# best parameter set. +df, best = solver.get_results() + +print("\n") +print("*"*100) +print("Best Parameter Set:\n{}".format(best)) +print("*"*100) + diff --git a/examples/solver_tutorial_II_hyppopy.py b/examples/tutorial_multisolver.py similarity index 96% rename from examples/solver_tutorial_II_hyppopy.py rename to examples/tutorial_multisolver.py index 49bfdc5..2cf6196 100644 --- a/examples/solver_tutorial_II_hyppopy.py +++ b/examples/tutorial_multisolver.py @@ -1,184 +1,182 @@ # In this tutorial we solve an optimization problem using the Hyperopt Solver (http://hyperopt.github.io/hyperopt/). # Hyperopt uses a Baysian - Tree Parzen Estimator - Optimization approach, which means that each iteration computes a # new function value of the blackbox, interpolates a guess for the whole energy function and predicts a point to # compute the next function value at. This next point is not necessarily a "better" value, it's only the value with # the highest uncertainty for the function interpolation. # # See a visual explanation e.g. here (http://philipperemy.github.io/visualization/) # import the HyppopyProject class keeping track of inputs from hyppopy.HyppopyProject import HyppopyProject -# import the HyperoptSolver classes +# import the HyoppopySolver classes from hyppopy.solver.HyperoptSolver import HyperoptSolver from hyppopy.solver.OptunitySolver import OptunitySolver from hyppopy.solver.RandomsearchSolver import RandomsearchSolver -from hyppopy.solver.GridsearchSolver import GridsearchSolver # import the Blackboxfunction class wrapping your problem for Hyppopy from hyppopy.BlackboxFunction import BlackboxFunction # Next step is defining the problem space and all settings Hyppopy needs to optimize your problem. # The config is a simple nested dictionary with two obligatory main sections, hyperparameter and settings. # The hyperparameter section defines your searchspace. Each hyperparameter is again a dictionary with: # # - a domain ['categorical', 'uniform', 'normal', 'loguniform'] # - the domain data [left bound, right bound] and # - a type of your domain ['str', 'int', 'float'] # # The settings section has two subcategories, solver and custom. The first contains settings for the solver, # here 'max_iterations' - is the maximum number of iteration. # # The custom section allows defining custom parameter. An entry here is transformed to a member variable of the # HyppopyProject class. These can be useful when implementing new solver classes or for control your hyppopy script. # Here we use it as a solver switch to control the usage of our solver via the config. This means with the script # below your can try out every solver by changing use_solver to 'optunity', 'randomsearch', 'gridsearch',... # It can be used like so: project.custom_use_plugin (see below) If using the gridsearch solver, max_iterations is # ignored, instead each hyperparameter must specifiy a number of samples additionally to the range like so: # 'data': [0, 1, 100] which means sampling the space from 0 to 1 in 100 intervals. config = { "hyperparameter": { "C": { "domain": "uniform", "data": [0.0001, 20], "type": "float" }, "gamma": { "domain": "uniform", "data": [0.0001, 20.0], "type": "float" }, "kernel": { "domain": "categorical", "data": ["linear", "sigmoid", "poly", "rbf"], "type": "str" }, "decision_function_shape": { "domain": "categorical", "data": ["ovo", "ovr"], "type": "str" } }, "settings": { "solver": { "max_iterations": 300 }, "custom": { "use_solver": "hyperopt" } }} -# When creating a HyppopyProject instance we pass the config dictionary to the constructor. +# When creating a HyppopyProject instance we +# pass the config dictionary to the constructor. project = HyppopyProject(config=config) # demonstration of the custom parameter access print("-"*30) print("max_iterations:\t{}".format(project.solver_max_iterations)) print("solver chosen -> {}".format(project.custom_use_solver)) print("-"*30) # Hyppopy offers a class called BlackboxFunction to wrap your problem for Hyppopy. # The function signature is as follows: # BlackboxFunction(blackbox_func=None, # dataloader_func=None, # preprocess_func=None, # callback_func=None, # data=None, # **kwargs) # # Means we can set a couple of function pointers, a data object and an arbitrary number of custom parameter via kwargs. # # - blackbox_func: a function pointer to the actual, user defined, blackbox function that is computing our loss # - dataloader_func: a function pointer to a function handling the dataloading # - preprocess_func: a function pointer to a function automatically executed before starting the optimization process # - callback_func: a function pointer to a function that is called after each iteration with the trail object as input # - data: setting data can be done via dataloader_func or directly # - kwargs are passed to all functions above and thus can be used for parameter sharing between the functions # # (more details see in the documentation) # # Below we demonstrate the usage of all the above by defining a my_dataloader_function which in fact only grabs the # iris dataset from sklearn and returns it. A my_preprocess_function which also does nothing useful here but # demonstrating that a custom parameter can be set via kwargs and used in all of our functions when called within # Hyppopy. The my_callback_function gets as input the dictionary containing the state of the iteration and thus can be # used to access the current state of each solver iteration. Finally we define the actual loss_function # my_loss_function, which gets as input a data object and params. Both parameter are fixed, the first is defined by # the user depending on what is dataloader returns or the data object set in the constructor, the second is a dictionary # with a sample of your hyperparameter space which content is in the choice of the solver. from sklearn.svm import SVC from sklearn.datasets import load_iris from sklearn.model_selection import cross_val_score def my_dataloader_function(**kwargs): print("Dataloading...") # kwargs['params'] allows accessing additional parameter passed, see below my_preproc_param, my_dataloader_input. print("my loading argument: {}".format(kwargs['params']['my_dataloader_input'])) iris_data = load_iris() return [iris_data.data, iris_data.target] def my_preprocess_function(**kwargs): print("Preprocessing...") # kwargs['data'] allows accessing the input data print("data:", kwargs['data'][0].shape, kwargs['data'][1].shape) # kwargs['params'] allows accessing additional parameter passed, see below my_preproc_param, my_dataloader_input. print("kwargs['params']['my_preproc_param']={}".format(kwargs['params']['my_preproc_param']), "\n") # if the preprocessing function returns something, # the input data will be replaced with the data returned by this function. x = kwargs['data'][0] y = kwargs['data'][1] for i in range(x.shape[0]): x[i, :] += kwargs['params']['my_preproc_param'] return [x, y] def my_callback_function(**kwargs): print("\r{}".format(kwargs), end="") def my_loss_function(data, params): clf = SVC(**params) return -cross_val_score(estimator=clf, X=data[0], y=data[1], cv=3).mean() # We now create the BlackboxFunction object and pass all function pointers defined above, # as well as 2 dummy parameter (my_preproc_param, my_dataloader_input) for demonstration purposes. blackbox = BlackboxFunction(blackbox_func=my_loss_function, dataloader_func=my_dataloader_function, preprocess_func=my_preprocess_function, callback_func=my_callback_function, my_preproc_param=1, my_dataloader_input='could/be/a/path') # Last step, is we use our use_solver config parameter defined in the custom section to decide which solver # should be used, create the solver instance respectively, give it the blackbox and run it. After execution # we can get the result via get_result() which returns a pandas dataframe containing the complete history and # a dict best containing the best parameter set. if project.custom_use_solver == "hyperopt": solver = HyperoptSolver(project) elif project.custom_use_solver == "optunity": solver = OptunitySolver(project) elif project.custom_use_solver == "randomsearch": solver = RandomsearchSolver(project) -elif project.custom_use_solver == "gridsearch": - solver = GridsearchSolver(project) if solver is not None: solver.blackbox = blackbox solver.run() df, best = solver.get_results() print("\n") print("*"*100) print("Best Parameter Set:\n{}".format(best)) print("*"*100) diff --git a/examples/solver_tutorial_I_hyppopy.py b/examples/tutorial_simple.py similarity index 94% rename from examples/solver_tutorial_I_hyppopy.py rename to examples/tutorial_simple.py index 0ddcf02..d7e77ef 100644 --- a/examples/solver_tutorial_I_hyppopy.py +++ b/examples/tutorial_simple.py @@ -1,73 +1,71 @@ # A hyppopy minimal example optimizing a simple demo function f(x,y) = x**2+y**2 # import the HyppopyProject class keeping track of inputs from hyppopy.HyppopyProject import HyppopyProject # import the HyperoptSolver class from hyppopy.solver.HyperoptSolver import HyperoptSolver # To configure the Hyppopy solver we use a simple nested dictionary with two obligatory main sections, # hyperparameter and settings. The hyperparameter section defines your searchspace. Each hyperparameter # is again a dictionary with: # # - a domain ['categorical', 'uniform', 'normal', 'loguniform'] # - the domain data [left bound, right bound] and # - a type of your domain ['str', 'int', 'float'] # # The settings section has two subcategories, solver and custom. The first contains settings for the solver, # here 'max_iterations' - is the maximum number of iteration. # # The custom section allows defining custom parameter. An entry here is transformed to a member variable of the # HyppopyProject class. These can be useful when implementing new solver classes or for control your hyppopy script. # Here we use it as a solver switch to control the usage of our solver via the config. This means with the script # below your can try out every solver by changing use_solver to 'optunity', 'randomsearch', 'gridsearch',... # It can be used like so: project.custom_use_plugin (see below) If using the gridsearch solver, max_iterations is # ignored, instead each hyperparameter must specifiy a number of samples additionally to the range like so: # 'data': [0, 1, 100] which means sampling the space from 0 to 1 in 100 intervals. config = { "hyperparameter": { "x": { "domain": "normal", "data": [-10.0, 10.0], "type": "float" }, "y": { "domain": "uniform", "data": [-10.0, 10.0], "type": "float" } }, "settings": { "solver": { "max_iterations": 500 }, "custom": {} }} # When creating a HyppopyProject instance we # pass the config dictionary to the constructor. project = HyppopyProject(config=config) # The user defined loss function def my_loss_function(x, y): return x**2+y**2 # create a solver instance solver = HyperoptSolver(project) # pass the loss function to the solver solver.blackbox = my_loss_function # run the solver solver.run() -# get the result via get_result() which returns a pandas dataframe -# containing the complete history and a dict best containing the -# best parameter set. + df, best = solver.get_results() print("\n") print("*"*100) print("Best Parameter Set:\n{}".format(best)) print("*"*100) diff --git a/hyppopy/Solver/GridsearchSolver.py b/hyppopy/Solver/GridsearchSolver.py index 0681213..d80d2d7 100644 --- a/hyppopy/Solver/GridsearchSolver.py +++ b/hyppopy/Solver/GridsearchSolver.py @@ -1,468 +1,220 @@ # DKFZ # # # Copyright (c) German Cancer Research Center, # Division of Medical and Biological Informatics. # All rights reserved. # # This software is distributed WITHOUT ANY WARRANTY; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. # # See LICENSE.txt or http://www.mitk.org for details. # # Author: Sven Wanner (s.wanner@dkfz.de) import os import copy import logging import datetime import numpy as np from hyperopt import Trials from scipy.stats import norm from itertools import product from hyppopy.globals import DEBUGLEVEL from .HyppopySolver import HyppopySolver from ..BlackboxFunction import BlackboxFunction LOG = logging.getLogger(os.path.basename(__file__)) LOG.setLevel(DEBUGLEVEL) def get_uniform_axis_sample(a, b, N, dtype): """ returns a uniform sample x(n) in the range [a,b] sampled at N pojnts :param a: left value range bound :param b: right value range bound :param N: discretization of intervall [a,b] :param dtype: data type :return: [list] axis range """ assert a < b, "condition a < b violated!" assert isinstance(N, int), "condition N of type int violated!" assert isinstance(dtype, str), "condition type of type str violated!" if dtype == "int": return list(np.linspace(a, b, N).astype(int)) elif dtype == "float" or dtype == "double": return list(np.linspace(a, b, N)) else: raise AssertionError("dtype {} not supported for uniform sampling!".format(dtype)) def get_norm_cdf(N): """ returns a normed gaussian cdf (range [0,1]) with N sampling points :param N: sampling points :return: [ndarray] gaussian cdf function values """ assert isinstance(N, int), "condition N of type int violated!" even = True if N % 2 != 0: N -= 1 even = False N = int(N/2) sigma = 1/3 x = np.linspace(0, 1, N) y1 = norm.cdf(x, loc=0, scale=sigma)-0.5 if not even: y1 = np.append(y1, [0.5]) y2 = 1-(norm.cdf(x, loc=0, scale=sigma)-0.5) y2 = np.flip(y2, axis=0) y = np.concatenate((y1, y2), axis=0) return y def get_gaussian_axis_sample(a, b, N, dtype): """ returns a function value f(n) where f is a gaussian cdf in range [a, b] and N sampling points :param a: left value range bound :param b: right value range bound :param N: discretization of intervall [a,b] :param dtype: data type :return: [list] axis range """ assert a < b, "condition a < b violated!" assert isinstance(N, int), "condition N of type int violated!" assert isinstance(dtype, str), "condition type of type str violated!" data = [] for n in range(N): x = a + get_norm_cdf(N)[n]*(b-a) if dtype == "int": data.append(int(x)) elif dtype == "float" or dtype == "double": data.append(x) else: raise AssertionError("dtype {} not supported for uniform sampling!".format(dtype)) return data def get_logarithmic_axis_sample(a, b, N, dtype): """ returns a function value f(n) where f is logarithmic function e^x sampling the exponent range [log(a), log(b)] linear at N sampling points. The function values returned are in the range [a, b]. :param a: left value range bound :param b: right value range bound :param N: discretization of intervall [a,b] :param dtype: data type :return: [list] axis range """ assert a < b, "condition a < b violated!" assert a > 0, "condition a > 0 violated!" assert isinstance(N, int), "condition N of type int violated!" assert isinstance(dtype, str), "condition type of type str violated!" + + # convert input range into exponent range lexp = np.log(a) rexp = np.log(b) exp_range = np.linspace(lexp, rexp, N) data = [] for n in range(exp_range.shape[0]): x = np.exp(exp_range[n]) if dtype == "int": data.append(int(x)) elif dtype == "float" or dtype == "double": data.append(x) else: raise AssertionError("dtype {} not supported for uniform sampling!".format(dtype)) return data class GridsearchSolver(HyppopySolver): - + """ + The GridsearchSolver class implements a gridsearch optimization. The gridsearch supports + categorical, uniform, normal and loguniform sampling. To use the GridsearchSolver, besides + a range, one must specifiy the number of samples in the domain, e.g. 'data': [0, 1, 100] + """ def __init__(self, project=None): HyppopySolver.__init__(self, project) self._tid = None def loss_function(self, params): loss = None vals = {} idx = {} for key, value in params.items(): vals[key] = [value] idx[key] = [self._tid] trial = {'tid': self._tid, 'result': {'loss': None, 'status': 'ok'}, 'misc': { 'tid': self._tid, 'idxs': idx, 'vals': vals }, 'book_time': datetime.datetime.now(), 'refresh_time': None } try: loss = self.blackbox(**params) if loss is None: trial['result']['loss'] = np.nan trial['result']['status'] = 'failed' else: trial['result']['loss'] = loss except Exception as e: LOG.error("execution of self.blackbox(**params) failed due to:\n {}".format(e)) trial['result']['loss'] = np.nan trial['result']['status'] = 'failed' trial['refresh_time'] = datetime.datetime.now() self._trials.trials.append(trial) if isinstance(self.blackbox, BlackboxFunction) and self.blackbox.callback_func is not None: cbd = copy.deepcopy(params) cbd['iterations'] = self._tid + 1 cbd['loss'] = loss cbd['status'] = trial['result']['status'] self.blackbox.callback_func(**cbd) return def execute_solver(self, searchspace): self._tid = 0 self._trials = Trials() for x in product(*searchspace[1]): params = {} for name, value in zip(searchspace[0], x): params[name] = value try: self.loss_function(params) self._tid += 1 except Exception as e: msg = "internal error in randomsearch execute_solver occured. {}".format(e) LOG.error(msg) raise BrokenPipeError(msg) self.best = self._trials.argmin def convert_searchspace(self, hyperparameter): + """ + the function converts the standard parameter input into a range list depending + on the domain. These rangelists are later used with itertools product to create + a paramater space sample of each combination. + :param hyperparameter: [dict] hyperparameter space + :return: [list] name and range for each parameter space axis + """ searchspace = [[], []] for name, param in hyperparameter.items(): if param["domain"] == "categorical": searchspace[0].append(name) searchspace[1].append(param["data"]) elif param["domain"] == "uniform": searchspace[0].append(name) searchspace[1].append(get_uniform_axis_sample(param["data"][0], param["data"][1], param["data"][2], param["type"])) elif param["domain"] == "normal": searchspace[0].append(name) searchspace[1].append(get_gaussian_axis_sample(param["data"][0], param["data"][1], param["data"][2], param["type"])) elif param["domain"] == "loguniform": searchspace[0].append(name) searchspace[1].append(get_logarithmic_axis_sample(param["data"][0], param["data"][1], param["data"][2], param["type"])) return searchspace - - - - -# def get_uniform_axis_sample(n, a, b, N): -# """ -# returns a uniform sample x(n) in the range [a,b] sampled at N pojnts -# :param n: input position within range [0,N] -# :param a: left value range bound -# :param b: right value range bound -# :param N: discretization of intervall [a,b] -# :return: [float] x(n) -# """ -# assert a < b, "condition a < b violated!" -# assert n >= 0, "condition n >= 0 violated!" -# assert n < N, "condition n < N violated!" -# assert isinstance(n, int), "condition n of type int violated!" -# assert isinstance(N, int), "condition N of type int violated!" -# return np.linspace(a, b, N)[n] -# -# -# def get_norm_cdf(N): -# """ -# returns a normed gaussian cdf (range [0,1]) with N sampling points -# :param N: sampling points -# :return: [ndarray] gaussian cdf function values -# """ -# assert isinstance(N, int), "condition N of type int violated!" -# even = True -# if N % 2 != 0: -# N -= 1 -# even = False -# N = int(N/2) -# sigma = 1/3 -# x = np.linspace(0, 1, N) -# y1 = norm.cdf(x, loc=0, scale=sigma)-0.5 -# if not even: -# y1 = np.append(y1, [0.5]) -# y2 = 1-(norm.cdf(x, loc=0, scale=sigma)-0.5) -# y2 = np.flip(y2, axis=0) -# y = np.concatenate((y1, y2), axis=0) -# return y -# -# -# def get_gaussian_axis_sample(n, a, b, N): -# """ -# returns a function value f(n) where f is a gaussian cdf in range [a, b] and N sampling points -# :param n: input position within range [0,N] -# :param a: left value range bound -# :param b: right value range bound -# :param N: discretization of intervall [a,b] -# :return: [float] f(n) -# """ -# assert a < b, "condition a < b violated!" -# assert n >= 0, "condition n >= 0 violated!" -# assert n < N, "condition n < N violated!" -# assert isinstance(n, int), "condition n of type int violated!" -# assert isinstance(N, int), "condition N of type int violated!" -# return a + get_norm_cdf(N)[n]*(b-a) -# -# -# def get_logarithmic_axis_sample(n, a, b, N): -# """ -# returns a function value f(n) where f is logarithmic function e^x sampling -# the exponent range [log(a), log(b)] linear at N sampling points. -# The function values returned are in the range [a, b]. -# :param n: sampling point [0, N-1] -# :param a: left range bound -# :param b: right range bound -# :param N: discretization of intervall [log(a),log(b)] -# :return: [float] f(x) -# """ -# assert a < b, "condition a < b violated!" -# assert n >= 0, "condition n >= 0 violated!" -# assert n < N, "condition n < N violated!" -# assert isinstance(n, int), "condition n of type int violated!" -# assert isinstance(N, int), "condition N of type int violated!" -# lexp = np.log(a) -# rexp = np.log(b) -# exp_range = np.linspace(lexp, rexp, N) -# return np.exp(exp_range[n]) -# -# -# class GridAxis(object): -# _data = None -# _name = None -# _type = None -# _domain = None -# _sampling = None -# _is_categorical = False -# _current_pos = 0 -# -# def __init__(self, name, param): -# self._name = name -# self._domain = param["domain"] -# self.data = param["data"] -# self.type = param["type"] -# if param["domain"] == "categorical": -# self._is_categorical = True -# -# def elems_left(self): -# return self._sampling - self._current_pos - 1 -# -# def increment(self): -# self._current_pos += 1 -# if self._current_pos > self._sampling - 1: -# self._current_pos = 0 -# -# def get_value(self): -# if self._domain == "categorical": -# return self.data[self._current_pos] -# elif self._domain == "uniform": -# return get_uniform_axis_sample(self._current_pos, self.data[0], self.data[1], self._sampling) -# elif self._domain == "normal": -# return get_gaussian_axis_sample(self._current_pos, self.data[0], self.data[1], self._sampling) -# elif self._domain == "loguniform": -# return get_logarithmic_axis_sample(self._current_pos, self.data[0], self.data[1], self._sampling) -# -# @property -# def name(self): -# return self._name -# -# @property -# def data(self): -# return self._data -# -# @data.setter -# def data(self, value): -# if self._domain == "categorical": -# assert len(value) > 0, "Precondition violation, empty data cannot be handled!" -# self._data = value -# self._sampling = len(value) -# else: -# assert len(value) == 3, "precondition violation, gridsearch axis needs low, high and sampling value!" -# self._data = value[0:2] -# self._sampling = value[2] -# -# @property -# def sampling(self): -# return self._sampling -# -# @property -# def type(self): -# return self._type -# -# @type.setter -# def type(self, value): -# assert isinstance(value, str), "precondition violation, value expects a str!" -# if value == "str": -# self._type = str -# elif value == "int": -# self._type = int -# if value == "float" or value == "double": -# self._type = float -# -# -# class GridSampler(object): -# -# def __init__(self): -# self._axis = [] -# self._loops = [] -# -# def get_gridsize(self): -# n = 1 -# for ax in self._axis: -# n *= ax.sampling -# return n -# -# def add_axis(self, axis): -# self._axis.append(axis) -# self.update_loops() -# -# def update_loops(self): -# if len(self._axis) == 1: -# self._loops.append(1) -# else: -# lens = [] -# for ax in self._axis: -# lens.append(ax.sampling) -# self._loops.append(np.cumprod(lens)) -# -# def get_sample(self): -# sample = [] -# for ax in self._axis: -# sample.append(ax.get_value()) -# return sample -# -# -# class GridsearchSolver(HyppopySolver): -# -# def __init__(self, project=None): -# HyppopySolver.__init__(self, project) -# self._tid = None -# -# def loss_function(self, params): -# loss = None -# vals = {} -# idx = {} -# for key, value in params.items(): -# vals[key] = [value] -# idx[key] = [self._tid] -# trial = {'tid': self._tid, -# 'result': {'loss': None, 'status': 'ok'}, -# 'misc': { -# 'tid': self._tid, -# 'idxs': idx, -# 'vals': vals -# }, -# 'book_time': datetime.datetime.now(), -# 'refresh_time': None -# } -# try: -# loss = self.blackbox(**params) -# if loss is None: -# trial['result']['loss'] = np.nan -# trial['result']['status'] = 'failed' -# else: -# trial['result']['loss'] = loss -# except Exception as e: -# LOG.error("execution of self.blackbox(**params) failed due to:\n {}".format(e)) -# trial['result']['loss'] = np.nan -# trial['result']['status'] = 'failed' -# trial['refresh_time'] = datetime.datetime.now() -# self._trials.trials.append(trial) -# if self.blackbox.callback_func is not None: -# cbd = copy.deepcopy(params) -# cbd['iterations'] = self._tid + 1 -# cbd['loss'] = loss -# cbd['status'] = trial['result']['status'] -# self.blackbox.callback_func(**cbd) -# return -# -# def execute_solver(self, searchspace): -# self._tid = 0 -# self._trials = Trials() -# -# while True: -# params = {} -# for axis in searchspace: -# params[axis.name] = axis.next() -# if params[axis.name] is None: -# break -# try: -# self.loss_function(params) -# self._tid += 1 -# except Exception as e: -# msg = "internal error in randomsearch execute_solver occured. {}".format(e) -# LOG.error(msg) -# raise BrokenPipeError(msg) -# self.best = self._trials.argmin -# -# def convert_searchspace(self, hyperparameter): -# searchspace = [] -# for name, param in hyperparameter.items(): -# if param["domain"] != "categorical": -# searchspace.append(GridAxis(name, param)) -# for name, param in hyperparameter.items(): -# if param["domain"] == "categorical": -# searchspace.append(GridAxis(name, param)) -# searchspace[-1].is_looping = False -# return searchspace