Métriques de classification multi-étiquettes sur scikit

19

J'essaie de construire un classificateur multi-étiquettes afin d'affecter des sujets aux documents existants à l'aide de scikit

Je suis en train de traiter mes documents en les passant par les TfidfVectorizerétiquettes à travers le MultiLabelBinarizeret en créant un OneVsRestClassifieravec un SGDClassifiercomme estimateur.

Cependant, lorsque je teste mon classificateur, je n'obtiens que des scores allant jusqu'à 0,29, ce qui d'après ce que j'ai lu est assez faible pour des problèmes similaires. J'ai essayé plusieurs options sur le TfidfVectorizer comme les mots vides, les unigrammes, le stemming et rien ne semble changer autant le résultat.

J'ai également utilisé GridSearchCVpour obtenir les meilleurs paramètres pour mon estimateur et actuellement je n'ai plus d'idées sur ce qu'il faut essayer ensuite.

En même temps, ce que je comprends que je ne peux pas utiliser scikit.metricsavec OneVsRestClassifieralors comment puis - je obtenir de façon à comprendre ce qui ne va pas certains paramètres (F1, précision, rappel , etc.)?

Cela pourrait-il être un problème avec mon corpus de données?

Mise à jour: J'ai également essayé d' utiliser CountVectorizeret HashingVectorizeret les pipelining pour TfidfTransformermais les résultats sont similaires. Je suppose donc que l'approche du sac de mots fait mieux dans le domaine de la tokenisation et que le reste appartient au classificateur ...

mobius
la source
1
Que mesure 0,29? Précision? Autre chose?
Sycorax dit Reinstate Monica
@GeneralAbrial Selon la documentation de scikit fonctionnant scoresur le classificateur,Returns the mean accuracy on the given test data and labels. In multi-label classification, this is the subset accuracy which is a harsh metric since you require for each sample that each label set be correctly predicted.
mobius
C'est ce que tu as fait? Il ne ressort pas clairement de votre question que c'est le cas, c'est donc une question parfaitement raisonnable.
Sycorax dit Réintégrer Monica le
@GeneralAbrial Oui, c'est ce que j'ai fait. Désolé pour la confusion, j'essayais de garder la question dans un mode plus théorique plutôt que de développement.
mobius
Pouvez-vous s'il vous plaît ajouter votre code ici? Spécifiquement utilisez-vous le sample_weight = "équilibré" pour SGD? Mais il pourrait y avoir d'autres choses à noter une fois que nous aurons vu votre code.
Diego

Réponses:

21

La précision du sous-ensemble est en effet une métrique sévère. Pour avoir une idée de la qualité du bon ou du mauvais 0,29, une idée:

  • regardez combien d'étiquettes vous avez en moyenne pour chaque échantillon
  • regardez l'accord interannotateur, s'il est disponible (sinon, essayez vous-même de voir quelle précision de sous-ensemble est obtenue lorsque vous êtes le classificateur)
  • penser si le sujet est bien défini
  • regardez combien d'échantillons vous avez pour chaque étiquette

Vous voudrez peut-être également calculer le score de hamming, pour voir si votre classificateur est sans aucune idée, ou s'il est plutôt bon, mais avez du mal à prédire correctement toutes les étiquettes. Voir ci-dessous pour calculer le score de hamming.

En même temps, d'après ce que je comprends, je ne peux pas utiliser scikit.metrics avec OneVsRestClassifier alors comment puis-je obtenir des métriques (F1, Precision, Recall, etc.) afin de déterminer ce qui ne va pas?

Voir Comment calculer la précision / le rappel pour la classification multiclasse-étiquette multiple? . J'ai oublié si sklearn le prend en charge, je me souviens qu'il avait certaines limites, par exemple sklearn ne prend pas en charge le multi-label pour la matrice de confusion . Ce serait une bonne idée de voir ces chiffres en effet.


Score de Hamming :

Dans un paramètre de classification à étiquettes multiples , sklearn.metrics.accuracy_scorene calcule que la précision du sous - ensemble (3): c'est-à-dire que l'ensemble d'étiquettes prévu pour un échantillon doit correspondre exactement à l'ensemble d'étiquettes correspondant dans y_true.

Cette façon de calculer la précision est parfois appelée, peut-être de manière moins ambiguë, rapport de correspondance exact (1):

entrez la description de l'image ici

Une autre façon typique de calculer la précision est définie en (1) et (2), et moins ambiguement appelée score de Hamming (4) (car elle est étroitement liée à la perte de Hamming), ou précision basée sur l'étiquette ). Il est calculé comme suit:

entrez la description de l'image ici

Voici une méthode python pour calculer le score de Hamming:

# Code by /programming//users/1953100/william
# Source: /programming//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
    /programming//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))) 

Les sorties:

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

(1) Sorower, Mohammad S. " Une étude de la littérature sur les algorithmes pour l'apprentissage multi-labels. " Oregon State University, Corvallis (2010).

(2) Tsoumakas, Grigorios et Ioannis Katakis. " Multi-label classification: An overview. " Dept. of Informatics, Université Aristote de Thessalonique, Grèce (2006).

(3) Ghamrawi, Nadia et Andrew McCallum. " Classification collective multi-labels " . Actes de la 14ème conférence internationale ACM sur la gestion de l'information et des connaissances. ACM, 2005.

(4) Godbole, Shantanu et Sunita Sarawagi. " Méthodes discriminatoires pour la classification multi-étiquetée. " Advances in Knowledge Discovery and Data Mining. Springer Berlin Heidelberg, 2004. 22-30.

Franck Dernoncourt
la source
excellente réponse, cela m'a juste rendu meilleur :) Je vais le lire plus en détail, essayez le score de Hamming et revenez vers vous!
mobius
Pour être honnête, il n'est pas entièrement clair pour moi quelle est exactement la précision du sous-ensemble (Exact Match Ratio). Pourriez-vous expliquer un peu plus? Il semble qu'en cas de multiclasse ce soit identique à rappeler.
Poete Maudit
La hamming_scorefonction génère des erreurs sur Keras: <ipython-input-34-16066d66dfdd> dans hamming_score (y_true, y_pred, normaliser, sample_weight) 60 '' '61 acc_list = [] ---> 62 pour i dans la plage (y_true.shape [ 0]): 63 set_true = set (np.where (y_true [i]) [0]) 64 set_pred = set (np.where (y_pred [i]) [0]) TypeError: index retourné non-int (type NoneType )
rjurney
0

Le score de 0,29 n'est-il pas suffisant? À quoi ressemble votre matrice de confusion? Y a-t-il des sujets qui ne peuvent pas être séparés peut-être en ne regardant que le contenu des mots?

Sinon, essayez de contourner votre problème: supposez que les faibles scores sont en fait les meilleurs que votre classificateur puisse faire sur vos données. Cela signifierait que vos documents ne peuvent pas être classés en utilisant cette approche.

Pour tester cette hypothèse, vous avez besoin d'un ensemble de documents de test avec des caractéristiques connues de sac de mot (que vous créez vous-même). Vous devriez obtenir des scores de 100%.

Si vous ne le faites pas, vous avez un bug. Sinon, vous avez besoin d'une approche différente pour classer vos documents. Demandez-vous: en quoi les documents des différentes classes diffèrent-ils les uns des autres? Dois-je regarder d'autres fonctionnalités de mes documents, etc.

Ytsen de Boer
la source
Mis à part les chiffres, je sens que 0,29 est faible. J'utilise le modèle formé pour prédire des sujets sur des documents que j'ai déjà utilisés dans la formation pour tester manuellement le classificateur. Je n'ai pas pu obtenir au moins le même nombre de sujets que l'utilisateur a entré manuellement sur le document. Je reçois généralement un sous-ensemble d'entre eux. Aussi en ce qui concerne la question de la matrice de confusion, je ne pense pas que je puisse obtenir une matrice de confusion sur OneVsRestClassifier en utilisant le scikit.metrics ... Je vais le vérifier cependant
mobius