web-dev-qa-db-fra.com

Pipeline de Scikit-Learn: une matrice clairsemée a été transmise, mais des données denses sont requises

J'ai du mal à comprendre comment réparer un Pipeline que j'ai créé (lire: largement collé à partir d'un tutoriel). C'est 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)

Quand je le lance, j'obtiens:

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

C'est pour la ligne pipeline.fit(numpy.asarray(df[0]), numpy.asarray(df[1])).

J'ai beaucoup expérimenté avec des solutions via numpy, scipy, etc., mais je ne sais toujours pas comment y remédier. Et oui, des questions similaires se sont posées auparavant, mais pas à l'intérieur d'un pipeline. Où dois-je appliquer toarray ou todense?

26
Ada Stra

Malheureusement, ces deux sont incompatibles. Un CountVectorizer produit une matrice clairsemée et le RandomForestClassifier nécessite une matrice dense. Il est possible de convertir en utilisant X.todense(). Cela augmentera considérablement votre empreinte mémoire.

Voici un exemple de code pour ce faire basé sur http://zacstewart.com/2014/08/05/pipelines-of-featureunions-of-pipelines.html qui vous permet d'appeler .todense() en phase de pipeline.

class DenseTransformer(TransformerMixin):

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

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

Une fois que vous avez votre DenseTransformer, vous pouvez l'ajouter en tant qu'étape de pipeline.

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

Une autre option consisterait à utiliser un classificateur destiné aux données éparses comme LinearSVC.

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

Les forêts aléatoires en 0.16-dev acceptent désormais des données rares.

16
Gilles Louppe

La solution la plus laconique serait d'utiliser un FunctionTransformer pour convertir en dense: cela implémentera automatiquement les fit, transform et fit_transform méthodes comme dans la réponse de David. De plus, si je n'ai pas besoin de noms spéciaux pour mes étapes de pipeline, j'aime utiliser le sklearn.pipeline.make_pipeline fonction de commodité pour activer un langage plus minimaliste pour décrire le modèle:

from sklearn.preprocessing import FunctionTransformer

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

vous pouvez changer pandas Series en tableaux en utilisant la méthode .values.

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

Cependant, je pense que le problème se produit ici parce que CountVectorizer() renvoie une matrice clairsemée par défaut et ne peut pas être redirigée vers le classificateur RF. CountVectorizer() a un dtype paramètre pour spécifier le type de tableau renvoyé. Cela dit, vous devez généralement effectuer une sorte de réduction de la dimensionnalité pour utiliser des forêts aléatoires pour la classification de texte, car le sac de mots contient des vecteurs de fonctionnalités très longs

3
JAB