Utilice scikit-learn para clasificar en varias categorías

80

Estoy tratando de usar uno de los métodos de aprendizaje supervisado de scikit-learn para clasificar fragmentos de texto en una o más categorías. La función de predicción de todos los algoritmos que probé solo devuelve una coincidencia.

Por ejemplo, tengo un fragmento de texto:

"Theaters in New York compared to those in London"

Y he entrenado el algoritmo para que elija un lugar para cada fragmento de texto que le proporcione.

En el ejemplo anterior, me gustaría que regresara New Yorky London, pero solo regresa New York.

¿Es posible usar scikit-learn para devolver múltiples resultados? ¿O incluso devolver la etiqueta con la siguiente probabilidad más alta?

Gracias por tu ayuda.

---Actualizar

Intenté usarlo, OneVsRestClassifierpero solo obtengo una opción por texto. A continuación se muestra el código de muestra que estoy usando

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

Resultado: ['Nueva York' 'Londres' 'Londres']

CodeMonkeyB
fuente

Respuestas:

111

Lo que desea se llama clasificación de etiquetas múltiples. Scikits-learn puede hacer eso. Vea aquí: http://scikit-learn.org/dev/modules/multiclass.html .

No estoy seguro de qué va mal en su ejemplo, mi versión de sklearn aparentemente no tiene WordNGramAnalyzer. ¿Quizás es una cuestión de usar más ejemplos de entrenamiento o probar un clasificador diferente? Aunque tenga en cuenta que el clasificador de etiquetas múltiples espera que el objetivo sea una lista de tuplas / listas de etiquetas.

Lo siguiente funciona para mí:

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

Para mí, esto produce la salida:

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

Espero que esto ayude.

mwv
fuente
1
Intenté eliminar los dos últimos ejemplos de entrenamiento que combinan los nombres de las ciudades y obtengo: hola bienvenido a nueva york. disfrútalo aquí y también en londres => Nueva York Ya no devuelve dos etiquetas. Para mí, solo devolver dos etiquetas si entreno las combinaciones de las dos ciudades. ¿Me estoy perdiendo de algo? Gracias de nuevo por toda su ayuda
CodeMonkeyB
1
Este es solo un conjunto de datos de juguetes, no sacaría demasiadas conclusiones de eso. ¿Ha probado este procedimiento con sus datos reales?
mwv
3
@CodeMonkeyB: realmente deberías aceptar esta respuesta, es correcta desde el punto de vista de la programación. Si funciona en la práctica depende de sus datos, no del código.
Fred Foo
2
¿Alguien más tiene un problema con min_ny max_n. Necesito cambiarlos para ngram_range=(1,2)que funcionen
emmagras
1
Está dando este error: ValueError: Parece que está utilizando una representación de datos de múltiples etiquetas heredada. La secuencia de secuencias ya no es compatible; use una matriz binaria o una matriz dispersa en su lugar.
MANU
61

EDITAR: actualizado para Python 3, scikit-learn 0.18.1 usando MultiLabelBinarizer como se sugiere.

También he estado trabajando en esto y realicé una ligera mejora en la excelente respuesta de mwv que puede ser útil. Toma etiquetas de texto como entrada en lugar de etiquetas binarias y las codifica usando 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)))

Esto me da el siguiente resultado:

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
fuente
13
labelBinarizerEsta anticuado. Úselo en su lb = preprocessing.MultiLabelBinarizer()lugar
Roman
1
No le da a Gran Bretaña porque las únicas etiquetas de salida son New Yorky London.
umop aplsdn
2
De acuerdo con scikit-learn, One-Vs-All es compatible con todos los modelos lineales excepto sklearn.svm.SVC y también multilabel es compatible con: árboles de decisión, bosques aleatorios, vecinos más cercanos, por lo que no usaría LinearSVC () para este tipo de tarea (también conocida como clasificación de etiquetas múltiples que supongo que desea usar)
PeterB
2
Fyi One-Vs-Todo lo que menciona @mindstorm, corresponde a la clase scikit-learn "OneVsRestClassifier" (observe "Rest" en lugar de "all"). Esta página de ayuda de scikit-learn lo aclara.
lucid_dreamer
1
Como menciona @mindstorm, es cierto que en esta página , la documentación menciona: "One-Vs-All: todos los modelos lineales excepto sklearn.svm.SVC". Sin embargo, otro ejemplo de varias etiquetas de la documentación de scikit-learn muestra un ejemplo de varias etiquetas con esta línea classif = OneVsRestClassifier(SVC(kernel='linear')). Perplejo.
lucid_dreamer
8

También me encontré con esto, y el problema para mí fue que mi y_Train era una secuencia de cadenas, en lugar de una secuencia de secuencias de cadenas. Aparentemente, OneVsRestClassifier decidirá basándose en el formato de la etiqueta de entrada si usar múltiples clases o múltiples etiquetas. Así que cambia:

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

a

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

Aparentemente esto desaparecerá en el futuro, ya que se rompe de todas las etiquetas son las mismas: https://github.com/scikit-learn/scikit-learn/pull/1987

usuario2824135
fuente
8

Cambie esta línea para que funcione en nuevas versiones de python

# lb = preprocessing.LabelBinarizer()
lb = preprocessing.MultiLabelBinarizer()
Srini Sídney
fuente
2

Algunos ejemplos de clasificación múltiple son los siguientes:

Ejemplo 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 salida es

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

Ejemplo 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 salida es

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