1. 程式人生 > 實用技巧 >[PyTorch 學習筆記] 4.2 損失函式

[PyTorch 學習筆記] 4.2 損失函式

本章程式碼:

這篇文章主要介紹了損失函式的概念,以及 PyTorch 中提供的常用損失函式。

損失函式

損失函式是衡量模型輸出與真實標籤之間的差異。我們還經常聽到代價函式和目標函式,它們之間差異如下:

  • 損失函式(Loss Function)是計算一個樣本的模型輸出與真實標籤的差異

    Loss $=f\left(y^{\wedge}, y\right)$

  • 代價函式(Cost Function)是計算整個樣本集的模型輸出與真實標籤的差異,是所有樣本損失函式的平均值

    $\cos t=\frac{1}{N} \sum_{i}^{N} f\left(y_{i}^{\wedge}, y_{i}\right)$

  • 目標函式(Objective Function)就是代價函式加上正則項

在 PyTorch 中的損失函式也是繼承於nn.Module,所以損失函式也可以看作網路層。

在邏輯迴歸的實驗中,我使用了交叉熵損失函式loss_fn = nn.BCELoss(),$BCELoss$ 的繼承關係:nn.BCELoss() -> _WeightedLoss -> _Loss -> Module

。在計算具體的損失時loss = loss_fn(y_pred.squeeze(), train_y),這裡實際上在 Loss 中進行一次前向傳播,最終呼叫BCELoss()forward()函式F.binary_cross_entropy(input, target, weight=self.weight, reduction=self.reduction)

下面介紹 PyTorch 提供的損失函式。注意在所有的損失函式中,size_averagereduce引數都不再使用。

nn.CrossEntropyLoss

nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')

功能:把nn.LogSoftmax()nn.NLLLoss()結合,計算交叉熵。nn.LogSoftmax()的作用是把輸出值歸一化到了 [0,1] 之間。

主要引數:

  • weight:各類別的 loss 設定權值
  • ignore_index:忽略某個類別的 loss 計算
  • reduction:計算模式,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

下面介紹熵的一些基本概念

  • 自資訊:$\mathrm{I}(x)=-\log [p(x)]$

  • 資訊熵就是求自資訊的期望:$\mathrm{H}(\mathrm{P})=E_{x \sim p}[I(x)]=-\sum_{i}^{N} P\left(x_{i}\right) \log P\left(x_{i}\right)$

  • 相對熵,也被稱為 KL 散度,用於衡量兩個分佈的相似性(距離):$\boldsymbol{D}{K L}(\boldsymbol{P}, \boldsymbol{Q})=\boldsymbol{E}{\boldsymbol{x} \sim p}\left[\log \frac{\boldsymbol{P}(\boldsymbol{x})}{Q(\boldsymbol{x})}\right]$。其中 $P(X)$ 是真實分佈,$Q(X)$ 是擬合的分佈

  • 交叉熵:$\mathrm{H}(\boldsymbol{P}, \boldsymbol{Q})=-\sum_{i=1}^{N} \boldsymbol{P}\left(\boldsymbol{x}{i}\right) \log \boldsymbol{Q}\left(\boldsymbol{x}{i}\right)$

相對熵展開可得:

$\begin{aligned} \boldsymbol{D}{K L}(\boldsymbol{P}, \boldsymbol{Q}) &=\boldsymbol{E}{\boldsymbol{x} \sim p}\left[\log \frac{P(x)}{Q(\boldsymbol{x})}\right] \ &=\boldsymbol{E}{\boldsymbol{x} \sim p}[\log P(\boldsymbol{x})-\log Q(\boldsymbol{x})] \ &=\sum{i=1}^{N} P\left(x_{i}\right)\left[\log P\left(\boldsymbol{x}{i}\right)-\log Q\left(\boldsymbol{x}{i}\right)\right] \ &=\sum_{i=1}^{N} P\left(\boldsymbol{x}{i}\right) \log P\left(\boldsymbol{x}{i}\right)-\sum_{i=1}^{N} P\left(\boldsymbol{x}{i}\right) \log \boldsymbol{Q}\left(\boldsymbol{x}{i}\right) \ &= H(P,Q) -H(P) \end{aligned}$

所以交叉熵 = 資訊熵 + 相對熵,即 $\mathrm{H}(\boldsymbol{P}, \boldsymbol{Q})=\boldsymbol{D}{K \boldsymbol{L}}(\boldsymbol{P}, \boldsymbol{Q})+\mathrm{H}(\boldsymbol{P})$,又由於資訊熵 $H(P)$ 是固定的,因此優化交叉熵 $H(P,Q)$ 等價於優化相對熵 $D{KL}(P,Q)$。

所以對於每一個樣本的 Loss 計算公式為:

$\mathrm{H}(\boldsymbol{P}, \boldsymbol{Q})=-\sum_{i=1}^{N} \boldsymbol{P}\left(\boldsymbol{x}{\boldsymbol{i}}\right) \log Q\left(\boldsymbol{x}{\boldsymbol{i}}\right) = logQ(x_{i})$,因為 $N=1$,$P(x_{i})=1$。

所以 $\operatorname{loss}(x, \text { class })=-\log \left(\frac{\exp (x[\text { class }])}{\sum_{j} \exp (x[j])}\right)=-x[\text { class }]+\log \left(\sum_{j} \exp (x[j])\right)$。

如果了類別的權重,則 $\operatorname{loss}(x, \text { class })=\operatorname{weight}[\text { class }]\left(-x[\text { class }]+\log \left(\sum_{j} \exp (x[j])\right)\right)$。

下面設有 3 個樣本做 2 分類。inputs 的形狀為 $3 \times 2$,表示每個樣本有兩個神經元輸出兩個分類。target 的形狀為 $3 \times 1$,注意類別從 0 開始,型別為torch.long

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np

# fake data
inputs = torch.tensor([[1, 2], [1, 3], [1, 3]], dtype=torch.float)
target = torch.tensor([0, 1, 1], dtype=torch.long)

# def loss function
loss_f_none = nn.CrossEntropyLoss(weight=None, reduction='none')
loss_f_sum = nn.CrossEntropyLoss(weight=None, reduction='sum')
loss_f_mean = nn.CrossEntropyLoss(weight=None, reduction='mean')

# forward
loss_none = loss_f_none(inputs, target)
loss_sum = loss_f_sum(inputs, target)
loss_mean = loss_f_mean(inputs, target)

# view
print("Cross Entropy Loss:\n ", loss_none, loss_sum, loss_mean)

輸出為:

Cross Entropy Loss:
  tensor([1.3133, 0.1269, 0.1269]) tensor(1.5671) tensor(0.5224)

我們根據單個樣本的 loss 計算公式 $\operatorname{loss}(x, \text { class })=-\log \left(\frac{\exp (x[\text { class }])}{\sum_{j} \exp (x[j])}\right)=-x[\text { class }]+\log \left(\sum_{j} \exp (x[j])\right)$,可以使用以下程式碼來手動計算第一個樣本的損失

idx = 0

input_1 = inputs.detach().numpy()[idx]      # [1, 2]
target_1 = target.numpy()[idx]              # [0]

# 第一項
x_class = input_1[target_1]

# 第二項
sigma_exp_x = np.sum(list(map(np.exp, input_1)))
log_sigma_exp_x = np.log(sigma_exp_x)

# 輸出loss
loss_1 = -x_class + log_sigma_exp_x

print("第一個樣本loss為: ", loss_1)

結果為:1.3132617

下面繼續看帶有類別權重的損失計算,首先設定類別的權重向量weights = torch.tensor([1, 2], dtype=torch.float),向量的元素個數等於類別的數量,然後在定義損失函式時把weight引數傳進去。

輸出為:

weights:  tensor([1., 2.])
tensor([1.3133, 0.2539, 0.2539]) tensor(1.8210) tensor(0.3642)

權值總和為:$1+2+2=5$,所以加權平均的 loss 為:$1.8210\div5=0.3642$,通過手動計算的方式程式碼如下:

weights = torch.tensor([1, 2], dtype=torch.float)
weights_all = np.sum(list(map(lambda x: weights.numpy()[x], target.numpy())))  # [0, 1, 1]  # [1 2 2]
mean = 0
loss_f_none = nn.CrossEntropyLoss(reduction='none')
loss_none = loss_f_none(inputs, target)
loss_sep = loss_none.detach().numpy()
for i in range(target.shape[0]):

x_class = target.numpy()[i]
tmp = loss_sep[i] * (weights.numpy()[x_class] / weights_all)
mean += tmp

print(mean)

結果為 0.3641947731375694

nn.NLLLoss

nn.NLLLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')

功能:實現負對數似然函式中的符號功能

主要引數:

  • weight:各類別的 loss 權值設定
  • ignore_index:忽略某個類別
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

每個樣本的 loss 公式為:$l_{n}=-w_{y_{n}} x_{n, y_{n}}$。還是使用上面的例子,第一個樣本的輸出為 [1,2],類別為 0,則第一個樣本的 loss 為 -1;第一個樣本的輸出為 [1,3],類別為 1,則第一個樣本的 loss 為 -3。

程式碼如下:

weights = torch.tensor([1, 1], dtype=torch.float)

loss_f_none_w = nn.NLLLoss(weight=weights, reduction='none')
loss_f_sum = nn.NLLLoss(weight=weights, reduction='sum')
loss_f_mean = nn.NLLLoss(weight=weights, reduction='mean')

# forward
loss_none_w = loss_f_none_w(inputs, target)
loss_sum = loss_f_sum(inputs, target)
loss_mean = loss_f_mean(inputs, target)

# view
print("\nweights: ", weights)
print("NLL Loss", loss_none_w, loss_sum, loss_mean)

輸出如下:

weights:  tensor([1., 1.])
NLL Loss tensor([-1., -3., -3.]) tensor(-7.) tensor(-2.3333)

nn.BCELoss

nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')

功能:計算二分類的交叉熵。需要注意的是:輸出值區間為 [0,1]。

主要引數:

  • weight:各類別的 loss 權值設定
  • ignore_index:忽略某個類別
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式為:$l_{n}=-w_{n}\left[y_{n} \cdot \log x_{n}+\left(1-y_{n}\right) \cdot \log \left(1-x_{n}\right)\right]$

使用這個函式有兩個不同的地方:

  • 預測的標籤需要經過 sigmoid 變換到 [0,1] 之間。
  • 真實的標籤需要轉換為 one hot 向量,型別為torch.float

程式碼如下:

inputs = torch.tensor([[1, 2], [2, 2], [3, 4], [4, 5]], dtype=torch.float)
target = torch.tensor([[1, 0], [1, 0], [0, 1], [0, 1]], dtype=torch.float)

target_bce = target

# itarget
inputs = torch.sigmoid(inputs)

weights = torch.tensor([1, 1], dtype=torch.float)

loss_f_none_w = nn.BCELoss(weight=weights, reduction='none')
loss_f_sum = nn.BCELoss(weight=weights, reduction='sum')
loss_f_mean = nn.BCELoss(weight=weights, reduction='mean')

# forward
loss_none_w = loss_f_none_w(inputs, target_bce)
loss_sum = loss_f_sum(inputs, target_bce)
loss_mean = loss_f_mean(inputs, target_bce)

# view
print("\nweights: ", weights)
print("BCE Loss", loss_none_w, loss_sum, loss_mean)

結果為:

BCE Loss tensor([[0.3133, 2.1269],
        [0.1269, 2.1269],
        [3.0486, 0.0181],
        [4.0181, 0.0067]]) tensor(11.7856) tensor(1.4732)

第一個 loss 為 0,3133,手動計算的程式碼如下:

x_i = inputs.detach().numpy()[idx, idx]
y_i = target.numpy()[idx, idx]              #

# loss
# l_i = -[ y_i * np.log(x_i) + (1-y_i) * np.log(1-y_i) ]      # np.log(0) = nan
l_i = -y_i * np.log(x_i) if y_i else -(1-y_i) * np.log(1-x_i)

nn.BCEWithLogitsLoss

nn.BCEWithLogitsLoss(weight=None, size_average=None, reduce=None, reduction='mean', pos_weight=None)

功能:結合 sigmoid 與二分類交叉熵。需要注意的是,網路最後的輸出不用經過 sigmoid 函式。這個 loss 出現的原因是有時網路模型最後一層輸出不希望是歸一化到 [0,1] 之間,但是在計算 loss 時又需要歸一化到 [0,1] 之間。

主要引數:

  • weight:各類別的 loss 權值設定
  • pos_weight:設定樣本類別對應的神經元的輸出的 loss 權值
  • ignore_index:忽略某個類別
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

程式碼如下:

inputs = torch.tensor([[1, 2], [2, 2], [3, 4], [4, 5]], dtype=torch.float)
target = torch.tensor([[1, 0], [1, 0], [0, 1], [0, 1]], dtype=torch.float)

target_bce = target

# itarget
# inputs = torch.sigmoid(inputs)

weights = torch.tensor([1], dtype=torch.float)
pos_w = torch.tensor([3], dtype=torch.float)        # 3

loss_f_none_w = nn.BCEWithLogitsLoss(weight=weights, reduction='none', pos_weight=pos_w)
loss_f_sum = nn.BCEWithLogitsLoss(weight=weights, reduction='sum', pos_weight=pos_w)
loss_f_mean = nn.BCEWithLogitsLoss(weight=weights, reduction='mean', pos_weight=pos_w)

# forward
loss_none_w = loss_f_none_w(inputs, target_bce)
loss_sum = loss_f_sum(inputs, target_bce)
loss_mean = loss_f_mean(inputs, target_bce)

# view
print("\npos_weights: ", pos_w)
print(loss_none_w, loss_sum, loss_mean)

輸出為

pos_weights:  tensor([3.])
tensor([[0.9398, 2.1269],
        [0.3808, 2.1269],
        [3.0486, 0.0544],
        [4.0181, 0.0201]]) tensor(12.7158) tensor(1.5895)

與 BCELoss 進行對比

BCE Loss tensor([[0.3133, 2.1269],
        [0.1269, 2.1269],
        [3.0486, 0.0181],
        [4.0181, 0.0067]]) tensor(11.7856) tensor(1.4732)

可以看到,樣本類別對應的神經元的輸出的 loss 都增加了 3 倍。

nn.L1Loss

nn.L1Loss(size_average=None, reduce=None, reduction='mean')

功能:計算 inputs 與 target 之差的絕對值

主要引數:

  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

公式:$l_{n}=\left|x_{n}-y_{n}\right|$

nn.MSELoss

功能:計算 inputs 與 target 之差的平方

公式:$l_{n}=\left(x_{n}-y_{n}\right)^{2}$

主要引數:

  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

程式碼如下:

inputs = torch.ones((2, 2))
target = torch.ones((2, 2)) * 3

loss_f = nn.L1Loss(reduction='none')
loss = loss_f(inputs, target)

print("input:{}\ntarget:{}\nL1 loss:{}".format(inputs, target, loss))

# ------------------------------------------------- 6 MSE loss ----------------------------------------------

loss_f_mse = nn.MSELoss(reduction='none')
loss_mse = loss_f_mse(inputs, target)

print("MSE loss:{}".format(loss_mse))

輸出如下:

input:tensor([[1., 1.],
        [1., 1.]])
target:tensor([[3., 3.],
        [3., 3.]])
L1 loss:tensor([[2., 2.],
        [2., 2.]])
MSE loss:tensor([[4., 4.],
        [4., 4.]])

nn.SmoothL1Loss

nn.SmoothL1Loss(size_average=None, reduce=None, reduction='mean')

功能:平滑的 L1Loss

公式:$z_{i}=\left{\begin{array}{ll}0.5\left(x_{i}-y_{i}\right)^{2}, & \text { if }\left|x_{i}-y_{i}\right|<1 \ \left|x_{i}-y_{i}\right|-0.5, & \text { otherwise }\end{array}\right.$

下圖中橙色曲線是 L1Loss,藍色曲線是 Smooth L1Loss


主要引數:
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

nn.PoissonNLLLoss

nn.PoissonNLLLoss(log_input=True, full=False, size_average=None, eps=1e-08, reduce=None, reduction='mean')

功能:泊松分佈的負對數似然損失函式

主要引數:

  • log_input:輸入是否為對數形式,決定計算公式
    • 當 log_input = True,表示輸入資料已經是經過對數運算之後的,loss(input, target) = exp(input) - target * input
    • 當 log_input = False,,表示輸入資料還沒有取對數,loss(input, target) = input - target * log(input+eps)
  • full:計算所有 loss,預設為 loss
  • eps:修正項,避免 log(input) 為 nan

程式碼如下:

inputs = torch.randn((2, 2))
target = torch.randn((2, 2))

loss_f = nn.PoissonNLLLoss(log_input=True, full=False, reduction='none')
loss = loss_f(inputs, target)
print("input:{}\ntarget:{}\nPoisson NLL loss:{}".format(inputs, target, loss))

輸出如下:

input:tensor([[0.6614, 0.2669],
        [0.0617, 0.6213]])
target:tensor([[-0.4519, -0.1661],
        [-1.5228,  0.3817]])
Poisson NLL loss:tensor([[2.2363, 1.3503],
        [1.1575, 1.6242]])

手動計算第一個 loss 的程式碼如下:

idx = 0

loss_1 = torch.exp(inputs[idx, idx]) - target[idx, idx]*inputs[idx, idx]

print("第一個元素loss:", loss_1)

結果為:2.2363

nn.KLDivLoss

nn.KLDivLoss(size_average=None, reduce=None, reduction='mean')

功能:計算 KLD(divergence),KL 散度,相對熵

注意事項:需要提前將輸入計算 log-probabilities,如通過nn.logsoftmax()

主要引數:

  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量),batchmean(batchsize 維度求平均值)

公式:$\begin{aligned} D_{K L}(P | Q)=E_{x-p}\left[\log \frac{P(x)}{Q(x)}\right] &=E_{x-p}[\log P(x)-\log Q(x)] =\sum_{i=1}^{N} P\left(x_{i}\right)\left(\log P\left(x_{i}\right)-\log Q\left(x_{i}\right)\right) \end{aligned}$

對於每個樣本來說,計算公式如下,其中 $y_{n}$ 是真實值 $P(x)$,$x_{n}$ 是經過對數運算之後的預測值 $logQ(x)$。

$l_{n}=y_{n} \cdot\left(\log y_{n}-x_{n}\right)$

程式碼如下:

inputs = torch.tensor([[0.5, 0.3, 0.2], [0.2, 0.3, 0.5]])
inputs_log = torch.log(inputs)
target = torch.tensor([[0.9, 0.05, 0.05], [0.1, 0.7, 0.2]], dtype=torch.float)

loss_f_none = nn.KLDivLoss(reduction='none')
loss_f_mean = nn.KLDivLoss(reduction='mean')
loss_f_bs_mean = nn.KLDivLoss(reduction='batchmean')

loss_none = loss_f_none(inputs, target)
loss_mean = loss_f_mean(inputs, target)
loss_bs_mean = loss_f_bs_mean(inputs, target)

print("loss_none:\n{}\nloss_mean:\n{}\nloss_bs_mean:\n{}".format(loss_none, loss_mean, loss_bs_mean))

輸出如下:

loss_none:
tensor([[-0.5448, -0.1648, -0.1598],
        [-0.2503, -0.4597, -0.4219]])
loss_mean:
-0.3335360586643219
loss_bs_mean:
-1.000608205795288

手動計算第一個 loss 的程式碼為:

idx = 0
loss_1 = target[idx, idx] * (torch.log(target[idx, idx]) - inputs[idx, idx])
print("第一個元素loss:", loss_1)

結果為:-0.5448。

nn.MarginRankingLoss

nn.MarginRankingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')

功能:計算兩個向量之間的相似度,用於排序任務

特別說明:該方法計算 兩組資料之間的差異,返回一個 $n \times n$ 的 loss 矩陣

主要引數:

  • margin:邊界值,$x_{1}$ 與 $x_{2}$ 之間的差異值
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式:$\operatorname{loss}(x, y)=\max (0,-y *(x 1-x 2)+\operatorname{margin})$,$y$ 的取值有 +1 和 -1。

  • 當 $y=1$ 時,希望 $x_{1} > x_{2}$,當 $x_{1} > x_{2}$,不產生 loss
  • 當 $y=-1$ 時,希望 $x_{1} < x_{2}$,當 $x_{1} < x_{2}$,不產生 loss

程式碼如下:

x1 = torch.tensor([[1], [2], [3]], dtype=torch.float)
x2 = torch.tensor([[2], [2], [2]], dtype=torch.float)

target = torch.tensor([1, 1, -1], dtype=torch.float)

loss_f_none = nn.MarginRankingLoss(margin=0, reduction='none')

loss = loss_f_none(x1, x2, target)

print(loss)

輸出為:

tensor([[1., 1., 0.],
        [0., 0., 0.],
        [0., 0., 1.]])

第一行表示 $x_{1}$ 中的第一個元素分別與 $x_{2}$ 中的 3 個元素計算 loss,以此類推。

nn.MultiLabelMarginLoss

nn.MultiLabelMarginLoss(size_average=None, reduce=None, reduction='mean')

功能:多標籤邊界損失函式

舉例:4 分類任務,樣本 x 屬於 0 類和 3 類,那麼標籤為 [0, 3, -1, -1],

主要引數:

  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式:$\operatorname{loss}(x, y)=\sum_{i j} \frac{\max (0,1-(x[y[j]]-x[i]))}{x \cdot \operatorname{size}(0)}$,表示每個真實類別的神經元輸出減去其他神經元的輸出。

程式碼如下:

x = torch.tensor([[0.1, 0.2, 0.4, 0.8]])
y = torch.tensor([[0, 3, -1, -1]], dtype=torch.long)

loss_f = nn.MultiLabelMarginLoss(reduction='none')

loss = loss_f(x, y)

print(loss)

輸出為:

0.8500

手動計算如下:

x = x[0]
item_1 = (1-(x[0] - x[1])) + (1 - (x[0] - x[2]))    # [0]
item_2 = (1-(x[3] - x[1])) + (1 - (x[3] - x[2]))    # [3]

loss_h = (item_1 + item_2) / x.shape[0]

print(loss_h)

nn.SoftMarginLoss

nn.SoftMarginLoss(size_average=None, reduce=None, reduction='mean')

功能:計算二分類的 logistic 損失

主要引數:

  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式:$\operatorname{loss}(x, y)=\sum_{i} \frac{\log (1+\exp (-y[i] * x[i]))}{\text { x.nelement } 0}$

程式碼如下:

inputs = torch.tensor([[0.3, 0.7], [0.5, 0.5]])
target = torch.tensor([[-1, 1], [1, -1]], dtype=torch.float)

loss_f = nn.SoftMarginLoss(reduction='none')

loss = loss_f(inputs, target)

print("SoftMargin: ", loss)

輸出如下:

SoftMargin:  tensor([[0.8544, 0.4032],
        [0.4741, 0.9741]])

手動計算第一個 loss 的程式碼如下:

idx = 0

inputs_i = inputs[idx, idx]
target_i = target[idx, idx]

loss_h = np.log(1 + np.exp(-target_i * inputs_i))

print(loss_h)

結果為:0.8544

nn.MultiLabelSoftMarginLoss

nn.MultiLabelSoftMarginLoss(weight=None, size_average=None, reduce=None, reduction='mean')

功能:SoftMarginLoss 的多標籤版本

主要引數:

  • weight:各類別的 loss 權值設定
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式:$\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)$

程式碼如下

inputs = torch.tensor([[0.3, 0.7, 0.8]])
target = torch.tensor([[0, 1, 1]], dtype=torch.float)

loss_f = nn.MultiLabelSoftMarginLoss(reduction='none')

loss = loss_f(inputs, target)

print("MultiLabel SoftMargin: ", loss)

輸出為:

MultiLabel SoftMargin:  tensor([0.5429])

手動計算的程式碼如下:

x = torch.tensor([[0.1, 0.2, 0.7], [0.2, 0.5, 0.3]])
y = torch.tensor([1, 2], dtype=torch.long)

loss_f = nn.MultiMarginLoss(reduction='none')

loss = loss_f(x, y)

print("Multi Margin Loss: ", loss)

nn.MultiMarginLoss

nn.MultiMarginLoss(p=1, margin=1.0, weight=None, size_average=None, reduce=None, reduction='mean')

功能:計算多分類的摺頁損失

主要引數:

  • p:可以選擇 1 或 2
  • weight:各類別的 loss 權值設定
  • margin:邊界值
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式:$\operatorname{loss}(x, y)=\frac{\left.\sum_{i} \max (0, \operatorname{margin}-x[y]+x[i])\right)^{p}}{\quad \text { x.size }(0)}$,其中 y 表示真實標籤對應的神經元輸出,x 表示其他神經元的輸出。

程式碼如下:

x = torch.tensor([[0.1, 0.2, 0.7], [0.2, 0.5, 0.3]])
y = torch.tensor([1, 2], dtype=torch.long)

loss_f = nn.MultiMarginLoss(reduction='none')

loss = loss_f(x, y)

print("Multi Margin Loss: ", loss)

輸出如下:

Multi Margin Loss:  tensor([0.8000, 0.7000])

手動計算第一個 loss 的程式碼如下:

x = x[0]
margin = 1

i_0 = margin - (x[1] - x[0])
# i_1 = margin - (x[1] - x[1])
i_2 = margin - (x[1] - x[2])

loss_h = (i_0 + i_2) / x.shape[0]

print(loss_h)

輸出為:0.8000

nn.TripletMarginLoss

nn.TripletMarginLoss(margin=1.0, p=2.0, eps=1e-06, swap=False, size_average=None, reduce=None, reduction='mean')

功能:計算三元組損失,人臉驗證中常用

主要引數:

  • p:範數的階,預設為 2
  • margin:邊界值
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式:$L(a, p, n)=\max \left{d\left(a_{i}, p_{i}\right)-d\left(a_{i}, n_{i}\right)+\text { margin, } 0\right}$,$d\left(x_{i}, y_{i}\right)=\left|\mathbf{x}{i}-\mathbf{y}{i}\right|{p}$,其中 $d(a{i}, p_{i})$ 表示正樣本對之間的距離(距離計算公式與 p 有關),$d(a_{i}, n_{i})$ 表示負樣本對之間的距離。表示正樣本對之間的距離比負樣本對之間的距離小 margin,就沒有了 loss。

程式碼如下:

anchor = torch.tensor([[1.]])
pos = torch.tensor([[2.]])
neg = torch.tensor([[0.5]])

loss_f = nn.TripletMarginLoss(margin=1.0, p=1)

loss = loss_f(anchor, pos, neg)

print("Triplet Margin Loss", loss)

輸出如下:

Triplet Margin Loss tensor(1.5000)

手動計算的程式碼如下:

margin = 1
a, p, n = anchor[0], pos[0], neg[0]

d_ap = torch.abs(a-p)
d_an = torch.abs(a-n)

loss = d_ap - d_an + margin

print(loss)

nn.HingeEmbeddingLoss

nn.HingeEmbeddingLoss(margin=1.0, size_average=None, reduce=None, reduction='mean')

功能:計算兩個輸入的相似性,常用於非線性 embedding 和半監督學習

特別注意:輸入 x 應該為兩個輸入之差的絕對值

主要引數:

  • margin:邊界值
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式:$l_{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.$

程式碼如下:

inputs = torch.tensor([[1., 0.8, 0.5]])
target = torch.tensor([[1, 1, -1]])

loss_f = nn.HingeEmbeddingLoss(margin=1, reduction='none')

loss = loss_f(inputs, target)

print("Hinge Embedding Loss", loss)

輸出為:

Hinge Embedding Loss tensor([[1.0000, 0.8000, 0.5000]])

手動計算第三個 loss 的程式碼如下:

margin = 1.
loss = max(0, margin - inputs.numpy()[0, 2])

print(loss)

結果為 0.5

nn.CosineEmbeddingLoss

torch.nn.CosineEmbeddingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')

功能:採用餘弦相似度計算兩個輸入的相似性

主要引數:

  • margin:邊界值,可取值 [-1, 1],推薦為 [0, 0.5]
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

計算公式:$\operatorname{loss}(x, y)=\left{\begin{array}{ll}1-\cos \left(x_{1}, x_{2}\right), & \text { if } y=1 \ \max \left(0, \cos \left(x_{1}, x_{2}\right)-\operatorname{margin}\right), & \text { if } y=-1\end{array}\right.$

其中 $\cos (\theta)=\frac{A \cdot B}{|A||B|}=\frac{\sum_{i=1}^{n} A_{i} \times B_{i}}{\sqrt{\sum_{i=1}{n}\left(A_{i}\right){2}} \times \sqrt{\sum_{i=1}{n}\left(B_{i}\right){2}}}$

程式碼如下:

x1 = torch.tensor([[0.3, 0.5, 0.7], [0.3, 0.5, 0.7]])
x2 = torch.tensor([[0.1, 0.3, 0.5], [0.1, 0.3, 0.5]])

target = torch.tensor([[1, -1]], dtype=torch.float)

loss_f = nn.CosineEmbeddingLoss(margin=0., reduction='none')

loss = loss_f(x1, x2, target)

print("Cosine Embedding Loss", loss)

輸出如下:

Cosine Embedding Loss tensor([[0.0167, 0.9833]])

手動計算第一個樣本的 loss 的程式碼為:

margin = 0.

def cosine(a, b):
numerator = torch.dot(a, b)
denominator = torch.norm(a, 2) * torch.norm(b, 2)
return float(numerator/denominator)

l_1 = 1 - (cosine(x1[0], x2[0]))

l_2 = max(0, cosine(x1[0], x2[0]))

print(l_1, l_2)

結果為:0.016662120819091797 0.9833378791809082

nn.CTCLoss

nn.CTCLoss(blank=0, reduction='mean', zero_infinity=False)

功能:計算 CTC 損失,解決時序類資料的分類,全稱為 Connectionist Temporal Classification

主要引數:

  • blank:blank label
  • zero_infinity:無窮大的值或梯度置 0
  • reduction:計算模式,,可以為 none(逐個元素計算),sum(所有元素求和,返回標量),mean(加權平均,返回標量)

對時序方面研究比較少,不展開講了。

參考資料


如果你覺得這篇文章對你有幫助,不妨點個贊,讓我有更多動力寫出好文章。