Coverage for credoai/evaluators/evaluator.py: 89%
70 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-08 07:32 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-08 07:32 +0000
1from abc import ABC, abstractmethod
3from connect.evidence import EvidenceContainer
4from credoai.utils import global_logger
5from credoai.utils.common import NotRunError, ValidationError
8class Evaluator(ABC):
9 """
10 Base abstract class for all lens evaluators.
12 Defines basic functions required from any evaluator object.
14 This class leverages the special method `__call__` to make artifacts
15 available in the class enclosure.
17 .. automethod:: __call__
18 .. automethod:: _init_artifacts
19 .. automethod:: _validate_arguments
20 .. automethod:: _setup
21 """
23 def __init__(self):
24 self._results = None
25 self.artifact_keys = []
26 self.logger = global_logger
27 self.metadata = {}
29 @property
30 def name(self):
31 """The name associated to the Evaluator, equals the class name."""
32 return self.__class__.__name__
34 @property
35 def results(self):
36 """
37 Container for all results.
39 It is expected to be a list of EvidenceContainers. This is enforced in
40 the associated setter method.
42 Raises
43 ------
44 NotRunError
45 It indicates that results are missing, the evaluator was not run.
46 """
47 if self._results is not None:
48 return self._results
49 else:
50 raise NotRunError(
51 "No results available, please call the method: 'evaluate'."
52 )
54 @results.setter
55 def results(self, results):
56 """Requires the results to be list of Evidence Containers"""
57 if not isinstance(results, list):
58 raise ValidationError("Results must be a list")
59 for result in results:
60 if not isinstance(result, EvidenceContainer):
61 raise ValidationError("All results must be EvidenceContainers")
62 self._results = results
64 @property
65 @abstractmethod
66 def required_artifacts(self):
67 """
68 The required artifacts necessary for the functioning of the evaluator
70 This set contains the :ref:`artifacts<credoai.artifacts>` that Lens can feed to
71 an evaluator, the accepted values are ``{"model", "assessment_data", "training_data", "data"}``.
73 The string "data" means that the evaluator can be run on assessment and/or training data
74 (DataProfiler is an example). Lens will iterate over all the available artifacts internally.
76 The set can also include the string "sensitive_feature". This is to indicate
77 that the evaluator depends on sensitive features. Lens will iterate over the available sensitive
78 features internally.
79 """
80 pass
82 def __call__(self, **kwargs):
83 """
84 This method is used to pass the model, assessment_data and training_data
85 artifacts to instantiated evaluator.
87 The method is called internally by the Lens instance, which only passes the
88 artifacts specified in the property :meth:`required_artifacts<Evaluator.required_artifacts>`.
90 After the artifacts are passed, it performs arguments validation and calls :meth:`_setup<Evaluator._setup>`
92 At the end of these operation, the validated artifacts are available in the evaluator enclosure.
93 """
94 self._init_artifacts(kwargs)
95 self._validate_arguments()
96 self._setup()
97 return self
99 @abstractmethod
100 def evaluate(self):
101 """
102 Execute any data/model processing required for the evaluator.
104 Populates the self.results object.
105 """
106 return self
108 def get_container_info(self, labels: dict = None, metadata: dict = None):
109 """
110 Expands the base labels and metadata used to populate evidences.
112 Parameters
113 ----------
114 labels : dict, optional
115 The default labels can be expanded by the user when defining a new evaluator.
116 A label is in general any information necessary to identify evidences in the Credo AI Platform,
117 therefore, by default None.
118 metadata : dict, optional
119 Any extra info the user wants to associate to the evidences. Compared
120 to labels these are not necessary for evidence identification, by default None.
121 """
122 info = self._base_container_info()
123 if labels:
124 info["labels"].update(labels)
125 if metadata:
126 info["metadata"].update(metadata)
127 return info
129 def _base_container_info(self):
130 """Extract basic info to populate labels and metadata."""
131 meta = {**self.metadata, **self._get_artifacts()}
132 labels = {"evaluator": self.name}
133 if "dataset_type" in meta:
134 labels["dataset_type"] = meta["dataset_type"]
135 return {"labels": labels, "metadata": meta}
137 def _get_artifacts(self):
138 """
139 Extract artifacts that will be used by the evaluator.
141 The method also extract name info from the available artifacts.
142 """
143 artifacts = {}
144 save_keys = {
145 "model": "model_name",
146 "data": "dataset_name",
147 "assessment_data": "assessment_dataset_name",
148 "training_data": "training_dataset_name",
149 }
150 for k in self.artifact_keys:
151 save_key = save_keys.get(k, k)
152 try:
153 artifacts[save_key] = self.__dict__[k].name
154 except AttributeError:
155 pass
156 return artifacts
158 def _init_artifacts(self, artifacts):
159 """Adds artifacts to evaluator object
161 Parameters
162 ----------
163 artifacts : dict
164 Dictionary of artifacts, e.g. {'model': Model}
165 """
166 self.artifact_keys = list(artifacts.keys())
167 self.__dict__.update(artifacts)
169 @abstractmethod
170 def _setup(self):
171 """
172 Contains any extra steps necessary to initialize the evaluator
173 """
174 pass
176 @abstractmethod
177 def _validate_arguments(self):
178 """
179 Check that basic requirements for the run of an evaluator are met.
180 """
181 pass