使用XGBoost之数据准备(LabelEncoder,One Hot Encode,处理缺失值)

由于其速度和性能,XGBoost是一种流行的梯度增强实现。

在内部,XGBoost模型将所有问题表示为仅以数值作为输入的回归预测建模问题。如果您的数据是另一种形式的,则必须将其准备成预期的格式。

在本文中,您将了解如何使用Python中的XGBoost库为梯度增强准备数据。

读完这篇文章你就会知道:

如何为分类编码字符串输出变量。
如何使用onehot准备分类输入变量。
如何使用XGBoost自动处理丢失的数据。

让我们开始吧。

标签编码字符串类值

鸢尾花分类问题是一个具有字符串类值的问题的例子。

这是一个预测问题,给定以厘米为单位的鸢尾花的测量值,其任务是预测给定的花属于哪个物种。

下面是原始数据集的示例。您可以了解关于这个数据集的更多信息,并从UCI机器学习存储库下载CSV格式的原始数据。

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa

XGBoost不能按原样对这个问题建模,因为它要求输出变量是数值的。

我们可以使用LabelEncoder轻松地将字符串值转换为整数值。这三个类值(鸢尾-setosa、鸢尾-versicolor、鸢尾-virginica)被映射到整数值(0,1,2)。

# encode string class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)

我们将标签编码器保存为一个单独的对象,这样我们就可以使用相同的编码方案转换训练数据集以及稍后的测试和验证数据集。

下面是一个完整的示例,演示如何加载iris数据集。注意,panda用于加载数据,以便处理字符串类值。

# multiclass classification

import pandas
import xgboost
from sklearn import model_selection
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder

# load data
data = pandas.read_csv('iris.csv', header=None)
dataset = data.values

# split data into X and y
X = dataset[:,0:4]
Y = dataset[:,4]

# encode string class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, label_encoded_y, test_size=test_size, random_state=seed)

# fit model no training data
model = xgboost.XGBClassifier()
model.fit(X_train, y_train)
print(model)

# make predictions for test data
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]

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

运行该示例将产生以下输出:

XGBClassifier(base_score=0.5, colsample_bylevel=1, colsample_bytree=1,
       gamma=0, learning_rate=0.1, max_delta_step=0, max_depth=3,
       min_child_weight=1, missing=None, n_estimators=100, nthread=-1,
       objective='multi:softprob', reg_alpha=0, reg_lambda=1,
       scale_pos_weight=1, seed=0, silent=True, subsample=1)
Accuracy: 92.00%

请注意XGBoost模型是如何配置为使用multi:softprob目标(softmax loss function的变体,用于建模类概率)自动对多类分类问题建模的。这表明,在内部,输出类被自动转换为一个one hot编码。

One Hot Encode Categorical Data

有些数据集只包含分类数据,例如乳腺癌数据集。

该数据集描述了乳腺癌活检的技术细节,预测任务是预测患者是否有癌症复发。

下面是原始数据集的示例。您可以在UCI机器学习存储库中了解关于此数据集的更多信息,并从mldata.org下载CSV格式的数据集。

'40-49','premeno','15-19','0-2','yes','3','right','left_up','no','recurrence-events'
'50-59','ge40','15-19','0-2','no','1','right','central','no','no-recurrence-events'
'50-59','ge40','35-39','0-2','no','2','left','left_low','no','recurrence-events'
'40-49','premeno','35-39','0-2','yes','3','right','left_low','yes','no-recurrence-events'
'40-49','premeno','30-34','3-5','yes','2','left','right_up','no','recurrence-events'

我们可以看到所有9个输入变量都是分类的,并以字符串格式描述。该问题是一个二分类预测问题,输出类值也用字符串格式描述。

我们可以重用上一节中的相同方法,并将字符串类值转换为整数值,从而使用LabelEncoder对预测进行建模。例如:

# encode string class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)

我们可以对X中的每个输入特性使用相同的方法,但这只是一个起点。

# encode string input values as integers
features = []
for i in range(0, X.shape[1]):
	label_encoder = LabelEncoder()
	feature = label_encoder.fit_transform(X[:,i])
	features.append(feature)
encoded_x = numpy.array(features)
encoded_x = encoded_x.reshape(X.shape[0], X.shape[1])

XGBoost可以假设每个输入变量的编码整数值都具有序号关系。例如,将“left-up”编码为0,将“left-low”编码为1的breast-quad变量具有整数形式的有意义的关系。在这种情况下,这个假设是不正确的。

相反,我们必须将这些整数值映射到新的二进制变量上,每个分类值对应一个新变量。

例如,breast-quad变量有以下值:

left-up
left-low
right-up
right-low
central

我们可以将其建模为5个二进制变量如下:

left-up, left-low, right-up, right-low, central
1,0,0,0,0
0,1,0,0,0
0,0,1,0,0
0,0,0,1,0
0,0,0,0,1

这叫做热编码。我们可以使用scikit-learn中的OneHotEncoder类对所有分类输入变量进行热编码。

我们可以在标签编码后对每个特性进行热编码。首先,我们必须将特征数组转换为一个二维的NumPy数组,其中每个整数值都是长度为1的特征向量。

feature = feature.reshape(X.shape[0], 1)

然后,我们可以创建OneHotEncoder并对特性数组进行编码。

onehot_encoder = OneHotEncoder(sparse=False)
feature = onehot_encoder.fit_transform(feature)

最后,我们可以通过将一个热编码特性一个接一个地连接起来,并将它们作为新列(axis=2)添加上去,从而构建输入数据集。我们最终得到一个由43个二进制输入变量组成的输入向量。

# encode string input values as integers
encoded_x = None
for i in range(0, X.shape[1]):
	label_encoder = LabelEncoder()
	feature = label_encoder.fit_transform(X[:,i])
	feature = feature.reshape(X.shape[0], 1)
	onehot_encoder = OneHotEncoder(sparse=False)
	feature = onehot_encoder.fit_transform(feature)
	if encoded_x is None:
		encoded_x = feature
	else:
		encoded_x = numpy.concatenate((encoded_x, feature), axis=1)
print("X shape: : ", encoded_x.shape)

理想情况下,我们可以尝试不使用一个热编码对某些输入属性进行编码,因为我们可以用显式的顺序关系对它们进行编码,例如第一个列age的值为“40-49”和“50-59”。如果您对扩展这个示例感兴趣,可以将其作为练习。

下面是带有标签和一个热编码输入变量和标签编码输出变量的完整示例。

# binary classification, breast cancer dataset, label and one hot encoded

import numpy
from pandas import read_csv
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder

# load data
data = read_csv('datasets-uci-breast-cancer.csv', header=None)
dataset = data.values

# split data into X and y
X = dataset[:,0:9]
X = X.astype(str)
Y = dataset[:,9]

# encode string input values as integers
encoded_x = None
for i in range(0, X.shape[1]):
	label_encoder = LabelEncoder()
	feature = label_encoder.fit_transform(X[:,i])
	feature = feature.reshape(X.shape[0], 1)
	onehot_encoder = OneHotEncoder(sparse=False)
	feature = onehot_encoder.fit_transform(feature)
	if encoded_x is None:
		encoded_x = feature
	else:
		encoded_x = numpy.concatenate((encoded_x, feature), axis=1)
print("X shape: : ", encoded_x.shape)

# encode string class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)

# split data into train and test sets
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = train_test_split(encoded_x, label_encoded_y, test_size=test_size, random_state=seed)

# fit model no training data
model = XGBClassifier()
model.fit(X_train, y_train)
print(model)

# make predictions for test data
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]

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

运行这个例子,我们得到以下输出:

('X shape: : ', (285, 43))
XGBClassifier(base_score=0.5, colsample_bylevel=1, colsample_bytree=1,
       gamma=0, learning_rate=0.1, max_delta_step=0, max_depth=3,
       min_child_weight=1, missing=None, n_estimators=100, nthread=-1,
       objective='binary:logistic', reg_alpha=0, reg_lambda=1,
       scale_pos_weight=1, seed=0, silent=True, subsample=1)
Accuracy: 71.58%

同样,我们可以看到XGBoost框架自动选择了“binary:logistic”目标,这是这个二进制分类问题的正确目标。

处理缺失数据

XGBoost可以自动学习如何最好地处理丢失的数据。

事实上,XGBoost被设计用于处理稀疏数据,就像上一节中的热编码数据一样,通过最小化损失函数,以处理稀疏或零值的方式处理丢失的数据。

Horse Colic dataset就是一个很好的例子,它包含了大量丢失的数据,大约30%。

您可以了解更多关于Horse Colic数据集的信息,并从UCI机器学习存储库下载原始数据文件

这些值由空格分隔,我们可以使用panda函数read_csv轻松地加载它。

dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None)

加载后,我们可以看到缺失的数据用问号字符(’ ? ')标记。我们可以将这些缺失的值更改为XGBoost期望的稀疏值,即值0(0)。

# set missing values to 0
X[X == '?'] = 0

因为缺少的数据被标记为字符串,所以那些缺少数据的列都被加载为字符串数据类型。现在我们可以将整个输入数据集转换为数值。

# convert to numeric
X = X.astype('float32')

最后,这是一个二进制分类问题,虽然类值是用整数1和2标记的。我们将XGBoost中的二元分类问题建模为logistic 0和logistic 1。我们可以使用LabelEncoder轻松地将Y数据集转换为0和1整数,就像我们在iris flowers示例中所做的那样。

# encode Y class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)

为了完整起见,下面提供了完整的代码清单。

# binary classification, missing data

from pandas import read_csv
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder

# load data
dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None)
dataset = dataframe.values

# split data into X and y
X = dataset[:,0:27]
Y = dataset[:,27]

# set missing values to 0
X[X == '?'] = 0

# convert to numeric
X = X.astype('float32')

# encode Y class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)

# split data into train and test sets
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = train_test_split(X, label_encoded_y, test_size=test_size, random_state=seed)

# fit model no training data
model = XGBClassifier()
model.fit(X_train, y_train)
print(model)

# make predictions for test data
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]

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

运行此示例将生成以下输出。

XGBClassifier(base_score=0.5, colsample_bylevel=1, colsample_bytree=1,
       gamma=0, learning_rate=0.1, max_delta_step=0, max_depth=3,
       min_child_weight=1, missing=None, n_estimators=100, nthread=-1,
       objective='binary:logistic', reg_alpha=0, reg_lambda=1,
       scale_pos_weight=1, seed=0, silent=True, subsample=1)
Accuracy: 83.84%

通过用非零值(如1)标记缺失值,我们可以梳理出XGBoost自动处理缺失值的效果。

X[X == '?'] = 1

重新运行这个例子表明模型的精度下降了。

Accuracy: 79.80%

我们还可以将缺失的数据赋予一个特定的值。

通常对列使用平均值或中值。我们可以使用scikit-learn Imputer类轻松地对缺失的数据进行归并。

# impute missing values as the mean
imputer = Imputer()
imputed_x = imputer.fit_transform(X)

下面是完整的示例,缺失的数据由每一列的平均值输入。

# binary classification, missing data, impute with mean

import numpy
from pandas import read_csv
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import Imputer

# load data
dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None)
dataset = dataframe.values

# split data into X and y
X = dataset[:,0:27]
Y = dataset[:,27]

# set missing values to 0
X[X == '?'] = numpy.nan

# convert to numeric
X = X.astype('float32')

# impute missing values as the mean
imputer = Imputer()
imputed_x = imputer.fit_transform(X)

# encode Y class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)

# split data into train and test sets
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = train_test_split(imputed_x, label_encoded_y, test_size=test_size, random_state=seed)

# fit model no training data
model = XGBClassifier()
model.fit(X_train, y_train)
print(model)

# make predictions for test data
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]

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

运行这个例子,我们看到的结果相当于将值固定为1(1)。这表明,至少在这种情况下,我们最好将缺失的值标记为0(0),而不是有效值(1)或输入值。

Accuracy: 79.80%

当您的数据缺少值时,尝试这两种方法(自动处理和输入)是一个很好的教训。

总结

在这篇文章中,您发现了如何使用Python中的XGBoost为梯度增强准备机器学习数据。

具体来说,你学会了:

如何使用标签编码为二进制分类准备字符串类值。
如何准备分类输入变量使用一个热编码模型作为二进制变量。
XGBoost如何自动处理丢失的数据,以及如何标记和输入丢失的值。

原文出处

猜你喜欢

转载自blog.csdn.net/liuzonghao88/article/details/88895986