mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-15 08:46:56 +08:00
add get_feature_importance to model interpret
This commit is contained in:
81
examples/model_interpreter.py
Normal file
81
examples/model_interpreter.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
|
||||||
|
import qlib
|
||||||
|
from qlib.config import REG_CN
|
||||||
|
|
||||||
|
from qlib.utils import exists_qlib_data, init_instance_by_config
|
||||||
|
from qlib.tests.data import GetData
|
||||||
|
|
||||||
|
market = "csi300"
|
||||||
|
benchmark = "SH000300"
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# config
|
||||||
|
###################################
|
||||||
|
data_handler_config = {
|
||||||
|
"start_time": "2008-01-01",
|
||||||
|
"end_time": "2020-08-01",
|
||||||
|
"fit_start_time": "2008-01-01",
|
||||||
|
"fit_end_time": "2014-12-31",
|
||||||
|
"instruments": market,
|
||||||
|
}
|
||||||
|
|
||||||
|
task = {
|
||||||
|
"model": {
|
||||||
|
"class": "LGBModel",
|
||||||
|
"module_path": "qlib.contrib.model.gbdt",
|
||||||
|
"kwargs": {
|
||||||
|
"loss": "mse",
|
||||||
|
"colsample_bytree": 0.8879,
|
||||||
|
"learning_rate": 0.0421,
|
||||||
|
"subsample": 0.8789,
|
||||||
|
"lambda_l1": 205.6999,
|
||||||
|
"lambda_l2": 580.9768,
|
||||||
|
"max_depth": 8,
|
||||||
|
"num_leaves": 210,
|
||||||
|
"num_threads": 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"dataset": {
|
||||||
|
"class": "DatasetH",
|
||||||
|
"module_path": "qlib.data.dataset",
|
||||||
|
"kwargs": {
|
||||||
|
"handler": {
|
||||||
|
"class": "Alpha158",
|
||||||
|
"module_path": "qlib.contrib.data.handler",
|
||||||
|
"kwargs": data_handler_config,
|
||||||
|
},
|
||||||
|
"segments": {
|
||||||
|
"train": ("2008-01-01", "2014-12-31"),
|
||||||
|
"valid": ("2015-01-01", "2016-12-31"),
|
||||||
|
"test": ("2017-01-01", "2020-08-01"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# use default data
|
||||||
|
provider_uri = "~/.qlib/qlib_data/cn_data" # target_dir
|
||||||
|
if not exists_qlib_data(provider_uri):
|
||||||
|
print(f"Qlib data is not found in {provider_uri}")
|
||||||
|
GetData().qlib_data(target_dir=provider_uri, region=REG_CN)
|
||||||
|
|
||||||
|
qlib.init(provider_uri=provider_uri, region=REG_CN)
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# train model
|
||||||
|
###################################
|
||||||
|
# model initialization
|
||||||
|
model = init_instance_by_config(task["model"])
|
||||||
|
dataset = init_instance_by_config(task["dataset"])
|
||||||
|
model.fit(dataset)
|
||||||
|
|
||||||
|
# get model feature importance
|
||||||
|
feature_importance = model.get_feature_importance()
|
||||||
|
print("feature importance:")
|
||||||
|
print(feature_importance)
|
||||||
@@ -10,9 +10,10 @@ from catboost.utils import get_gpu_device_count
|
|||||||
from ...model.base import Model
|
from ...model.base import Model
|
||||||
from ...data.dataset import DatasetH
|
from ...data.dataset import DatasetH
|
||||||
from ...data.dataset.handler import DataHandlerLP
|
from ...data.dataset.handler import DataHandlerLP
|
||||||
|
from ...model.interpret.base import FeatureInt
|
||||||
|
|
||||||
|
|
||||||
class CatBoostModel(Model):
|
class CatBoostModel(Model, FeatureInt):
|
||||||
"""CatBoost Model"""
|
"""CatBoost Model"""
|
||||||
|
|
||||||
def __init__(self, loss="RMSE", **kwargs):
|
def __init__(self, loss="RMSE", **kwargs):
|
||||||
@@ -69,6 +70,18 @@ class CatBoostModel(Model):
|
|||||||
x_test = dataset.prepare(segment, col_set="feature", data_key=DataHandlerLP.DK_I)
|
x_test = dataset.prepare(segment, col_set="feature", data_key=DataHandlerLP.DK_I)
|
||||||
return pd.Series(self.model.predict(x_test.values), index=x_test.index)
|
return pd.Series(self.model.predict(x_test.values), index=x_test.index)
|
||||||
|
|
||||||
|
def get_feature_importance(self, *args, **kwargs) -> pd.Series:
|
||||||
|
"""get feature importance
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
parameters references:
|
||||||
|
https://catboost.ai/docs/concepts/python-reference_catboost_get_feature_importance.html#python-reference_catboost_get_feature_importance
|
||||||
|
"""
|
||||||
|
return pd.Series(
|
||||||
|
data=self.model.get_feature_importance(*args, **kwargs), index=self.model.feature_names_
|
||||||
|
).sort_values(ascending=False)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
cat = CatBoostModel()
|
cat = CatBoostModel()
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ from typing import Text, Union
|
|||||||
from ...model.base import Model
|
from ...model.base import Model
|
||||||
from ...data.dataset import DatasetH
|
from ...data.dataset import DatasetH
|
||||||
from ...data.dataset.handler import DataHandlerLP
|
from ...data.dataset.handler import DataHandlerLP
|
||||||
|
from ...model.interpret.base import FeatureInt
|
||||||
from ...log import get_module_logger
|
from ...log import get_module_logger
|
||||||
|
|
||||||
|
|
||||||
class DEnsembleModel(Model):
|
class DEnsembleModel(Model, FeatureInt):
|
||||||
"""Double Ensemble Model"""
|
"""Double Ensemble Model"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -121,8 +122,8 @@ class DEnsembleModel(Model):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("LightGBM doesn't support multi-label training")
|
raise ValueError("LightGBM doesn't support multi-label training")
|
||||||
|
|
||||||
dtrain = lgb.Dataset(x_train.values, label=y_train, weight=weights)
|
dtrain = lgb.Dataset(x_train, label=y_train, weight=weights)
|
||||||
dvalid = lgb.Dataset(x_valid.values, label=y_valid)
|
dvalid = lgb.Dataset(x_valid, label=y_valid)
|
||||||
return dtrain, dvalid
|
return dtrain, dvalid
|
||||||
|
|
||||||
def sample_reweight(self, loss_curve, loss_values, k_th):
|
def sample_reweight(self, loss_curve, loss_values, k_th):
|
||||||
@@ -203,8 +204,8 @@ class DEnsembleModel(Model):
|
|||||||
for i_b, b in enumerate(sorted_bins):
|
for i_b, b in enumerate(sorted_bins):
|
||||||
b_feat = features[g["bins"] == b]
|
b_feat = features[g["bins"] == b]
|
||||||
num_feat = int(np.ceil(self.sample_ratios[i_b] * len(b_feat)))
|
num_feat = int(np.ceil(self.sample_ratios[i_b] * len(b_feat)))
|
||||||
res_feat = res_feat + np.random.choice(b_feat, size=num_feat).tolist()
|
res_feat = res_feat + np.random.choice(b_feat, size=num_feat, replace=False).tolist()
|
||||||
return pd.Index(res_feat)
|
return pd.Index(set(res_feat))
|
||||||
|
|
||||||
def get_loss(self, label, pred):
|
def get_loss(self, label, pred):
|
||||||
if self.loss == "mse":
|
if self.loss == "mse":
|
||||||
@@ -249,3 +250,16 @@ class DEnsembleModel(Model):
|
|||||||
x_data, y_data = df_data["feature"].loc[:, features], df_data["label"]
|
x_data, y_data = df_data["feature"].loc[:, features], df_data["label"]
|
||||||
pred_sub = pd.Series(submodel.predict(x_data.values), index=x_data.index)
|
pred_sub = pd.Series(submodel.predict(x_data.values), index=x_data.index)
|
||||||
return pred_sub
|
return pred_sub
|
||||||
|
|
||||||
|
def get_feature_importance(self, *args, **kwargs) -> pd.Series:
|
||||||
|
"""get feature importance
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
parameters reference:
|
||||||
|
https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.Booster.html?highlight=feature_importance#lightgbm.Booster.feature_importance
|
||||||
|
"""
|
||||||
|
res = []
|
||||||
|
for _model, _weight in zip(self.ensemble, self.sub_weights):
|
||||||
|
res.append(pd.Series(_model.feature_importance(*args, **kwargs), index=_model.feature_name()) * _weight)
|
||||||
|
return pd.concat(res, axis=1, sort=False).sum(axis=1).sort_values(ascending=False)
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ from typing import Text, Union
|
|||||||
from ...model.base import ModelFT
|
from ...model.base import ModelFT
|
||||||
from ...data.dataset import DatasetH
|
from ...data.dataset import DatasetH
|
||||||
from ...data.dataset.handler import DataHandlerLP
|
from ...data.dataset.handler import DataHandlerLP
|
||||||
|
from ...model.interpret.base import LightGBMFInt
|
||||||
|
|
||||||
|
|
||||||
class LGBModel(ModelFT):
|
class LGBModel(ModelFT, LightGBMFInt):
|
||||||
"""LightGBM Model"""
|
"""LightGBM Model"""
|
||||||
|
|
||||||
def __init__(self, loss="mse", **kwargs):
|
def __init__(self, loss="mse", **kwargs):
|
||||||
@@ -33,8 +34,8 @@ class LGBModel(ModelFT):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("LightGBM doesn't support multi-label training")
|
raise ValueError("LightGBM doesn't support multi-label training")
|
||||||
|
|
||||||
dtrain = lgb.Dataset(x_train.values, label=y_train)
|
dtrain = lgb.Dataset(x_train, label=y_train)
|
||||||
dvalid = lgb.Dataset(x_valid.values, label=y_valid)
|
dvalid = lgb.Dataset(x_valid, label=y_valid)
|
||||||
return dtrain, dvalid
|
return dtrain, dvalid
|
||||||
|
|
||||||
def fit(
|
def fit(
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
# Copyright (c) Microsoft Corporation.
|
# Copyright (c) Microsoft Corporation.
|
||||||
# Licensed under the MIT License.
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
import warnings
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import lightgbm as lgb
|
import lightgbm as lgb
|
||||||
|
|
||||||
from qlib.model.base import ModelFT
|
from ...model.base import ModelFT
|
||||||
from qlib.data.dataset import DatasetH
|
from ...data.dataset import DatasetH
|
||||||
from qlib.data.dataset.handler import DataHandlerLP
|
from ...data.dataset.handler import DataHandlerLP
|
||||||
import warnings
|
from ...model.interpret.base import LightGBMFInt
|
||||||
|
|
||||||
|
|
||||||
class HFLGBModel(ModelFT):
|
class HFLGBModel(ModelFT, LightGBMFInt):
|
||||||
"""LightGBM Model for high frequency prediction"""
|
"""LightGBM Model for high frequency prediction"""
|
||||||
|
|
||||||
def __init__(self, loss="mse", **kwargs):
|
def __init__(self, loss="mse", **kwargs):
|
||||||
@@ -97,8 +98,8 @@ class HFLGBModel(ModelFT):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("LightGBM doesn't support multi-label training")
|
raise ValueError("LightGBM doesn't support multi-label training")
|
||||||
|
|
||||||
dtrain = lgb.Dataset(x_train.values, label=y_train)
|
dtrain = lgb.Dataset(x_train, label=y_train)
|
||||||
dvalid = lgb.Dataset(x_valid.values, label=y_valid)
|
dvalid = lgb.Dataset(x_valid, label=y_valid)
|
||||||
return dtrain, dvalid
|
return dtrain, dvalid
|
||||||
|
|
||||||
def fit(
|
def fit(
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ from typing import Text, Union
|
|||||||
from ...model.base import Model
|
from ...model.base import Model
|
||||||
from ...data.dataset import DatasetH
|
from ...data.dataset import DatasetH
|
||||||
from ...data.dataset.handler import DataHandlerLP
|
from ...data.dataset.handler import DataHandlerLP
|
||||||
|
from ...model.interpret.base import FeatureInt
|
||||||
|
|
||||||
|
|
||||||
class XGBModel(Model):
|
class XGBModel(Model, FeatureInt):
|
||||||
"""XGBModel Model"""
|
"""XGBModel Model"""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@@ -42,8 +43,8 @@ class XGBModel(Model):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("XGBoost doesn't support multi-label training")
|
raise ValueError("XGBoost doesn't support multi-label training")
|
||||||
|
|
||||||
dtrain = xgb.DMatrix(x_train.values, label=y_train_1d)
|
dtrain = xgb.DMatrix(x_train, label=y_train_1d)
|
||||||
dvalid = xgb.DMatrix(x_valid.values, label=y_valid_1d)
|
dvalid = xgb.DMatrix(x_valid, label=y_valid_1d)
|
||||||
self.model = xgb.train(
|
self.model = xgb.train(
|
||||||
self._params,
|
self._params,
|
||||||
dtrain=dtrain,
|
dtrain=dtrain,
|
||||||
@@ -62,3 +63,13 @@ class XGBModel(Model):
|
|||||||
raise ValueError("model is not fitted yet!")
|
raise ValueError("model is not fitted yet!")
|
||||||
x_test = dataset.prepare(segment, col_set="feature", data_key=DataHandlerLP.DK_I)
|
x_test = dataset.prepare(segment, col_set="feature", data_key=DataHandlerLP.DK_I)
|
||||||
return pd.Series(self.model.predict(xgb.DMatrix(x_test.values)), index=x_test.index)
|
return pd.Series(self.model.predict(xgb.DMatrix(x_test.values)), index=x_test.index)
|
||||||
|
|
||||||
|
def get_feature_importance(self, *args, **kwargs) -> pd.Series:
|
||||||
|
"""get feature importance
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-------
|
||||||
|
parameters reference:
|
||||||
|
https://xgboost.readthedocs.io/en/latest/python/python_api.html#xgboost.Booster.get_score
|
||||||
|
"""
|
||||||
|
return pd.Series(self.model.get_score(*args, **kwargs)).sort_values(ascending=False)
|
||||||
|
|||||||
0
qlib/model/interpret/__init__.py
Normal file
0
qlib/model/interpret/__init__.py
Normal file
33
qlib/model/interpret/base.py
Normal file
33
qlib/model/interpret/base.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Interfaces to interpret models
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureInt:
|
||||||
|
"""Feature (Int)erpreter"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_feature_importance(self) -> pd.Series:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class LightGBMFInt(FeatureInt):
|
||||||
|
"""LightGBM (F)eature (Int)erpreter"""
|
||||||
|
|
||||||
|
def get_feature_importance(self, *args, **kwargs) -> pd.Series:
|
||||||
|
"""get feature importance
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
parameters reference:
|
||||||
|
https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.Booster.html?highlight=feature_importance#lightgbm.Booster.feature_importance
|
||||||
|
"""
|
||||||
|
return pd.Series(self.model.feature_importance(*args, **kwargs), index=self.model.feature_name()).sort_values(
|
||||||
|
ascending=False
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user