图像分类定义
图像分类:通过输入图像,输出对该图像内容分类的描述的问题。它是计算机视觉的核心,实际应用广泛。图片分类的任务是对于一个给定的图片,预测其中的类别标签。比如猫狗分类,数字0-9识别等。
对于一张输入(图片,在计算机中以矩阵或张量形式表示)——>经过模型——>得到输出(标量或向量)。
在分类问题中,通常用到标量(字典dict{key:value})或向量(独热编码(1,0,0))来表示输出,但是往往不用标量表示,因为标量用0,1,2,3,4…来给类别计数无形中给类别增加了数量大小以及远近的限制。
图像分类的任务类型
前面提到图像分类的任务是对一个图片的内容进行分类,那么这个任务可以主要概括为以下四种类型的任务:
按数据集的标签情况进行划分,有单标签、多标签、无标签的情况
(1)单标签任务包括二分类,多分类(比如猫狗识别,MNIST数据集识别,CIFAR10/100,这些都是单标签问题)
(2)多标签任务中一个物体可以同时属于多个类别有多个标签
(3)无标签任务就是通常我们所说的聚类问题
分类任务的模型如下图所示:经过卷积神经网络输出一个向量,其中,输出向量中每个元素的值在0—1之间,所有元素的和为1,而每个元素代表对应某类别的概率。(通过sigmoid将其映射到0,1之间,softmax也可以实现,主要用于多分类任务)
单标签的多分类任务及交叉熵、softmax
二分类,多分类统属于单标签分类任务,多问类问题则涉及交叉熵的使用。
1.交叉熵的推导在logistic回归里写过。
每个类别的交叉熵的计算公式:
对每个类别求交叉熵,最后把所有类别的交叉熵再求和
2.softmax
经过CNN得到的输出往往不能保证都介于0~1之间,所以需要一个映射关系将其映射到【0,1】,在多分类问题中这个映射关系由softmax来担任。
那么softmax是如何映射的呢?
step1:每个值取e
step2:向量中所有元素求和
step3:1的值除以2的和作为新的值
经过上述操作,每个元素的值都映射到【0,1】之间,而且和为1。
前面提到多分类问题的标签通常不采用字典而采用独热编码的方式进行标记。那么对于某一个输入样本Xi,经过CNN的输出和softmax的映射得到上图所示的输出,其对应的标签为:
一个样本的损失函数为
所有样本的损失函数为
Pytorch中的binary classification接口实现
1.BCELoss:指定模型输出和标签
import torch
import numpy as np
#假设二分类的输出结果out,假定其形状为2,3,即输出两个向量,每个向量有三个类别值结果
out = torch.randn(2,3)#随机生成两张图片,三个类别,满足0,1之间的正态分布
print(out) #tensor([[-1.9609, -0.2974, 0.1152],
#[ 0.5912, 1.7348, -2.4041]]
label = torch.tensor([[1,0,0],[0,0,1]]).float() #假设真实标签的类别
my_sigmoid = torch.nn.Sigmoid() #sigmoid需要先实例化再声明使用,用于将输出归一化到01之间
print(my_sigmoid(out)) #tensor([[0.6293, 0.1712, 0.6457],
# [0.5994, 0.8830, 0.1837]])
#调用BCELoss接口计算交叉熵,reduction表示对最终结果求平均
my_bceloss = torch.nn.BCELoss(reduction='mean') #tensor(1.0739)
my_bceloss = torch.nn.BCELoss(reduction='none')
print(my_bceloss(my_sigmoid(out),label))
2.不调库手动推导BCELoss
import torch
import numpy as np
out = torch.randn(2,3)
print(out)
label = torch.tensor([[1,0,0],[0,0,1]]).float()
my_sigmoid = torch.nn.Sigmoid()
print(my_sigmoid(out)) # tensor([[0.3094, 0.7146, 0.5527],
# [0.4998, 0.2921, 0.2590]])
# BCEloss,对应torch.BCEloss()的手动代码实现 yilogpi+(1-yi)log(1-pi)
# class1
col_11 = -1/2*(1* np.log(0.3094)+0*np.log(1-0.3094))# 第一个位置的BCEloss计算公式,第一列第一行的Tensor为pi取值,第一列第一行的label为yi取值
col_21 = -1/2*(0*np.log(0.4998)+1*np.log(1-0.4998))# 第一列第二行
class1 = (col_11 + col_21)/2 #标签的第一列对应第一类的真实值,输出的第一列对应第一类的预测值,第一列分别求BCEloss,然后加和求平均得到第一类
# 依次类推,一共是二行三列的两个矩阵,一个标签矩阵,一个预测矩阵,对应位置分别求每个样本的BCEloss,同列求平均得该列对应类别的BCEloss
# 简化写法 :class1 = -1/2*(1*np.log(0.3094)+1*np.log(1-0.4998))
class1 = -1/2*(1*np.log(0.3094)+1*np.log(1-0.4998))
class2 = -1/2*(1*np.log(1-0.7146)+1*np.log(1-0.2921))
class3 = -1/2*(1*np.log(0.5527)+1*np.log(0.2590))
mean = (class1+class2+class3)/3
print(mean)
#调用BCE验证
my_BCEloss = torch .nn.BCELoss(reduction='mean')
print(my_sigmoid(out),label)
3.BCEWithLogitsLoss
# BCEWithLogitsLoss
my_BCEloss = torch .nn.BCEWithLogitsLoss(reduction='mean')
print(out,label)
# 与BCELOSS相比,不需要sigmoid一下out,直接用out和label进行计算
# BCE loss + sigmoid ==BCE With Logits Loss
Pytorch中的Multi-Class classification接口实现
1.softmax
outputs = torch.randn(2,3)
print(outputs)
my_softmax = torch.nn.Softmax(dim=1)
probs = my_softmax(outputs)
labels = torch.FloatTensor([[0,0,1],[0,1,0]])
#-np.log(probs) #-logPi
my_logsoftmax = torch.nn.LogSoftmax(dim=1) #与上行代码结果一致,只是正负号不一致
# logsoftmax == softmax + negative log
2.nllloss
#nllloss -xiyi
my_nllloss = torch.nn.NLLLoss(reduction='mean')
my_nllloss(my_logsoftmax(outputs),labels)#报错,因为NLLLOSS的标签需要用标签类别的序号
my_nllloss(my_logsoftmax(outputs),torch.LongTensor([]))#让标签值变成样本的序号值
3.CrossEntropyLoss
my_celoss = torch.nn.CrossEntropyLoss(reduction='mean')
my_celoss(outputs,torch.LongTensor([]))#outputs不再进行softmax,因为接口本身包含一次softmax运算
#CrossENtropyLoss == LogSoftmax+NllLoss
可以看出,LogSoftmax和CrossENtropyLoss接口只选一个就可以了,两者不要同时使用。