Verwenden Sie scikit-learn, um in mehrere Kategorien zu klassifizieren

80

Ich versuche, eine der von scikit-learn überwachten Lernmethoden zu verwenden, um Textteile in eine oder mehrere Kategorien einzuteilen. Die Vorhersagefunktion aller Algorithmen, die ich ausprobiert habe, gibt nur eine Übereinstimmung zurück.

Zum Beispiel habe ich einen Text:

"Theaters in New York compared to those in London"

Und ich habe den Algorithmus so trainiert, dass er für jedes Textschnipsel, das ich füttere, einen Ort auswählt.

Im obigen Beispiel würde ich will es zurück New Yorkund London, aber es gibt nur New York.

Ist es möglich, mit scikit-learn mehrere Ergebnisse zurückzugeben? Oder sogar das Etikett mit der nächsthöheren Wahrscheinlichkeit zurückgeben?

Danke für Ihre Hilfe.

---Aktualisieren

Ich habe es versucht, OneVsRestClassifieraber ich bekomme immer noch nur eine Option pro Textstück zurück. Unten ist der Beispielcode, den ich verwende

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

Ergebnis: ['New York' 'London' 'London']

CodeMonkeyB
quelle

Antworten:

111

Was Sie wollen, heißt Multi-Label-Klassifizierung. Scikits-Learn kann das. Siehe hier: http://scikit-learn.org/dev/modules/multiclass.html .

Ich bin nicht sicher, was in Ihrem Beispiel falsch läuft. Meine Version von sklearn verfügt anscheinend nicht über WordNGramAnalyzer. Vielleicht geht es darum, mehr Trainingsbeispiele zu verwenden oder einen anderen Klassifikator auszuprobieren? Beachten Sie jedoch, dass der Multi-Label-Klassifikator erwartet, dass das Ziel eine Liste von Tupeln / Listen von Labels ist.

Folgendes funktioniert für mich:

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))

Für mich ergibt dies die Ausgabe:

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

Hoffe das hilft.

mwv
quelle
1
Ich habe versucht, die letzten beiden Trainingsbeispiele zu entfernen, die die Städtenamen kombinieren, und ich bekomme: Hallo, willkommen in New York. genieße es hier und auch in London => New York Es werden nicht mehr zwei Labels zurückgegeben. Für mich gibt es nur zwei Labels zurück, wenn ich die Kombinationen der beiden Städte trainiere. Vermisse ich etwas
Nochmals vielen
1
Dies ist nur ein Spielzeugdatensatz, ich würde daraus nicht zu viele Schlussfolgerungen ziehen. Haben Sie dieses Verfahren an Ihren realen Daten ausprobiert?
MWV
3
@CodeMonkeyB: Sie sollten diese Antwort wirklich akzeptieren, sie ist aus programmtechnischer Sicht korrekt. Ob es in der Praxis funktioniert, hängt von Ihren Daten ab, nicht vom Code.
Fred Foo
2
Bekommt sonst noch jemand ein Problem mit min_nund max_n. Ich muss sie ändern, um ngram_range=(1,2)zu arbeiten
Emmagras
1
Es wird der folgende Fehler ausgegeben: ValueError: Sie verwenden anscheinend eine ältere Datendarstellung mit mehreren Etiketten. Sequenz von Sequenzen wird nicht mehr unterstützt; Verwenden Sie stattdessen ein binäres Array oder eine dünne Matrix.
MANU
61

BEARBEITEN: Aktualisiert für Python 3, scikit-learn 0.18.1 mit MultiLabelBinarizer wie vorgeschlagen.

Ich habe auch daran gearbeitet und die ausgezeichnete Antwort von mwv leicht verbessert, die nützlich sein könnte. Es werden eher Textbeschriftungen als binäre Beschriftungen als Eingabe verwendet und mit MultiLabelBinarizer codiert.

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)))

Dies gibt mir die folgende Ausgabe:

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
J Maurer
quelle
13
labelBinarizerist veraltet. Verwenden Sie lb = preprocessing.MultiLabelBinarizer()stattdessen
Roman
1
Es gibt Großbritannien nicht, weil die einzigen Ausgabe-Labels New Yorkund sind London.
Umop Aplsdn
2
Laut scikit-learn wird One-Vs-All von allen linearen Modellen außer sklearn.svm.SVC unterstützt und auch Multilabel wird unterstützt von: Entscheidungsbäumen, zufälligen Wäldern, nächsten Nachbarn, daher würde ich LinearSVC () für diesen Typ nicht verwenden of task (auch bekannt als Multilabel-Klassifizierung, von der ich annehme, dass Sie sie verwenden möchten)
PeterB
2
Fyi One-Vs-All, das @mindstorm erwähnt, entspricht der Scikit-Lernklasse "OneVsRestClassifier" (beachten Sie "Rest" statt "all"). Diese Scikit-Learn-Hilfeseite verdeutlicht dies.
lucid_dreamer
1
Wie @mindstorm erwähnt, wird in der Dokumentation auf dieser Seite Folgendes erwähnt : "One-Vs-All: Alle linearen Modelle außer sklearn.svm.SVC". Ein weiteres Multilabel-Beispiel aus der Scikit-Learn-Dokumentation zeigt jedoch ein Multilabel-Beispiel mit dieser Zeile classif = OneVsRestClassifier(SVC(kernel='linear')). Verwirrt.
lucid_dreamer
8

Ich bin auch darauf gestoßen, und das Problem für mich war, dass mein y_Train eher eine Folge von Strings als eine Folge von Folgen von String war. Anscheinend wird OneVsRestClassifier basierend auf dem Eingabeetikettenformat entscheiden, ob mehrere Klassen oder mehrere Etiketten verwendet werden sollen. Also ändere:

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

zu

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

Anscheinend wird dies in Zukunft verschwinden, da alle Etiketten gleich sind: https://github.com/scikit-learn/scikit-learn/pull/1987

user2824135
quelle
8

Ändern Sie diese Zeile, damit sie in neuen Python-Versionen funktioniert

# lb = preprocessing.LabelBinarizer()
lb = preprocessing.MultiLabelBinarizer()
Srini Sydney
quelle
2

Einige Beispiele für Mehrfachklassifizierungen sind wie folgt: -

Beispiel 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)

Ausgabe ist

[[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]]

Beispiel 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)

Ausgabe ist

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