# @Time : 2021/6/23
# @Author : Zihan Lin
# @Email : zhlin@ruc.edu.cn
# UPDATE
# @Time : 2021/8/29
# @Author : Zhichao Feng
# @email : fzcbupt@gmail.com
"""
recbole.evaluator.register
################################################
"""
import inspect
import sys
[docs]def cluster_info(module_name):
"""Collect information of all metrics, including:
- ``metric_need``: Information needed to calculate this metric, the combination of ``rec.items, rec.topk,
rec.meanrank, rec.score, data.num_items, data.num_users, data.count_items, data.count_users, data.label``.
- ``metric_type``: Whether the scores required by metric are grouped by user, range in ``EvaluatorType.RANKING``
and ``EvaluatorType.VALUE``.
- ``smaller``: Whether the smaller metric value represents better performance,
range in ``True`` and ``False``, default to ``False``.
Note:
For ``metric_type``: in current RecBole, all the "grouped-score" metrics are ranking-based and all the
"non-grouped-score" metrics are value-based. To keep with our paper, we adopted the more formal terms:
``RANKING`` and ``VALUE``.
Args:
module_name (str): the name of module ``recbole.evaluator.metrics``.
Returns:
dict: Three dictionaries containing the above information
and a dictionary matching metric names to metric classes.
"""
smaller_m = []
m_dict, m_info, m_types = {}, {}, {}
metric_class = inspect.getmembers(
sys.modules[module_name],
lambda x: inspect.isclass(x) and x.__module__ == module_name,
)
for name, metric_cls in metric_class:
name = name.lower()
m_dict[name] = metric_cls
if hasattr(metric_cls, "metric_need"):
m_info[name] = metric_cls.metric_need
else:
raise AttributeError(f"Metric '{name}' has no attribute [metric_need].")
if hasattr(metric_cls, "metric_type"):
m_types[name] = metric_cls.metric_type
else:
raise AttributeError(f"Metric '{name}' has no attribute [metric_type].")
if metric_cls.smaller is True:
smaller_m.append(name)
return smaller_m, m_info, m_types, m_dict
metric_module_name = "recbole.evaluator.metrics"
smaller_metrics, metric_information, metric_types, metrics_dict = cluster_info(
metric_module_name
)
[docs]class Register(object):
"""Register module load the registry according to the metrics in config.
It is a member of DataCollector.
The DataCollector collect the resource that need for Evaluator under the guidance of Register
"""
def __init__(self, config):
self.config = config
self.metrics = [metric.lower() for metric in self.config["metrics"]]
self._build_register()
def _build_register(self):
for metric in self.metrics:
metric_needs = metric_information[metric]
for info in metric_needs:
setattr(self, info, True)
[docs] def has_metric(self, metric: str):
if metric.lower() in self.metrics:
return True
else:
return False
[docs] def need(self, key: str):
if hasattr(self, key):
return getattr(self, key)
return False