Понимание потери шарнира в scikit Learn

Я видел эту диаграмму потери шарнира:

https://math.stackexchange.com/questions/782586/how-do-you-minimize-hinge-loss

А также здесь:

https://programmatically.com/understanding-hinge-loss-and-the-svm-cost-function/

Однако создание «того же» графика с помощью scikit-learn очень похоже, но кажется «противоположным». Код выглядит следующим образом:

      from sklearn.metrics import hinge_loss
import matplotlib.pyplot as plt
import numpy as np


predicted = np.arange(-10, 11, 1)
y_true = [1] * len(predicted)
loss = [0] * len(predicted)
for i, (p, y) in enumerate(zip(predicted, y_true)):
  loss[i] = hinge_loss(np.array([y]), np.array([p]))
plt.plot(predicted, loss)

plt.axvline(x = 0, color = 'm', linestyle='dashed')
plt.axvline(x = -1, color = 'r', linestyle='dashed')
plt.axvline(x = 1, color = 'g', linestyle='dashed')

потеря петли для y_true == 1

И некоторые конкретные моменты на графике выше:

      hinge_loss([1], [-5]) = 0.0,
hinge_loss([1], [-1]) = 0.0,
hinge_loss([1], [0])  = 1.0,
hinge_loss([1], [1])  = 2.0,
hinge_loss([1], [5])  = 6.0
      predicted = np.arange(-10, 11, 1)
y_true = [-1] * len(predicted)
loss = [0] * len(predicted)
for i, (p, y) in enumerate(zip(predicted, y_true)):
  loss[i] = hinge_loss(np.array([y]), np.array([p]))
plt.plot(predicted, loss)

plt.axvline(x = 0, color = 'm', linestyle='dashed')
plt.axvline(x = -1, color = 'r', linestyle='dashed')
plt.axvline(x = 1, color = 'g', linestyle='dashed')

потеря шарнира для y_true == -1

И некоторые конкретные моменты на графике выше:

      hinge_loss([-1], [-5]) = 0.0,
hinge_loss([-1], [-1]) = 0.0,
hinge_loss([-1], [0])  = 1.0,
hinge_loss([-1], [1])  = 2.0,
hinge_loss([-1], [5])  = 6.0

Может кто-нибудь объяснить мне, почему scikit-learn кажется противоположным двум другим первым графикам?

Спасибо заранее


РЕДАКТИРОВАТЬ: Основываясь на ответе, я могу воспроизвести тот же результат, не переворачивая значения. Это основано на следующем: какhinge_loss([0], [-1])==0иhinge_loss([-2], [-1])==0. Исходя из этого, я могу назватьhinge_loss()с массивом из двух значений без изменения рассчитанного убытка.

Следующий код не переворачивает значения:

      predicted = np.arange(-10, 11, 1)
y_true = [1] * len(predicted)
loss = [0] * len(predicted)
for i, (p, y) in enumerate(zip(predicted, y_true)):
  loss[i] = hinge_loss(np.array([y, 0]), np.array([p, -1])) * 2
plt.plot(predicted, loss)

plt.axvline(x = 0, color = 'm', linestyle='dashed')
plt.axvline(x = -1, color = 'r', linestyle='dashed')
plt.axvline(x = 1, color = 'g', linestyle='dashed')
      predicted = np.arange(-10, 11, 1)
y_true = [-1] * len(predicted)
loss = [0] * len(predicted)
for i, (p, y) in enumerate(zip(predicted, y_true)):
  loss[i] = hinge_loss(np.array([y,-2]), np.array([p,-1])) * 2
plt.plot(predicted, loss)

plt.axvline(x = 0, color = 'm', linestyle='dashed')
plt.axvline(x = -1, color = 'r', linestyle='dashed')
plt.axvline(x = 1, color = 'g', linestyle='dashed')

Теперь возникает вопрос, почему для каждого соответствующего случая эти «комбинации» значений работают хорошо.

1 ответ

Взглянув на код, лежащий в основеhinge_lossреализации, в бинарном случае происходит следующее:

      lbin = LabelBinarizer(neg_label=-1)
y_true = lbin.fit_transform(y_true)[:, 0]

try:
    margin = y_true * pred_decision
except TypeError:
    raise TypeError("pred_decision should be an array of floats.")

losses = 1 - margin
np.clip(losses, 0, None, out=losses)
np.average(losses, weights=sample_weight)

Благодаря тому факту, чтоLabelBinarizer.fit_transform()поведение в случае одной метки по умолчанию возвращает массив отрицательных меток

      from sklearn.preprocessing import LabelBinarizer

lbin = LabelBinarizer(neg_label=-1)
lbin.fit_transform([1, 1, 1, 1, 1, 1, 1])   # returns array([[-1],[-1],[-1][-1],[-1],[-1],[-1]])

это означает, что (уникальный) знак метки переворачивается, что объясняет полученный вами сюжет.

Несмотря на то, что пример с одной меткой довольно странный, по этому вопросу были некоторые дебаты, см. https://github.com/scikit-learn/scikit-learn/issues/6723 , например. Копаясь в проблемах github, кажется, что они еще не приняли окончательного решения о возможных исправлениях, которые необходимо применить.

Ответ на РЕДАКТИРОВАТЬ:

Имо, как ты обогащаешься иpв цикле работает, потому что - таким образом - вы эффективно "убегаете" от случая с одной меткой (в частности, что действительно важно, так это то, как вы имеете дело сy). Действительно,

      lbin = LabelBinarizer(neg_label=-1)
lbin.fit_transform(y_true)[:, 0]

сy_true=np.array([y,0])=np.array([1,0])(первый случай) или сy_true=np.array([y,-2])=np.array([-1,-2])(второй случай) соответственно return иarray([ 1, -1]). С другой стороны, другая возможность для вашего второго случая, которую вы упоминаете в комментарии, а именноy_true=np.array([y,-1])=np.array([-1,-1])не дает уйти от случая с одной этикеткой (lbin.fit_transform(np.array([-1, -1]))[:, 0]возвращаетсяarray([-1, -1]); таким образом, вы снова попадаете в «ошибку», описанную выше).

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