叶子生长策略不同。
时间复杂度不同。
类别名称的处理。
优化方法不同
决策树算法存在差异。
体现在每一次构造基分类器的过程中,都会考虑到上一次的样本权重,目标函数就是使得分类之后的损失函数最小,会考虑到样本权重
从偏差方差关系来看。需要平衡好偏差和方差,既拟合的不错,又得泛化性能好。
Bagging:并行训练很多不同的分类器的目的就是降低方差,所以基分类器应该尽可能降低偏差,即会采用深度很深甚至不剪枝的决策树。
Boosting:每一步我们都会在上一轮的基础上更加拟合原数据,所以可以保证偏差在不断减小,故基分类器应该尽可能的降低方差,即更简单的分类器,所以我们选择了深度很浅的决策树。
大的框架
什么是XGBoost?
XGBoost求解方法有两种:
XGBoost优点/缺点:
优点:
缺点:
Bagging + 决策树 = 随机森林
AdaBoost + 决策树 = 提升树
Gradient Boosting + 决策树 = GBDT
如何得到若干个个体学习器
如何选择一种结合策略,将这些个体学习器集合成一个强学习器。
个体学习器分为同质和异质两种。
那我们用哪个比较多?
现在知道了大部分都是同质个体学习器的组合,那么如何组合呢?两种方式!
这一部分直接引言刘建平老师的博客,真的写的太好了!http://www.cnblogs.com/pinard/p/6131423.html
stacking:
当使用stacking的结合策略时,我们不是对弱学习器的结果做简单的逻辑处理,而是再加上一层学习器,也就是说,我们将训练集弱学习器的学习结果作为输入,将训练集的输出作为输出,重新训练一个学习器来得到最终结果。在这种情况下,我们将弱学习器称为初级学习器,将用于结合的学习器称为次级学习器。对于测试集,我们首先用初级学习器预测一次,得到次级学习器的输入样本,再用次级学习器预测一次,得到最终的预测结果。
就是首先对于样本进行自助抽样,然后分别对每一个样本进行建模,最后根据所有单模型的预测结果进行投票决定最终结果。
bagging = boostrap aggregation
boostrap:在统计学中,自助法(Bootstrap Method,Bootstrapping,或自助抽样法)是一种从给定训练集中有放回的均匀抽样,也就是说,每当选中一个样本,它等可能地被再次选中并被再次添加到训练集中。 自助法由Bradley Efron于1979年在《Annals of Statistics》上发表。
至于为什么boostrap效果比较好,可以见知乎:https://www.zhihu.com/question/38429969
个人主要觉得是样本量比较小的时候,boostrap效果会比较好
进行Boostrap。从原始样本集中使用Bootstraping方法(自助法)随机抽取样本容量为n的样本,共进行k轮抽取,得到k个训练集。(k个训练集之间相互独立,元素可以有重复)
建立模型。对于k个训练集,我们训练k个模型(这k个模型可以根据具体问题而定,比如决策树,knn等)
集成得结果。对于分类问题:由投票表决产生分类结果;对于回归问题:由k个模型预测结果的均值作为最后预测结果。
使用bagging之后就可以不用交叉验证来求测试误差了!而是采用OOB误差!为啥呢?
主要衍生就是:随机森林
CART树!
随机森林是bagging的进化版,进行了改进,那改进的地方有哪些呢?
那如何确定nsub呢?
随机森林有下面三种扩展
相对于普通随机森林,它的变化有:
故模型的方差相对于RF进一步减少,但是偏倚相对于RF进一步增大
思想:
具体过程:
作用:
具体过程:
也就是如何构建随机森林的?三步!
优点:
缺点:
Boosting算法的工作机制是:
首先从训练集用初始权重训练出一个弱学习器1,根据弱学习的学习误差率表现来更新训练样本的权重,使得之前弱学习器1学习误差率高的训练样本点的权重变高,使得这些误差率高的点在后面的弱学习器2中得到更多的重视。
然后基于调整权重后的训练集来训练弱学习器2······,如此重复进行,直到弱学习器数达到事先指定的数目T
回答上面四个问题:
解释1:Adaboost是前向分步算法的一个实现,模型为加法模型,损失函数为指数损失,算法为前向分步算法。
那什么叫前向分步算法呢?
见《统计学习方法》或者刘建平老师博客,不难。
Adaboost最基本的性质是它能在学习过程中不断减少训练误差,既在训练数据集上的分类误差率,而Adaboost的训练误差界定理则保证了这一结论!
优点:
缺点:
提升树系列算法里面应用最广泛的是梯度提升树(Gradient Boosting Tree)。
分类问题的提升树。相当于Adaboost算法中基分类器限制为二类分类树即可。
回归问题的提升树。
因为回归问题的提升树也是前向分步算法,每步得到的基模型都是当前情况下损失函数最小时候的模型,而损失函数推导之后的结果也就是下图中r和T的差的平方(因为回归问题一般都采用平方损函数),结果肯定是大于等于0的,现在要最小,即为0,如何为0呢?也就是下一步模型T=r,也就是残差!这样就能保证每一步损失函数最小了!最后是将每一步进行累加汇总!
基学习器存在差异。Adaboost什么都可以,但一般来说,使用最广泛的是决策树和神经网络;而GBDT限定了只能使用CART回归树模型(GBDT 无论用于分类还是回归一直都是使用的 CART 回归树)。
迭代思路存在差异。Adaboost是利用前一轮迭代弱学习器的误差率来更新训练集的权重,这样一轮轮的迭代下去;而GBDT则是每一步迭代找到决策树,要让样本的损失尽量变得更小。
使用损失函数进行判断!对于普通的损失函数,比如平方损失和指数损失,每一步求解损失是比较容易的,但是对于一般损失函数就不容易了,这时候可以采用损失函数的负梯度表示误差!
GBDT的分类算法从思想上和GBDT的回归算法没有区别,但是由于样本输出不是连续的值,而是离散的类别,导致我们无法直接从输出类别去拟合类别输出的误差。
GBDT分类:
GBDT回归:
三种方式:
步长。和Adaboost类似的正则化项,即步长(learning rate),定义为 ν,0≤v≤1,对于同样的训练集学习效果,较小的ν意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。
子采样比例(subsample)。区别于bagging,是无放回抽样。一般取值[0.5,0.8],即每次训练模型不是使用全部样本,而是抽一部分。这种方法也被称为随机梯度提升树(Stochastic Gradient Boosting Tree, SGBT),由于使用了子采样,程序可以通过采样分发到不同的任务去做boosting的迭代过程,最后形成新树,从而减少弱学习器难以并行学习的弱点。
对于弱学习器即CART回归树进行正则化剪枝
优点:
缺点:
XGBoost 是 “Extreme Gradient Boosting” 的缩写,XGBoost 算法的步骤和 GBDT 基本相同,都是首先初始化为一个常数,GBDT 是根据一阶导数,XGBoost 是根据一阶导数 gi 和二阶导数 hi,迭代生成基学习器,相加更新学习器。
不仅是CART树,也支持多分类的!
回归树有以下四个优点:
贪心法:
第一种XGBoost算法(基于贪婪算法)的流程:
2. 近似法:
- 思想:根据百分位法列举几个可能成为分割点的候选者,然后从候选者中根据上面求分割点的公式计算找出最佳的分割点。XGBoost会一直分裂到指定的最大深度(max_depth),然后回过头来剪枝。如果某个节点之后不再有正值,它会去除这个分裂。
- 为什么近似法?因为上面太慢了,每一次排序结束之后,都是线性扫描来决定最终的分割点,但关键信息是顺序,尝试用分位数来进行分割!而不是遍历所有的值
优点:
缺点:
特征并行方案。通过在本地保存全部数据避免对数据切分结果的通信。特征并行的主要思想是在不同机器在不同的特征集合上分别寻找最优的分割点,然后在机器间同步最优的分割点。
数据并行方案。数据并行中使用分散规约(Reduce scatter)把直方图合并的任务分摊到不同的机器,让不同的机器先在本地构造直方图,然后进行全局的合并,最后在合并的直方图上面寻找最优分割点。同时利用直方图做差,进一步减少了一半的通信量。
基于投票的并行方案。有理论证明,这种voting parallel以很大的概率选出实际最优的特征,因此不用担心top k的问题。
解决的问题
思想
解决的问题
思想
时间复杂度不同。
类别名称的处理。
优化方法不同
决策树算法存在差异。
import warnings
warnings.filterwarnings('ignore')
数据准备-电信数据
import pandas as pd
df = pd.read_csv('./data/telecom_churn.csv')
print(df.shape)
df.head()
(3463, 20)
subscriberID | churn | gender | AGE | edu_class | incomeCode | duration | feton | peakMinAv | peakMinDiff | posTrend | negTrend | nrProm | prom | curPlan | avgplan | planChange | posPlanChange | negPlanChange | call_10000 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 19164958 | 1 | 0 | 20 | 2 | 12 | 16 | 0 | 113.666667 | -8.0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
1 | 39244924 | 1 | 1 | 20 | 0 | 21 | 5 | 0 | 274.000000 | -371.0 | 0 | 1 | 2 | 1 | 3 | 2 | 2 | 1 | 0 | 1 |
2 | 39578413 | 1 | 0 | 11 | 1 | 47 | 3 | 0 | 392.000000 | -784.0 | 0 | 1 | 0 | 0 | 3 | 3 | 0 | 0 | 0 | 1 |
3 | 40992265 | 1 | 0 | 43 | 0 | 4 | 12 | 0 | 31.000000 | -76.0 | 0 | 1 | 2 | 1 | 3 | 3 | 0 | 0 | 0 | 1 |
4 | 43061957 | 1 | 1 | 60 | 0 | 9 | 14 | 0 | 129.333333 | -334.0 | 0 | 1 | 0 | 0 | 3 | 3 | 0 | 0 | 0 | 0 |
df['churn'].value_counts()
0 1929
1 1534
Name: churn, dtype: int64
切分训练集测试集
X = df.iloc[:, 3:].values
y = df['churn'].values
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3,
random_state = 23)
print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)
(2424, 17) (1039, 17) (2424,) (1039,)
from sklearn.ensemble import BaggingClassifier
bgc = BaggingClassifier(n_estimators=500)
bgc.fit(X_train, y_train)
bgc.predict(X_test)
array([0, 0, 1, ..., 0, 0, 1])
from sklearn.metrics import classification_report
print(classification_report(y_test, bgc.predict(X_test)))
precision recall f1-score support
0 0.82 0.88 0.85 595
1 0.82 0.74 0.77 444
micro avg 0.82 0.82 0.82 1039
macro avg 0.82 0.81 0.81 1039
weighted avg 0.82 0.82 0.82 1039
针对样本:训练集内!
from sklearn.model_selection import cross_val_score
cross_val_score(bgc, X_train, y_train, cv = 5) # 指标为precision 即准确率
array([0.82474227, 0.85979381, 0.82061856, 0.84123711, 0.85743802])
# 看测试集AUC为多少
from sklearn import metrics
pred = bgc.predict_proba(X_test)[:,1]
fpr, tpr, thresholds = metrics.roc_curve(y_test, pred)
metrics.auc(fpr, tpr)
0.9118139147550912
def CalAuc(model, X_test, y_test):
# 看测试集AUC为多少
from sklearn import metrics
pred = model.predict_proba(X_test)[:,1]
fpr, tpr, thresholds = metrics.roc_curve(y_test, pred)
auc = metrics.auc(fpr, tpr)
print('模型在测试集上的AUC值为: %.4f' %(auc))
CalAuc(bgc, X_test, y_test)
模型在测试集上的AUC值为: 0.9118
from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier(oob_score=True, random_state=23)
rfc.fit(X_train, y_train)
# 查看袋外误差
print('模型袋外误差为: %.4f' % (rfc.oob_score_))
# 查看测试集auc
CalAuc(rfc, X_test, y_test)
模型袋外误差为: 0.8106
模型在测试集上的AUC值为: 0.8930
/Users/apple/anaconda3/lib/python3.6/site-packages/sklearn/ensemble/forest.py:246: FutureWarning: The default value of n_estimators will change from 10 in version 0.20 to 100 in 0.22.
"10 in version 0.20 to 100 in 0.22.", FutureWarning)
/Users/apple/anaconda3/lib/python3.6/site-packages/sklearn/ensemble/forest.py:458: UserWarning: Some inputs do not have OOB scores. This probably means too few trees were used to compute any reliable oob estimates.
warn("Some inputs do not have OOB scores. "
/Users/apple/anaconda3/lib/python3.6/site-packages/sklearn/ensemble/forest.py:463: RuntimeWarning: invalid value encountered in true_divide
predictions[k].sum(axis=1)[:, np.newaxis])
RF需要调参的参数也包括两部分:
# 导入相应的库
from sklearn.model_selection import GridSearchCV
# 设置要调参的参数
param_test1 = {'n_estimators':range(10,71,10)} # 即[10, 20, 30, 40, 50, 60, 70]
# 搭建模型
estimator = RandomForestClassifier(min_samples_split=100, # 内部节点再划分所需最小样本数
min_samples_leaf=20, # 叶子节点最少样本数
max_depth=8, # 决策树最大深度
max_features='sqrt', # RF划分时考虑的最大特征数 "sqrt"或者"auto"意味着划分时最多考虑根号N个特征。
random_state=10)
# 网格调参框架搭建
gsearch1 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test1, # 参数
scoring='roc_auc', # 评价准则
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch1.fit(X_train, y_train)
# 查看调参结果
gsearch1.best_params_, gsearch1.best_score_, gsearch1.best_estimator_
({'n_estimators': 20},
0.9179481242409072,
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=8, max_features='sqrt', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=20, min_samples_split=100,
min_weight_fraction_leaf=0.0, n_estimators=20, n_jobs=None,
oob_score=False, random_state=10, verbose=0, warm_start=False))
# 设置要调参的参数
param_test2 = {'max_depth': range(3,14,2),
'min_samples_split': range(50,201,20)}
# 搭建模型
estimator = RandomForestClassifier(n_estimators=20, # 内部节点再划分所需最小样本数
min_samples_leaf=20, # 叶子节点最少样本数
max_features='sqrt', # RF划分时考虑的最大特征数 "sqrt"或者"auto"意味着划分时最多考虑根号N个特征。
oob_score=True,
random_state=23)
# 网格调参框架搭建
gsearch2 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test2, # 参数
scoring='roc_auc', # 评价准则
iid=False,
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch2.fit(X_train, y_train)
# 查看调参结果
gsearch2.best_params_, gsearch2.best_score_, gsearch2.best_estimator_
({'max_depth': 9, 'min_samples_split': 50},
0.9193420878244473,
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=9, max_features='sqrt', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=20, min_samples_split=50,
min_weight_fraction_leaf=0.0, n_estimators=20, n_jobs=None,
oob_score=True, random_state=23, verbose=0, warm_start=False))
rf1 = RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=9, max_features='sqrt', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=20, min_samples_split=50,
min_weight_fraction_leaf=0.0, n_estimators=20, n_jobs=None,
oob_score=True, random_state=23, verbose=0, warm_start=False)
rf1.fit(X,y)
print(rf1.oob_score_)
0.8255847531042448
没有调参之前模型袋外误差为: 0.8106,现在调参之后提升了!
# 设置要调参的参数
param_test3 = {'min_samples_leaf': range(10,60,10),
'min_samples_split': range(50,201,20)}
# 搭建模型
estimator = RandomForestClassifier(n_estimators=20, # 内部节点再划分所需最小样本数
max_depth=9,
max_features='sqrt', # RF划分时考虑的最大特征数 "sqrt"或者"auto"意味着划分时最多考虑根号N个特征。
oob_score=True,
random_state=23)
# 网格调参框架搭建
gsearch3 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test3, # 参数
scoring='roc_auc', # 评价准则
iid=False,
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch3.fit(X_train, y_train)
# 查看调参结果
gsearch3.best_params_, gsearch3.best_score_, gsearch3.best_estimator_
({'min_samples_leaf': 10, 'min_samples_split': 50},
0.9208079341741634,
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=9, max_features='sqrt', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=10, min_samples_split=50,
min_weight_fraction_leaf=0.0, n_estimators=20, n_jobs=None,
oob_score=True, random_state=23, verbose=0, warm_start=False))
最后我们再对最大特征数max_features做调参
# 设置要调参的参数
param_test4 = {'max_features': range(3,11,2)}
# 搭建模型
estimator = RandomForestClassifier(n_estimators=20, # 内部节点再划分所需最小样本数
max_depth=9,
min_samples_leaf=10,
min_samples_split=50,
oob_score=True,
random_state=23)
# 网格调参框架搭建
gsearch4 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test4, # 参数
scoring='roc_auc', # 评价准则
iid=False,
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch4.fit(X_train, y_train)
# 查看调参结果
gsearch4.best_params_, gsearch4.best_score_, gsearch4.best_estimator_
({'max_features': 9},
0.924027507693055,
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=9, max_features=9, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=10, min_samples_split=50,
min_weight_fraction_leaf=0.0, n_estimators=20, n_jobs=None,
oob_score=True, random_state=23, verbose=0, warm_start=False))
用我们搜索到的最佳参数,我们再看看最终的模型拟合
rf_final = RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=9, max_features=9, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=10, min_samples_split=50,
min_weight_fraction_leaf=0.0, n_estimators=20, n_jobs=None,
oob_score=True, random_state=23, verbose=0, warm_start=False)
rf_final.fit(X_train, y_train)
# 查看袋外误差
print('调参后模型袋外误差为: %.4f' % (rf_final.oob_score_))
# 查看测试集auc
CalAuc(rf_final, X_test, y_test)
调参后模型袋外误差为: 0.8350
模型在测试集上的AUC值为: 0.9127
截图贴上调参之前的oob和测试集auc
可以看到调参后比调参之前,无论是模型的袋外误差还是测试集上AUC均有提升!
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
# 单颗树的参数
DTC = DecisionTreeClassifier(max_depth=2, min_samples_split=20, min_samples_leaf=5)
# 搭建模型
adb_clf = AdaBoostClassifier(DTC,
algorithm="SAMME",
n_estimators=200,
learning_rate=0.8)
# 拟合
adb_clf.fit(X_train, y_train)
# 查看预测结果
CalAuc(adb_clf, X_test, y_test)
模型在测试集上的AUC值为: 0.9128
可以看到没有调参的Adaboost在测试集上的auc就比调参之后的随机森林要好了!boosting牛逼!当然不同的数据集上表现肯定也会存在差异
# 导入相应的库
from sklearn.model_selection import GridSearchCV
# 设置要调参的参数
param_test1 = {'n_estimators':range(200,500,100)} # 即[10, 20, 30, 40, 50, 60, 70]
# 搭建模型
estimator = AdaBoostClassifier(DTC,
algorithm="SAMME",
learning_rate=0.8)
# 网格调参框架搭建
gsearch1 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test1, # 参数
scoring='roc_auc', # 评价准则
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch1.fit(X_train, y_train)
# 查看调参结果
gsearch1.best_params_, gsearch1.best_score_, gsearch1.best_estimator_
({'n_estimators': 400},
0.9391287966806948,
AdaBoostClassifier(algorithm='SAMME',
base_estimator=DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=2,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=5, min_samples_split=20,
min_weight_fraction_leaf=0.0, presort=False, random_state=None,
splitter='best'),
learning_rate=0.8, n_estimators=400, random_state=None))
# 查看效果
CalAuc(gsearch1.best_estimator_, X_test, y_test)
模型在测试集上的AUC值为: 0.9123
# 设置要调参的参数
param_test2 = {'learning_rate': [0.5, 0.6, 0.7, 0.8]}
# 搭建模型
estimator = AdaBoostClassifier(DTC,
algorithm="SAMME",
n_estimators=400)
# 网格调参框架搭建
gsearch2 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test2, # 参数
scoring='roc_auc', # 评价准则
iid=False,
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch2.fit(X_train, y_train)
# 查看调参结果
gsearch2.best_params_, gsearch2.best_score_, gsearch2.best_estimator_
({'learning_rate': 0.8},
0.9391284752443939,
AdaBoostClassifier(algorithm='SAMME',
base_estimator=DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=2,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=5, min_samples_split=20,
min_weight_fraction_leaf=0.0, presort=False, random_state=None,
splitter='best'),
learning_rate=0.8, n_estimators=400, random_state=None))
# 查看效果
CalAuc(gsearch2.best_estimator_, X_test, y_test)
模型在测试集上的AUC值为: 0.9123
感觉调参效果不是很理想哈~暂时这样,掌握方法就ok,可能不同数据集表现不一样!
重要参数分为两类:
from sklearn.ensemble import GradientBoostingClassifier
# 拟合模型
gbm0 = GradientBoostingClassifier(random_state=23)
gbm0.fit(X,y)
# 查看预测结果
CalAuc(gbm0, X_test, y_test)
模型在测试集上的AUC值为: 0.9555
gbm0
GradientBoostingClassifier(criterion='friedman_mse', init=None,
learning_rate=0.1, loss='deviance', max_depth=3,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=100,
n_iter_no_change=None, presort='auto', random_state=23,
subsample=1.0, tol=0.0001, validation_fraction=0.1,
verbose=0, warm_start=False)
首先我们从步长(learning rate)和迭代次数(n_estimators)入手。一般来说,开始选择一个较小的步长来网格搜索最好的迭代次数。这里,我们将步长初始值设置为0.1。对于迭代次数进行网格搜索如下:
# 导入相应的库
from sklearn.model_selection import GridSearchCV
# 设置要调参的参数
param_test1 = {'n_estimators':range(80,300,20),
'learning_rate': [0.1, 0.2, 0.3]} # 即[10, 20, 30, 40, 50, 60, 70]
# 搭建模型
estimator = GradientBoostingClassifier(
min_samples_split=300,
min_samples_leaf=20,
max_depth=8,
max_features='sqrt',
subsample=0.8, # 子采样
random_state=23
)
# 网格调参框架搭建
gsearch1 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test1, # 参数
scoring='roc_auc', # 评价准则
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch1.fit(X_train, y_train)
# 查看调参结果
gsearch1.best_params_, gsearch1.best_score_, gsearch1.best_estimator_
({'learning_rate': 0.1, 'n_estimators': 180},
0.9426413486391743,
GradientBoostingClassifier(criterion='friedman_mse', init=None,
learning_rate=0.1, loss='deviance', max_depth=8,
max_features='sqrt', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=20, min_samples_split=300,
min_weight_fraction_leaf=0.0, n_estimators=180,
n_iter_no_change=None, presort='auto', random_state=23,
subsample=0.8, tol=0.0001, validation_fraction=0.1,
verbose=0, warm_start=False))
# 查看预测结果
CalAuc(gsearch1, X_test, y_test) # 尴尬了 降了这么多。。。
模型在测试集上的AUC值为: 0.9134
现在我们开始对决策树进行调参。首先我们对决策树最大深度max_depth和内部节点再划分所需最小样本数min_samples_split进行网格搜索。
# 设置要调参的参数
param_test2 = {'max_depth': range(3,14,2),
'min_samples_split': range(100,801,200)}
# 搭建模型
estimator = GradientBoostingClassifier(min_samples_leaf=20,
learning_rate=0.1,
n_estimators=180,
max_features='sqrt',
subsample=0.8, # 子采样
random_state=23
)
# 网格调参框架搭建
gsearch2 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test2, # 参数
scoring='roc_auc', # 评价准则
iid=False,
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch2.fit(X_train, y_train)
# 查看调参结果
gsearch2.best_params_, gsearch2.best_score_, gsearch2.best_estimator_
({'max_depth': 7, 'min_samples_split': 300},
0.9423875377548085,
GradientBoostingClassifier(criterion='friedman_mse', init=None,
learning_rate=0.1, loss='deviance', max_depth=7,
max_features='sqrt', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=20, min_samples_split=300,
min_weight_fraction_leaf=0.0, n_estimators=180,
n_iter_no_change=None, presort='auto', random_state=23,
subsample=0.8, tol=0.0001, validation_fraction=0.1,
verbose=0, warm_start=False))
# 查看预测结果
CalAuc(gsearch2, X_test, y_test)
模型在测试集上的AUC值为: 0.9140
由于决策树深度7是一个比较合理的值,我们把它定下来,对于内部节点再划分所需最小样本数min_samples_split,我们暂时不能一起定下来,因为这个还和决策树其他的参数存在关联。下面我们再对内部节点再划分所需最小样本数min_samples_split和叶子节点最少样本数min_samples_leaf一起调参。
# 设置要调参的参数
param_test3 = {'min_samples_leaf':range(60,101,10),
'min_samples_split': range(800,1900,200)}
# 搭建模型
estimator = GradientBoostingClassifier(
max_depth=7,
learning_rate=0.1,
n_estimators=180,
max_features='sqrt',
subsample=0.8, # 子采样
random_state=23
)
# 网格调参框架搭建
gsearch3 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test3, # 参数
scoring='roc_auc', # 评价准则
iid=False,
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch3.fit(X_train, y_train)
# 查看调参结果
gsearch3.best_params_, gsearch3.best_score_, gsearch3.best_estimator_
({'min_samples_leaf': 70, 'min_samples_split': 800},
0.9386033762893987,
GradientBoostingClassifier(criterion='friedman_mse', init=None,
learning_rate=0.1, loss='deviance', max_depth=7,
max_features='sqrt', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=70, min_samples_split=800,
min_weight_fraction_leaf=0.0, n_estimators=180,
n_iter_no_change=None, presort='auto', random_state=23,
subsample=0.8, tol=0.0001, validation_fraction=0.1,
verbose=0, warm_start=False))
# 查看预测结果
CalAuc(gsearch3, X_test, y_test)
模型在测试集上的AUC值为: 0.9221
# 设置要调参的参数
param_test4 = {'max_features': range(7,18,2)}
# 搭建模型
estimator = GradientBoostingClassifier(min_samples_leaf=70,
min_samples_split = 800,
max_depth=7,
learning_rate=0.1,
n_estimators=180,
subsample=0.8, # 子采样
random_state=23
)
# 网格调参框架搭建
gsearch4 = GridSearchCV(estimator = estimator, # 预测器
param_grid = param_test4, # 参数
scoring='roc_auc', # 评价准则
iid=False,
cv=5 ) # 交叉验证次数
# 拟合模型 开始漫漫调参路 针对训练集
gsearch4.fit(X_train, y_train)
# 查看调参结果
gsearch4.best_params_, gsearch4.best_score_, gsearch4.best_estimator_
({'max_features': 13},
0.9389232022433157,
GradientBoostingClassifier(criterion='friedman_mse', init=None,
learning_rate=0.1, loss='deviance', max_depth=7,
max_features=13, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=70, min_samples_split=800,
min_weight_fraction_leaf=0.0, n_estimators=180,
n_iter_no_change=None, presort='auto', random_state=23,
subsample=0.8, tol=0.0001, validation_fraction=0.1,
verbose=0, warm_start=False))
其余还可以继续调参的:
import xgboost as xgb # 直接引用xgboost。接下来会用到其中的“cv”函数。
from xgboost.sklearn import XGBClassifier # 是xgboost的sklearn包。这个包允许我们像GBM一样使用Grid Search 和并行处理。
all_params = {'learning_rate': 0.1, # 学习速率
'n_estimators': 500, # 树的棵树
'max_depth': 5, # 最大深度
'min_child_weight': 1, # 决定最小叶子节点样本权重和。这个参数用于避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。但是如果这个值过高,会导致欠拟合。这个参数需要使用CV来调整。
'seed': 0, # 随机数的种子 默认为0
'subsample': 0.8, # 和GBM中的subsample参数一模一样。这个参数控制对于每棵树,随机采样的比例。
'colsample_bytree': 0.8, # 和GBM里面的max_features参数类似。用来控制每棵随机采样的列数的占比(每一列是一个特征)。
'gamma': 0, # 在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。
'reg_alpha': 0, # L1 regularization term on weights
'reg_lambda': 1 # L2 regularization term on weights
}
# 搭建模型
xgb_clf = xgb.XGBClassifier(**all_params)
# 拟合模型
xgb_clf.fit(X_train, y_train)
# 查看AUC
CalAuc(xgb_clf, X_test, y_test)
模型在测试集上的AUC值为: 0.9108
from sklearn.model_selection import GridSearchCV
# 需要调整的参数
cv_params = {'n_estimators': [400, 500, 600, 700, 800]}
# 固定参数
other_params = {'learning_rate': 0.1, 'n_estimators': 500, 'max_depth': 5, 'min_child_weight': 1, 'seed': 0,
'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0, 'reg_alpha': 0, 'reg_lambda': 1}
# 搭模型
model = xgb.XGBClassifier(**other_params)
# 网格搜索
optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params,
scoring='r2', cv=5, verbose=1, n_jobs=4)
# 拟合
optimized_GBM.fit(X_train, y_train)
optimized_GBM.best_estimator_
Fitting 5 folds for each of 5 candidates, totalling 25 fits
[Parallel(n_jobs=4)]: Using backend LokyBackend with 4 concurrent workers.
[Parallel(n_jobs=4)]: Done 25 out of 25 | elapsed: 18.5s finished
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
colsample_bytree=0.8, gamma=0, learning_rate=0.1, max_delta_step=0,
max_depth=5, min_child_weight=1, missing=None, n_estimators=400,
n_jobs=1, nthread=None, objective='binary:logistic', random_state=0,
reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=0, silent=True,
subsample=0.8)
# 查看AUC
CalAuc(optimized_GBM, X_test, y_test)
模型在测试集上的AUC值为: 0.9125
同理可以调试:
LightGBM,我们有核心参数,学习控制参数,IO参数,目标参数,度量参数,网络参数,GPU参数,模型参数,这里我常修改的便是:
import lightgbm as lgb
from lightgbm.sklearn import LGBMClassifier # 是lightgbm的sklearn包。这个包允许我们像GBM一样使用Grid Search 和并行处理。
# 搭建模型
model_lgb = lgb.LGBMClassifier(
boosting_type='gbdt',
objective = 'binary',
metric = 'auc',
verbose = 0,
learning_rate = 0.01,
num_leaves = 35,
feature_fraction=0.8,
bagging_fraction= 0.9,
bagging_freq= 8,
lambda_l1= 0.6,
lambda_l2= 0
)
# 拟合模型
model_lgb.fit(X_train, y_train)
LGBMClassifier(bagging_fraction=0.9, bagging_freq=8, boosting_type='gbdt',
class_weight=None, colsample_bytree=1.0, feature_fraction=0.8,
importance_type='split', lambda_l1=0.6, lambda_l2=0,
learning_rate=0.01, max_depth=-1, metric='auc',
min_child_samples=20, min_child_weight=0.001, min_split_gain=0.0,
n_estimators=100, n_jobs=-1, num_leaves=35, objective='binary',
random_state=None, reg_alpha=0.0, reg_lambda=0.0, silent=True,
subsample=1.0, subsample_for_bin=200000, subsample_freq=0,
verbose=0)
# 查看AUC
CalAuc(model_lgb, X_test, y_test)
模型在测试集上的AUC值为: 0.9219
parameters = {
'max_depth': [15, 20, 25]
# 'learning_rate': [0.01, 0.02, 0.05, 0.1, 0.15],
# 'feature_fraction': [0.6, 0.7, 0.8, 0.9, 0.95],
# 'bagging_fraction': [0.6, 0.7, 0.8, 0.9, 0.95],
# 'bagging_freq': [2, 4, 5, 6, 8],
# 'lambda_l1': [0, 0.1, 0.4, 0.5, 0.6],
# 'lambda_l2': [0, 10, 15, 35, 40],
# 'cat_smooth': [1, 10, 15, 20, 35]
}
# 注:实际调参的时候可以把上述代码取消注释,因为测试本机跑太慢了 所以只保留一个
gbm = lgb.LGBMClassifier(boosting_type='gbdt',
objective = 'binary',
metric = 'auc',
verbose = 0,
learning_rate = 0.01,
num_leaves = 35,
feature_fraction=0.8,
bagging_fraction= 0.9,
bagging_freq= 8,
lambda_l1= 0.6,
lambda_l2= 0)
# 有了gridsearch我们便不需要fit函数
gsearch = GridSearchCV(gbm, param_grid=parameters, scoring='accuracy', cv=3)
gsearch.fit(X_train, y_train)
GridSearchCV(cv=3, error_score='raise-deprecating',
estimator=LGBMClassifier(bagging_fraction=0.9, bagging_freq=8, boosting_type='gbdt',
class_weight=None, colsample_bytree=1.0, feature_fraction=0.8,
importance_type='split', lambda_l1=0.6, lambda_l2=0,
learning_rate=0.01, max_depth=-1, metric='auc',
min_child_samples=2..., silent=True,
subsample=1.0, subsample_for_bin=200000, subsample_freq=0,
verbose=0),
fit_params=None, iid='warn', n_jobs=None,
param_grid={'max_depth': [15, 20, 25]}, pre_dispatch='2*n_jobs',
refit=True, return_train_score='warn', scoring='accuracy',
verbose=0)
# 查看AUC
CalAuc(gsearch, X_test, y_test)
模型在测试集上的AUC值为: 0.9219
注:实际案例可以继续上述调参过程!本博客注重掌握方法,代码逻辑即可!