Sklearn jaccard_score дает неверный результат

Я использовал sklearn.metrics.jaccard_score для сбора приведенной оценки из теста бинарной классификации моей модели Python. Он выводит, как показано ниже, но когда я вычисляю показатель вручную, он дает другое значение. Я ошибаюсь в значении «жаккарда» в использовании этой функции? Или я неправильно его использую? Все остальные показатели, собранные функциями sklearn, возвращают правильные значения. Далее следует мой код с проверкой жаккарда вручную (выполнение в калькуляторе путем сравнения векторов как наборов дает то же самое, что и я (не так сильно) с облегчением).

def test(X, y, model):
  predictions = model.predict(X, verbose=1).ravel()
  report = classification_report(y, predictions, target_names=['nao_doentes', 'doentes'])
  confMatrix = confusion_matrix(y, predictions)
  tn, fp, fn, tp = confMatrix.ravel()
  jaccard = jaccard_score(y, predictions) # Se comportando de forma estranha

  print(tn, fp, fn, tp)
  print(predictions)
  print(y)
  print(report)
  print(confMatrix)
  print("Jaccard by function: {}".format(jaccard))
      
  # Note that in binary classification, recall of the positive class is also known as “sensitivity”;
  # recall of the negative class is “specificity”.

  dice = ((2*tp) / ((2*tp) + fp + fn))
  jaccard = ((tp + tn) / ((2*(tp + tn + fn + fp)) - (tp + tn)))
  print(dice)
  print("Jaccard by hand: {}".format(jaccard))

И затем следует вывод:

2 0 1 1
[1. 0. 0. 0.]
[1 0 1 0]
              precision    recall  f1-score   support

 nao_doentes       0.67      1.00      0.80         2
     doentes       1.00      0.50      0.67         2

    accuracy                           0.75         4
   macro avg       0.83      0.75      0.73         4
weighted avg       0.83      0.75      0.73         4

[[2 0]
 [1 1]]
Jaccard by function: 0.5
0.6666666666666666
Jaccard by hand: 0.6

Во-вторых, почему classification_report, кажется, ставит nao_doentes (небольной, на португальском языке) как 1, а doentes (больной) как 0? Разве это не должно быть наоборот? nao_doentes устанавливается как 0, а doentes как 1 в моих наборах (то есть в y).

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
799
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Глядя на страницу справки , оценка Жаккара определяется как:

размер пересечения, разделенный на размер союза двух наборы этикеток,

И смотрят только на положительный класс:

jaccard_score может быть плохой метрикой, если нет положительных результатов для некоторых образцы или классы. Жаккара не определено, если нет истинных или предсказанные метки, и наша реализация вернет 0 баллов с предупреждение.

В матрице путаницы у вас есть:

intersection = tp # you have 1
union = tp+fp # you have 2 
jaccard = intersection / union

и должно дать вам 1/(1+1) = 0,5.

Ваш ярлык правильный. Вы можете преобразовать метки, и вы увидите, что вы получаете ту же матрицу путаницы:

import pandas as pd

labels = pd.Categorical(['nao_doentes','doentes'],categories=['nao_doentes','doentes'])
prediction = [1 ,0 ,0, 0]
y = [1 ,0, 1, 0]

pd.crosstab(labels[y],labels[prediction])

col_0   nao_doentes doentes
row_0       
nao_doentes 2   0
doentes     1   1

Оценка Jaccard, рассчитанная вручную в вашем вопросе, отличается от той, которая рассчитана с использованием scikit-learn по умолчанию jaccard_score, потому что уравнение, которое вы использовали вручную, вычисляет микроусредненную оценку Jaccard, тогда как версия scikit-learn по умолчанию вычисляет оценка только для положительного класса ("Doentes").

Просто чтобы увидеть, как это происходит, мы можем посмотреть, как sklearn jaccard_score с использованием метода по умолчанию сравнивается с расчетом вручную:

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

tp = 1
tn = 2
fp = 0
fn = 1

jaccard_score(y_true, y_pred)
#  0.5

# And we can check this by using the definition of the Jaccard score for the positive class:

tp / (tp + fp + fn)
#  0.5

Теперь давайте посмотрим на микроусредненный балл Жаккара (определение «микроусредненного» здесь взято из документации scikit-learn:

# scikit-learn:

jaccard_score(y_true, y_pred, average='micro')
# 0.6

# Definition of micro-averaged ("Calculate metrics globally by counting 
# the total true positives, false negatives and false positives"). 
# Here we have to define another set of outcomes but this time with the 
# original negative class as the positive class:

tp_0 = 2
fp_0 = 1
tn_0 = 1
fn_0 = 0

(tp+tp_0)/(tp+tp_0+fp+fp_0+fn+fn_0)
# 0.6

# And let's now compare this to the original calculation by hand in the question:
(tp + tn) / ((2*(tp + tn + fn + fp)) - (tp + tn))
# 0.6

Другие вопросы по теме