赞
踩
老样子先说一下为什么我会看到这篇文章。答案是“自动标注”。
这个事情是这样,自动驾驶不光需要做目标检测任务也需要语义分割的信息给到后处理。当然现在做自动驾驶都在往BEV方案上靠了,但是传统的后融合方案还是有在做的。所以我们需要对一些目标检测和语义分割模型进行训练,就我知道的几家公司还是用yolo5或者其他版本,主要还是yolo,transformer当然也在搞。要训练神经网络就需要对训练的数据进行标注但是标注成本实在是太高了,对大公司来说直接劳动力密集型覆盖当然没什么问题,但是中小公司标注还是很慢的没法满足训练快速迭代的需要,所以就有了自动标注。我们用一个专门用来做语义分割的超大模型对现有标注好的数据进行训练,再用它对未标注数据进行预测生产伪标注。这个预测要求结果尽可能真实,最好和人工标注相差无异,理想是美好的,现实其实稍微差一点也可以接受。用人工标注的真实标签和大模型生成的伪标签来训练一个可以在车上跑的小模型,有提升就可以了。
然后恰好,前段时间的分割一切(segment anything)的模型出来以后非常热,那我们当然也要搞一搞。然后不出意外就会遇到很多问题。比如prompt怎么给,分割效果怎么样,怎么才能将模型特性和我们的需求比较好的结合等等。
https://arxiv.org/pdf/2304.02643.pdf 这文章并不是说做了一个什么样的模型,而是一个项目,如上图C,通过标注数据,训练模型,然后又通过模型生成标注数据,不断迭代。最后的成果模型是一方面,另外还有一个数据集。后面模型,数据,都会单独出来讲。这篇主要说一下在搞SAM的过程中遇到的坑。
首先,如果要搞自动标注要面对的第一个问题是,SAM的输出结果并没有语义信息,这是最要紧的,当然我花了一些时间去找paper看别人是怎么做的。稍微放出来一些;
说到这了那就顺便来看一下这个投票模块。下面就是所谓“Semantic voting module",原理很简单,先对mask面积进行排序,然后遍历mask为有效值取出语义部分,如果语义部分只有一类那就赋值类别,如果不是那就按mask对应面积的最大语义来赋值,精髓torch.bincount(propose_classes_ids.flatten()).topk(1).indices
没什么好说的。oneformer,segformer后面有空再说。
def semantic_segment_anything_inference(filename, output_path, rank, img=None, save_img=False, semantic_branch_processor=None, semantic_branch_model=None, mask_branch_model=None, dataset=None, id2label=None, model='segformer'): anns = {'annotations': mask_branch_model.generate(img)} h, w, _ = img.shape class_names = [] if model == 'oneformer': class_ids = oneformer_func[dataset](Image.fromarray(img), semantic_branch_processor, semantic_branch_model, rank) elif model == 'segformer': class_ids = segformer_func(img, semantic_branch_processor, semantic_branch_model, rank) else: raise NotImplementedError() semantc_mask = class_ids.clone() anns['annotations'] = sorted(anns['annotations'], key=lambda x: x['area'], reverse=True) for ann in anns['annotations']: valid_mask = torch.tensor(maskUtils.decode(ann['segmentation'])).bool() # get the class ids of the valid pixels propose_classes_ids = class_ids[valid_mask] num_class_proposals = len(torch.unique(propose_classes_ids)) if num_class_proposals == 1: semantc_mask[valid_mask] = propose_classes_ids[0] ann['class_name'] = id2label['id2label'][str(propose_classes_ids[0].item())] ann['class_proposals'] = id2label['id2label'][str(propose_classes_ids[0].item())] class_names.append(ann['class_name']) # bitmasks.append(maskUtils.decode(ann['segmentation'])) continue top_1_propose_class_ids = torch.bincount(propose_classes_ids.flatten()).topk(1).indices top_1_propose_class_names = [id2label['id2label'][str(class_id.item())] for class_id in top_1_propose_class_ids] semantc_mask[valid_mask] = top_1_propose_class_ids ann['class_name'] = top_1_propose_class_names[0] ann['class_proposals'] = top_1_propose_class_names[0] class_names.append(ann['class_name']) # bitmasks.append(maskUtils.decode(ann['segmentation'])) del valid_mask del propose_classes_ids del num_class_proposals del top_1_propose_class_ids del top_1_propose_class_names sematic_class_in_img = torch.unique(semantc_mask) semantic_bitmasks, semantic_class_names = [], [] # semantic prediction anns['semantic_mask'] = {} for i in range(len(sematic_class_in_img)): class_name = id2label['id2label'][str(sematic_class_in_img[i].item())] class_mask = semantc_mask == sematic_class_in_img[i] class_mask = class_mask.cpu().numpy().astype(np.uint8) semantic_class_names.append(class_name) semantic_bitmasks.append(class_mask) anns['semantic_mask'][str(sematic_class_in_img[i].item())] = maskUtils.encode(np.array((semantc_mask == sematic_class_in_img[i]).cpu().numpy(), order='F', dtype=np.uint8)) anns['semantic_mask'][str(sematic_class_in_img[i].item())]['counts'] = anns['semantic_mask'][str(sematic_class_in_img[i].item())]['counts'].decode('utf-8')
方案也确定了,如果事情可以按着原来的方案走就好啦。现实的情况是,SAM的分割效果并不完美。
所以基于以上种种原因,SAM并不适合做全自动的标注,但是做一些半自动的辅助标注来说还是可以的,但辅助完的边界还是需要精修,得不偿失。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。