[PaddleSeg源码阅读] PaddleSeg Validation 中添加 Boundary IoU的计算(2)——inference 部分

查看 parse_args 函数

parser.add_argument(
        "--config", dest="cfg", help="The config file.", default=None, type=str)
parser.add_argument(
        '--model_path',
        dest='model_path',
        help='The path of model for evaluation',
        type=str,
        default=None)

为了调试方便,直接用 args.cfgargs.model_path 设置,注意参数是 dest="cfg",所以没有args.config

if __name__ == '__main__':
    args = parse_args()
    
    root = r"xxxx\PaddleSeg-release-2.5\save"
    args.cfg = os.path.join(root, "config.yml")
    args.model_path = os.path.join(root, "model.pdparams")
    
    main(args)

这是文件结构
、
这是config:

val_dataset:
  type: txtFileDataset
  # dataset_root: /root/share/program/save/data/portraint
  # val_path: /root/share/program/save/data/portraint/txtfiles/test.txt
  data_root: ../..
  img_ann_file: ../../test/test.txt
  num_classes: 2
  transforms:
    - type: Resize
      target_size: [224, 224]
    - type: Normalize
  mode: val

model:
  type: PPLiteSeg
  backbone:
    type: STDC2
  backbone_indices: [0, 1, 2, 3]
  arm_out_chs: [16, 32, 32, 64]
  seg_head_inter_chs: [8, 16, 16, 32]

因为 val.py ,所以只需要 model 和 val_dataset

之后单步调试,进入 paddleseg/core/val.py 文件的 evaluate 函数

主要是几个分支:
一是 aug_eval

Whether to use mulit-scales and flip augment for evaluation
是否使用 多尺度翻转增强 的方式进行eval

如果为 True,则使用 infer.aug_inference 进行推理
如果为 False, 则使用 infer.inference 进行推理

而如果 precision == 'fp16',推理需要在这个上下文管理器中进行:

with paddle.amp.auto_cast(
       level=amp_level,
       enable=True,
       custom_white_list={
           "elementwise_add", "batch_norm",
           "sync_batch_norm"
       },
       custom_black_list={'bilinear_interp_v2'}):

amp 是自动混合精度的意思

看下官方文档:

paddle.amp.auto_cast(enable=True, custom_white_list=None, custom_black_list=None, level='O1')

https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/amp/auto_cast_cn.html#auto-cast

  • custom_white_list (set|list, 可选) - 自定义算子白名单。这个名单中的算子在支持float16计算时会被认为是数值安全的,并且对性能至关重要。如果设置了白名单,该名单中的算子会使用float16计算。

  • custom_black_list (set|list, 可选) - 自定义算子黑名单。这个名单中的算子在支持float16计算时会被认为是数值危险的,它们的影响也可能会在下游操作中观察到。这些算子通常不会转为float16计算。

稍微注意下一个是 黑名单 一个是白名单


咱直接看 infer.aug_inferenceinfer.inference 的结果

都返回了

pred, logits = infer.aug_inference()
pred, logits = infer.inference()

这里说明一下:
pred.shape[bs, 1, w, h]
logits .shape[bs, cls_num, w, h]

pred 变量是每个像素点的类别,而 logits 会显示每个像素点每个类的置信度

infer.aug_inference 中为:

final_logit = reverse_transform(
        final_logit, ori_shape, transforms, mode='bilinear')
pred = paddle.argmax(final_logit, axis=1, keepdim=True, dtype='int32')
return pred, final_logit

infer.inference 中为:

logit = reverse_transform(logit, ori_shape, transforms, mode='bilinear')
pred = paddle.argmax(logit, axis=1, keepdim=True, dtype='int32')
return pred, logit

pred 都是 argmax 变量logit 置信度 维度的最大值,也就是类别

intersect_area, pred_area, label_area = metrics.calculate_area(
                pred,
                label,
                eval_dataset.num_classes,
                ignore_index=eval_dataset.ignore_index)

我们在博客

[PaddleSeg 源码阅读] PaddleSeg计算 mIoUhttps://blog.csdn.net/HaoZiHuang/article/details/125574913

中看过,在计算 Dice/mIoU 之类的指标,是先用 metrics.calculate_area 计算

  • intersect_area :交集区域有多少像素点
  • pred_area :预测到的区域有多少像素点
  • label_area :GT标注的区域有多少像素点

以上这三个变量,都是
[int, int, ..., int] 这样的内容,每个元素都代表对应的一个类

然后将这三个变量送给函数 metrics.mean_iou,计算mIoU

metrics_input = (intersect_area_all, pred_area_all, label_area_all)
class_iou, miou = metrics.mean_iou(*metrics_input)

具体可以查看:
PaddleSeg计算 mIoU

后面有计算其他指标的:
acc、Kappa 和 Dice

最后通过这一段来打印信息:

if print_detail:
    infor = "[EVAL] #Images: {} mIoU: {:.4f} Acc: {:.4f} Kappa: {:.4f} Dice: {:.4f}".format(
        len(eval_dataset), miou, acc, kappa, mdice)
    infor = infor + auc_infor if auc_roc else infor
    logger.info(infor)
    logger.info("[EVAL] Class IoU: \n" + str(np.round(class_iou, 4)))
    logger.info("[EVAL] Class Precision: \n" + str(
        np.round(class_precision, 4)))
    logger.info("[EVAL] Class Recall: \n" + str(np.round(class_recall, 4)))

还有个变量 nranks,用来表示并行的卡数

nranks = paddle.distributed.ParallelEnv().nranks

paddle.distributed.get_rank() 来获取当前是第几张卡,默认是0

所以后续会有测试多卡的的if语句,用来做多卡并行的操作:

if nranks > 1:
	...
else:
	单卡操作

并行操作需要注意的地方就两点:

intersect_area_list = []
pred_area_list = []
label_area_list = []
paddle.distributed.all_gather(intersect_area_list,
                              intersect_area)
paddle.distributed.all_gather(pred_area_list, pred_area)
paddle.distributed.all_gather(label_area_list, label_area)

一是这个 all_gather 的使用,如果有同学学过 MPI 或者 OpenMP 那么可以直接跳过啦

这里我直接搬运官方API文档的用法 all-gather

paddle.distributed.all_gather(tensor_list, 
                              tensor, 
                              group=0)
  • group 参数不用管,基本用不着
  • 该函数的用法,就是将各个GPU上的运算 Tensor 统一扔到 列表 tensor_list 中

如下图所示,4个GPU分别开启4个进程,每张卡上的数据用卡号代表, 经过all_gather算子后,每张卡都会拥有所有卡的数据:

在这里插入图片描述

另一个就是这个操作:

# Some image has been evaluated and should be eliminated in last iter
if (iter + 1) * nranks > len(eval_dataset):
    valid = len(eval_dataset) - iter * nranks
    intersect_area_list = intersect_area_list[:valid]
    pred_area_list = pred_area_list[:valid]
    label_area_list = label_area_list[:valid]

这个操作的意思是,当进行到最后一个 batch,不一定所有的GPU上都有数据,举个例子:

一个 batchsize 有32,我有4张卡,每张卡batchsize为8
但是,最后一个batch时,我只有30条数据了,于是前三个GPU都有8条数据,而最后一个GPU只能吃6条数据

但是,为了对齐,方便并行处理,于是会给最后那块GPU,添加 dataset[0]dataset[1],也就是说,差几条数据,我就从前边的数据拿过来,补齐了

这样,我每张卡依旧是8条数据,但是我 dataset[0]dataset[1] 的数据已经计算过了,所以要去掉,也就是索引前 valid 条数据 [:valid]

(我个人感觉,这个API 感觉会有改动)

感觉要铺垫的细节,已经差不多了,还有一个小点就是 auc_roc —— 计算 roc 部分,这个之后专门说一下如何计算的

下一节咱们讲一下 Boundary IoU 到底怎么加

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值