在分类列上训练 xgboost 时遇到问题

Posted

技术标签:

【中文标题】在分类列上训练 xgboost 时遇到问题【英文标题】:Trouble training xgboost on categorical column 【发布时间】:2019-09-28 23:49:14 【问题描述】:

我正在尝试运行 Python 笔记本 (link)。在 [446] 下面的行:作者训练 XGBoost,我收到一个错误

ValueError:数据的 DataFrame.dtypes 必须是 int、float 或 bool。 没想到StateHoliday、Assortment字段中的数据类型

# XGB with xgboost library
dtrain = xgb.DMatrix(X_train[predictors], y_train)
dtest = xgb.DMatrix(X_test[predictors], y_test)

watchlist = [(dtrain, 'train'), (dtest, 'test')]

xgb_model = xgb.train(params, dtrain, 300, evals = watchlist,
                      early_stopping_rounds = 50, feval = rmspe_xg, verbose_eval = True)

这是用于测试的最少代码

import pickle
import numpy as np
import xgboost as xgb
from sklearn.model_selection import train_test_split

with open('train_store', 'rb') as f:
    train_store = pickle.load(f)

train_store.shape

predictors = ['Store', 'DayOfWeek', 'Open', 'Promo', 'StateHoliday', 'SchoolHoliday', 'Year', 'Month', 'Day', 
              'WeekOfYear', 'StoreType', 'Assortment', 'CompetitionDistance', 'CompetitionOpenSinceMonth', 
              'CompetitionOpenSinceYear', 'Promo2', 'Promo2SinceWeek', 'Promo2SinceYear', 'CompetitionOpen', 
              'PromoOpen']

y = np.log(train_store.Sales) # log transformation of Sales
X = train_store

# split the data into train/test set
X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size = 0.3, # 30% for the evaluation set
                                                    random_state = 42)

# base parameters
params = 
    'booster': 'gbtree', 
    'objective': 'reg:linear', # regression task
    'subsample': 0.8,          # 80% of data to grow trees and prevent overfitting
    'colsample_bytree': 0.85,  # 85% of features used
    'eta': 0.1, 
    'max_depth': 10, 
    'seed': 42 # for reproducible results

num_round = 60 # default 300

dtrain = xgb.DMatrix(X_train[predictors], y_train)
dtest  = xgb.DMatrix(X_test[predictors],  y_test)

watchlist = [(dtrain, 'train'), (dtest, 'test')]

xgb_model = xgb.train(params, dtrain, num_round, evals = watchlist,
                      early_stopping_rounds = 50, feval = rmspe_xg, verbose_eval = True)

train_store 数据文件链接:Link 1

【问题讨论】:

这不是最小的,就像在 MCVE 中一样。你的意思是StateHoliday 列是分类的吗?如果是,请在问题中这样说。 【参考方案1】:

我在做罗斯曼销售预测项目时遇到了完全相同的问题。 似乎新版本的 xgboost 不接受 StateHolidayAssortmentStoreType 的数据类型。 您可以使用以下方法检查 Mykhailo Lisovyi 建议的数据类型

print(test_train.dtypes)

你需要在这里用你的 X_train 替换 test_train

你可能会得到

DayOfWeek                      int64
Promo                          int64
StateHoliday                   int64
SchoolHoliday                  int64
StoreType                     object
Assortment                    object
CompetitionDistance          float64
CompetitionOpenSinceMonth    float64
CompetitionOpenSinceYear     float64
Promo2                         int64
Promo2SinceWeek              float64
Promo2SinceYear              float64
Year                           int64
Month                          int64
Day                            int64

object 类型引发的错误。您可以将它们转换为

from sklearn import preprocessing
lbl = preprocessing.LabelEncoder()
test_train['StoreType'] = lbl.fit_transform(test_train['StoreType'].astype(str))
test_train['Assortment'] = lbl.fit_transform(test_train['Assortment'].astype(str))

完成这些步骤后一切都会顺利。

【讨论】:

【参考方案2】:

试试这个

train_store['StateHoliday'] = pd.to_numeric(train_store['StateHoliday'])
train_store['Assortment'] = pd.to_numeric(train_store['Assortment'])

【讨论】:

如果您想在生产中使用经过训练的模型,并且将来需要对测试样本应用相同的编码,则必须使用另一种编码方式,例如scikit智元在他的回答中展示了变形金刚,这样变形金刚可以和模型一起保存。对新数据运行 pd.to_numeric() 可能会导致与您最初在训练期间使用的映射不同【参考方案3】:

正如错误消息所暗示的,xgboost 很不高兴,因为您尝试为其提供未知类型。它说它不能处理分类或日期时间特征。检查StateHoliday, Assortment 特征的类型并以某种方式将它们编码为数字(例如 One-Hot Encoding、标签编码(适用于基于树的模型)或目标编码)

【讨论】:

我检查了数据类型是int 您能否从完整的堆栈跟踪中检查哪个命令导致ValueError 并将该数据帧的.dtypes 转储添加到原始问题中?问题的根源在于 xgboost 不支持的类型。代码中列出了支持的类型:github.com/dmlc/xgboost/blob/…【参考方案4】:

H2O 包中的 XGBoost 版本可以处理分类变量(但不能太多!),但 XGBoost 作为其自己的包似乎不能。

我用 pandas 数据框试过这个,但 xgboost 不喜欢它

categoricals = ['StoreType', ] . # etc.
pdf[categorical] = pdf[categorical].astype('category')

要将 H2O 用于分类,您必须先将字符串转换为分类:

h2odf[categoricals] = h2odf[categoricals].asfactor()

还要注意,h2o 有自己的数据框,与 pandas 不同。

【讨论】:

这是不准确的。 XGBoost 可以自己处理分类。 xgboost.readthedocs.io/en/latest/tutorials/…

以上是关于在分类列上训练 xgboost 时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

关于级联分类器训练过程中遇到的问题

有啥方法可以有效地堆叠/集成用于图像分类的预训练模型?

如何在 2 列上训练 ML 模型以解决分类问题?

无法运行插入符号 xgboost 分类

Xgboost 处理不平衡的分类数据

R语言构建xgboost模型并评估模型(测试集训练集每一轮):误分类率指标(misclassification rate)logloss