深度学习炼丹手册

深度学习炼丹手册

一、图像预处理篇

  1. 对于像素级别的图像预处理,如:将3通道的RGB彩色标签图映射为标签连续的单通道8bit掩码图,在同样调用opencv并且采用相同算法时,c++的处理速度可能是python(opencv-python)的近百倍。目前不清楚具体原因,但是根据观察,实际python运行时只调用了单个cpu,而c++版本则将任务平均分配给了所有cpu。但是python的文件接口很好用,在处理轻量级任务优先python。
  2. 语义分割图像的label图需要转换为标签连续单通道8bit掩码图,并用-1表示不感兴趣的类别。
  3. 图像预处理方法:参见文章https://mp.weixin.qq.com/s/ffL7pV-CohX6tgye00w1HQ,自己写了一部分在语义分割上的复现:https://github.com/ChenJiahao031008/Preprocessing4Semantic
  4. 图像预处理在分割、检测、分类上的应用(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 语义分割部分
  1. 参考:https://zhuanlan.zhihu.com/p/106005484
  2. 基于交叉熵的损失函数系列:通过逐像素计算预测分布与groundtruth分布之间的“差距”得到损失函数的值。数学上可证明交叉熵损失函数等价于最大似然估计
    • 交叉熵(Cross Entorpy,CE)
    • 加权交叉熵(Weighted Cross Entorpy,WCE)/ BCE(在unet上提出):交叉熵损失分别计算每个像素的交叉熵,然后对所有像素进行平均,这意味着我们默认每类像素对损失的贡献相等。 如果各类像素在图像中的数量不平衡,则可能出现问题,因为数量最多的类别会对损失函数影响最大,从而主导训练过程。对交叉熵损失函数进行加权后,可以削弱样本类数量不平衡引起的问题。
    • Focal Loss:为了降低易分样本对损失函数的贡献,使模型更加专注于区分难分样本,Lin等人用难易区分权重对加权交叉熵损失函数进行进一步改进,得到Focal Loss,它解决了类数量不平衡,并且动态增加了针对难分样本的权重,随着训练的进行,难分样本的权重也会慢慢降低
  3. 基于重合度度量的损失函数:基于重叠度量的方法在克服不平衡方面表现出相当好的性能。
    • 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部分
  1. 参考:https://zhuanlan.zhihu.com/p/104236411

  2. Smooth L1 Loss → \to IoU Loss → \to GIoU Loss → \to DIoU Loss → \to CIoU Loss

  3. 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.5x2x0.5x1else

    • 从损失函数对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对于尺度不具有不变性

  4. 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值不能反映两个框是如何相交的
  5. GIoU Loss

    • 由斯坦福学者提出,解决IoU的缺点,发表于CVPR2019,开源:https://github.com/generalized-iou/g-darknet
    • GIoU和IoU一样,可以作为一种距离的衡量方式;GIoU具有尺度不变性两者重叠相同时 GIoU =1,当不相交时,GIoU=-1
    • 缺点:当目标框完全包裹预测框的时候,IoU和GIoU的值都一样,此时GIoU退化为IoU, 无法区分其相对位置关系
  6. 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的惩罚项基础上加了一个影响因子,这个因子把预测框长宽比拟合目标框的长宽比考虑进去。

四、多任务学习(补充学习)

  1. 参考:https://www.zhihu.com/question/268105631
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值