带有VotingClassifier的Typeerror

时间:2018-04-10 19:47:26

标签: python-3.x numpy scikit-learn typeerror catboost

我想使用VotingClassifier,但我在交叉验证方面遇到了一些问题

    x_train, x_validation, y_train, y_validation = train_test_split(x, y, test_size=.22, random_state=2)
    x_train = x_train.fillna(0)
    clf1 = CatBoostClassifier()
    clf2 = RandomForestClassifier()
    clf = VotingClassifier(estimators=[('cb', clf1), ('rf', clf2)])
    clf.fit(x_train.values(), y_train)

soooo,我预测错误...

    cross_validate(clf, x_train, y_train, scoring='accuracy', return_train_score = True, n_jobs = 4)
TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe'

(所有错误here

并在此处下载x_train和y_train↓

x_train
y_train

1 个答案:

答案 0 :(得分:0)

这个错误是因为这一行:

np.bincount(x, weights=self._weights_not_none)

此处x是VotingClassifier中各个分类器返回的预测。

根据np.bincount的文件:

  

计算非负数组中每个值的出现次数   整数。

     

x:array_like,1维,非负整数

此方法仅需要数组中的int值。

现在,如果将CatBoostClassifier替换为任何其他Scikit-learn分类器,您的代码将会起作用。因为所有scikit-learn估算器都从np.int64返回predict()的数组。

但CatBoostClassifier返回np.float64作为输出。因而错误。实际上它也应该返回int64,因为predict()函数应该返回类而不是任何浮点值。但我不知道为什么它会返回浮动。

您可以通过扩展CatBoostClassifier类并动态转换预测来解决此问题。

import numpy as np
from catboost import CatBoostClassifier
class CatBoostClassifierInt(CatBoostClassifier):
    def predict(self, data, prediction_type='Class', ntree_start=0, ntree_end=0, thread_count=1, verbose=None):
        predictions = self._predict(data, prediction_type, ntree_start, ntree_end, thread_count, verbose)

        # This line is the only change I did
        return np.asarray(predictions, dtype=np.int64).ravel()

clf1 = CatBoostClassifierInt()
clf2 = RandomForestClassifier()
clf = VotingClassifier(estimators=[('cb', clf1), ('rf', clf2)])
cross_validate(clf, x_train, y_train, scoring='accuracy', return_train_score = True)

现在你不会得到那个错误。

更正确的版本应该是这个。这将处理具有匹配输入和输出的所有类型的标签,并且可以轻松地在scikit中使用:

class CatBoostClassifierCorrected(CatBoostClassifier):
    def fit(self, X, y=None, cat_features=None, sample_weight=None, baseline=None, use_best_model=None,
        eval_set=None, verbose=None, logging_level=None, plot=False, column_description=None, verbose_eval=None):

        self.le_ = LabelEncoder().fit(y)
        transformed_y = self.le_.transform(y)

        self._fit(X, transformed_y, cat_features, None, sample_weight, None, None, None, baseline, use_best_model, eval_set, verbose, logging_level, plot, column_description, verbose_eval)
        return self

    def predict(self, data, prediction_type='Class', ntree_start=0, ntree_end=0, thread_count=1, verbose=None):
        predictions = self._predict(data, prediction_type, ntree_start, ntree_end, thread_count, verbose)

        # This line is the only change I did
        return self.le_.inverse_transform(predictions.astype(np.int64))

这将处理所有不同类型的标签