深度学习模型特征可视化(以yolov4为例)

一般来说,深度学习模型中的特征可视化分为两种:GAM/Grad_GAM、特征图可视化。

  • 一、GAM/Grad_GAM可视化
  • 二、特征图可视化
  • 一、GAM/Grad_GAM可视化

    GAM/Grad_GAM即为热力图,可以很直观的看到模型关注的重点区域,如图所示。这种可视化方法有很多帖子可参考,因此不再赘述,放上大佬改好的开源代码链接https://github.com/IDayday/YOLOv4_CAM
    这里为特征图不同感受野对应的CAM热力图,由于我需要检测的物体是二轮车,可以看到模型的注意力集中在二轮车区域附近
    对于YOLOv4的GAM可视化作者源码,同学在使用过程中出现了一些BUG,现在进行一下说明,帮助大家避坑。
    源码使用方法如下:
    1、在yolo_cam.py里面将model_path、classes_path、path改为自己的训练好的模型权重、目标类别、输入图片路径(相对位置)
    2、直接运行或调试yolo_cam.py
    同学在使用过程中出现的报错如下:

    这是因为对张量f进行reshape操作时出现了维度不匹配的情况。让我们从第221行代码进行分析,首先通过赋值过的path读取图片,将图片存入image中,然后调用detect_image()函数,将图片输入进行检测后,将检测结果返回到output_list这个张量中,output_list是一个存了3个tensor的列表,其shape为[[1,507,6],[1,2028,6],[1,8112,6]]。报错的第223行代码通过枚举函数和for语句将output_list中的tensor和对应索引依次取出,进行reshape后加入ret列表中。这里取出第一个tensor处理时就出现了报错,其shape为[1,507,6],总的维度数量为1x507x6=3042。而此时reshape函数中的参数为(1,3,13,13,10),总的维度数量为1x3x13x13x10=5070,总的维度数量不等,所以报错。
    解决方案: 将224行ret.append(f.reshape(1,3,stride[i],stride[i],10))中的10改为6,将156行def show_CAM(image_path, feature_maps, class_id, all_ids=10, show_one_layer=True):函数定义处的 all_ids改为6。最后将230行show_CAM(path, ret, 1)中的1改为想要可视化类别的索引即可,但是要注意这里的索引值从0开始,并且不能超过classes.txt的维度大小。比如classes.txt中只有1个类别,索引设为了1,必然报错(应设置为0)。

    二、特征图可视化

    特征图可视化需要对特征图进行通道分离,将tensor展开后以图片的形式保存。为直观展示效果,从YOLOv4主干网络CSPdarknet53中分别提取shape为(416,416,32)、(208,208,64)、(104,104,128)的特征进行可视化。在CSPDarkNet类中的forward(self, x)函数进行修改,调用模型即可,使用前要先在项目目录下分别创建名为“new_feature_picture0”、“new_feature_picture1”、“new_feature_picture2”的文件夹,代码如下:

    def forward(self, x):
        x = self.conv1(x)
        out0 = x
        x = self.stages[0](x)
        out1 = x
        x = self.stages[1](x)
        out2 = x
        out3 = self.stages[2](x)
        out4 = self.stages[3](out3)
        out5 = self.stages[4](out4)
        #-----------------------------------特征可视化---------------------------------#
        image0s = out0
        image1s = out1
        image2s = out2
        #--------通道分离tensor,并可视化每个维度的特征图-------#
        #先清空文件夹中的内容
        shutil.rmtree('new_feature_picture0/')
        os.mkdir('new_feature_picture0/')
    
        image0s=torch.split(image0s,1,dim=0)   #dim=0是通道的索引,代表image0的第一个通道。'1'表示分离成单个维度,此处分离后image0为装有单通道tensor的元组。  
        k=0
        for image0 in image0s:   
            q=0
            image0=torch.split(image0,1,dim=1)
            for i in image0:
                t='backbone-32-416-416-picture'+str(k)+'-channel-'+str(q)+'.jpg'
                save_image(i,os.path.join('new_feature_picture0',t))
                q=q+1
    
                # feature_map_sum = sum(ele for ele in image0)
                # save_image(feature_map_sum,os.path.join('new_feature_picture0','feature_map_sum'+'_'+str(k)+'.png'))
            k=k+1        
    
        #--------通道分离tensor,并可视化每个维度的特征图-------#
        #先清空文件夹中的内容
        shutil.rmtree('new_feature_picture1/')
        os.mkdir('new_feature_picture1/')   
    
        image1s=torch.split(image1s,1,dim=0)   #dim=0是通道的索引,代表image0的第一个通道。'1'表示分离成单个维度,此处分离后image0为装有单通道tensor的元组。  
        k=0
        for image1 in image1s:   
            q=0
            image1=torch.split(image1,1,dim=1)
            for i in image1:
                t='backbone-64-208-208-picture'+str(k)+'-channel-'+str(q)+'.jpg'
                save_image(i,os.path.join('new_feature_picture1',t))
                q=q+1
    
                # feature_map_sum = sum(ele for ele in image1)
                # save_image(feature_map_sum,os.path.join('new_feature_picture1','feature_map_sum'+'_'+str(k)+'.png'))
            k=k+1
    
        #--------通道分离tensor,并可视化每个维度的特征图-------#
        #先清空文件夹中的内容
        shutil.rmtree('new_feature_picture2/')
        os.mkdir('new_feature_picture2/')     
            
        image2s=torch.split(image2s,1,dim=0)   #dim=0是通道的索引,代表image0的第一个通道。'1'表示分离成单个维度,此处分离后image0为装有单通道tensor的元组。  
        k=0
        for image2 in image2s:   
            q=0
            image2=torch.split(image2,1,dim=1)
            for i in image2:
                t='backbone-128-104-104-picture'+str(k)+'-channel-'+str(q)+'.jpg'
                save_image(i,os.path.join('new_feature_picture2',t))
                q=q+1
    
                # feature_map_sum = sum(ele for ele in image2)
                # save_image(feature_map_sum,os.path.join('new_feature_picture2','feature_map_sum'+'_'+str(k)+'.png'))
            k=k+1
        return out3, out4, out5
    

    可视化完成后,如需要对特征图进行拼接,可参考我的上一篇博客https://blog.csdn.net/weixin_44944382/article/details/123829981?spm=1001.2014.3001.5501,最终效果如下:
    原图
    (416,416,32),32通道,所以一共由32张图片拼接而成。

    (208,208,64)

    (104,104,128)

    来源:tanjiawei1015@163

    物联沃分享整理
    物联沃-IOTWORD物联网 » 深度学习模型特征可视化(以yolov4为例)

    发表评论