Few-Shot Object Detection: A Comprehensive Survey 这是一篇2022年的综述,将目前的few-shot目标检测分为单分支、双分支和迁移学习三个方向。只看了dual-branch的部分。这是它的中文翻译。paper-with-code的榜单上列出了在MS-COCO(30-shot)数据集上各个模型的AP50,最高的目前只有0.3,这个结果相较于目标检测领域的0.8还是有较大差距的,所以很可能是不适合应用于工业环境的。但也有可能是因为COCO数据集上所需要的泛化能力太强了,few-shot才会不拿手,具体还要再看工业上的few-shot应用。
安装真的是踩了大坑,一连几个周没有走得出来,这里记录一下。
首先是mmdetection的安装及demo运行。做这一步的原因是想学习一下mmdetection,毕竟是mmfewshot的基础。然后他在安装的时候遇到的问题主要是:各种版本不匹配的bug。这里找到原因了。mmdetection版本更新以后,很多在demo里用到的函数都取消了,所以运行得很艰难。这里找到了服务器上的一版一年前的mmdetection工程,这里面demo文件所需要的函数都还保留着。说白了就是,光更新工程版本去了,没重写demo。
然后是mmfewshot的安装及demo运行。产生bug的原因有二:一是mmfewshot要求mmcv和mmdetection的版本很低,我的方法比较暴力,直接找到限制的版本区间,改大。二是mmfewshot里用到了mmdetection和mmcls。前者没有问题,后者也出现了mmdetection一样的问题,但是我又找不到像它一样的解决方案,所以我就把mmfew里关于mmcls的引用都取消了,干脆一点不调用mmcls,这也就是服务器里mmfew-mmcls环境的来历。
5.4晚上
跑了一下test:
FileNotFoundError: FewShotVOCDataset: [Errno 2] No such file or directory: 'data/VOCdevkit/VOC2007/ImageSets/Main/test.txt'
数据集没有准备。明天上午循着教程,把VOC数据集下到data目录下,同时,也要保存好对应的trainval.txt和test.txt。
5.5上午
VOC2007和2012的tranval和VOC2007的test已经下载。由于只想运行test.py,所以在目录中只放入了test数据集。
问题1:所有类别的检测精度均为0,甚至base_class的精度也为0。
我觉得是不是和读取的ckpt有关系呢?因为有这样的提示词:
size mismatch for rpn_head.rpn_conv.weight: copying a param with shape torch.Size([1024, 1024, 3, 3]) from checkpoint, the shape in current model is torch.Size([256, 256, 3, 3]).
说明ckpt里的尺寸和现模型不一样,下午仔细读一下文档和test.py,尤其关注模型读入和参数配置。
问题2:关于数据集的路径,VOC2007的trainval和test的路径名称都一样,所以两个不能同时在data目录下,需要把其中一个改名,例如VOC2007test。但是这就涉及到程序中路径读取问题,这个也需要在源码中去试着解决一下。
5.5下午
问题1解决:加载的checkpoint是coco的,测试时的模型是voc的,那能行嘛
问题2解决:voc数据集中trainval和test中的图片编号本就是不重复的,所以只需要手动把两个VOC2007合并就好了。具体的配置在下面这个路径里。
configs/detection/_base_/datasets/fine_tune_based/few_shot_voc.py
5.5晚上
这个问题应该是因为版本问题,但我觉得应该可以手动修复。其实直接删除掉代码中log_level这个选项也可以,不过还会出现其他问题。明天可以先试着删除,看看能不能删到运行出来。或者看看能不能再config中加入这些类呢?
Traceback (most recent call last):
File "/home/dl/didi/mmfewshot-main/tools/detection/train.py", line 236, in <module>
main()
File "/home/dl/didi/mmfewshot-main/tools/detection/train.py", line 159, in main
logger = get_root_logger(log_file=log_file, log_level=cfg.log_level)
File "/home/dl/anaconda3/envs/mmfew2/lib/python3.8/site-packages/mmcv/utils/config.py", line 519, in __getattr__
return getattr(self._cfg_dict, name)
File "/home/dl/anaconda3/envs/mmfew2/lib/python3.8/site-packages/mmcv/utils/config.py", line 50, in __getattr__
raise ex
AttributeError: 'ConfigDict' object has no attribute 'log_level'
5.6上午
训练跑起来了,ConfigDict中缺少两个东西:log_level和data。其实后来自己检查发现,我先前给train.py的config文件是第一个路径。但这其实是个父类,具体的data和log_level配置都是在类似第二个路径下面的config中。
configs/detection/meta_rcnn/meta-rcnn_r101_c4.py
configs/detection/meta_rcnn/voc/split3/meta-rcnn_r101_c4_8xb4_voc-split3_10shot-fine-tuning.py
目前还有两个问题:
1、我在调试的时候,也是搭载得服务器的。本地上出现bug的地方无法跳转。所以我想在本地上搭建一个一模一样的环境用于调试。本地新建了一个mmfew2,照着服务器配置。
2、在这一步训练得太慢了,不知道它是在干嘛。而且在服务器上占用的显存太小,batch_size需要改设置。
2023-05-06 10:34:40,910 - mmfewshot - INFO - Iter [500/1500] lr: 1.000e-03, eta: 0:02:53, time: 0.300, data_time: 0.024, memory: 988, loss_rpn_cls: 0.1112, loss_rpn_bbox: 0.0348, loss_cls: 0.1086, acc: 98.1641, loss_bbox: 0.1726, loss: 0.4271
2023-05-06 10:34:40,912 - mmdet - INFO - starting model initialization...
[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] 192/192, 137.8 task/s, elapsed: 1s, ETA: 0s2023-05-06 10:34:42,363 - mmdet - INFO - model initialization done.
[>>>>>>> ] 1372/4952, 0.7 task/s, elapsed: 1952s, ETA: 5093s
5.11
对于上面提到的问题2,我将apis\train.py中的下列代码修改workers和samples。无效。前500个iter的训练过程没有加速,迷惑过程也没有加速,占用的显存没有增多,大概2.5G。
train_dataloader_default_args = dict(
samples_per_gpu=64,
workers_per_gpu=64,
# `num_gpus` will be ignored if distributed
num_gpus=len(cfg.gpu_ids),
dist=distributed,
seed=cfg.seed,
data_cfg=copy.deepcopy(cfg.data),
use_infinite_sampler=cfg.use_infinite_sampler,
persistent_workers=False)
5.13
基于YOLO的few-shot实现有两种:
1、将query图像经过特征提取后,通过global pooling生成向量,然后与主支路特征逐通道相乘。
2、写一个双分支的检测头。最后predict时,通过下面的公式进行筛选。
上面两个方法都是单阶段网络的双过程训练。
先试第一种吧,总感觉第二种好单薄。可能那篇BC-yolo那篇论文里蒸馏损失函数这项优化对训练结果有比较大的帮助吧。要不总感觉,两个检测头还是在各干各的,第二个检测头训练数据也很少。
第一种方法还是在yolov5的基础上改进吧。至于说多尺度的问题,我可以把第二次训练得到的特征图也提出三个尺度,然后逐通道乘积到neck的输入上。先用裂纹数据集跑通网络吧,相当于是1way-30shot的few-shot目标检测。
现在设计一下思路
训练阶段一:在2798张开源裂纹数据集上进行训练,获得一个权重
训练阶段二:
修改neck的网络模型。这个网络有两个输入:不定量的train数据和2way30shot的query数据。后者经过共享权重提取特征,经过全局平均池化,然后与train的特征逐通道相乘,注意是三个尺度上的特征。但是第二个输入其实只是为了获得三个特征向量,因此两个输入可以分两次进网络,先进行后者,获取模型参量后加载到前者中。
先准备30张开源裂纹和30张船体裂纹,后者需要修改标签0->1,Crack->Hull Crack,组成2way-30shot的数据集
冻结backbone上的权重
测试:
测试怎么搞?测试时能不能记录下三个尺度上的值,这样test时就不用再跑一遍第二个分支。
5.14
任务:将2way-30shot的数据集送入backbone中进行训练,获得三个尺度上的输出权重,并加载到原模型里。
1、先处理数据集:准备30张开源裂纹和30张船体裂纹,后者需要修改标签0->1,Crack->Hull Crack,组成2way-30shot的数据集。直接跑detect获取结果就行,不用跑train。跑detect也有问题,我输入图像的类别增多了,输送进去跑的话应该会跑出报错的。
现在已经找到模型是在这定义的了。那我可以编译到这里,看下backbone是到哪一层,然后加载backbone的权重构成模型,再手动添加全局平均池化层。获得一个输出特征向量,保存下来。然后再调整加载的层数到第二个尺度的输出,再平均池化获得输出特征向量。第三个也是,这样就获得了三个特征向量。但也有个问题:保存成啥格式的,才能和主支路的特征图做逐通道卷积呢?
model.append(ckpt.fuse().eval() if fuse and hasattr(ckpt, 'fuse') else ckpt.eval()) # model in eval mode
不对啊,query数据集是拿来训练的,不应该只是提供一个简单的权重而已,finetune到底该怎么进行呢?先再看meta rcnn的论文,然后结合cursor看源码。
5.15
还是一个双输入问题,support数据和有标签的query数据使用共享权重的backbone,有个问题:query的标签怎么用?
5.21
不耽误了,直接开工!
1、首先是使用VOC数据集上的权重,一共是20个类别。相当于这20个类别是Base。
2、从coco上找3个类别做Novel。给这23个类别各凑100张图片做query,各找3、5、10张图片做support。把support图片中box之外的部分设置为0,且如果存在多个类别,只显示当前类别。或者是只用3个Novel类别做query呢?后者简单,先试试。
3、给support设置一个dataloader。用于在train时进行加载。每次读取一个batch和整个support数据集。
4、这里有两个思路:第一是之前的思路,截取support在三个尺度上的特征,分别生成特征向量,然后逐通道卷积到各个尺度的特征图上。第二是support经过backbone后会生成一个特征向量,将这一个特征向量分别与三个尺度上的特征图进行逐通道卷积。后一个简单一些,可以先试试。不管哪个思路,都需要将pred = model(imgs)进行拆分。这部分还需要再具体看看。
5、val需要改不?
6、predict时先将support图像输入backbone获得特征,然后与不同输入图像做处理即可。
5.22
之前认识得还是不充分,现在重新定一下思路。
1、数据集。我先找15个base类。提前找好他们做Cmeta时的10shot数据集Support,数据集Query。我做5way-10shot,两个数据集都需要屏蔽这5个类别之外的其他类别的标签。然后在support数据集里,为了显示出标签,将画面中标签以外的部分都置为0。然后对于support数据集,我觉得需要提前设置好他们的txt。每次读取直接从这里拿。我可以做一个train的txt,生成方法就是每次挑选五个类,给每个类找batch个图像,把他们的路径随机写在txt里。然后记住label。support数据集也这样排布。
2、数据读取。做两个dataloader。注意query是和for epoch在一起的,而support和next(iter(metadataset))在一起。保证一个batch的图像和整个support的图像一起进入网络。一个episode,就是选择几个base类,然后对于选择的每个类再选择support set和query set,用选择的某几个类的support set和query set训练一次模型。下一个episode,再选择其他几个类,然后在选择support set和query set训练模型。一个epoch中存在多个eposide。
3、修改网络结构。两部分数据经过共享权重的backbone。为了简单,support经过backbone后再经过全局池化生成一列特征向量。与query的特征图逐通道相乘。
4、二阶段的训练也是类似的步骤,不过这次support的数量就需要减少了。暂定200张,跑一个微调。
5、执行predict。
考虑到自身代码能力和实验室算力做了很多简化,具体如下:
特征融合上,实际生成的特征向量应该和种类数相关,但目前还看不懂meta-yolo源码的这部分,所以暂时就生成一列向量。
下午的时间检验一下两个简化是否成立。
看meta-RCNN的模型库和第二次训练的调用模型。×
看meta-YOLO的reweighting net最后输出结果。