大家好,我是小寒。
我们都知道在训练机器学习模型时,都需要人工指定一组超参数。
例如,逻辑回归模型具有不同的求解器,用于查找可以为我们提供最佳输出的系数。
每个求解器都使用不同的算法来找到最佳结果,并且这些算法都没有一个比另一个更好。除非你尝试所有这些求解器,否则很难判断哪个求解器在你的数据集上表现最好。
最好的超参数是主观的,并且对于每个数据集都不同。
Python 中的 Scikit-learn 库有一组默认的超参数,它们在所有模型上都表现得相当好,但这些不一定对每个问题都是最好的。
为你的数据集找到最佳超参数的唯一方法是反复试验,这是超参数优化背后的主要概念。
简而言之,超参数优化是一种技术,它涉及搜索一系列值以找到在给定数据集上实现最佳性能的结果子集。
有两种流行的技术用于执行超参数优化——网格搜索和随机搜索。
网格搜索
在执行超参数优化时,我们首先需要定义一个参数空间或参数网格,其中我们包含一组可用于构建模型的可能的超参数值。
然后使用网格搜索技术将这些超参数放置在类似矩阵的结构中,并根据超参数值的每个组合对模型进行训练。
然后选择具有最佳性能的模型。
随机搜索
网格搜索查看超参数的每个可能组合以找到最佳模型,而随机搜索仅选择和测试超参数的随机组合。
该技术从超参数网格中随机采样,而不是进行详尽的搜索。
我们可以指定随机搜索在返回最佳模型之前应该尝试的总运行次数。
现在你对随机搜索和网格搜索的工作原理有了基本的了解,我将向你展示如何使用 Scikit-learn 库来实现这些技术。
使用网格搜索和随机搜索优化随机森林分类器
1、加载数据集
import pandas as pd
df = pd.read_csv("../../data/wine/winequalityN.csv")
df.head()
2、数据预处理
目标变量 quality 包含 1 到 10 之间的值。
我们将把它变成一个二元分类任务,将值 0 分配给 quality 小于或等于 5 的所有数据点,并将值 1 分配给剩余的观察值:
import numpy as np
df['target'] = np.where(df['quality']>5, 1, 0)
删除缺失值
df.dropna(inplace=True)
df.isnull().sum()
type 0
fixed acidity 0
volatile acidity 0
citric acid 0
residual sugar 0
chlorides 0
free sulfur dioxide 0
total sulfur dioxide 0
density 0
pH 0
sulphates 0
alcohol 0
quality 0
target 0
dtype: int64
让我们拆分此数据集中的因变量和自变量:
df2 = df.drop(['quality','type'],axis=1)
X = df2.drop(['target'],axis=1)
y = df2[['target']]
df2.head()
3、构建模型
现在,让我们构建一个随机森林分类器。我们将调整该模型的超参数,为我们的数据集创建最佳模型。
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier()
4、使用 Scikit-Learn 实现网格搜索
定义超参数空间
我们现在将尝试调整该模型的以下一组超参数:
- “Max_depth”:这个超参数表示随机森林模型中每棵树的最大深度。更深的树表现良好,并捕获有关训练数据的大量信息,但不能很好的泛化到测试数据。默认情况下,该值在 Scikit-Learn 库中设置为 “None”,这意味着树完全展开。
- “Max_features”:随机森林模型允许在每次拆分时尝试的最大特征数。默认情况下,在 Scikit-Learn 中,此值设置为数据集中变量总数的平方根。
- “N_estimators”:随机森林中决策树的数量。Scikit-Learn 中的默认估计器数量为 10。
- “Min_samples_leaf”:每棵树的叶节点所需的最小样本数。Scikit-Learn 中的默认值为 1。
- “Min_samples_split”:拆分每棵树的内部节点所需的最小样本数。Scikit-Learn 中的默认值为 2。
现在将为所有上述超参数创建一个包含多个可能值的字典。这也称为超参数空间,将被搜索以找到参数的最佳组合:
grid_space={ 'max_depth':[3,5,10,None],
'n_estimators':[10,100,200],
'max_features':[3,5,7],
'min_samples_leaf':[1,2,3],
'min_samples_split':[2,3]
}
运行网格搜索
现在,我们需要执行搜索,以找到模型的最佳超参数组合:
from sklearn.model_selection import GridSearchCV
grid = GridSearchCV(rf,param_grid=grid_space,cv=3,scoring='accuracy')
model_grid = grid.fit(X,y)
评估模型结果
最后,让我们打印出最佳模型精度,以及产生这个分数的一组超参数:
print('Best hyperparameters are: '+str(model_grid.best_params_))
print('Best score is: '+str(model_grid.best_score_))
Best hyperparameters are: {'max_depth': 3, 'max_features': 7, 'min_samples_leaf': 3, 'min_samples_split': 2, 'n_estimators': 10}
Best score is: 0.726290769309208
最佳模型的准确度得分约为 0.73。
现在,让我们在同一个数据集上使用随机搜索,看看我们是否得到相似的结果。
5:使用 Scikit-Learn 实现随机搜索
定义超参数空间
现在,让我们定义超参数空间来实现随机搜索。
这个参数空间可以有比我们为网格搜索构建的更大范围的值,因为随机搜索不会尝试超参数的每一个组合。
它随机采样超参数以找到最佳参数,这意味着与网格搜索不同,随机搜索可以快速查看大量值。
from scipy.stats import randint
rs_space={'max_depth':list(np.arange(10, 100, step=10)) + [None],
'n_estimators':np.arange(10, 500, step=50),
'max_features':randint(1,7),
'criterion':['gini','entropy'],
'min_samples_leaf':randint(1,4),
'min_samples_split':np.arange(2, 10, step=2)
}
运行随机搜索
我们 **指定了 n_iter=500,这意味着随机搜索将运行 500 次,**然后才能选择最佳模型。
你可以尝试不同的迭代次数以看看哪一个能给你最佳结果。
请记住,大量迭代会带来更好的性能,但很耗时。
from sklearn.model_selection import RandomizedSearchCV
rf = RandomForestClassifier()
rf_random = RandomizedSearchCV(rf, rs_space, n_iter=500, scoring='accuracy', n_jobs=-1, cv=3)
model_random = rf_random.fit(X,y)
评估模型结果
现在,运行以下代码行来打印通过随机搜索找到的最佳超参数,以及最佳模型的最高精度:
print('最佳超参数是:'+str(model_random.best_params_))
print('最好成绩是:'+str(model_random.best_score_))
最佳超参数是:{'criterion': 'entropy', 'max_depth': 20, 'max_features': 5, 'min_samples_leaf': 3, 'min_samples_split': 4, 'n_estimators': 60}
最好成绩是:0.718243078759207
构建的所有模型的最高精度为 0.72。
观察网格搜索和随机搜索在数据集上的表现都相当好。
网格搜索与随机搜索如何选择?
如果你发现自己试图在网格搜索和随机搜索之间进行选择,这里有一些提示可以帮助你决定使用哪一个:
-
如果你已经拥有可以很好地执行的**已知超参数值的大致范围,请使用网格搜索。**确保你的参数空间很小,因为网格搜索可能非常耗时。
-
如果你还不知道在模型上表现良好的参数,请对广泛的值使用随机搜索。随机搜索比网格搜索要快,并且在参数空间较大时应始终使用。
-
同时使用随机搜索和网格搜索以获得最佳结果也是一个好主意。
你可以先使用具有较大参数空间的随机搜索,因为它更快。然后,使用随机搜索找到的最佳超参数来缩小参数网格,并将较小范围的值提供给网格搜索。“