從零開始Pytorch-YOLOv3【筆記】(一)配置檔案解讀

攻城獅?發表於2022-03-10

前言

這是github上的一個專案YOLO_v3_tutorial_from_scratch,它還有相應的blog對其詳細的解讀。機器之心翻譯了他的tutorial:從零開始PyTorch專案:YOLO v3目標檢測實現。教程中的內容就不再贅述,寫這篇部落格的目的在於記錄自己在學習這篇教程時的筆記。

本教程包含五個部分:

  1. YOLO 的工作原理

  2. 建立 YOLO 網路層級

  3. 實現網路的前向傳播

  4. objectness 置信度閾值和非極大值抑制

  5. 設計輸入和輸出管道

1. YOLO 的工作原理

2. 建立 YOLO 網路層級

這一部分要求讀者已經基本瞭解 YOLO 的執行方式和原理,以及關於 PyTorch 的基本知識,例如如何通過 nn.Module、nn.Sequential 和 torch.nn.parameter 等類來構建自定義的神經網路架構。

Pytorch的基本知識可以看B站小土堆的PyTorch深度學習快速入門教程。程式碼:https://github.com/xiaotudui/PyTorch-Tutorial

一個簡單的Pytorch神經網路model如下:
pytorch-tutorial/src/model.py

# -*- coding: utf-8 -*-
# 作者:小土堆
# 公眾號:土堆碎念
import torch
from torch import nn

# 搭建神經網路
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(3, 32, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(64*4*4, 64),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = self.model(x)
        return x


if __name__ == '__main__':
    tudui = Tudui()
    input = torch.ones((64, 3, 32, 32))
    output = tudui(input)
    print(output.shape)

其他類和函式等到後續用到的時候再詳細說。

2.1.配置檔案

我們將使用官方的 cfg 檔案構建網路,它是由 YOLO 的作者釋出的。
yolov3.cfg: https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg

2.1.1配置檔案解讀

yolov3.cfg主要有以下幾塊,[net],[convolutional],[shortcut],[yolo]

net層

配置整個網路

[net]                  ★ net block

# Testing              ★ 註釋方式:# xxx ,,在解析cfg的檔案時會忽略該行。
# batch=1              ★ 這裡的註釋是說在Testing模型的時候設定batch=1,subdivisions=1。因為這樣可以避免一些錯誤。
# subdivisions=1

# Training             ★ training模型的配置

batch=64               ★ 這兒batch與機器學習中的batch有少許差別,僅表示網路積累多少個樣本後進行一次反向傳播(backforward propagation,BP)

subdivisions=16        ★ 這個參數列示將一個batch的圖片分sub次完成網路的前向傳播
                         ★★ 敲黑板:在Darknet中,batch和sub是結合使用的,例如這兒的batch=64,sub=16表示訓練的過程中將一次性載入64張圖片進記憶體,然後分16次完成前向傳播,意思是每次4張,前向傳播的迴圈過程中累加loss求平均,待64張圖片都完成前向傳播後,再一次性後傳更新引數,subdivisions越大,可以減輕顯示卡壓力
                           ★★★ 調參經驗:sub一般設定16,不能太大或太小,且為8的倍數,其實也沒啥硬性規定,看著舒服就好。batch的值可以根據視訊記憶體佔用情況動態調整,一次性加減sub大小即可,通常情況下batch越大越好,還需注意一點,在測試的時候batch和sub都設定為1,避免發生神祕錯誤!

width=608              ★ 網路輸入的寬width

height=608             ★ 網路輸入的高height
                         ★★敲黑板:width和height一定要為32的倍數,否則不能載入網路
                           ★★★ 提示:width也可以設定為不等於height,通常情況下,width和height的值越大,對於小目標的識別效果越好,但受到了視訊記憶體的限制,讀者可以自行嘗試不同組合。
channels=3             ★ 網路輸入的通道數channels。若為灰度圖,則chennels=1,另外還需修改/scr/data.c檔案中的load_data_detection函式;若為RGB則 channels=3 ,無需修改/scr/data.c檔案
 -------
data load_data_detection(int n, char **paths, int m, int w, int h, int boxes, int classes, float jitter, float hue, float saturation, float exposure)
{
    char **random_paths = get_random_paths(paths, n, m);
    int i;
    data d = {0};
    d.shallow = 0;
 
    d.X.rows = n;
    d.X.vals = calloc(d.X.rows, sizeof(float*));
    d.X.cols = h*w;                   //灰階圖
    //d.X.cols = h*w*3;               //RGB圖
 -------

momentum=0.9          ★ 動量DeepLearning中最優化方法中的動量引數,這個值影響著梯度下降到最優值得速度

decay=0.0005          ★ 權重衰減正則項,防止過擬合

angle=0               ★ 資料增強引數,通過旋轉角度來生成更多訓練樣本
 
saturation = 1.5      ★ 資料增強引數,通過調整飽和度來生成更多訓練樣本
 
exposure = 1.5        ★ 資料增強引數,通過調整曝光量來生成更多訓練樣本
 
hue=.1                ★ 資料增強引數,通過調整色調來生成更多訓練樣本
 
learning_rate=0.001   ★ 學習率決定著權值更新的速度,設定得太大會使結果超過最優值,太小會使下降速度過慢。如果僅靠人為干預調整引數,需要不斷修改學習率。剛開始訓練時可以將學習率設定的高一點,而一定輪數之後,將其減小在訓練過程中,一般根據訓練輪數設定動態變化的學習率。
剛開始訓練時:學習率以 0.01 ~ 0.001 為宜。一定輪數過後:逐漸減緩。接近訓練結束:學習速率的衰減應該在100倍以上。學習率的調整參考https://blog.csdn.net/qq_33485434/article/details/80452941
 
                               ★★★ 學習率調整一定不要太死,實際訓練過程中根據loss的變化和其他指標動態調整,手動ctrl+c結束此次訓練後,修改學習率,再載入剛才儲存的模型繼續訓練即可完成手動調參,調整的依據是根據訓練日誌來,如果loss波動太大,說明學習率過大,適當減小,變為1/5,1/10均可,如果loss幾乎不變,可能網路已經收斂或者陷入了區域性極小,此時可以適當增大學習率,注意每次調整學習率後一定要訓練久一點,充分觀察,調參是個細活,慢慢琢磨
 
                               ★★ 一點小說明:實際學習率與GPU的個數有關,例如你的學習率設定為0.001,如果你有4塊GPU,那真實學習
率為0.001*4(一種說法)
learning_rate=0.001
一塊gpu跑的時候,列印出來的學習率0.001
2 塊gpu跑的時候,列印出來的學習率0.002
4 塊gpu跑的時候,列印出來的學習率0.004
所以應該是相乘的關係,而不是相除的關係(另一種說法)
 
burn_in=1000                    ★ 在迭代次數小於burn_in時,其學習率的更新有一種方式,大於burn_in時,才採用policy的更新方式
 
max_batches = 500200            ★ 訓練次數達到max_batches後停止學習,一次為跑完一個batch
 
policy=steps                    ★ 學習率調整的策略:constant, steps, exp, poly, step, sig, RANDOM,constant等方式
                                  參考https://nanfei.ink/2018/01/23/YOLOv2%E8%B0%83%E5%8F%82%E6%80%BB%E7%BB%93/#more
 
steps=400000,450000          
scales=.1,.1                    ★ steps和scale是設定學習率的變化,比如迭代到400000次時,學習率衰減十倍,45000次迭代時,學習率又會在前一個學習率的基礎上衰減十倍

卷積層:convolution

[convolutional]                 ★ 一層卷積層的配置說明
 
batch_normalize=1               ★ 是否進行BN處理,1為是,0為不是(batch_normalization)
 
filters=32                      ★ 卷積核個數,也是輸出通道數
 
size=3                          ★ 卷積核尺寸
 
stride=1                        ★ 卷積步長
 
pad=1                           ★ 卷積時是否進行padding,padding的個數與卷積核尺寸有關,為size/2向下取整,如3/2=1
                                ★ 取值0/1,當pad=1時,padding = (kernel-1) // 2;當pad=0的時候,padding就是依據給出的padding值。
 
activation=leaky                ★ 網路層啟用函式
                                ★★ 卷積核尺寸3*3配合padding且步長為1時,不改變feature map的大小

下采樣

下采樣也是通過卷積實現的

# Downsample
 
[convolutional]                 ★ 下采樣層的配置說明
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky                 ★★ 卷積核尺寸為3*3,配合padding且步長為2時,feature map變為原來的一半大小

上取樣

[upsample]
stride=2

路由層(Route)

它的引數 layers 有一個或兩個值。

當只有一個值時,它輸出這一層通過該值索引的特徵圖。在我們的實驗中設定為了-4,所以層級將輸出路由層之前第四個層的特徵圖。

當層級有兩個值時,它將返回由這兩個值索引的拼接特徵圖。在我們的實驗中為-1 和 61,因此該層級將輸出從前一層級(-1)到第 61 層的特徵圖,並將它們按深度拼接。

[route]
layers = -4

[route]
layers = -1, 61

shotcut層

跳躍連線,合併相同尺寸的feature map(該術語來自於ResNet殘差塊的跳躍連線,構成殘差網路)

[shortcut]                       ★ shotcut層配置說明
 
from=-3                          ★ 與前面的多少層進行融合,-3表示前面第三層
 
activation=linear                ★ 層啟用函式

YOLO層前面一層卷積層配置說明

[convolutional]                  ★ YOLO層前面一層卷積層配置說明
 
size=1
stride=1
pad=1                            
filters=255                      ★每一個[region/yolo]層前的最後一個卷積層中的 filters=(classes+1+coords)*anchors_num,
其中anchors_num 是該層mask的一個值。如果沒有mask則anchors_num=num是這層的anchor數。COCO為80,num表示YOLO中每個cell預測的框的個數,YOLOV3中為3,即為mask的個數。
 
                                 ★★★ 自己使用時,此處的值一定要根據自己的資料集進行更改,例如你識別4個類,則filters=3*(4+5)=27,三個fileters 
都需要修改,切記
 
activation=linear

yolo層:

[yolo]                             ★ YOLO層配置說明。yolov2中
 
mask = 0,1,2                       ★ 使用anchor的索引,0,1,2表示使用下面定義的0-9個anchors中的前三個anchor
 
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326   ★ anchor的<width>,<height>
 
classes=80                         ★ 類別數目
 
num=9                              ★ 每個grid cell總共預測幾個box,和anchors的數量一致。(3*3,3個anchor,3個尺度)當想要使用更多anchors時需要調大num
                                        在每個尺度上,每個單元使用 3 個錨點預測 3 個邊界框,錨點的總數為 9(不同尺度的anchor不同)。
 
jitter=.3                          ★ 資料增強手段,此處jitter為隨機調整寬高比的範圍,該引數不好理解。利用資料抖動產生更多資料,YOLOv2中使用的是crop,filp,以及net層的angle,flip是隨機的,jitter就是crop的引數,tiny-yolo-voc.cfg中jitter=.3,就是在0~0.3中進行crop
 
 
ignore_thresh = .7                 ★ 參與計算的IOU閾值大小。當預測的檢測框與ground true的IOU大於ignore_thresh的時候,參與loss的計算,否則,檢測框的不參與損失計算。
 
                                ★ 理解:目的是控制參與loss計算的檢測框的規模,當ignore_thresh過於大,接近於1的時候,那麼參與檢測框迴歸loss的個數就會比較少,同時也容易造成過擬合;而如果ignore_thresh設定的過於小,那麼參與計算的會數量
規模就會很大。同時也容易在進行檢測框迴歸的時候造成欠擬合。
 
                                 ★ 引數設定:一般選取0.5-0.7之間的一個值,之前的計算基礎都是小尺度(13*13)用的是0.7,(26*26)用的是0.5。這次先將0.5更改為0.7。參考:https://www.e-learn.cn/content/qita/804953
 
truth_thresh = 1                  
 
random=1                         ★ 為1開啟隨機多尺度訓練,為0則關閉。如果為1,每次迭代圖片大小隨機從320到608,步長為32;如果為0,每次訓練大小與輸入大小一致
                                 ★★ 提示:當開啟隨機多尺度訓練時,前面設定的網路輸入尺寸width和height其實就不起作用了,width會在320到608之間隨機取值,且width=height,每10輪隨機改變一次,一般建議可以根據自己需要修改隨機尺度訓練的範圍,這樣可以增大batch,望讀者自行嘗試!

相關文章