29 votos

Métricas de clasificación multietiqueta en scikit

Estoy tratando de construir un clasificador multi-etiqueta para asignar temas a documentos existentes usando scikit

Estoy tramitando mis documentos pasándolos por el TfidfVectorizer las etiquetas a través del MultiLabelBinarizer y creó un OneVsRestClassifier con un SGDClassifier como el estimador.

Sin embargo, al probar mi clasificador sólo obtengo puntuaciones de hasta .29 que por lo que he leído es bastante bajo para problemas similares. He probado múltiples opciones en el TfidfVectorizer como stopwords, unigramas, stemming y nada parece cambiar mucho el resultado.

También he utilizado GridSearchCV para obtener los mejores parámetros para mi estimador y actualmente me he quedado sin ideas sobre qué probar a continuación.

Al mismo tiempo, por lo que tengo entendido no puedo utilizar scikit.metrics con OneVsRestClassifier ¿Cómo puedo obtener algunas métricas (F1, Precisión, Recuperación, etc.) para averiguar qué está mal?

¿Podría ser un problema con mi corpus de datos?

Actualización: También he probado a utilizar CountVectorizer et HashingVectorizer y los canaliza a TfidfTransformer pero los resultados son similares. Así que supongo que el enfoque de la bolsa de palabras está dando lo mejor de sí en el ámbito de la tokenización y el resto depende del clasificador...

32voto

Franck Dernoncourt Puntos 2128

La precisión de los subconjuntos es, en efecto, una métrica dura. Para hacerse una idea de lo bueno o malo que es 0,29, alguna idea:

  • mira cuántas etiquetas tienes una media para cada muestra
  • mirar la concordancia entre anotadores, si está disponible (si no, pruebe usted mismo para ver qué precisión de subconjunto se obtiene cuando usted es el clasificador)
  • pensar si los temas están bien definidos
  • mira el número de muestras que tienes para cada etiqueta

También puede calcular la puntuación hamming, para ver si su clasificador no tiene ni idea, o si por el contrario es decentemente bueno pero tiene problemas para predecir todas las etiquetas correctamente. Vea a continuación cómo calcular la puntuación hamming.

Al mismo tiempo, por lo que tengo entendido, no puedo utilizar scikit.metrics con OneVsRestClassifier, así que ¿cómo puedo obtener algunas métricas (F1, Precision, Recall, etc.) para averiguar qué está mal?

Voir ¿Cómo calcular la precisión/recuperación de la clasificación multiclase-multilabel? . He olvidado si sklearn lo soporta, recuerdo que tenía algunas limitaciones, por ejemplo sklearn no admite la multietiqueta para la matriz de confusión . Sería una buena idea ver estos números en efecto.


Puntuación de Hamming :

En un clasificación multietiqueta el escenario, sklearn.metrics.accuracy_score sólo calcula el precisión del subconjunto (3): es decir, el conjunto de etiquetas predichas para una muestra debe coincidir exactamente con el correspondiente conjunto de etiquetas en y_true.

Esta forma de calcular la precisión se denomina en alguna ocasión, quizás de forma menos ambigua, proporción de coincidencia exacta (1):

enter image description here

Otra forma típica de calcular la precisión se define en (1) y (2), y se denomina de forma menos ambigua Puntuación de Hamming (4) (ya que está estrechamente relacionado con la pérdida de Hamming), o precisión basada en la etiqueta ). Se calcula como sigue:

enter image description here

Aquí hay un método de python para calcular la puntuación de Hamming:

# Code by https://stackoverflow.com/users/1953100/william
# Source: https://stackoverflow.com/a/32239764/395857
# License: cc by-sa 3.0 with attribution required

import numpy as np

y_true = np.array([[0,1,0],
                   [0,1,1],
                   [1,0,1],
                   [0,0,1]])

y_pred = np.array([[0,1,1],
                   [0,1,1],
                   [0,1,0],
                   [0,0,0]])

def hamming_score(y_true, y_pred, normalize=True, sample_weight=None):
    '''
    Compute the Hamming score (a.k.a. label-based accuracy) for the multi-label case
    https://stackoverflow.com/q/32239577/395857
    '''
    acc_list = []
    for i in range(y_true.shape[0]):
        set_true = set( np.where(y_true[i])[0] )
        set_pred = set( np.where(y_pred[i])[0] )
        #print('\nset_true: {0}'.format(set_true))
        #print('set_pred: {0}'.format(set_pred))
        tmp_a = None
        if len(set_true) == 0 and len(set_pred) == 0:
            tmp_a = 1
        else:
            tmp_a = len(set_true.intersection(set_pred))/\
                    float( len(set_true.union(set_pred)) )
        #print('tmp_a: {0}'.format(tmp_a))
        acc_list.append(tmp_a)
    return np.mean(acc_list)

if __name__ == "__main__":
    print('Hamming score: {0}'.format(hamming_score(y_true, y_pred))) # 0.375 (= (0.5+1+0+0)/4)

    # For comparison sake:
    import sklearn.metrics

    # Subset accuracy
    # 0.25 (= 0+1+0+0 / 4) --> 1 if the prediction for one sample fully matches the gold. 0 otherwise.
    print('Subset accuracy: {0}'.format(sklearn.metrics.accuracy_score(y_true, y_pred, normalize=True, sample_weight=None)))

    # Hamming loss (smaller is better)
    # $$ \text{HammingLoss}(x_i, y_i) = \frac{1}{|D|} \sum_{i=1}^{|D|} \frac{xor(x_i, y_i)}{|L|}, $$
    # where
    #  - \\(|D|\\) is the number of samples  
    #  - \\(|L|\\) is the number of labels  
    #  - \\(y_i\\) is the ground truth  
    #  - \\(x_i\\)  is the prediction.  
    # 0.416666666667 (= (1+0+3+1) / (3*4) )
    print('Hamming loss: {0}'.format(sklearn.metrics.hamming_loss(y_true, y_pred))) 

Salidas:

Hamming score: 0.375
Subset accuracy: 0.25
Hamming loss: 0.416666666667

(1) Sorower, Mohammad S. " Un estudio de la literatura sobre algoritmos para el aprendizaje multietiqueta. " Universidad Estatal de Oregón, Corvallis (2010).

(2) Tsoumakas, Grigorios, y Ioannis Katakis. " Clasificación multietiqueta: Una visión general. " Departamento de Informática, Universidad Aristóteles de Tesalónica, Grecia (2006).

(3) Ghamrawi, Nadia, y Andrew McCallum. " Clasificación colectiva multietiqueta. " Actas de la 14ª conferencia internacional de ACM sobre gestión de la información y el conocimiento. ACM, 2005.

(4) Godbole, Shantanu, y Sunita Sarawagi. " Métodos discriminativos para la clasificación de etiquetas múltiples. " Avances en descubrimiento de conocimientos y minería de datos. Springer Berlin Heidelberg, 2004. 22-30.

1voto

user184673 Puntos 11

La pérdida de Hamming y la coincidencia exacta (también llamada precisión del subconjunto) pueden calcularse utilizando Scikit-learn de la siguiente manera.

import numpy as np
from sklearn.metrics import hamming_loss, accuracy_score 
y_true = np.array([[0,1,0],
                   [0,1,1],
                   [1,0,1],
                   [0,0,1]])

y_pred = np.array([[0,1,1],
                   [0,1,1],
                   [0,1,0],
                   [0,0,0]])

print("accuracy_score:", accuracy_score(y_true, y_pred))
print("Hamming_loss:", hamming_loss(y_true, y_pred))

Salida

accuracy_score: 0.25
Hamming_loss: 0.4166666666666667

0voto

Ytsen de Boer Puntos 136

¿No es suficiente la puntuación de 0,29? ¿Cómo es su matriz de confusión? ¿Hay algunos temas que no se pueden separar tal vez sólo mirando el contenido de las palabras?

De lo contrario, intente darle la vuelta a su problema: Suponga que las bajas puntuaciones son en realidad lo mejor que puede hacer su clasificador con sus datos. Eso significaría que sus documentos no son clasificables con este enfoque.

Para probar esta hipótesis, necesita un conjunto de documentos de prueba con características conocidas de la bolsa de palabras (que usted mismo crea). Debería obtener una puntuación del 100%.

Si no lo hace, entonces tiene un error. Si no, necesitas un enfoque diferente para clasificar tus documentos. Pregúntese: ¿en qué se diferencian los documentos de las distintas clases? ¿Necesito mirar otras características de mis documentos, etc.?

0voto

Umar Puntos 66

La siguiente es una versión vectorizada de la puntuación Hamming:

import numpy as np

def hamming_score(pred, answer):
    out = ((pred & answer).sum(axis=1) / (pred | answer).sum(axis=1)).mean()
    if np.isinf(out):
        out = np.array(1.0)
    return out

pred = np.array([[0, 1, 1], [0, 1, 1], [0, 1, 0], [0, 0, 0]])
answer = np.array([[0, 1, 0], [0, 1, 1], [1, 0, 1], [0, 0, 1]])

hamming_score(pred, answer)

o en PyTorch

import torch

def hamming_score(pred, answer):
    out = ((pred & answer).sum(dim=1) / (pred | answer).sum(dim=1)).mean()
    if out.isnan():
        out = torch.tensor(1.0)
    return out

answer = torch.tensor([[0, 1, 0], [0, 1, 1], [1, 0, 1], [0, 0, 1]])
pred = torch.tensor([[0, 1, 1], [0, 1, 1], [0, 1, 0], [0, 0, 0]])

hamming_score(pred, answer)

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X