Métricas de classificação de vários rótulos no scikit

19

Estou tentando criar um classificador de várias etiquetas para atribuir tópicos a documentos existentes usando o scikit

Estou processando meus documentos passando-os TfidfVectorizerpelos rótulos MultiLabelBinarizere criando um OneVsRestClassifiercom um SGDClassifiercomo estimador.

No entanto, ao testar meu classificador, apenas recebo pontuações de 0,29, o que pelo que li é bastante baixo para problemas semelhantes. Tentei várias opções no TfidfVectorizer, como palavras irrelevantes, unigramas, stemming e nada parece mudar tanto o resultado.

Também usei GridSearchCVpara obter os melhores parâmetros para meu estimador e, atualmente, estou sem ideias sobre o que tentar em seguida.

Ao mesmo tempo, pelo que eu entendo que não posso usar scikit.metricscom OneVsRestClassifierassim como eu posso obter algumas métricas (F1, precisão, Recall etc), de modo a descobrir o que está errado?

Pode haver um problema com meu corpus de dados?

Update: Eu também tentei usar CountVectorizere HashingVectorizere canalizando-os para TfidfTransformer, mas os resultados são semelhantes. Por isso, acho que a abordagem do pacote de palavras está fazendo o melhor no domínio da tokenização e o resto depende do classificador ...

mobius
fonte
1
O que é medição de 0,29? Precisão? Algo mais?
Sycorax diz Restabelecer Monica
@GeneralAbrial acordo com a documentação scikit correndo scoreno classificador,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
Foi isso que você fez? Não está claro, de maneira alguma, a sua pergunta de que esse é o caso, portanto é uma pergunta perfeitamente razoável.
Sycorax diz Restabelecer Monica
@GeneralAbrial Sim, foi isso que fiz. Desculpe a confusão, eu estava tentando manter a questão de um modo mais teórico do que de desenvolvimento.
mobius
Você pode adicionar seu código aqui? Especificamente, você está usando o sample_weight = "equilibrado" para SGD? Mas pode haver outras coisas a serem observadas depois que vermos seu código.
Diego

Respostas:

21

A precisão do subconjunto é de fato uma métrica severa. Para ter uma noção de quão bom ou ruim é 0,29, alguma idéia:

  • veja quantas etiquetas você tem uma média para cada amostra
  • observe o contrato entre anotadores, se disponível (se não estiver, tente ver qual precisão de subconjunto é obtida quando você é o classificador)
  • pense se o tópico está bem definido
  • veja quantas amostras você tem para cada etiqueta

Você também pode calcular a pontuação de hamming, para ver se o seu classificador não tem noção ou é decentemente bom, mas tem problemas para prever todos os rótulos corretamente. Veja abaixo para calcular a pontuação de hamming.

Ao mesmo tempo, pelo que entendi, não posso usar o scikit.metrics com o OneVsRestClassifier, então como posso obter algumas métricas (F1, Precision, Recall etc.) para descobrir o que está errado?

Consulte Como calcular a precisão / recuperação para a classificação multiclass-multilabel? . Eu esqueci se o sklearn suporta, lembro que tinha algumas limitações, por exemplo, o sklearn não suporta rótulos múltiplos para matriz de confusão . Seria uma boa ideia ver esses números de fato.


Pontuação de Hamming :

Em uma configuração de classificação de vários rótulos , sklearn.metrics.accuracy_scoreapenas calcula a precisão do subconjunto (3): ou seja, o conjunto de rótulos previsto para uma amostra deve corresponder exatamente ao conjunto de rótulos correspondente em y_true.

Essa maneira de calcular a precisão é às vezes chamada, talvez menos ambiguamente, da proporção exata de correspondência (1):

insira a descrição da imagem aqui

Outra maneira típica de calcular a precisão é definida em (1) e (2), e menos ambiguamente referida como a pontuação de Hamming (4) (uma vez que está intimamente relacionada à perda de Hamming) ou precisão baseada em rótulo ). É calculado da seguinte forma:

insira a descrição da imagem aqui

Aqui está um método python para calcular a pontuação 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))) 

Saídas:

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

(1) Sorower, Mohammad S. " Uma pesquisa bibliográfica sobre algoritmos para aprendizado de vários rótulos " . Oregon State University, Corvallis (2010).

(2) Tsoumakas, Grigorios e Ioannis Katakis. "Classificação multi-etiqueta: Uma visão geral. " Departamento de Informática, Universidade Aristóteles de Thessaloniki, Grécia (2006).

(3) Ghamrawi, Nadia e Andrew McCallum. "Classificação coletiva de rótulos múltiplos. " Anais da 14ª conferência internacional da ACM sobre gestão de informações e conhecimento. ACM, 2005.

(4) Godbole, Shantanu e Sunita Sarawagi. " Métodos discriminatórios para classificação com vários rótulos. " Avanços na descoberta de conhecimento e mineração de dados. Springer Berlin Heidelberg, 2004. 22-30.

Franck Dernoncourt
fonte
ótima resposta, apenas me fez melhorar :) Vou ler mais detalhadamente, tente a pontuação de Hamming e volte para você!
mobius 11/09/16
Para ser sincero, não está totalmente claro para mim qual é exatamente a precisão do subconjunto (relação de correspondência exata). Poderia explicar um pouco mais? Parece que, no caso de multiclasse, isso é idêntico ao recall.
Poete Maudit 03/06
A hamming_scorefunção gera erros no Keras: <ipython-input-34-16066d66dfdd> em hamming_score (y_true, y_pred, normalize, sample_weight) 60 '' '61 acc_list = [] ---> 62 para i no intervalo (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: índice retornado não int (tipo NoneType )
rjurney 22/07
0

A pontuação de 0,29 não é suficiente? Como é a sua matriz de confusão? Existem alguns tópicos que não podem ser separados talvez apenas olhando o conteúdo da palavra?

Caso contrário, tente inverter o problema: Imagine que as pontuações mais baixas são realmente o melhor que o seu classificador pode fazer nos seus dados. Isso significaria que seus documentos não são classificáveis ​​usando essa abordagem.

Para testar esta hipótese, você precisa de um conjunto de documentos de teste com características conhecidas da palavra-chave (que você mesmo cria). Você deve obter 100% de pontuação.

Caso contrário, você tem um bug. Caso contrário, você precisará de uma abordagem diferente para classificar seus documentos. Pergunte a si mesmo: como os documentos das diferentes classes diferem entre si? Preciso examinar outros recursos dos meus documentos, etc.

Ytsen de Boer
fonte
Além dos números, sinto que 0,29 é baixo. Eu uso o modelo treinado para prever tópicos em documentos que eu já usei no treinamento para testar manualmente o classificador. Não consegui obter pelo menos o mesmo número de tópicos que o usuário inseriu manualmente no documento. Normalmente, apenas pego um subconjunto deles. Também no que diz respeito à questão da matriz de confusão, eu não acho que eu posso obter uma matriz de confusão no OneVsRestClassifier usando os scikit.metrics ... Vou verificá-la embora
mobius