HyperGBM

DataCanvas

HyperGBM is an open source project created by DataCanvas .

内容:

概览

关于HyperGBM

HyperGBM是一款全Pipeline自动机器学习工具,可以端到端的完整覆盖从数据清洗、预处理、特征加工和筛选以及模型选择和超参数优化的全过程,是一个真正的结构化数据AutoML工具包。

大部分的自动机器学习工具主要解决的是算法的超参数优化问题,而HyperGBM是将从数据清洗到算法优化整个的过程放入同一个搜索空间中统一优化。这种端到端的优化过程更接近于SDP(Sequential Decision Process)场景,因此HyperGBM采用了强化学习、蒙特卡洛树搜索等算法并且结合一个meta-leaner来更加高效的解决全Pipeline优化的问题,并且取得了非常出色的效果。

正如名字中的含义,HyperGBM中的机器学习算法使用了目前最流行的几种GBM算法(更准确的说是梯度提升树模型),目前包括XGBoost、LightGBM、CatBoost和HistGridientBoosting。同时,HyperGBM也引入了Hypernets的CompeteExperiment在数据清洗、特征工程、模型融合等环节的很多高级特性。

HyperGBM中的优化算法和搜索空间表示技术以及CompeteExperiment由 Hypernets项目提供支撑。

功能特性

HyperGBM有3中运行模式,分别为:

  • 单机模式:在一台服务器上运行,使用Pandas和Numpy数据结构

  • 单机分布式:在一台服务器上运行,使用Dask数据结构,在运行HyperGBM之前需要创建运行在单机上的Dask集群

  • 多机分布式:在多台服务器上运行,使用Dask数据结构,在运行HyperGBM之前需要创建能管理多台服务器资源的Dask集群

不同运行模式的功能特性支持稍有差异,HyperGBM的功能特性清单及各种的运行模式的支持情况如下表:

功能特性

单机模式

单机分布式

多机分布式

数据清洗

特殊空值字符处理

自动识别类别列

列类型校正

常量列清理

重复列清理

删除标签列为空的样本

非法值替换

id列清理

数据集拆分

按比例拆分

对抗验证

特征工程

特征衍生

特征降维

数据预处理

SimpleImputer

SafeOrdinalEncoder

SafeOneHotEncoder

TruncatedSVD

StandardScaler

MinMaxScaler

MaxAbsScaler

RobustScaler

数据不平衡处理

ClassWeight

降采样(Nearmiss,Tomekslinks,Random)

过采样(SMOTE,ADASYN,Random)

搜索算法

蒙特卡洛树算法

进化算法

随机搜索

历史回放

提前停止策略

最大用时间提前停止

n次搜索都不再提升,提前停止

expected_reward

trail discriminator

建模算法

XGBoost

LightGBM

CatBoost

HistGridientBoosting

评估方法

交叉验证(Cross-Validation)

Train-Validation-Holdout验证

高级特性

自动任务类型推断

共线性特征检测

数据漂移检测

特征筛选

特征筛选(二阶段)

伪标签(二阶段)

通过降采样进行预搜索

模型融合

安装HyperGBM

推荐使用pip命令来安装HyperGBM;如果您有Docker环境,也可以在Docker容器中安装并运行HyperGBM。

使用pip

安装HyperGBM之前,您需要准备Python3.6或以上版本的运行环境并保证 pip 命令可正常运行。 使用pip安装HyperGBM:

pip install hypergbm

可选的, 如果您希望在JupyterLab中使用HyperGBM, 可通过如下命令安装HyperGBM:

pip install hypergbm[notebook]

可选的, 如果您希望在特征衍生时支持中文字符中, 可通过如下命令安装HyperGBM:

pip install hypergbm[zhcn]

可选的, 如果您希望安装HyperGBM以及所有依赖包,则可通过如下形式安装:

pip install hypergbm[all]

使用Docker

HyperGBM支持在Docker容器中运行,您可在Dockerfile中通过 pip 安装HyperGBM,然后使用。

我们在Docker Hub中发布了一个参考镜像,可直接下载使用,该镜像中包括:

  • Python3.7

  • HyperGBM及其依赖包

  • JupyterLab

下载镜像:

docker pull datacanvas/hypergbm

运行镜像:

docker run -ti -e NotebookToken="your-token" -p 8888:8888 datacanvas/hypergbm

打开浏览器,访问http://<your-ip>:8888,输入您设置的token即可开始使用。

快速开始

本章介绍HyperGBM主要功能,假设您已经知道机器学习的基本知识(加载数据、模型训练、预测、评估等),如果您还没安装请参照[安装文档](installation.md)来安装HyperGBM。 您可以通过Python和命令行工具来使用HyperGBM。

通过Python使用HyperGBM

HyperGBM基于Python开发,推荐利用Python工具 make_experiment 创建实验并进行训练得到模型。

通过 make_experiment 训练模型的基本步骤:

  • 准备数据集(pandas 或 dask DataFrame)

  • 通过工具 make_experiment 创建实验

  • 执行实验 .run() 方法进行训练得到模型

  • 利用模型进行数据预测或Python工具 pickle 存储模型

准备数据集

可以根据实际业务情况通过pandas或dask加载数据,得到用于模型训练的DataFrame。

以sklearn内置数据集 breast_cancer 为例,可如下处理得到数据集:

import pandas as pd
from sklearn import datasets
from sklearn.model_selection import train_test_split

X,y = datasets.load_breast_cancer(as_frame=True,return_X_y=True)
X_train,X_test,y_train,y_test = train_test_split(X,y,train_size=0.7,random_state=335)
train_data = pd.concat([X_train,y_train],axis=1)

其中 train_data 用于模型训练(包括目标列),X_testy_test 用于模型评价。

创建实验并进行训练

假设希望最终模型有比较好的precision,为前面准备的训练数据集创建实验并开始训练模型如下:

from hypergbm import make_experiment


experiment = make_experiment(train_data, target='target', reward_metric='precision')
estimator = experiment.run()

其中 estimator 就是训练所得到的模型。

输出模型

推荐利用 pickle 存储HyperGBM模型,如下:

import pickle
with open('model.pkl','wb') as f:
  pickle.dump(estimator, f)
评价模型

可利用sklearn提供的工具进行模型评价,如下:

from sklearn.metrics import classification_report

y_pred=estimator.predict(X_test)
print(classification_report(y_test, y_pred, digits=5))

输出:

              precision    recall  f1-score   support

           0    0.96429   0.93103   0.94737        58
           1    0.96522   0.98230   0.97368       113

    accuracy                        0.96491       171
   macro avg    0.96475   0.95667   0.96053       171
weighted avg    0.96490   0.96491   0.96476       171
更多信息

关于 make_experiment 的更多信息,您可以查看该工具的docstring,如:

print(make_experiment.__doc__)

如果您正在Notebook或IPython中使用HyperGBM, 可以通过如下方式获取 make_experiment 的更多信息:

make_experiment?

通过命令行使用HyperGBM

HyperGBM提供了命令行工具 hypergbm 进行模型训练、评估和预测数据,查看命令行帮助:

hypergm -h

usage: hypergbm [-h] [--log-level LOG_LEVEL] [-error] [-warn] [-info] [-debug]
                [--verbose VERBOSE] [-v] [--enable-dask ENABLE_DASK] [-dask]
                [--overload OVERLOAD]
                {train,evaluate,predict} ...

hypergbm提供三个子命令:trainevaluatepredict,可通过hypergbm <子命令> -h获取更多信息,如:

hypergbm train -h
usage: hypergbm train [-h] --train-data TRAIN_DATA [--eval-data EVAL_DATA]
                      [--test-data TEST_DATA]
                      [--train-test-split-strategy {None,adversarial_validation}]
                      [--target TARGET]
                      [--task {binary,multiclass,regression}]
                      [--max-trials MAX_TRIALS] [--reward-metric METRIC]
                      [--cv CV] [-cv] [-cv-] [--cv-num-folds NUM_FOLDS]
                      [--pos-label POS_LABEL]
                      ...
准备数据

使用命令行工具训练模型时,训练数据必须是csv或parquet格式的文件,并以 .csv.parquet结尾;输出模型是pickle格式,以.pkl结尾。

以训练数据Bank Marketing为例子,可准备数据如下:

from hypernets.tabular.datasets import dsutils
from sklearn.model_selection import train_test_split

df = dsutils.load_bank().head(10000)
df_train, df_test = train_test_split(df, test_size=0.3, random_state=9527)
df_train.to_csv('bank_train.csv', index=None)
df_test.to_csv('bank_eval.csv', index=None)

df_test.pop('y')
df_test.to_csv('bank_to_pred.csv', index=None)

其中:

  • bank_train.csv:用于模型训练

  • bank_eval.csv:用于模型评价

  • bank_to_pred.csv:是没有目标列的数据,用于模拟待预测数据

模型训练

在准备好训练数据之后,可通过命令进行模型训练:

hypergbm train --train-data bank_train.csv --target y --model-file model.pkl

等待命令结束,可看到模型文件:model.pkl

ls -l model.pkl

rw-rw-r-- 1 xx xx 9154959    17:09 model.pkl
模型评价

在模型训练之后,可利用评价数据对所得到的模型进行评价:

hypergbm evaluate --model model.pkl --data bank_eval.csv --metric f1 recall auc

{'f1': 0.7993779160186626, 'recall': 0.7099447513812155, 'auc': 0.9705420982746849}
数据预测

在模型训练之后,可利用所得到的模型进行数据预测:

hypergbm predict --model model.pkl --data bank_to_pred.csv --output bank_output.csv

预测结果会保存到文件bank_output.csv 中。

如果您希望将预测数据的某一列数据(如”id”)与预测结果一起写到输出文件,则可通过参数 --with-data 指定,如:

hypergbm predict --model model.pkl --data bank_to_pred.csv --output bank_output.csv --with-data id
head bank_output.csv

id,y
1563,no
124,no
218,no
463,no
...

如果,您希望输出文件除了包含预测结果之外,还希望有预测数据的所有列,则可将参数 --with-data 设置为”*”,如:

hypergbm predict --model model.pkl --data bank_to_pred.csv --output bank_output.csv --with-data '*'
head bank_output.csv

id,age,job,marital,education,default,balance,housing,loan,contact,day,month,duration,campaign,pdays,previous,poutcome,y
1563,55,entrepreneur,married,secondary,no,204,no,no,cellular,14,jul,455,13,-1,0,unknown,no
124,51,management,single,tertiary,yes,-55,yes,no,cellular,11,may,281,2,266,6,failure,no
218,49,blue-collar,married,primary,no,305,yes,yes,telephone,10,jul,834,10,-1,0,unknown,no
463,35,blue-collar,divorced,secondary,no,3102,yes,no,cellular,20,nov,138,1,-1,0,unknown,no
2058,50,management,divorced,tertiary,no,201,yes,no,cellular,24,jul,248,1,-1,0,unknown,no
...

使用示例

基础应用

本节通过示例讲解如何使用实验进行模型训练,示例中使用数据集blood,该数据集中的目标列Class,数据如下:

Recency,Frequency,Monetary,Time,Class
2,50,12500,98,1
0,13,3250,28,1
1,16,4000,35,1
2,20,5000,45,1
1,24,6000,77,0
4,4,1000,4,0

...

本节示例从hypernets.tabular中加载该数据集。

以缺省配置创建并运行实验

利用工具make_experiment可快速创建一个可运行的实验对象,执行该实验对象的run方法即可开始训练并得到模型。使用该工具时只有实验数据train_data是必须的,其它都是可选项。数据的目标列如果不是y的话,需要通过参数target设置。

示例代码:

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils

train_data = dsutils.load_blood()
experiment = make_experiment(train_data, target='Class')
estimator = experiment.run()
print(estimator)

输出:

Pipeline(steps=[('data_clean',
                 DataCleanStep(...),
                ('estimator',
                 GreedyEnsemble(...)])

可以看出,训练得到的是一个Pipeline,最终模型是由多个模型构成的融合模型。

如果您的训练数据是csv或parquet格式,而且数据文件的扩展名是“.csv”或“.parquet”的话,可以直接使用文件路径创建实验,make_experiment会自动将数据加载为DataFrame,如:

from hypergbm import make_experiment

train_data = '/path/to/mydata.csv'
experiment = make_experiment(train_data, target='my_target')
estimator = experiment.run()
print(estimator)
设置最大搜索次数(max_trials)

缺省情况下,make_experiment所创建的实验最多搜索10种参数便会停止搜索。实际使用中,建议将最大搜索次数设置为30以上。

from hypergbm import make_experiment

train_data = ...
experiment = make_experiment(train_data, max_trials=50)
...
交叉验证

可通过参数cv指定是否启用交叉验证。当cv设置为False时表示禁用交叉验证并使用经典的train_test_split方式进行模型训练;当cv设置为True(缺省)时表示开启交叉验证,折数可通过参数num_folds设置(默认:3)。

启用交叉验证的示例代码:

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils

train_data = dsutils.load_blood()
experiment = make_experiment(train_data, target='Class', cv=True, num_folds=5)
estimator = experiment.run()
print(estimator)
指定验证数据集(eval_data)

在禁用交叉验证时,模型训练除了需要训练数据集,还需要评估数据集,您可在make_experiment时通过eval_data指定评估集,如:

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils
from sklearn.model_selection import train_test_split

train_data = dsutils.load_blood()
train_data,eval_data=train_test_split(train_data,test_size=0.3)
experiment = make_experiment(train_data, target='Class', eval_data=eval_data, cv=False)
estimator = experiment.run()
print(estimator)

在禁用交叉验证时,如果您未指定eval_data,实验对象将从train_data中拆分部分数据作为评估集,拆分大小可通过eval_size设置,如:

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils

train_data = dsutils.load_blood()

experiment = make_experiment(train_data, target='Class', cv=False, eval_size=0.2)
estimator = experiment.run()
print(estimator)
指定模型的评价指标

使用make_experiment创建实验时,分类任务默认的模型评价指标是accuracy,回归任务默认的模型评价指标是rmse,可通过参数reward_metric指定,如:

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils

train_data = dsutils.load_blood()
experiment = make_experiment(train_data, target='Class', reward_metric='auc')
estimator = experiment.run()
print(estimator)
设置搜索次数和早停(Early Stopping)策略

使用make_experiment时,可通过参数early_stopping_roundearly_stopping_time_limitearly_stopping_reward设置实验的早停策略。

将搜索时间设置为最多3小时的示例代码:

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils

train_data = dsutils.load_blood()

experiment = make_experiment(train_data, target='Class', max_trials=300, early_stopping_time_limit=3600 * 3)
estimator = experiment.run()
print(estimator)
指定搜索算法(Searcher)

HyperGBM通过Hypernets中的搜索算法进行参数搜索,包括:EvolutionSearcher(缺省)、MCTSSearcher、RandomSearch,在make_experiment时可通过参数searcher指定,可以指定搜索算法的类名(class)、搜索算法的名称(str)。

示例代码:

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils

train_data = dsutils.load_blood()

experiment = make_experiment(train_data, target='Class', searcher='random')
estimator = experiment.run()
print(estimator)

您也可以自己创建searcher对象,然后用所创建的对象创建实验,如:

from hypergbm import make_experiment
from hypergbm.search_space import search_space_general
from hypernets.searchers import MCTSSearcher
from hypernets.tabular.datasets import dsutils

my_searcher = MCTSSearcher(lambda: search_space_general(n_estimators=100),
                           max_node_space=20,
                           optimize_direction='max')

train_data = dsutils.load_blood()

experiment = make_experiment(train_data, target='Class', searcher=my_searcher)
estimator = experiment.run()
print(estimator)
模型融合

为了获取较好的模型效果,make_experiment创建实验时默认开启了模型融合的特性,并使用效果最好的20个模型进行融合,可通过参数ensemble_size指定参与融合的模型的数量。当ensemble_size设置为0时则表示禁用模型融合。

调整参与融合的模型数量的示例代码:

train_data = ...
experiment = make_experiment(train_data, ensemble_size=10, ...)
调整日志级别

如果希望在训练过程中看到使用进度信息的话,可通过log_level指定日志级别,可以是strint。关于日志级别的详细定义可参考python的logging包。 另外,如果将verbose设置为1的话,可以得到更详细的信息。

将日志级别设置为INFO的示例代码如下:

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils

train_data = dsutils.load_blood()
experiment = make_experiment(train_data, target='Class', log_level='INFO', verbose=1)
estimator = experiment.run()
print(estimator)

输出:

14:24:33 I hypernets.tabular.u._common.py 30 - 2 class detected, {0, 1}, so inferred as a [binary classification] task
14:24:33 I hypergbm.experiment.py 699 - create experiment with ['data_clean', 'drift_detection', 'space_search', 'final_ensemble']
14:24:33 I hypergbm.experiment.py 1262 - make_experiment with train data:(748, 4), test data:None, eval data:None, target:Class
14:24:33 I hypergbm.experiment.py 716 - fit_transform data_clean
14:24:33 I hypergbm.experiment.py 716 - fit_transform drift_detection
14:24:33 I hypergbm.experiment.py 716 - fit_transform space_search
14:24:33 I hypernets.c.meta_learner.py 22 - Initialize Meta Learner: dataset_id:7123e0d8c8bbbac8797ed9e42352dc59
14:24:33 I hypernets.c.callbacks.py 192 - 
Trial No:1
--------------------------------------------------------------
(0) estimator_options.hp_or:                                0
(1) numeric_imputer_0.strategy:                 most_frequent
(2) numeric_scaler_optional_0.hp_opt:                    True


...

14:24:35 I hypergbm.experiment.py 716 - fit_transform final_ensemble
14:24:35 I hypergbm.experiment.py 737 - trained experiment pipeline: ['data_clean', 'estimator']
Pipeline(steps=[('data_clean',
                 DataCleanStep(...),
                ('estimator',
                 GreedyEnsemble(...)

高级应用

HyperGBM make_experiment 所创建的是 Hypernets 的 CompeteExeriment 实例,CompeteExeriment 具备很多建模的高级特性,本章逐一介绍。

flowchart LR dc[数<br/>据<br/>清<br/>洗] fg[特征衍生] cd[共线性检测] dd[漂移检测] fs[特征筛选] s1[优化搜索] pi[二阶段<br/>特征筛选] pl[伪标签] s2[二阶段<br/>优化搜索] em[模<br/>型<br/>融<br/>合] op2[op] subgraph 一阶段 direction LR subgraph op direction TB cd-->dd dd-->fs end fg-->op op-->s1 end subgraph 二阶段 direction LR subgraph op2 direction TB pi-->pl end op2-->s2 end dc-->一阶段-->二阶段-->em style 二阶段 stroke:#6666,stroke-width:2px,stroke-dasharray: 5, 5; style 二阶段 stroke:#6666,stroke-width:2px,stroke-dasharray: 5, 5;
数据清洗

CompeteExeriment 的第一步就是利用Hypernets的DataCleaner进行数据清洗,此步骤不可禁用,但可通过参数对DataCleaner的行为进行调整,包括:

  • nan_chars: value or list, (default None), 将哪些值字符替换为np.nan

  • correct_object_dtype: bool, (default True), 是否尝试修正数据类型

  • drop_constant_columns: bool, (default True), 是否删除常量列

  • drop_duplicated_columns: bool, (default False), 是否删除重复列

  • drop_idness_columns: bool, (default True), 是否删除id列

  • drop_label_nan_rows: bool, (default True), 是否删除目标列为np.nan的行

  • replace_inf_values: (default np.nan), 将np.inf替换为什么值

  • drop_columns: list, (default None), 强行删除哪些列

  • reserve_columns: list, (default None), 数据清洗时保留哪些列不变

  • reduce_mem_usage: bool, (default False), 是否尝试降低对内存的需求

  • int_convert_to: bool, (default ‘float’), 将int列转换为何种类型,None表示不转换

调用 make_expperiment 时,可通过参数 data_cleaner_args 对DataCleaner的配置进行调整。

假设,训练数据中用字符’\N’表示nan,希望在数据清洗阶段将其替换为np.nan,则可如下设置:

from hypergbm import make_experiment

train_data = ...
experiment = make_experiment(train_data, target='...',
                            data_cleaner_args={'nan_chars': r'\N'})
...
特征衍生

CompeteExeriment 中提供了特征衍生的能力, 在通过 make_experiment 创建实验时设置 feature_generation=True 即可,与之匹配的选项包括:

  • feature_generation_continuous_cols:list (default None)), 参与特征衍生的初始连续型特征,如果为None则依据训练数据的特征类型自行推断。

  • feature_generation_categories_cols:list (default None)), 参与特征衍生的初始类别型特征,需要明确指定,CompeteExeriment 不会自行推断参与特征衍生的初始类别型特征。

  • feature_generation_datetime_cols:list (default None), 参与特征衍生的初始日期型特征,如果为None则依据训练数据的特征类型自行推断。

  • feature_generation_latlong_cols:list (default None), 参与特征衍生的经纬度特征,如果为None则依据训练数据自行推断。说明:经纬度特征列的数据格式必须是 tuple(lat,long)

  • feature_generation_text_cols:list (default None), 参与特征衍生的初始文本性特征,如果为None则依据训练数据自行推断。

  • feature_generation_trans_primitives:list (default None), 用于特征衍生的算子,如果为None则依据特征类型自行推断所采用的算子。

当feature_generation_trans_primitives=None时,CompeteExeriment 依据参与特征衍生的初始特征自行推断所采用的算子,针对不同类型的特征采取不同算子,如下:

  • continuous_cols: 无(需自行指定)。

  • categories_cols: cross_categorical。

  • datetime_cols: month、week、day、hour、minute、second、weekday、is_weekend。

  • latlong_cols: haversine、geohash

  • text_cols:tfidf

启用特征衍生的示例代码:

from hypergbm import make_experiment

train_data = ...
experiment = make_experiment(train_data,
                           feature_generation=True,
                           ...)
...

关于特征衍生的更多信息请参考 [featuretools](https://docs.featuretools.com/).

共线性检测

有时训练数据中会出现一些相关度很高的特征,这些并没有提供太多的信息量,相反,数据集拥有更多的特征意味着更容易收到噪声的影响,更容易收到特征偏移的影响等等。

CompeteExeriment 中提供了删除发生共线性的特征的能力, 在通过 make_experiment 创建实验时设置 collinearity_detection=True 即可。

启用共线性检测的示例代码:

from hypergbm import make_experiment

train_data = ...
experiment = make_experiment(train_data, target='...', collinearity_detection=True)
...
漂移检测

数据漂移是建模过程中的一个主要挑战。当数据的分布随着时间在不断的发现变化时,模型的表现会越来越差,CompeteExeriment 中引入了对抗验证的方法专门处理数据漂移问题。这个方法会自动的检测是否发生漂移,并且找出发生漂移的特征并删除他们,以保证模型在真实数据上保持良好的状态。

为了开启飘逸检测,使用 make_experiment 创建实验时需要设置 drift_detection=True (缺省)并提供测试集 test_data

漂移检测相关的参数包括:

  • drift_detection_remove_shift_variable : bool, (default=True),是否首先检查每一列数据的稳定性。

  • drift_detection_variable_shift_threshold : float, (default=0.7),稳定性指标高于该阈值的列将被删除

  • drift_detection_threshold : float, (default=0.7),检测指标高于该阈值的列将被删除。

  • drift_detection_remove_size : float, (default=0.1),每一轮检测所删除的列占总列数的比例。

  • drift_detection_min_features : int, (default=10),至少保留多少列。

  • drift_detection_num_folds : int, (default=5),在漂移检测阶段训练模型时的cv折数。

需要注意的是,启用漂移检测时必须指定 test_data (不包含目标列), 示例代码:

from io import StringIO
import pandas as pd
from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils

test_data = """
Recency,Frequency,Monetary,Time
2,10,2500,64
4,5,1250,23
4,9,2250,46
4,5,1250,23
4,8,2000,40
2,12,3000,82
11,24,6000,64
2,7,1750,46
4,11,2750,61
1,7,1750,57
2,11,2750,79
2,3,750,16
4,5,1250,26
2,6,1500,41
"""

train_data = dsutils.load_blood()
test_df = pd.read_csv(StringIO(test_data))
experiment = make_experiment(train_data, test_data=test_df,
                             drift_detection=True,
                             ...)

...
特征筛选

进行特征筛选也是建模过程中的一个重要环节,CompeteExeriment 通过训练一个常规模型对训练数据的特征重要性进行评估,进而筛选出最重要的特征参与到后续模型训练中。

在通过 make_experiment 创建实验时设置 feature_selection=True 可开启特征筛选,与之匹配的选项包括:

  • feature_selection_strategy:str, 筛选策略(default threshold), 可用的策略包括 thresholdnumberquantile

  • feature_selection_threshold:float, (default 0.1), 当策略为 threshold 时的筛选阈值,重要性高于该阈值的特征会被选择。

  • feature_selection_quantile:float, (default 0.2), 当策略为 quantile 时的筛选阈值,重要性分位高于该阈值的特征会被选择。

  • feature_selection_number:int or float, (default 0.8), 当策略为 number 时,筛选的特征数量。

启用特征筛选的示例代码:

from hypergbm import make_experiment

train_data=...
experiment = make_experiment(train_data,
                             feature_selection=True,
                             feature_selection_strategy='quantile',
                             feature_selection_quantile=0.3,
                             ...)
降采样预搜索

通常,在进行模型参数优化搜索时是使用全部训练数据进行模型训练的,当数据量较大时使用全部训练数据进行模型训练会消耗较长的时间,为此可通过降采样减少参与模型训练的数据量,进行预搜索,以便在相同的时间内尝试更多的模型参数;然后从预搜索结果中挑选表现较好的参数再利用全量数据进行训练和评估,进一步筛选最佳的模型参数。

通过 make_experiment 创建实验时,设置 down_sample_search=True 可开启预搜索,与之相关的选项包括:

  • down_sample_search_size:int, float(0.0~1.0) or dict (default 0.1), 参与预搜索的样本数量。对于分类任务,可通过dict指定每个类别数据的采样数量。

  • down_sample_search_time_limit:int, (default early_stopping_time_limit*0.33), 预搜索的时间限制。

  • down_sample_search_max_trials:int, (default max_trials*3), 预搜索的最大尝试次数。

启用预搜索的示例代码:

from hypergbm import make_experiment

train_data=...
experiment = make_experiment(train_data,
                             down_sample_search=True,
                             down_sample_search_size=0.2,
                             ...)
二阶段特征筛选

CompeteExperiment 支持在模型参数优化搜索之后,利用得到的模型对训练数据进行处理,然后再次进行模型参数优化搜索,即 二阶段搜索。目前 CompeteExperiment 支持的第二阶段数据处理方式包括二阶段特征筛选和伪标签,本章余下的两个小节中分别介绍。

CompeteExperiment 中,二阶段特征筛选是指从第一阶段选择若干表现较好的模型,进行 permutation_importance 评估,然后筛选出重要的特征。

通过 make_experiment 创建实验时,设置 feature_reselection=True 可开启二阶段特征筛选,与之相关的配置项包括:

  • feature_reselection_estimator_size:int, (default=10), 用于评估特征重要性的模型数量(在一阶段搜索中表现最好的n个模型)。

  • feature_reselection_strategy:str, 筛选策略(default threshold), 可用的策略包括 thresholdnumberquantile

  • feature_reselection_threshold:float, (default 1e-5), 当策略为 threshold 时的筛选阈值,重要性高于该阈值的特征会被选择。

  • feature_reselection_quantile:float, (default 0.2), 当策略为 quantile 时的筛选阈值,重要性分位高于该阈值的特征会被选择。

  • feature_reselection_number:int or float, (default 0.8), 当策略为 number 时,筛选的特征数量。

启用二阶段特征筛选的示例代码:

from hypergbm import make_experiment

train_data=...
experiment = make_experiment(train_data,
                             feature_reselection=True,
                             ...)

关于 permutation_importance 的更多信息请参考 [scikit-learn](https://scikit-learn.org/stable/modules/permutation_importance.html)

伪标签

伪标签是一种半监督学习技术,将测试集中未观测标签列的特征数据通过一阶段训练的模型预测标签后,将置信度高于一定阈值的样本添加到训练数据中重新训练模型,有时候可以进一步提升模型在新数据上的拟合效果。

在通过 make_experiment 创建实验时设置 pseudo_labeling=True 可开启伪标签训练,与之相关的配置项包括:

  • pseudo_labeling_strategy:str, 筛选策略(default threshold), 可用的策略包括 thresholdnumberquantile

  • pseudo_labeling_proba_threshold:float(default 0.8), 当策略为 threshold 时的筛选阈值,置信度高于该阈值的样本会被选择。

  • pseudo_labeling_proba_quantile:float(default 0.8), 当策略为 quantile 时的筛选阈值,置信度分位高于该阈值的样本会被选择。

  • pseudo_labeling_sample_number:float(0.0~1.0) or int (default 0.2), 当策略为 number 时,对样本按置信度高低排序后选择的样本数(top n)。

  • pseudo_labeling_resplit:bool(default=False), 添加新的伪标签数据后是否重新分割训练集和评估集. 如果为False, 直接把所有伪标签数据添加到训练集中重新训练模型,否则把训练集、评估集及伪标签数据合并后重新分割。

启用伪标签技术的示例代码:

from hypergbm import make_experiment

train_data=...
test_data=...
experiment = make_experiment(train_data,
                             test_data=test_data,
                             pseudo_labeling=True,
                             ...)

说明: 伪标签 仅对分类任务有效。

处理不平衡数据

数据不平衡也是业务建模过程中的一个重要挑战,数据的不平衡往往会导致建模效果不理想。HyperGBM 在建模时支持两种类型的不平衡数据处理方式,下面分别介绍。

利用ClassWeight建模

在利用底层建模算法(如lightgbm等)建模时,首先计算样本分布比例,然后利用底层算法对模型进行优化。

为了利用ClassWeight建模,在调用make_experiment时,设置参数class_balancing=‘ClassWeight’即可,示例如下:

from hypergbm import make_experiment

train_data = ...
experiment = make_experiment(train_data,
                             class_balancing='ClassWeight',
                             ...)

欠采样或过采样

在建模之前,通过欠采样或过采样技术调整样本数据,然后再利用调整后的数据进行建模,以得到表现较好的模型。目前支持的采样策略包括:RandomOverSamplerSMOTEADASYNRandomUnderSamplerNearMissTomekLinksEditedNearestNeighbours

为了利用欠采样或过采样技建模,在调用make_experiment时,设置参数class_balancing=‘<采用策略>’即可,示例如下:

from hypergbm import make_experiment

train_data = ...
experiment = make_experiment(train_data,
                             class_balancing='SMOTE',
                             ...)

关于欠采样或过采样技术的更多信息,请参考 imbalanced-learn

自定义SearchSpace

make_experiment缺省使用的搜索空间是search_space_general,定义如下:

search_space_general = GeneralSearchSpaceGenerator(n_estimators=200)
自定义搜索空间(SearchSpace)

在调用make_experiment时可通过参数search_space指定自定义的搜索空间。如:指定xgboostmax_depth=20

from hypergbm import make_experiment
from hypergbm.search_space import GeneralSearchSpaceGenerator

my_search_space = \
    GeneralSearchSpaceGenerator(n_estimators=200, xgb_init_kwargs={'max_depth': 20})

train_data = ...

experiment = make_experiment(train_data,
                             search_space=my_search_space,
                             ...)

如果您希望自定义的参数是可搜索的,推荐定义GeneralSearchSpaceGenerator的一个子类,比如:指定xgboostmax_depth在10、20、30之间搜索:

from hypergbm import make_experiment
from hypergbm.search_space import GeneralSearchSpaceGenerator
from hypernets.core.search_space import Choice

class MySearchSpace(GeneralSearchSpaceGenerator):
    @property
    def default_xgb_init_kwargs(self):
        return { **super().default_xgb_init_kwargs,
                'max_depth': Choice([10, 20 ,30]),
        }

my_search_space = MySearchSpace()
train_data = ...

experiment = make_experiment(train_data, 
                             search_space=my_search_space,
                             ...)
自定义建模算法

HyperGBM实现了对XGBoost、LightGBM、CatBoost和HistGridientBoosting的支持,并作为SearchSpace的一部分在建模优化时进行搜索。如果您需要增加对其他算法的支持,则需要:

  • 将您选择的算法封装为HyperEstimator的子类

  • 将封装后的算法增加到SearchSpace中,并定义搜索参数

  • 在make_experiment中年使用您自定义的SearchSpace

示例:

from sklearn import svm

from hypergbm import make_experiment
from hypergbm.estimators import HyperEstimator
from hypergbm.search_space import GeneralSearchSpaceGenerator
from hypernets.core.search_space import Choice, Int, Real
from hypernets.tabular.datasets import dsutils


class SVMEstimator(HyperEstimator):
    def __init__(self, fit_kwargs, C=1.0, kernel='rbf', gamma='auto', degree=3, random_state=666, probability=True,
                 decision_function_shape=None, space=None, name=None, **kwargs):
        if C is not None:
            kwargs['C'] = C
        if kernel is not None:
            kwargs['kernel'] = kernel
        if gamma is not None:
            kwargs['gamma'] = gamma
        if degree is not None:
            kwargs['degree'] = degree
        if random_state is not None:
            kwargs['random_state'] = random_state
        if decision_function_shape is not None:
            kwargs['decision_function_shape'] = decision_function_shape
        kwargs['probability'] = probability
        HyperEstimator.__init__(self, fit_kwargs, space, name, **kwargs)

    def _build_estimator(self, task, kwargs):
        if task == 'regression':
            hsvm = SVMRegressorWrapper(**kwargs)
        else:
            hsvm = SVMClassifierWrapper(**kwargs)
        hsvm.__dict__['task'] = task
        return hsvm


class SVMClassifierWrapper(svm.SVC):
    def fit(self, X, y=None, **kwargs):
        return super().fit(X, y)


class SVMRegressorWrapper(svm.SVC):
    def fit(self, X, y=None, **kwargs):
        return super().fit(X, y)


class GeneralSearchSpaceGeneratorPlusSVM(GeneralSearchSpaceGenerator):
    def __init__(self, enable_svm=True, **kwargs):
        super(GeneralSearchSpaceGeneratorPlusSVM, self).__init__(**kwargs)
        self.enable_svm = enable_svm

    @property
    def default_svm_init_kwargs(self):
        return {
            'C': Real(0.1, 5, 0.1),
            'kernel': Choice(['rbf', 'poly', 'sigmoid']),
            'degree': Int(1, 5),
            'gamma': Real(0.0001, 5, 0.0002)
        }

    @property
    def default_svm_fit_kwargs(self):
        return {}

    @property
    def estimators(self):
        r = super().estimators
        if self.enable_svm:
            r['svm'] = (SVMEstimator, self.default_svm_init_kwargs, self.default_svm_fit_kwargs)
        return r


my_search_space = GeneralSearchSpaceGeneratorPlusSVM()

train_data = dsutils.load_blood()
experiment = make_experiment(train_data, target='Class',
                             search_space=my_search_space)
estimator = experiment.run()
print(estimator)

分布式训练

快速实验

HyperGBM支持使用Dask进行分布式训练,在运行实验之前您需要部署Dask集群并初始化Dask客户端Client对象;如果您的训练数据是csv或parquet格式,而且数据文件的扩展名是“.csv”或“.parquet”的话,可以直接使用文件路径创建实验,make_experiment在检测到Dask环境是会自动将数据加载为Dask的DataFrame对象并进行搜索和训练。

示例代码(以单节点为例,假设您的训练数据文件是‘/opt/data/my_data.csv’):

from dask.distributed import LocalCluster, Client

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils


def train():
    cluster = LocalCluster(processes=True)
    client = Client(cluster)

    train_data = '/opt/data/my_data.csv'

    experiment = make_experiment(train_data, target='...')
    estimator = experiment.run()
    print(estimator)


if __name__ == '__main__':
    train()

对于大规模数据,为了加速数据的加载过程,建议您将数据拆分为多个文件并保存在一个目录下(如:/opt/data/my_data/),然后使用目录下的文件创建实验,如:

from dask.distributed import LocalCluster, Client

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils


def train():
    cluster = LocalCluster(processes=True)
    client = Client(cluster)

    train_data = '/opt/data/my_data/*.parquet'

    experiment = make_experiment(train_data, target='...')
    estimator = experiment.run()
    print(estimator)


if __name__ == '__main__':
    train()

您也可以直接使用dask.dataframe提供的方法将数据加载为Dask DataFrame然后创建实验,如:

from dask import dataframe as dd
from dask.distributed import LocalCluster, Client

from hypergbm import make_experiment
from hypernets.tabular.datasets import dsutils


def train():
    cluster = LocalCluster(processes=False)
    client = Client(cluster)

    train_data = dd.from_pandas(dsutils.load_blood(), npartitions=1)

    experiment = make_experiment(train_data, target='Class')
    estimator = experiment.run()
    print(estimator)


if __name__ == '__main__':
    train()

关于使用Dask DataFrame的更多信息请参考Dask官方文档中的 Create DataFrames

自定义SearchSpace

在Dask环境下运行实验时,搜索空间中使用的Transformer和Estimator必须都支持Dask数据类型,您可以参考或基于HyperGBM内置的支持Dask的搜索空间定义自己的搜索空间。

from dask import dataframe as dd
from dask.distributed import LocalCluster, Client

from hypergbm import make_experiment
from hypergbm.dask.search_space import search_space_general
from hypernets.tabular.datasets import dsutils


def my_search_space():
    return search_space_general(n_estimators=100)


def train():
    cluster = LocalCluster(processes=False)
    client = Client(cluster)

    train_data = dd.from_pandas(dsutils.load_blood(), npartitions=1)

    experiment = make_experiment(train_data, target='Class', searcher='mcts', search_space=my_search_space)
    estimator = experiment.run()
    print(estimator)


if __name__ == '__main__':
    train()


How-To

如何在centos7上安装shap?

  1. 安装系统依赖

    yum install epel-release centos-release-scl -y  && yum clean all && yum make cache # llvm9.0 is in epel, gcc9 in scl
    yum install -y llvm9.0 llvm9.0-devel python36-devel devtoolset-9-gcc devtoolset-9-gcc-c++ make cmake 
    
  2. 配置安装环境

    whereis llvm-config-9.0-64  # find your `llvm-config` path
    # llvm-config-9: /usr/bin/llvm-config-9.0-64
    
    export LLVM_CONFIG=/usr/bin/llvm-config-9.0-64  # set to your path
    scl enable devtoolset-9 bash
    
  3. 安装shap

    pip -v install numpy==1.19.1  # prepare shap dependency
    pip -v install scikit-learn==0.23.1  # prepare shap dependency
    pip -v install shap==0.28.5
    

Release Notes

历史:

Version 0.2.3

本次发布优化及新增的特性:

  • 数据清洗

    • 支持从数据类型为数值型的特征中自动识别类别列

    • 可指定在数据清洗时对某些列不做处理

  • 特征衍生

    • 增加对时间、文本、经纬度类型的支持

    • 增加对分布式的支持

  • 建模算法

    • XGBoost:分布式建模从 dask_xgboost 迁移到 xgboost.dask ,与XGBoost官网保值一致

    • LightGBM:增加对多机分布式的支持

  • 模型训练

    • 搜索过程可复现

    • 支持低保真搜索

    • 基于统计信息预测学习曲线

    • 支持非侵入式超参数优化

    • EarlyStopping时间限制的范围调整为对整个实验的训练周期

    • 训练时支持自定义pos_label

    • 分布式场景下,eval-set支持Dask数据集

    • 优化模型训练中间结果的缓存策略

  • 搜索算法

    • 增加GridSearch算法

    • 增加Playback算法

  • 高级特性

    • 增加一阶段特征筛选并支持多种策略

    • 二阶段特征筛选支持多种策略

    • 伪标签支持多种数据筛选策略,并增加对多分类的支持

    • 优化概念漂移处理的执行性能

    • 增加对高级特性执行中间结果的缓存机制

  • 可视化

    • 实验信息可视化

    • 训练过程可视化

  • 命令行工具

    • 模型训练时可通过命令行参数支持实验的大部分特性

    • 支持模型评价

    • 支持模型预测

Version 0.2.2

本次发布新增以下特性:

特征工程

  • 特征衍生

  • 特征降维

数据清洗

  • 特殊空值字符处理

  • 列类型校正

  • 常量列清理

  • 重复列清理

  • 删除标签列为空的样本

  • 非法值替换

  • id列清理

  • 共线性特征清理

数据集拆分

  • 对抗验证

建模算法

  • XGBoost

  • Catboost

  • LightGBM

  • HistGridientBoosting

模型训练

  • 自动任务类型推断

  • 命令行工具

评估方法

  • 交叉验证(Cross-Validation)

  • Train-Validation-Holdout验证

搜索算法

  • 蒙特卡洛树算法

  • 进化算法

  • 随机搜索算法

不平衡数据处理

  • 类平衡(Class Weight)

  • 降采样(Under -Samping)支持

    • Near miss

    • Tomeks links

    • Random

  • 过采样(Over-Samping)支持

    • SMOTE

    • ADASYN

    • Random

提前停止策略

  • n次搜索都不再提升,提前停止

  • 最大用时提前停止

  • 到达预期指标提前停止

高级特性

  • 二阶段搜索

    • 伪标签

    • 特征选择

  • 概念漂移处理

  • 模型融合