应用TensorFlow训练、调优、评估、解释和部署基于树的模型的残缺教程

两年前TensorFlow (TF)团队开源了一个库来训练基于树的模型,称为TensorFlow决策森林(TFDF)。通过了2年的测试,他们在上个月终于发表这个包曾经筹备好公布了,也就是说咱们能够真正的开始应用了。所以这篇文章将具体介绍这个软件包,并向你展现如何(无效地)应用它。

在这篇文章中,咱们将应用美国小企业管理局数据集训练一些贷款守约预测模型。模型将应用曾经预处理的数据进行训练。装置TensorFlow决策森林非常简单,只需运行

pip install tensorflow_decision_forests

TensorFlow Decision Forest

1、什么是TFDF?

TensorFlow决策森林实际上是建设在c++的Yggdrasil决策森林之上库的,Yggdrasil决策森林也是由谷歌开发的。最后的c++算法旨在构建可扩大的决策树模型,能够解决大型数据集和高维特色空间。通过将这个库集成到更宽泛的TF生态系统中,用户无需学习另一种语言就能够轻松地构建可扩大的RF和GBT模型。

2、为什么要用它?

与XGBoost或LightGBM相比,这个库的次要劣势在于它与其余TF生态系统组件严密集成。对于曾经将其余TensorFlow模型作为管道的一部分或应用TFX的团队来说,这是十分有用的,因为TFDF能够很容易地与NLP模型集成。如果你正在应用TF Serving为模型提供对外服务,这个库也是能够用的,因为它是官网的原生反对(不须要ONNX或其余跨包序列化办法)模型的部署。最初这个库为还提供了大量参数,能够依据XGBoost、LightGBM和许多其余梯度加强机(GBM)办法来调整取得近似模型。这意味着不须要在训练过程中在不同的GBM库之间切换,这从代码可维护性的角度来说十分好。

模型训练

1、数据筹备

咱们应用了数据处理后的版本,所以不须要进行数据的预处理:

 # Read in data train_data: pd.DataFrame=pd.read_parquet("../data/train_data.parquet") val_data: pd.DataFrame=pd.read_parquet("../data/val_data.parquet") test_data: pd.DataFrame=pd.read_parquet("../data/test_data.parquet")  # Set data types NUMERIC_FEATURES= [     "Term",     "NoEmp",     "CreateJob",     "RetainedJob",     "longitude",     "latitude",     "GrAppv",     "SBA_Appv",     "is_new",     "same_state", ]  CATEGORICAL_FEATURES= [     "FranchiseCode",     "UrbanRural",     "City",     "State",     "Bank",     "BankState",     "RevLineCr",     "naics_first_two", ]  TARGET="is_default"  # Make sure that datatypes are consistent dsets= [train_data,val_data,test_data] fordindsets:     d[NUMERIC_FEATURES] =d[NUMERIC_FEATURES].astype(np.float32)     d[CATEGORICAL_FEATURES] =d[CATEGORICAL_FEATURES].astype(str)

2、特色

为了确保我的项目构造良好并防止意外行为,能够为每个个性指定一个FeatureUsage,只管这不是强制性的,然而应用这种办法能够让咱们的我的项目更加标准。并且这也是一项简略的工作:只须要从反对的六种类型(BOOLEAN、CATEGORICAL、CATEGORICAL_SET、DISCRETIZED_NUMERICAL、HASH和NUMERICAL)中决定将哪些特色类型调配给哪个类型就能够了。其中一些类型带有额定的参数,所以请确保在这里浏览更多对于它们的信息。

在本例中咱们将放弃简略,只应用数值和类别数据类型,然而须要阐明下DISCRETIZED_NUMERICAL,它能够显著放慢训练过程(相似于LightGBM)。咱们应用的代码如下,指定所选的数据类型,对于类别特色,还要指定min_vocab_frequency参数以去除常见值。

 importtensorflow_decision_forestsastfdf  # Prepare Feature Usage list feature_usages= []  # Numerical features forfeature_nameinNUMERIC_FEATURES:     feature_usage=tfdf.keras.FeatureUsage(         name=feature_name, semantic=tfdf.keras.FeatureSemantic.NUMERICAL     )     feature_usages.append(feature_usage)  # Categorical features forfeature_nameinCATEGORICAL_FEATURES:     feature_usage=tfdf.keras.FeatureUsage(         name=feature_name,         semantic=tfdf.keras.FeatureSemantic.CATEGORICAL,         min_vocab_frequency=1000,     )     feature_usages.append(feature_usage)

3、应用TF Dataset读取数据

读取数据的最简略办法是应用TF Dataset。TFDF有一个十分好的实用函数pd_dataframe_to_tf_dataset,它使这一步变得非常简单。

 # Use TF Dataset to read in data train_dataset=tfdf.keras.pd_dataframe_to_tf_dataset(     train_data, label=TARGET, weight=None, batch_size=1000 ) val_dataset=tfdf.keras.pd_dataframe_to_tf_dataset(     val_data, label=TARGET, weight=None, batch_size=1000 ) test_dataset=tfdf.keras.pd_dataframe_to_tf_dataset(     test_data, label=TARGET, weight=None, batch_size=1000 )

在下面的代码中,咱们将DataFrame对象传递到函数中,并提供以下参数:

  • label 列的名称
  • weight 列的名称(在本例中为None)
  • 批大小(有助于放慢数据的读取)

失去的数据集是就是TF Dataset的格局。也能够创立本人的办法来读取数据集,但必须特地留神输入的格局,没有这个办法这样不便。

3、TFDF默认参数

 ## Define the models # Gradient Boosted Trees model gbt_model=tfdf.keras.GradientBoostedTreesModel(     features=feature_usages,     exclude_non_specified_features=True, ) # Random Forest modle rf_model=tfdf.keras.RandomForestModel(     features=feature_usages,     exclude_non_specified_features=True, )  # Compile the models (Optional) gbt_model.compile(metrics=[tf.keras.metrics.AUC(curve="PR")]) rf_model.compile(metrics=[tf.keras.metrics.AUC(curve="PR")])  # Fit the models gbt_model.fit(train_dataset, validation_data=val_dataset) rf_model.fit(train_dataset, validation_data=val_dataset)

只须要几行代码就能够应用默认参数构建和训练GBT和RF模型。当应用ROC和PR auc评估这两个模型时能够看到性能曾经相当好了。

 # GBT with Default Parameters PR AUC: 0.8367 ROC AUC: 0.9583  # RF with Default Parameters PR AUC: 0.8102 ROC AUC: 0.9453

那么是否能够进行超参数调优进一步改善这些后果呢。

超参数调优

Yggdrasil官网文档中有大量的参数能够进行调优,每一个参数都有很好的解释。TFDF也提供了一些内置选项来调优参数,为了简略也应用超参数的搜寻库,例如Optuna或Hyperpot。

1、超参数模板

TFDF提供的十分好个性就是超参数模板。这些参数在论文中被证实在宽泛的数据集上体现最好。有两个可用的模板:better_default和benchmark_rank。如果你工夫不够,或者对机器学习不太熟悉,这是一个不错的抉择。指定这些参数只须要一行代码。

 # Define the models better_default_gbt_model=tfdf.keras.GradientBoostedTreesModel(     hyperparameter_template='better_default',  # template 1     features=feature_usages,     exclude_non_specified_features=True, )  benchmark_gbt_model=tfdf.keras.GradientBoostedTreesModel(     hyperparameter_template='benchmark_rank1',  # template 2     features=feature_usages,     exclude_non_specified_features=True, )  # Fit the models (notice that we're skipping compiling step) better_default_gbt_model.fit(train_dataset, validation_data=val_dataset) benchmark_gbt_model.fit(train_dataset, validation_data=val_dataset)

看看后果怎么样:应用better_default参数,在ROC和PR auc中失去轻微的晋升。benchmark_rank参数的性能要差得多。这就是为什么在部署后果模型之前正确地评估它们是很重要的。

 GBT with 'Better Default' Parameters PR AUC: 0.8483 ROC AUC: 0.9593  GBT with 'Benchmark Rank 1' Parameters PR AUC: 0.7869 ROC AUC: 0.9442

2、定义搜寻空间

TFDF附带了一个很好的程序,叫做RandomSearch,它在许多可用参数之间执行随机网格搜寻。TFDF能够应用预约义的搜寻空间或者通过一个选项能够手动指定这些参数(见示例)。如果您不太熟悉ML,这可能是一个很好的抉择,因为它不须要手动设置这些参数。

 # Create a Random Search tuner with 50 trials and automatic hp configuration. tuner=tfdf.tuner.RandomSearch(num_trials=50, use_predefined_hps=True)  # Define and train the model. tuned_model=tfdf.keras.GradientBoostedTreesModel(tuner=tuner) tuned_model.fit(train_dataset, validation_data=val_dataset, verbose=2)

留神:无论那个超参数搜寻都会消耗大量的工夫,所以请依据模型审慎抉择。

进行完搜寻后,能够应用上面的命令查看所有尝试过的组合。

 tuning_logs = tuned_model.make_inspector().tuning_logs()

咱们进行了12次迭代,最佳模型的体现比基线稍差,所以请审慎应用内置的调优办法,倡议应用其余库,比方Optuna。

 PR AUC: 0.8216 ROC AUC: 0.9418

3、optuna

上面咱们介绍如何应用optuna进行调优。

 importoptuna   defobjective(trial: optuna.Trial) ->float:     params= {         "max_depth": trial.suggest_int("max_depth", 2, 10),         "l1_regularization": trial.suggest_float("l1_regularization", 0.01, 20),         "l2_regularization": trial.suggest_float("l2_regularization", 0.01, 20),         "growing_strategy": trial.suggest_categorical(             "growing_strategy", ["LOCAL", "BEST_FIRST_GLOBAL"]         ),         "loss": trial.suggest_categorical(             "loss", ["BINOMIAL_LOG_LIKELIHOOD", "BINARY_FOCAL_LOSS"]         ),         "min_examples": trial.suggest_int("min_examples", 5, 1000, step=5),         "focal_loss_alpha": trial.suggest_float("focal_loss_alpha", 0.05, 0.6),         "num_candidate_attributes_ratio": trial.suggest_float(             "num_candidate_attributes_ratio", 0.05, 0.95         ),         "shrinkage": trial.suggest_float("shrinkage", 0.01, 0.9),         "early_stopping_num_trees_look_ahead": 50,         "num_trees": 2000,     }      model=tfdf.keras.GradientBoostedTreesModel(**params)     model.fit(train_dataset, validation_data=val_dataset, verbose=0)     preds=model.predict(val_dataset).ravel()     ap=average_precision_score(val_data[TARGET], preds)      returnap   study=optuna.create_study(direction="maximize") study.optimize(objective, n_trials=50)

这些参数中的大多数对于gbt来说是相当规范的,但也有一些值得注意的参数:

  • 将growing_strategy更改为BEST_FIRST_GLOBAL(又称为按叶成长),这是LightGBM应用的策略。
  • 应用BINARY_FOCAL_LOSS,它应该对不均衡的数据集更好。
  • 更改split_axis参数以应用 sparse oblique splits,这在论文中证实是十分无效的。
  • 应用honest参数构建“honest trees”。

能够看到应用最佳参数取得的后果,自定义永远要比主动的好。

 GBT with Custom Tuned Parameters PR AUC: 0.8666 ROC AUC: 0.9631

当初咱们曾经确定了超参数,能够从新训练模型并持续进行咱们的工作。

模型测验

TFDF提供了一个很好的实用工具来查看经过训练的模型,称为Inspector,他有3个主要用途:

  • 查看模型的属性,如类型,树的数量或应用的特色
  • 获取个性的重要性
  • 提取树结构

1、查看模型属性

inspector类存储了模型各种属性:模型类型(GBT或RF)、树的数量、训练指标以及用于训练模型的特色等等

 inspector = manual_tuned.make_inspector()  print("Model type:", inspector.model_type()) print("Number of trees:", inspector.num_trees()) print("Objective:", inspector.objective()) print("Input features:", inspector.features())

或者应用

manual_tuned.summary()

来更具体地查看模型。

2、特色的重要性

像所有其余库一样,TFDF带有内置的个性重要性评分。对于gbt,能够拜访NUM_NODES, SUM_SCORE, INV_MEAN_MIN_DEPTH, NUM_AS_ROOT办法。须要留神的是,能够在训练期间将compute_permutation_variable_importance参数设置为True,这将增加一些额定的办法,然而模型的训练速度会慢。

 defplot_tfdf_importances(     inspector: tfdf.inspector.AbstractInspector, importance_type: str ):     """Extracts and plots TFDF importances from the given inspector object     Args:         inspector (tfdf.inspector.AbstractInspector): inspector object created from your TFDF model         importance_type (str): importance type to plotß     """     try:         importances=inspector.variable_importances()[importance_type]     exceptKeyError:         raiseValueError(             f"No {importance_type} importances found in the given inspector object"         )     names= []     scores= []     forfinimportances:         names.append(f[0].name)         scores.append(f[1])      sns.barplot(x=scores, y=names, color="#5a7dbf")     plt.xlabel(importance_type)     plt.title("Variable Importance")     plt.show()

能够看到,Term变量始终是最重要的特色,紧随其后的是Bank、State和Bank State等类别变量。然而TFDF库最大的毛病之一是不能应用SHAP,这样可解释性的查看就有一些不不便。

3、查看树的构造

为了解释或模型验证,咱们心愿查看单个树。TFDF能够不便地拜访所有树。比方GBT模型的第一棵树,因为它通常是信息量最大的树。

 first_tree=inspector.extract_tree(tree_idx=0) print(first_tree.pretty())

当解决较大的树时,应用print语句查看它们不太不便。TFDF提供了一个树绘图工具——TFDF .model_plotter

 withopen("plot.html", "w") asf:      f.write(tfdf.model_plotter.plot_model(manual_tuned, tree_idx=0, max_depth=4))

这样就不便很多了。

TF Serving

咱们曾经对模型进行了训练、调优和评估。最初的一个工作就是部署了,这部分也很简略,因为TFDF是官网的,必然会反对TF Serving。如果曾经有了一个TF服务实例,那么所须要做的就是在model_base_path参数中指向要公布的模型。

首先就是保留咱们的模型:

 manual_tuned.save("../models/loan_default_model/1/")

而后就是在本地装置TF services,并应用正确的参数启动它。

 ./tensorflow_model_server \     --rest_api_port=8501 \     --model_name=loan_default_model \     --model_base_path=/path/models/loan_default_model/1

这里的model_base_path肯定要是绝对路径。在TF服务服务器启动后,就能够开始接管申请了。有两种预期的格局——实例和输出。

 # Input data formatted correctly data= {     "Bank": ["Other"],     "BankState": ["TN"],     "City": ["Other"],     "CreateJob": [12.0],     "FranchiseCode": ["0"],     "GrAppv": [14900000.0],     "NoEmp": [28.0],     "RetainedJob": [16.0],     "RevLineCr": ["N"],     "SBA_Appv": [14900000.0],     "State": ["TN"],     "Term": [240.0],     "UrbanRural": ["0"],     "is_new": [0.0],     "latitude": [35.3468],     "longitude": [-86.22],     "naics_first_two": ["44"],     "same_state": [1.0],     "ApprovalFY": [1] } payload= {"inputs": data}  # Send the request url='http://localhost:8501/v1/models/default_model:predict' response=requests.post(url, json=payload)  # Print out the response print(json.loads(response.text)['outputs']) # Expected output: [[0.0138759678]]

返回的json串就蕴含了模型的预测后果。

总结

通过了2年的测试,TFDF终于公布正式版了。它是在TensorFlow中基于训练树的模型的一个弱小且可扩大的库。TFDF模型与TensorFlow生态系统的其余局部很好地集成在一起,所以如果你正在应用TFX,在生产中有其余TF模型,或者正在应用TF服务,你会发现这个库十分有用。

本文的代码局部并不齐全,如果你想本人摸索,能够在这里下载实现的代码和数据集:

https://avoid.overfit.cn/post/db8eeaae665f410f90111b4ca0017b94

作者:Antons Tocilins-Ruberts