文章目录
- 模型集成
- Committees
-
- 多个模型的结果进行融合。
- Bagging
-
- [Bagging 特点](#Bagging 特点)
- Boosting
- GBDT
- XGBoost
模型集成
三个臭皮匠顶一个诸葛亮。
将多个模型融合在一起,得到一个单一模型。
应用:分类,回归,聚类,推荐...
为什么要集成?
结论:将多个模型融合会得到更好的精确度。
假设有 5 个 Accuracy = 70% 的分类器,相互独立。 采用投票的方式将 5 个分类器的结果进行集成:当一个样本被三个及以上的分类器判断为正例,那么集成模型就判断为正例。
集成模型的精度为: C 5 2 ∗ 0. 7 3 ∗ 0. 3 2 + C 5 4 ∗ 0. 7 4 ∗ 0. 3 1 + C 5 5 ∗ 0. 7 5 = 83.7 % C_5^2*0.7^3*0.3^2+C_5^4*0.7^4*0.3^1+C_5^5*0.7^5=83.7\% C52∗0.73∗0.32+C54∗0.74∗0.31+C55∗0.75=83.7%
如果是 101 个分类器(必须保证101个分类器相互独立,这个很难),那么 Accuracy = 99.9%
模型集成要解决的问题
-
如何获得多个模型,并且尽量相互独立。
-
如何将多模型的结果融合。
主要的集成思想
- Committees:委员会,就是投票。每个模型都有投票权。
- Boosting:贪心算法。如果要训练 n 个模型,每次训练一个模型,根据上一个模型训练的结果训练下一个模型。
- Space split:将一个很大问题空间分割成 n 块,每次训练一个模型。比如:决策树
- Mixture Model:空间软化分
Committees
多个模型的结果进行融合。
- 分类问题:投票。
- 回归问题:模型的输出的均值。
Bagging
解决问题:获得多个模型,并且尽量相互独立。也就是获取多个独立的训练集。可以并行训练模型。
Bagging(Bootstrap Aggregation)自举集成。
算法描述
对训练数据集进行等概率放回采样,得到多个训练数据集,分别训练模型。
带放回的采样概率分析:
训练集有 n 条样本,放回的随机抽出 n 个样本。问:每个样本被抽的概率是多少?
分析:
样本被抽到的概率: 1 n \frac{1}{n} n1
样本被没抽到的概率: 1 − 1 n 1-\frac{1}{n} 1−n1
n 次抽样都被没抽到的概率: ( 1 − 1 n ) n (1-\frac{1}{n})^n (1−n1)n
n 次抽样都至少一次被抽到的概率: 1 − ( 1 − 1 n ) n 1-(1-\frac{1}{n})^n 1−(1−n1)n
当 n 很大时:KaTeX parse error: Expected '}', got 'EOF' at end of input: ...t_{n\rightarrow }{1-(1-\frac{1}{n})^n}=1-\frac{1}{e}=0.632$
Bagging 特点
Bagging 适合弱分类器
- 不稳定:随机采样会得到不同的基分类器。
- 每个基分分类器准确率略高于 50%。
- Bagging较适合偏差小,方差大的基分类器(决策树)。
Bagging 不适合强分类器
- 每个基分类器只有更少的训练样本,集成后反而不如单个分类器。
Boosting
核心思想:对训练数据集进行采样,当前分错的样本采样权重更大。
Boosting 工作机制:
迭代 k 次,得到 k 个模型。
多模型融合:不等权投票,加大误差率小的分类器的投票权重。
AdaBoost
解决问题:通过不同权重的重采样获取不过训练集。必须顺序训练模型。
算法过程
假设是一个二分类任务。
输入:训练集 T = [ ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x n , y n ) ] ; x i ∈ X ⊆ R n ; y i ∈ Y = [ − 1 , 1 ] T=[(x_1,y_1),(x_2,y_2),...,(x_n,y_n)]\,\,; x_i \in X \subseteq R^n\,;y_i \in Y=[-1,1] T=[(x1,y1),(x2,y2),...,(xn,yn)];xi∈X⊆Rn;yi∈Y=[−1,1]
输出:G(x)
-
初始化训练数据的权值分布(等权):
D 1 = ( w 1 , 1 , w 1 , 2 , . . . , w 1 , i , . . . , w 1 , n ) ; w 1 , i = 1 N , i = 1 , 2 , . . . , N D_1=(w_{1,1},w_{1,2},...,w_{1,i},...,w_{1,n}) \,;w_{1,i}=\frac{1}{N},i = 1,2,...,N D1=(w1,1,w1,2,...,w1,i,...,w1,n);w1,i=N1,i=1,2,...,N
-
根据样本的权重,有放回抽样,进行 M 轮,得到 M 个模型。
-
在 D m D_m Dm 上,训练出一个基分类器: G m ( x ) : X → [ − 1 , + 1 ] G_m(x):X\rightarrow [-1,+1] Gm(x):X→[−1,+1]
-
计算$G_m(x) $在训练集 D m D_m Dm 上的分类误差率: e m = P ( G m ( x i ) ! = y i ) = ∑ i − 1 N w m i I ( G m ( x i ) ! = y i ) e_m = P(G_m(x_i)!=y_i)=\sum_{i-1}^N{w_{mi}I(G_m(x_i)!=y_i)} em=P(Gm(xi)!=yi)=∑i−1NwmiI(Gm(xi)!=yi)
注意:加权分类误差率 w m i w_{mi} wmi
-
计算 G m ( x ) G_m(x) Gm(x) 的系数(模型的权重): a m = 1 2 l o g e 1 − e m e m a_m = \frac{1}{2}log_e{\frac{1-e_m}{e_m}} am=21logeem1−em
-
更新训练数据的权值分布: D m + 1 = ( w m + 1 , 1 , w m + 1 , 2 , . . . , w m + 1 , i , . . . , w m + 1 , n ) D_{m+1}=(w_{m+1,1},w_{m+1,2},...,w_{m+1,i},...,w_{m+1,n}) Dm+1=(wm+1,1,wm+1,2,...,wm+1,i,...,wm+1,n)
w m + 1 , i = w m , i Z m e − a m y i G m ( x i ) ; i = 1 , 2 , . . . , N w_{m+1,i} = \frac{w_{m,i}}{Z_m}e^{-a_my_iG_m(x_i)};i=1,2,...,N wm+1,i=Zmwm,ie−amyiGm(xi);i=1,2,...,N
Z m Z_m Zm规范化因子: Z m = ∑ i = 1 N e x p ( − a m y i G m ( x i ) ) Z_m=\sum_{i=1}^Nexp(-a_my_iG_m(x_i)) Zm=∑i=1Nexp(−amyiGm(xi))
Z m Z_m Zm 使 D m D_m Dm 成为一个分布。
-
-
构建基本分类器的线性组合: f ( x ) = ∑ i = 1 M a m G m ( x ) f(x)=\sum_{i=1}^M{a_mG_m(x)} f(x)=∑i=1MamGm(x)
最终分类器: G ( x ) = s i g n ( f ( x ) ) = s i g n ( ∑ i = 1 M a m G m ( x i ) ) G(x)=sign(f(x))=sign(\sum_{i=1}^M{a_mG_m(x_i)}) G(x)=sign(f(x))=sign(∑i=1MamGm(xi))
python
def ada_boost_train(data_set, label, num=40):
class_list = []
n = len(data_set)
w = [1.0 / n] * n
agg_class_est = [0.0] * n
for i in range(num):
# 构建基分类器:决策树
best_stump, error, class_est = build_stump(data_set, label, w)
alpha = float(0.5 * math.log((1.0 - error) / error, math.e))
best_stump["alpha"] = alpha
class_list.append(best_stump)
print("alpha=%s, classEst=%s, bestStump=%s, error=%s " % (alpha, class_est, best_stump, error))
expon = [0.0] * n
for i in range(n):
expon[i] = -1 * alpha * label[i] * class_est[i]
for i in range(n):
w[i] = w[i] * math.e(expon[i])
z = sum(w)
w = [w_i / z for w_i in w]
agg_class_est = [agg_class_est[i] + alpha * class_est[i] for i in range(n)]
return class_list, agg_class_est
GBDT
Gradient Boosting Decision Tree,梯度提升决策树。
GBDT 的基分类器限定只能是:CART 回归树模型。
每一次对残差(实际值与预测值之差)进行拟合,最后把预测值相加得到最终的预测值。
比如:灯泡的寿命是10年,经过三棵树的训练(拟合10),第一颗树的训练结果为 5 年,那么残差为10-5=5年,在此基础上训练(拟合10 - 5 =5),第二棵树的训练结果为 3 年,残差为5-3=2年,接着第三棵树(拟合5 - 3 =2),训练结果为1年,残差为2-1=1年,结束,那么这棵树最终预测的寿命=5+3+1=9年,这个结果显然更接近于真实值。
残差拟合思想蛮简单,但是损失的拟合不好度量。
负梯度拟合
通过梯度提升的方法集成多个决策树。
大牛Freidman提出了用损失函数的负梯度来拟合本轮损失的近似值,进而拟合一个CART回归树。
通过损失函数的负梯度来拟合,我们找到了一种通用的拟合损失误差的办法,这样无论是分类问题还是回归问题,我们通过其损失函数的负梯度的拟合,就可以用GBDT来解决我们的分类回归问题。区别仅仅在于损失函数不同导致的负梯度不同而已。
加法模型
y i = ∑ k = 1 K f k ( x i ) , f k ∈ F y_i = \sum_{k=1}^K{f_k(x_i)},f_k \in F yi=∑k=1Kfk(xi),fk∈F
如何学习加法模型?
前向分布算法(forward stagewise algorithm),因为学习的是加法模型,如果能够从前往后,每一步只学习一个基函数及其系数(结构),逐步逼近优化目标函数,那么就可以简化复杂度。这一学习过程称之为 Boosting。
例如:
决策树的复杂度可以由正则项
$\Omega(f_t) = r*T+\frac{1}{2}\lambda \sum_{j=1}^T{w_j^2} $
- T:树的叶子节点数量
- w:叶子节点对应的值向量的 L2 范数
节点分裂收益
假设当前节点为 C ,分裂后左孩子节点为 L,右孩子节点为 R。obj 为目标函数值。
G a i n = o b j c − o b j L − o b j R Gain = obj_c - obj_L - obj_R Gain=objc−objL−objR
G a i n = 1 2 [ G L 2 H L + λ + G R 2 H R + λ − ( G L + G R ) 2 H L + H R + λ ] − r Gain = \frac{1}{2}[\frac{G_L^2}{H_L+\lambda} +\frac{G_R^2}{H_R+\lambda}-\frac{(G_L+G_R)^2}{H_L+H_R+\lambda} ] - r Gain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−r
− r -r −r 表示:因为增加了树的复杂性(该分裂增加了一个叶子节点)带来的惩罚。
参考:https://cloud.tencent.com/developer/article/1005611
XGBoost
XGBoost是一种集成学习算法,通过CART 回归树,每一次对残差(实际值与预测值之差)进行拟合,最后把预测值相加得到最终的预测值。
XGBoost 中树都是二叉树。
比如:灯泡的寿命是10年,经过三棵树的训练(拟合10),第一颗树的训练结果为 5 年,那么残差为10-5=5年,在此基础上训练(拟合10 - 5 =5),第二棵树的训练结果为 3 年,残差为5-3=2年,接着第三棵树(拟合5 - 3 =2),训练结果为1年,残差为2-1=1年,结束,那么这棵树最终预测的寿命=5+3+1=9年,这个结果显然更接近于真实值。
XGBoost 特点
- w 是最优化求出来的,不是平均值或规则指定的,这是XGBoost的一个创新。
- loss function中包含了正则化防止过拟合的技术。
- 支持自定义loss function,只要能泰勒展开(能求一阶导和二阶导)就行。
- 支持并行化,训练速度快,boosting技术中下一棵树依赖上述树的训练和预测,所以树与树之间应该是只能串行!但是在选择最佳分裂点,进行枚举的时候并行!(这个也是树形成最耗时的阶段)。
- XGBoost还特别设计了针对稀疏数据的算法。
- 可实现后剪枝
- 交叉验证,方便选择最好的参数
- 行采样、列采样,随机森林的套路(防止过拟合)。
- 预测值为每个回归树预测值的加和,这里也可以是加权。
- XGBoost还支持设置样本权重,这个权重体现在梯度g和二阶梯度h上,是不是有点adaboost的意思,重点关注某些样本。
XGBoost 参数
通用参数
-
n_estimators:
-
booster : 默认值 gbtree。
选择基模型,gbtree:树模型,gbline:线性模型。
-
Silent :默认值为 0。
Silent = 1静默模式开启,不会输出任何日志。0 帮助我们了解模型运行情况。
-
nthread:默认值最大可能的线程数。
多线程的控制,应当输入系统的核数。如果希望使用cpu 全部核,就不要输入这个参数,算法会自动检测。
booster 参数
-
eta[默认是0.3] 和GBM中的learning rate参数类似。通过减少每一步的权重,可以提高模型的鲁棒性。典型值0.01-0.2
-
min_child_weight[默认是1] 决定最小叶子节点样本权重和。当它的值较大时,可以避免模型学习到局部的特殊样本。但如果这个值过高,会导致欠拟合。这个参数需要用cv来调整
-
max_depth [默认是6] 树的最大深度,这个值也是用来避免过拟合的3-10
-
max_leaf_nodes 树上最大的节点或叶子的数量,可以代替max_depth的作用,应为如果生成的是二叉树,一个深度为n的树最多生成2n个叶子,如果定义了这个参数max_depth会被忽略
-
gamma[默认是0] 在节点分裂时,只有在分裂后损失函数的值下降了,才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。这个参数值越大,算法越保守。
-
max_delta_step[默认是0] 这参数限制每颗树权重改变的最大步长。如果是0意味着没有约束。如果是正值那么这个算法会更保守,通常不需要设置。
-
subsample[默认是1] 这个参数控制对于每棵树,随机采样的比例。减小这个参数的值算法会更加保守,避免过拟合。但是这个值设置的过小,它可能会导致欠拟合。典型值:0.5-1
-
colsample_bytree[默认是1] 用来控制每颗树随机采样的列数的占比每一列是一个特征0.5-1
-
colsample_bylevel[默认是1] 用来控制的每一级的每一次分裂,对列数的采样的占比。
-
lambda[默认是1] 权重的L2正则化项
-
alpha[默认是1] 权重的L1正则化项
-
scale_pos_weight[默认是1] 各类样本十分不平衡时,把这个参数设置为一个正数,可以使算法更快收敛。
学习目标参数
-
Objective:默认值 reg:linear
这个参数定义需要被最小化的损失函数。最常用的值有:binary:logistic二分类的逻辑回归,返回预测的概率非类别。multi:softmax使用softmax的多分类器,返回预测的类别。在这种情况下,你还要多设置一个参数:num_class类别数目。
-
eval_metric:默认值取决于objective参数的取值。
对于有效数据的度量方法。对于回归问题,默认值是rmse,对于分类问题,默认是error。典型值有:rmse均方根误差;mae平均绝对误差;logloss负对数似然函数值;error二分类错误率;merror多分类错误率;mlogloss多分类损失函数;auc曲线下面积。
-
seed:默认是 0
随机数的种子,设置它可以复现随机数据的结果,也可以用于调整参数。
模型保存
python
# 初始化 xgboost 实例
XGB = xgb.XGBClassifier(n_estimators=100, objective="binary:logistic")
# 训练
XGB.fit(x_train, y_train)
# 保存模型
# 模型文件位置
XGB.get_booster().dump_model("/Users/dongyf/dongyf/data/status_rank/model.txt",fmap='/Users/dongyf/dongyf/data/status_rank/tmp_fmap')
fmap :指定导出模型的特征名。
fmap 的文件格式(以空分隔):共三列
Feature_id featrue_name (q or i or int)
- feature_id:从 0 开始直到特征的个数为止,从小到大列。
- Feature_name:model 要指定的 name
- 第三列
- q 表示是数字特征,可以省略 int 表示特征为整数
- i 表示是二分类特征
fmap 文件
tex
0 user_stock_score q
1 status_uid q
2 time q
3 allSymbolsCharLength q
4 allSymbolsSize q
5 contentLength q
6 favCount q
7 followerCount q
8 likeCount q
9 replyCount q
10 retweetCount q
11 staticScore q
12 symbolAccessOrde q
模式为:
python
booster[0]:
0:[replyCount<3.5] yes=1,no=2,missing=1
1:[staticScore<16.7780495] yes=3,no=4,missing=3
3:[status_uid<1.00048115e+09] yes=7,no=8,missing=7
7:[time<61080400] yes=15,no=16,missing=15
15:[time<60873856] yes=31,no=32,missing=31
31:[contentLength<47.5] yes=61,no=62,missing=61
61:leaf=-0.491739154
62:leaf=-0.554545462
32:[time<60969072] yes=63,no=64,missing=63
63:leaf=-0.0666666701
64:leaf=0.150000006
16:leaf=-0.565105617
8:[staticScore<10.0133038] yes=17,no=18,missing=17
17:[status_uid<1.00948416e+09] yes=33,no=34,missing=33
33:[time<19740952] yes=65,no=66,missing=65
65:leaf=0.150000006
66:leaf=-0.163636371
34:[favCount<20.5] yes=67,no=68,missing=67
67:leaf=-0.530347824
68:leaf=-0.47329843
18:[contentLength<13.5] yes=35,no=36,missing=35
35:[contentLength<12.5] yes=69,no=70,missing=69
69:leaf=-0.443478286
70:leaf=-0.0923077017
36:[time<462659840] yes=71,no=72,missing=71
71:leaf=-0.498947412
72:leaf=-0.272727281