Como calcular a precisão, recall, exatidão e pontuação f1 para o caso multiclasse com o scikit learn?

109

Estou trabalhando em um problema de análise de sentimento, os dados se parecem com este:

label instances
    5    1190
    4     838
    3     239
    1     204
    2     127

Portanto, meus dados estão desequilibrados, visto que 1190 instancesestão marcados com 5. Para a classificação, estou usando o SVC do scikit . O problema é que eu não sei como equilibrar meus dados da maneira certa para calcular com precisão a precisão, recuperação, exatidão e pontuação f1 para o caso multiclasse. Então, tentei as seguintes abordagens:

Primeiro:

    wclf = SVC(kernel='linear', C= 1, class_weight={1: 10})
    wclf.fit(X, y)
    weighted_prediction = wclf.predict(X_test)

print 'Accuracy:', accuracy_score(y_test, weighted_prediction)
print 'F1 score:', f1_score(y_test, weighted_prediction,average='weighted')
print 'Recall:', recall_score(y_test, weighted_prediction,
                              average='weighted')
print 'Precision:', precision_score(y_test, weighted_prediction,
                                    average='weighted')
print '\n clasification report:\n', classification_report(y_test, weighted_prediction)
print '\n confussion matrix:\n',confusion_matrix(y_test, weighted_prediction)

Segundo:

auto_wclf = SVC(kernel='linear', C= 1, class_weight='auto')
auto_wclf.fit(X, y)
auto_weighted_prediction = auto_wclf.predict(X_test)

print 'Accuracy:', accuracy_score(y_test, auto_weighted_prediction)

print 'F1 score:', f1_score(y_test, auto_weighted_prediction,
                            average='weighted')

print 'Recall:', recall_score(y_test, auto_weighted_prediction,
                              average='weighted')

print 'Precision:', precision_score(y_test, auto_weighted_prediction,
                                    average='weighted')

print '\n clasification report:\n', classification_report(y_test,auto_weighted_prediction)

print '\n confussion matrix:\n',confusion_matrix(y_test, auto_weighted_prediction)

Terceiro:

clf = SVC(kernel='linear', C= 1)
clf.fit(X, y)
prediction = clf.predict(X_test)


from sklearn.metrics import precision_score, \
    recall_score, confusion_matrix, classification_report, \
    accuracy_score, f1_score

print 'Accuracy:', accuracy_score(y_test, prediction)
print 'F1 score:', f1_score(y_test, prediction)
print 'Recall:', recall_score(y_test, prediction)
print 'Precision:', precision_score(y_test, prediction)
print '\n clasification report:\n', classification_report(y_test,prediction)
print '\n confussion matrix:\n',confusion_matrix(y_test, prediction)


F1 score:/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:676: DeprecationWarning: The default `weighted` averaging is deprecated, and from version 0.18, use of precision, recall or F-score with multiclass or multilabel data or pos_label=None will result in an exception. Please set an explicit value for `average`, one of (None, 'micro', 'macro', 'weighted', 'samples'). In cross validation use, for instance, scoring="f1_weighted" instead of scoring="f1".
  sample_weight=sample_weight)
/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:1172: DeprecationWarning: The default `weighted` averaging is deprecated, and from version 0.18, use of precision, recall or F-score with multiclass or multilabel data or pos_label=None will result in an exception. Please set an explicit value for `average`, one of (None, 'micro', 'macro', 'weighted', 'samples'). In cross validation use, for instance, scoring="f1_weighted" instead of scoring="f1".
  sample_weight=sample_weight)
/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:1082: DeprecationWarning: The default `weighted` averaging is deprecated, and from version 0.18, use of precision, recall or F-score with multiclass or multilabel data or pos_label=None will result in an exception. Please set an explicit value for `average`, one of (None, 'micro', 'macro', 'weighted', 'samples'). In cross validation use, for instance, scoring="f1_weighted" instead of scoring="f1".
  sample_weight=sample_weight)
 0.930416613529

No entanto, estou recebendo avisos como este:

/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:1172:
DeprecationWarning: The default `weighted` averaging is deprecated,
and from version 0.18, use of precision, recall or F-score with 
multiclass or multilabel data or pos_label=None will result in an 
exception. Please set an explicit value for `average`, one of (None, 
'micro', 'macro', 'weighted', 'samples'). In cross validation use, for 
instance, scoring="f1_weighted" instead of scoring="f1"

Como posso lidar corretamente com meus dados desequilibrados para calcular as métricas do classificador da maneira certa?

new_with_python
fonte
Então, por que não adicionar averageparâmetro no terceiro caso?
yangjie
1
@yangjie eu não sei. Acabei de verificar a documentação, mas não estou entendendo como usar corretamente as métricas para dados não balanceados. Você poderia fornecer uma explicação mais ampla e um exemplo ?. Obrigado!
new_with_python

Respostas:

164

Acho que há muita confusão sobre quais pesos são usados ​​para quê. Não tenho certeza se sei exatamente o que o incomoda, então vou abordar vários tópicos, tenha paciência;).

Pesos de classe

Os pesos do class_weightparâmetro são usados ​​para treinar o classificador . Eles não são usados ​​no cálculo de nenhuma das métricas que você está usando : com pesos de classe diferentes, os números serão diferentes simplesmente porque o classificador é diferente.

Basicamente, em cada classificador scikit-learn, os pesos das classes são usados ​​para dizer ao seu modelo o quão importante é uma classe. Isso significa que durante o treinamento, o classificador fará um esforço extra para classificar adequadamente as classes com pesos elevados.
Como eles fazem isso é específico do algoritmo. Se quiser detalhes sobre como ele funciona para o SVC e o documento não fizer sentido para você, fique à vontade para mencioná-lo.

As métricas

Depois de ter um classificador, você deseja saber como está seu desempenho. Aqui você pode usar as métricas que você mencionou: accuracy, recall_score, f1_score...

Normalmente, quando a distribuição de classes é desequilibrada, a precisão é considerada uma escolha ruim, pois dá pontuações altas aos modelos que apenas prevêem a classe mais frequente.

Não vou detalhar todas essas métricas, mas observo que, com exceção de accuracy, elas são naturalmente aplicadas no nível da classe: como você pode ver neste printrelatório de classificação, elas são definidas para cada classe. Eles se baseiam em conceitos como true positivesou false negativeque exigem a definição de qual classe é a positiva .

             precision    recall  f1-score   support

          0       0.65      1.00      0.79        17
          1       0.57      0.75      0.65        16
          2       0.33      0.06      0.10        17
avg / total       0.52      0.60      0.51        50

O aviso

F1 score:/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:676: DeprecationWarning: The 
default `weighted` averaging is deprecated, and from version 0.18, 
use of precision, recall or F-score with multiclass or multilabel data  
or pos_label=None will result in an exception. Please set an explicit 
value for `average`, one of (None, 'micro', 'macro', 'weighted', 
'samples'). In cross validation use, for instance, 
scoring="f1_weighted" instead of scoring="f1".

Você recebe este aviso porque está usando o f1-score, recall e precisão sem definir como eles devem ser calculados! A pergunta poderia ser reformulada: a partir do relatório de classificação acima, como você produz um número global para a pontuação f1? Você poderia:

  1. Faça a média da pontuação f1 para cada classe: esse é o avg / totalresultado acima. Também é chamado de média de macro .
  2. Calcule a pontuação f1 usando a contagem global de verdadeiros positivos / falsos negativos, etc. (você soma o número de verdadeiros positivos / falsos negativos para cada classe). Aka micro média.
  3. Calcule uma média ponderada da pontuação f1. O uso 'weighted'no scikit-learn pesará a pontuação f1 pelo apoio da classe: quanto mais elementos uma classe tiver, mais importante será a pontuação f1 para essa classe no cálculo.

Essas são 3 das opções no scikit-learn; o aviso está lá para dizer que você deve escolher uma . Portanto, você deve especificar um averageargumento para o método de pontuação.

Qual você escolher depende de como você deseja medir o desempenho do classificador: por exemplo, a macro-média não leva em conta o desequilíbrio da classe e a pontuação f1 da classe 1 será tão importante quanto a pontuação f1 da classe 5. Se você usar a média ponderada, no entanto, terá mais importância para a classe 5.

Toda a especificação do argumento nessas métricas não é superclara no scikit-learn agora, ela ficará melhor na versão 0.18 de acordo com os documentos. Eles estão removendo alguns comportamentos padrão não óbvios e emitindo avisos para que os desenvolvedores os percebam.

Pontuação de computação

A última coisa que quero mencionar (sinta-se à vontade para pular se você estiver ciente disso) é que as pontuações só são significativas se forem calculadas em dados que o classificador nunca viu . Isso é extremamente importante, pois qualquer pontuação obtida nos dados que foram usados ​​para ajustar o classificador é completamente irrelevante.

Esta é uma maneira de fazer isso usando StratifiedShuffleSplit, que fornece a você uma divisão aleatória de seus dados (após embaralhamento) que preserva a distribuição do rótulo.

from sklearn.datasets import make_classification
from sklearn.cross_validation import StratifiedShuffleSplit
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, classification_report, confusion_matrix

# We use a utility to generate artificial classification data.
X, y = make_classification(n_samples=100, n_informative=10, n_classes=3)
sss = StratifiedShuffleSplit(y, n_iter=1, test_size=0.5, random_state=0)
for train_idx, test_idx in sss:
    X_train, X_test, y_train, y_test = X[train_idx], X[test_idx], y[train_idx], y[test_idx]
    svc.fit(X_train, y_train)
    y_pred = svc.predict(X_test)
    print(f1_score(y_test, y_pred, average="macro"))
    print(precision_score(y_test, y_pred, average="macro"))
    print(recall_score(y_test, y_pred, average="macro"))    

Espero que isto ajude.

ldirer
fonte
Para uma multiclasse, como você especifica o peso da classe? Por exemplo, o que class_weight={1:10}significa um dado que tem 3 classes?
Aziz Javed
Existe alguma maneira de obter pontuações de precisão do rótulo inteligente?
Ankur Sinha
Você pode explicar como o micro funciona de forma mais clara. Além disso, você não menciona nada sobre binário
humilde
Para mim, o shuffle estratificado estava criando problemas, então voltei para a divisão de teste de trem conforme estava aparecendo ValueError: The least populated class in y has only 1 member, which is too few. The minimum number of labels for any class cannot be less than 2.. Está funcionando bem com a divisão de teste de trem, mas alguém pode me ajudar por que estou recebendo esse erro com SSS? Obrigado.
Akash Kandpal
HI eu testei seu código, mas tenho esta mensagem de erro C: \ Usuários \\ Anaconda3 \ lib \ site-packages \ sklearn \ metrics \ identification.py: 976: Aviso de descontinuação: A partir da versão 0.18, a entrada binária não será tratada especialmente ao usar precisão / recall / pontuação F média. Use média = 'binário' para relatar apenas o desempenho positivo da classe. 'desempenho de classe positivo.',
Aviso de suspensão
73

Muitas respostas detalhadas aqui, mas não acho que você está respondendo às perguntas certas. Pelo que entendi a pergunta, existem duas preocupações:

  1. Como pontuar um problema multiclasse?
  2. Como faço para lidar com dados desequilibrados?

1

Você pode usar a maioria das funções de pontuação no scikit-learn tanto com problemas multiclasse quanto com problemas de classe única. Ex.:

from sklearn.metrics import precision_recall_fscore_support as score

predicted = [1,2,3,4,5,1,2,1,1,4,5] 
y_test = [1,2,3,4,5,1,2,1,1,4,1]

precision, recall, fscore, support = score(y_test, predicted)

print('precision: {}'.format(precision))
print('recall: {}'.format(recall))
print('fscore: {}'.format(fscore))
print('support: {}'.format(support))

Dessa forma, você acaba com números tangíveis e interpretáveis ​​para cada uma das classes.

| Label | Precision | Recall | FScore | Support |
|-------|-----------|--------|--------|---------|
| 1     | 94%       | 83%    | 0.88   | 204     |
| 2     | 71%       | 50%    | 0.54   | 127     |
| ...   | ...       | ...    | ...    | ...     |
| 4     | 80%       | 98%    | 0.89   | 838     |
| 5     | 93%       | 81%    | 0.91   | 1190    |

Então...

2

... você pode dizer se os dados desequilibrados são mesmo um problema. Se a pontuação para as classes menos representadas (classes 1 e 2) for menor do que para as classes com mais amostras de treinamento (classes 4 e 5), então você sabe que os dados desequilibrados são de fato um problema e você pode agir de acordo, como descrito em algumas das outras respostas neste tópico. No entanto, se a mesma distribuição de classe estiver presente nos dados que você deseja prever, seus dados de treinamento desequilibrado são um bom representante dos dados e, portanto, o desequilíbrio é uma coisa boa.

wonderkid2
fonte
1
Ótimo post e bem falado. Obrigado
Alvis
1
Ei, só uma pergunta complementar: como você imprimiu as etiquetas usando precision_recall_fscore_support? As etiquetas são impressas por encomenda?
BigD
@BigD Sim, consulte scikit-learn.org/stable/modules/generated/… na parte inferior. Defina average=Nonee defina os rótulos, então você obtém a métrica que está procurando, para cada um dos rótulos especificados.
wonderkid2
Existe alguma maneira de obter pontuações de precisão do rótulo inteligente?
Ankur Sinha
@trollster Não tenho certeza do que você quer dizer? Não é o que eu mostro nas pontuações de precisão labelwise das respostas?
wonderkid2
16

Questão colocada

Respondendo à pergunta 'qual métrica deve ser usada para classificação multiclasse com dados desequilibrados': Macro-F1-medir. Macro Precisão e Macro Recall também podem ser usados, mas eles não são tão facilmente interpretáveis ​​como para a classificação binária, eles já estão incorporados na medida F e métricas em excesso complicam a comparação de métodos, ajuste de parâmetros e assim por diante.

A micro média é sensível ao desequilíbrio de classe: se o seu método, por exemplo, funciona bem para os rótulos mais comuns e bagunça totalmente os outros, a micro média métrica mostra bons resultados.

A média de ponderação não é adequada para dados desequilibrados, porque ela pondera por contagens de rótulos. Além disso, é dificilmente interpretável e impopular: por exemplo, não há menção de tal média na seguinte pesquisa muito detalhada que eu recomendo fortemente que você analise:

Sokolova, Marina e Guy Lapalme. "Uma análise sistemática de medidas de desempenho para tarefas de classificação." Processamento e gerenciamento de informações 45,4 (2009): 427-437.

Pergunta específica do aplicativo

No entanto, voltando à sua tarefa, eu pesquisaria 2 tópicos:

  1. métricas comumente usadas para sua tarefa específica - permite (a) comparar seu método com outros e entender se você fez algo errado, e (b) não explorar isso sozinho e reutilizar as descobertas de outra pessoa;
  2. custo de erros diferentes de seus métodos - por exemplo, o caso de uso de seu aplicativo pode contar com revisões de 4 e 5 estrelas apenas - neste caso, uma boa métrica deve contar apenas esses 2 rótulos.

Métricas comumente usadas. Como posso inferir depois de olhar a literatura, existem 2 métricas de avaliação principais:

  1. Precisão , que é usada, por exemplo, em

Yu, April e Daryl Chang. "Multiclass Sentiment Prediction using Yelp Business."

( link ) - observe que os autores trabalham com quase a mesma distribuição de classificações, consulte a Figura 5.

Pang, Bo e Lillian Lee. "Vendo estrelas: explorando relações de classe para categorização de sentimento com respeito a escalas de avaliação." Anais da 43ª Reunião Anual da Association for Computational Linguistics. Association for Computational Linguistics, 2005.

( link )

  1. MSE (ou, com menos frequência, Erro Médio Absoluto - MAE ) - ver, por exemplo,

Lee, Moontae e R. Grafe. "Análise de sentimento multiclasse com críticas de restaurantes." Projetos Finais de CS N 224 (2010).

( link ) - eles exploram tanto a precisão quanto o MSE, considerando o último ser melhor

Pappas, Nikolaos, Rue Marconi e Andrei Popescu-Belis. "Explicando as estrelas: aprendizado ponderado de várias instâncias para análise de sentimento baseada em aspectos." Anais da Conferência de 2014 sobre Métodos Empíricos em Processamento de Linguagem Natural. No. EPFL-CONF-200899. 2014.

( link ) - eles utilizam o scikit-learn para avaliação e abordagens de linha de base e afirmam que seu código está disponível; entretanto, não consigo encontrar, então se precisar, escreva uma carta aos autores, o trabalho é bem novo e parece ter sido escrito em Python.

Custo de erros diferentes . Se você se preocupa mais em evitar erros grosseiros, por exemplo, atribuir uma crítica de 1 a 5 estrelas ou algo assim, dê uma olhada no MSE; se a diferença importa, mas não tanto, tente MAE, uma vez que não ao quadrado diff; caso contrário, fique com Precisão.

Sobre abordagens, não métricas

Tente abordagens de regressão, por exemplo, SVR , uma vez que geralmente superam classificadores Multiclass como SVC ou OVA SVM.

Nikita Astrakhantsev
fonte
13

Em primeiro lugar, é um pouco mais difícil usar apenas a análise de contagem para saber se seus dados estão desequilibrados ou não. Por exemplo: 1 em 1000 observação positiva é apenas um ruído, erro ou um avanço na ciência? Nunca se sabe.
Portanto, é sempre melhor usar todo o seu conhecimento disponível e escolher seu status com toda sabedoria.

Ok, e se estiver realmente desequilibrado?
Mais uma vez - observe seus dados. Às vezes, você pode encontrar uma ou duas observações multiplicadas por cem vezes. Às vezes é útil criar essas observações falsas de uma classe.
Se todos os dados estiverem limpos, a próxima etapa é usar pesos de classe no modelo de previsão.

E quanto às métricas multiclasse?
Na minha experiência, nenhuma de suas métricas é normalmente usada. Há duas razões principais.
Primeiro: é sempre melhor trabalhar com probabilidades do que com predição sólida (porque de que outra forma você poderia separar modelos com predição de 0,9 e 0,6 se ambos fornecem a mesma classe?)
E segundo: é muito mais fácil comparar seus modelos de predição e construir novos aqueles dependendo de apenas uma boa métrica. Pela
minha experiência, eu poderia recomendar logloss ou MSE (ou apenas significar erro quadrático).

Como consertar os avisos do sklearn?
Simplesmente (como yangjie notou) sobrescrever o averageparâmetro com um destes valores: 'micro'(calcular métricas globalmente), 'macro'(calcular métricas para cada rótulo) ou 'weighted'(igual ao macro, mas com pesos automáticos).

f1_score(y_test, prediction, average='weighted')

Todos os seus avisos vieram depois de chamar funções de métricas com averagevalor padrão 'binary'que é impróprio para previsão multiclasse.
Boa sorte e divirta-se com o aprendizado de máquina!

Edit:
eu encontrei outra recomendação do respondente para mudar para abordagens de regressão (por exemplo, SVR) com a qual não posso concordar. Pelo que me lembro, não existe nem mesmo regressão multiclasse. Sim, existe uma regressão multilabel que é muito diferente e sim, é possível, em alguns casos, alternar entre regressão e classificação (se as classes forem classificadas de alguma forma), mas é muito raro.

O que eu recomendaria (no escopo do scikit-learn) é tentar outras ferramentas de classificação muito poderosas: aumento de gradiente , floresta aleatória (minha favorita), KNeighbors e muito mais.

Depois disso, você pode calcular a média aritmética ou geométrica entre as previsões e, na maioria das vezes, você obterá resultados ainda melhores.

final_prediction = (KNNprediction * RFprediction) ** 0.5
Vlad Mironov
fonte
1
> "alternar entre regressão e classificação (se as classes forem classificadas de alguma forma), mas é muito raro" É o caso: 5> 4> 3> 2> 1. Eu sugiro que você dê uma olhada em documentos para esta tarefa - há muitas abordagens de regressão e classificação para a tarefa (às vezes no mesmo trabalho).
Nikita Astrakhantsev
Então, não é nem mesmo uma classificação multiclasse, mas uma regressão simples.
Vlad Mironov
Sim, internamente ou do ponto de vista do ML, é uma regressão, mas na etapa final, convertemos os resultados da regressão em rótulos, portanto, é uma classificação multiclasse - do ponto de vista do usuário ou aplicativo.
Nikita Astrakhantsev