目录

1. 前言

1.1 安装或更新

2. 第一个例子: Predict Onset of Diabetes¶

2.1 加载数据

2.2 模型训练

2.3 预测和评估

3 过拟合¶

3.1 降低学习率

3.2 减小树的深度

3.3 减小树的个数

4. 归一化有好处吗?¶

5. 与其它模型的对比


1. 前言

        梯度提升机是集成学习中Boosting这一流派(其它集成学习方法还有bagging、stacking等)的一种方法,boosting流派中另外一种得到广泛应用的是自适应提升法(Adaptive Boost,简称Adaboost)。

        XGBoost( eXtreme Gradient Boosting:极限梯度提升)是基于决策树的集成机器学习算法,它以梯度提升(Gradient Boost)为框架,由GBDT发展而来。它的主要目标是提升模型运行的速度和有效性(efficiency)。同样是利用加法模型与前向分步算法实现学习的优化过程,但与GBDT是有区别的,主要包括以下几点:

(1) 目标函数:XGBoost的损失函数添加了正则化项,使用正则用以控制模型的复杂度,正则项里包含了树的叶子节点个数、每个叶子节点权重(叶结点的socre值)的平方和。

(2) 优化方法:GBDT在优化时只使用了一阶导数信息,XGBoost使用了一、二阶导数信息。

(3) 缺失值处理:XBGoost通过学习模型自动选择最优的缺失值默认切分方向。

(4) 防止过拟合: XGBoost除了增加了正则项来防止过拟合,还支持行列采样的方式来防止过拟合。

基于以上改进,它可以在最短时间内用更少的计算资源得到更好的结果。

        XGBoost被大量运用于竞赛中,比如Kaggle竞赛,在Kaggle2015年公布的29个获胜者中有17个使用了XGBoost,同样在KDDCup2015的竞赛中XGBoost也被大量使用。

        本文不涉及更深的理论介绍(感兴趣者可以去读原论文, Ref3提供了一个不错的解读),仅限于基于代码实验简要介绍如何构建你的第一个XGboost应用模型。

1.1 安装或更新

        要使用XGboost,当然首先要安装XGboost(for use in Python)。如下一键式命令即可。

conda/pip install xgboost

        或者如你是要更新的话:

conda update xgboost

or

pip install -upgrade xgboost

        XGBoost采用了scikit-learn中通用的封装方式,其使用方法和使用scikit-learn的内置模型是一样的。

2. 第一个例子: Predict Onset of Diabetes

        在这个例子中,我们使用Pima Indians onset of diabetes dataset,该数据集(csv文件)可以从以下连接下载:(https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.csv)。相关的数据说明文件:https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.names

        该数据集描述的是患者所接收的医疗记录,其标签则表示是否患有糖尿病的诊断结果。 该数据集包含8个特征(all numeric-valued),如下所示:

  1. Number of times pregnant
  2. Plasma glucose concentration a 2 hours in an oral glucose tolerance test
  3. Diastolic blood pressure (mm Hg)
  4. Triceps skin fold thickness (mm)
  5. 2-Hour serum insulin (mu U/ml)
  6. Body mass index (weight in kg/(height in m)^2)
  7. Diabetes pedigree function
  8. Age (years) 标签为二进制值{0,1},表示是否患有糖尿病。
  9. Class variable (0 or 1)

        从说明文件“pima-indians-diabetes.names.txt”或者UCI Machine Learning Repository website可以获得更加详细的信息。 这个数据集由于其各特征都是数值(numberic)类型,且是一个简单的二分类问题,但是由于它过于简单而且数据集也比较小,所以,不一定是XGboost能够发挥优势的地方。

2.1 加载数据

        以下用两种方法加载读入csv文件中的数据,一是pandas.read_csv,一是numpy.loadtxt()。两种方法输出的数据格式不一样,前者是Pandas DataFrame,后者是numpy ndarray,两者都可以。后续的fit()等函数对于两者都支持。Pandas读入生成的DataFrame的视觉效果更好一些。

import xgboost as xgb
import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
%matplotlib inline

        用pandas.read_csv()读入数据 :

# Read the data with Pandas
df = pd.read_csv('pima-indians-diabetes.csv',header=None) # There is no header row in this csv file.
# X = df[[0,1,2,3,4,5,6,7]]
X_df = df.drop(columns=[8],axis=1) # Throw the column#8 to form train samples. The column#8 is label
Y_df  = df[8]
df

        或者用numpy.loadtxt()读入数据:

# Read the data with Numpy
dataset = np.loadtxt('pima-indians-diabetes.csv', delimiter=",")
# split data into X and y
X_np = dataset[:,0:8]
Y_np = dataset[:,8]
dataset

将数据分为训练集和测试集: 

# split data into train and test sets
from sklearn.model_selection import train_test_split

#X,Y = X_df, Y_df # Both are OK!
X,Y = X_np, Y_np

seed = 7
test_size = 0.2
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=test_size, random_state=seed)

2.2 模型训练

        XGBoost提供了一个wrapper class,使得其中的模型可以以scikit-learn框架中的分类或者回归模型同样的方式使用。这也意味着XGBoost和scikit-learn库可以无缝结合使用。

        XGBoost中的分类模型为XGBClassifier,回归模型为XGBRegressor.本节我们用前者进行分类实验。XGBClassifier有很多参数可以使用,但是作为第一个例子我们尽量使用缺省的参数组合。

model = xgb.XGBClassifier(random_state=1,use_label_encoder=False) # 如果不指定use_label_encoder=False的话会导致警告
model.fit(X_train, y_train)
print(model) # 打印模型信息

 

2.3 预测和评估

        可以用model.score()直接给出该模型分别在训练集和测试集上的预测准确度。

print('Accuracy over train set: ', model.score(X_train, y_train))
print('Accuracy over test set: ', model.score(X_test,y_test))

 运行结果:

Accuracy over train set:  1.0
Accuracy over test set:  0.7402597402597403

        如果想知道针对每个样本的预测结果,则可以使用model.predict()函数。当然也可以使用model.predict_proba()函数得出针对每个样本的软预测结果,即概率性的预测结果,其中包含了预测结果的置信度信息。基于model.predict()调用结果,再调用accuracy_score也同样可以得到accuracy结果,model.score()相当于连续调用model.predict()和accuracy_score()但是没有输出中间的预测结果而已!

# evaluate predictions
from sklearn.metrics import accuracy_score

test_pred_proba = model.predict_proba(X_test)
test_pred = model.predict(X_test)
print(test_pred)
print(test_pred_proba[:5,:])

accuracy = accuracy_score(y_test, test_pred)
print("Accuracy: %.2f%%" % (accuracy * 100.0))

        如上所示,软预测结果提供了一些额外的信息。比如说第3个样本,[0.50282574 0.49717423],这意味着模型对这个样本的class#0和class#1的预测概率几乎相当,这说明本模型对于这个样本没有什么可说瞎蒙了一个而已。很可能在不同随机种子条件下进行训练得到的预测结果就不相同的。

        以下把代码串在一起构成一个完整pipeline。

from numpy import loadtxt
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# Read the data with Pandas
df = pd.read_csv('pima-indians-diabetes.csv',header=None) # There is no header row in this csv file.
# X = df[[0,1,2,3,4,5,6,7]]
X = df.drop(columns=[8],axis=1) # Throw the column#8 to form train samples. The column#8 is label
Y = df[8]

# split data into train and test sets
seed = 7
test_size = 0.2
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=test_size, random_state=seed)
model = xgb.XGBClassifier(random_state=1,use_label_encoder=False)
model.fit(X_train, y_train)
print('Accuracy over train set: ', model.score(X_train, y_train))
print('Accuracy over test set: ', model.score(X_test,y_test))

3 过拟合

        从以上结果来看,在训练集上达到了惊人的100%的预测准确度,而在测试集上只有74%,这个是明显的过拟合了。如何解决过拟合问题呢?

        过拟合与模型复杂度相关联,要想解消过拟合问题就必须降低模型复杂度。一般来说,梯度提升方法有以下三个参数与模型复杂度相关联:

        第一个是树的最大深度max_depth,树的深度越大意味着模型复杂度越高,其计算复杂度(内存需求、计算负荷)也更高。所以减小树的深度有利于解消过拟合问题。

        第二个是学习率learning_rate,用于控制每棵树纠正前一棵树的错误的强度。较高的学习率意味着每棵树都可以做出较强的修正,这样模型更加复杂。反过来说,降低学习率有助于降低模型复杂度并减轻过拟合问题。以下我们两种方法都试一试看。

        第三个是树的个数,由n_estimators指定。更多的树意味着更高的模型复杂度,因为模型有更多的机会纠正训练集上的错误。

3.1 降低学习率

        XGBoost的缺省的学习率是0.1,以下我们将学习率将到0.01,训练集上的准确度降维90%,测试集上的准确度提高了2.5个百分点。过拟合现象有一定程度的解消,但是仍然比较严重。如果降到0.05的话,训练集和测试集上的准确度分为变为{88%, 78%}。进一步降低学习率变化不大,看来降低学习率不能完全解决过拟合的问题。

model = xgb.XGBClassifier(random_state=1,learning_rate=0.01,use_label_encoder=False)
model.fit(X_train, y_train)
print('Accuracy over train set: ', model.score(X_train, y_train))
print('Accuracy over test set: ', model.score(X_test,y_test))
Accuracy over train set:  0.8957654723127035
Accuracy over test set:  0.7662337662337663

3.2 减小树的深度

XGBoost的缺省的max_depth为6,以下我们在将学习率固定为0.01的条件下,降低该参数值看看会发生什么。

learning_rate max_depth train_accuracy test_accuracy

0.01 6 89.5% 76.6%

0.01 3 78.8% 77.6%

0.01 1 75.0% 74.6%

看上去减小max_depth不是很有效,虽然max_depth=3在测试集上比max_depth=6要稍微好一些。

model = xgb.XGBClassifier(random_state=1,learning_rate=0.01,max_depth=1, use_label_encoder=False)
model.fit(X_train, y_train)
print('Accuracy over train set: ', model.score(X_train, y_train))
print('Accuracy over test set: ', model.score(X_test,y_test))

3.3 减小树的个数

XGBoost的缺省的n_estimators为100,以下我们在将学习率固定为0.01,max_depth=3的条件下,降低该参数值看看会发生什么。

n_estimators learning_rate max_depth train_accuracy test_accuracy

100 0.01 3 78.8% 77.6%

80 0.01 3 78.1% 77.9%

60 0.01 3 77.8% 77.9%

如上所示,将树的个数降为80时,训练集和测试集上的准确度基本持平了,这个意味着已经很难再得到进一步的改进了。

model = xgb.XGBClassifier(random_state=1,learning_rate=0.01,max_depth=3,n_estimators=60,use_label_encoder=False)
model.fit(X_train, y_train)
print('Accuracy over train set: ', model.score(X_train, y_train))
print('Accuracy over test set: ', model.score(X_test,y_test))

4. 归一化有好处吗?

        一般来说基于树的模型对于数据的缩放都不敏感,因此数据不需要进行归一化(包括均值归一化和方差归一化)。以下我们也通过实验来确定一下XGBoost分类器模型对于数据归一化是否敏感。

from numpy import loadtxt
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 用numpy.loadtxt读取对于做归一化处理更方便
dataset = np.loadtxt('pima-indians-diabetes.csv', delimiter=",")
# split data into X and y
X = dataset[:,0:8]
Y = dataset[:,8]

# normalization of each feature
X = (X - np.mean(X,axis=0))/np.std(X,axis=0)
feature0 = X[:,0]
print(feature0.shape, np.mean(feature0), np.std(feature0))

# split data into train and test sets
test_size = 0.2
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=test_size)

model = xgb.XGBClassifier(random_state=1,learning_rate=0.01,max_depth=3,n_estimators=80,use_label_encoder=False)
model.fit(X_train, y_train)
print('Accuracy over train set: ', model.score(X_train, y_train))
print('Accuracy over test set: ', model.score(X_test,y_test))
Accuracy over train set:  0.7947882736156352
Accuracy over test set:  0.7792207792207793

        与上一节的结果基本持平,确实对归一化处理不敏感。

5. 与其它模型的对比

        作为对比,以下我们也用GradientBoostingClassifier和KNeighborsClassifier基于同样的数据进行实验,看看相互之间的性能对比情况。

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.neighbors import KNeighborsClassifier

gbrt = GradientBoostingClassifier(random_state=1, learning_rate=0.01, max_depth=3, n_estimators=80)
gbrt.fit(X_train,y_train)
print('Accuracy over train set: ', gbrt.score(X_train, y_train))
print('Accuracy over test set: ', gbrt.score(X_test,y_test))

training_accuracy = []
test_accuracy = []
# try n_neighbors from 1 to 10
neighbors_settings = range(1, 11)
 
for n_neighbors in neighbors_settings:
    # build the model
    clf = KNeighborsClassifier(n_neighbors=n_neighbors)
    clf.fit(X_train, y_train)
    # record training set accuracy
    training_accuracy.append(clf.score(X_train, y_train))
    # record generalization accuracy
    test_accuracy.append(clf.score(X_test, y_test))
    
plt.plot(neighbors_settings, training_accuracy, label="training accuracy")
plt.plot(neighbors_settings, test_accuracy, label="test accuracy")
plt.ylabel("Accuracy")
plt.xlabel("n_neighbors")
plt.grid()
plt.legend()

        GradientBoosting的结果(采用相同的参数设置):

Accuracy over train set:  0.7638436482084691
Accuracy over test set:  0.7207792207792207

        KNN的结果:

         看上,XGBoost在测试集上的77.9%准确度还是要略胜一筹。不过,前面已经说过,真正能够体现XGBoost优势的是在大数据集上的处理运行效率。

Reference

  1. How to Develop Your First XGBoost Model in Python

  2. Machine Learning with XGBoost and Scikit-learn | Engineering Education (EngEd) Program | Section

  3. 对xgboost的理解 - 知乎

  4. Andreas C.Muller, Sarah Guido, Introduction to Machine Leanring with Python(《Python机器学习基础教程》)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐