Canalización de Scikit-Learn: se pasó una matriz dispersa, pero se requieren datos densos

Me resulta difícil entender cómo arreglar un Pipeline que creé (lea: en gran parte pegado de un tutorial). Es 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) 

Cuando lo ejecuto, me sale:

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

Esto es para la línea pipeline.fit(numpy.asarray(df[0]), numpy.asarray(df[1])) .

He experimentado mucho con soluciones a través de entumecimiento, scipy, etc., pero todavía no sé cómo solucionarlo. Y sí, han surgido preguntas similares antes, pero no dentro de una tubería. ¿Dónde es que tengo que aplicar toarray o todense ?

Lamentablemente esos dos son incompatibles. Un CountVectorizer produce una matriz dispersa y el RandomForestClassifier requiere una matriz densa. Es posible convertir usando X.todense() . Hacer esto boostá sustancialmente tu huella de memoria.

A continuación se muestra un código de ejemplo para hacerlo basado en http://zacstewart.com/2014/08/05/pipelines-of-featureunions-of-pipelines.html que le permite llamar a .todense() en una etapa de canalización.

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

Una vez que tenga su DenseTransformer , podrá agregarlo como un paso de canalización.

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

Otra opción sería utilizar un clasificador destinado a datos dispersos como LinearSVC .

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

Los bosques aleatorios en 0.16-dev ahora aceptan datos dispersos.

La solución más concisa sería usar un FunctionTransformer para convertir a denso: esto implementará automáticamente los métodos de fit , transform y fit_transform como en la respuesta de David. Además, si no necesito nombres especiales para mis pasos de canalización, me gusta usar la función de conveniencia sklearn.pipeline.make_pipeline para habilitar un lenguaje más minimalista para describir el modelo:

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

puede cambiar las Series pandas a arreglos usando el método .values .

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

Sin embargo, creo que el problema aquí ocurre porque CountVectorizer() devuelve una matriz dispersa de forma predeterminada, y no se puede canalizar al clasificador de RF. CountVectorizer() tiene un parámetro dtype para especificar el tipo de matriz devuelta. Dicho esto, normalmente es necesario hacer algún tipo de reducción de dimensionalidad para usar bosques aleatorios para la clasificación de texto, porque los vectores de características de la bolsa de palabras son muy largos.

con esta tubería agregar TfidTransformer plus

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