# -*- coding: utf-8 -*-
"""Base class for hyperparameter optimization search functions"""
# Import from __future__
from __future__ import absolute_import, print_function, with_statement
# Import standard library
import operator as op
[docs]class SearchBase(object):
[docs] def assertions(self):
"""Assertion method to check :code:`optimizer` input
Raises
------
TypeError
When :code:`optimizer` does not have an `'optimize'` attribute.
"""
# Check type of optimizer object
if not hasattr(self.optimizer, "optimize"):
raise TypeError(
"Parameter `optimizer` must have an " "`'optimize'` attribute."
)
[docs] def __init__(
self,
optimizer,
n_particles,
dimensions,
options,
objective_func,
iters,
bounds=None,
velocity_clamp=(0, 1),
):
"""Initialize the Search
Attributes
----------
optimizer: pyswarms.single
either LocalBestPSO or GlobalBestPSO
n_particles : int
number of particles in the swarm.
dimensions : int
number of dimensions in the space.
options : dict with keys :code:`{'c1', 'c2', 'w', 'k', 'p'}`
a dictionary containing the parameters for the specific
optimization technique
* c1 : float
cognitive parameter
* c2 : float
social parameter
* w : float
inertia parameter
* k : int
number of neighbors to be considered. Must be a
positive integer less than :code:`n_particles`
* p: int {1,2}
the Minkowski p-norm to use. 1 is the
sum-of-absolute values (or L1 distance) while 2 is
the Euclidean (or L2) distance.
objective_func: function
objective function to be evaluated
iters: int
number of iterations
bounds : tuple of np.ndarray, optional (default is None)
a tuple of size 2 where the first entry is the minimum bound
while the second entry is the maximum bound. Each array must
be of shape :code:`(dimensions,)`.
velocity_clamp : tuple (default is :code:`None`)
a tuple of size 2 where the first entry is the minimum velocity
and the second entry is the maximum velocity. It
sets the limits for velocity clamping.
"""
# Assign attributes
self.optimizer = optimizer
self.n_particles = n_particles
self.dims = dimensions
self.options = options
self.bounds = bounds
self.vclamp = velocity_clamp
self.objective_func = objective_func
self.iters = iters
# Invoke assertions
self.assertions()
[docs] def generate_score(self, options):
"""Generate score for optimizer's performance on objective function
Parameters
----------
options: dict
a dict with the following keys: {'c1', 'c2', 'w', 'k', 'p'}
"""
# Intialize optimizer
f = self.optimizer(
self.n_particles,
self.dims,
options,
self.bounds,
velocity_clamp=self.vclamp,
)
# Return score
return f.optimize(self.objective_func, self.iters)[0]
[docs] def search(self, maximum=False):
"""Compare optimizer's objective function performance scores
for all combinations of provided parameters
Parameters
----------
maximum: bool
a bool defaulting to False, returning the minimum value for the
objective function. If set to True, will return the maximum value
for the objective function.
"""
# Generate the grid of all hyperparameter value combinations
grid = self.generate_grid()
# Calculate scores for all hyperparameter combinations
scores = [self.generate_score(i) for i in grid]
# Default behavior
idx, self.best_score = min(enumerate(scores), key=op.itemgetter(1))
# Catches the maximum bool flag
if maximum:
idx, self.best_score = max(enumerate(scores), key=op.itemgetter(1))
# Return optimum hyperparameter value property from grid using index
self.best_options = op.itemgetter(idx)(grid)
return self.best_score, self.best_options