1
0
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:
zhupr
2021-05-27 14:18:17 +08:00
parent 114162693f
commit 0a4e241608
8 changed files with 419 additions and 265 deletions

View 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)

View File

@@ -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()

View File

@@ -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)

View File

@@ -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(

View File

@@ -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(

View File

@@ -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)

View File

View 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
)