Резюме функций потерь в pytorch - классификация и сегментация

PyTorch

Практические заметки по интерактивной сегментации 2

Одна из проблем, которую необходимо учитывать при обучении сети глубокому обучению, — это выбор функции потерь. В последние годы Focal Loss был очень популярен в сегментации, но во многих проектах все еще используются базовые функции потерь, такие как Dice и CrossEntropy, и разница в эффекте неудивительна.Видно, что есть еще много мест, которые стоит изучить в традиционных Потеря.

В этой статье в основном проводится анализ функции потерь в стиле справочника в задаче сегментации и классификации, а также описываются характеристики различных потерь, которые я знаю, с упором на фактический метод расчета, чтобы интуитивно понять их значение.

Версия, на которой основан эксперимент, — это текущая стабильная версия pytorch-1.7.1.

содержание

Детали формулы расчета

Формула расчета общих потерь выполняетсяL(x,y)=func{l1,l2,...,lN}L(x,y)=func\{l_1, l_2, ..., l_N\}^\top, поэтому следующие формулы записывают толькоlnl_nрасчетная часть.

nn.L1Loss

Это MAE (средняя абсолютная ошибка), а формула расчетаln=xnynl_n = |x_n-y_n|Есть два режима среднего и суммы, которые контролируются редукцией.

пример

target = torch.tensor([1,1,0,1,0]).float()
output = torch.tensor([1,0,0,0,0]).float()

loss_fn = torch.nn.L1Loss(reduction='mean')
loss = loss_fn(output, target)
print(loss)

loss_fn = torch.nn.L1Loss(reduction='sum')
loss = loss_fn(output, target)
print(loss)

результат

tensor(0.4000)
tensor(2.)

nn.MSELoss

Как следует из названия, среднеквадратическая ошибка, которая является обычным членом L2, рассчитывается какln=(xnyn)2l_n = (x_n-y_n)^2Есть два режима среднего и суммы, которые контролируются редукцией.

пример

target = torch.tensor([1,0,0,1,0]).float()
output = torch.tensor([1,2,0,0,0]).float()

loss_fn = torch.nn.MSELoss(reduction='mean')
loss = loss_fn(output, target)
print(loss)

loss_fn = torch.nn.MSELoss(reduction='sum')
loss = loss_fn(output, target)
print(loss)

результат

tensor(1.)
tensor(5.)

nn.SmoothL1Loss

Небольшое сглаживание выполняется на уровне L1, который менее чувствителен к выбросам, чем MSELoss.

ln={0.5(xnyn)2/beta, if xnyn<betaxnyn0.5*beta, otherwise l_{n}=\left\{\begin{array}{ll} 0.5\left(x_{n}-y_{n}\right)^{2} / \text {beta}, & \text { if }\left|x_{n}-y_{n}\right|<\text {beta} \\ \left|x_{n}-y_{n}\right|-0.5 * \text {beta}, & \text { otherwise } \end{array}\right.

Используется в Fast-RCNN, чтобы избежать взрывных градиентов.

nn.NLLLoss

отрицательная логарифмическая потеря вероятности для обучения классификаторов n-класса, Для несбалансированных наборов данных к категориям можно добавить веса, формулаln=wynxn,yn,wc=weight[c]1l_n = -w_{y_n}x_{n,y_n}, -w_c=weight[c]\cdot1

Ожидаемая форма ввода(N,C)и(N)(Н, С) и (Н)NN— размер партии, а C — количество категорий;

Вычислите отрицательное значение вероятности соответствующей категории цели каждого случая, а затем получите среднее значение/сумму, которое обычно используется в сочетании с LogSoftMax для получения логарифмической вероятности.

пример

target = torch.tensor([1,0,3])
output = torch.randn(3,5)
print(output)

loss_fn = torch.nn.NLLLoss()
loss = loss_fn(output, target)
print(loss)

результат

tensor([[ 0.1684, -0.2378, -0.5189,  1.5398, -1.1828],
        [-0.4370,  0.3035,  1.3718, -0.2823, -0.4714],
        [ 0.2863, -0.3008,  0.8902,  0.4902, -0.4487]])
tensor(0.0615)

результат0.0615×3=(0.2378)(0.4370)(0.4902)0.0615 \times 3 = -(-0.2378)-(-0.4370)-(0.4902)

nn.CrossEntropyLoss

Классический убыток, формула расчета:

weight[class](log(exp(x[class])jexp(x[j])))=weight[class](x[class]+log(jexp(x[j])))weight[class](-\log \left(\frac {\exp (x[\text {class}])}{\sum_{j} \exp (x[j])}\right))=weight[class](-x[\text {class}]+\log \left(\sum_{j} \exp (x[j])\right))

Это эквивалентно первому сопоставлению выходного значения с каждым значением через softmax в[0,1][0,1], и на пространстве, где сумма равна 1. Есть надежда, что чем меньше потери, соответствующие правильному классу, тем лучше, так что да(exp(x[class])jexp(x[j]))\left(\frac {\exp (x[\text {class}])}{\sum_{j} \exp (x[j])}\right)спроситьlog()-log(), Пучок[0,1][0,1]сопоставить с[0,+][0,+\infty]Чем выше вероятность правильных элементов, тем меньше общий проигрыш.

CrossEntropyLoss(x) в факеле эквивалентен NLLLoss(LogSoftmax(x))

Ожидаемый ввод представляет собой ненормализованную оценку, а форма ввода такая же, как у NLL, т. е.(N,C)и(N)(Н, С) и (Н)

Пример 1

target = torch.tensor([1,0,3])
output = torch.randn(3,5)
print(output)

loss_fn = torch.nn.CrossEntropyLoss()
loss = loss_fn(output, target)
print(loss)

результат

tensor([[-0.6324,  0.1134,  0.0695, -1.6937, -0.3634],
        [ 1.2044,  2.0876, -1.6558, -0.4869, -0.8516],
        [-0.7290, -0.4808,  0.8488, -0.3595, -1.3598]])
tensor(1.4465)

Пример 2 — CrossEntropyLoss, реализованный в numpy

target = torch.tensor([1,0,3])
output = torch.randn(3,5)
print(output)

result = np.array([0.0, 0.0, 0.0])
for ix in range(3):
    log_sum = 0.0
    for iy in range(5):
        if(iy==target[ix]): result[ix] += -output[ix, iy]
        log_sum += exp(output[ix, iy])
    result[ix] += log(log_sum)
print(result)
print(np.mean(result))

loss_fn = torch.nn.CrossEntropyLoss()
loss = loss_fn(output, target)
print(loss)

результат

tensor([[ 1.6021,  0.5762, -1.9105, -1.0844, -0.0256],
        [ 1.0483,  0.8033,  1.1037, -1.2296,  1.2662],
        [ 0.7592, -2.6041, -1.6092, -0.2643,  1.2362]])
[1.52833433 1.43165374 2.15453246]
1.704840179536648
tensor(1.7048)

nn.BCELoss и nn.BCEWithLogitsLoss

Бинарная перекрестная энтропия, формула выглядит следующим образом:

ln=wn[ynlogxn+(1yn)log(1xn)]l_{n}=-w_{n}\left[y_{n} \cdot \log x_{n}+\left(1-y_{n}\right) \cdot \log \left(1-x_{n}\right)\right]

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

BCELoss необходимо сначала вручную ввести сигмоид, а затем добавить 1 для каждой позиции, если классификация равна 1.log(exp(x))-log(exp(x))иначе добавитьlog(exp(1x))-log(exp(1-x)), и, наконец, усредняется.

BCEWithLogitsLoss сигмоид не нужен, все остальное точно так же.

пример

target = torch.tensor([[1,0,1],[0,1,1]]).float()
raw_output = torch.randn(2,3)
output = torch.sigmoid(raw_output)
print(output)

result = np.zeros((2,3), dtype=np.float)
for ix in range(2):
    for iy in range(3):
        if(target[ix, iy]==1): 
            result[ix, iy] += -log(output[ix, iy])
        elif(target[ix, iy]==0): 
            result[ix, iy] += -log(1-output[ix, iy])

print(result)
print(np.mean(result))

loss_fn = torch.nn.BCELoss(reduction='none')
print(loss_fn(output, target))
loss_fn = torch.nn.BCELoss(reduction='mean')
print(loss_fn(output, target))
loss_fn = torch.nn.BCEWithLogitsLoss(reduction='mean')
print(loss_fn(raw_output, target))

результат

tensor([[0.3370, 0.2463, 0.4499],
        [0.2124, 0.3505, 0.7828]])
[[1.08756434 0.28280236 0.79866814]
 [0.23878274 1.04849163 0.24483089]]
0.6168566833989618
tensor([[1.0876, 0.2828, 0.7987],
        [0.2388, 1.0485, 0.2448]])
tensor(0.6169)
tensor(0.6169)

nn.MultiLabelMarginLoss

многоклассовая многоклассовая потеря шарнира В отличие от BCELoss, который переводит проблему в 2 категории, этот проигрыш рассчитан на не взаимоисключающую мультикатегорию (мультикатегория мультикатегория),

loss(x,y)=ijmax(0,1(x[y[j]]x[i]))xsize(0)\operatorname{loss}(x, y)=\sum_{i j} \frac {\max (0,1-(x[y[j]]-x[i]))}{\mathrm{x} \cdot \operatorname{size}(0)}

Распространенной формой HingeLoss являетсяln=max(0,1xnyn)l_n = \mathrm{max}(0, 1-x_ny_n)вxnx_nдля предсказания,yny_nявляется истинным значением. еслиxnx_nиyn(+1/1)y_n(+1/-1)Символы те же, тогдаxn|x_n|Чем больше потеря, тем меньше потеря, до 0 Если знаки разные, потери должны быть больше 1, аxn|x_n|Чем больше потеря, тем больше потеря.

В общем, цель этой тренировки функции потерь состоит в том, чтобы подогнать кучу±1\pm1Метка, чтобы вывод окончательно определял результат в соответствии со знаком.

nn.MultiLabelSoftMarginLoss

loss(x,y)=1C*iy[i]*log((1+exp(x[i]))1)+(1y[i])*log(exp(x[i])(1+exp(x[i])))\operatorname{loss}(x, y)=-\frac {1}{C} * \sum_{i} y[i] * \log \left((1+\exp (-x[i]))^{-1}\right)+(1-y[i]) * \log \left(\frac{\exp (-x[i])}{(1+\exp (-x[i]))}\right)

(1+exp(x[i]))1(1+exp(-x[i]))^-1Диапазон значений(0,1)(0,1), метод расчета аналогичен BCE, т. е. положить(1+exp(x[i]))1(1+exp(-x[i]))^-1заполнено до н.э.xnx_nсреди. В документации сказано, что для многоклассовых (взаимоисключающих) задач эта формула рассчитывается на основе максимальной энтропии. Формула BCELoss выглядит следующим образом

ln=wn[ynlogxn+(1yn)log(1xn)]l_{n}=-w_{n}\left[y_{n} \cdot \log x_{n}+\left(1-y_{n}\right) \cdot \log \left(1-x_{n}\right)\right]

nn.MultiMarginLoss

Формула выглядит следующим образом:

loss(x,y)=imax(0,marginx[y]+x[i]))pxsize(0)\operatorname{loss}(x, y)=\frac {\left.\sum_{i} \max (0, \operatorname{margin}-x[y]+x[i])\right)^{p}}{\operatorname{x} \cdot \operatorname{size}(0)}

Это очень похоже на формулу MultiLabelMarginLoss.Если вы внимательно посмотрите, то обнаружите, что это другой интерфейс той же функции, но nn.MultiMarginLoss не поддерживает мульти-метки и мульти-классификацию, поэтому вход y_true должен быть[0,3,5,2][0,3,5,2]Таким образом, категория с несколькими категориями напрямую задается в формате(N,C)(N,C)и(N)(N).

nn.HingeEmbeddingLoss

Формула выглядит следующим образом:

ln={xn, if yn=1max{0,Δxn}, if yn=1l_{n}=\left\{\begin{array}{ll} x_{n}, & \text { if } y_{n}=1 \\ \max \left\{0, \Delta-x_{n}\right\}, & \text { if } y_{n}=-1 \end{array}\right.

Подобно стандартной форме HingeLoss для nn.MultiLabelMarginLoss, метка, которую вы хотите разместить,±1\pm 1Δ\Deltaуказанная маржа, по умолчанию 1,0;xnx_nв действительностиxnyn|x_n-y_n|.

Обычно используется при нелинейном встраивании или полуконтролируемом.

nn.PoissonNLLLoss

версия NLL с распределением Пуассона, входная форма становится(N,*)(N,*)и(N,*)(N,*), ФормулаtargetPoisson(input)цель ~ Пуассона (ввод) loss(input,target)=inputtarget*log(input)+log(target!)потеря (вход, цель) = вход-цель * журнал (вход) + журнал (цель!)

targettargetсчитается удовлетворяющимλ=input\lambda=inputРаспределение Пуассона , я не очень часто использовал этот вид Loss, и в Интернете нет ничего, связанного с ним.

nn.KLDivLoss

Дивергенция KL, также известная как относительная энтропия, используется для сравнения потери информации между двумя распределениями. Формула расчета:

ln=yn(logynlogxn)l_n = y_n \cdot (\mathrm{log} y_n-\mathrm{log} x_n)

Здесь добавлено, что общая формула расчета информационной энтропии:

ynlogyny_n \cdot - \mathrm{log} y_n

Формула для расчета перекрестной энтропии:

ynlogxny_n \cdot - \mathrm{log} x_n

Очевидно, что KL-расхождение напрямую вычисляет ожидаемый разрыв в собственной информации (логарифмический член) между двумя распределениями по y и не может быть непосредственно понят как расстояние (поскольку KLDiv(x,y)!=KLDiv(y,x) ) , что можно понимать как количество информации, потерянной при подгонке y к x

Резюме

  • Самый базовый L1, MSE, SmoothL1 ни о чем не говорит, это оценка схожести последовательностей, которую можно использовать где угодно
  • NLL, CrossEntropy, BCE (WithLogits) — это набор базовых Loss, которые повсеместно используются в задачах классификации. Лично на данный момент разница невелика. BCE может быть более чувствительным.
  • MultiLabelMargin, MultiLabelSoftMargin и MultiMarginLoss — это реализации стандартного HingeLoss в различных сценариях, общая потеря для задач множественной классификации, но в настоящее время я видел слишком мало статей, и неясно, какие сети использовались.