Source code for moe.views.schemas.bandit_pretty_view

# -*- coding: utf-8 -*-
"""Base schemas for creating SampledArm and allocations for bandit endpoints along with base request/response schema components."""
import colander

from moe.bandit.constant import DEFAULT_EPSILON, DEFAULT_TOTAL_SAMPLES, EPSILON_SUBTYPE_FIRST, EPSILON_SUBTYPE_GREEDY
from moe.bandit.data_containers import SampleArm
from moe.views.schemas import base_schemas


[docs]class ArmAllocations(colander.MappingSchema): """Colander SingleArm Dictionary of (arm name, allocation) key-value pairs.""" schema_type = colander.MappingSchema title = 'Arm Allocations' def __init__(self): """Allow any arm name to be a valid key.""" super(ArmAllocations, self).__init__(colander.Mapping(unknown='preserve'))
[docs] def validator(self, node, cstruct): """Raise an exception if the node value (cstruct) is not a valid dictionary of (arm name, allocation) key-value pairs. The total allocation must sums to 1. Each allocation is in range [0,1]. :param node: the node being validated (usually self) :type node: colander.SchemaNode subclass instance :param cstruct: the value being validated :type cstruct: dictionary of (arm name, allocation) key-value pairs """ total_allocation = 0.0 for arm_name, allocation in cstruct.iteritems(): total_allocation += allocation if not 0.0 <= allocation <= 1.0: raise colander.Invalid(node, msg='Allocation = {:f} must be in range [0,1].'.format(allocation)) if total_allocation != 1.0: raise colander.Invalid(node, msg='Total Allocation = {:f} must be 1.0.'.format(total_allocation))
[docs]class ArmsSampled(colander.MappingSchema): """Colander SingleArm Dictionary of (arm name, SingleArm) key-value pairs.""" schema_type = colander.MappingSchema title = 'Arms Sampled' def __init__(self): """Allow any arm name to be a valid key.""" super(ArmsSampled, self).__init__(colander.Mapping(unknown='preserve'))
[docs] def validator(self, node, cstruct): """Raise an exception if the node value (cstruct) is not a valid dictionary of (arm name, SingleArm) key-value pairs. Default value for loss is 0. Default value for variance of an arm is None. :param node: the node being validated (usually self) :type node: colander.SchemaNode subclass instance :param cstruct: the value being validated :type cstruct: dictionary of (arm name, SingleArm) key-value pairs """ for arm_name, sample_arm in cstruct.iteritems(): if 'loss' not in sample_arm: sample_arm['loss'] = 0 if 'variance' not in sample_arm: sample_arm['variance'] = None if not (set(sample_arm.keys()) == set(map(lambda s: s.lstrip('_'), SampleArm.__slots__))): raise colander.Invalid(node, msg='Value = {:s} must be a valid SampleArm.'.format(sample_arm)) SampleArm(sample_arm['win'], sample_arm['loss'], sample_arm['total'], sample_arm['variance'])
[docs]class BanditEpsilonFirstHyperparameterInfo(base_schemas.StrictMappingSchema): """The hyperparameter info needed for every Bandit Epsilon-First request. **Required fields** :ivar epsilon: (*0.0 <= float64 <= 1.0*) epsilon value for epsilon-first bandit. This strategy pulls the optimal arm (best expected return) with if it is in exploitation phase (number sampled > epsilon * total_samples). Otherwise a random arm is pulled (exploration). :ivar total_samples: (*int >= 0*) total number of samples for epsilon-first bandit. total_samples is T from :doc:`bandit`. """ epsilon = colander.SchemaNode( colander.Float(), validator=colander.Range(min=0.0, max=1.0), missing=DEFAULT_EPSILON, ) total_samples = colander.SchemaNode( colander.Int(), validator=colander.Range(min=0), missing=DEFAULT_TOTAL_SAMPLES, )
[docs]class BanditEpsilonGreedyHyperparameterInfo(base_schemas.StrictMappingSchema): """The hyperparameter info needed for every Bandit Epsilon request. **Required fields** :ivar epsilon: (*0.0 <= float64 <= 1.0*) epsilon value for epsilon-greedy bandit. This strategy pulls the optimal arm (best expected return) with probability 1-epsilon. With probability epsilon a random arm is pulled. """ epsilon = colander.SchemaNode( colander.Float(), validator=colander.Range(min=0.0, max=1.0), missing=DEFAULT_EPSILON, ) #: Mapping from bandit epsilon subtypes (:const:`moe.bandit.constant.EPSILON_SUBTYPES`) to #: hyperparameter info schemas, e.g., :class:`moe.views.schemas.bandit_pretty_view.BanditEpsilonFirstHyperparameterInfo`.
BANDIT_EPSILON_SUBTYPES_TO_HYPERPARAMETER_INFO_SCHEMA_CLASSES = { EPSILON_SUBTYPE_FIRST: BanditEpsilonFirstHyperparameterInfo, EPSILON_SUBTYPE_GREEDY: BanditEpsilonGreedyHyperparameterInfo, }
[docs]class BanditHistoricalInfo(base_schemas.StrictMappingSchema): """The Bandit historical info needed for every request. Contains: * arms_sampled - ArmsSampled """ arms_sampled = ArmsSampled()
[docs]class BanditResponse(base_schemas.StrictMappingSchema): """A :mod:`moe.views.rest.bandit_epsilon` and :mod:`moe.views.rest.bandit_ucb` response colander schema. **Output fields** :ivar endpoint: (*str*) the endpoint that was called :ivar arms: (:class:`moe.views.schemas.bandit_pretty_view.ArmAllocations`) a dictionary of (arm name, allocation) key-value pairs :ivar winner: (*str*) winning arm name **Example Response** .. sourcecode:: http Content-Type: text/javascript { "endpoint":"bandit_epsilon", "arm_allocations": { "arm1": 0.95, "arm2": 0.025, "arm3": 0.025, } "winner": "arm1", } """ endpoint = colander.SchemaNode(colander.String()) arm_allocations = ArmAllocations() winner = colander.SchemaNode(colander.String())