Введение нескольких общих функций потерь Функция потерь и реализация Pytorch

машинное обучение

1. Введение в функцию потерь

Функция потерь, также известная как целевая функция, представляет собой функцию, используемую для вычисления разницы между истинным значением и прогнозируемым значением, а оптимизатор является важным элементом при составлении модели нейронной сети. Потеря должна быть скаляром, потому что векторы нельзя сравнивать по размеру (сами векторы нужно сравнивать по скалярам, ​​таким как норма). Функции потерь обычно делятся на 4 типа: функция потерь HingeLoss 0-1, функция потерь абсолютного значения, функция квадратичных потерь и функция логарифмических потерь.

Суть функции потерь

у любого естьПотеря, состоящая из отрицательного логарифмического правдоподобияОба являются перекрестной энтропией между эмпирическим распределением, определенным в обучающей выборке, и распределением вероятностей, определенным в модели. Например, среднеквадратическая ошибка — это перекрестная энтропия между эмпирическим распределением и моделью Гаусса.

Сначала мы определяем прогнозируемое значениеsampleи целевое значениеtarget, а затем использовать различные функции потерь для расчета значения потерь.

import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
sample = Variable(torch.ones(2,2))
target = Variable(torch.tensor([[0,1],[2,3]]))

Стоимость выборки:tensor([[1., 1.], [1., 1.]]).

Значение цели равно:tensor([[0, 1], [2, 3]]).

2. Потеря абсолютного значения Потеря L1

2.1 nn.L1Loss

L1Loss берет среднее значение абсолютных ошибок прогнозируемых и истинных значений для регрессии.

(x,y)=1NnNxnyn\ell(x, y) = \frac{1}{N} \sum_n^N \left|x_{n}-y_{n}\right|

criterion = nn.L1Loss(reduction='mean')
loss = criterion(sample, target)
print(loss)

оказаться:1.L1Lossтакже поддерживаетreduction=sum.

2.2 nn.SmoothL1Loss

SmoothL1Loss также называется Huber Loss, ошибка представляет собой квадрат потери на (-1,1), в противном случае это потеря L1, применяемая к регрессии.

criterion = nn.SmoothL1Loss()
loss = criterion(sample, target)
print(loss)

Окончательный результат:0.625.

Зачем использовать потерю Хубера?

Результатом квадрата потерь L2 является несмещенная оценка среднего арифметического, а результатом функции потерь L1 является несмещенная оценка медианы.在这里插入图片描述

Однако на квадрат потерь легко влияют выбросы. Потери Хьюбера сильно выпуклы вблизи 0, сочетая в себе преимущества квадратичных потерь и потерь абсолютного значения.

3. Квадрат потери MSE Loss

3.1 nn.MSELoss

Функция квадрата потерь, вычисляющая среднее значение суммы квадратов между прогнозируемым и истинным значениями, используемая для регрессии.

criterion = nn.MSELoss(reduction='mean')
loss = criterion(sample, target)
print(loss)

Окончательный результат:1.5.

4. Потеря журнала — перекрестная энтропия

4.1 nn.CrossEntropyLoss

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

Потеря CE используется для классификации.

Определение класса CrossEntropyLoss

import numpy as np
class CrossEntropyLoss():
    def __init__(self, weight=None, size_average=True):
        """
        初始化参数,因为要实现 torch.nn.CrossEntropyLoss 的两个比较重要的参数

        :param weight: 给予每个类别不同的权重
        :param size_average: 是否要对 loss 求平均
        """
        self.weight = weight
        self.size_average = size_average
    def __call__(self, input, target):
        """
        计算损失
        这个方法让类的实例表现的像函数一样,像函数一样可以调用

        :param input: (batch_size, C),C是类别的总数
        :param target: (batch_size, 1)
        :return: 损失
        """
        batch_loss = 0.
        for i in range(input.shape[0]):
            numerator = np.exp(input[i, target[i]])     # 分子
            denominator = np.sum(np.exp(input[i, :]))   # 分母
            # 计算单个损失
            loss = -np.log(numerator / denominator)
            if self.weight:
                loss = self.weight[target[i]] * loss
            # 损失累加
            batch_loss += loss
        # 整个 batch 的总损失是否要求平均
        if self.size_average == True:
            batch_loss /= input.shape[0]
        return batch_loss

Формула для CE:

в,wlabelw_{label}вес разных классов, по умолчанию 1.

должен быть в курсе,входитьinputТребование логит (вывод модели без softmax),targetКатегория, соответствующая образцу, тензор длинного типа (int64 бит).

import torch 
sample = torch.tensor([[2.0,3],[1,3]])
target = torch.tensor([0,1])

criterion = nn.CrossEntropyLoss(reduction='mean')
loss = criterion(sample, target)
print(loss)

Вывод:tensor(0.7201)

Приведенная выше целевая информация CEloss используется в качестве индекса, который не совпадает с общеизвестной перекрестной энтропией:

H(p,q)=xp(x)logq(x)H(p, q)=-\sum_x p(x) \log q(x)

Следующий вывод доказывает, что CELoss иH(p,q)H(p,q)Оба эквивалентны. Использование numpy для этого:

import numpy as np
def labelEncoder(y):
    nClass = max(y)+1
    tmp = np.zeros(shape = (y.shape[0], nClass))
    for i in range(y.shape[0]):
        tmp[i][y[i]] = 1
    return tmp
    
def softmax(x):
    print('exp',np.exp(x))
    print('sum',np.sum(np.exp(x),axis=1,keepdims=True))
    return np.exp(x)/np.sum(np.exp(x),axis=1,keepdims=True)
    
def crossEntropy(pred_logit, target):
    '''
    需要注意,这里只是计算一个样本的CE,如果样本数不为1,一般是需要除于样本数的.
    '''
    target = labelEncoder(target)
    pred = softmax(pred_logit)
    H = np.mean(np.sum(-target*np.log(pred),axis=1))
    return H
pred_logit = np.array([[2.0,3],[1,3]])
target = np.array([0,1])
H = crossEntropy(pred_logit, target)
print("H",H)

вывод:

H 0.7200948492805976

Правильно! Оглядываясь назад, формула

在这里插入图片描述

Здесь класс является индексом (вызовnn.CrossEntropyLossПримечание), здесьSoftmaxпопрошайничествоpиylog(p)Я написал это вместе, но я не ответил сначала.

4.2 nn.BCELoss

Двоичная перекрестная энтропияy,1y{y, 1-y}В случае двух распределений расчетная потеря больше, чем перекрестная энтропия (поскольку включена перекрестная энтропия положительного и отрицательного классов). Формула расчета:

(x,y)=L={l1,,lN},ln=wn[ynlogxn+(1yn)log(1xn)]\ell(x, y)=L=\left\{l_{1}, \ldots, l_{N}\right\}, \quad l_{n}=-w_{n}\left[y_{n} \cdot \log x_{n}+\left(1-y_{n}\right) \cdot \log \left(1-x_{n}\right)\right]

интерфейс:torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')

Определение класса потерь BCE:

class BCELoss(_WeightedLoss):
    r"""
    Examples::
        >>> m = nn.Sigmoid()
        >>> loss = nn.BCELoss()
        >>> input = torch.randn(3, requires_grad=True)
        >>> target = torch.empty(3).random_(2)
        >>> output = loss(m(input), target)
        >>> output.backward()
    """
    __constants__ = ['reduction', 'weight']

    def __init__(self, weight=None, size_average=None, reduce=None, reduction='mean'):
        super(BCELoss, self).__init__(weight, size_average, reduce, reduction)

    def forward(self, input, target):
        return F.binary_cross_entropy(input, target, weight=self.weight, reduction=self.reduction)

inputВвод представляет собой значение логита после сигмоида;targetявляется вектором, а элементы вектора принимают значение 0 или 1, здесьнетГорячая форма, конкретное понимание того, принадлежит ли образец к определенной этикетке. Например, есть только два типа,targetвозможно[[1],[0]], что означает, что первая выборка является положительным классом, вторая выборка — антиклассом, а соответствующийinputразмерность(2,1).targetвозможно[[1,0],[0,0]], что означает, что первый образец принадлежит первой метке, но не принадлежит второй метке, а второй образец не принадлежит ни первой, ни второй метке.

BCE можно применять для задач классификации с несколькими метками.

Пример: В качестве примера возьмем две этикетки, то есть образец может состоять из двух этикеток.

sample = torch.tensor([[2.0,3.0],[1,3]])
target = torch.tensor([0,1])

one_hot_target = torch.zeros(target.shape[0], max(target)+1)
one_hot_target[torch.arange(target.shape[0]), target] = 1
sigmoid_input = torch.sigmoid(sample)
print('sigmoid_input',sigmoid_input)
print('one_hot_target',one_hot_target)

criterion = nn.BCELoss()
loss = criterion(sigmoid_input, one_hot_target)
print('bce',loss)

результат операции:

sigmoid_input:
 tensor([[0.8808, 0.9526],
        [0.7311, 0.9526]])
one_hot_target:
 tensor([[1., 0.],
        [0., 1.]])
bce tensor(1.1343)

Ручной расчет: Каждая выборка BCE Loss будет усреднена; наконец, потеря должна быть в количестве выборок.

a = -(1*np.log(sigmoid_input[0][0]) + np.log(1-sigmoid_input[0][1]))/2
b = -(np.log(1-sigmoid_input[1][0]) + 1*np.log(sigmoid_input[1][1]))/2
print('cal bce',(a+b)/2)

результат:cal bce tensor(1.1343)

4.3 nn.NLLLoss

Функция потери отрицательного логарифмического правдоподобия (отрицательное логарифмическое правдоподобие), также используемая для классификации. Определение потери NLL:

L=Xi=1Clabel(i)log(predict(i))L= - \sum_{X} \sum_{i=1}^{C} \operatorname{label}(i) \log ( \operatorname{predict} (i))

По сравнению с CrossEntropy Loss, потеря NLL требует вводаinputдаlogitпроходить черезLogSoftmaxОбработанное значение, вход потери CElogit; обаtargetвсе скалярные значения.

LogSoftmaxопределяется как:

logо(xi)=logexp(xi)jexp(xj)=xilog(jexp(xj))\log \sigma\left(x_{i}\right)=\log \frac{\exp \left(x_{i}\right)}{\sum_{j} \exp \left(x_{j}\right)}=x_{i}-\log \left(\sum_{j} \exp \left(x_{j}\right)\right)

Пример:

# NLL loss
torch.manual_seed(0)
input = torch.randn(3, 5)
target = torch.tensor([1, 0, 4])
print("input", input)

m = nn.LogSoftmax(dim=1)
loss = nn.NLLLoss()
output = loss(m(input), target)
print("NLL loss", output)
# CE loss
loss = nn.CrossEntropyLoss()
output = loss(input, target)
print("CE loss", output)

результат:

input tensor([[ 1.5410, -0.2934, -2.1788,  0.5684, -1.0845],
        [-1.3986,  0.4033,  0.8380, -0.7193, -0.4033],
        [-0.5966,  0.1820, -0.8567,  1.1006, -1.0712]])
NLL loss tensor(2.7184)
CE loss tensor(2.7184)

API потерь NLL и CE потерь только встроеныLogSoftmaxразница. Если убыток NLL рассчитывается вручную:

def LogSoftmax(x):
    return torch.log(torch.exp(x)/torch.sum(torch.exp(x),axis=1,keepdims=True))
log_softmax_input = LogSoftmax(input)

print("cal NLL loss", -(log_softmax_input[0][target[0]] + 
log_softmax_input[1][target[1]] + log_softmax_input[2][target[2]])/3)

результат:cal NLL loss tensor(2.7184)

4.4 nn.NLLLoss2d

Подобно приведенному выше, но с еще несколькими размерами, обычно используемыми в изображениях.

input, (N, C, H, W)
target, (N, H, W)

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

criterion = nn.NLLLoss2d()
loss = criterion(sample, target)
print(loss)

4.5 BCEWithLogitsLoss

BCEWithLogitsLossопределение:

ln=wn[tnlogо(xn)+(1tn)log(1о(xn))]l_{n}=-w_{n}\left[t_{n} \cdot \log \sigma\left(x_{n}\right)+\left(1-t_{n}\right) \cdot \log \left(1-\sigma\left(x_{n}\right)\right)\right]

иBCELossпо сравнению с,input xВыходной логит для модели без сигмоидальной обработки. обаtarget tВсе в одном горячем виде. Пример:

# BCELoss
sigmoid = nn.Sigmoid()
torch.manual_seed(0)
input = torch.randn(3,2)
torch.manual_seed(3)
# target one-hot type,such as tensor([0., 1.]).
target = torch.empty(3,2).random_(2)
sigmoid_input = sigmoid(input)
criterion = nn.BCELoss()
print('bce',criterion(sigmoid_input,target))

#BCE_logit Loss
criterion = nn.BCEWithLogitsLoss()
print('BCE_logit',criterion(input,target))

результат операции:

bce tensor(0.9232)
BCE_logit tensor(0.9232)

4.6 MultiLabelSoftMarginLoss

MultiLabelSoftMarginLoss и BCEWithLogitsLoss имеют тот же эффект.

Зачем нужны мягкие потери краев для мультиэтикеток?

Поскольку он поддерживает сэмпл с несколькими метками, такими какtarget = [1,0,1], что означает, что образец принадлежит к классу 0 и классу 2.

# MultiLabelSoftMarginLoss
import torch 
from torch import nn
torch.manual_seed(0)
x = torch.randn(10, 3)
y = torch.FloatTensor(10, 3).random_(2)

bce_criterion = nn.BCEWithLogitsLoss(weight=None, reduce=False)
multi_criterion = nn.MultiLabelSoftMarginLoss(weight=None, reduce=False)

bce_loss = bce_criterion(x, y)
multi_loss = multi_criterion(x, y)

print('weight=None, bce_loss:\n',torch.mean(bce_loss, dim = 1))
print('weight=None, multi_loss:\n', multi_loss)

результат операции:

weight=None, bce_loss:
 tensor([1.1364, 0.8481, 1.2125, 0.5471, 0.5923, 0.7981, 0.9105, 0.4012, 0.5593,
        0.6012])
weight=None, multi_loss:
 tensor([1.1364, 0.8481, 1.2125, 0.5471, 0.5923, 0.7981, 0.9105, 0.4012, 0.5593,
        0.6012])

При добавлении весов классов или добавлении весов на выборку:

#  the loss for class 1
class_weight = torch.FloatTensor([1.0, 2.0, 1.0])
#  the loss for last sample
element_weight = torch.FloatTensor([1.0]*9 + [2.0]).view(-1, 1)
element_weight = element_weight.repeat(1, 3)

bce_criterion_class = nn.BCEWithLogitsLoss(weight=class_weight, reduce=False)
multi_criterion_class = nn.MultiLabelSoftMarginLoss(weight=class_weight, reduce=False)
bce_criterion_element = nn.BCEWithLogitsLoss(weight=element_weight, reduce=False)
multi_criterion_element = nn.MultiLabelSoftMarginLoss(weight=element_weight, reduce=False)

bce_loss_class = bce_criterion_class(x, y)
multi_loss_class = multi_criterion_class(x, y)
bce_loss_element = bce_criterion_element(x, y)
multi_loss_element = multi_criterion_element(x, y)

print("class weight, BCE loss:\n", torch.mean(bce_loss_class,dim=1))
print("class weight, multi loss:\n",multi_loss_class)
print("element weight, BCE loss:\n", torch.mean(bce_loss_element,dim=1))
print("element weight, multi loss:\n",multi_loss_element)

результат операции:

class weight, BCE loss:
 tensor([1.6121, 1.2497, 1.9556, 0.7249, 0.6772, 1.1468, 1.1855, 0.5207, 0.6838,
        0.9306])
class weight, multi loss:
 tensor([1.6121, 1.2497, 1.9556, 0.7249, 0.6772, 1.1468, 1.1855, 0.5207, 0.6838,
        0.9306])
element weight, BCE loss:
 tensor([1.1364, 0.8481, 1.2125, 0.5471, 0.5923, 0.7981, 0.9105, 0.4012, 0.5593,
        1.2023])
element weight, multi loss:
 tensor([1.1364, 0.8481, 1.2125, 0.5471, 0.5923, 0.7981, 0.9105, 0.4012, 0.5593,
        1.2023])

Расширение: функция потерь, соответствующая TensorFlow и BCE.

питорчBCEWithLogitsLossи ТензорФлоуsigmoid_cross_entropy_with_logitsтот же эффект.

# BCE
from torch import nn
bce_criterion = nn.BCEWithLogitsLoss(weight = None, reduce = False)
logits = torch.tensor([[12,3,2],[3,10,1],[1,2,5],
[4,6.5,1.2],[3,6,1]],dtype=torch.float64)
target = torch.tensor([[1,0,1],[0,1,0],[0,0,1],[1,1,0],
[0,1,0]],dtype=torch.float64)
print("BCE",bce_criterion(logits, target))
# sigmoid_cross_entropy_with_logits
import tensorflow as tf
sess =tf.Session()
sigmoid_CE = sess.run(tf.nn.sigmoid_cross_entropy_with_logits(logits=logits,labels=target))
print('sigmoid_CE',sigmoid_CE)

результат:

BCE tensor([[6.1442e-06, 3.0486e+00, 1.2693e-01],
        [3.0486e+00, 4.5399e-05, 1.3133e+00],
        [1.3133e+00, 2.1269e+00, 6.7153e-03],
        [1.8150e-02, 1.5023e-03, 1.4633e+00],
        [3.0486e+00, 2.4757e-03, 1.3133e+00]], dtype=torch.float64)
sigmoid_CE [[6.14419348e-06 3.04858735e+00 1.26928011e-01]
 [3.04858735e+00 4.53988992e-05 1.31326169e+00]
 [1.31326169e+00 2.12692801e+00 6.71534849e-03]
 [1.81499279e-02 1.50231016e-03 1.46328247e+00]
 [3.04858735e+00 2.47568514e-03 1.31326169e+00]]

Судя по результатам, они эквивалентны.

Расширение: функция потерь, соответствующая Keras и BCE

Keras binary_crossentropyназываетсяTf sigmoid_cross_entropy_with_logits. исходный код keras binary_crossentropy;

# binary_crossentropy
import keras.backend as K
def loss_fn( y_true, y_pred):
    bce_loss = (K.binary_crossentropy(y_true, y_pred,from_logits=True))
    return bce_loss
logits = K.variable([[12,3,2],[3,10,1],[1,2,5],[4,6.5,1.2],[3,6,1]])
target = K.variable([[1,0,1],[0,1,0],[0,0,1],[1,1,0],[0,1,0]])
loss = loss_fn(target,logits)
print("keras bce loss",K.get_value(loss))

Результаты также одинаковы:

keras bce loss [[6.1441933e-06 3.0485873e+00 1.2692802e-01]
 [3.0485873e+00 4.5398901e-05 1.3132617e+00]
 [1.3132617e+00 2.1269281e+00 6.7153485e-03]
 [1.8149929e-02 1.5023102e-03 1.4632825e+00]
 [3.0485873e+00 2.4756850e-03 1.3132617e+00]]

Примечательно, что интерфейс Keras:binary_crossentropy(target, output, from_logits=False), если вход логит, установитьfrom_logits=True.

Расширение: softmax_cross_entropy_with_logits

тензорный поток иsoftmax_cross_entropy_with_logitsниже сравнивается разница между ней и BCE Loss (реализованной здесь с помощью sigmoid_cross_entropy_with_logits):

# sigmoid_cross_entropy_with_logits
import tensorflow as tf
logits =tf.constant([[12,3,2],[3,10,1],[1,2,5],[4,6.5,1.2],[3,6,1]],dtype=tf.float64)
target = tf.constant([[1,0,1],[0,1,0],[0,0,1],[1,1,0],[0,1,0]],dtype=tf.float64)
sess =tf.Session()
sigmoid_CE = sess.run(tf.nn.sigmoid_cross_entropy_with_logits(logits=logits,
labels=target))
print('sigmoid_CE', np.mean(sigmoid_CE,axis=1))
# softmax CE with logits
softmax_CE = sess.run(tf.nn.softmax_cross_entropy_with_logits(
logits=logits,labels=target))
print("softmax_CE",softmax_CE)

результат операции:

sigmoid_CE [1.05850717 1.45396481 1.14896835 0.49431157 1.45477491]
softmax_CE [1.00003376e+01 1.03475622e-03 6.58839038e-02 2.66698414e+00
 5.49852354e-02]

Два результата отличаются, обратите внимание, что параlogitвыполнить сигмовидную операцию; выполняетсяsoftmaxработать. Как работает Софтмакс:

def log_softmax(x):
    return -np.log(np.exp(x)/np.sum(np.exp(x),axis=1,keepdims=True))
logits = np.array([[12,3,2],[3,10,1],[1,2,5],[4,6.5,1.2],[3,6,1]])
target = np.array([[1,0,1],[0,1,0],[0,0,1],[1,1,0],[0,1,0]])
softmax_logit = log_softmax(logits)
loss = np.sum(softmax_logit*target,axis=1)
print("cal softmax loss",loss)

результат операции:

cal softmax loss [1.00003376e+01 1.03475622e-03 6.58839038e-02 2.66698414e+00
 5.49852354e-02]

(Добро пожаловать в публичный аккаунт: Путь машинного обучения и алгоритмов)

Ссылаться на:

  1. Pytorch Форум;
  2. Сообщество Тьюринга;
  3. заметки sshuair Функция потери в PyTorch;
  4. Difference of implementation between tensorflow softmax_cross_entropy_with_logits and sigmoid_cross_entropy_with_logits;
  5. Использование tf.nn.softmax_cross_entropy_with_logits;
  6. функция потери pytorch, включая BCELoss;
  7. рекомендовать! блог Роль кросс-энтропии в нейронных сетях;
  8. stack exchange Cross Entropy in network;
  9. Cs231 softmax потери и перекрестная энтропия;
  10. Pytorch nn.CrossEntropyLoss ;
  11. Разница между NLLLoss и CrossEntropyLoss cnblog;
  12. обратное распространение функции потерь;
  13. книга глубокое обучение глубокое обучение;
  14. Функция потерь в машинном обучении (2) Функция потерь для задач регрессии;