背景:今天在复现一篇3D图像分割论文的时候,思考了一个问题,我们的数据集一般由原始数据和对应的Ground Truth组成,一般的3D数据都是(Height,Width,Depth)的形状,而对应的Ground Truth也是(Height,Width,Depth)的形状。这个时候就会产生一个疑问,比如我们的分割类别加上背景总共有4类,这个时候(Height,Width,Depth)形状的Ground Truth里面的每一个元素的是都是0、1、2、3其中的一个,分别代表着背景、类别1、类别2、类别3。这样看似十分合理,但是跑过深度学习模型的uu们都知道,我们的模型最后都会经过一个softmax()函数,将模型输出转换为类别的预测概率(其实分割也是“分类”,不过是在更加细粒度层面、像素级的分类而已),概率一般都是0~1之间,但是我们的Ground Truth此时却是0、1、2、3,这样一来就需要我们对Ground Truth进行预处理,也就是将类别0、1、2、3转换为4种类别所属的概率,一种比较简单的思路是在原始的Ground Truth的形状(Height,Width,Depth)的基础上面再添加一个维度,变为
(4,Height,Width,Depth),这里的4代表着4种类别,可以简单的理解为:后面的Height,Width,Depth是3D数据中的某一个像素块,而4就是这个像素块属于每一个类别的概率。这样以来,我们就解决了上面的问题,此时Ground Truth是类别概率,模型的训练输出也是预测的类别概率,从而可以顺利计算loss。
(上面的这段叙述由于笔者不善描述,比较抽象,可以略过论述直接看下面的np.eye()函数的使用介绍)
用法一:创建数组
#创建数组 np.eye(N, M=None, k=0, dtype=<class 'float'>, order='C')
#创建一个N*N的单位矩阵,对角线值为1,其余为0
ar1 = np.eye(3,dtype=np.int)
print('ar1:')
print(ar1)
ar2 = np.eye(3,2)
print('ar2:')
print(ar2)
ar3 = np.eye(3,4,k=2)
print('ar3:')
print(ar3)
输出:
ar1:
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
ar2:
[[1. 0.]
[0. 1.]
[0. 0.]]
ar3:
[[0. 0. 1. 0.]
[0. 0. 0. 1.]
[0. 0. 0. 0.]]
用法二:生成one-hot形式数组(也就是解决上面论述中的问题)
labels=np.array([[1],[2],[0],[1]]) #一共三类
print(labels)
print(labels.reshape(-1))
res=np.eye(3)[labels.reshape(-1)]
print("labels转成one-hot形式的结果:\n",res,"\n")
print("labels转化成one-hot后的大小:",res.shape)
输出:
[[1]
[2]
[0]
[1]]
[1 2 0 1]
labels转成one-hot形式的结果:
[[0. 1. 0.]
[0. 0. 1.]
[1. 0. 0.]
[0. 1. 0.]]
labels转化成one-hot后的大小: (4, 3)
总结:np.eye()这个函数还是挺有意思的,之前也没有感觉到,但是今天在3D视觉分割任务上面遇到了问题,并且这个问题还被np.eye()这个函数巧妙的解决的时候,感觉这个函数还真的是非常的巧妙。同时也能看出一个学习技巧:很多时候没有目的性的学习是很低效的,尤其对于计算机视觉领域,多动手,多在实践中遇到问题解决问题才能学到更多的东西。