多分类合页损失函数(hinge loss),对于一个样本不是考虑样本输出与真实类别之间的误差,而是考虑对应真实类别与其他类别之间的误差
对于包含 N N N个样本的batch数据 D ( x , y ) D(x, y) D(x,y), x x x为神经网络的输出, y y y是真实的类别标签,假设类别数为 C C C, 0 ≤ y n ≤ C − 1 0 \leq y_{n} \leq C-1 0≤yn≤C−1。
第 n n n个样本的损失值 l n l_{n} ln计算如下:
l n = 1 C ∑ i = 0 & i ≠ y n C − 1 max ( 0 , margin − x n [ y n ] + x n [ i ] ) p l_{n}=\frac1{C}{ \sum_{i=0 \&i \neq y_{n}}^{C-1} \max (0, \operatorname{margin}-x_{n}[y_{n}]+x_{n}[i])^{p}} ln=C1i=0&i=yn∑C−1max(0,margin−xn[yn]+xn[i])p
为了处理多个类别之间的样本不平衡问题,对于每一类可传入相应的权值 w w w。
l n = 1 C ∑ i = 0 & i ≠ y n C − 1 max ( 0 , w [ y n ] ( margin − x n [ y n ] + x n [ i ] ) ) p l_{n}=\frac1{C}{ \sum_{i=0 \&i \neq y_{n}}^{C-1} \max (0, w[y_{n}](\operatorname{margin}-x_{n}[y_{n}]+x_{n}[i]))^{p}} ln=C1i=0&i=yn∑C−1max(0,w[yn](margin−xn[yn]+xn[i]))p
若 p = 1 p=1 p=1, 由以上公式可知:
(1) 当 m a r g i n − x n [ y n ] + x n [ i ] < 0 {margin}-x_{n}[y_{n}]+x_{n}[i]<0 margin−xn[yn]+xn[i]<0时,即 m a r g i n + x n [ i ] < x n [ y n ] {margin}+x_{n}[i]
此时,样本预测为正确类别 y n y_{n} yn的概率大于预测为 i i i类别的概率,并且差值大于 m a r g i n margin margin。 这样的样本被认为是易分类样本,直接忽略其带来的误差。
(2)当 m a r g i n − x n [ y n ] + x n [ i ] > 0 {margin}-x_{n}[y_{n}]+x_{n}[i]>0 margin−xn[yn]+xn[i]>0时,该样本被误认为类别 i i i的 l o s s loss loss为 m a r g i n − x n [ y n ] + x n [ i ] {margin}-x_{n}[y_{n}]+x_{n}[i] margin−xn[yn]+xn[i], 分为两种情况:
m a r g i n + x n [ i ] > x n [ y n ] > x n [ i ] {margin}+x_{n}[i]>x_{n}[y_{n}]>x_{n}[i] margin+xn[i]>xn[yn]>xn[i],对应难分类样本,样本被误认为类别 i i i的概率值小于正确类别 y n y_{n} yn,但是两者的差值小于 m a r g i n margin margin。
m a r g i n + x n [ i ] > x n [ i ] > x n [ y n ] {margin}+x_{n}[i]>x_{n}[i]>x_{n}[y_{n}] margin+xn[i]>xn[i]>xn[yn],对应非常难分类样本,容易被分为类别 i i i
class MultiMarginLoss(_WeightedLoss):
__constants__ = ['p', 'margin', 'weight', 'reduction']
def __init__(self, p=1, margin=1., weight=None, size_average=None,
reduce=None, reduction='mean'):
super(MultiMarginLoss, self).__init__(weight, size_average, reduce, reduction)
if p != 1 and p != 2:
raise ValueError("only p == 1 and p == 2 supported")
assert weight is None or weight.dim() == 1
self.p = p
self.margin = margin
def forward(self, input, target):
return F.multi_margin_loss(input, target, p=self.p, margin=self.margin,
weight=self.weight, reduction=self.reduction)
pytorch中通过torch.nn.MultiMarginLoss
类实现,也可以直接调用F.multi_margin_loss
函数,代码中的weight
即是 w w w。size_average
与reduce
已经弃用。reduction有三种取值mean
, sum
, none
,对应不同的返回 ℓ ( x , y ) \ell(x, y) ℓ(x,y). 默认为mean
,对应于一般情况下整体 l o s s loss loss的计算。
L = { l 1 , … , l N } L=\left\{l_{1}, \ldots, l_{N}\right\} L={l1,…,lN}
ℓ ( x , y ) = { L , if reduction = ’none’ 1 N ∑ n = 1 N l n , if reduction = ’mean’ ∑ n = 1 N l n if reduction = ’sum’ \ell(x, y)=\left\{\begin{array}{ll}\operatorname L, & \text { if reduction }=\text { 'none' } \\ \frac1{N}\sum_{n=1}^{N} l_{n}, & \text { if reduction }=\text { 'mean' } \\ \sum_{n=1}^{N} l_{n} & \text { if reduction }=\text { 'sum' }\end{array} \right. ℓ(x,y)=⎩⎨⎧L,N1∑n=1Nln,∑n=1Nln if reduction = ’none’ if reduction = ’mean’ if reduction = ’sum’
p p p 值默认为1,另外可设为2,其他数值不支持。 m a r g i n margin margin也是人为设定的值,默认为1
例子:
x = torch.FloatTensor([[0.1, 0.2, 0.4, 0.8], [0.1, 0.2, 0.4, 0.8]])
print(x.size())
y = torch.LongTensor([3, 3])
print(y.size())
loss = nn.MultiMarginLoss(reduction="none")
loss_val = loss(x, y)
print(loss_val)
loss = nn.MultiMarginLoss(reduction="sum")
loss_val = loss(x, y)
print(loss_val.item())
print(loss_val.item() / x.size(0))
#验证
print(1 / 2 * 1 / 4 * ((1 - 0.8 + 0.1) + (1 - 0.8 + 0.2) + (1 - 0.8 + 0.4) +
(1 - 0.8 + 0.1) + (1 - 0.8 + 0.2) + (1 - 0.8 + 0.4)))
运行结果:
torch.Size([2, 4])
torch.Size([2])
tensor([0.3250, 0.3250])
0.6499999761581421
0.32499998807907104
0.32499999999999996
多标签合页损失(hinge loss),上述的多分类合页损失MultiMarginLoss
应用于一个样本仅仅对应一个真实类别的情况。而MultiLabelMarginLoss
应用于一个样本对应多个真实类别的情况,但是类别总数不超过 C C C
对于包含 N N N个样本的batch数据 D ( x , y ) D(x, y) D(x,y), x x x为神经网络的输出, y y y是真实的类别。
第 n n n个样本的损失值 l n l_{n} ln计算如下:
l n = 1 C ∑ j ∈ y n ∑ i ∉ y n max ( 0 , 1 − x n [ j ] + x n [ i ] ) l_{n}=\frac1{C}{ \sum_{j \in y_{n}}\sum_{i \notin y_{n}} \max (0, 1-x_{n}[j]+x_{n}[i])} ln=C1j∈yn∑i∈/yn∑max(0,1−xn[j]+xn[i])
其中,每个样本对应的类别数量不同,只考虑 y n y_{n} yn中数值 − 1 -1 −1之前的连续类别。 若某个样本对应的 y n = [ 2 , 3 , − 1 , 0 ] y_{n}=[2,3,-1,0] yn=[2,3,−1,0],表示总的类别有四个,而2和3类别属于该样本,0和1类别不属于该样本。 y n = [ 2 , 3 , − 1 , 0 ] y_{n}=[2,3,-1,0] yn=[2,3,−1,0] 与 y n = [ 3 , 2 , − 1 , 0 ] y_{n}=[3,2,-1,0] yn=[3,2,−1,0]两种表示相同。
class MultiLabelMarginLoss(_Loss):
__constants__ = ['reduction']
def __init__(self, size_average=None, reduce=None, reduction='mean'):
super(MultiLabelMarginLoss, self).__init__(size_average, reduce, reduction)
def forward(self, input, target):
return F.multilabel_margin_loss(input, target, reduction=self.reduction)
pytorch中通过torch.nn.MultiLabelMarginLoss
类实现,也可以直接调用F.multilabel_margin_loss
函数,代码中的weight
即是 w w w。size_average
与reduce
已经弃用。reduction有三种取值mean
, sum
, none
,对应不同的返回 ℓ ( x , y ) \ell(x, y) ℓ(x,y). 默认为mean
,对应于一般情况下整体 l o s s loss loss的计算。
L = { l 1 , … , l N } L=\left\{l_{1}, \ldots, l_{N}\right\} L={l1,…,lN}
ℓ ( x , y ) = { L , if reduction = ’none’ 1 N ∑ n = 1 N l n , if reduction = ’mean’ ∑ n = 1 N l n if reduction = ’sum’ \ell(x, y)=\left\{\begin{array}{ll}\operatorname L, & \text { if reduction }=\text { 'none' } \\ \frac1{N}\sum_{n=1}^{N} l_{n}, & \text { if reduction }=\text { 'mean' } \\ \sum_{n=1}^{N} l_{n} & \text { if reduction }=\text { 'sum' }\end{array} \right. ℓ(x,y)=⎩⎨⎧L,N1∑n=1Nln,∑n=1Nln if reduction = ’none’ if reduction = ’mean’ if reduction = ’sum’
例子:
loss = nn.MultiLabelMarginLoss()
x = torch.FloatTensor([[0.1, 0.2, 0.4, 0.8]])
y = torch.LongTensor([[3, 0, -1, 1]])
print(x.size())
print(y.size())
loss_val = loss(x, y)
print(loss_val.item())
# 验证
print(0.25 * ((1 - (0.8 - 0.2)) + (1 - (0.8 - 0.4)) + (1 - (0.1 - 0.2)) + (1 - (0.1 - 0.4))))
y = torch.LongTensor([[3, 0, 1, -1]])
loss_val = loss(x, y)
print(loss_val.item())
# 验证
print(0.25 * ((1 - (0.8 - 0.4)) + (1 - (0.1 - 0.4)) + (1 - (0.2 - 0.4))))
y = torch.LongTensor([[3, 0, 2, 1]])
loss_val = loss(x, y)
print(loss_val.item())
# 如果包含全部的类别,loss为0?
输出结果:
torch.Size([1, 4])
torch.Size([1, 4])
0.8499999642372131
0.8500000000000001
0.7749999761581421
0.7749999999999999
0.0
但是,如果样本包含全部的类别,对应的loss就为0。这样定义loss不是很奇怪嘛。