在机器学习中,超参数是用来控制机器学习模型学习过程的参数。为了区分从数据中学到的机器学习模型参数,称为超参数。超参数的构成决定了机器学习模型的性能,每组独特的超参数在学习后都可以对应于机器学习模型。对于大多数最新的机器学习模型,所有可能的超参数组合的集合都可能很大。大多数机器学习模型包的基本参数值都经过了特别调整,以获得良好的基准性能。也就是说,可以直接使用,但必须为特定情况找到特定的超参数值,才能获得最佳性能。
许多算法和库提供了自动的超参数选择。超参数选择是大象函数用模型表示的优化过程。优化任务是找到最能实现机器学习模型性能的参数集。
超参数优化的空间非常丰富,第一个也是最简单的优化方法是暴力搜索。通过仔细搜索所有可能的超参数组合,可以找到最佳的超参数。如果能详细搜索超级参数空间,就能提供最佳的超级参数组合集。但是在计算资源和时间方面,暴力搜索超参数空间一般是不可能的。超参数搜索属于非凸优化,几乎不可能找到全局优化。这是因为它可能会陷入多个优秀的“陷阱”中的一个(也称为本地最小值)。这使得算法难以搜索超参数的整个空间。
暴力搜索优化的替代方案是黑盒(Black-Box)非凸优化技术。黑箱非凸优化算法根据一些预定义的度量,寻找最优局部最小值(或最大值)的子最优解。
python有很多这样的工具。例如,sklearn的GridSearchCV就是暴力的优化。IBM开发的RBFopt软件包提供了黑匣子优化方法。其工作方式是使用Radial basis函数构造和微调正在优化的函数的代理模型。另外,不需要对优化函数的形状或行为做任何假设,可以用于优化深度神经网络等复杂模型。
本文使用Kaggle公开提供的通信客户损失数据集。数据集可以通过Apache 2.0许可证免费使用、修改和共享。
准备数据
首先,我将使用pandas读取数据:
df=(')可以查看包含客户ID、性别、身份等字段的数据。
与客户是否重复购买相对应的字段“churn”。值“否”表示客户重复购买,值“Yes”表示客户停止购买。
使用Gender、senorcitizen、InternetService、DeviceProtection、MonthlyCharges和TotalCharges字段作为输入来预测客户是否会丢失的简单分类模型,因此必须将分类列转换为机器可读值。因为只有数字类型的值才能作为输入传递给机器学习模型。
Df ['gender']=df ['gender']。astype ('category ')
df[' gender _ cat ']=df[' gender ']. cat . codes
df[' senior citizen ']=df[' senior citizen ']。as type ('category ')
df[' senior citizen _ cat ']=df[' senior citizen ']. cat . codes
df[' internet service ']=df[' internet service ']。astype ('category ')
df[' internet service _ cat ']=df[' internet service ']. cat . codes
df[' device protection ']=df[' device protection ']。astype ('category ')
df[' device protection _ cat ']=df[' device protection ']. cat . codes
Df ['gender _ cat '' senior citizen _ cat
9;, 'InternetService_cat', 'DeviceProtection_cat']].head()我们还必须对churn列,也就是我们的目标列做一类似的操作:
df['Churn'] = df['Churn'].astype('category')
df['Churn_cat'] = df['Churn'].cat.codes
因为有一些缺失值,所以需要处理TotalCharges列,将无效值替换为NaN,并用TotalCharges的平均值填充
df['TotalCharges'] = (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 import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
训练模型
我们使用随机森林分类模型,并且只使用默认参数进行训练,作为基类模型
from import RandomForestClassifier
model = RandomForestClassifier()
model.fit(X_train, y_train)
让我们打印模型的默认参数值。在模型对象上调用get_params()方法:
model.get_params()
使用精度来评估我们的分类模型。
from import precision_score
y_pred_default = model.predict(X_test)
precision = precision_score(y_test, y_pred_default)
precision
现在让我们看看如何应用暴力的网格搜索来找到最佳随机森林分类模型。
GridSearchCV
GridSearchCv等暴力搜索方法的工作原理是在整个搜索空间中搜索最佳超参数集。所以就需要定义用于指定参数的字典,GridSearch会遍历字典中所有的组合,然后找到最好的组合。
from 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_(x_train, y_train)
训练完成后可以显示最佳参数:
gscv_params = grid_
gscv_params
获得最佳参数后,使用最佳参数重新训练随机森林模型:
gscv_params = grid_
model_rf_gscv = RandomForestClassifier(**gscv_params)
model_r(X_train, y_train)
看看结果
y_pred_gscv = model_r(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 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_rb(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 = rb(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 = rb(max_evaluations=max_fun_calls)
alg = rb(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_rb(X_train, y_train)
y_pred_rbfopt = model_rb(X_test)
precision_rbfopt = precision_score(y_test, y_pred_rbfopt)
precision_rbfopt
不仅精度上有了轻微的提升,优化算法也执行的更快速了,这对于大型超参数搜索空间的情况特别有用。
总结
虽然大多数机器学习算法的默认超参数提供了良好的基线性能,但为了得到更好的性能,超参数调整通常是必要的。暴力优化技术是有用的,但它在时间和计算方面需求很大。更有效的黑盒优化方法(如RBFopt)是暴力优化一个很好的替代。RBFopt是一种非常有用的黑盒技术,如果你想进行超参数的优化,可以从它开始。
作者:Sadrach Pierre, Ph.D.