在机器学习中,超参数是用于管制机器学习模型的学习过程的参数。为了与从数据中学到的机器学习模型参数辨别开,所以称其为超参数。超参数的配置决定了机器学习模型的性能,每组独特的超参数集能够对应一个学习后的机器学习模型。对于大多数最先进的机器学习模型,所有可能的超参数组合的汇合可能会很大。大多数机器学习模型软件包的默认参数值都通过了一些特地的调整优化,可实现不错的基线性能。这意味着能够间接应用,但这些如果针对特定的状况还是须要找到特定的超参数值,这样能力达到最佳的性能。

许多算法和库都提供了自动化的超参数抉择。超参数抉择是一种优化的过程,在该过程中指标函数由模型体现示意。优化工作是找到一组让机器学习模型的性能体现得最好的参数。

超参数优化的空间十分丰盛,最后也是最简略的优化形式是暴力搜寻:通过详尽搜寻所有可能的超参数组合来找到最佳的超参数。如果能够详尽地搜寻超参数空间,那么必定能够提供一组最佳超参数组合。然而在计算资源和工夫方面,暴力搜寻搜寻超参数空间通常是不可行的,这是因为超参数搜寻属于非凸优化的领域,寻找全局最优简直是不可行的,因为它可能会陷入几个次优的“陷阱”之一,也称为部分最小值,这使得算法很难搜寻超参数的整个空间。

暴力搜寻优化的一个代替计划是黑盒(Black-Box)非凸优化技术。黑盒非凸优化算法可依据某些预约义的度量找到足够最佳的部分最小值(或最大值)的次优解。

Python具备许多这样的工具。比方sklearn中的GridSearchCV就是暴力优化。而IBM开发的RBFopt包则提供了黑盒优化的办法。它的工作原理是应用径向基函数来构建和细化正在优化的函数的代理模型。并且它不须要对被优化函数的形态或行为做任何假如,而且能够被用于优化简单的模型,如深度神经网络。

本文中将应用Kaggle上公开可用的电信客户散失数据集。数据集能够在Apache 2.0许可证下收费应用,批改和共享。

数据筹备

首先,让咱们应用pandas读取数据:

 df = pd.read_csv("telco_churn.csv")

咱们看到数据蕴含诸如客户ID、性别、身份等字段。

字段“churn”,它对应于客户是否反复购买。值为“No”示意该客户反复购买,值为“Yes”示意该客户进行购买。

这是一个简略的分类模型,以gender、senorcitizen、InternetService、DeviceProtection、MonthlyCharges和TotalCharges字段作为输出,并预测客户是否会散失。所以须要将分类列转换为机器可读的值,因为只有数值类型的值才能够作为输出传入机器学习模型。

 df['gender'] = df['gender'].astype('category') df['gender_cat'] = df['gender'].cat.codes df['SeniorCitizen'] = df['SeniorCitizen'].astype('category') df['SeniorCitizen_cat'] = df['SeniorCitizen'].cat.codes df['InternetService'] = df['InternetService'].astype('category') df['InternetService_cat'] = df['InternetService'].cat.codes df['DeviceProtection'] = df['DeviceProtection'].astype('category') df['DeviceProtection_cat'] = df['DeviceProtection'].cat.codes  df[['gender_cat', 'SeniorCitizen_cat', 'InternetService_cat', 'DeviceProtection_cat']].head()

咱们还必须对churn列,也就是咱们的指标列做一类似的操作:

 df['Churn'] = df['Churn'].astype('category') df['Churn_cat'] = df['Churn'].cat.codes

因为有一些缺失值,所以须要解决TotalCharges列,将有效值替换为NaN,并用TotalCharges的平均值填充

 df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], 'coerce') df['TotalCharges'].fillna(df['TotalCharges'].mean(), inplace=True)

定义一个变量X,它将是一个series ,蕴含咱们模型的输出。输入将是一个名为Y的变量,它将蕴含流失率值:

 X = df[['TotalCharges', 'MonthlyCharges', 'gender_cat', 'SeniorCitizen_cat', 'InternetService_cat', 'DeviceProtection_cat']] y = df['Churn_cat']

下一步就是拆分用于训练和测试的数据。还是应用scikit-learn中model_selection模块中的train_test_split办法:

 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

训练模型

咱们应用随机森林分类模型,并且只应用默认参数进行训练,作为基类模型

 from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier() model.fit(X_train, y_train)

让咱们打印模型的默认参数值。在模型对象上调用get_params()办法:

 model.get_params()

应用精度来评估咱们的分类模型。

 from sklearn.metrics import precision_score y_pred_default = model.predict(X_test) precision = precision_score(y_test, y_pred_default) precision

当初让咱们看看如何利用暴力的网格搜寻来找到最佳随机森林分类模型。

GridSearchCV

GridSearchCv等暴力搜寻办法的工作原理是在整个搜寻空间中搜寻最佳超参数集。所以就须要定义用于指定参数的字典,GridSearch会遍历字典中所有的组合,而后找到最好的组合。

 from sklearn.model_selection import GridSearchCV params = {'n_estimators': [10, 100], 'max_features': ['sqrt'], 'max_depth' : [5, 20], 'criterion' :['gini']}

定义好字典后,初始化对象,并开始训练:

 grid_search_rf = GridSearchCV(estimator=model, param_grid=params, cv= 20, scoring='precision') grid_search_rf.fit(x_train, y_train)

训练实现后能够显示最佳参数:

 gscv_params = grid_search_rf.best_params_ gscv_params

获得最佳参数后,应用最佳参数从新训练随机森林模型:

 gscv_params = grid_search_rf.best_params_ model_rf_gscv = RandomForestClassifier(**gscv_params) model_rf_gscv.fit(X_train, y_train)

看看后果

 y_pred_gscv = model_rf_gscv.predict(X_test) precision_gscv = precision_score(y_test, y_pred_gscv) precision_gscv

能够看到,精度比咱们的默认参数的基线模型有了很大的晋升,然而如果对于大型的模型不可能有资源和工夫遍历所有的超参数空间,所以就须要咱们应用以前介绍的贝叶斯优化或者本文的黑盒优化办法了。

RBFopt黑盒优化

当初让咱们应用RBFopt进行超参数黑盒优化。

装置RBFopt:

 %pip install -U rbfopt

为了进行优化,所以须要为的模型参数定义一个上界和下界列表。下界列表将蕴含10个预计器的数量和5个最大深度。上界列表将蕴含100个估算数和20个最大深度:

 lbounds = [10, 5] ubounds = [100, 20]

而后就是定义指标函数:承受n_estimators和max_depth的输出,并为每组参数构建多个模型。对于每个模型,咱们将计算并返回精度。RBFopt会主动的为n_estimators和max_depth找到一组能最大化精度的值。因为RBFOPT是找到最小值,然而咱们的指标是最大化精度,所以咱们要返回精度的相反数:

 import rbfopt from sklearn.model_selection import cross_val_score def precision_objective(X):     n_estimators, max_depth = X     n_estimators = int(n_estimators)     max_depth = int(max_depth)     params = {'n_estimators':n_estimators, 'max_depth': max_depth}     model_rbfopt = RandomForestClassifier(criterion='gini', max_features='sqrt', **params)     model_rbfopt.fit(X_train, y_train)     precision = cross_val_score(model_rbfopt, X_train, y_train, cv=20, scoring='precision')     return -np.mean(precision)

而后就是指定运行的次数、函数调用和超参数的维度数:

 num_runs = 1 max_fun_calls = 8 ndim = 2

运行RBFopt:

 obj_fun = precision_objective bb = rbfopt.RbfoptUserBlackBox(dimension=ndim, var_lower=np.array(lbounds, dtype=np.float), var_upper=np.array(ubounds, dtype=np.float), var_type=['R'] * ndim, obj_funct=obj_fun) settings = rbfopt.RbfoptSettings(max_evaluations=max_fun_calls) alg = rbfopt.RbfoptAlgorithm(settings, bb)

查看找到的超参数

 fval, sol, iter_count, eval_count, fast_eval_count = alg.optimize() obj_vals = fval sol_int = [int(x) for x in sol] params_rbfopt = {'n_estimators': sol_int[0], 'max_depth': sol_int[1]} params_rbfopt

RBFopt为n_estimators和max_depth别离找到了最优值81和5。

将这些最优参数传递到新模型中,并拟合训练数据和查看后果:

 model_rbfopt = RandomForestClassifier(criterion=’gini’, max_features=’sqrt’, **params_rbfopt) model_rbfopt.fit(X_train, y_train)  y_pred_rbfopt = model_rbfopt.predict(X_test) precision_rbfopt = precision_score(y_test, y_pred_rbfopt) precision_rbfopt

不仅精度上有了轻微的晋升,优化算法也执行的更疾速了,这对于大型超参数搜寻空间的状况特地有用。

总结

尽管大多数机器学习算法的默认超参数提供了良好的基线性能,但为了失去更好的性能,超参数调整通常是必要的。暴力优化技术是有用的,但它在工夫和计算方面需要很大。更无效的黑盒优化办法(如RBFopt)是暴力优化一个很好的代替。RBFopt是一种十分有用的黑盒技术,如果你想进行超参数的优化,能够从它开始。

本文代码:
https://avoid.overfit.cn/post/cd167390bfe54da6b4b1edbef01a8f91

作者:Sadrach Pierre, Ph.D.