1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-13 07:46:53 +08:00

ensemble & get_exp & dataset_pickle

This commit is contained in:
lzh222333
2021-03-31 02:39:14 +00:00
parent eae94d1ee8
commit 544365f3a9
7 changed files with 249 additions and 164 deletions

View File

@@ -7,166 +7,69 @@ from qlib.workflow.task.utils import list_recorders
class Collector:
"""
This class will divide disorderly records or anything worth collecting into different groups based on the group_key.
After grouping, we can reduce the useful information from different groups.
"""The collector to collect different results based on experiment backend and ensemble method
"""
def group(self, *args, **kwargs):
"""
According to the get_group_key_func, divide disorderly things into different groups.
For example:
.. code-block:: python
input:
[thing1, thing2, thing3, thing4, thing5]
output:
{
"group_name1": [thing3, thing5, thing1]
"group_name2": [thing2, thing4]
}
def collect(self, ensemble, get_group_key_func, *args, **kwargs):
"""To collect the results, we need to get the experiment record firstly and divided them into
different groups. Then use ensemble methods to merge the group.
Args:
get_group_key_func (Callable): get a group key based on a thing
things_list (list): a list of things
Returns:
dict: a dict including the group key and members of the group.
ensemble (Ensemble): an instance of Ensemble
get_group_key_func (Callable): a function to get the group of a experiment record
"""
raise NotImplementedError(f"Please implement the `group` method.")
def reduce(self, things_group: dict):
"""
Using the dict from `group`, reduce useful information.
Args:
things_group (dict): a dict after grouping
Returns:
dict: a dict including the group key, the information key and the information value
"""
raise NotImplementedError(f"Please implement the `reduce` method.")
def collect(self, *args, **kwargs):
"""group and reduce
Returns:
dict: a dict including the group key, the information key and the information value
"""
grouped = self.group(*args, **kwargs)
return self.reduce(grouped)
raise NotImplementedError(f"Please implement the `collect` method.")
class RecorderCollector(Collector):
"""
The Recorder's Collector. This class is a implementation of Collector, collecting some artifacts saved by Recorder.
"""
def __init__(self, experiment_name: str) -> None:
self.exp_name = experiment_name
self.logger = get_module_logger(self.__class__.__name__)
_artifacts_key_path = {"pred": "pred.pkl", "IC": "sig_analysis/ic.pkl"}
_artifacts_key_merge_method = {}
def default_merge(self, artifact_list):
"""Merge disorderly artifacts in artifact list.
def __init__(self, exp_name, artifacts_path = {"pred": "pred.pkl", "IC": "sig_analysis/ic.pkl"}) -> None:
"""init RecorderCollector
Args:
artifact_list (list): A artifact list.
Raises:
NotImplementedError: [description]
exp_name (str): the name of Experiment
artifacts_path (dict, optional): The artifacts name and its path in Recorder. Defaults to {"pred": "pred.pkl", "IC": "sig_analysis/ic.pkl"}.
"""
raise NotImplementedError(f"Please implement the `default_merge` method.")
self.exp_name = exp_name
self.artifacts_path = artifacts_path
def group(self, get_group_key_func, rec_filter_func=None):
"""
Filter recorders and group recorders by group key.
def collect(self, ensemble, get_group_key_func, artifacts_key=None, rec_filter_func=None):
"""Collect different artifacts based on recorder after filtering and ensemble method.
Group recorder by get_group_key_func.
Args:
get_group_key_func (Callable): get a group key based on a recorder
rec_filter_func (Callable, optional): filter the recorders in this experiment. Defaults to None.
ensemble (Ensemble): an instance of Ensemble
get_group_key_func (Callable): a function to get the group of a experiment record
artifacts_key (str or List, optional): the artifacts key you want to get. Defaults to None.
rec_filter_func (Callable, optional): filter the recorder by return True or False. Defaults to None.
Returns:
dict: a dict including the group key and recorders of the group
dict: the dict after collected.
"""
if artifacts_key is None:
artifacts_key = self.artifacts_path.keys()
if isinstance(artifacts_key, str):
artifacts_key = [artifacts_key]
# prepare_ensemble
ensemble_dict = {}
for key in artifacts_key:
ensemble_dict.setdefault(key,{})
# filter records
recs_flt = list_recorders(self.exp_name, rec_filter_func)
# group
recs_group = {}
for _, rec in recs_flt.items():
group_key = get_group_key_func(rec)
recs_group.setdefault(group_key, []).append(rec)
return recs_group
def reduce(self, recs_group: dict, artifact_keys_list: list = None):
"""
Reduce artifacts based on the dict of grouped recorder.
The artifacts need be declared by artifact_keys_list.
The artifacts path in recorder need be declared by _artifacts_key_path.
If there is no declartion in _artifacts_key_merge_method, then use default_merge method to merge it.
Args:
recs_group (dict): The dict grouped by `group`
artifact_keys_list (list): The list of artifact keys. If it is None, then use all artifacts in _artifacts_key_path.
Returns:
a dict including the group key, the artifact key and the artifact value.
For example:
.. code-block:: python
{
group_key: {"pred": <VALUE>, "IC": <VALUE>}
}
"""
if artifact_keys_list == None:
artifact_keys_list = self._artifacts_key_path.keys()
reduce_group = {}
for group_key, recorder_list in recs_group.items():
reduced_artifacts = {}
for artifact_key in artifact_keys_list:
artifact_list = []
for recorder in recorder_list:
artifact_list.append(recorder.load_object(self._artifacts_key_path[artifact_key]))
merge_method = self._artifacts_key_merge_method.get(artifact_key, self.default_merge)
artifact = merge_method(artifact_list)
reduced_artifacts[artifact_key] = artifact
reduce_group[group_key] = reduced_artifacts
return reduce_group
for key in artifacts_key:
artifact = rec.load_object(self.artifacts_path[key])
ensemble_dict[key][group_key] = artifact
class RollingCollector(RecorderCollector):
"""
Collect the record results of the rolling tasks
"""
if isinstance(artifacts_key, str):
return ensemble(ensemble_dict[artifacts_key])
def __init__(self, experiment_name: str):
super().__init__(experiment_name)
self.logger = get_module_logger(self.__class__.__name__)
def default_merge(self, artifact_list):
"""merge disorderly artifacts based on the datetime.
Args:
artifact_list (list): a list of artifacts from different recorders
Returns:
merged artifact
"""
# Make sure the pred are sorted according to the rolling start time
artifact_list.sort(key=lambda x: x.index.get_level_values("datetime").min())
artifact = pd.concat(artifact_list)
# If there are duplicated predition, we use the latest perdiction
artifact = artifact[~artifact.index.duplicated(keep="last")]
artifact = artifact.sort_index()
return artifact
collect_dict = {}
for key in artifacts_key:
collect_dict[key] = ensemble(ensemble_dict[key])
return collect_dict