Трубопровод Scikit-Learn: была передана разреженная матрица, но требуются плотные данные

Мне трудно понять, как исправить созданный мной конвейер (читай: в основном вставлено из учебника). Это питон 3.4.2:

df = pd.DataFrame
df = DataFrame.from_records(train)

test = [blah1, blah2, blah3]

pipeline = Pipeline([('vectorizer', CountVectorizer()), ('classifier', RandomForestClassifier())])

pipeline.fit(numpy.asarray(df[0]), numpy.asarray(df[1]))
predicted = pipeline.predict(test)

Когда я запускаю его, я получаю:

TypeError: A sparse matrix was passed, but dense data is required. Use X.toarray() to convert to a dense numpy array.

Это для линии pipeline.fit(numpy.asarray(df[0]), numpy.asarray(df[1])),

Я много экспериментировал с решениями через numpy, scipy и так далее, но я до сих пор не знаю, как это исправить. И да, подобные вопросы возникали раньше, но не внутри конвейера. Где это я должен подать заявку toarray или же todense?

6 ответов

Решение

К сожалению, эти два несовместимы. CountVectorizer создает разреженную матрицу, а для RandomForestClassifier требуется плотная матрица. Можно конвертировать используя X.todense(), Это существенно увеличит объем памяти.

Ниже приведен пример кода для этого, который я нашел по адресу http://zacstewart.com/2014/08/05/pipelines-of-featureunions-of-pipelines.html который позволяет вам вызывать.todense() на стадии конвейера.

class DenseTransformer(TransformerMixin):

    def transform(self, X, y=None, **fit_params):
        return X.todense()

    def fit_transform(self, X, y=None, **fit_params):
        self.fit(X, y, **fit_params)
        return self.transform(X)

    def fit(self, X, y=None, **fit_params):
        return self

Как только у вас есть DenseTransformerВы можете добавить его как шаг конвейера.

pipeline = Pipeline([
     ('vectorizer', CountVectorizer()), 
     ('to_dense', DenseTransformer()), 
     ('classifier', RandomForestClassifier())
])

Другим вариантом будет использование классификатора, предназначенного для разреженных данных, таких как LinearSVC,

from sklearn.svm import LinearSVC
pipeline = Pipeline([('vectorizer', CountVectorizer()), ('classifier', LinearSVC())])

Самое чистое решение будет использовать FunctionTransformer преобразовать в плотный: это будет автоматически реализовывать fit, transform а также fit_transform методы, как в ответе Дэвида. Кроме того, если мне не нужны специальные имена для шагов конвейера, я бы хотел использовать sklearn.pipeline.make_pipeline удобная функция для включения более минималистичного языка для описания модели:

from sklearn.preprocessing import FunctionTransformer

pipeline = make_pipeline(
     CountVectorizer(), 
     FunctionTransformer(lambda x: x.todense(), accept_sparse=True), 
     RandomForestClassifier()
)

Случайные леса в 0.16 dev теперь принимают разреженные данные.

Вы можете изменить панд Series для массивов с использованием .values метод.

pipeline.fit(df[0].values, df[1].values)

Однако я думаю, что проблема здесь происходит потому, что CountVectorizer() по умолчанию возвращает разреженную матрицу и не может быть передан в классификатор RF. CountVectorizer() есть dtype параметр для указания типа возвращаемого массива. Тем не менее, как правило, вам нужно сделать какое-то уменьшение размерности, чтобы использовать случайные леса для классификации текста, потому что векторы мешков слов очень длинные

Я обнаружил, что FunctionTransformer и использование x.toarray() вместо x.todense() мне помогли.

      'pipeline': Pipeline(
        [
            ('vect', TfidfVectorizer()),
            ('dense', FunctionTransformer(lambda x: x.toarray(), accept_sparse=True)),
            ('clf', GaussianProcessClassifier())
        ]
    )

С этим конвейером добавим TfidTransformer plus

        pipelinex = Pipeline([('bow',vectorizer),
                           ('tfidf',TfidfTransformer()),
                           ('to_dense', DenseTransformer()), 
                           ('classifier',classifier)])
Другие вопросы по тегам