MultiMarginLoss

多分类合页损失函数(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 0ynC1

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=ynC1max(0,marginxn[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=ynC1max(0,w[yn](marginxn[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 marginxn[yn]+xn[i]<0时,即 m a r g i n + x n [ i ] < x n [ y n ] {margin}+x_{n}[i]<x_{n}[y_{n}] margin+xn[i]<xn[yn], 该样本被误认为类别 i i i l o s s loss loss为0。

此时,样本预测为正确类别 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 marginxn[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] marginxn[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 wsize_averagereduce已经弃用。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,N1n=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

MultiLabelMarginLoss

多标签合页损失(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=C1jyni/ynmax(0,1xn[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 wsize_averagereduce已经弃用。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,N1n=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不是很奇怪嘛。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐