XGBoost 因其速度和性能而成为梯度提升的一种流行实现。
在内部,XGBoost 模型将所有问题表示为回归预测建模问题,只接受数值作为输入。如果您的数据格式不同,则必须将其准备成预期的格式。
在这篇文章中,您将发现如何为在 Python 中使用 XGBoost 库进行梯度提升准备数据。
阅读本文后,您将了解
- 如何对用于分类的字符串输出变量进行编码。
- 如何使用独热编码准备分类输入变量。
- 如何使用 XGBoost 自动处理缺失数据。
通过我的新书《XGBoost With Python》启动您的项目,其中包括所有示例的分步教程和 Python 源代码文件。
让我们开始吧。
- 2016 年 9 月更新:我更正了输入示例中的几个小错字。
- 2017 年 1 月更新:已更新以反映 scikit-learn API 0.18.1 版本中的更改。
- 2017 年 1 月更新:更新了乳腺癌示例,将输入数据转换为字符串。
- 2019 年 10 月更新:更新了 OneHotEncoder 的用法以抑制警告。
- 2019 年 12 月更新:更新了示例,修复了多分类示例中 API 用法中的错误。
- 2020 年 5 月更新:更新为在新的 v0.22 API 中使用 SimpleImputer。

在 Python 中为 XGBoost 的梯度提升准备数据
图片由 Ed Dunens 拍摄,保留部分权利。
在 Python 中使用 XGBoost 需要帮助吗?
参加我的免费 7 天电子邮件课程,探索 xgboost(含示例代码)。
立即点击注册,还将免费获得本课程的 PDF 电子书版本。
标签编码字符串类值
鸢尾花分类问题就是一个具有字符串类值的例子。
这是一个预测问题,给定鸢尾花的厘米测量值,任务是预测给定花朵属于哪个物种。
下载数据集并将其放在当前工作目录中,文件名为“iris.csv”。
以下是原始数据集的示例。
1 2 3 4 5 6 |
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 轻松地将字符串值转换为整数值。三个类值(Iris-setosa、Iris-versicolor、Iris-virginica)被映射到整数值(0、1、2)。
1 2 3 4 |
# 将字符串类值编码为整数 label_encoder = LabelEncoder() label_encoder = label_encoder.fit(Y) label_encoded_y = label_encoder.transform(Y) |
我们将标签编码器保存为单独的对象,以便可以使用相同的编码方案转换训练数据集以及后来的测试和验证数据集。
下面是一个完整的示例,演示如何加载鸢尾花数据集。请注意,为了处理字符串类值,使用了 Pandas 来加载数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# 多分类 import pandas import xgboost from sklearn import model_selection from sklearn.metrics import accuracy_score 从 sklearn.preprocessing 导入 LabelEncoder # 加载数据 data = pandas.read_csv('iris.csv', header=None) dataset = data.values # 将数据拆分为 X 和 y X = dataset[:,0:4] Y = dataset[:,4] # 将字符串类值编码为整数 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 = model_selection.train_test_split(X, label_encoded_y, test_size=test_size, random_state=seed) # 拟合模型,无训练数据 model = xgboost.XGBClassifier() model.fit(X_train, y_train) print(model) # 对测试数据进行预测 y_pred = model.predict(X_test) predictions = [round(value) for value in y_pred] # 评估预测 accuracy = accuracy_score(y_test, predictions) print("Accuracy: %.2f%%" % (accuracy * 100.0)) |
注意:鉴于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
运行示例会产生以下输出。
1 2 3 4 5 6 |
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) 准确率:92.00% |
请注意,XGBoost 模型配置为自动使用 multi:softprob 目标(softmax 损失函数的变体)对多分类问题进行建模,以模拟类别概率。这表明在内部,输出类别会自动转换为独热编码类型。
独热编码分类数据
有些数据集只包含分类数据,例如乳腺癌数据集。
该数据集描述了乳腺癌活检的技术细节,预测任务是预测患者是否复发癌症。
下载数据集并将其放置在当前工作目录中,文件名为“breast-cancer.csv”。
以下是原始数据集的示例。
1 2 3 4 5 6 |
'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 将字符串类值转换为整数值来建模预测。例如
1 2 3 4 |
# 将字符串类值编码为整数 label_encoder = LabelEncoder() label_encoder = label_encoder.fit(Y) label_encoded_y = label_encoder.transform(Y) |
我们可以在 X 中的每个输入特征上使用这种相同的方法,但这只是一个起点。
1 2 3 4 5 6 7 8 |
# 将字符串输入值编码为整数 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 可能假设每个输入变量的编码整数值具有序数关系。例如,将“左上”编码为 0,“左下”编码为 1 的乳腺象限变量具有有意义的整数关系。在这种情况下,此假设是不正确的。
相反,我们必须将这些整数值映射到新的二元变量上,每个分类值对应一个新变量。
例如,乳腺象限变量具有以下值:
1 2 3 4 5 |
左上 左下 右上 右下 中央 |
我们可以将其建模为 5 个二元变量,如下所示:
1 2 3 4 5 6 |
左上,左下,右上,右下,中央 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 的特征向量。
1 |
feature = feature.reshape(X.shape[0], 1) |
然后我们可以创建 OneHotEncoder 并对特征数组进行编码。
1 2 |
onehot_encoder = OneHotEncoder(sparse=False, categories='auto') feature = onehot_encoder.fit_transform(feature) |
最后,我们可以通过逐一连接独热编码特征,将其作为新列(axis=2)添加到输入数据集中。我们最终得到一个包含 43 个二元输入变量的输入向量。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 将字符串输入值编码为整数 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, categories='auto') 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) |
理想情况下,我们可以尝试不独热编码某些输入属性,因为我们可以用明确的序数关系对其进行编码,例如,第一个年龄列,其值如“40-49”和“50-59”。如果您有兴趣扩展此示例,这作为一项练习留给您。
下面是带有标签和独热编码输入变量以及标签编码输出变量的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# 二元分类,乳腺癌数据集,标签和独热编码 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 # 加载数据 data = read_csv('breast-cancer.csv', header=None) dataset = data.values # 将数据拆分为 X 和 y X = dataset[:,0:9] X = X.astype(str) Y = dataset[:,9] # 将字符串输入值编码为整数 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, categories='auto') 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) # 将字符串类值编码为整数 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 = train_test_split(encoded_x, label_encoded_y, test_size=test_size, random_state=seed) # 拟合模型,无训练数据 model = XGBClassifier() model.fit(X_train, y_train) print(model) # 对测试数据进行预测 y_pred = model.predict(X_test) predictions = [round(value) for value in y_pred] # 评估预测 accuracy = accuracy_score(y_test, predictions) print("Accuracy: %.2f%%" % (accuracy * 100.0)) |
注意:鉴于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
运行此示例,我们得到以下输出。
1 2 3 4 5 6 7 |
('X 形状::', (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) 准确率:71.58% |
我们再次看到 XGBoost 框架自动选择了“binary:logistic”目标,这是此二元分类问题的正确目标。
支持缺失数据
XGBoost 可以自动学习如何最好地处理缺失数据。
事实上,XGBoost 旨在处理稀疏数据,例如上一节中的独热编码数据,缺失数据的处理方式与稀疏值或零值的处理方式相同,都是通过最小化损失函数来实现的。
有关 XGBoost 中缺失值处理方式的技术细节的更多信息,请参阅论文 XGBoost: A Scalable Tree Boosting System 第 3.4 节“Sparsity-aware Split Finding”。
马疝气数据集是演示此功能的一个很好的例子,因为它包含大量缺失数据,大约 30%。
下载数据集并将其放置在当前工作目录中,文件名为“horse-colic.csv”。
以下是原始数据集的示例。
1 2 3 4 5 6 |
2 1 530101 38.50 66 28 3 3 ? 2 5 4 4 ? ? ? 3 5 45.00 8.40 ? ? 2 2 11300 00000 00000 2 1 1 534817 39.2 88 20 ? ? 4 1 3 4 2 ? ? ? 4 2 50 85 2 2 3 2 02208 00000 00000 2 2 1 530334 38.30 40 24 1 1 3 1 3 3 1 ? ? ? 1 1 33.00 6.70 ? ? 1 2 00000 00000 00000 1 1 9 5290409 39.10 164 84 4 1 6 2 2 4 4 1 2 5.00 3 ? 48.00 7.20 3 5.30 2 1 02208 00000 00000 1 2 1 530255 37.30 104 35 ? ? 6 2 ? ? ? ? ? ? ? ? 74.00 7.40 ? ? 2 2 04300 00000 00000 2 ... |
这些值由空格分隔,我们可以使用 Pandas 函数 read_csv 轻松加载它。
1 |
dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None) |
加载后,我们可以看到缺失数据用问号字符('?')标记。我们可以将这些缺失值更改为 XGBoost 期望的稀疏值,即零(0)。
1 2 |
# 将缺失值设置为 0 X[X == '?'] = 0 |
由于缺失数据被标记为字符串,因此所有带有缺失数据的列都被加载为字符串数据类型。现在我们可以将整个输入数据集转换为数值。
1 2 |
# 转换为数值 X = X.astype('float32') |
最后,这是一个二元分类问题,尽管类别值用整数 1 和 2 标记。我们在 XGBoost 中将二元分类问题建模为逻辑 0 和 1 值。我们可以使用 LabelEncoder 轻松地将 Y 数据集转换为 0 和 1 整数,就像我们在鸢尾花示例中所做的那样。
1 2 3 4 |
# 将 Y 类值编码为整数 label_encoder = LabelEncoder() label_encoder = label_encoder.fit(Y) label_encoded_y = label_encoder.transform(Y) |
完整的代码清单如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# 二元分类,缺失数据 from pandas import read_csv from xgboost import XGBClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score 从 sklearn.preprocessing 导入 LabelEncoder # 加载数据 dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None) dataset = dataframe.values # 将数据拆分为 X 和 y X = dataset[:,0:27] Y = dataset[:,27] # 将缺失值设置为 0 X[X == '?'] = 0 # 转换为数值 X = X.astype('float32') # 将 Y 类值编码为整数 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 = train_test_split(X, label_encoded_y, test_size=test_size, random_state=seed) # 拟合模型,无训练数据 model = XGBClassifier() model.fit(X_train, y_train) print(model) # 对测试数据进行预测 y_pred = model.predict(X_test) predictions = [round(value) for value in y_pred] # 评估预测 accuracy = accuracy_score(y_test, predictions) print("Accuracy: %.2f%%" % (accuracy * 100.0)) |
注意:鉴于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
运行此示例将产生以下输出。
1 2 3 4 5 6 |
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) 准确率:83.84% |
我们可以通过将缺失值标记为非零值(例如 1)来探究 XGBoost 自动处理缺失值的影响。
1 |
X[X == '?'] = 1 |
注意:鉴于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
重新运行示例表明模型准确率下降。
1 |
准确率:79.80% |
我们还可以用特定值填充缺失数据。
通常使用列的平均值或中位数。我们可以使用 scikit-learn 的 SimpleImputer 类轻松填充缺失数据。
1 2 3 |
# 将缺失值作为均值进行填充 imputer = SimpleImputer() imputed_x = imputer.fit_transform(X) |
下面是缺失数据用每列的平均值填充的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# 二元分类,缺失数据,用均值填充 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.impute import SimpleImputer # 加载数据 dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None) dataset = dataframe.values # 将数据拆分为 X 和 y X = dataset[:,0:27] Y = dataset[:,27] # 将缺失值设置为 0 X[X == '?'] = numpy.nan # 转换为数值 X = X.astype('float32') # 将缺失值作为均值进行填充 imputer = SimpleImputer() imputed_x = imputer.fit_transform(X) # 将 Y 类值编码为整数 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 = train_test_split(imputed_x, label_encoded_y, test_size=test_size, random_state=seed) # 拟合模型,无训练数据 model = XGBClassifier() model.fit(X_train, y_train) print(model) # 对测试数据进行预测 y_pred = model.predict(X_test) predictions = [round(value) for value in y_pred] # 评估预测 accuracy = accuracy_score(y_test, predictions) print("Accuracy: %.2f%%" % (accuracy * 100.0)) |
注意:鉴于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
运行此示例,我们看到的结果与将值固定为一(1)的结果等效。这表明,至少在这种情况下,我们最好将缺失值标记为一个不同的值零(0),而不是一个有效值(1)或一个填充值。
1 |
准确率:79.80% |
这是一个很好的经验教训,当您有缺失值时,可以尝试两种方法(自动处理和填充)。
总结
在这篇文章中,您发现了如何为 Python 中使用 XGBoost 进行梯度提升准备机器学习数据。
具体来说,你学到了:
- 如何使用标签编码准备二元分类的字符串类值。
- 如何使用独热编码准备分类输入变量,将其建模为二元变量。
- XGBoost 如何自动处理缺失数据以及如何标记和填充缺失值。
你有什么问题吗?
在评论中提出您的问题,我将尽力回答。
你好 Jason,最后一个示例的训练数据应该是 imputed_x,但你使用了包含缺失数据的原始 X。我尝试使用 imputed_x 数据,准确率达到 79.8%
谢谢 Ralph。已修复。
嗨,Jason,
感谢提供如此有用的信息教程!我对您在乳腺癌数据集上应用的标签编码和独热编码有一个问题。
您对整个数据集执行标签编码和独热编码,然后将其拆分为训练集和测试集。这样可以确保所有数据都以相同的编码配置进行转换。
然而,如果我们有新的未见过的原始数据集类型数据,我们如何确保标签编码和独热编码仍然以相同的方式转换未见过的数据?我们是否需要保存编码器以便处理未见数据?
提前感谢!
好问题,Qichang。
我会在训练数据上准备编码,存储映射(或 pickle 对象),然后将编码重新用于测试数据。
这意味着我们必须确信训练数据代表我们将来可能需要预测的数据。
这到底该怎么做呢?
哪一部分让你感到困扰?
嗨,Jason,
我有一个问题。
如何在训练数据上准备编码,存储映射?
提前感谢!
您可以直接保存 python 对象,例如,您可以使用 pickle 库。
嗨,Jason,
谢谢您的回复。您能给我一个在训练数据上准备编码的例子吗?我仍然不知道该怎么做。例如,有一个名为 color 的类别列,其值为“red”、“blue”和“black”,如何进行 pickle?
此外,我还有一个问题。当我使用 xgboost sklearn api 训练 xgboost 模型时,例如,
n_estimators = [50, 100, 150, 200]
max_depth = [2, 4, 6, 8]
k_neighbors = [7]
params = dict(clf__max_depth=max_depth, clf__n_estimators=n_estimators,
over__k_neighbors=k_neighbors)
over = select_smote.selectSmoteType(build_model_util.smote_type)
under = RandomUnderSampler(sampling_strategy=0.5)
if classifier_strategy == ‘gbc’
clf_estimator = GradientBoostingClassifier()
rfe = RFECV(estimator=clf_estimator)
clf = XGBClassifier(objective=’binary:logistic’, use_label_encoder=False)
pipeline = Pipeline(steps=[(‘over’, over), (‘under’, under), (‘s’, rfe), (‘clf’, clf)])
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=1)
CV_result = build_model_util.train_estimator(
pipeline, params,
X, y,
skf, scoring=’roc_auc’)
发生了警告:与目标“binary:logistic”一起使用的默认评估指标从“error”更改为“logloss”。如果您想恢复旧行为,请明确设置 eval_metric。为什么?
我期待您的回复。提前感谢。
您可以安全地忽略该警告。
您还可以在这里了解更多关于 xgboost 损失函数的信息:
https://machinelearning.org.cn/xgboost-loss-functions/
谢谢 Jason 的及时回复。
除了这种数据转换,我们是否需要考虑在将输入变量传递给 XGBoost 之前对其进行缩放或标准化?我们知道这通常会为 SVM 带来更好的结果,尤其是带有核函数的情况下。
通常不需要缩放。您可能会通过展开单变量分布来突出特定特征(例如,使用平方、对数、平方根等)而获得一些好处。
你好 Jason,你的文章非常有帮助。非常感谢!
我有一个关于如何处理 XGBoost 连续预测变量的“默认”值的问题。例如,假设属性 X 可能取连续值(例如在 1-100 范围内)。但是某些记录可能具有一些默认值(例如 9999),这表示某些客户群体的预测变量 X 无法计算或不可用。我们是否可以直接将预测变量 X 用作 XGBoost 模型的输入变量?或者,我们应该对 X 进行一些数据处理吗?如果是这样,那会是什么?
不胜感激
好问题,JChen。
我会首先尝试按原样建模数据。XGBoost 会解决这个问题。
然后,您可以尝试一些特征工程(也许为这类情况添加一个新标志变量),看看是否可以进一步列出性能。
谢谢您的回复!这很有帮助
很高兴听到这个消息,JChen。
在您的缺失数据部分,您将“?”替换为 0。但是您在定义 XGBClassifier 模型时没有提到在您的数据集中将 0 视为缺失值。而且默认情况下,“missing”参数值为 None,这等同于将 NaN 视为缺失值。所以我认为您的模型没有处理缺失值。
谢谢你的提醒,Sargam。
当源数据中的缺失值为“nan”而不是将它们设置为零 (0) 时,我获得了更好的平均准确率结果。有人能更详细地解释一下吗?在这里和其他资料中,我读到可以将 nan 值设置为零,但我想知道“稀疏感知分裂查找”算法是否将其识别为 nan 值?
是的,xgboost 会将 nan 建模为一个值。
嗨,Jason,
我从另一个页面来到这里:IRIS,
来源: https://machinelearning.org.cn/multi-class-classification-tutorial-keras-deep-learning-library/#comment-396630
在该网页中,您的代码以 96.6% 的准确率对 IRIS 进行分类,这非常好。
在评论区,您告诉这位朋友(Abhilash Menon)使用 XGBoost 进行梯度提升。
这就是本教程。这里也进行 IRIS 分类。
嗯,我不明白。
在这里,使用 XGBoost 进行梯度提升的 IRIS 分类准确率仅为 79% 或 83%。
那我们为什么要使用 XGBoost 进行梯度提升呢?
准确率太低了。
本教程演示了如何使用该算法。
作为机器学习从业者,您的任务是针对您的问题尝试多种算法,看看哪种效果最好。
请看这篇文章
https://machinelearning.org.cn/a-data-driven-approach-to-machine-learning/
你好,Jason。首先,我要感谢您提供的精彩材料。我一直在尝试 XGBoost 算法,它在我的电脑上似乎表现异常。第一个鸢尾花物种数据集我得到了 21.2% 的分数。现在这个乳腺癌数据集我的准确率是 2.1%。我真的不知道出了什么问题,请您帮帮我
听到这个消息我很抱歉。也许库没有正确安装?
我建议在 stackoverflow 甚至 xgboost 用户组上发布一个问题。
告诉我进展如何。
你好 Jason,我知道对于回归模型,我们应该删除第一个虚拟变量以避免“虚拟变量陷阱”。在这种情况下,我没有看到您这样做。这是因为虚拟变量陷阱只适用于线性回归模型而不适用于梯度提升算法吗?
我相信虚拟变量陷阱适用于线性模型和变量多重共线的情况。
你好,Jason。
如果我有一个带有 NA 的字符串列,会发生什么?
我应该先用 0 代替 NA,然后再使用 OneHotEncounter 吗?
XGBoost 可以处理 NA。无需做任何事情。
如果您想进行插补或类似操作,请参阅此帖子
https://machinelearning.org.cn/handle-missing-data-python/
谢谢你,Jason。
非常有用的文章。
我遇到了 CatBoost 库,它具有有趣的选项来配置分类变量。也许,这会对您的旅程有所帮助。
谢谢,你有链接吗?
关于“参数调优”部分的链接: https://tech.yandex.com/catboost/doc/dg/concepts/parameter-tuning-docpage/#choosing-cat-features
您可以在此网站上看到许多有趣的资料。
+ 如果您感兴趣,还有其他基于树集成模型的有趣库。例如,“TPOT”(基于树的管道优化技术)中的遗传算法实现。
谢谢亚历山大。
亲爱的 Jason,
感谢您提供的精彩教程!
不幸的是,我无法获得“datasets-uci-breast-cancer.csv”——看起来它已从网站上删除。我在网上搜索但找不到类似的文件来尝试您的示例。
您能把这个文件发给我吗?
非常感谢
Dani
就在这里
https://gist.github.com/jbrownlee/ceb34e3014be83da5f8a255c75b026d7
非常感谢!
在将特征传递给 XGBoost 之前,我们需要处理异常值吗?
也许吧。尝试有无异常值去除,并比较模型技能。
嗨,Jason,
感谢您花时间详细说明这一切。在阅读您的教程之前,我使用了 DataCamp 的 XGBoost 课程作为指导,其中他们使用两个步骤来编码分类变量:LabelEncoder,然后是 OneHotEncoder。
因此,一个有 5 个级别的分类变量被转换为 0-4 的值,然后这些值被独热编码为 5 列。
我对特征重要性很感兴趣,所以 xgb.plot_importance 是一个很好的工具。然而,这些特征与它们的原始状态相距两步。
您将如何撤销这种两步编码以获取原始变量名称?
独热编码可以通过每个向量上的 argmax() 反转。
标签编码在 sklearn 中有一个 inverse_transform() 函数。
您能讨论一下这个及其在 XGBoost 中的适用性吗?
http://roamanalytics.com/2016/10/28/are-categorical-variables-getting-lost-in-your-random-forests/
谢谢您的链接。
Jason,您对在多标签分类问题上应用 XGBoost 有什么想法吗?我只需要您在数据准备方面提供一些帮助。我从 stackoverflow 上找到的一些建议是,通过为每个具有 k 个正确/正标签的数据点创建 k 个副本,稍微处理一下数据。然后您可以通过这种方式解决一个更简单的多类问题……任何建议都将不胜感激!
抱歉,我没有多标签示例。
已经一年了,你现在有了吗?
我这里有一个多标签分类的例子:
https://machinelearning.org.cn/how-to-develop-a-convolutional-neural-network-to-classify-satellite-photos-of-the-amazon-rainforest/
嗨,我有一个问题。如果数据集是多热类别、独热类别、连续和字符串类型的混合,这意味着所有列的形状都不同,我们应该怎么做?Sklearn 机器学习模块如 SVM 似乎无法处理这种情况,我必须将所有列重塑为相同的形状。有没有更好的解决方案?谢谢!
我建议将所有数据转换为数字。例如,一个“样本”就是一个数字向量。
你好 🙂 精彩的文章!!!我能问一个问题吗?例如,如果用于预测的新数据具有训练数据中不存在的分类值,如何解决?我的意思是例如特征“颜色”。训练数据中“颜色”的值是白色、绿色、黑色。所以独热编码器创建了三列。但是如果用于预测的新数据中“颜色”有一个额外的值——橙色——那么编码器会创建 4 列。当我使用 XGboostClassifier 时,我得到了一些关于不匹配的错误。
您必须确保训练集具有代表性。
或者,在了解所有可能值的情况下准备数据。
所以你的意思是为未来的分类值(“橙色”)在训练集中添加一个 0 列吗?并且包含该零列不会改变没有该分类值的测试数据的预测能力?也就是说,我想知道这是否正确:通过添加额外的列只是为了准备一个未见的类别会使模型更具包容性/通用性。
您有多种选择,例如在表示中留出空间,或者将未见值标记为 0,或者使用不同的表示等。选择最有效且符合您项目要求的方法。
旧回复,但对于未来需要的人:
OneHotEncoder
具有categories
参数,该参数将保存所有“预期类别”。因此,您需要创建所有预期类别的列表以输入到此(列的元数据)。这与 Pandas Categories 数据类型配合得很好。在您的每个“对象”类型列上定义类别。在此 plot_stack_predictors 页面上有一个很好的示例 https://scikit-learn.cn/stable/auto_examples/ensemble/plot_stack_predictors.html?highlight=categorical%20encoding谢谢!
您还可以将其配置为忽略训练期间未见的标签。
你知道如果我有一个带有 NA 的字符串列会发生什么吗?
如果整个列都是 NA,那么您可以安全地将其删除。
我非常喜欢阅读您的实践文章,Jason。感谢您的分享。
如何更好地编码数值——例如年龄组、收入范围等——以捕捉序数关系?我觉得通过独热编码这些属性,我们正在丢弃一些有用的信息。
谢谢。
很好的问题!
第一步是整数编码——看看您会得到什么结果。
第二步是嵌入——我期望会得到更好的结果。
将这两种方法与独热编码进行对比。
您所说的嵌入具体指什么?能给我一些参考文献吗?
是的,也许从这里开始
https://machinelearning.org.cn/?s=embedding&post_type=post&submit=搜索
与下面的方法类似吗?
https://medium.com/@krishnakalyan3/a-gentle-introduction-to-embedding-567d8738372b
是的。
我的数据集中有 IP 地址字段。我该如何编码它?您能用一个例子解释一下吗?或者给我一些可以找到这类例子的链接。
先谢谢您了。
我没有示例。
也许可以查阅文献,了解 IP 地址的常见编码方案?
你好,Jason
在独热编码分类数据部分,我只使用了 labelencoder 编码方法进行输入 x,然后训练模型,我得到了与使用独热编码方法相同的性能。
是不是因为 labelencoder 方法在这里刚好表现不错?
是的,对于单个变量来说,这很好。它们的作用相同。
嗨,Jason,
机器学习模型中的准确率分数只适用于分类吗?我可以使用均方误差、平均绝对误差或 R2 误差进行分类吗?
感谢您的建议。
是的。准确率仅用于分类。
MSE、MAE、R2 仅用于回归。
谢谢您的回复。
为什么在分类中我们追求准确率,而在回归中我们追求误差?
看这里
https://machinelearning.org.cn/faq/single-faq/what-is-the-difference-between-classification-and-regression
嗨,Jason,
对于二元分类,乳腺癌示例,很难理解为什么您需要执行以下操作:
“... 我们必须将特征数组转换为一个二维 NumPy 数组,其中每个整数值都是一个长度为 1 的特征向量。”
feature = label_encoder.fit_transform(X[:,i])
feature = feature.reshape(X.shape[0], 1)
希望能用更简单的通俗语言启发我。谢谢。
在该示例中,我们分别编码每个特征。
使用新的 OrdinalEncoder 会更容易。
为什么在分别编码每个特征后仍然需要重塑它?
为了满足 API 的期望。
您如何知道每个特征需要 API 的二维 NumPy 数组?
谢谢 Jason。
一行一列。
嗨,Jason,
从 Pandas 数据框读取数据后,必须将其转换为 NumPy 数组才能拟合模型,是吗?
如果在从数据框读取数据后,在缩放特征时进行拟合和转换,这是否意味着数据会自动转换为数组?
请原谅我,我是机器学习新手。感谢您的建议。
是的。
是的。
您好,我测试了您另一篇文章中的代码 (https://machinelearning.org.cn/xgboost-for-regression/),xgboost 可以直接在 pandas 数据框上拟合,而无需转换为 numpy 数组。
我写了
X, y = dataframe.iloc[:,:-1], dataframe.iloc[:,-1:]
而不是
data = dataframe.values
X, y = data[:, :-1], data[:, -1]
我不知道它是否隐式转换了?谢谢您。
如果我错了请纠正我
1. 作为一种基于树的算法,XGBoost 不需要缩放。
2. 我在带有原始分类变量的数据和带有对应于每个分类值的虚拟变量的相同数据上拟合了梯度提升决策树,并获得了相似的结果。是否可以只使用分类版本(无论是数字 0、1、99 还是字符串值“A0”、“A1”、“A99”)而无需独热编码?
独热编码不是缩放。
在使用 xgboost 之前,您可能需要使用序数或独热编码来准备分类输入。
我说的缩放是指 minmaxscaler() 和 standardscaler()。
如果缩放不重要,为什么将类别分解成虚拟变量很重要?我确实看到只使用原始分类变量时结果更好。在我看来,基于树的分类器可以很好地映射任何值,无论是字符串还是数值。想听听更多关于这个话题的意见。
因为模型不能直接操作标签,它需要输入是数字。
可以说尝试将分类变量映射到数字类别,或者映射到与类别数量相同的 0/1 虚拟变量,然后看看哪个版本的分类变量能获得更好的结果,这样公平吗?
你可以试试。
当目标是连续的时,如何预测准确性?
你不能,请看这里
https://machinelearning.org.cn/faq/single-faq/how-do-i-calculate-accuracy-for-regression
你好 Jason,对于马绞痛数据集的例子,你为什么没有对一些输入进行独热编码,比如手术或毛细血管再充盈时间。
听起来是个很棒的扩展!试试看是否有帮助。
当像文章中那样开发一个模型,并且我想在新的传入数据上部署和预测时,我们知道传入数据没有经过与训练/测试数据相同的嵌入过程。如何才能保留该嵌入过程并将其应用于新的传入数据?也许可以使用管道,如果可能的话,你能指出一个详细的例子吗?谢谢。
新数据必须以与训练数据相同的方式进行准备。
通常很容易通过保存数据准备对象以及模型,或保存整个管道对象来实现。
XGBoost 对数据集大小有限制吗?使用 XGBoost 需要最少多少行数据?对于行数和特征数的比率有没有经验法则?当然,你需要比特征更多的行,但你遵循的经验法则是什么?
好问题,没有。
更多数据通常更好。
https://machinelearning.org.cn/faq/single-faq/how-much-training-data-do-i-need
你好 Jason。你的教程很棒,非常有帮助。
我有一个数据集,其中有几列包含混合整数和分类类型数据。大部分值是整数,有些是像“Q”或“X”这样的代码。在输入 XGBoost 之前,如何最好地预处理这些列?
谢谢。
谢谢!
也许映射到一个新的整数序列(整数/序数编码)。
当你在整个数据集而不是训练集上进行均值插补时,是否存在数据泄露的风险?
是的,理想情况下,我们应该在训练集上拟合变换,然后将其应用于训练集和测试集。
更多细节在此
https://machinelearning.org.cn/data-preparation-without-data-leakage/
我从未见过有人先标签编码,然后独热编码。你能解释一下或分享一些补充材料来理解这种特征工程方法的优缺点吗?特别是潜在的内存效率?
我的理解是,这将导致标签编码固有地强制建立序数关系,并导致 xgboost 尝试根据其各自的基数(将 0.9-1 和 0.01-0.1 分组,因为它们彼此接近)将观察值分组在一起。如果随后进行独热编码,这种约束会自行纠正吗?
我目前正在使用一个非常大的数据集的 xgboost 模型。独热编码后,我的数据集是 400kx2k,进行 HP 网格搜索会导致由于内存分配错误而彻底失败(机器有 48gb RAM)。
现在通常可以直接进行独热编码。以前(几年前)必须先进行整数/标签编码,然后再进行独热编码。
独热编码在类别和行数很多时会变得低效,可以改用稀疏表示,或者使用嵌入、哈希等替代编码。
https://machinelearning.org.cn/faq/single-faq/how-do-i-handle-a-large-number-of-categories
感谢 Jason 的这篇文章,它非常有帮助!
我有一个回归案例,其中一些特征是连续的,取值范围从 1 到 2,000,000,而另一些是二分的(1/0)或序数,取值非常具体(0,1,2),我观察到 XGBoost 对后两种特征的使用远少于第一种。
有没有办法更好地利用后两种特征,以便在模型中加强它们的使用?
感谢您的回复!
不客气。
模型会选择对预测最有用的,无需强迫模型使用无用的变量。
你可以尝试提供具有不同变换的变量副本,看看模型是否能找到更好的使用它们的方法。
我有一个关于对象保存的问题,如果有一个名为“颜色”的分类列,它的值是“红色”、“蓝色”和“黑色”,如何将其序列化?感谢您阅读我的问题。
也许这能帮你保存数据。
https://machinelearning.org.cn/how-to-save-a-numpy-array-to-file-for-machine-learning/
你好 Jason,感谢你出色的工作,它对我帮助很大。
我的数据有很多名义变量和缺失变量。
我想利用 XGboost 自身的缺失值特征,但是 Sklearn 的 Onehot 编码器无法处理缺失值。
—— 一个技巧是跟踪缺失值的行号,然后强制将列设为 nan。
—— 我也考虑过将缺失值作为“-1”进行插补,并给它们自己的类别。也就是说,一个二元变量将有三个级别。但这会大大增加变量计数。
你会怎么处理这个问题?
不客气。
也许你可以直接使用 XGBoost API?
也许你可以按照你建议的,将缺失值标记为特殊值(-1)?当一个值缺失时,你已经有3个值了。
嗨,Jason,
感谢这个例子。我尝试在没有独热编码(或标签编码)的情况下使用 XGBoost,它对 Iris 数据集有效。这就是我现在困惑的地方。你提到“XGBoost 无法直接对这个问题建模,因为它要求输出变量是数值型的。” 但它对我来说是有效的。我是否遗漏了什么?请澄清。
也许你的数据有数字输出?
也许 API 改变了?
也许模型需要数字输入而不是数字输出?
我正在使用 XGboost 训练模型并将其存储在 blob 中,我尝试对另一个数据集进行预测时收到以下错误。
ValueError: feature_names mismatch
尽管我的特征是相同的。您能给些建议吗?
抱歉,我没有见过那个错误。也许你可以尝试将你的代码和错误发布到 stackoverflow.com
嗨,Jason,
GBDT 能自动处理缺失数据(nan 值)吗?
我期待您的回复。提前感谢。
XGBoost 的实现可以直接包含缺失数据。
嗨,Jason,
我一直在期待你的非常有用的博客。
“独热编码分类数据”部分中第 7 个代码框的最后一行如下。
# 将字符串输入值编码为整数
:
encoded_x = encoded_x.reshape(X.shape[0], X.shape[1])
我想知道是不是这样的。
encoded_x = encoded_x.T
谢谢你,
我相信代码是正确的。你为什么要 encodedd_x.T?
嘿,我有一个关于xgboost的问题。
渐近地(如果我们有一个非常大的样本),如果我们用 OLS 拟合线性模型,
拟合价格还是对数价格有关系吗?如果
我们用 XGBoost 拟合一个非常灵活的模型呢?
OLS 在价格和对数(价格)之间有很大关系,因为对数是非线性的。
我相信 XGBoost 不会关心是否是对数,因为它是一个决策树,而且对数是一个单调函数。
在 OLS 回归中,非常大的样本量与小的样本量如何影响使用对数价格与非对数价格的预测?
可能与是否取对数无关,而更多地与模型中的参数数量有关。通常的经验法则是获得模型参数数量的 30 倍。
非常感谢这篇出色的教程。我的数据集目前包含独热编码的分类变量(因为这些变量有许多相关值),以及值包含在单个列中的二元变量(即,一个不包含缺失值的列,其中 0=否,1=是)。在使用 XGBoost 时,我是否可以用单个列表示二元变量,而不是使用独热编码?如果是这样,XGBoost 会将该列中的零视为缺失值吗?
你好 Erin……以下讨论可能会让你感兴趣
https://www.quora.com/How-does-XGBoost-treat-missing-values-during-training-and-prediction
嗨 Jason
我有一些2D数据(纬度、经度、var1、var2……)。我想在特定网格定义点上预测变量值。XGBoost 可以做到吗?你有例子吗?
谢谢
你好 Dzevad……你可能会发现以下内容很有趣
https://towardsdatascience.com/geopandas-hands-on-building-geospatial-machine-learning-pipeline-9ea8ae276a15