web-dev-qa-db-fra.com

Utilisez scikit-learn pour classer en plusieurs catégories

J'essaie d'utiliser l'une des méthodes d'apprentissage supervisé de scikit-learn pour classer des morceaux de texte en une ou plusieurs catégories. La fonction de prédiction de tous les algorithmes que j'ai essayés ne renvoie qu'une correspondance.

Par exemple, j'ai un morceau de texte:

"Theaters in New York compared to those in London"

Et j’ai appris à l’algorithme à choisir un emplacement pour chaque extrait de texte que je nourris.

Dans l'exemple ci-dessus, je voudrais qu'il renvoie New York et London, mais il ne renvoie que New York.

Est-il possible d'utiliser scikit-learn pour renvoyer plusieurs résultats? Ou même renvoyer l'étiquette avec la probabilité la plus élevée suivante? 

Merci de votre aide.

---Mettre à jour 

J'ai essayé d'utiliser OneVsRestClassifier mais je n'ai toujours qu'une seule option par morceau de texte. Voici l'exemple de code que j'utilise

y_train = ('New York','London')


train_set = ("new york nyc big Apple", "london uk great britain")
vocab = {'new york' :0,'nyc':1,'big Apple':2,'london' : 3, 'uk': 4, 'great britain' : 5}
count = CountVectorizer(analyzer=WordNGramAnalyzer(min_n=1, max_n=2),vocabulary=vocab)
test_set = ('Nice day in nyc','london town','hello welcome to the big Apple. enjoy it here and london too')

X_vectorized = count.transform(train_set).todense()
smatrix2  = count.transform(test_set).todense()


base_clf = MultinomialNB(alpha=1)

clf = OneVsRestClassifier(base_clf).fit(X_vectorized, y_train)
Y_pred = clf.predict(smatrix2)
print Y_pred

Résultat: ['New York' 'Londres' 'Londres']

69
CodeMonkeyB

Ce que vous voulez s'appelle une classification multi-étiquettes. Scikits-learn peut le faire. Voir ici: http://scikit-learn.org/dev/modules/multiclass.html .

Je ne sais pas ce qui ne va pas dans votre exemple, ma version de sklearn ne contient apparemment pas WordNGramAnalyzer. Peut-être s'agit-il d'utiliser plus d'exemples de formation ou d'essayer un classificateur différent? Notez cependant que le classificateur multi-étiquettes s'attend à ce que la cible soit une liste de tuples/listes d'étiquettes.

Ce qui suit fonctionne pour moi:

import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.svm import LinearSVC
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.multiclass import OneVsRestClassifier

X_train = np.array(["new york is a hell of a town",
                    "new york was originally dutch",
                    "the big Apple is great",
                    "new york is also called the big Apple",
                    "nyc is Nice",
                    "people abbreviate new york city as nyc",
                    "the capital of great britain is london",
                    "london is in the uk",
                    "london is in england",
                    "london is in great britain",
                    "it rains a lot in london",
                    "london hosts the british museum",
                    "new york is great and so is london",
                    "i like london better than new york"])
y_train = [[0],[0],[0],[0],[0],[0],[1],[1],[1],[1],[1],[1],[0,1],[0,1]]
X_test = np.array(['Nice day in nyc',
                   'welcome to london',
                   'hello welcome to new york. enjoy it here and london too'])   
target_names = ['New York', 'London']

classifier = Pipeline([
    ('vectorizer', CountVectorizer(min_n=1,max_n=2)),
    ('tfidf', TfidfTransformer()),
    ('clf', OneVsRestClassifier(LinearSVC()))])
classifier.fit(X_train, y_train)
predicted = classifier.predict(X_test)
for item, labels in Zip(X_test, predicted):
    print '%s => %s' % (item, ', '.join(target_names[x] for x in labels))

Pour moi, cela produit la sortie:

Nice day in nyc => New York
welcome to london => London
hello welcome to new york. enjoy it here and london too => New York, London

J'espère que cela t'aides.

102
mwv

EDIT: mis à jour pour Python 3, scikit-learn 0.18.1 en utilisant MultiLabelBinarizer comme suggéré.

J'ai également travaillé sur cette question et apporté une légère amélioration à l'excellente réponse de mwv qui pourrait être utile. Il prend les étiquettes de texte en entrée plutôt que les étiquettes binaires et les code à l'aide de MultiLabelBinarizer. 

import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.svm import LinearSVC
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.multiclass import OneVsRestClassifier
from sklearn.preprocessing import MultiLabelBinarizer

X_train = np.array(["new york is a hell of a town",
                    "new york was originally dutch",
                    "the big Apple is great",
                    "new york is also called the big Apple",
                    "nyc is Nice",
                    "people abbreviate new york city as nyc",
                    "the capital of great britain is london",
                    "london is in the uk",
                    "london is in england",
                    "london is in great britain",
                    "it rains a lot in london",
                    "london hosts the british museum",
                    "new york is great and so is london",
                    "i like london better than new york"])
y_train_text = [["new york"],["new york"],["new york"],["new york"],["new york"],
                ["new york"],["london"],["london"],["london"],["london"],
                ["london"],["london"],["new york","london"],["new york","london"]]

X_test = np.array(['Nice day in nyc',
                   'welcome to london',
                   'london is rainy',
                   'it is raining in britian',
                   'it is raining in britian and the big Apple',
                   'it is raining in britian and nyc',
                   'hello welcome to new york. enjoy it here and london too'])
target_names = ['New York', 'London']

mlb = MultiLabelBinarizer()
Y = mlb.fit_transform(y_train_text)

classifier = Pipeline([
    ('vectorizer', CountVectorizer()),
    ('tfidf', TfidfTransformer()),
    ('clf', OneVsRestClassifier(LinearSVC()))])

classifier.fit(X_train, Y)
predicted = classifier.predict(X_test)
all_labels = mlb.inverse_transform(predicted)

for item, labels in Zip(X_test, all_labels):
    print('{0} => {1}'.format(item, ', '.join(labels)))

Cela me donne la sortie suivante:

Nice day in nyc => new york
welcome to london => london
london is rainy => london
it is raining in britian => london
it is raining in britian and the big Apple => new york
it is raining in britian and nyc => london, new york
hello welcome to new york. enjoy it here and london too => london, new york
51
J Maurer

Modifiez cette ligne pour la faire fonctionner dans les nouvelles versions de python

# lb = preprocessing.LabelBinarizer()
lb = preprocessing.MultiLabelBinarizer()
6
Srini Sydney

Je viens juste de le rencontrer aussi, et le problème pour moi était que mon y_Train était une séquence de chaînes, plutôt qu'une séquence de séquences de chaînes. Apparemment, OneVsRestClassifier décidera, en fonction du format de l'étiquette d'entrée, d'utiliser plusieurs classes par rapport à plusieurs. Alors changez:

y_train = ('New York','London')

à

y_train = (['New York'],['London'])

Apparemment, cela disparaîtra dans le futur, car tous les labels sont identiques: https://github.com/scikit-learn/scikit-learn/pull/1987

6
user2824135

Quelques exemples de multi-classification sont les suivants: -

Exemple 1:-

import numpy as np
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()

arr2d = np.array([1, 2, 3,4,5,6,7,8,9,10,11,12,13,14,1])
transfomed_label = encoder.fit_transform(arr2d)
print(transfomed_label)

La sortie est 

[[1 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 1 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 1 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 1 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 1 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 1 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 1 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 1 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 1 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 1 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 1]
 [1 0 0 0 0 0 0 0 0 0 0 0 0 0]]

Exemple 2: -

import numpy as np
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()

arr2d = np.array(['Leopard','Lion','Tiger', 'Lion'])
transfomed_label = encoder.fit_transform(arr2d)
print(transfomed_label)

La sortie est

[[1 0 0]
 [0 1 0]
 [0 0 1]
 [0 1 0]]
1
Goyal Vicky