02月09, 2020

多标签分类

多标签分类问题的损失函数

链接文本

适用场景:一个输入对应多个label,或输入类别间不互斥

def sigmoid(z):
    return [1 / (1 + math.exp(-n)) for n in z]

In [7]:
z = [-1.0, 5.0, -0.5, 5.0, -0.5]
sigmoid(z)
    Out[7]:
[0.2689414213699951,
 0.9933071490757153,
 0.3775406687981454,
 0.9933071490757153,
 0.3775406687981454]

为什么softmax损失函数不适用

softmax后的结果是一个多项式概率

z = [1.0, 2.0, 3.0, 4.0, 1.0]
softmax(z)

由于单个类别的概率依赖其它类别,无法直接用输出概率>0.5判断

Out[4]:
[0.031062774127550943,
 0.0844373744524495,
 0.22952458061688552,
 0.623912496675563,
 0.031062774127550943]

链接文本

预定义损失函数

  1. Pytorch使用torch.nn.BCEloss

  2. Tensorflow使用tf.losses.sigmoid_cross_entropy

  3. Caffe使用SigmoidCrossEntropyLoss

在output和target之间构建binary cross entropy,其中i为每一个类。

import torch
import torch.nn as nn
import numpy as np
import torch.optim as optim
from torch.autograd import Variable

# (1, 0) => target labels 0+2
# (0, 1) => target labels 1
# (1, 1) => target labels 3
train = []
labels = []
for i in range(10000):
    category = (np.random.choice([0, 1]), np.random.choice([0, 1]))
    if category == (1, 0):
        train.append([np.random.uniform(0.1, 1), 0])
        labels.append([0, 1, 1])
    if category == (0, 1):
        train.append([0, np.random.uniform(0.1, 1)])
        labels.append([1, 1, 0])
    if category == (0, 0):
        train.append([np.random.uniform(0.1, 1), np.random.uniform(0.1, 1)])
        labels.append([0, 1, 0])

class _classifier(nn.Module):
    def __init__(self, nlabel):
        super(_classifier, self).__init__()
        self.main = nn.Sequential(
            nn.Linear(2, 64),
            nn.ReLU(),
            nn.Linear(64, nlabel),
        )

    def forward(self, input):
        return self.main(input)

nlabel = len(labels[0]) # => 3
classifier = _classifier(nlabel)

optimizer = optim.Adam(classifier.parameters())
criterion = nn.BCEWithLogitsLoss()

epochs = 5
for epoch in range(epochs):
    losses = []
    for i, sample in enumerate(train):
        inputv = Variable(torch.FloatTensor(sample)).view(1, -1)
        labelsv = Variable(torch.FloatTensor(labels[i])).view(1, -1)

        output = classifier(inputv)
        loss = criterion(output, labelsv)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        losses.append(loss.data.mean())
    print('[%d/%d] Loss: %.3f' % (epoch+1, epochs, np.mean(losses)))

输出: 大于0 表示输出类别为正

tensor([[ 10.6224,  33.0637, -60.3843]])

注意target的形式,要写成01编码形式,eg:如果同时为第一类和第三类则,[1, 0, 1]

主要是结合sigmoid来使用,经过classifier分类过后的输出为(batch_size,num_class)为每个数据的标签, 标签不是one-hot的主要体现在sigmoid输出之后,仍然为(batch_size,num_class),对于一个实例,它的各个label的分数加起来不一定等于1,bceloss在每个类维度上求cross entropy loss然后加和求平均得到,这里就体现了多标签的思想。

自定义损失函数

[CVPR2015] Is object localization for free? – Weakly-supervised learning with convolutional neural networks这篇论文里设计了针对多标签问题的loss,传统的类别分类不适用,作者把这个任务视为多个二分类问题,loss function和分类的分数如下:

链接文本

参考来源

PyTorch 学习笔记(六):PyTorch的十八个损失函数

Guide To Multi-Class Multi-Label Classification With Neural Networks In Python

本文链接:http://57km.cc/post/multi-hot classify.html

-- EOF --

Comments