转换为 DMatrix 后 XGBoost 在训练和测试特征上的差异

XGBoost difference in train and test features after converting to DMatrix

只是想知道下一个案例怎么可能:

 def fit(self, train, target):
     xgtrain = xgb.DMatrix(train, label=target, missing=np.nan)
     self.model = xgb.train(self.params, xgtrain, self.num_rounds)

我将火车数据集作为 csr_matrix 传递,其中包含 5233 列,在转换为 DMatrix 后,我得到了 5322 个特征。

稍后在预测步骤中,由于上述错误,我得到了一个错误:(

 def predict(self, test):
     if not self.model:
         return -1
     xgtest = xgb.DMatrix(test)
     return self.model.predict(xgtest)

Error: ... training data did not have the following fields: f5232

如何保证将 train/test 数据集正确转换为 DMatrix?

是否有机会在 Python 中使用类似于 R 的东西?

# get same columns for test/train sparse matrixes
col_order <- intersect(colnames(X_train_sparse), colnames(X_test_sparse))
X_train_sparse <- X_train_sparse[,col_order]
X_test_sparse <- X_test_sparse[,col_order]

不幸的是,我的方法不起作用:

def _normalize_columns(self):
    columns = (set(self.xgtest.feature_names) - set(self.xgtrain.feature_names)) | \
          (set(self.xgtrain.feature_names) - set(self.xgtest.feature_names))
    for item in columns:
        if item in self.xgtest.feature_names:
            self.xgtest.feature_names.remove(item)
        else:
            # seems, it's immutable structure and can not add any new item!!!
            self.xgtest.feature_names.append(item) 

one-hot编码后可能会出现这种情况。例如,

ar = np.array([
        [1, 2],
        [1, 0]
])

enc = OneHotEncoder().fit(ar)
ar2 = enc.transform(ar)
b = np.array([[1, 0]])
b2 = enc.transform(b)
xgb_ar = xgb.DMatrix(ar2)
xgb_b = xgb.DMatrix(b2)

print(b2.shape) # (1, 3)
print(xgb_b.num_col()) # 2

因此,当稀疏矩阵中的列全为零时,DMatrix 会删除此列(我认为,因为此列对 XGBoost 没有用)

通常,我会在矩阵中添加一个假行,该行在所有列中都为 1。

另一种可能性是只在训练数据中而不是在测试数据中具有一个特征级别。这种情况主要发生在 post 一个热编码的结果是大矩阵的情况下,每个级别的分类特征都有级别。在您的情况下, "f5232" 似乎在训练或测试数据中是独占的。如果任一案例模型评分可能会抛出错误(在大多数 ML 包的实现中),因为:

  1. 如果专用于训练:模型对象将在模型方程中引用该特征。评分时会抛出错误,提示我找不到此列。
  2. 如果专用于测试(不太可能因为测试数据通常小于训练数据):模型对象将不会在模型方程中引用此特征。在评分时它会抛出错误,说我得到了这一列,但模型方程没有这一列。这也不太可能,因为大多数实现都认识到这种情况。

解决方案:

  1. 最好的 "automated" 解决方案是只保留那些对训练和测试都通用的列 post 一种热编码。
  2. 对于即席分析,如果由于特征的重要性而无法降低特征级别,则进行分层抽样以确保所有级别的特征都分布到训练和测试数据中。

当 RandomUnderSampler (RUS) 方法返回 np.array 而不是带有列名的 Pandas DataFrame 时,我遇到了这样的问题。

from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(return_indices=True)
X_rus, y_rus, id_rus = rus.fit_sample(X_train, y_train)

我解决了这个问题:

X_rus = pd.DataFrame(X_rus, columns = X_train.columns)

基本上获取 RUS 方法的输出并从中创建一个 Pandas DataFrame,其中的列名来自原始 X_train 数据,这是 RUS 方法的输入。

这可以推广到 XGBoost 期望读取列名但未能读取的任何类似问题。只需创建一个 Pandas DataFrame 并相应地分配列名。