集成学习:随机森林、GBDT

集成学习:随机森林、GBDT

集成学习(Ensemble Learning)

• 集成学习的思想是将若干个学习器(分类器&回归器)组合之后产生一个新学习器。弱分类器(weak learner)指那些分类准确率只稍微好于随机猜测的分类器(error rate < 0.5)

• 集成算法的成功在于保证弱分类器的多样性(Diversity)。而且集成不稳定的算法也能够得到一个比较明显的性能提升。

• 常见的集成学习思想有:

• Bagging

• Boosting

• Stacking

• 1. 弱分类器间存在一定的差异性,这会导致分类的边界不同,也就是说可能存在错误。那么将多个弱分类器合并后,就可以得到更加合理的边界,减少整体的错误率,实现更好的效果;

• 2. 对于数据集过大或者过小,可以分别进行划分和有放回的操作产生不同的数据子集,然后使用数据子集训练不同的分类器,最终再合并成为一个大的分类器;

• 3. 如果数据的划分边界过于复杂,使用线性模型很难描述情况,那么可以训练多个模型,然后再进行模型的融合;

• 4. 对于多个异构的特征集的时候,很难进行融合,那么可以考虑每个数据集构建一个分类模型,然后将多个模型融合。

Bagging方法

• Bagging方法又叫做自举汇聚法(Bootstrap Aggregating),思想是:在原始数据集上通过有放回的抽样的方式,重新选择出S个新数据集来分别训练S个分类器的集成技术。

• Bagging方法训练出来的模型在预测新样本分类/回归的时候,会使用多数投票或者求均值的方式来统计最终的分类/回归结果。

• Bagging方法的弱学习器可以是基本的算法模型,eg: Linear、Ridge、Lasso、Logistic、Softmax、ID3、C4.5、CART、SVM、KNN等。

• NOTE:Bagging方式是有放回的抽样,并且每个子集的样本数量必须和原始样本数量一致,所以抽取出来的子集中是存在重复数据的,模型训练的时候允许存在重复数据

• NOTE:差不多有1/3的样本数据是不在Bagging的每个子模型的训练数据中的。
l i m m − > + ∞ ( 1 − 1 m ) m = 1 e = 0.368 lim_{m->+∞}(1-\frac{1}{m})^m=\frac{1}{e}=0.368 limm>+(1m1)m=e1=0.368

随机森林(Random Forest)

• 在Bagging策略的基础上进行修改后的一种算法

• 1. 从原始样本集(n个样本)中用Bootstrap采样(有放回重采样)选出n个样本;

• 2. 使用抽取出来的子数据集(存在重复数据)来训练决策树;从所有属性中随机选择K个属性,从K个属性中选择出最佳分割属性作为当前节点的划分属性,按照这种方式来迭代的创建决策树。

• 3. 重复以上两步m次,即建立m棵决策树;

• 4. 这m个决策树形成随机森林,通过投票表决结果决定数据属于那一类

RF的推广算法

• RF算法在实际应用中具有比较好的特性,应用也比较广泛,主要应用在:分类、回归、特征转换、异常点检测等。常见的RF变种算法如下:

• Extra Tree

• Totally Random Trees Embedding(TRTE)

• Isolation Forest

Extra Tree

• Extra Tree是RF的一个变种,原理基本和RF一样,区别如下:

• 1. RF会随机重采样来作为子决策树的训练集,而Extra Tree每个子决策树采用原始数据集训练;

• 2. RF在选择划分特征点的时候会和传统决策树一样,会基于信息增益、信息增益率、基尼系数、均方差等原则来选择最优特征值;而Extra Tree会随机的选择一个特征值来划分决策树。

• Extra Tree因为是随机选择特征值的划分点,这样会导致决策树的规模一般大于RF所生成的决策树。也就是说Extra Tree模型的方差相对于RF进一步减少。在某些情况下,Extra Tree的泛化能力比RF的强。

Totally Random Trees Embedding(TRTE)

• TRTE是一种非监督的数据转化方式。将低维的数据集映射到高维,从而让映射到高维的数据更好的应用于分类回归模型。

• TRTE算法的转换过程类似RF+KDTree算法的方法,建立T个决策树来拟合数据(是类似KD-Tree一样基于特征属性的方差选择划分特征)。当决策树构建完成后,数据集里的每个数据在T个决策树中叶子节点的位置就定下来了,将位置信息转换为向量就完成了特征转换操作。

• 案例:有3棵决策树,各个决策树的叶子节点数目分别为:5,5,4,某个数据x划分到第一个决策树的第3个叶子节点,第二个决策树的第一个叶子节点,第三个决策树的第四个叶子节点,那么最终的x映射特征编码为:(0,0,1,0,0, 1,0,0,0,0, 0,0,0,1)

Isolation Forest(IForest)

• IForest是一种异常点检测算法,使用类似RF的方式来检测异常点;IForest算法和RF算法的区别在于:

• 1. 在随机采样的过程中,一般只需要少量数据即可;

• 2. 在进行决策树构建过程中,IForest算法会随机选择一个划分特征,并对划

分特征随机选择一个划分阈值;

• 3. IForest算法构建的决策树一般深度max_depth是比较大的。

• 区别原因:目的是异常点检测,所以只要能够区分异常的即可,不需要大量数据;

• 对于异常点的判断,则是将测试样本x拟合到m棵决策树上。计算在每棵树上该样本的叶子节点的深度ht(x)。从而计算出平均深度h(x);然后就可以使用下列公式计算样本点x的异常概率值,p(s,m)的取值范围为[0,1],越接近于1,则是异常点的概率越大。备注:如果落在的叶子节点为正常样本点,那么当前决策树不考虑,如果所有决策树上都是正常样本点,那么直接认为异常点概率为0.
p ( x , m ) = 2 − h ( x ) c ( ( m ) ) c ( m ) = 2 l n ( m − 1 ) + ζ − 2   m − 1 m p(x,m)=2{-\frac{h(x)}{c((m))}}\\ c(m)=2ln(m-1)+\zeta-2\ \frac{m-1}{m}\\ p(x,m)=2c((m))h(x)c(m)=2ln(m1)+ζ2 mm1
(m为样本个数,为欧拉常数)

RF随机森林总结

• RF的主要优点:

• 1. 训练可以并行化,对于大规模样本的训练具有速度的优势;

• 2. 由于进行随机选择决策树划分特征列表,这样在样本维度比较高的时候,仍然具有比较高的训练性能;

• 3. 给以给出各个特征的重要性列表;

• 4. 由于存在随机抽样,训练出来的模型方差小,泛化能力强,能够缓解过拟合的情况;

• 5. RF实现简单;

• 6. 对于部分特征的缺失不敏感。

• RF的主要缺点:

• 1. 在某些噪音比较大的特征上(数据特别异常情况),RF模型容易陷入过拟合;

• 2. 取值比较多的划分特征对RF的决策会产生更大的影响,从而有可能影响模型的效果。

RF scikit-learn相关参数

参数RandomForestClassifierRandomForestRegressor
criterion指定划分标准,默认为gini,不支持其它参数指定划分标准,可选”mse”和”mae”; 默认mse
loss不支持指定误差的计算方式,可选参数”linear”, “square”, “exponential”, 默认为”linear”;一般不用改动
n_estimators最大迭代次数,也就是最多允许的决策树的数目,值过小可能会导致欠拟合,值过大可能会导致过拟合,一般50~100比较适合,默认10
max_features给定在进行最佳特征划分的时候,选择多少个特征进行考虑;默认为auto;max_features=sqrt(n_features);一般不建议改动,具体参数见官网文档
max_depth给定树的深度,默认为None,表示一致扩展到叶子节点足够纯或者样本数小于min_samples_split
min_samples_split给定树构建过程中,叶子节点中最少样本数量,默认为2
min_samples_leaf给定每个叶子节点中,最少的样本数目是多少,默认为2
bootstrap是否进行有放回的重采样,默认为True

随机森林的思考

• 在随机森林的构建过程中,由于各棵树之间是没有关系的,相对独立的;在构建的过程中,构建第m棵子树的时候,不会考虑前面的m-1棵树。

• 思考:

• 如果在构建第m棵子树的时候,考虑到前m-1棵子树的结果,会不会对最终结果产生有益的影响?

• 各个决策树组成随机森林后,在形成最终结果的时候能不能给定一种既定的决策顺序呢?(也就是那颗子树先进行决策、那颗子树后进行决策)

'''
bagging 回归
'''
import numpy as np
import pandas as pd
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import r2_score

###数据
df = pd.DataFrame([[1, 10.56],
                   [2, 27],
                   [3, 39.1],
                   [4, 40.4],
                   [5, 58],
                   [6, 60.5],
                   [7, 79],
                   [8, 87],
                   [9, 90],
                   [10, 95]],
                  columns=['X', 'Y'])
# 用来存放弱分类器
M = []
# 若分类器数量
n_trees = 200

for i in range(n_trees): ##循环训练我们的弱学习器
    ###对样本进行有放回的抽样m次
    '''
      sample() 抽样
      n=None,  抽样数据的条数
      frac=None, 抽样的比例
      replace=False, 是否有放回抽样
      weights=None, 权重
      random_state=None, 随机数种子
      axis=None 维度
    '''
    temp = df.sample(frac=1.0, replace=True)
    model = DecisionTreeRegressor(max_depth=1)
    x = temp.iloc[:, :-1]
    y = temp.iloc[:, -1]
    model.fit(x, y)
    M.append(model)

X = df.iloc[:, :-1]
Y = df.iloc[:, -1]
model1 =  DecisionTreeRegressor(max_depth=1)
model1.fit(X, Y)
print(model1.predict(X))
print(model1.score(X, Y))
print('-' * 100)
res = np.zeros(df.shape[0])

for i in M:
    res += i.predict(X)
predict = res / n_trees

print(predict)
print("R2:", r2_score(Y, predict))
import numpy as np
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, f1_score
# "bagging 实现方法"

df = pd.DataFrame([[0, 1],
                   [1, 1],
                   [2, 1],
                   [3, -1],
                   [4, -1],
                   [5, -1],
                   [6, 1],
                   [7, 1],
                   [8, 1],
                   [9, -1]])

M = []
n_trees = 101

for i in range(n_trees):
    df.sample(frac=1.0, replace=True)
    x = df.iloc[:, :-1]
    y = df.iloc[:, -1]

    model = DecisionTreeClassifier(max_depth=1, splitter='random', max_features=1)
    model.fit(x, y)

    M.append(model)

x = df.iloc[:, :-1]
y = df.iloc[:, -1]
model1 = DecisionTreeClassifier(max_depth=1)
model1.fit(x, y)
y_hat1 = model1.predict(x)
print(y_hat1)
print(f1_score(y, y_hat1))
print(accuracy_score(y, y_hat1))

print("——" * 100)

res = np.zeros(df.shape[0])
for i in M:
    res += i.predict(x)
print(res)
y_hat = np.sign(res)
print(f1_score(y, y_hat))
print(accuracy_score(y, y_hat))
"随机森林"
import joblib
import pandas as pd
from sklearn.impute import SimpleImputer
import numpy as np
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.pipeline import  Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier
import joblib

pd.set_option("display.max_columns", None)

# 获取数据
df = pd.read_csv('D:/save/datas/risk_factors_cervical_cancer.csv', sep=',')
names = df.columns
# print(df.info())

# 数据清洗
df.replace('?', np.nan, inplace=True)
impt = SimpleImputer()
df = impt.fit_transform(df)
df = pd.DataFrame(df, columns=names)

# 数据划分
X = df.iloc[:, :-4]
Y = df.iloc[:, -4:]

x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=22)

# 模型构建
models = [Pipeline([('standatscale', StandardScaler()),
                    ('pca', PCA()),
                    ('RF', RandomForestClassifier())]),
          Pipeline([('pca', PCA(n_components=0.5)),
                    ('RF', RandomForestClassifier(n_estimators=100, max_depth=5))])]
###设置参数
params = {'pca__n_componts':[0.5, 0.6, 0.7, 0.8, 0.9],
          'RF__n_estimators':[50,100,150],
          'RF__max_depth':[1,3,5,7]}

# ##网格调参
# model = GridSearchCV(estimator=models[1], param_grid=params, cv=5)
# ##训练
# model.fit(x_train, y_train)
# print('最优参数:', model.best_params_)
# print('最优模型:', model.best_estimator_)
# print('最优模型的分数:', model.best_score_)

model = models[1]
model.fit(x_train, y_train)
print(model.predict(x_test))
print(model.score(x_train, y_train))
print(model.score(x_test, y_test))

# 保村模型
joblib.dump(model, 'D:/save/model/risk01.m')

Boosting

• 提升学习(Boosting)是一种机器学习技术,可以用于回归分类的问题,它每一步产生弱预测模型(如决策树),并加权累加到总模型中;如果每一步的弱预测模型的生成都是依据损失函数的梯度方式的,那么就称为梯度提升(Gradient boosting);

• 提升技术的意义:如果一个问题存在弱预测模型,那么可以通过提升技术的办法得到一个强预测模型;

• 常见的模型有:

• Adaboost

• Gradient Boosting(GBT/GBDT/GBRT)

AdaBoost算法原理

• Adaptive Boosting是一种迭代算法。每轮迭代中会在训练集上产生一个新的学习器,然后使用该学习器对所有训练样本进行预测,以评估每个样本的重要性(Informative)。换句话来讲就是,算法/子模型会为每个样本赋予一个权重,每次用训练好的学习器标注/预测各个样本(训练数据),如果某个样本点被预测的越正确,则将样本权重降低;否则提高样****本的权重。权重越高的样本在下一个迭代训练中所占的权重就越大,也 就是说越难区分的样本在训练过程中会变得越重要;

• 整个迭代过程直到错误率足够小或者达到一定的迭代次数为止。

Adaboost算法

• Adaboost算法将基分类器的线性组合作为强分类器,同时给分类误差率较小的基本分类器以大的权值,给分类误差率较大的基分类器以小的权重值;构建的线性组合为:
f ( X ) = ∑ m = 1 M α m G m ( x ) f(X)= \sum^M_{m=1}\alpha_mG_m(x) f(X)=m=1MαmGm(x)
• 最终分类器是在线性组合的基础上进行Sign函数转换:
G ( x ) = s i g n ( f ( x ) ) = s i g n [ ∑ m = 1 M α m G m ( x ) ] G(x)=sign(f(x))=sign[\sum^M_{m=1}\alpha_mG_m(x)] G(x)=sign(f(x))=sign[m=1MαmGm(x)]
• 损失函数(以错误率作为损失函数):
∑ i = 1 n w i = 1 l o s s = ∑ i = 1 n w i I ( G ( x i ) ≠ y i ) \sum^n_{i=1}w_i=1 \quad\quad loss=\sum^n_{i=1}w_iI(G(x_i)\neq y_i) i=1nwi=1loss=i=1nwiI(G(xi)=yi)
• 损失函数(上界):
l o s s = ∑ i = 1 n w i I ( G ( x i ) ≠ y i ) ≤ ∑ i = 1 n w i e − y i f ( x ) loss=\sum^n_{i=1}w_iI(G(x_i)\neq y_i)\leq\sum^n_{i=1}w_ie^{-y_if(x)} loss=i=1nwiI(G(xi)=yi)i=1nwieyif(x)
• 第k-1轮的强学习器:
f k − 1 ( x ) = ∑ j = 1 k − 1 α j G j ( x ) f_{k-1}(x)=\sum^{k-1}_{j=1}\alpha_jG_j(x) fk1(x)=j=1k1αjGj(x)
• 第k轮的强学习器:
f k ( x ) = ∑ j = 1 k α j G j ( x ) f k ( x ) = f k − 1 + α k G k ( x ) f_k(x)=\sum^k_{j=1}\alpha_jG_j(x) \quad\quad f_k(x)=f_{k-1}+\alpha_kG_k(x) fk(x)=j=1kαjGj(x)fk(x)=fk1+αkGk(x)
• 损失函数:
l o s s ( α m , G m ( x ) ) = ∑ i = 1 n w 1 , i e − y i ( f m − 1 ( x ) + α m G m ( x ) loss(\alpha_m,G_m(x))=\sum^n_{i=1}w_{1,i}e^{-y_i(f_{m-1}(x)+\alpha _mG_m(x)} loss(αm,Gm(x))=i=1nw1,ieyi(fm1(x)+αmGm(x)
• 使下列公式达到最小值的αm和Gm就是AdaBoost算法的最终解
l o s s ( α m , G m ( x ) ) = ∑ i = 1 n w ‾ m i e − y i α m G m ( x ) loss(\alpha_m,G_m(x))=\sum^n_{i=1}\overline{w}_{mi}e^{-y_i\alpha_mG_m(x)} loss(αm,Gm(x))=i=1nwmieyiαmGm(x)
• G这个分类器在训练的过程中,是为了让误差率最小,所以可以认为G越好其实就是误差率越小。
G m ∗ ( x ) = m i n G ( x ) ∑ i = 1 n w m i ‾ I ( y i ≠ G m m ( x i ) ) ε m = ∑ y i ≠ G m ( x i ) w m i ‾ G^*_m(x)=min_{G(x)}\sum^n_{i=1}\overline{w_{mi}}I(y_i\neq G_mm(x_i))\\ \varepsilon_m=\sum_{y_i\neq G_m(x_i)}\overline{w_{mi}} Gm(x)=minG(x)i=1nwmiI(yi=Gmm(xi))εm=yi=Gm(xi)wmi
• 对于αm而言,通过求导然后令导数为零,可以得到公式(log对象可以以e为底也可以以2为底):
$$

$$

a m ∗ = 1 2 l n ( 1 − ε m ε m ) a^*_m=\frac{1}{2}ln(\frac{1-\varepsilon_m}{\varepsilon_m}) am=21ln(εm1εm)

Adaboost算法构建过程一

• 1. 假设训练数据集T={(X1 ,Y1 ),(X2 ,Y2 )…(Xn,Yn)}

• 2. 初始化训练数据权重分布
D 1 = ( w 11 , w 12 , . . . , w 1 n ) , w 1 i = 1 n , i = 1 , 2 , . . , n D_1=(w_{11}, w_{12}, ... ,w_{1n}), \quad\quad w_{1i}=\frac{1}{n}, i=1,2,..,n D1=(w11,w12,...,w1n),w1i=n1,i=1,2,..,n
• 3. 使用具有权值分布Dm的训练数据集学习,得到基本分类器

• 4. 计算Gm(x)在训练集上的分类误差

• 5. 计算Gm(x)模型的权重系数αm

• 6. 权重训练数据集的权值分布
D m = 1 = ( w m + 1 , 1 , w m + 1 , 2 , . . . , w m + 1 , n ) , w m + 1 , i = w m , i Z m e − α m y i G m ( x i ) , i = 1 , 2 , . . , n D_{m=1}=(w_{m+1,1}, w_{m+1,2}, ... ,w_{m+1,n}), \quad\quad w_{m+1,i}=\frac{w_{m,i}}{Z_m}e^{- \alpha_m y_iG_m(x_i)}, i=1,2,..,n Dm=1=(wm+1,1,wm+1,2,...,wm+1,n),wm+1,i=Zmwm,ieαmyiGm(xi),i=1,2,..,n
• 7. 这里Zm是规范化因子(归一化)
Z = ∑ i = 1 n w m , i e − α m y i G m ( x i ) Z_=\sum_{i=1}^nw_{m,i}e^{-\alpha_my_iG_m(x_i)} Z=i=1nwm,ieαmyiGm(xi)
• 8. 构建基本分类器的线性组合

• 9. 得到最终分类器

import numpy as np
import pandas as pd
from sklearn.tree import DecisionTreeClassifier

df = pd.DataFrame([[0, 1],
                   [1, 1],
                   [2, 1],
                   [3, -1],
                   [4, -1],
                   [5, -1],
                   [6, 1],
                   [7, 1],
                   [8, 1],
                   [9, -1]])


X = df.iloc[:, :-1]
Y = df.iloc[:, -1]

###第一个弱学习器
## 初始化样本权重
w1 = np.ones(shape=df.shape[0]) / df.shape[0]
# print(w1)
##构造弱分类器G1
model1 = DecisionTreeClassifier(max_depth=1)
model1.fit(X, Y, sample_weight=w1)

# print(model1.predict(X))
e1 = np.sum(w1[model1.predict(X) != Y])
# print([model1.predict(X) != Y])
# print(e1)

a1 = 1/2 * np.log((1 - e1) / e1)
# print(a1)

y_hat = np.sign(a1 * model1.predict(X))
print(y_hat)

print("-" * 50)

w2 = w1 * np.exp(-a1 * Y * model1.predict(X))
# print(w2)
w2 = np.array(w2 / sum(w2))

model2 = DecisionTreeClassifier(max_depth=1)
model2.fit(X, Y, sample_weight=w2)

# print(model2.predict(X))

e2 = sum(w2[model2.predict(X) != Y])
a2 = 1/2 * np.log((1 - e2) / e2)
# print(a2)

y_hat = np.sign(a1 * model1.predict(X) + a2 * model2.predict(X))
print(y_hat)

print("-" * 50)

w3 = w2 * np.exp(-a2 * Y * model2.predict(X))
w3 = np.array(w3 / sum(w3))


model3 = DecisionTreeClassifier(max_depth=1)

model3.fit(X, Y, sample_weight=w3)
e3 = sum(w3[model3.predict(X) != Y])

a3 = 1/2 * np.log((1 - e3) / e3)
y_hat = np.sign(a1 * model1.predict(X) + a2 * model2.predict(X) + a3 * model3.predict(X))
print(y_hat)
参数AdaBoostClassifierAdaBoostRegressor
algorithmSAMME和SAMME.R;SAMME表示构建过程中使用样本集分类效果作为弱分类器的权重;SAMME.R使用对样本集分类的预测概率大小作为弱分类器的权重。由于SAMME.R使用了连续的概率度量值,所以一般迭代比SAMME快,默认参数为SAMME.R;强调:使用SAMME.R必须要求base_estimator指定的弱分类器模型必须支持概率预测,即具有predict_proba方法。不支持
loss不支持指定误差的计算方式,可选参数”linear”, “square”, “exponential”, 默认为”linear”;一般不用改动
n_estimators最大迭代次数,值过小可能会导致欠拟合,值过大可能会导致过拟合,一般50~100比较适合,默认50
learning_rate指定每个弱分类器的权重缩减系数v,默认为1;一般从一个比较小的值开始进行调参;该值越小表示需要更多的弱分类器

AdaBoost总结

• AdaBoost的优点如下:

• 可以处理连续值和离散值;

• 模型的鲁棒性比较强;

• 解释强,结构简单。

• AdaBoost的缺点如下:

• 对异常样本敏感,异常样本可能会在迭代过程中获得较高的权重值,最终影响模型效果。

梯度提升迭代决策树GBDT

• GBDT也是Boosting算法的一种,但是和AdaBoost算法不同;区别如下:AdaBoost算法是利用前一轮的弱学习器的误差来更新样本权重值,然后一轮一轮的迭代;GBDT也是迭代,但是GBDT要求弱学习器必须是回归CART模型,而且GBDT在模型训练的时候,是要求模型预测的样本损失尽可能的小。

• 备注:所有GBDT算法中,底层都是回归树。

• 别名:GBT(Gradient Boosting Tree)、GTB(Gradient Tree Boosting)、GBRT(Gradient Boosting Regression Tree)、GBDT(Gradient Boosting DecisonTree)、MART(Multiple Additive Regression Tree)

• 当给定步长时候,给定一个步长step,在构建下一棵树的时候使用step*残差值作为输入值,这种方式可以减少过拟合的发生

梯度提升迭代决策树GBDT

• GBDT由三部分构成:DT(Regression Decistion Tree)、GB(GradientBoosting)和Shrinkage(衰减)

• 由多棵决策树组成,所有树的结果累加起来就是最终结果

• 迭代决策树和随机森林的区别:

• 随机森林使用抽取不同的样本构建不同的子树,也就是说第m棵树的构建和前m-1棵树的结果是没有关系的

• 迭代决策树在构建子树的时候,使用之前子树构建结果后形成的残差作为输入数据构建下一个子树;然后最终预测的时候按照子树构建的顺序进行预测,并将预测结果相加

GBDT算法原理

• 给定输入向量X和输出变量Y组成的若干训练样本(X1 ,Y1 ),(X2 ,Y2 )…(Xn,Yn),目标是找到近似函数F(X),使得损失函数L(Y,F(X))的损失值最小。

• 损失函数一般采用最小二乘损失函数或者绝对值损失函数。
L ( y , F ( x ) ) = 1 2 ( y − F ( x ) ) 2 L ( y , F ( x ) ) = ∣ y − F ( x ) ∣ L(y,F(x))=\frac{1}{2}(y-F(x))^2 \quad L(y,F(x))=|y-F(x)| L(y,F(x))=21(yF(x))2L(y,F(x))=yF(x)
• 最优解为
F ∗ ( X ) = a r g m i n F L ( y , F ( X ) ) F^*(X)=argmin_FL(y,F(X)) F(X)=argminFL(y,F(X))
• 以贪心算法的思想扩展得到Fm(X),求解最优f
F m ( x ) = F x − 1 + a r g m i n f ∑ i = 1 n L ( y i , F m − 1 ( X i ) + f m ( X i ) ) F_m(x)=F_{x-1}+argmin_f\sum_{i=1}^nL(y_i,F_{m-1}(X_i)+f_m(X_i)) Fm(x)=Fx1+argminfi=1nL(yi,Fm1(Xi)+fm(Xi))

• 以贪心法在每次选择最优基函数f时仍然困难,使用梯度下降的方法近似计算

• 给定常数函数F0 (X)
F o ( X ) = a r g m i n c ∑ i = 1 n L ( y i , c ) F_o(X)=argmin_c\sum_{i=1}^nL(y_i,c) Fo(X)=argminci=1nL(yi,c)
• 计算损失函数的负梯度值
y i m = − [ ∂ L ( y i , F ( x i ) ) ∂ F ( x i ) ] F ( x ) = F m − 1 ( x ) y_{im}=-[\frac{\partial L(y_i,F(x_i))}{\partial F(x_i)}]_{F(x)=F_{m-1}(x)} yim=[F(xi)L(yi,F(xi))]F(x)=Fm1(x)
• 使用数据(x**i , y**im ) (i=1……n )计算拟合残差找到一个CART回归树,得到第m棵树
c m j = a r g m i n c ∑ x i ∈ l e a f i L ( y i m , c ) f m ( x ) = ∑ ∣ l e a d ∣ m c m j I ( x ∈ l e a f m j ) c_{mj}=argmin_c\sum_{x_i \in leaf_i}L(y_{im},c) \quad f_m(x)=\sum^{|lead|_m}c_{mj}I(x \in leaf_{mj}) cmj=argmincxileafiL(yim,c)fm(x)=leadmcmjI(xleafmj)
• 更新模型
F m ( x ) = F m − 1 ( x ) + ∑ ∣ l e a d ∣ m c m j I ( x ∈ l e a f m j ) F ( x ) = F 0 ( x ) + ∑ m = 1 M ∑ j = 1 ∣ l e a f ∣ m c m j I ( x ∈ l e a f m j ) F_m(x)=F_{m-1}(x)+\sum^{|lead|_m}c_{mj}I(x \in leaf_{mj})\\ F(x) = F_0(x) + \sum_{m=1}^M \sum_{j=1}^{|leaf|_m}c_{mj}I(x \in leaf_{mj}) Fm(x)=Fm1(x)+leadmcmjI(xleafmj)F(x)=F0(x)+m=1Mj=1leafmcmjI(xleafmj)

GBDT回归算法和分类算法的区别

• 两者唯一的区别就是选择不同的损失函数、以及对应的负梯度值和模型初值采用不一样的值。

• 回归算法选择的损失函数一般是均方差(最小二乘)和绝对值误差,分类算法中一般选择对数损失函数来表示。

GBDT总结

• GBDT的优点如下:

• 可以处理连续值和离散值;

• 在相对少的调参情况下,模型的预测效果也会不错;

• 模型的鲁棒性比较强。

• GBDT的缺点如下:

• 由于弱学习器之间存在关联关系,难以并行训练模型。也就是模型训练的速度慢。

参数GradientBoostingClassifierGradientBoostingRegressor
alpha不支持当使用huber或者quantile损失函数的时候,需要给定分位数的值,默认为0.9;如果噪音数据比较多,可以适当的降低该参数值
loss给定损失函数,可选对数似然函数deviance和指数损失函数exponential;默认为deviance;不建议修改给定损失函数,可选均方差ls、绝对损失lad、Huber损失huber、分位数损失quantile;默认ls;一般采用默认;如果噪音数据比较多,推荐huber;如果是分段预测,推荐quantile
n_estimators最大迭代次数,值过小可能会导致欠拟合,值过大可能会导致过拟合,一般50~100比较适合,默认50
learning_rate指定每个弱分类器的权重缩减系数v,默认为1;一般从一个比较小的值开始进行调参;该值越小表示需要更多的弱分类器
subsample给定训练模型的时候,进行子采样的比例值,取值范围(0,1], 默认为1,表示不采用子采样;给值小于1表示采用部分数据进行模型训练,可以降低模型的过拟合情况;推荐[0.5,0.8];采样采用的方式是不放回采样
init给定初始化的模型,可以不给定

Bagging、Boosting的区别

• 样本选择:Bagging算法是有放回的随机采样;Boosting算法是每一轮训练集不变,只是训练集中的每个样例在分类器中的权重发生变化或者目标属性y发生变化,而权重&y值都是根据上一轮的预测结果进行调整;

• 样例权重:Bagging使用随机抽样,样例是等权重;Boosting根据错误率不断的调整样例的权重值,错误率越大则权重越大(Adaboost);

• 预测函数:Bagging所有预测模型的权重相等;Boosting算法对于误差小的分类器具有更大的权重(Adaboost)。

• 并行计算:Bagging算法可以并行生成各个基模型;Boosting理论上只能顺序生产,因为后一个模型需要前一个模型的结果;

Bagging是减少模型的variance(方差);Boosting是减少模型的Bias(偏度)

• Bagging里每个分类模型都是强分类器,因为降低的是方差,方差过高需要降低是过拟合;Boosting里每个分类模型都是弱分类器,因为降低的是偏度,偏度过高是欠拟合。

Stacking

Stacking概述

• Stacking(有时候也称之为stacked generalization)是指训练一个模型用于组合(combine)其他各个模型。即首先我们先训练多个不同的模型,然后再以之前训练的各个模型的输出为输入来训练一个模型,以得到一个最终的输出。

• 如果可以选用任意一个组合算法,那么理论上,Stacking可以表示前面提到的各种Ensemble方法。然而,实际中,我们通常使用单层logistic回归作为组合模型。

• 注意:Stacking有两层,一层是不同的基学习器(classifiers/regressors),第二个是用于组合基学习器的元学习器(meta_classifier/meta_regressor)

Stacking原理讲解

用XGBoost作为基础模型Model1,5折交叉验证就是先拿出四折作为training learn,另外一折作为testing predict)。注意:在stacking中此部分数据会用到整个traing data。如:假设我们整个training data包含10000行数据,testing data包含2500行数据,那么每一次交叉验证其实就是对training data进行划分,在每一次的交叉验证中training learn将会是8000行,testing predict是2000行。

• 每一次的交叉验证包含两个过程,1. 基于training learn训练模型;2. 基于training learn训练生成的模型对testing predict进行预测。在整个第一次的交叉验证完成之后我们将会得到关于当前testing predict的预测值,这将会是一个一维2000行的数据,记为a1。注意!在这部分操作完成后,我们还要对数据集原来的整个testing data进行预测,这个过程会生成2500个预测值,这部分预测值将会作为下一层模型testingdata的一部分,记为b1。因为我们进行的是5折交叉验证,所以以上提及的过程将会进行五次,最终会生成针对testing data数据预测的5列2000行的数据a1,a2,a3,a4,a5,对testing set的预测会是5列2500行数据b1,b2,b3,b4,b5。

• 在完成对Model1的整个步骤之后,我们可以发现a1,a2,a3,a4,a5其实就是对原来整个training data的预测值,将他们拼凑起来,会形成一个10000行一列的矩阵,记为A1。而对于b1,b2,b3,b4,b5这部分数据,我们将各部分相加取平均值,得到一个2500行一列的矩阵,记为B1。

• 以上就是stacking中一个模型的完整流程,stacking中同一层通常包含多个模型,假设还有Model2: LR,Model3:RF,Model4: GBDT,Model5:SVM,对于这四个模型,我们可以重复以上的步骤,在整个流程结束之后,我们可以得到新的A2,A3,A4,A5,B2,B3,B4,B5矩阵。

• 在此之后,我们把A1,A2,A3,A4,A5并列合并得到一个10000行五列的矩阵作为新的training data,B1,B2,B3,B4,B5并列合并得到一个2500行五列的矩阵作为新的testing data。让下一层的模型(元学习器),基于他们进一步训练。


import time
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')
matplotlib.rcParams['font.sans-serif'] = [u'simHei']

import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier, RandomForestClassifier
from sklearn.pipeline import Pipeline


from mlxtend.classifier import StackingClassifier
from mlxtend.feature_selection import ColumnSelector

# 获取数据
df = pd.read_csv('D:/save/datas/iris.data', header=None, names=['A1', 'A2', 'A3', 'A4', 'Y'], sep=',')
# print(df.info())

# 数据清洗

# 数据划分
X = df.iloc[:, :-1]
Y = df.iloc[:, -1]

lab = LabelEncoder()
Y = lab.fit_transform(Y)
# print(Y)

x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=22)

# 构建模型
# 基学习器
knn = KNeighborsClassifier(n_neighbors=7)
'''
正则化强度较强( C=0.1 )。
使用 lbfgs 作为优化算法,适合大数据集和多分类问题。
处理多分类问题时,使用多项式逻辑回归(Softmax回归)。
不包括截距项( fit_intercept=False )。
'''
softmax = LogisticRegression(C=0.1, solver='lbfgs', multi_class='multinomial', fit_intercept=False)
gbdt = GradientBoostingClassifier(learning_rate=0.1, max_depth=3, n_estimators=100)
rf = RandomForestClassifier(max_depth=5, n_estimators=150)

# 元学习器
lr = LogisticRegression(C=0.1, solver='lbfgs', multi_class='multinomial')

### stacking学习器
'''
1、最基本的使用方法,用前面基学习器的输出作为元学习器的输入
2、使用基学习器的输出类别的概率值作为元学习器输入,use_probas=True,若average_probas=True,那么这些基分类器对每一个类别产生的概率进行平均,否者直接拼接
 classifier1  = [0.2,0.5,0.3]
 classifier2  = [0.3,0.3,0.4]
  average_probas=True: [0.25,0.4,0.35]
  average_probas=Flase: [0.2,0.5,0.3,0.3,0.3,0.4]

3、对训练集中的特征维度进行操作,每次训练不同的基学习器的时候用不同的特征,比如我再训练KNN的时候只用前两个特征,训练RF的时候用其他的几个特征
    通过pipline来实现
'''
'''
classifiers, 基学习器
meta_classifier, 元学习器
use_probas=False, 
drop_last_proba=False,
average_probas=False, 
verbose=0,
use_features_in_secondary=False,
store_train_meta_features=False,
use_clones=True
'''
stacking1 = StackingClassifier(classifiers=[knn, softmax, gbdt, rf],
                               meta_classifier= lr)

stacking2 = StackingClassifier(classifiers=[knn, softmax, gbdt, rf],
                               meta_classifier=lr,
                               use_probas=True,
                               average_probas=False)

pipe_knn = Pipeline([('x', ColumnSelector([0, 1])),
                     ('knn', knn)])
pipe_softmax = Pipeline([('x', ColumnSelector([2, 3])),
                         ('softmax', softmax)])
pipe_rf = Pipeline([('x', ColumnSelector([0, 3])),
                    ('rf', rf)])
pipe_gbdt = Pipeline([('x', ColumnSelector([1, 2])),
                      ('gbdt', gbdt)])

stacking3 = StackingClassifier(classifiers=[pipe_knn, pipe_softmax, pipe_rf, pipe_gbdt], meta_classifier=lr)

# 模型训练与比较
models = []
times = []
train_scores = []
test_scores = []

for clf, modelname in zip([knn, softmax, gbdt, rf, stacking1, stacking2, stacking3],
                          ['knn', 'softmax', 'gdbt', 'rf',  'stacking1', 'stacking2', 'stacking3']):
    start = time.time()
    clf.fit(x_train, y_train)
    end = time.time()
    print('{}耗时:{}'.format(modelname, end - start))
    score_train = clf.score(x_train, y_train)
    score_test = clf.score(x_test, y_test)
    train_scores.append(score_train)
    test_scores.append(score_test)
    times.append(end - start)
    models.append(modelname)

print('score_train:', train_scores)
print('score_test:', test_scores)
print('models:', models)

print('-----------开始画图-----------')
plt.figure(num=1)
plt.plot([0, 1, 2, 3, 4, 5, 6], train_scores, 'r', label=u'训练集')
plt.plot([0, 1, 2, 3, 4, 5, 6], test_scores, 'b', label=u'测试集')
plt.title(u'鸢尾花数据不同分类器准确率比较', fontsize=16)
plt.xticks([0, 1, 2, 3, 4, 5, 6], models, rotation=0)
plt.legend(loc='lower left')
plt.figure(num=2)
plt.plot([0, 1, 2, 3, 4, 5, 6], times)
plt.title(u'鸢尾花数据不同分类器训练时间比较', fontsize=16)
plt.xticks([0, 1, 2, 3, 4, 5, 6], models, rotation=0)
plt.legend(loc='lower left')
plt.show()
import time

import pandas as pd
import numpy as np

from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.neighbors import KNeighborsRegressor
from sklearn.ensemble import RandomForestRegressor

from sklearn.svm import SVR

from mlxtend.regressor import StackingRegressor

import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')
matplotlib.rcParams['font.sans-serif'] = [u'simHei']


# 获取数据
df = pd.read_csv('D:/save/datas/household_power_consumption_1000_2.txt', sep=';')
# print(df.info())
# print(df.describe())

# 数据清洗
df.replace('?', np.nan, inplace=True)
# how='any': 指定只要行中有任何一个元素是缺失值,就将整行删除。
df = df.dropna(axis=0, how='any')

# 特征值,目标值
X = df.iloc[:, :2]
Y = df.iloc[:, 4].astype(np.float64)

def date_fromat(dt):
    date_str = time.strptime(' '.join(dt), '%d/%m/%Y %H:%M:%S')
    return [date_str.tm_year, date_str.tm_mon, date_str.tm_mday, date_str.tm_hour, date_str.tm_min, date_str.tm_sec]

X = X.apply(lambda row: pd.Series(date_fromat(row)), axis=1)
# print(X.shape)

# 训练数据集划分
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=22)
# print(x_train.shape)
# print(y_train.shape)

# 构建模型
# 基学习器
linear = LinearRegression(fit_intercept=True)
ridge = Ridge(alpha=0.1)
knn = KNeighborsRegressor(weights='distance')
rf = RandomForestRegressor(max_depth=3, n_estimators=100)

# 元学习器
svr_rbf = SVR(kernel='rbf', gamma=0.1, C=0.1)

stacking = StackingRegressor(regressors=[linear, ridge, knn, rf], meta_regressor=svr_rbf)


# params = {'linearregression__fit_intercept': [True,False],
#           'ridge__alpha': [0.01,0.1, 1.0, 10.0],
#           'kneighborsregressor__n_neighbors':[1,3,5,7,9],
#           'randomforestregressor__n_estimators':[50,100,150],
#           'randomforestregressor__max_depth':[1,3,5,7,9],
#           'meta_regressor__C': [0.1, 1.0, 10.0],
#           'meta_regressor__gamma': [0.1, 1.0, 10.0]}
#
# grid = GridSearchCV(estimator= stacking,
#                     param_grid= params,
#                     cv=5,
#                     refit=True)
#
# # print(grid.get_params().keys())
#
# grid.fit(x_train, y_train)
# print("最优参数:{}".format(grid.best_params_))

# 训练
stacking.fit(x_train, y_train)
print(stacking.score(x_train, y_train))
print(stacking.score(x_test, y_test))

# 画图
y_hat = stacking.predict(x_test)
plt.plot(range(len(x_test)), y_test, label=u'真实值')
plt.plot(range(len(x_test)), y_hat, label=u'预测值')
plt.legend()
plt.title('时间和电压回归预测')
plt.show()
  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值