Module topicnet.cooking_machine.models.frozen_score
Expand source code
import warnings
from enum import Enum
from numbers import Number
from typing import (
List,
Optional
)
from .base_score import BaseScore
class FrozenScore(BaseScore):
"""
Custom scores can have anything inside.
So there is a probability that pickle will not be able to dump them.
Frozen score helps to store the value of the original score without its internal logic,
so as it can be saved.
"""
def __init__(self, value: List[Optional[float]], original_score: BaseScore = None):
super().__init__()
self.value = value
self._original_score: BaseScore = None
if original_score is not None:
self._save_original(original_score)
def __repr__(self):
return f'{self.__class__.__name__}(original_score={self._original_score!r})'
def __getattr__(self, attribute_name):
if attribute_name.startswith('__'):
raise AttributeError()
if attribute_name == '_original_score': # some dill-loading stuff?
raise AttributeError()
if self._original_score is not None and hasattr(self._original_score, attribute_name):
return getattr(self._original_score, attribute_name)
raise AttributeError(
f'Frozen score doesn\'t have such attribute: "{attribute_name}"'
)
def update(self, score_value: float) -> None:
"""
Update is not supposed to be applied to Frozen score.
It is not supposed to be changed.
Still, the situation with an endeavour to update can generally happen if one tries
to train the model further after loading.
"""
warnings.warn(
f'Trying to update Frozen score! Update value "{score_value}". '
f'Frozen score is not supposed to be updated, '
f'as there is no computation logic inside'
)
if score_value is not None:
# TODO: it shouldn't be possible to pass such score_value value to update()
# other than the one returned by self.call()
warnings.warn(
f'Can\'t update Frozen score with value other than None: "{score_value}"!'
f' Saving None score'
)
self.value.append(None)
def call(self, model, *args, **kwargs) -> Optional[float]:
return None
def _save_original(self, original_score: BaseScore) -> None:
field_types_for_saving = (Number, str, bool, Enum)
self._original_score = BaseScore()
for field_name in dir(original_score):
field_value = getattr(original_score, field_name)
if field_value is not None and not isinstance(field_value, field_types_for_saving):
continue
try:
setattr(self._original_score, field_name, field_value)
except AttributeError:
# TODO: log?
pass
self._name = self._original_score._name
Classes
class FrozenScore (value: List[Union[float, NoneType]], original_score: BaseScore = None)
-
Custom scores can have anything inside. So there is a probability that pickle will not be able to dump them. Frozen score helps to store the value of the original score without its internal logic, so as it can be saved.
Parameters
name
- Name of the score
should_compute
-
Function which decides whether the score should be computed on the current fit iteration or not. If
should_compute
isNone
, then score is going to be computed on every iteration. At the same time, whatever function one defines, score is always computed on the last fit iteration. This is done for two reasons. Firstly, so that the score is always computed at least once duringmodel._fit()
. Secondly, so thatexperiment.select()
works correctly.The parameter
should_compute
might be helpful if the score is slow but one still needs to get the dependence of the score on iteration (for the described case, one may compute the score on every even iteration or somehow else). However, be aware that ifshould_compute
is used for some model's scores, then the scores may have different number of values inmodel.scores
! Number of score values is the number of times the scores was calculated; first value corresponds to the first fit iteration which passedshould_compute
etc.There are a couple of things also worth noting. Fit iteration numbering starts from zero. And every new
model._fit()
call is a new range of fit iterations.
Examples
Scores created below are unworkable (as BaseScore has no
call
method inplemented). These are just the examples of how one can create a score and set some of its parameters.Scores to be computed on every iteration:
>>> score = BaseScore() >>> score = BaseScore(should_compute=BaseScore.compute_always) >>> score = BaseScore(should_compute=lambda i: True) >>> score = BaseScore(should_compute=True)
Scores to be computed only on the last iteration:
>>> score = BaseScore(should_compute=BaseScore.compute_on_last) >>> score = BaseScore(should_compute=lambda i: False) >>> score = BaseScore(should_compute=False)
Score to be computed only on even iterations:
>>> score = BaseScore(should_compute=lambda i: i % 2 == 0)
Expand source code
class FrozenScore(BaseScore): """ Custom scores can have anything inside. So there is a probability that pickle will not be able to dump them. Frozen score helps to store the value of the original score without its internal logic, so as it can be saved. """ def __init__(self, value: List[Optional[float]], original_score: BaseScore = None): super().__init__() self.value = value self._original_score: BaseScore = None if original_score is not None: self._save_original(original_score) def __repr__(self): return f'{self.__class__.__name__}(original_score={self._original_score!r})' def __getattr__(self, attribute_name): if attribute_name.startswith('__'): raise AttributeError() if attribute_name == '_original_score': # some dill-loading stuff? raise AttributeError() if self._original_score is not None and hasattr(self._original_score, attribute_name): return getattr(self._original_score, attribute_name) raise AttributeError( f'Frozen score doesn\'t have such attribute: "{attribute_name}"' ) def update(self, score_value: float) -> None: """ Update is not supposed to be applied to Frozen score. It is not supposed to be changed. Still, the situation with an endeavour to update can generally happen if one tries to train the model further after loading. """ warnings.warn( f'Trying to update Frozen score! Update value "{score_value}". ' f'Frozen score is not supposed to be updated, ' f'as there is no computation logic inside' ) if score_value is not None: # TODO: it shouldn't be possible to pass such score_value value to update() # other than the one returned by self.call() warnings.warn( f'Can\'t update Frozen score with value other than None: "{score_value}"!' f' Saving None score' ) self.value.append(None) def call(self, model, *args, **kwargs) -> Optional[float]: return None def _save_original(self, original_score: BaseScore) -> None: field_types_for_saving = (Number, str, bool, Enum) self._original_score = BaseScore() for field_name in dir(original_score): field_value = getattr(original_score, field_name) if field_value is not None and not isinstance(field_value, field_types_for_saving): continue try: setattr(self._original_score, field_name, field_value) except AttributeError: # TODO: log? pass self._name = self._original_score._name
Ancestors
Methods
def update(self, score_value: float) ‑> NoneType
-
Update is not supposed to be applied to Frozen score. It is not supposed to be changed. Still, the situation with an endeavour to update can generally happen if one tries to train the model further after loading.
Expand source code
def update(self, score_value: float) -> None: """ Update is not supposed to be applied to Frozen score. It is not supposed to be changed. Still, the situation with an endeavour to update can generally happen if one tries to train the model further after loading. """ warnings.warn( f'Trying to update Frozen score! Update value "{score_value}". ' f'Frozen score is not supposed to be updated, ' f'as there is no computation logic inside' ) if score_value is not None: # TODO: it shouldn't be possible to pass such score_value value to update() # other than the one returned by self.call() warnings.warn( f'Can\'t update Frozen score with value other than None: "{score_value}"!' f' Saving None score' ) self.value.append(None)
Inherited members