From 19d93744f3678881a29e110a3b2b4ea48b070fee Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Fri, 5 Mar 2021 07:15:25 +0000 Subject: [PATCH 01/13] Add set_log_basic_config function support re-directing log stream --- qlib/config.py | 8 +++---- qlib/log.py | 31 +++++++++++++++++++++------ qlib/workflow/recorder.py | 45 +++++++++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/qlib/config.py b/qlib/config.py index 52b05568d57..75ab0fa3e8e 100644 --- a/qlib/config.py +++ b/qlib/config.py @@ -105,7 +105,7 @@ def set_conf_from_C(self, config_c): "redis_port": 6379, "redis_task_db": 1, # This value can be reset via qlib.init - "logging_level": "INFO", + "logging_level": logging.INFO, # Global configuration of qlib log # logging_level can control the logging level more finely "logging_config": { @@ -124,12 +124,12 @@ def set_conf_from_C(self, config_c): "handlers": { "console": { "class": "logging.StreamHandler", - "level": "DEBUG", + "level": logging.DEBUG, "formatter": "logger_format", "filters": ["field_not_found"], } }, - "loggers": {"qlib": {"level": "DEBUG", "handlers": ["console"]}}, + "loggers": {"qlib": {"level": logging.DEBUG, "handlers": ["console"]}}, }, # Defatult config for experiment manager "exp_manager": { @@ -185,7 +185,7 @@ def set_conf_from_C(self, config_c): # The nfs should be auto-mounted by qlib on other # serversS(such as PAI) [auto_mount:True] "timeout": 100, - "logging_level": "INFO", + "logging_level": logging.INFO, "region": REG_CN, ## Custom Operator "custom_ops": [], diff --git a/qlib/log.py b/qlib/log.py index 6553dcb114e..78f12eb09b6 100644 --- a/qlib/log.py +++ b/qlib/log.py @@ -3,8 +3,7 @@ import logging -import logging.handlers -import os +from typing import Optional, Text, Dict, Any import re from logging import config as logging_config from time import time @@ -13,16 +12,13 @@ from .config import C -def get_module_logger(module_name, level=None): +def get_module_logger(module_name, level: Optional[int] = None): """ Get a logger for a specific module. :param module_name: str Logic module name. :param level: int - :param sh_level: int - Stream handler log level. - :param log_format: str :return: Logger Logger object. """ @@ -103,7 +99,7 @@ def logt(cls, name="", show_start=False): cls.log_cost_time(info=f"{name} Done") -def set_log_with_config(log_config: dict): +def set_log_with_config(log_config: Dict[Text, Any]): """set log with config :param log_config: @@ -112,6 +108,27 @@ def set_log_with_config(log_config: dict): logging_config.dictConfig(log_config) +def set_log_basic_config(filename: Optional[Text] = None, format: Optional[Text] = None, level: Optional[int] = None): + """ + Set the basic configuration for the logging system. + See details at https://docs.python.org/3/library/logging.html#logging.basicConfig + + :param filename: str or None + The path to save the logs. + :param format: the logging format + :param level: int + :return: Logger + Logger object. + """ + if level is None: + level = C.logging_level + + if format is None: + format = C.logging_config["formatters"]["logger_format"]["format"] + + logging.basicConfig(filename=filename, format=format, level=level) + + class LogFilter(logging.Filter): def __init__(self, param=None): self.param = param diff --git a/qlib/workflow/recorder.py b/qlib/workflow/recorder.py index 31077176dc4..519b69710c7 100644 --- a/qlib/workflow/recorder.py +++ b/qlib/workflow/recorder.py @@ -11,7 +11,7 @@ logger = get_module_logger("workflow", "INFO") -class Recorder(object): +class Recorder: """ This is the `Recorder` class for logging the experiments. The API is designed similar to mlflow. (The link: https://mlflow.org/docs/latest/python_api/mlflow.html) @@ -201,7 +201,7 @@ class MLflowRecorder(Recorder): def __init__(self, experiment_id, uri, name=None, mlflow_run=None): super(MLflowRecorder, self).__init__(experiment_id, name) self._uri = uri - self.artifact_uri = None + self._artifact_uri = None self.client = mlflow.tracking.MlflowClient(tracking_uri=self._uri) # construct from mlflow run if mlflow_run is not None: @@ -220,14 +220,45 @@ def __init__(self, experiment_id, uri, name=None, mlflow_run=None): else None ) + def __repr__(self): + name = self.__class__.__name__ + space_length = len(name) + 1 + return "{name}(info={info},\n{space}uri={uri},\n{space}artifact_uri={artifact_uri},\n{space}client={client})".format( + name=name, + space=" " * space_length, + info=self.info, + uri=self.uri, + artifact_uri=self.artifact_uri, + client=self.client, + ) + + @property + def uri(self): + return self._uri + + @property + def artifact_uri(self): + return self._artifact_uri + + @property + def root_uri(self): + start_str = "file:" + if self.artifact_uri is not None: + xpath = self.artifact_uri.strip(start_str) + return (Path(xpath) / "..").resolve() + else: + raise Exception( + "Please make sure the recorder has been created and started properly before getting artifact uri." + ) + def start_run(self): # set the tracking uri - mlflow.set_tracking_uri(self._uri) + mlflow.set_tracking_uri(self.uri) # start the run run = mlflow.start_run(self.id, self.experiment_id, self.name) # save the run id and artifact_uri self.id = run.info.run_id - self.artifact_uri = run.info.artifact_uri + self._artifact_uri = run.info.artifact_uri self.start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.status = Recorder.STATUS_R logger.info(f"Recorder {self.id} starts running under Experiment {self.experiment_id} ...") @@ -247,7 +278,7 @@ def end_run(self, status: str = Recorder.STATUS_S): self.status = status def save_objects(self, local_path=None, artifact_path=None, **kwargs): - assert self._uri is not None, "Please start the experiment and recorder first before using recorder directly." + assert self.uri is not None, "Please start the experiment and recorder first before using recorder directly." if local_path is not None: self.client.log_artifacts(self.id, local_path, artifact_path) else: @@ -259,7 +290,7 @@ def save_objects(self, local_path=None, artifact_path=None, **kwargs): shutil.rmtree(temp_dir) def load_object(self, name): - assert self._uri is not None, "Please start the experiment and recorder first before using recorder directly." + assert self.uri is not None, "Please start the experiment and recorder first before using recorder directly." path = self.client.download_artifacts(self.id, name) with Path(path).open("rb") as f: return pickle.load(f) @@ -289,7 +320,7 @@ def get_artifact_uri(self): ) def list_artifacts(self, artifact_path=None): - assert self._uri is not None, "Please start the experiment and recorder first before using recorder directly." + assert self.uri is not None, "Please start the experiment and recorder first before using recorder directly." artifacts = self.client.list_artifacts(self.id, artifact_path) return [art.path for art in artifacts] From 131f0e2e676fb6368d9e76d05be4d17a3512741b Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Fri, 5 Mar 2021 12:07:43 +0000 Subject: [PATCH 02/13] Add count_parameters for pytorch models in contrib --- qlib/contrib/model/pytorch_alstm.py | 10 +++++++--- qlib/contrib/model/pytorch_alstm_ts.py | 6 +++--- qlib/contrib/model/pytorch_gru.py | 6 +++++- qlib/contrib/model/pytorch_gru_ts.py | 8 ++++++-- qlib/contrib/model/pytorch_lstm.py | 2 +- qlib/contrib/model/pytorch_lstm_ts.py | 2 +- qlib/contrib/model/pytorch_nn.py | 6 +++--- qlib/contrib/model/pytorch_utils.py | 21 +++++++++++++++++++++ 8 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 qlib/contrib/model/pytorch_utils.py diff --git a/qlib/contrib/model/pytorch_alstm.py b/qlib/contrib/model/pytorch_alstm.py index bbbb61851b1..6df996e1117 100644 --- a/qlib/contrib/model/pytorch_alstm.py +++ b/qlib/contrib/model/pytorch_alstm.py @@ -23,6 +23,7 @@ import torch.nn as nn import torch.optim as optim +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH from ...data.dataset.handler import DataHandlerLP @@ -39,8 +40,8 @@ class ALSTM(Model): the evaluate metric used in early stop optimizer : str optimizer name - GPU : str - the GPU ID(s) used for training + GPU : int + the GPU ID used for training """ def __init__( @@ -76,7 +77,7 @@ def __init__( self.early_stop = early_stop self.optimizer = optimizer.lower() self.loss = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.use_gpu = torch.cuda.is_available() self.seed = seed @@ -123,6 +124,9 @@ def __init__( num_layers=self.num_layers, dropout=self.dropout, ) + self.logger.info("model:\n{:}".format(self.ALSTM_model)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.ALSTM_model))) + if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.ALSTM_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": diff --git a/qlib/contrib/model/pytorch_alstm_ts.py b/qlib/contrib/model/pytorch_alstm_ts.py index 725568de855..c8854e8d339 100644 --- a/qlib/contrib/model/pytorch_alstm_ts.py +++ b/qlib/contrib/model/pytorch_alstm_ts.py @@ -40,8 +40,8 @@ class ALSTM(Model): the evaluate metric used in early stop optimizer : str optimizer name - GPU : str - the GPU ID(s) used for training + GPU : int + the GPU ID used for training """ def __init__( @@ -78,7 +78,7 @@ def __init__( self.early_stop = early_stop self.optimizer = optimizer.lower() self.loss = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.n_jobs = n_jobs self.use_gpu = torch.cuda.is_available() self.seed = seed diff --git a/qlib/contrib/model/pytorch_gru.py b/qlib/contrib/model/pytorch_gru.py index 84f863b9fb0..720e6b4f1ab 100755 --- a/qlib/contrib/model/pytorch_gru.py +++ b/qlib/contrib/model/pytorch_gru.py @@ -23,6 +23,7 @@ import torch.nn as nn import torch.optim as optim +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH from ...data.dataset.handler import DataHandlerLP @@ -76,7 +77,7 @@ def __init__( self.early_stop = early_stop self.optimizer = optimizer.lower() self.loss = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.use_gpu = torch.cuda.is_available() self.seed = seed @@ -123,6 +124,9 @@ def __init__( num_layers=self.num_layers, dropout=self.dropout, ) + self.logger.info("model:\n{:}".format(self.gru_model)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.gru_model))) + if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.gru_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": diff --git a/qlib/contrib/model/pytorch_gru_ts.py b/qlib/contrib/model/pytorch_gru_ts.py index bb6618b854c..cbf1c1add2d 100755 --- a/qlib/contrib/model/pytorch_gru_ts.py +++ b/qlib/contrib/model/pytorch_gru_ts.py @@ -24,6 +24,7 @@ import torch.optim as optim from torch.utils.data import DataLoader +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH, TSDatasetH from ...data.dataset.handler import DataHandlerLP @@ -78,7 +79,7 @@ def __init__( self.early_stop = early_stop self.optimizer = optimizer.lower() self.loss = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.n_jobs = n_jobs self.use_gpu = torch.cuda.is_available() self.seed = seed @@ -127,7 +128,10 @@ def __init__( hidden_size=self.hidden_size, num_layers=self.num_layers, dropout=self.dropout, - ).to(self.device) + ) + self.logger.info("model:\n{:}".format(self.gru_model)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.gru_model))) + if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.GRU_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": diff --git a/qlib/contrib/model/pytorch_lstm.py b/qlib/contrib/model/pytorch_lstm.py index 163d500ec87..61e3724252a 100755 --- a/qlib/contrib/model/pytorch_lstm.py +++ b/qlib/contrib/model/pytorch_lstm.py @@ -76,7 +76,7 @@ def __init__( self.early_stop = early_stop self.optimizer = optimizer.lower() self.loss = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.use_gpu = torch.cuda.is_available() self.seed = seed diff --git a/qlib/contrib/model/pytorch_lstm_ts.py b/qlib/contrib/model/pytorch_lstm_ts.py index cf4f8fb9f1f..44d1366760c 100755 --- a/qlib/contrib/model/pytorch_lstm_ts.py +++ b/qlib/contrib/model/pytorch_lstm_ts.py @@ -78,7 +78,7 @@ def __init__( self.early_stop = early_stop self.optimizer = optimizer.lower() self.loss = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.n_jobs = n_jobs self.use_gpu = torch.cuda.is_available() self.seed = seed diff --git a/qlib/contrib/model/pytorch_nn.py b/qlib/contrib/model/pytorch_nn.py index 16fcea9ff53..f8b0b7748ae 100644 --- a/qlib/contrib/model/pytorch_nn.py +++ b/qlib/contrib/model/pytorch_nn.py @@ -42,8 +42,8 @@ class DNNModelPytorch(Model): learning rate decay steps optimizer : str optimizer name - GPU : str - the GPU ID(s) used for training + GPU : int + the GPU ID used for training """ def __init__( @@ -80,7 +80,7 @@ def __init__( self.lr_decay_steps = lr_decay_steps self.optimizer = optimizer.lower() self.loss_type = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.use_GPU = torch.cuda.is_available() self.seed = seed self.weight_decay = weight_decay diff --git a/qlib/contrib/model/pytorch_utils.py b/qlib/contrib/model/pytorch_utils.py new file mode 100644 index 00000000000..532969eb592 --- /dev/null +++ b/qlib/contrib/model/pytorch_utils.py @@ -0,0 +1,21 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +import numpy as np +import torch.nn as nn + + +def count_parameters(model_or_parameters, unit="mb"): + if isinstance(model_or_parameters, nn.Module): + counts = np.sum(np.prod(v.size()) for v in model_or_parameters.parameters()) + else: + counts = np.sum(np.prod(v.size()) for v in model_or_parameters) + if unit.lower() == "mb": + counts /= 1e6 + elif unit.lower() == "kb": + counts /= 1e3 + elif unit.lower() == "gb": + counts /= 1e9 + elif unit is not None: + raise ValueError("Unknow unit: {:}".format(unit)) + return counts From 49697b1f1568608e3077450b72fe3ed5b92ec1e5 Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Fri, 5 Mar 2021 12:46:41 +0000 Subject: [PATCH 03/13] Show model size for pytorch models --- qlib/contrib/model/pytorch_alstm_ts.py | 6 +++++- qlib/contrib/model/pytorch_gats.py | 10 +++++++--- qlib/contrib/model/pytorch_gats_ts.py | 10 +++++++--- qlib/contrib/model/pytorch_nn.py | 4 ++++ qlib/contrib/model/pytorch_sfm.py | 12 ++++++++---- qlib/contrib/model/pytorch_tabnet.py | 7 +++++-- 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/qlib/contrib/model/pytorch_alstm_ts.py b/qlib/contrib/model/pytorch_alstm_ts.py index c8854e8d339..933905439f0 100644 --- a/qlib/contrib/model/pytorch_alstm_ts.py +++ b/qlib/contrib/model/pytorch_alstm_ts.py @@ -24,6 +24,7 @@ import torch.optim as optim from torch.utils.data import DataLoader +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH, TSDatasetH from ...data.dataset.handler import DataHandlerLP @@ -127,7 +128,10 @@ def __init__( hidden_size=self.hidden_size, num_layers=self.num_layers, dropout=self.dropout, - ).to(self.device) + ) + self.logger.info("model:\n{:}".format(self.ALSTM_model)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.ALSTM_model))) + if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.ALSTM_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": diff --git a/qlib/contrib/model/pytorch_gats.py b/qlib/contrib/model/pytorch_gats.py index 07048e1bc1a..061feaa8474 100644 --- a/qlib/contrib/model/pytorch_gats.py +++ b/qlib/contrib/model/pytorch_gats.py @@ -22,6 +22,7 @@ import torch.nn as nn import torch.optim as optim +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH from ...data.dataset.handler import DataHandlerLP @@ -42,8 +43,8 @@ class GATs(Model): the evaluate metric used in early stop optimizer : str optimizer name - GPU : str - the GPU ID(s) used for training + GPU : int + the GPU ID used for training """ def __init__( @@ -83,7 +84,7 @@ def __init__( self.base_model = base_model self.with_pretrain = with_pretrain self.model_path = model_path - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.use_gpu = torch.cuda.is_available() self.seed = seed @@ -135,6 +136,9 @@ def __init__( dropout=self.dropout, base_model=self.base_model, ) + self.logger.info("model:\n{:}".format(self.GAT_model)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.GAT_model))) + if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.GAT_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": diff --git a/qlib/contrib/model/pytorch_gats_ts.py b/qlib/contrib/model/pytorch_gats_ts.py index 1e94f56e418..b179a6df6da 100644 --- a/qlib/contrib/model/pytorch_gats_ts.py +++ b/qlib/contrib/model/pytorch_gats_ts.py @@ -24,6 +24,7 @@ from torch.utils.data import DataLoader from torch.utils.data import Sampler +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH from ...data.dataset.handler import DataHandlerLP @@ -62,8 +63,8 @@ class GATs(Model): the evaluate metric used in early stop optimizer : str optimizer name - GPU : str - the GPU ID(s) used for training + GPU : int + the GPU ID used for training """ def __init__( @@ -104,7 +105,7 @@ def __init__( self.base_model = base_model self.with_pretrain = with_pretrain self.model_path = model_path - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.n_jobs = n_jobs self.use_gpu = torch.cuda.is_available() self.seed = seed @@ -157,6 +158,9 @@ def __init__( dropout=self.dropout, base_model=self.base_model, ) + self.logger.info("model:\n{:}".format(self.GAT_model)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.GAT_model))) + if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.GAT_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": diff --git a/qlib/contrib/model/pytorch_nn.py b/qlib/contrib/model/pytorch_nn.py index f8b0b7748ae..a51481c858d 100644 --- a/qlib/contrib/model/pytorch_nn.py +++ b/qlib/contrib/model/pytorch_nn.py @@ -15,6 +15,7 @@ import torch.nn as nn import torch.optim as optim +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH from ...data.dataset.handler import DataHandlerLP @@ -129,6 +130,9 @@ def __init__( self._scorer = mean_squared_error if loss == "mse" else roc_auc_score self.dnn_model = Net(input_dim, output_dim, layers, loss=self.loss_type) + self.logger.info("model:\n{:}".format(self.dnn_model)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.dnn_model))) + if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.dnn_model.parameters(), lr=self.lr, weight_decay=self.weight_decay) elif optimizer.lower() == "gd": diff --git a/qlib/contrib/model/pytorch_sfm.py b/qlib/contrib/model/pytorch_sfm.py index d5169e6c7bd..55058797a28 100644 --- a/qlib/contrib/model/pytorch_sfm.py +++ b/qlib/contrib/model/pytorch_sfm.py @@ -23,6 +23,7 @@ import torch.nn.init as init import torch.optim as optim +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH from ...data.dataset.handler import DataHandlerLP @@ -196,8 +197,8 @@ class SFM(Model): learning rate optimizer : str optimizer name - GPU : str - the GPU ID(s) used for training + GPU : int + the GPU ID used for training """ def __init__( @@ -216,7 +217,7 @@ def __init__( eval_steps=5, loss="mse", optimizer="gd", - GPU="0", + GPU=0, seed=None, **kwargs ): @@ -239,7 +240,7 @@ def __init__( self.eval_steps = eval_steps self.optimizer = optimizer.lower() self.loss = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") + self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu") self.use_gpu = torch.cuda.is_available() self.seed = seed @@ -295,6 +296,9 @@ def __init__( dropout_U=self.dropout_U, device=self.device, ) + self.logger.info("model:\n{:}".format(self.sfm_model)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.sfm_model))) + if optimizer.lower() == "adam": self.train_optimizer = optim.Adam(self.sfm_model.parameters(), lr=self.lr) elif optimizer.lower() == "gd": diff --git a/qlib/contrib/model/pytorch_tabnet.py b/qlib/contrib/model/pytorch_tabnet.py index 62e32d701ce..ff56b056162 100644 --- a/qlib/contrib/model/pytorch_tabnet.py +++ b/qlib/contrib/model/pytorch_tabnet.py @@ -23,6 +23,7 @@ import torch.nn.functional as F from torch.autograd import Function +from .pytorch_utils import count_parameters from ...model.base import Model from ...data.dataset import DatasetH from ...data.dataset.handler import DataHandlerLP @@ -49,7 +50,7 @@ def __init__( loss="mse", metric="", early_stop=20, - GPU="1", + GPU=0, pretrain_loss="custom", ps=0.3, lr=0.01, @@ -75,7 +76,7 @@ def __init__( self.n_epochs = n_epochs self.logger = get_module_logger("TabNet") self.pretrain_n_epochs = pretrain_n_epochs - self.device = "cuda:%s" % (GPU) if torch.cuda.is_available() else "cpu" + self.device = "cuda:%s" % (GPU) if torch.cuda.is_available() and GPU >= 0 else "cpu" self.loss = loss self.metric = metric self.early_stop = early_stop @@ -98,6 +99,8 @@ def __init__( self.tabnet_decoder = TabNet_Decoder(self.out_dim, self.d_feat, n_shared, n_ind, vbs, n_steps, self.device).to( self.device ) + self.logger.info("model:\n{:}\n{:}".format(self.tabnet_model, self.tabnet_decoder)) + self.logger.info("model size: {:.4f} MB".format(count_parameters(self.tabnet_model) + count_parameters(self.tabnet_decoder))) if optimizer.lower() == "adam": self.pretrain_optimizer = optim.Adam( From 91fd53ab4d0724df73ccf8855ed83b6e1760bb08 Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Sat, 6 Mar 2021 05:33:08 -0800 Subject: [PATCH 04/13] Add reset_default_uri func for R and expm --- qlib/workflow/__init__.py | 11 ++++++++++- qlib/workflow/exp.py | 5 ++++- qlib/workflow/expm.py | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/qlib/workflow/__init__.py b/qlib/workflow/__init__.py index 7bff505ce35..54297ecd7c9 100644 --- a/qlib/workflow/__init__.py +++ b/qlib/workflow/__init__.py @@ -2,6 +2,7 @@ # Licensed under the MIT License. from contextlib import contextmanager +from typing import Text, Optional from .expm import MLflowExpManager from .exp import Experiment from .recorder import Recorder @@ -20,7 +21,9 @@ def __repr__(self): return "{name}(manager={manager})".format(name=self.__class__.__name__, manager=self.exp_manager) @contextmanager - def start(self, experiment_name=None, recorder_name=None, uri=None): + def start( + self, experiment_name: Optional[Text] = None, recorder_name: Optional[Text] = None, uri: Optional[Text] = None + ): """ Method to start an experiment. This method can only be called within a Python's `with` statement. Here is the example code: @@ -282,6 +285,12 @@ def get_uri(self): """ return self.exp_manager.uri + def reset_default_uri(self, uri: Text): + """ + Method to reset the default uri of current experiment manager. + """ + self.exp_manager.reset_default_uri(uri) + def get_recorder(self, recorder_id=None, recorder_name=None, experiment_name=None): """ Method for retrieving a recorder. diff --git a/qlib/workflow/exp.py b/qlib/workflow/exp.py index 18b0a143d93..9cda020c375 100644 --- a/qlib/workflow/exp.py +++ b/qlib/workflow/exp.py @@ -23,7 +23,7 @@ def __init__(self, id, name): self.active_recorder = None # only one recorder can running each time def __repr__(self): - return "{name}(info={info})".format(name=self.__class__.__name__, info=self.info) + return "{name}(id={id}, info={info})".format(name=self.__class__.__name__, id=self.id, info=self.info) def __str__(self): return str(self.info) @@ -175,6 +175,9 @@ def __init__(self, id, name, uri): self._default_rec_name = "mlflow_recorder" self._client = mlflow.tracking.MlflowClient(tracking_uri=self._uri) + def __repr__(self): + return "{name}(id={id}, info={info})".format(name=self.__class__.__name__, id=self.id, info=self.info) + def start(self, recorder_name=None): logger.info(f"Experiment {self.id} starts running ...") # set up recorder diff --git a/qlib/workflow/expm.py b/qlib/workflow/expm.py index 4ba72a63440..f9a5d0252a8 100644 --- a/qlib/workflow/expm.py +++ b/qlib/workflow/expm.py @@ -33,6 +33,10 @@ def __repr__(self): name=self.__class__.__name__, duri=self._default_uri, curi=self._current_uri ) + def reset_default_uri(self, uri: Text): + self._default_uri = uri + self.set_uri(None) + def start_exp( self, experiment_name: Optional[Text] = None, From 73b7107ee89f890456f62fc222f71b2e9f5ed23e Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Sun, 7 Mar 2021 00:52:27 -0800 Subject: [PATCH 05/13] Remove useless verbose kwarg --- qlib/contrib/model/pytorch_alstm.py | 1 - qlib/contrib/model/pytorch_alstm_ts.py | 1 - qlib/contrib/model/pytorch_gats.py | 1 - qlib/contrib/model/pytorch_gats_ts.py | 1 - qlib/contrib/model/pytorch_gru.py | 1 - qlib/contrib/model/pytorch_gru_ts.py | 1 - qlib/contrib/model/pytorch_lstm.py | 1 - qlib/contrib/model/pytorch_lstm_ts.py | 1 - qlib/contrib/model/pytorch_sfm.py | 1 - qlib/contrib/model/pytorch_tabnet.py | 1 - 10 files changed, 10 deletions(-) diff --git a/qlib/contrib/model/pytorch_alstm.py b/qlib/contrib/model/pytorch_alstm.py index 6df996e1117..e8b3167959b 100644 --- a/qlib/contrib/model/pytorch_alstm.py +++ b/qlib/contrib/model/pytorch_alstm.py @@ -218,7 +218,6 @@ def fit( self, dataset: DatasetH, evals_result=dict(), - verbose=True, save_path=None, ): diff --git a/qlib/contrib/model/pytorch_alstm_ts.py b/qlib/contrib/model/pytorch_alstm_ts.py index 933905439f0..1907b9d762c 100644 --- a/qlib/contrib/model/pytorch_alstm_ts.py +++ b/qlib/contrib/model/pytorch_alstm_ts.py @@ -205,7 +205,6 @@ def fit( self, dataset, evals_result=dict(), - verbose=True, save_path=None, ): dl_train = dataset.prepare("train", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L) diff --git a/qlib/contrib/model/pytorch_gats.py b/qlib/contrib/model/pytorch_gats.py index 061feaa8474..7d3b00232ba 100644 --- a/qlib/contrib/model/pytorch_gats.py +++ b/qlib/contrib/model/pytorch_gats.py @@ -236,7 +236,6 @@ def fit( self, dataset: DatasetH, evals_result=dict(), - verbose=True, save_path=None, ): diff --git a/qlib/contrib/model/pytorch_gats_ts.py b/qlib/contrib/model/pytorch_gats_ts.py index b179a6df6da..99ae3d4da3e 100644 --- a/qlib/contrib/model/pytorch_gats_ts.py +++ b/qlib/contrib/model/pytorch_gats_ts.py @@ -249,7 +249,6 @@ def fit( self, dataset, evals_result=dict(), - verbose=True, save_path=None, ): diff --git a/qlib/contrib/model/pytorch_gru.py b/qlib/contrib/model/pytorch_gru.py index 720e6b4f1ab..d4dc884527b 100755 --- a/qlib/contrib/model/pytorch_gru.py +++ b/qlib/contrib/model/pytorch_gru.py @@ -218,7 +218,6 @@ def fit( self, dataset: DatasetH, evals_result=dict(), - verbose=True, save_path=None, ): diff --git a/qlib/contrib/model/pytorch_gru_ts.py b/qlib/contrib/model/pytorch_gru_ts.py index cbf1c1add2d..1f2137c8fa3 100755 --- a/qlib/contrib/model/pytorch_gru_ts.py +++ b/qlib/contrib/model/pytorch_gru_ts.py @@ -205,7 +205,6 @@ def fit( self, dataset, evals_result=dict(), - verbose=True, save_path=None, ): dl_train = dataset.prepare("train", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L) diff --git a/qlib/contrib/model/pytorch_lstm.py b/qlib/contrib/model/pytorch_lstm.py index 61e3724252a..be8d20a151a 100755 --- a/qlib/contrib/model/pytorch_lstm.py +++ b/qlib/contrib/model/pytorch_lstm.py @@ -214,7 +214,6 @@ def fit( self, dataset: DatasetH, evals_result=dict(), - verbose=True, save_path=None, ): diff --git a/qlib/contrib/model/pytorch_lstm_ts.py b/qlib/contrib/model/pytorch_lstm_ts.py index 44d1366760c..3142f15c526 100755 --- a/qlib/contrib/model/pytorch_lstm_ts.py +++ b/qlib/contrib/model/pytorch_lstm_ts.py @@ -201,7 +201,6 @@ def fit( self, dataset, evals_result=dict(), - verbose=True, save_path=None, ): dl_train = dataset.prepare("train", col_set=["feature", "label"], data_key=DataHandlerLP.DK_L) diff --git a/qlib/contrib/model/pytorch_sfm.py b/qlib/contrib/model/pytorch_sfm.py index 55058797a28..40b991c9c42 100644 --- a/qlib/contrib/model/pytorch_sfm.py +++ b/qlib/contrib/model/pytorch_sfm.py @@ -369,7 +369,6 @@ def fit( self, dataset: DatasetH, evals_result=dict(), - verbose=True, save_path=None, ): diff --git a/qlib/contrib/model/pytorch_tabnet.py b/qlib/contrib/model/pytorch_tabnet.py index ff56b056162..7e47f58dc16 100644 --- a/qlib/contrib/model/pytorch_tabnet.py +++ b/qlib/contrib/model/pytorch_tabnet.py @@ -162,7 +162,6 @@ def fit( self, dataset: DatasetH, evals_result=dict(), - verbose=True, save_path=None, ): if self.pretrain: From d13c9ae01869a31f123285a792b674694f844370 Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Sun, 7 Mar 2021 11:25:53 +0000 Subject: [PATCH 06/13] Avoid dividing zero in model.double_ensemble --- qlib/contrib/model/double_ensemble.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qlib/contrib/model/double_ensemble.py b/qlib/contrib/model/double_ensemble.py index a340489c205..541f74e99ca 100644 --- a/qlib/contrib/model/double_ensemble.py +++ b/qlib/contrib/model/double_ensemble.py @@ -184,7 +184,7 @@ def feature_selection(self, df_train, loss_values): / M ) loss_feat = self.get_loss(y_train.values.squeeze(), pred.values) - g.loc[i_f, "g_value"] = np.mean(loss_feat - loss_values) / np.std(loss_feat - loss_values) + g.loc[i_f, "g_value"] = np.mean(loss_feat - loss_values) / (np.std(loss_feat - loss_values) + 1e-7) x_train_tmp.loc[:, feat] = x_train.loc[:, feat].copy() # one column in train features is all-nan # if g['g_value'].isna().any() From 7bed3b4c2eba8ec0a04bdd2c1227be72d267f820 Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Mon, 8 Mar 2021 06:39:03 +0000 Subject: [PATCH 07/13] Fix python format by black --- qlib/contrib/model/pytorch_tabnet.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qlib/contrib/model/pytorch_tabnet.py b/qlib/contrib/model/pytorch_tabnet.py index 7e47f58dc16..682e1b19fd9 100644 --- a/qlib/contrib/model/pytorch_tabnet.py +++ b/qlib/contrib/model/pytorch_tabnet.py @@ -100,7 +100,9 @@ def __init__( self.device ) self.logger.info("model:\n{:}\n{:}".format(self.tabnet_model, self.tabnet_decoder)) - self.logger.info("model size: {:.4f} MB".format(count_parameters(self.tabnet_model) + count_parameters(self.tabnet_decoder))) + self.logger.info( + "model size: {:.4f} MB".format(count_parameters(self.tabnet_model) + count_parameters(self.tabnet_decoder)) + ) if optimizer.lower() == "adam": self.pretrain_optimizer = optim.Adam( From ca48345b29dd40d6b50c87bd027eca51cb522a05 Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Mon, 8 Mar 2021 08:16:17 +0000 Subject: [PATCH 08/13] Simplify count_parameters --- qlib/contrib/model/pytorch_tabnet.py | 4 +--- qlib/contrib/model/pytorch_utils.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/qlib/contrib/model/pytorch_tabnet.py b/qlib/contrib/model/pytorch_tabnet.py index 682e1b19fd9..020bbaff28a 100644 --- a/qlib/contrib/model/pytorch_tabnet.py +++ b/qlib/contrib/model/pytorch_tabnet.py @@ -100,9 +100,7 @@ def __init__( self.device ) self.logger.info("model:\n{:}\n{:}".format(self.tabnet_model, self.tabnet_decoder)) - self.logger.info( - "model size: {:.4f} MB".format(count_parameters(self.tabnet_model) + count_parameters(self.tabnet_decoder)) - ) + self.logger.info("model size: {:.4f} MB".format(count_parameters([self.tabnet_model, self.tabnet_decoder]))) if optimizer.lower() == "adam": self.pretrain_optimizer = optim.Adam( diff --git a/qlib/contrib/model/pytorch_utils.py b/qlib/contrib/model/pytorch_utils.py index 532969eb592..f2457c9a6d5 100644 --- a/qlib/contrib/model/pytorch_utils.py +++ b/qlib/contrib/model/pytorch_utils.py @@ -1,15 +1,18 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -import numpy as np import torch.nn as nn -def count_parameters(model_or_parameters, unit="mb"): - if isinstance(model_or_parameters, nn.Module): - counts = np.sum(np.prod(v.size()) for v in model_or_parameters.parameters()) +def count_parameters(models_or_parameters, unit="mb"): + if isinstance(models_or_parameters, nn.Module): + counts = sum(v.numel() for v in models_or_parameters.parameters()) + elif isinstance(models_or_parameters, nn.Parameter): + counts = models_or_parameters.numel() + elif isinstance(models_or_parameters, (list, tuple)): + return sum(count_parameters(x, unit) for x in model_or_parameters) else: - counts = np.sum(np.prod(v.size()) for v in model_or_parameters) + counts = sum(v.numel() for v in models_or_parameters) if unit.lower() == "mb": counts /= 1e6 elif unit.lower() == "kb": From 03ef918dd8efb22d4b0561b281420795ec028c89 Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Mon, 8 Mar 2021 08:24:23 +0000 Subject: [PATCH 09/13] Fix bugs in count_parameters --- qlib/contrib/model/pytorch_utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qlib/contrib/model/pytorch_utils.py b/qlib/contrib/model/pytorch_utils.py index f2457c9a6d5..c0d483db3ad 100644 --- a/qlib/contrib/model/pytorch_utils.py +++ b/qlib/contrib/model/pytorch_utils.py @@ -3,14 +3,13 @@ import torch.nn as nn - def count_parameters(models_or_parameters, unit="mb"): if isinstance(models_or_parameters, nn.Module): counts = sum(v.numel() for v in models_or_parameters.parameters()) elif isinstance(models_or_parameters, nn.Parameter): counts = models_or_parameters.numel() elif isinstance(models_or_parameters, (list, tuple)): - return sum(count_parameters(x, unit) for x in model_or_parameters) + return sum(count_parameters(x, unit) for x in models_or_parameters) else: counts = sum(v.numel() for v in models_or_parameters) if unit.lower() == "mb": From e061443560a9cdaef837760c9a485fedd0899007 Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Mon, 8 Mar 2021 08:27:58 +0000 Subject: [PATCH 10/13] Fix lint error with Black --- qlib/contrib/model/pytorch_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qlib/contrib/model/pytorch_utils.py b/qlib/contrib/model/pytorch_utils.py index c0d483db3ad..e7a8e8d672f 100644 --- a/qlib/contrib/model/pytorch_utils.py +++ b/qlib/contrib/model/pytorch_utils.py @@ -3,6 +3,7 @@ import torch.nn as nn + def count_parameters(models_or_parameters, unit="mb"): if isinstance(models_or_parameters, nn.Module): counts = sum(v.numel() for v in models_or_parameters.parameters()) From f6ed1750702eae3fce06f0f3f1c0fc5a0f377e3c Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Thu, 11 Mar 2021 02:33:00 +0000 Subject: [PATCH 11/13] Remove set_log_basic_config, refine count_parameters, rename root_uri as get_local_dir --- qlib/contrib/model/pytorch_utils.py | 27 ++++++++++++++++++++------- qlib/log.py | 21 --------------------- qlib/workflow/recorder.py | 16 +++++++++++----- tests/test_all_pipeline.py | 2 ++ 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/qlib/contrib/model/pytorch_utils.py b/qlib/contrib/model/pytorch_utils.py index e7a8e8d672f..1148a596af5 100644 --- a/qlib/contrib/model/pytorch_utils.py +++ b/qlib/contrib/model/pytorch_utils.py @@ -4,7 +4,19 @@ import torch.nn as nn -def count_parameters(models_or_parameters, unit="mb"): +def count_parameters(models_or_parameters, unit="m"): + """ + This function is to obtain the storage size unit of a (or multiple) models. + + Parameters + ---------- + models_or_parameters : PyTorch model(s) or a list of parameters. + unit : the storage size unit. + + Returns + ------- + The number of parameters of the given model(s) or parameters. + """ if isinstance(models_or_parameters, nn.Module): counts = sum(v.numel() for v in models_or_parameters.parameters()) elif isinstance(models_or_parameters, nn.Parameter): @@ -13,12 +25,13 @@ def count_parameters(models_or_parameters, unit="mb"): return sum(count_parameters(x, unit) for x in models_or_parameters) else: counts = sum(v.numel() for v in models_or_parameters) - if unit.lower() == "mb": - counts /= 1e6 - elif unit.lower() == "kb": - counts /= 1e3 - elif unit.lower() == "gb": - counts /= 1e9 + unit = unit.lower() + if unit == "kb" or unit == "k": + counts /= 2 ** 10 + elif unit == "mb" or unit == "m": + counts /= 2 ** 20 + elif unit == "gb" or unit == "g": + counts /= 2 ** 30 elif unit is not None: raise ValueError("Unknow unit: {:}".format(unit)) return counts diff --git a/qlib/log.py b/qlib/log.py index 78f12eb09b6..126acb9d26c 100644 --- a/qlib/log.py +++ b/qlib/log.py @@ -108,27 +108,6 @@ def set_log_with_config(log_config: Dict[Text, Any]): logging_config.dictConfig(log_config) -def set_log_basic_config(filename: Optional[Text] = None, format: Optional[Text] = None, level: Optional[int] = None): - """ - Set the basic configuration for the logging system. - See details at https://docs.python.org/3/library/logging.html#logging.basicConfig - - :param filename: str or None - The path to save the logs. - :param format: the logging format - :param level: int - :return: Logger - Logger object. - """ - if level is None: - level = C.logging_level - - if format is None: - format = C.logging_config["formatters"]["logger_format"]["format"] - - logging.basicConfig(filename=filename, format=format, level=level) - - class LogFilter(logging.Filter): def __init__(self, param=None): self.param = param diff --git a/qlib/workflow/recorder.py b/qlib/workflow/recorder.py index 519b69710c7..bc0a9ef7757 100644 --- a/qlib/workflow/recorder.py +++ b/qlib/workflow/recorder.py @@ -240,12 +240,18 @@ def uri(self): def artifact_uri(self): return self._artifact_uri - @property - def root_uri(self): - start_str = "file:" + def get_local_dir(self): + """ + This function will return the directory path of this recorder. + """ if self.artifact_uri is not None: - xpath = self.artifact_uri.strip(start_str) - return (Path(xpath) / "..").resolve() + local_file_prefix = "file:" + if self.artifact_uri.startswith(local_file_prefix): + xpath = self.artifact_uri.lstrip(local_file_prefix) + return (Path(xpath) / "..").resolve() + else: + raise RuntimeError("This recorder is not saved in the local file system.") + else: raise Exception( "Please make sure the recorder has been created and started properly before getting artifact uri." diff --git a/tests/test_all_pipeline.py b/tests/test_all_pipeline.py index d9d684697c0..fbf15d29ad7 100644 --- a/tests/test_all_pipeline.py +++ b/tests/test_all_pipeline.py @@ -123,6 +123,8 @@ def train(): recorder = R.get_recorder() # To test __repr__ print(recorder) + # To test get_local_dir + print(recorder.get_local_dir()) rid = recorder.id sr = SignalRecord(model, dataset, recorder) sr.generate() From cda96be8c3cf8554587c06857c6e35cbe5adbd6d Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Thu, 11 Mar 2021 02:49:03 +0000 Subject: [PATCH 12/13] Refine default uri in expm --- qlib/workflow/__init__.py | 6 +++--- qlib/workflow/expm.py | 25 ++++++++++++++----------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/qlib/workflow/__init__.py b/qlib/workflow/__init__.py index 54297ecd7c9..834bd059e68 100644 --- a/qlib/workflow/__init__.py +++ b/qlib/workflow/__init__.py @@ -285,11 +285,11 @@ def get_uri(self): """ return self.exp_manager.uri - def reset_default_uri(self, uri: Text): + def set_uri(self, uri: Optional[Text]): """ - Method to reset the default uri of current experiment manager. + Method to reset the current uri of current experiment manager. """ - self.exp_manager.reset_default_uri(uri) + self.exp_manager.set_uri(uri) def get_recorder(self, recorder_id=None, recorder_name=None, experiment_name=None): """ diff --git a/qlib/workflow/expm.py b/qlib/workflow/expm.py index f9a5d0252a8..56fe810c38d 100644 --- a/qlib/workflow/expm.py +++ b/qlib/workflow/expm.py @@ -10,6 +10,7 @@ from typing import Optional, Text from .exp import MLflowExperiment, Experiment +from ..config import C from .recorder import Recorder from ..log import get_module_logger @@ -23,19 +24,12 @@ class ExpManager: """ def __init__(self, uri: Text, default_exp_name: Optional[Text]): - self._default_uri = uri - self._current_uri = None + self._current_uri = uri self.default_exp_name = default_exp_name self.active_experiment = None # only one experiment can active each time def __repr__(self): - return "{name}(default_uri={duri}, current_uri={curi})".format( - name=self.__class__.__name__, duri=self._default_uri, curi=self._current_uri - ) - - def reset_default_uri(self, uri: Text): - self._default_uri = uri - self.set_uri(None) + return "{name}(current_uri={curi})".format(name=self.__class__.__name__, curi=self._current_uri) def start_exp( self, @@ -221,6 +215,15 @@ def delete_exp(self, experiment_id=None, experiment_name=None): """ raise NotImplementedError(f"Please implement the `delete_exp` method.") + @property + def default_uri(self): + """ + Get the default tracking URI from qlib.config.C + """ + if "kwargs" not in C.exp_manager or "uri" not in C.exp_manager["kwargs"]: + raise ValueError("The default URI is not set in qlib.config.C") + return C.exp_manager["kwargs"]["uri"] + @property def uri(self): """ @@ -230,7 +233,7 @@ def uri(self): ------- The tracking URI string. """ - return self._current_uri or self._default_uri + return self._current_uri or self.default_uri def set_uri(self, uri: Optional[Text] = None): """ @@ -243,7 +246,7 @@ def set_uri(self, uri: Optional[Text] = None): """ if uri is None: logger.info("No tracking URI is provided. Use the default tracking URI.") - self._current_uri = self._default_uri + self._current_uri = self.default_uri else: # Temporarily re-set the current uri as the uri argument. self._current_uri = uri From 0ef7c8e0e62f1b08f89f676eac97cbbd2bcc1657 Mon Sep 17 00:00:00 2001 From: D-X-Y <280835372@qq.com> Date: Thu, 11 Mar 2021 03:05:31 +0000 Subject: [PATCH 13/13] Fix bugs for get_local_dir --- qlib/contrib/backtest/account.py | 7 +++---- qlib/contrib/online/operator.py | 2 +- qlib/workflow/cli.py | 2 +- qlib/workflow/recorder.py | 8 ++++---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/qlib/contrib/backtest/account.py b/qlib/contrib/backtest/account.py index d8b28501903..a614f08b674 100644 --- a/qlib/contrib/backtest/account.py +++ b/qlib/contrib/backtest/account.py @@ -104,10 +104,9 @@ def update_daily_end(self, today, trader): # if suspend, no new price to be updated, profit is 0 if trader.check_stock_suspended(code, today): continue - else: - today_close = trader.get_close(code, today) - profit += (today_close - self.current.position[code]["price"]) * self.current.position[code]["amount"] - self.current.update_stock_price(stock_id=code, price=today_close) + today_close = trader.get_close(code, today) + profit += (today_close - self.current.position[code]["price"]) * self.current.position[code]["amount"] + self.current.update_stock_price(stock_id=code, price=today_close) self.rtn += profit # update holding day count self.current.add_count_all() diff --git a/qlib/contrib/online/operator.py b/qlib/contrib/online/operator.py index c8b44f57858..d2307dad545 100644 --- a/qlib/contrib/online/operator.py +++ b/qlib/contrib/online/operator.py @@ -148,7 +148,7 @@ def execute(self, date, exchange_config, path): for user_id, user in um.users.items(): dates, trade_exchange = prepare(um, trade_date, user_id, exchange_config) executor = SimulatorExecutor(trade_exchange=trade_exchange) - if not str(dates[0].date()) == str(pred_date.date()): + if str(dates[0].date()) != str(pred_date.date()): raise ValueError( "The account data is not newest! last trading date {}, today {}".format( dates[0].date(), trade_date.date() diff --git a/qlib/workflow/cli.py b/qlib/workflow/cli.py index 6eba962779b..879c0aaeb5f 100644 --- a/qlib/workflow/cli.py +++ b/qlib/workflow/cli.py @@ -16,7 +16,7 @@ def get_path_list(path): if isinstance(path, str): return [path] else: - return [p for p in path] + return list(path) def sys_config(config, config_path): diff --git a/qlib/workflow/recorder.py b/qlib/workflow/recorder.py index bc0a9ef7757..5915e58da64 100644 --- a/qlib/workflow/recorder.py +++ b/qlib/workflow/recorder.py @@ -245,10 +245,10 @@ def get_local_dir(self): This function will return the directory path of this recorder. """ if self.artifact_uri is not None: - local_file_prefix = "file:" - if self.artifact_uri.startswith(local_file_prefix): - xpath = self.artifact_uri.lstrip(local_file_prefix) - return (Path(xpath) / "..").resolve() + local_dir_path = Path(self.artifact_uri.lstrip("file:")) / ".." + local_dir_path = str(local_dir_path.resolve()) + if os.path.isdir(local_dir_path): + return local_dir_path else: raise RuntimeError("This recorder is not saved in the local file system.")