概述
深度学习炼丹手册
一、图像预处理篇
- 对于像素级别的图像预处理,如:将3通道的RGB彩色标签图映射为标签连续的单通道8bit掩码图,在同样调用opencv并且采用相同算法时,c++的处理速度可能是python(opencv-python)的近百倍。目前不清楚具体原因,但是根据观察,实际python运行时只调用了单个cpu,而c++版本则将任务平均分配给了所有cpu。但是python的文件接口很好用,在处理轻量级任务优先python。
- 语义分割图像的label图需要转换为标签连续的单通道8bit掩码图,并用-1表示不感兴趣的类别。
- 图像预处理方法:参见文章https://mp.weixin.qq.com/s/ffL7pV-CohX6tgye00w1HQ,自己写了一部分在语义分割上的复现:https://github.com/ChenJiahao031008/Preprocessing4Semantic
- 图像预处理在分割、检测、分类上的应用(GridMask Data Augmentation):https://zhuanlan.zhihu.com/p/103992528
二、调参技巧篇
这里还是主要聚焦于如何调整损失函数,以TensorFlow和keras为样本,介绍tensor张量的使用与调整。
首先,我们要了解在TensorFlow中,loss是怎样被构建的。正常来说,loss的构建是在模型搭建这一步被选择,如:
parallel_model.compile(loss=loss_func,optimizer=optimizer_name,metrics=['accuracy'])
这之后再会填充数据:
history = parallel_model.fit_generator(train_ge, epochs=epochs, callbacks=call_backs,
steps_per_epoch=int(num_train / train_batch_size),
verbose=1, shuffle=True, max_q_size=10, workers=1)
因此,在loss刚刚构建的时候,loss本质上是作为一个层(layer)去构造的,传入和传出都是tensor张量而非常见的numpy.array。并且其中的格式并不是我们常用的tf.constant或者tf.Variable,而是一种不太常见的类型(对于我个人而言):tf.placeholder(占位符)。占位符有一些独特的特点,即本质上它是不存在数据的,仅仅是一个框架。假如我想把tensor 转化为 numpy.array 这种我们习惯的格式,在对象是tf.constant等的时候是可以的,但是对象是占位符时候则会报错,因为压根没有数据怎么能够转换呢?再比如,我们打印真实值的维度,可能会出现 [None,None,None] 这样的情况,因为我们尚未获得真实数据的维度。更多内容可以参考:https://blog.csdn.net/zjs975584714/article/details/103233139。但是模仿他的方式出了点问题,所以大家参考下理论部分就好。
所以,最理想也是大家通用的方法是直接在张量上进行操作,张量的操作和numpy有一些相似,但是也相对复杂一点。一些常用的操作如下:(有一部分是我自己琢磨的,不一定正确,欢迎大家讨论)
-
索引和切片:
part_pred = y_pred[:,0]
-
比较大小:这里不能直接采用>、<之类的符号,尤其是和单个常量相比时候会比较麻烦。有一个任务是将小于th的数据置为0,我的操作如下:
pr_2 = tf.greater(pr_1, th) # 返回的是和pr_1维度相同的张量,内容是tf.bool # 小于:tf.less();相等: tf.equal();不相等:tf.not_equal() pr_2 = tf.cast(pr_2, dtype=tf.float32) # 将布尔变量转换为tf.float32 pr = pr_2 * pr_1 # 小于th的置零,大于th的保持原值
-
类似:当raw大于阈值后填充tensor_1,否则tensor_2。(?)
new = tf.where(raw > th, tensor_1, tensor_2)
-
找出每一个维度中最大/最小值(是值而不是位置):
x_h = tf.maximum(x1_true, x1_pred) # 最大值 x_a = tf.minimum(x1_true, x1_pred) # 最小值
-
输出当前张量的维度:
print(tensor_1.get_shape().as_list())
-
生成全1或者全零tensor:
from tensorflow.python.ops import array_ops ones = array_ops.ones_like(raw, dtype=raw.dtype) zeros = array_ops.zeros_like(raw, dtype=raw.dtype)
-
总计各维度的和、平均值:
new = tf.reduce_sum(raw, axis=-1, keepdims=True) # 类似于 K.sum() new = tf.reduce_mean(raw, axis=-1, keepdims=True) # 类似于 K.mean()
-
缩放到规定区间:
from keras import backend as K # 这里的 K.epsilon() 也常用于避免被除数为0 new = tf.clip_by_value(raw, K.epsilon(), 1 - K.epsilon())
三、损失函数篇
1 语义分割部分
- 参考:https://zhuanlan.zhihu.com/p/106005484
- 基于交叉熵的损失函数系列:通过逐像素计算预测分布与groundtruth分布之间的“差距”得到损失函数的值。数学上可证明交叉熵损失函数等价于最大似然估计
- 交叉熵(Cross Entorpy,CE)
- 加权交叉熵(Weighted Cross Entorpy,WCE)/ BCE(在unet上提出):交叉熵损失分别计算每个像素的交叉熵,然后对所有像素进行平均,这意味着我们默认每类像素对损失的贡献相等。 如果各类像素在图像中的数量不平衡,则可能出现问题,因为数量最多的类别会对损失函数影响最大,从而主导训练过程。对交叉熵损失函数进行加权后,可以削弱样本类数量不平衡引起的问题。
- Focal Loss:为了降低易分样本对损失函数的贡献,使模型更加专注于区分难分样本,Lin等人用难易区分权重对加权交叉熵损失函数进行进一步改进,得到Focal Loss,它解决了类数量不平衡,并且动态增加了针对难分样本的权重,随着训练的进行,难分样本的权重也会慢慢降低
- 基于重合度度量的损失函数:基于重叠度量的方法在克服不平衡方面表现出相当好的性能。
- Dice Loss(DL):Dice系数可以衡量两个样本之间重叠程度,与F1-Score等价,与IoU基本相似。Dice Loss在2016年的V-Net中首次提出,非常适用于类别不平衡问题,本身可以有效抑制类别不平衡引起的问题。
- Tversky Loss(TL):Tversky Loss是对Dice Loss的正则化版本,为控制FP和FN对损失函数的贡献,TL对它们进行了加权:
- 指数对数损失(Exponential Logarithmic Loss):使用指数对数Dice Loss和加权指数交叉熵损失的加权和构成的损失函数,以提高小结构的分割精度,这些要分割的小结构对象的大小一般存在较大差异。
- 边界损失(Boundary Loss,BL):Kervadec提出了一种新的损失函数,第一部分是正则化后的Dice Loss,第二部分是边界损失(Boundary Loss)。
2 目标检测Bounding Box Regression部分
-
参考:https://zhuanlan.zhihu.com/p/104236411
-
Smooth L1 Loss → to → IoU Loss → to → GIoU Loss → to → DIoU Loss → to → CIoU Loss
-
Smooth L1 Loss(Fast RCNN):
-
L o s s = { 0.5 x 2 ∣ x ∣ ≤ 1 ∣ x ∣ − 0.5 e l s e Loss = begin{cases} 0.5x^2 & |x|leq 1\ |x|-0.5 & else end{cases} Loss={0.5x2∣x∣−0.5∣x∣≤1else
-
从损失函数对x的导数可知: L1损失函数对x的导数为常数,在训练后期,x很小时,如果learning rate 不变,损失函数会在稳定值附近波动,很难收敛到更高的精度。 L2损失函数对x的导数在x值很大时,其导数也非常大,在训练初期不稳定。Smooth L1 Los完美的避开了前两者的缺点。
-
但是,上面的三种Loss用于计算目标检测的Bounding Box Loss时,独立的求出4个点的Loss,然后进行相加得到最终的Bounding Box Loss,这种做法的假设是4个点是相互独立的,实际是有一定相关性的;实际评价框检测的指标是使用IOU,这两者是不等价的,多个检测框可能有相同大小的Loss,但是IOU不同。并且,L1和L2的距离的loss对于尺度不具有不变性。
-
-
IoU Loss
- 由旷视提出,发表于2016 ACM。提出IoU Loss,其将4个点构成的box看成一个整体进行回归。采用 l n ( I o U ) ln(IoU) ln(IoU)形式,具体详见论文
- 缺点:当预测框和目标框不相交时,IoU(A,B)=0时,不能反映A,B距离的远近,此时损失函数不可导,IoU Loss无法优化两个框不相交的情况。并且,假设预测框和目标框的大小都确定,只要两个框的相交值是确定的,其IoU值是相同时,IoU值不能反映两个框是如何相交的。
-
GIoU Loss
- 由斯坦福学者提出,解决IoU的缺点,发表于CVPR2019,开源:https://github.com/generalized-iou/g-darknet
- GIoU和IoU一样,可以作为一种距离的衡量方式;GIoU具有尺度不变性;两者重叠相同时 GIoU =1,当不相交时,GIoU=-1;
- 缺点:当目标框完全包裹预测框的时候,IoU和GIoU的值都一样,此时GIoU退化为IoU, 无法区分其相对位置关系
-
DIoU/CIoU Loss
- 发表在AAAI 2020,开源:https://github.com/Zzh-tju/DIoU-darknet
- 好的目标框回归损失应该考虑三个重要的几何因素:重叠面积,中心点距离,长宽比。基于问题一,作者提出了DIoU Loss,相对于GIoU Loss收敛速度更快,该Loss考虑了重叠面积和中心点距离,但没有考虑到长宽比;针对问题二,作者提出了CIoU Loss,其收敛的精度更高,以上三个因素都考虑到了。
- 尺度不变性:当两个框完全重合时,Loss=0 , 当2个框不相交时Loss=2;DIoU Loss可以直接优化2个框直接的距离,比GIoU Loss收敛速度更快;对于目标框包裹预测框的这种情况,DIoU Loss可以收敛的很快,而GIoU Loss此时退化为IoU Loss收敛速度较慢
- CIoU的惩罚项是在DIoU的惩罚项基础上加了一个影响因子,这个因子把预测框长宽比拟合目标框的长宽比考虑进去。
四、多任务学习(补充学习)
- 参考:https://www.zhihu.com/question/268105631
最后
以上就是多情画板为你收集整理的深度学习炼丹手册的全部内容,希望文章能够帮你解决深度学习炼丹手册所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复