yolov5特征圖可視化的使用步驟

前言

最近寫論文需要觀察中間特征層的特征圖,使用的是yolov5的代碼倉庫,但是苦於找不到很好的輪子,於是參考瞭很多,隻找瞭這個,但是我覺得作者寫的太復雜瞭(我之前就是這個作者的小粉絲),在參考瞭github的yolov5作者給出的issue建議後,自己寫瞭個輪子,沒有復雜的步驟,借助torchvision中的transforms將tensor轉化為PIL,再通過matplotlib保存特圖。希望能給大傢帶來一些幫助。

一、效果圖

先上一下效果圖,因為深層的特征有高達1024個,這裡我隻打印瞭8*8的特征圖,用plt.subplot將64張特征圖展示在一張圖片上。原圖為我在百度上隨便搜的貓咪:

這是yolov5x.pt進行detect過程中,經過可視化後的第一個C3模塊的前64張特征圖:

這裡也可以設置為灰度圖,後續代碼中會給出。 

可以看到不同特征圖所提取到的特征幾乎都不相同,有的側重邊緣,有的則是側重整體,當然這隻是第一個C3的特征圖,相對於更深層的特征來說,淺層的特征大多是完整的,而更深層的特征則會更小,而且是提取到的細小特征,當然,這些特征圖也都是相互聯系的,網絡結構是個整體。

借助yolov5作者在issue裡說到的:

BTW, a single feature map may be in my opinion a shallow set of information, as you are looking at a 2d spatial slice but are not aptly observing relationships across the feature space (as the convolutions do).

I guess an analogy is that you would be viewing the R, G, B layers of a color image by themselves, when it helps to view them together to get the complete picture.

單個特征圖可能是一組淺層信息,因為你正在查看 2d 空間切片,但並未恰當地觀察特征空間中的關系(如卷積所做的那樣)。

這裡是我自己的理解,通過特征圖的可視化,也進一步的理解瞭卷積到底幹瞭些什麼事情,如果有想進一步交流的小夥伴,私信一起討論,一起學習呀。

二、使用步驟

1.使用方法

 使用方法很簡單,隻需要在utils中的general.py或者plots.py添加如下函數:

import matplotlib.pyplot as plt
from torchvision import transforms
 
def feature_visualization(features, model_type, model_id, feature_num=64):
    """
    features: The feature map which you need to visualization
    model_type: The type of feature map
    model_id: The id of feature map
    feature_num: The amount of visualization you need
    save_dir = "features/"
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)
    # print(features.shape)
    # block by channel dimension
    blocks = torch.chunk(features, features.shape[1], dim=1)
    # # size of feature
    # size = features.shape[2], features.shape[3]
    plt.figure()
    for i in range(feature_num):
        torch.squeeze(blocks[i])
        feature = transforms.ToPILImage()(blocks[i].squeeze())
        # print(feature)
        ax = plt.subplot(int(math.sqrt(feature_num)), int(math.sqrt(feature_num)), i+1)
        ax.set_xticks([])
        ax.set_yticks([])
        plt.imshow(feature)
        # gray feature
        # plt.imshow(feature, cmap='gray')
    # plt.show()
    plt.savefig(save_dir + '{}_{}_feature_map_{}.png'
                .format(model_type.split('.')[2], model_id, feature_num), dpi=300)

接著在models中的yolo.py中的這個地方:

def forward_once(self, x, profile=False):
        y, dt = [], []  # outputs
        for m in self.model:
            if m.f != -1:  # if not from previous layer
                x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f]  # from earlier layers
 
            if profile:
                o = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 if thop else 0  # FLOPS
                t = time_synchronized()
                for _ in range(10):
                    _ = m(x)
                dt.append((time_synchronized() - t) * 100)
                print('%10.1f%10.0f%10.1fms %-40s' % (o, m.np, dt[-1], m.type))
            x = m(x)  # run
            y.append(x if m.i in self.save else None)  # save output
            # add in here
        if profile:
            print('%.1fms total' % sum(dt))
        return x

添加如下代碼:

            feature_vis = True
            if m.type == 'models.common.C3' and feature_vis:
                print(m.type, m.i)
                feature_visualization(x, m.type, m.i)

添加在yolo.py後,無論是在detect.py還是在train.py中都會進行可視化特征圖。

然而訓練的過程中並不一定需要一直可視化特征圖,feature_vis參數是用來控制是否保存可視化特征圖的,保存的特征圖會存在features文件夾中。如果想看其它層的特征隻需要修改m.type或是用m.i來進行判斷是否可視化特征圖。m.type對應的是yaml文件中的module,即yolov5的基礎模塊,例如c3,conv,spp等等,而m.i則更好理解,即是模塊的id,通常就是順序,如果你嘗試修改過配置文件,那麼你肯定知道是什麼。

如果不明白,多使用print函數,用list.len()和tensor.size去查看列表長度和張量維度,打印出來你就知道瞭。

這裡有一個點我很迷惑,不知道有沒有大佬可以告訴我原因,就是我並沒有找到yolo.py和detect.py之間的關聯,detect.py中使用的是:

model = attempt_load(weights, map_location=device)

而並沒有使用yolo.py中的Model函數,但是運行detect.py同樣可以可視化特征圖,不是很懂pytorch代碼中的這個機制,希望有大佬可以指教一下,代碼還是有些菜。

2.註意事項

註意1:在yolo.py的開頭import feature_visualization:

from utils.general import feature_visualization

註意2:yolov5無論是在detect還是在train的過程中,都會先對模型進行Summary,即驗證你的模型的層數,參數以及是否有梯度,這個過程也會保存特征圖,但是不要擔心,因為你保存的特征圖名字是相同的,會被覆蓋,如果你打印的出來log就會看到整個模型跑瞭兩次:

Model Summary: 476 layers, 87730285 parameters, 0 gradients

註意3:建議訓練完成的網絡使用detect.py來進行驗證特征圖。

當然在yolo.py裡面也可以將'__main__'中的 :

model = Model(opt.cfg).to(device)

替換為:

model = attempt_load(opt.weights, map_location=device)

同樣可以跑通(把detect.py中的opt.weights復制過來)。在yolo.py中打開Profile,將隨機生成的圖片換成自己的圖片,就可以正常的進行驗證。

總結

周末摸魚時間寫瞭這個(也不算摸魚,下周該寫論文初稿瞭orz),希望給大傢帶來幫助,如果有疑問或者錯誤,在評論區或者私信聯系我,之後我會把這個提交一個pr到yolov5的官方倉庫裡(之前提交瞭一個visdrone.yaml的配置文件,幸被采用瞭,參考的就是這個作者的代碼,感謝!),就到這裡,最後上一個spp結構的特征圖輸出,希望和大傢一起討論。

以上。

參考

pytorch特征圖可視化

pytorch 提取卷積神經網絡的特征圖可視化

深度學習筆記~卷積網絡中特征圖的可視化

自用代碼 | YOLOv5 特征圖可視化代碼

將tensor張量轉換成圖片格式並保存

Pytorch中Tensor與各種圖像格式的相互轉化

到此這篇關於yolov5特征圖可視化的文章就介紹到這瞭,更多相關yolov5可視化內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: