机器学习与集成学习学习记录

集成学习上

第一章:机器学习基础

1 机器学习的三大主要任务

2 回归问题

3 基本的回归模型

4 使用sklearn构建完整的分类项目

5 模型性能评估和调参

第一章:机器学习基础
1 机器学习的三大主要任务
机器学习的一个重要的目标就是利用数学模型来理解数据,发现数据中的规律,用作数据的分析和预测。数据通常由一组向量组成,这组向量中的每个向量都是一个样本,我们用 𝑥𝑖 来表示一个样本,其中 𝑖=1,2,3,…,𝑁i=1,2,3,…,N ,共N个样本,每个样本 𝑥𝑖=(𝑥𝑖1,𝑥𝑖2,…,𝑥𝑖𝑝,𝑦𝑖),共p+1个维度,前p个维度的每个维度我们称为一个特征,最后一个维度 𝑦𝑖yi 我们称为因变量(响应变量)。特征用来描述影响因变量的因素,如:我们要探寻身高是否会影响体重的关系的时候,身高就是一个特征,体重就是一个因变量。通常在一个数据表dataframe里面,一行表示一个样本 𝑥𝑖,一列表示一个特征。
根据数据是否有因变量,机器学习的任务可分为:有监督学习和无监督学习

有监督学习:给定某些特征去估计因变量,即因变量存在的时候,我们称这个机器学习任务为有监督学习。如:我们使用房间面积,房屋所在地区,环境等级等因素去预测某个地区的房价。
无监督学习:给定某些特征但不给定因变量,建模的目的是学习数据本身的结构和关系。如:我们给定某电商用户的基本信息和消费记录,通过观察数据中的哪些类型的用户彼此间的行为和属性类似,形成一个客群。

而根据因变量的是否连续,有监督学习又分为回归和分类

回归:因变量是连续型变量,如:房价,体重等。
分类:因变量是离散型变量,如:是否患癌症,西瓜是好瓜还是坏瓜等。

2 回归问题
2.1 线性回归模型
线性回归就是回归问题中的一种,线性回归假设目标值与特征之间线性相关,即满足一个多元一次方程。通过构建损失函数,来求解损失函数最小时的参数w :
假设:数据集 𝐷={(𝑥1,𝑦1),…,(𝑥𝑁,𝑦𝑁)} 𝑥𝑖∈𝑅𝑝,𝑦𝑖∈𝑅,𝑖=1,2,…,𝑁
𝑋=(𝑥1,𝑥2,…,𝑥𝑁)^𝑇,
𝑌=(𝑦1,𝑦2,…,𝑦𝑁)^𝑇,
假设X和Y之间存在线性关系,模型的具体形式为 𝑦̂ =f(w)=w^Tx
采用最小二乘估计估计w:
我们需要衡量真实值 𝑦𝑖 与线性回归模型的预测值 𝑤^𝑇𝑥𝑖 之间的差距,在这里我们和使用二范数的平方和L(w)来描述这种差距,即:
在这里插入图片描述
对w求导后得在这里插入图片描述
使用sklearn的线性回归实例

from sklearn import datasets
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
# 载入波士顿房价数据库
bostondata=datasets.load_boston()
X=bostondata.data
Y=bostondata.target

#划分测试集和训练集
x_train,x_test,y_train,y_test=train_test_split(X,Y,test_size=0.2)
LR=LinearRegression()
LR.fit(x_train,y_train)
print ('系数',LR.coef_)
print ('截距',LR.intercept_)
print ('测试集得分',LR.score(x_test,y_test))
print ('训练集得分',LR.score(x_train,y_train))
y_pred=LR.predict(x_test)
plt.scatter(y_test,y_pred,c='r',marker='o')
plt.scatter(y_test,y_test,c='g',marker='+')
plt.show()

2.2 线性回归的推广
在线性回归中,我们假设因变量与特征之间的关系是线性关系,这样的假设使得模型很简单,但是缺点也是显然的,那就是当数据存在非线性关系时,我们使用线性回归模型进行预测会导致预测性能极其低下,因为模型的形式本身是线性的,无法表达数据中的非线性关系。我们一个很自然的想法就是去推广线性回归模型,使得推广后的模型更能表达非线性的关系。
(a) 多项式回归:
为了体现因变量和特征的非线性关系,一个很自然而然的想法就是将标准的线性回归模型换成一个多项式函数:
在这里插入图片描述
对于多项式的阶数d不能取过大,一般不大于3或者4,因为d越大,多项式曲线就会越光滑,在X的边界处有异常的波动。

(b) 广义可加模型(GAM):
广义可加模型GAM实际上是线性模型推广至非线性模型的一个框架,在这个框架中,每一个变量都用一个非线性函数来代替,但是模型本身保持整体可加性。GAM模型不仅仅可以用在线性回归的推广,还可以将线性分类模型进行推广。具体的推广形式是:
在这里插入图片描述
求解回归问题中的GAM:
1 限定fj 为样条基函数时,很容易使用最小二乘法求得解析解。
2 限定 fj 为回归树函数时,使用gradient boost和back-fitting进行迭代求解。

GAM模型实例:

from pygam import LinearGAM
gam = LinearGAM().fit(boston_data[boston.feature_names], y)
gam.summary()

GAM模型的优点与不足:

优点:简单容易操作,能够很自然地推广线性回归模型至非线性模型,使得模型的预测精度有所上升;由于模型本身是可加的,因此GAM还是能像线性回归模型一样把其他因素控制不变的情况下单独对某个变量进行推断,极大地保留了线性回归的易于推断的性质。

缺点:GAM模型会经常忽略一些有意义的交互作用,比如某两个特征共同影响因变量,不过GAM还是能像线性回归一样加入交互项 𝑥(𝑖)×𝑥(𝑗)的形式进行建模;但是GAM模型本质上还是一个可加模型,如果我们能摆脱可加性模型形式,可能还会提升模型预测精度,详情请看后面的算法。
2.3 回归树
基于树的回归方法主要是依据分层和分割的方式将特征空间划分为一系列简单的区域。对某个给定的待预测的自变量,用他所属区域中训练集的平均数或者众数对其进行预测。由于划分特征空间的分裂规则可以用树的形式进行概括,因此这类方法称为决策树方法。决策树由结点(node)和有向边(diredcted edge)组成。结点有两种类型:内部结点(internal node)和叶结点(leaf node)。内部结点表示一个特征或属性,叶结点表示一个类别或者某个值。区域 𝑅1,𝑅2 等称为叶节点,将特征空间分开的点为内部节点

建立回归树的过程大致可以分为以下两步:
1 将自变量的特征空间(即 𝑥(1),𝑥(2),𝑥(3),…,𝑥(𝑝)的可能取值构成的集合分割成J个互不重叠的区域 𝑅1,𝑅2,…,𝑅𝑗 。
2 对落入区域 𝑅𝑗Rj 的每个观测值作相同的预测,预测值等于 𝑅𝑗Rj 上训练集的因变量的简单算术平均。

即采用的是启发式的方法。假如有n个特征,每个特征有 s i (i∈(1,n))个取值,那我们遍历所有特征,尝试该特征所有取值,对空间进行划分,直到取到特征j的取值s,使得损失函数最小,这样就得到了一个划分点。

回归树与线性模型的比较:
线性模型的模型形式与树模型的模型形式有着本质的区别,具体而言,线性回归对模型形式做了如下假定:在这里插入图片描述

而回归树则是
在这里插入图片描述

如果特征变量与因变量的关系能很好的用线性关系来表达,那么线性回归通常有着不错的预测效果,拟合效果则优于不能揭示线性结构的回归树。反之,如果特征变量与因变量的关系呈现高度复杂的非线性,那么树方法比传统方法更优。

树模型的优缺点:
树模型的解释性强,在解释性方面可能比线性回归还要方便。
树模型更接近人的决策方式。
树模型可以用图来表示,非专业人士也可以轻松解读。
树模型可以直接做定性的特征而不需要像线性回归一样哑元化。
树模型能很好处理缺失值和异常值,对异常值不敏感,但是这个对线性模型来说却是致命的。
树模型的预测准确性一般无法达到其他回归模型的水平,但是改进的方法很多。

回归树总体流程类似于分类树,分枝时穷举每一个特征的每一个阈值,来寻找最优切分特征j和最优切分点s,衡量的方法是平方误差最小化。分枝直到达到预设的终止条件(如叶子个数上限)就停止。
在这里插入图片描述

sklearn使用回归树的实例:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor
from sklearn import linear_model

# Data set
x = np.array(list(range(1, 11))).reshape(-1, 1)
y = np.array([5.56, 5.70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05]).ravel()

# Fit regression model
model1 = DecisionTreeRegressor(max_depth=1)
model2 = DecisionTreeRegressor(max_depth=3)
model3 = linear_model.LinearRegression()
model1.fit(x, y)
model2.fit(x, y)
model3.fit(x, y)

# Predict
X_test = np.arange(0.0, 10.0, 0.01)[:, np.newaxis]
# print(X_test)
y_1 = model1.predict(X_test)
print(y_1.shape)
y_2 = model2.predict(X_test)
y_3 = model3.predict(X_test)

# Plot the results
plt.figure()
plt.scatter(x, y, s=20, edgecolor="black",
            c="darkorange", label="data")
plt.plot(X_test, y_1, color="cornflowerblue",
         label="max_depth=1", linewidth=2)
plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=3", linewidth=2)
plt.plot(X_test, y_3, color='red', label='liner regression', linewidth=2)
plt.xlabel("data")
plt.ylabel("target")
plt.title("Decision Tree Regression")
plt.legend()
plt.show()

2.4支持向量机回归(SVR)
SVM是要使到超平面最近的样本点的“距离”最大;
SVR则是要使到超平面最远的样本点的“距离”最小。
在这里插入图片描述

在线性回归的理论中,每个样本点都要计算平方损失,但是SVR却是不一样的。SVR认为:落在 𝑓(𝑥) 的 𝜖 邻域空间中的样本点不需要计算损失,这些都是预测正确的,其余的落在 𝜖 邻域空间以外的样本才需要计算损失,因此SVR问题可形式化为:
在这里插入图片描述
引入松弛变量 ξ i 和 ( ξ i ),可将式重写为:
在这里插入图片描述
引入拉格朗日乘子 μi 和αi:
在这里插入图片描述
再对w,b, ξ i 和 ( ξ i )求导可得:
w=∑(1,m)(α^-α)x
0=∑(1,m)(α^-α)
C=α+μ
C=α^ +μ^
SVR的解形如:
在这里插入图片描述
若考虑特征映射形式,则:
f(x)=∑(1,m)(α^-α)k(x,x(i))+b
其中k(x,x(i))为核函数

sklearn中使用SVR实例:

from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler     # 标准化数据
from sklearn.pipeline import make_pipeline   # 使用管道,把预处理和模型形成一个流程

reg_svr = make_pipeline(StandardScaler(), SVR(C=1.0, epsilon=0.2))
reg_svr.fit(X, y)
reg_svr.score(X,y)

2.5 拉格朗日对偶问题
(1)构造拉格朗日函数
1 引入KKT乘子 μj ( μj ≥ 0),把不等式约束条件转化为等式约束条件。
2 引入拉格朗日乘子 λk ,把等式约束转化为无约束优化问题
在这里插入图片描述
(2)定义拉格朗日对偶函数为拉格朗日函数,并把 λ,μ当作常数,关于x取最小值,即:
在这里插入图片描述
(3)求满足原问题约束下的最优解f*
在这里插入图片描述
故g(λ,μ)是原问题最优解的下界,所有通过找到其最大的下界,从而确定拉格朗日对偶问题为:max g(λ,μ ),μ>=0
原问题的关于x的最小化转化为了对偶问题关于λ,μ的最大化。

3 基本的回归模型
3.1 训练均方误差与测试均方误差
在回归中,我们最常用的评价指标为均方误差,即: 在这里插入图片描述其中f(xi)是样本 xi 应用建立的模型f预测的结果。
但通常而言,当我们的模型的训练均方误差达到很小时,测试均方误差反而很大,这种现象即为过拟合。
3.2 偏差-方差的权衡:
我们的测试均方误差的期望值可以分解为f(x0)的方差、f(x0)的偏差平方和误差项𝜖的方差,即:
在这里插入图片描述
为了使得模型的测试均方误差达到最小值,也就是同时最小化偏差的平方和方差。由于我们知道偏差平方和方差本身是非负的,因此测试均方误差的期望不可能会低于误差的方差,因此我们称 Var(𝜖)为建模任务的难度,这个量在我们的任务确定后是无法改变的,也叫做不可约误差。

偏差度量的是单个模型的学习能力,而方差度量的是同一个模型在不同数据集上的稳定性。“偏差-方差分解”说明:泛化性能是由学习算法的能力、数据的充分性以及学习任务本身的难度所共同决定的。
给定学习任务,为了取得好的泛化性能,则需使偏差较小,即能够充分拟合数据,并且使方差较小,即使得数据扰动产生的影响小。
在这里插入图片描述
3.3 特征提取
对测试误差进行估计的方式有两种:训练误差修正与交叉验证
1 训练误差修正
先构造一个特征较多的模型使其过拟合,此时训练误差很小而测试误差很大,那这时我们加入关于特征个数的惩罚。因此,当我们的训练误差随着特征个数的增加而减少时,惩罚项因为特征数量的增加而增大,抑制了训练误差随着特征个数的增加而无休止地减小。其表达式为:
在这里插入图片描述
2 交叉验证
K折交叉验证:把训练样本分成K等分,然后用K-1个样本集当做训练集,剩下的一份样本集为验证集去估计由K-1个样本集得到的模型的精度,这个过程重复K次取平均值得到测试误差的一个估计:
在这里插入图片描述
其优点为能够给出测试误差的一个直接估计。

在测试误差能够被合理的估计出来以后,我们做特征选择的目标就是:从p个特征中选择m个特征,使得对应的模型的测试误差的估计最小。对应的方法有

1 最优子集选择
(1)记不含任何特征的模型为M0,计算这个M0的测试误差。
(2) 在M0基础上增加一个变量,计算p个模型的RSS,选择RSS最小的模型记作M1,并计算该模型M1的测试误差。
(3) 再增加变量,计算p-1个模型的RSS,并选择RSS最小的模型记作M2,并计算该模型M2的测试误差。
(4)重复以上过程直到拟合的模型有p个特征为止,并选择p+1个模型{M0,M1……Mp}中测试误差最小的模型作为最优模型。
随着数据特征维度p的增加,子集的数量为 2^p

2 向前逐步选择
(1)记不含任何特征的模型为M0,只用于估计各观测的样本均值,计算这个M0的测试误差。
(2) 在M0基础上增加一个变量,计算p个模型的RSS,选择RSS最小的模型记作M1,并计算该模型M1的测试误差。
(3) 在最小的RSS模型下继续增加一个变量,选择RSS最小的模型记作M2,并计算该模型M2的测试误差。
(4)重复以上过程直到拟合的模型有p个特征为止,并选择p+1个模型{M0,M1……Mp}中测试误差最小的模型作为最优模型。
随着数据特征维度p的增加,子集的数量为 1+p(p+1)/2
3.4 正则化
通过对回归的系数进行约束或者加罚的技巧对p个特征的模型进行拟合,显著降低模型方差。
(1)Ridge回归(L2正则化)

在这里插入图片描述

def gradientDescent_ridge(X,y,theta,iterations,alpha,lamda=0.02):
    c = np.ones(X.shape[0]).transpose()
    X = np.insert(X,0,values=c,axis=1)#对原始数据加入一个全为1的列
    m = X.shape[0]
    n = X.shape[1]
    costs = np.zeros(iterations)
    for num in range(iterations):
        for j in range(n):
            theta[j] = theta[j]+(alpha/m)*np.sum(y-np.dot(X,theta)*X[:,j].reshape(-1,1))-2*lamda*theta[j]
        costs[num] = computeCost(X,y,theta)
    return theta,costs

(2)LASSO回归(L1正则化)
在这里插入图片描述

def lasso_regression(X, y, iterations,lambd=0.2):
    m, n = X.shape
    theta = np.matrix(np.zeros((n, 1)))
    for it in range(iterations):
        for k in range(n):#n个特征
            # 计算常量值z_k和p_k
            z_k = np.sum(X[:, k]**2)
            p_k = 0
            for i in range(m):
                #开始,根据公式计算p_k
                p_k += X[i, k] * (y[i, 0] - np.sum([X[i, j] * theta[j, 0] for j in range(n) if j != k]))
                #结束
            #w_k是个临时变量,根据p_k的不同取值进行计算
            if p_k < -lambd/2:
                w_k = (p_k+lambd/2)/z_k
            elif p_k > lambd/2:
                w_k = (p_k-lambd/2)/z_k
            else:
                w_k = 0
            theta[k, 0] = w_k
    return theta

3.5 降维
降维就是指采用某种映射方法,将原高维空间中的数据点映射到低维度的空间中。降维的本质是学习一个映射函数 f : x->y,其中x是原始数据点的表达,目前最多使用向量表达形式。 y是数据点映射后的低维向量表达,通常y的维度小于x的维度。

主成分分析(PCA):
主成分分析的思想:通过最大投影方差将原始空间进行重构,即由特征相关重构为无关,即落在某个方向上的点(投影)的方差最大。
具体算法:
在这里插入图片描述
总结:方差的控制方式
(1)使用原始变量的子集
(2)将变量系数压缩至零(降维)
(3)将原始的特征空间投影到一个低维的空间实现变量的数量变少
3.6 模型超参数的调优**
参数与超参数:对于可以使用最小二乘法或者梯度下降法等最优化算法优化出来的数我们称为参数,类似于𝜆一样,我们无法使用最小二乘法或者梯度下降法等最优化算法优化出来的数我们称为超参数。
超参数的选择方法:
(1)网格搜索GridSearchCV:网格搜索的思想非常简单,比如你有2个超参数需要去选择,那你就把所有的超参数选择列出来分别做排列组合。举个例子:𝜆 = 0.01, 0.1, 1.0和𝛼 = 0.01, 0.1, 1.0,你可以做一个排列组合,即:{[0.01,0.01],[0.01,0.1],[0.01,1],[0.1,0.01],[0.1,0.1],[0.1,1.0],[1,0.01],[1,0.1],[1,1]} ,然后针对每组超参数分别建立一个模型,然后选择测试误差最小的那组超参数。

(2)随机搜索 RandomizedSearchCV:参数的随机搜索中的每个参数都是从可能的参数值的分布中采样的。与网格搜索相比,这有两个主要优点:可以独立于参数数量和可能的值来选择计算成本和添加不影响性能的参数不会降低效率。

SVR参数调优

from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler  # 由于SVR基于距离计算,引入对数据进行标准化
from sklearn.pipeline import make_pipeline  # 引入管道简化学习流程
from sklearn.model_selection import GridSearchCV  # 引入网格搜索调优
from sklearn.model_selection import cross_val_score  # 引入K折交叉验证
from sklearn import datasets
import numpy as np
import pandas as pd

boston = datasets.load_boston()
X = boston.data
y = boston.target
features = boston.feature_names
# data=pd.DataFrame(X,columns=features)
# print(data.head())
pipe_SVR = make_pipeline(StandardScaler(), SVR())
score1 = cross_val_score(estimator=pipe_SVR, X=X, y=y, scoring='r2', cv=10)
print("CV accuracy: %.3f +/- %.3f" % ((np.mean(score1)), np.std(score1)))

# 下面我们使用网格搜索来对SVR调参:
from sklearn.pipeline import Pipeline

pipe_svr = Pipeline([("StandardScaler", StandardScaler()),
                     ("svr", SVR())])
param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0]
param_grid = [{"svr__C": param_range, "svr__kernel": ["linear"]},  # 注意__是指两个下划线,一个下划线会报错的
              {"svr__C": param_range, "svr__gamma": param_range, "svr__kernel": ["rbf"]}]
gs = GridSearchCV(estimator=pipe_svr,
                  param_grid=param_grid,
                  scoring='r2',
                  cv=10)  # 10折交叉验证
gs = gs.fit(X, y)
print("网格搜索最优得分:", gs.best_score_)
print("网格搜索最优参数组合:\n", gs.best_params_)

# 下面我们使用随机搜索来对SVR调参:
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform  # 引入均匀分布设置参数

pipe_svr = Pipeline([("StandardScaler", StandardScaler()),
                     ("svr", SVR())])
distributions = dict(svr__C=uniform(loc=1.0, scale=4),  # 构建连续参数的分布
                     svr__kernel=["linear", "rbf"],  # 离散
                     svr__gamma=uniform(loc=0, scale=4))
rs = RandomizedSearchCV(estimator=pipe_svr,
                        param_distributions=distributions,
                        scoring='r2',
                        cv=10)  # 10折交叉验证
rs = rs.fit(X, y)
print("随机搜索最优得分:", rs.best_score_)
print("随机搜索最优参数组合:\n", rs.best_params_)

4 使用sklearn构建完整的分类项目
(1) 收集数据集并选择合适的特征

from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
feature = iris.feature_names
data = pd.DataFrame(X,columns=feature)
data['target'] = y
data.head()

(2) 选择度量模型性能的指标
真阳性TP:预测值和真实值都为正例;
真阴性TN:预测值与真实值都为正例;
假阳性FP:预测值为正,实际值为负;
假阴性FN:预测值为负,实际值为正;

分类模型的指标:
准确率:分类正确的样本数占总样本的比例 .
精度:预测为正且分类正确的样本占预测值为正的比例,即: PRE=TP/(TP+FP).
召回率:预测为正且分类正确的样本占类别为正的比例,即:REC=TP/(TP+FN) .
F1值:综合衡量精度和召回率,即: .F1=2PRE*REC/(PRE+REC)
ROC曲线:以假阳率为横轴,真阳率为纵轴画出来的曲线,曲线下方面积越大越好。

(3) 选择具体的模型并进行训练
1 逻辑回归
假设函数:在这里插入图片描述
代价函数:在这里插入图片描述
梯度下降法进行参数更新:
在这里插入图片描述
2 线性判别分析
降维分类的思想理解线性判别分析:
在这里插入图片描述
希望降维之后:类内方差小,类间方差大,即同一类之间数据应该更相似,不同类别之间数据应该不相似。
定义全样本投影均值𝑧¯ ,全样本投影协方差𝑆𝑧 ,c1样本投影均值𝑧1¯ ,c1样本投影协方差𝑆𝑧1 ,c2样本投影均值𝑧2¯ ,c2样本投影协方差𝑆𝑧2 ,类间差距:(𝑧¯1 − 𝑧¯2 )^2,类内方差:S1+S2,则损失函数为:在这里插入图片描述
对w求导后得:在这里插入图片描述

3 朴素贝叶斯
朴素贝叶斯算法对线性判别分析作进一步的模型简化,它将线性判别分析中的协方差矩阵中的协方差全部变成0,只保留各自特征的方差,也就是朴素贝叶斯假设各个特征之间是不相关的。在之前所看到的偏差-方差理论中,我们知道模型的简化可以带来方差的减少但是增加偏差,因此朴素贝叶斯也不例外,它比线性判别分析模型的方差小,偏差大。

#逻辑回归
from sklearn.linear_model import LogisticRegression
log_iris = LogisticRegression()
log_iris.fit(X,y)
log_iris.score(X,y)

#线性判别分析
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
lda_iris = LinearDiscriminantAnalysis()
lda_iris.fit(X,y)
lda_iris.score(X,y)

#朴素贝叶斯
from sklearn.naive_bayes import GaussianNB
NB_iris = GaussianNB()
NB_iris.fit(X, y)
NB_iris.score(X,y)

4 决策树
在回归树中,对一个给定的观测值,因变量的预测值取它所属的终端结点内训练集的平均因变量。与之相对应,对于分类树来说,给定一个观测值,因变量的预测值为它所属的终端结点内训练集的最常出现的类。

决策树构建步骤:
a. 选择最优切分特征j以及该特征上的最优点s:
遍历特征j以及固定j后遍历切分点s,选择使得基尼系数或者交叉熵最小的(j,s)
b. 按照(j,s)分裂特征空间,每个区域内的类别为该区域内样本比例最多的类别。
c. 继续调用步骤1,2直到满足停止条件,就是每个区域的样本数小于等于5。
d. 将特征空间划分为J个不同的区域,生成分类树。

决策树采用贪婪思想进行分裂,即选择可以得到最优分裂结果的属性进行分裂。选择分裂属性是要找出能够使所有叶子节点数据最纯的属性,决策树使用信息增益或者信息增益率作为选择属性的依据。 用信息增益表示分裂前后跟的数据复杂度和分裂节点数据复杂度的变化值,信息增益越大,分裂后的复杂度减小得越多,分类的效果越明显。节点的复杂度可以用交叉熵或者基尼指数衡量。

# 使用决策树算法对iris分类:
'''
criterion:{“gini”, “entropy”}, default=”gini”
max_depth:树的最大深度。
min_samples_split:拆分内部节点所需的最少样本数
min_samples_leaf :在叶节点处需要的最小样本数。
'''
from sklearn.tree import DecisionTreeClassifier
tree_iris = DecisionTreeClassifier(min_samples_leaf=5)
tree_iris.fit(X,y)
tree_iris.score(X,y)

5 模型性能评估和调参
1 用管道简化工作流

# 把所有的操作全部封在一个管道pipeline内形成一个工作流:
## 标准化+PCA+逻辑回归


### 方式1:make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline

pipe_lr1 = make_pipeline(StandardScaler(),PCA(n_components=2),LogisticRegression(random_state=1))
pipe_lr1.fit(X_train,y_train)
y_pred1 = pipe_lr.predict(X_test)
print("Test Accuracy: %.3f"% pipe_lr1.score(X_test,y_test))

2 使用k折交叉验证评估模型性能

# 评估方式1:k折交叉验证

from sklearn.model_selection import cross_val_score

scores1 = cross_val_score(estimator=pipe_lr,X = X_train,y = y_train,cv=10,n_jobs=1)
print("CV accuracy scores:%s" % scores1)
print("CV accuracy:%.3f +/-%.3f"%(np.mean(scores1),np.std(scores1)))

3 使用学习和验证曲线调试算法

# 用学习曲线诊断偏差与方差
from sklearn.model_selection import learning_curve

pipe_lr3 = make_pipeline(StandardScaler(),LogisticRegression(random_state=1,penalty='l2'))
train_sizes,train_scores,test_scores = learning_curve(estimator=pipe_lr3,X=X_train,y=y_train,train_sizes=np.linspace(0.1,1,10),cv=10,n_jobs=1)
train_mean = np.mean(train_scores,axis=1)
train_std = np.std(train_scores,axis=1)
test_mean = np.mean(test_scores,axis=1)
test_std = np.std(test_scores,axis=1)
plt.plot(train_sizes,train_mean,color='blue',marker='o',markersize=5,label='training accuracy')
plt.fill_between(train_sizes,train_mean+train_std,train_mean-train_std,alpha=0.15,color='blue')
plt.plot(train_sizes,test_mean,color='red',marker='s',markersize=5,label='validation accuracy')
plt.fill_between(train_sizes,test_mean+test_std,test_mean-test_std,alpha=0.15,color='red')
plt.xlabel("Number of training samples")
plt.ylabel("Accuracy")
plt.legend(loc='lower right')
plt.ylim([0.8,1.02])
plt.show()

4 通过网格搜索进行超参数调优

from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
import time

start_time = time.time()
pipe_svc = make_pipeline(StandardScaler(),SVC(random_state=1))
param_range = [0.0001,0.001,0.01,0.1,1.0,10.0,100.0,1000.0]
param_grid = [{'svc__C':param_range,'svc__kernel':['linear']},{'svc__C':param_range,'svc__gamma':param_range,'svc__kernel':['rbf']}]
gs = GridSearchCV(estimator=pipe_svc,param_grid=param_grid,scoring='accuracy',cv=10,n_jobs=-1)
gs = gs.fit(X_train,y_train)
end_time = time.time()
print("网格搜索经历时间:%.3f S" % float(end_time-start_time))
print(gs.best_score_)
print(gs.best_params_)

5 比较不同的性能评估指标

# 各种指标的计算
from sklearn.metrics import precision_score,recall_score,f1_score

print('Precision:%.3f'%precision_score(y_true=y_test,y_pred=y_pred))
print('recall_score:%.3f'%recall_score(y_true=y_test,y_pred=y_pred))
print('f1_score:%.3f'%f1_score(y_true=y_test,y_pred=y_pred))

一 XGBoost算法
1 定义:以CART决策树为子模型,通过Gradient Tree Boosting实现多棵CART树的集成学习,得到最终模型。
2 特点:本质仍为GBDT,在Gradient Boosting框架下实现,提供了并行树提升,且利用了核外计算,计算速度较快。
3 具体算法:
(1)构造目标函数
假设有K棵树,则第i个样本的输出为
在这里插入图片描述
因此目标函数为:
在这里插入图片描述
其中, ∑𝑙(𝑦̂ 𝑖,𝑦𝑖)为损失函数, ∑Ω(𝑓𝑘) 为正则化项。
(2)叠加式的训练
通过前K-1棵树的预测结果,目标函数可分解为:
在这里插入图片描述
由于正则化项也可以分解为前K-1棵树的复杂度加第K棵树的复杂度,因此:
在这里插入图片描述
由于 ∑Ω(𝑓𝑘) 在模型构建到第K棵树的时候已经固定,无法改变,因此是一个已知的常数,可以在最优化的时候省去,因此:
在这里插入图片描述
(3)使用泰勒级数近似目标函数:
在这里插入图片描述
由于 ∑𝑙(𝑦𝑖,𝑦̂ (𝐾−1))在模型构建到第K棵树的时候已经固定,无法改变,因此是一个已知的常数,可以在最优化的时候省去,故:
在这里插入图片描述
(4)定义一棵树
模型复杂度 Ω(𝑓𝐾)为:
在这里插入图片描述
因此目标函数为:
在这里插入图片描述
将目标函数化简为一个关于w的二次函数:
在这里插入图片描述
根据二次函数求极值的公式,求得:
在这里插入图片描述
(5)寻找树的形状
使用目标函数的变化来作为分裂节点的标准来寻找一棵树。
选择最优特征和最优切分点的方法:
1 精确贪心分裂算法:
首先找到所有的候选特征及所有的候选切分点, 求得其Lsplit ,然后选择 Lsplit 最大的特征及对应切分点作为最优特征和最优切分点。
2 基于直方图的近似算法:
当数据不能完全加载到内存时,精确贪心算法会变得非常低效。而基于直方图的近似算法的主要思想是:对某一特征寻找最优切分点时,首先对该特征的所有切分点按分位数 (如百分位) 分桶, 得到一个候选切分点集。特征的每一个切分点都可以分到对应的分桶; 然后,对每个桶计算特征统计G和H得到直方图, G为该桶内所有样本一阶特征统计g之和, H为该桶内所有样本二阶特征统计h之和; 最后,选择所有候选特征及候选切分点中对应桶的特征统计收益最大的作为最优特征及最优切分点。

二 XGBoost算法参数
XGBoost的参数分为三种:
1 通用参数:
booster:使用哪个弱学习器训练,默认gbtree,可选gbtree,gblinear 或dart
nthread:用于运行XGBoost的并行线程数,默认为最大可用线程数
verbosity:打印消息的详细程度。有效值为0(静默),1(警告),2(信息),3(调试)
其中Tree Booster的参数:
eta(learning_rate):learning_rate,在更新中使用步长收缩以防止过度拟合,默认= 0.3,范围:[0,1];典型值一般设置为:0.01-0.2。
gamma(min_split_loss):默认= 0,分裂节点时,损失函数减小值只有大于等于gamma节点才分裂,gamma值越大,算法越保守,越不容易过拟合,但性能就不一定能保证,需要平衡。范围:[0,∞]。
max_depth:默认= 6,一棵树的最大深度。增加此值将使模型更复杂,并且更可能过度拟合。范围:[0,∞]。
min_child_weight:默认值= 1,如果新分裂的节点的样本权重和小于min_child_weight则停止分裂 。这个可以用来减少过拟合,但是也不能太高,会导致欠拟合。范围:[0,∞]。
subsample:默认值= 1,构建每棵树对样本的采样率,如果设置成0.5,XGBoost会随机选择一半的样本作为训练集。范围:(0,1]。
colsample_bytree:默认= 1,列采样率,也就是特征采样率。范围为(0,1]。
lambda(reg_lambda):默认=1,L2正则化权重项。增加此值将使模型更加保守。
alpha(reg_alpha):默认= 0,权重的L1正则化项。增加此值将使模型更加保守。
tree_method:默认=auto,XGBoost中使用的树构建算法。auto:使用启发式选择最快的方法。
对于小型数据集,exact将使用精确贪婪()。对于较大的数据集,approx将选择近似算法()。
scale_pos_weight:控制正负权重的平衡,这对于不平衡的类别很有用。Kaggle竞赛一般设置sum(negative instances) / sum(positive instances),在类别高度不平衡的情况下,将参数设置大于0,可以加快收敛。
2 任务参数(这个参数用来控制理想的优化目标和每一步结果的度量方法)
objective:默认=reg:squarederror,表示最小平方误差。
eval_metric:验证数据的评估指标,将根据目标分配默认指标(回归均方根,分类误差,排名的平均平均精度),用户可以添加多个评估指标。
3 命令行参数

XGBoost的调参步骤:
1 确定学习速率和提升参数调优的初始值
2 max_depth 和 min_child_weight 参数调优
3 gamma参数调优
4 subsample 和 colsample_bytree 参数调优
5 正则化参数alpha调优
6 降低学习速率和使用更多的决策树

三 XGBoost具体使用:

import pandas as pd
import xgboost as xgb

df_wine = pd.read_csv('wine.data', header=None)
df_wine.columns = ['Class label', 'Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash', 'Magnesium', 'Total phenols',
                   'Flavanoids', 'Nonflavanoid phenols', 'Proanthocyanins', 'Color intensity', 'Hue',
                   'OD280/OD315 of diluted wines', 'Proline']
df_wine = df_wine[df_wine['Class label'] != 1]  # drop 1 class
y = df_wine['Class label'].values
X = df_wine[['Alcohol', 'OD280/OD315 of diluted wines']].values
from sklearn.model_selection import train_test_split  # 切分训练集与测试集
from sklearn.preprocessing import LabelEncoder  # 标签化分类变量

le = LabelEncoder()
y = le.fit_transform(y)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1, stratify=y)
dtrain = xgb.DMatrix(X_train, label=y_train)  # 保存DMatrix到XGBoost二进制文件中
dtest = xgb.DMatrix(X_test)
# 1.Booster 参数
params = {
    'booster': 'gbtree',
    'objective': 'multi:softmax',  # 多分类的问题
    'num_class': 10,  # 类别数,与 multisoftmax 并用
    'gamma': 0.1,  # 用于控制是否后剪枝的参数,越大越保守,一般0.1、0.2这样子。
    'max_depth': 12,  # 构建树的深度,越大越容易过拟合
    'lambda': 2,  # 控制模型复杂度的权重值的L2正则化项参数,参数越大,模型越不容易过拟合。
    'subsample': 0.7,  # 随机采样训练样本
    'colsample_bytree': 0.7,  # 生成树时进行的列采样
    'min_child_weight': 3,  # 如果新分裂的节点的样本权重和小于min_child_weight则停止分裂
    'silent': 1,  # 设置成1则没有运行信息输出,最好是设置为0.
    'eta': 0.007,  # 学习率
    'seed': 1000,
    'nthread': 4,  # cpu 线程数
    'eval_metric': 'auc'
}
plst = list(params.items())
# evallist = [(dtest, 'eval'), (dtrain, 'train')]   # 指定验证集


# 2.训练
num_round = 10
bst = xgb.train(plst, dtrain, num_round)
# bst = xgb.train( plst, dtrain, num_round, evallist )


# 3.保存模型
bst.save_model('0001.model')
# dump model
bst.dump_model('dump.raw.txt')
# dump model with feature map
# bst.dump_model('dump.raw.txt', 'featmap.txt')

# 4.加载保存的模型:
bst = xgb.Booster({'nthread': 4})  # init model
bst.load_model('0001.model')  # load data

# 5.预测
ypred = bst.predict(dtest)

# 6.绘图
# 1.绘制重要性
xgb.plot_importance(bst)
# 2.绘制输出树
#xgb.plot_tree(bst, num_trees=2)

四 XGBoost案例
1 分类案例

from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score   # 准确率

# 加载数据集
iris=load_iris()
X,y=iris.data,iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234565)


# 算法参数
params = {
    'booster': 'gbtree',
    'objective': 'multi:softmax',
    'num_class': 3,
    'gamma': 0.1,
    'max_depth': 6,
    'lambda': 2,
    'subsample': 0.7,
    'colsample_bytree': 0.75,
    'min_child_weight': 3,
    'silent': 0,
    'eta': 0.1,
    'seed': 1,
    'nthread': 0,
}

plst=list(params.items())

# 模型训练
dtrain=xgb.DMatrix(X_train,y_train)
dtest = xgb.DMatrix(X_test)
num_rounds=500
model=xgb.train(plst,dtrain,num_rounds)

# 模型预测
y_pred=model.predict(dtest)
accuracy=accuracy_score(y_pred,y_test)
print("accuarcy: %.2f%%" % (accuracy*100.0))

# 显示特征
plot_importance(model)
plt.show()

在这里插入图片描述
最终得到accuarcy: 96.67%

2 回归案例

import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_boston
from sklearn.metrics import  accuracy_score

boston=load_boston()
X,y=boston.data,boston.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234565)

params = {
    'booster': 'gbtree',
    'objective': 'reg:squarederror',
    'gamma': 0.1,
    'max_depth': 5,
    'lambda': 3,
    'subsample': 0.7,
    'colsample_bytree': 0.7,
    'min_child_weight': 3,
    'silent': 1,
    'eta': 0.1,
    'seed': 100,
    'nthread': 0,
}


plst=list(params.items())

dtrain=xgb.DMatrix(X_train,y_train)
dtest=xgb.DMatrix(X_test)
num_rounds=500
model=xgb.train(plst,dtrain,num_rounds)

y_pred=model.predict(dtest)

# 显示特征
plot_importance(model)
plt.show()

在这里插入图片描述

3 XGBoost调参

import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris

iris = load_iris()
X, y = iris.data, iris.target
col = iris.target_names
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234565)

parameters = {
    'max_depth': [5, 10, 15, 20, 25],
    'learning_rate': [0.01, 0.02, 0.05, 0.1, 0.15],
    'n_estimators': [500, 1000, 2000, 3000, 5000],
    'min_child_weight': [0, 2, 5, 10, 20],
    'max_delta_step': [0, 0.2, 0.6, 1, 2],
    'subsample': [0.6, 0.7, 0.8, 0.85, 0.95],
    'colsample_bytree': [0.5, 0.6, 0.7, 0.8, 0.9],
    'reg_alpha': [0, 0.25, 0.5, 0.75, 1],
    'reg_lambda': [0.2, 0.4, 0.6, 0.8, 1],
    'scale_pos_weight': [0.2, 0.4, 0.6, 0.8, 1]

}

xlf = xgb.XGBClassifier(max_depth=10,
                        learning_rate=0.01,
                        n_estimators=2000,
                        silent=True,
                        objective='multi:softmax',
                        num_class=3,
                        nthread=-1,
                        gamma=0,
                        min_child_weight=1,
                        max_delta_step=0,
                        subsample=0.85,
                        colsample_bytree=0.7,
                        colsample_bylevel=1,
                        reg_alpha=0,
                        reg_lambda=1,
                        scale_pos_weight=1,
                        seed=0,
                        missing=None)

gs = GridSearchCV(xlf, param_grid=parameters, scoring='accuracy', cv=3)
gs.fit(X_train, y_train)

print("Best score: %0.3f" % gs.best_score_)
print("Best parameters set: %s" % gs.best_params_)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值