XGBoost实战:sklearn机器学习调用示例
Posted K的笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XGBoost实战:sklearn机器学习调用示例相关的知识,希望对你有一定的参考价值。
一、XGBoost简单应用
import xgboost as xgb
import numpy as np
import pandas as pd
import re
import os
import warnings
warnings.filterwarnings("ignore")
xgb_train = xgb.DMatrix("xgboost_source_code/demo/data/agaricus.txt.train")
xgb_test = xgb.DMatrix("xgboost_source_code/demo/data/agaricus.txt.test")
param = {'max_depth':2, 'eta':1, 'objective':'binary:logistic'}
num_round = 5
watch_list = [(xgb_train, "train"), (xgb_test, "test")]
model = xgb.train(param, xgb_train, num_round, watch_list)
[0] train-error:0.04652 test-error:0.04283
[1] train-error:0.02226 test-error:0.02173
[2] train-error:0.00706 test-error:0.00621
[3] train-error:0.01520 test-error:0.01800
[4] train-error:0.00706 test-error:0.00621
preds = model.predict(xgb_test)
preds
array([0.08073306, 0.92217326, 0.08073306, ..., 0.98059034, 0.01182149,
0.98059034], dtype=float32)
二、机器学习算法基础
我们首先介绍几个基础的机器学习算法的实现原理和应用,如KNN、线性回归、逻辑回归等,使读者对机器学习算法有一个基本认识的同时,了解如何在模型训练过程中进行优化,以及如何对模型结果进行评估。然后,对决策树模型做了详细介绍。决策树是XGBoost模型的重要组成部分,学习和掌握决策树的生成、剪枝等内容将会对后续的学习提供巨大帮助。排序问题是机器学习中的常见问题,神经网络和支持向量机也是经常采用的机器学习算法,最后将分别介绍两者的实现原理,结合详细的公式推导过程,使读者能够深入理解算法背后的数学原理。
1. KNN做鸢尾花数据预测
KNN的主要算法思想为:特征空间中的一个样本,如果与其最相似的k个样本中的大部分属于某个类别,则该样本也属于该类别。KNN既可以用于解决分类问题,也可以用于回归问题。
对于分类问题,离样本最近的k个邻居中占多数的类别作为该样本的类别,如果k=1,则选取最近邻居的类别作为该样本的类别。对于回归问题,样本的预测值是最近的k个邻居的平均值。
KNN的计算步骤如下。
-
计算测试样本与训练集中所有(或大部分)样本的距离,该距离可以是欧氏距离、余弦距离等,较常用的是欧氏距离。 -
找到步骤1中距离最短的k个样本,作为预测样本的邻居。 -
对于分类问题,通过投票机制选出k个邻居中最多的类别作为预测样本的预测值。对于回归问题,则采用k个邻居的平均值。
Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集。数据集包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。
数据描述
变量名 | sepal_length | sepal_width | petal_length | petal_width | species |
---|---|---|---|---|---|
变量解释 | 花萼长度(单位cm) | 花萼宽度(单位cm) | 花瓣长度(单位cm) | 花瓣宽度(单位cm) | 种类 |
数据类型 | numeric | numeric | numeric | numeric | categorical |
1.1 导入数据集并观察分布
from matplotlib import pyplot
from sklearn.datasets import load_iris
iris = load_iris()
pd.DataFrame(iris.data, columns=iris.feature_names).head()
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | |
---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 |
1 | 4.9 | 3.0 | 1.4 | 0.2 |
2 | 4.7 | 3.2 | 1.3 | 0.2 |
3 | 4.6 | 3.1 | 1.5 | 0.2 |
4 | 5.0 | 3.6 | 1.4 | 0.2 |
setosa_sepal_len = iris.data[:50, 0]
setosa_sepal_width = iris.data[:50, 1]
versi_sepal_len = iris.data[50:100, 0]
versi_sepal_width = iris.data[50:100, 1]
vergi_sepal_len = iris.data[100:, 0]
vergi_sepal_width = iris.data[100:, 1]
pyplot.scatter(setosa_sepal_len, setosa_sepal_width, marker = 'o', c = 'b', s = 30, label = 'Setosa')
pyplot.scatter(versi_sepal_len, versi_sepal_width, marker = 'o', c = 'r', s = 50, label = 'Versicolour')
pyplot.scatter(vergi_sepal_len, vergi_sepal_width, marker = 'o', c = 'y', s = 35, label = 'Virginica')
pyplot.xlabel("sepal length")
pyplot.ylabel("sepal width")
pyplot.title("sepal length and width scatter")
pyplot.legend(loc = "upper right")
iris_data = pd.DataFrame(iris.data, columns=iris.feature_names)
iris_data["class"] = iris.target
iris_data.head()
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | class | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | 0 |
1 | 4.9 | 3.0 | 1.4 | 0.2 | 0 |
2 | 4.7 | 3.2 | 1.3 | 0.2 | 0 |
3 | 4.6 | 3.1 | 1.5 | 0.2 | 0 |
4 | 5.0 | 3.6 | 1.4 | 0.2 | 0 |
from matplotlib import pyplot as plt
grouped_data = iris_data.groupby("class")
group_mean = grouped_data.mean()
group_mean.plot(kind='bar')
plt.legend(loc="center right", bbox_to_anchor=(1.4, 0.3), ncol=1)
plt.show()
3. 划分数据集
因为我们至少需要一个训练集来训练模型(KNN则用于最终预测计算),一个测试集来检验模型对新样本的预测能力,而目前只有一个数据集,因此需要对数据集进行划分。划分数据集有很多方法,比如留出法(hold-out)、交叉验证法等,本示例采用较常用的留出法。留出法的实现原理是,按照一定比例将数据集划分为互不相交的两部分,分别作为训练集和测试集。此处选用的训练集、测试集的比例为4:1,代码如下:
msk = np.random.rand(len(iris_data)) < 0.8
train_data_origin = iris_data[msk]
test_data_origin = iris_data[~msk]
train_data = train_data_origin.reset_index(drop=True)
test_data = test_data_origin.reset_index(drop=True)
train_label = train_data["class"]
test_label = test_data["class"]
train_fea = train_data.drop("class", 1)
test_fea = test_data.drop("class", 1)
# 数据归一化
train_norm = (train_fea - train_fea.min()) / (train_fea.max() - train_fea.min())
test_norm = (test_fea - test_fea.min()) / (test_fea.max() - test_fea.min())
from sklearn import neighbors
from sklearn.metrics import accuracy_score
knn = neighbors.KNeighborsClassifier(n_neighbors=3)
knn.fit(train_norm, train_label)
predict = knn.predict(test_norm)
accuracy = accuracy_score(test_label, predict)
accuracy
0.9166666666666666
KNN算法是机器学习中最简单、有效的算法。上面通过鸢尾花品种分类的示例详细介绍了KNN算法的实现原理和应用。KNN算法属于懒惰学习算法,当数据集的样本容量比较大时,计算量也会比较大,并且需要较大的存储空间。此外,它无法给出数据的任何基础结构信息,后面介绍的算法将会解决这个问题。
2. 线性回归预测波士顿房价
下面通过一个示例来说明如何应用线性回归。以波士顿房屋价格数据集作为示例数据集,该数据集包含了波士顿房屋以及周边环境的一些详细信息,包括城镇人均犯罪率、一氧化碳浓度、住宅平均房屋数等。该数据集包含506个样本、13个特征字段、1个label字段
数据描述
No | 属性 | 数据类型 | 字段描述 |
---|---|---|---|
1 | CRIM | Float | 城镇人均犯罪率 |
2 | ZN | Float | 占地面积超过2.5万平方英尺的住宅用地比例 |
3 | INDUS | Float | 城镇非零售业务地区的比例 |
4 | CHAS | Integer | 查尔斯河虚拟变量 (= 1 如果土地在河边;否则是0) |
5 | NOX | Float | 一氧化氮浓度(每1000万份) |
6 | RM | Float | 平均每居民房数 |
7 | AGE | Float | 在1940年之前建成的所有者占用单位的比例 |
8 | DIS | Float | 与五个波士顿就业中心的加权距离 |
9 | RAD | Integer | 辐射状公路的可达性指数 |
10 | TAX | Float | 每10,000美元的全额物业税率 |
11 | PTRATIO | Float | 城镇师生比例 |
12 | B | Float | 1000(Bk - 0.63)^ 2其中Bk是城镇黑人的比例 |
13 | LSTAT | Float | 人口中地位较低人群的百分数 |
14 | MEDV | Float | (目标变量/类别属性)以1000美元计算的自有住房的中位数 |
from sklearn.datasets import load_boston
boston = load_boston()
X = boston.data
y = boston.target
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score
from sklearn.metrics import mean_squared_error
lr = LinearRegression()
lr.fit(X_train, y_train)
y_pred = lr.predict(X_test)
mean_squared_error(y_test, y_pred)
23.380836480270315
另外XGBoost也提供了线性回归的API,其数据加载步骤与上述scikit-learn
的方法相同,不再赘述。使用XGBoost,首先要把数据转化为其自定义的DMatrix
格式,该格式为XGBoost特定的输入格式。然后定义模型参数,此处定义较为简单,只选用了2个参数,如下:
params = {"objective":"reg:linear", "booster":"gblinear"}
其中,objective
用于确定模型的目标函数,这里以reg:squarederror
作为目标函数。参数booster
用于确定采用什么样的模型,此处选择的是线性模型(gblinear),读者也可根据应用场景选择其他模型(gbtree、dart),因本节主要介绍线性回归,因此选用线性模型。定义好参数后即可训练模型,最后用该模型对测试集进行预测。代码如下:
train_xgb = xgb.DMatrix(X_train, y_train)
params = {"objective":"reg:squarederror", "booster":"gblinear"}
model = xgb.train(dtrain=train_xgb, params=params)
y_pred = model.predict(xgb.DMatrix(X_test))
综上,线性回归是一种解决回归问题的常见方法。在线性回归中,求解最优参数的方法是最小化其损失函数。最小化损失函数有两种方法:正规方程和梯度下降法。
正规方程通过矩阵运算求得最优参数,但其必须满足 可逆,当样本数比特征数还少时, 的逆是不能直接计算的。
梯度下降法是沿负梯度的方向一步步最小化损失函数,求解最优参数。梯度下降法需要指定步长并进行多次迭代,但相比于正规方程,梯度下降法可以应用于特征数较大的情况。最后,通过波士顿房价的示例展示了通过scikit-learn和XGBoost如何应用线性回归。
3. 逻辑回归预测良性/恶性乳腺肿瘤
下面将使用逻辑回归预测乳腺肿瘤是良性的还是恶性的。示例采用的数据集为威斯康星诊断乳腺癌数据集,它通过细胞核的相关特征来预测乳腺肿瘤为良性/恶性,这是一个非常著名的二分类数据集。该数据集包含569个样本,其中有212个恶性肿瘤样本,357个良性肿瘤样本。共有32个字段,字段1为ID,字段2为label,其他30个字段为细胞核的相关特征,例如:
-
半径(从中心到周边点的平均距离) -
纹理(灰度值的标准偏差) -
周长 -
面积 -
光滑度(半径长度的局部变化) -
紧凑性(周长的二次方/面积的负一次方) -
凹度(轮廓的凹陷程度) -
凹点(轮廓中凹部的数量) -
对称 -
分形维数
对于每张图像,分别计算以上10个特征的平均值、标准误差和最差/最大(最大的3个值的平均)值,由此生成30个特征。例如,字段3表示平均半径,字段13表示半径的标准误差,字段23表示最差半径。所有特征都保留4位有效数字。
scikit-learn已经集成了该数据集,并进行了相应的处理(如去掉了ID字段),使用时直接加载即可,如下:
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
X = cancer.data
y = cancer.target
其中,X为特征数据,包含上面介绍的30个特征,y为标签数据,标记乳腺肿瘤类型,1代表良性,0代表恶性。下面按4:1的比例将数据集划分为训练集和测试集:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
lr = LogisticRegression()
lr.fit(X_train, y_train)
y_pred = lr.predict(X_test)
print(classification_report(y_test, y_pred, target_names=["Benign", "Malignant"]))
precision recall f1-score support
Benign 0.95 0.90 0.93 42
Malignant 0.95 0.97 0.96 72
accuracy 0.95 114
macro avg 0.95 0.94 0.94 114
weighted avg 0.95 0.95 0.95 114
其中,列表的左边一列为分类的标签名,avg/total
为各列的均值。support
表示该类别样本出现的次数。
XGBoost提供了逻辑回归的API,读者可以通过XGBoost中的逻辑回归对数据集进行预测,代码如下:
train_xgb = xgb.DMatrix(X_train, y_train)
params = {"objective":"reg:logistic", "booster":"gblinear"}
model = xgb.train(dtrain=train_xgb, params=params)
y_pred = model.predict(xgb.DMatrix(X_test))
XGBoost逻辑回归API的调用方式和线性回归类似,唯一不同的是目标函数objective
改为reg:logistic
,booster
仍然选择线性模型。
注意,XGBoost在预测结果上和scikit-learn有些差别,XGBoost的预测结果是概率,而scikit-learn的预测结果是0或1的分类(scikit-learn也可通过predict_proba
输出概率)。在XGBoost中,如果需要输出0或1的分类,需要用户自己对其进行转化,例如:
ypred_bst = np.array(y_pred)
y_pred_bst = ypred_bst > 0.5
y_pred_bst = y_pred_bst.astype(int)
print(classification_report(y_test, y_pred_bst, target_names=["Benign", "Malignant"]))
precision recall f1-score support
Benign 0.90 0.67 0.77 42
Malignant 0.83 0.96 0.89 72
accuracy 0.85 114
macro avg 0.87 0.81 0.83 114
weighted avg 0.86 0.85 0.84 114
4. 决策树解决肿瘤分类问题
scikit-learn实现了决策树算法,它采用的是一种优化的CART版本,既可以解决分类问题,也可以解决回归问题。分类问题使用DecisionTreeClassifier类,回归问题使用DecisionTreeRegressor类。两个类的参数相似,只有部分有所区别,以下是对主要参数的说明。
-
criterion
:特征选择采用的标准。DecisionTreeClassifier分类树默认采用gini
(基尼系数)进行特征选择,也可以使用entropy
(信息增益)。DecisionTreeRegressor默认采用MSE(均方误差),也可以使用MAE(平均绝对误差)。 -
splitter
:节点划分的策略。支持best
和random
两种方式,默认为best
,即选取所有特征中最优的切分点作为节点的分裂点,random
则随机选取部分切分点,从中选取局部最优的切分点作为节点的分裂点。 -
max_depth
:树的最大深度,默认为None,表示没有最大深度限制。节点停止分裂的条件是:样本均属于相同类别或所有叶子节点包含的样本数量小于 。若将该参数设置为None以外的其他值,则决策树生成过程中达到该阈值深度时,节点停止分裂。 -
min_samples_split
:节点划分的最小样本数,默认为2。若节点包含的样本数小于该值,则该节点不再分裂。若该字段设置为浮点数,则表示最小样本百分比,划分的最小样本数为 。 -
min_samples_leaf
:叶子节点包含的最小样本数,默认为1。此字段和min_samples_split
类似,取值可以是整型,也可以是浮点型。整型表示一个叶子节点包含的最小样本数,浮点型则表示百分比。叶子节点包含的最小样本数为 。 -
max_features
:划分节点时备选的最大特征数,默认为None,表示选用所有特征。若该字段为整数,表示选用的最大特征数;若为浮点数,则表示选用特征的最大百分比。最大特征数为 。
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.metrics import classification_report
from sklearn import tree
cancer = load_breast_cancer()
X = cancer.data
y = cancer.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=8)
clf = tree.DecisionTreeClassifier(max_depth=4)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(classification_report(y_test, y_pred, target_names=["Benign", "Malignant"]))
precision recall f1-score support
Benign 0.95 0.87 0.91 46
Malignant 0.92 0.97 0.94 68
accuracy 0.93 114
macro avg 0.93 0.92 0.93 114
weighted avg 0.93 0.93 0.93 114
为便于读者直观地理解树模型,可以使用Graphviz工具包将模型可视化。Graphviz是一个开源的图形可视化软件,可以将结构数据转化为形象的图形或网络,在软件工程、数据库、机器学习等领域的可视化界面中有应用。函数export_graphviz
可以将scikit-learn
中的决策树导出为Graphviz的格式,导出完成后即可对Graphviz格式的决策树进行图形渲染,如下:
import graphviz
dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=cancer.feature_names,
class_names=cancer.target_names,
filled=True, rounded=True,
special_characters=True)
graph = graphviz.Source(dot_data)
# graph
# 将dot_data写入到txt文件中
f = open('output/img/dot_data.txt', 'w')
f.write(dot_data)
f.close()
5. 神经网络识别手写体数字
手写体数字数据集(MNIST)是一个经典的多分类数据集,由不同的手写体数字图片以及0~9的数字标签样本构成。scikit-learn中的手写体数字数据集共有1797个样本,每个样本包含一个8×8像素的图像和0~9的数字标签。scikit-learn通过MLPClassifier
类实现的多层感知器完成分类任务,通过MLPRegressor
类完成回归任务。对于手写体数字数据集这样的多分类问题,显然要采用MLPClassifier
。MLPClassifier
的常用参数如下:
-
hidden_layer_sizes
:用来指定隐藏层包含的节点数量,其类型为tuple,长度是n_layers-2
,其中n_layers为网络总层数; -
activation
:指定隐藏层的激活函数,默认为relu; -
solver
:指定权重的更新方法,默认为sgd,即随机梯度下降法; -
alpha
:指定L2正则的惩罚系数; -
learning_rate
:指定训练过程中学习率更新方法,有constant、invscaling和adaptive这3种方法。其中,constant表示学习率在训练过程中为固定值;invscaling表示随着训练的进行,学习率指数降低;adaptive表示动态调整,当训练误差不断减少时(减少量超过一定阈值),学习率保持不变,若连续两次迭代训练损失未达到上述条件,则学习率缩小为原值的1/5。 -
max_iter
表示迭代的最大轮数,对于solver为sgd和adam的情况,max_iter
相当于epoch的数量。
了解了MLPClassifier
类的常用参数后,下面介绍如何使用MLPClassifier
来解决识别手写体数字的问题。
from sklearn.datasets import load_digits
digits = load_digits()
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=8)
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score
mlp = MLPClassifier(hidden_layer_sizes=(128, 64), max_iter=50, alpha=1e-4, solver='sgd')
mlp.fit(X_train, y_train)
y_pred = mlp.predict(X_test)
print("Accuracy: ", accuracy_score(y_test, y_pred))
Accuracy: 0.9555555555555556
6. 支持向量机识别手写体数字
下面仍以手写体数字数据集(MNIST)为例,介绍如何使用SVM解决分类问题。SVM既可以解决二分类问题,也能解决多分类问题。SVM解决多分类问题的方法主要有两种:one-vs-one和one-vs-the-rest。
-
one-vs-one
为每两类样本建立一个二分类器,则 个类别的样本需要建立 个二分类器。 -
one-vs-the-rest
是为每个类别和其他剩余类别建立一个二分类器,从中选择预测概率最大的分类作为最终分类,k个类别的样本需建立k个二分类器。
scikit-learn通过SVC类来解决分类问题,通过SVR类来解决回归问题(SVM也可以解决回归问题),下面采用SVC类解决手写体数字识别的多分类问题。
SVC可以通过参数kernel指定采用的核函数,支持的核函数有:linear
(线性核函数)、poly
(多项式)、rbf
(高斯)、sigmoid
、precomputed
以及自定义形式callable
。若不指定kernel,其默认采用rbf
。SVC还有几个比较常用的参数:
-
惩罚参数 ,即前面松弛变量中介绍的不满足约束条件样本的惩罚系数; -
参数 degree
是多项式核函数(kernel设置为poly
)的阶数; -
参数gamma表示高斯核和sigmoid核中的内核系数,在高斯核中对应的是高斯核函数公式中的 。
数据集的加载和划分同神经网络中的示例,不再赘述。此处主要介绍模型拟合与评估。
先定义一个SVC模型,这里采用高斯核函数,惩罚系数C为1.0,gamma为0.001,当然也可以通过参数调优来确定参数。定义模型之后即可训练模型,然后对测试集进行预测,最后以准确率为指标评估预测结果。具体代码如下:
from sklearn import svm
from sklearn.metrics import accuracy_score
svc = svm.SVC(C=1.0, kernel="rbf", gamma=0.001)
svc.fit(X_train, y_train)
y_pred = svc.predict(X_test)
print("Accuracy", accuracy_score(y_test, y_pred))
Accuracy 0.9861111111111112
也可以采用其他核函数,如多项式核函数,代码如下:
svc = svm.SVC(C=1.0, kernel="poly", degree=3)
svc.fit(X_train, y_train)
y_pred = svc.predict(X_test)
print("Accuracy", accuracy_score(y_test, y_pred))
Accuracy 0.9861111111111112
可以看到,在本例中采用多项式核函数和高斯核函数的预测准确率是相同的。读者也可自行尝试其他参数,观察不同参数对模型预测的影响。
- END -以上是关于XGBoost实战:sklearn机器学习调用示例的主要内容,如果未能解决你的问题,请参考以下文章