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

Мне трудно понять, как исправить созданный мной конвейер (читайте: в основном вставляемый из учебника). Это python 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?

Ответы

Ответ 1

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

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

class DenseTransformer(TransformerMixin):

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

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

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

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

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

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

Ответ 2

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

Ответ 3

Наиболее кратким решением было бы использование 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()
)

Ответ 4

вы можете изменить pandas Series на массивы с помощью метода .values.

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

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

Ответ 5

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

        pipelinex = Pipeline([('bow',vectorizer),
                           ('tfidf',TfidfTransformer()),
                           ('to_dense', DenseTransformer()), 
                           ('classifier',classifier)])