集成(ensemble)是合并多个机器学习模型来构建更强大的模型的方法。主要须要解决两个问题:node
个体学习器能够分为同质和异质两种:python
目前来讲,同质个体学习器的应用是最普遍的,通常咱们常说的集成学习的方法都是指的同质个体学习器,而同质个体学习器使用最多的模型是CART决策树和神经网络。算法
同质学习器按照个体学习器之间是否存在依赖关系可分为两类:缓存
以下图所示,bagging的个体学习器使用的训练集由随机采样得到,分别训练后集成。网络
随机采样最经常使用的方法是自助采样,其方法是在m个样本中有放回的随机采样m次以得到m个样本的训练集。其中,某个样本没有被使用的几率是$$P=(1-\frac1m)^m$$令m趋向于正无穷可得$$\lim _{m\rightarrow+\infty}P=\frac 1e$$即大约36.8%的数据不会被取到。并发
bagging的结合策略是简单的投票法和算术平均值,因为Bagging算法每次都进行采样来训练模型,所以泛化能力很强,对于下降模型的方差颇有做用。固然对于训练集的拟合程度就会差一些,也就是模型的偏倚会大一些。app
随机森林是使用cart决策树做为弱学习器的使用bagging方法的集成模型,可是对cart决策树进了改进以加强泛化能力。Cart树在每一个节点进行二分时,选择的是最优的特征,即划分后基尼系数最小或方差最小的特征,决策树中的弱学习器在选择特征时,先随机选择k个特征,而后在这k个特征中选择最优特征。框架
import numpy as np import matplotlib.pyplot as plt %matplotlib inline import warnings warnings.filterwarnings('ignore') #在two_moon数据集上构造5棵树组成的随机森林 import seaborn as sns import mglearn from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import make_moons from sklearn.model_selection import train_test_split X,y = make_moons(n_samples=100,noise=0.25,random_state=3) X_train,X_test,y_train,y_test = train_test_split(X,y,stratify=y,random_state=42) RFC = RandomForestClassifier(n_estimators=5,random_state=2).fit(X_train,y_train) #将每棵树及总预测可视化 fig,axes = plt.subplots(2,3,figsize=(20,10)) for i,(ax,tree) in enumerate(zip(axes.ravel(),RFC.estimators_)): ax.set_title('Tree{}'.format(i)) mglearn.plots.plot_tree_partition(X_train,y_train,tree,ax=ax) mglearn.plots.plot_2d_separator(RFC,X_train,fill=True,ax=axes[-1,-1],alpha=.4) axes[-1,-1].set_title('Random Forest') mglearn.discrete_scatter(X_train[:,0],X_train[:,1],y_train)
能够看出,5棵决策树结果彻底不一样,综合后获得随机森林。dom
#在乳腺癌数据上构建随机森林 from sklearn.datasets import load_breast_cancer cancer = load_breast_cancer() X_train,X_test,y_train,y_test=train_test_split(cancer.data,cancer.target,stratify=cancer.target,random_state=0) RFC = RandomForestClassifier(n_estimators=100,max_features=5,oob_score=True,random_state=88).fit(X_train,y_train) print('训练精度:{}'.format(RFC.score(X_train,y_train))) print('测试精度:{}'.format(RFC.score(X_test,y_test))) print('袋外分数:{}'.format(RFC.oob_score_))
训练精度:1.0 测试精度:0.958041958041958 袋外分数:0.9624413145539906
随机森林的特征重要性:机器学习
fig = plt.figure(dpi=100) plt.barh(range(len(RFC.feature_importances_)),RFC.feature_importances_) plt.yticks(range(len(RFC.feature_importances_)),cancer.feature_names) plt.xlabel("Feature Importance") plt.show()
Bagging框架参数:
决策树参数
决策树参数中最重要的是前四个,后三个通常不会使用。
优势
缺点:
以下图所示,boosting的基本思想是,先训练出一个模型1,此时,每一个样本的权重是相同的,都是样本个数的倒数,计算学习偏差率e1,而后根据学习结果来更新样本权重,增长被错误分类的样本的权重,训练模型2,计算学习偏差率e2。重复该过程以得到T个个体学习器,而后使用结合策略结合成强学习器。所以,boosting系列算法须要解决如下四个问题:
Adaboost回归有不少种方法,这里只介绍分类。
Adaboost二分类中标签y取值为1,-1
Adaboost框架参数
Adaboost分类器调参较为简单,框架参数基本上只须要调节学习率和最大迭代次数。
下面是sklearn实现分类,首先导入相关包并建立数据,数据是二维数据,分布如图所示:
from sklearn.ensemble import AdaBoostClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.datasets import make_gaussian_quantiles # 生成2维正态分布,生成的数据按分位数分为两类,500个样本,2个样本特征,协方差系数为2 X1, y1 = make_gaussian_quantiles(cov=2.0,n_samples=500, n_features=2,n_classes=2, random_state=1) # 生成2维正态分布,生成的数据按分位数分为两类,400个样本,2个样本特征均值都为3,协方差系数为2 X2, y2 = make_gaussian_quantiles(mean=(3, 3), cov=1.5,n_samples=400, n_features=2, n_classes=2, random_state=1) #将两组数据合成一组数据 X = np.concatenate((X1, X2)) y = np.concatenate((y1, - y2 + 1)) plt.scatter(X[:, 0], X[:, 1], marker='o', c=y) plt.show()
#训练模型 ada = AdaBoostClassifier(DecisionTreeClassifier(max_depth=10,min_samples_leaf=5),n_estimators=600,learning_rate=0.7).fit(X,y) print(ada.score(X,y)) #绘图 x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02)) Z = ada.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) cs = plt.contourf(xx, yy, Z, cmap=plt.cm.Paired) plt.scatter(X[:, 0], X[:, 1], marker='o', c=y) plt.show()
1.0
优势:
缺点:
梯度提高树的原理是,构造一系列决策树,每一棵树在前一棵基础上进行修正,以此来得到效果较好的决策树。举个例子,一我的150kg,先训练一个模型,该模型为第T个模型,用这个模型估计他的体重为140kg,误差为10kg,而后再训练一个模型t,用于估计误差,误差的估计值为8kg,而后将这两个模型加起来做为T+1个模型,来估计体重。
首先训练一个Cart树$$f_0(x) = \underbrace{arg\; min}_{c}\sum\limits_{i=1}^{m}L(y_i, c)$$其损失函数为$$L=\sum \frac12[f(x_i)-y_i]^2$$其负梯度为$$r_{ti} = -\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)}=y_i-f(x_i)$$计算每条数据的负梯度,获得数据集$$(x_i,r_{ti})\;\; (i=1,2,..m)$$负梯度恰好是实际值与模型拟合值之间的误差,再训练一个模型用于拟合这个误差$$c_{tj} = \underbrace{arg\; min}_{c}\sum\limits_{x_i \in R_{tj}} L(y_i,f_{t-1}(x_i) +c)$$而后更新学习器$$f_{t}(x) = f_{t-1}(x) + \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})$$
获得学习器t,重复此过程直到结束,最终预测值为$$f(x) = f_T(x) =f_0(x) + \sum\limits_{t=1}^{T}\sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})$$
分类的原理与回归是相似的,只是损失函数不一样,致使负梯度拟合不一样,其余步骤相同。损失函数使用似然函数。
二元损失函数$$L(y, f(x)) = log(1+ exp(-yf(x)))$$$$r_{ti} = -\bigg[\frac{\partial L(y, f(x_i)))}{\partial f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)} = y_i/(1+exp(y_if(x_i)))$$多元损失函数$$L(y, f(x)) = - \sum\limits_{k=1}^{K}y_klog\;p_k(x)$$
框架参数
对于分类模型,有对数似然损失函数"deviance"和指数损失函数"exponential"二者输入选择。默认是对数似然损失函数"deviance"。通常来讲,推荐使用默认的"deviance"。它对二元分离和多元分类各自都有比较好的优化。而指数损失函数等于把咱们带到了Adaboost算法。
对于回归模型,有均方差"ls", 绝对损失"lad", Huber损失"huber"和分位数损失“quantile”。默认是均方差"ls"。通常来讲,若是数据的噪音点很少,用默认的均方差"ls"比较好。若是是噪音点较多,则推荐用抗噪音的损失函数"huber"。而若是咱们须要对训练集进行分段预测的时候,则采用“quantile”。
弱学习器是Cart树,参数与cart树相同。
下面是调参实践,首先训练一个默认模型:
#梯度提高树 from sklearn.ensemble import GradientBoostingClassifier from sklearn.metrics import roc_auc_score gbdt=GradientBoostingClassifier(random_state=10).fit(X_train,y_train) print('训练精度:{}'.format(gbdt.score(X_train,y_train))) print('测试精度:{}'.format(gbdt.score(X_test,y_test))) print('测试AUC:{}'.format(roc_auc_score(y_test,gbdt.predict_proba(X_test)[:,1])))
训练精度:1.0 测试精度:0.958041958041958 测试AUC:0.978511530398323
而后令学习率为0.1,寻找最优的学习器个数,为80个。
from sklearn.model_selection import GridSearchCV param_test1 = {'n_estimators':range(20,101,10)} gsearch1 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1, max_depth=8, random_state=10), param_grid = param_test1, scoring='roc_auc',iid=False,cv=5).fit(X_train,y_train) gsearch1.best_params_,gsearch1.best_score_
({'n_estimators': 80}, 0.9836974622979643)
使用以上两参数,来网格搜索最大深度和最小叶片分割数以及最小叶片数据量。
param_test2 = {'max_depth':range(2,14,2), 'min_samples_split':range(10,41,5), 'min_samples_leaf':[5,41,5]} gsearch2 = GridSearchCV(GradientBoostingClassifier(n_estimators=80,learning_rate=0.1,random_state=10), param_test2,scoring='roc_auc',cv=5).fit(X_train,y_train) gsearch2.best_params_,gsearch2.best_score_
({'max_depth': 10, 'min_samples_leaf': 5, 'min_samples_split': 15}, 0.9939034991321204)
使用新模型参数预测:
gbdt_grided = GradientBoostingClassifier(max_depth=10,min_samples_leaf=5,min_samples_split=10, n_estimators=160,learning_rate=0.05,random_state=10).fit(X_train,y_train) print('训练集AUC{}'.format(roc_auc_score(y_train,gbdt_grided.predict_proba(X_train)[:,1]))) print('测试集AUC{}'.format(roc_auc_score(y_test,gbdt_grided.predict_proba(X_test)[:,1])))
训练集AUC1.0 测试集AUC0.9865828092243186
优势:
缺点:
XGBoost是GBDT的一个高效实现,主要从下面三个方面作了优化:
Boosting加法模型能够表示为$$\hat y_i=\sum _{k=1}^Kf_k(x_i)$$其中K为弱学习器个数,整个模型的损失函数为$$L=\sum_{i=1}^n l(y_i,\hat y_i)$$n为样本容量,上一节所说的GBDT使用的是MSE做为损失函数,固然,也能够用其余损失函数,考虑正则化项,损失函数变为$$L=\sum _{i=1}^nl(y_i,\hat y_i)+\sum_{k=1}^K\Omega (f_k)$$因为$$\hat y_i^t=\hat y_i^{t-1}(x_i)+f_t(x_i)$$其中$f_t(x_i)$就是第t个新加入的模型,所以$$L=\sum _{i=1}^nl(y_i,\hat y_i^{t-1}+f_t)+\sum_{k=1}^K\Omega (f_k)$$
由泰勒定理,$$f(x)=\sum_{i=0}^n\frac {f^{(i)}(a)}{i!}(x-a)^i+R_{n+1}(x)$$其中$R_{n+1}(x)$为$x^n$的高阶无穷小,当n = 2时$$f(x+\delta x)=f(x)+f'(x)\delta x+\frac 12f''(x)\delta x^2$$
作以下替换$$\hat y_i^{t-1}=x,f_t=\delta x$$得$$L=\sum _{i=1}^n[l(y_i,\hat y_i^{t-1})+g_i\delta f_t+\frac 12h_i\delta f_t^2]+\sum_{k=1}^K\Omega (f_k)$$
其中$$g_i=\frac{\partial l(y_i,\hat y_i^{t-1})}{\partial\hat y_i^{t-1}} ,h_i=\frac{\partial^2 l(y_i,\hat y_i^{t-1})}{\partial[\hat y_i^{t-1}]^2}$$
因为已经训练完成第t-1个模型,所以,上一个模型的损失函数为常数,最终第t个模型的损失函数转化为$$\begin{split} L&\approx\sum_{i=1}^n(g_if_t+\frac12h_if_t^2)+\sum_{k=1}^K\Omega(f_k)\\&= \sum_{i=1}^n \left [ g_iw_{q(x_i)}+\frac12h_iw_{q(x_i)}^2 \right] + \gamma T + \frac12 \lambda \sum_{j=1}^T w_j^2 \\&= \sum_{j=1}^T \left[(\sum_{i \in I_j}g_i)w_j + \frac12(\sum_{i \in I_j}h_i + \lambda)w_j^2 \right]+ \gamma T\end{split}$$其中因为决策树同节点中的预测值相等,有$$f_t(x)=w_{q(x)}$$
令$$G_j=\sum_{i \in I_j}g_i,H_j=\sum_{i \in I_j}h_i$$则$$L = \sum_{j=1}^T \left[G_iw_j + \frac12(H_i + \lambda)w_j^2 \right] + \gamma T$$
令一阶偏导为0得$$w_j^*=-\frac{G_j}{H_j+\lambda}$$此时$$L = -\frac12 \sum_{j=1}^T \frac{G_j^2}{H_j+\lambda} + \gamma T$$
以上就是模型的损失函数,在构建决策树模型时,使用L代替MSE进行分叉。
import xgboost as xgb from sklearn.datasets.samples_generator import make_classification from sklearn.model_selection import GridSearchCV # X为样本特征,y为样本类别输出, 共10000个样本,每一个样本20个特征,输出有2个类别,没有冗余特征,每一个类别一个簇 X, y = make_classification(n_samples=10000, n_features=20, n_redundant=0, n_clusters_per_class=1, n_classes=2, flip_y=0.1) X_train, X_test, y_train, y_test = train_test_split(X,y,stratify = y,random_state=1) sklearn_xgb = xgb.XGBClassifier(learning_rate= 0.5, verbosity=1, objective='binary:logistic',random_state=1) sklearn_xgb.fit(X_train, y_train, early_stopping_rounds=10, eval_metric="error", eval_set=[(X_test, y_test)]) gsCv = GridSearchCV(sklearn_xgb, {'max_depth': [4,5,6], 'n_estimators': [5,10,20]}) gsCv.fit(X_train,y_train) print(gsCv.best_score_) print(gsCv.best_params_)
[0] validation_0-error:0.1248 Will train until validation_0-error hasn't improved in 10 rounds. [1] validation_0-error:0.116 [2] validation_0-error:0.116 [3] validation_0-error:0.116 [4] validation_0-error:0.1164 [5] validation_0-error:0.1164 [6] validation_0-error:0.1164 [7] validation_0-error:0.116 [8] validation_0-error:0.1164 [9] validation_0-error:0.1184 [10] validation_0-error:0.118 [11] validation_0-error:0.1172 Stopping. Best iteration: [1] validation_0-error:0.116 0.8838666666666667 {'max_depth': 5, 'n_estimators': 5}
对于XGBoost的框架参数,最重要的是3个参数: booster,n_estimators和objectve。
这里只讨论使用gbtree默认弱学习器的参数。要调参的参数主要是决策树的相关参数以下:
XGBoost还有一些其余的参数须要注意,主要是learning_rate。
learning_rate控制每一个弱学习器的权重缩减系数,和sklearn GBDT的learning_rate相似,较小的learning_rate意味着咱们须要更多的弱学习器的迭代次数。一般咱们用步长和迭代最大次数一块儿来决定算法的拟合效果。因此这两个参数n_estimators和learning_rate要一块儿调参才有效果。固然也能够先固定一个learning_rate,而后调完n_estimators,再调完其余全部参数后,最后再来调learning_rate和n_estimators。
此外,n_jobs控制算法的并发线程数, scale_pos_weight用于类别不平衡的时候,负例和正例的比例。相似于sklearn中的class_weight。importance_type则能够查询各个特征的重要性程度。能够选择“gain”, “weight”, “cover”, “total_gain” 或者 “total_cover”。最后能够经过调用booster的get_score方法获取对应的特征权重。“weight”经过特征被选中做为分裂特征的计数来计算重要性,“gain”和“total_gain”则经过分别计算特征被选中作分裂特征时带来的平均增益和总增益来计算重要性。“cover”和 “total_cover”经过计算特征被选中作分裂时的平均样本覆盖度和整体样本覆盖度来来计算重要性。
from lightgbm import LGBMRegressor from sklearn.metrics import mean_squared_error from sklearn.model_selection import GridSearchCV from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.externals import joblib # 加载数据 iris = load_iris() data = iris.data target = iris.target # 划分训练数据和测试数据 X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2) # 模型训练 gbm = LGBMRegressor(objective='regression', num_leaves=31, learning_rate=0.05, n_estimators=20) gbm.fit(X_train, y_train, eval_set=[(X_test, y_test)], eval_metric='l1', early_stopping_rounds=5) # 模型存储 joblib.dump(gbm, 'loan_model.pkl') # 模型加载 gbm = joblib.load('loan_model.pkl') # 模型预测 y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration_) # 模型评估 print('The rmse of prediction is:', mean_squared_error(y_test, y_pred) ** 0.5) # 特征重要度 print('Feature importances:', list(gbm.feature_importances_)) # 网格搜索,参数优化 estimator = LGBMRegressor(num_leaves=31) param_grid = { 'learning_rate': [0.01, 0.1, 1], 'n_estimators': [20, 40] } gbm = GridSearchCV(estimator, param_grid) gbm.fit(X_train, y_train) print('Best parameters found by grid search are:', gbm.best_params_)
[1] valid_0's l1: 0.770574 valid_0's l2: 0.729519 Training until validation scores don't improve for 5 rounds [2] valid_0's l1: 0.736515 valid_0's l2: 0.662428 [3] valid_0's l1: 0.70474 valid_0's l2: 0.602743 [4] valid_0's l1: 0.673927 valid_0's l2: 0.548122 [5] valid_0's l1: 0.647149 valid_0's l2: 0.498853 [6] valid_0's l1: 0.619693 valid_0's l2: 0.454836 [7] valid_0's l1: 0.59361 valid_0's l2: 0.41521 [8] valid_0's l1: 0.568332 valid_0's l2: 0.378919 [9] valid_0's l1: 0.546353 valid_0's l2: 0.346133 [10] valid_0's l1: 0.523806 valid_0's l2: 0.316952 [11] valid_0's l1: 0.502036 valid_0's l2: 0.290314 [12] valid_0's l1: 0.481625 valid_0's l2: 0.266628 [13] valid_0's l1: 0.461931 valid_0's l2: 0.245002 [14] valid_0's l1: 0.445287 valid_0's l2: 0.225741 [15] valid_0's l1: 0.427082 valid_0's l2: 0.20802 [16] valid_0's l1: 0.409383 valid_0's l2: 0.190996 [17] valid_0's l1: 0.392466 valid_0's l2: 0.175393 [18] valid_0's l1: 0.377264 valid_0's l2: 0.162742 [19] valid_0's l1: 0.36181 valid_0's l2: 0.150029 [20] valid_0's l1: 0.348036 valid_0's l2: 0.139806 Did not meet early stopping. Best iteration is: [20] valid_0's l1: 0.348036 valid_0's l2: 0.139806 The rmse of prediction is: 0.37390595890720224 Feature importances: [1, 9, 40, 18] Best parameters found by grid search are: {'learning_rate': 0.1, 'n_estimators': 40}