天天看點

YOLOv3:訓練自己的資料(附優化與問題總結)

環境說明

  1. 系統:ubuntu16.04
  2. 顯示卡:Tesla k80 12G顯存
  3. python環境: 2.7 && 3.6
  4. 前提條件:cuda9.0 cudnn7.0 opencv3.4.0
    • 安裝cuda和cudnn教程
    • 安裝opencv3.4.0教程

實作YOLOV3的demo

  1. 首先安裝darknet架構,官網連結
    git clone https://github.com/pjreddie/darknet.git
    cd darknet
    vim  Makefile
               
    • 根據情況修改Makefile,如果使用GPU、cudnn和opencv,就将其标志位改成1。
      YOLOv3:訓練自己的資料(附優化與問題總結)
    • 編譯
      make
                 
    • 編譯完成,測試一下
      ./darknet
                 
      應該會出現以下資訊
      usage: ./darknet <function>
                 
      如果有使用opencv,則可以測試下檢測用例,會出現很多老鷹的圖檔說明darknet的安裝odk了
      ./darknet imtest data/eagle.jpg
                 
  2. 使用yolov3預訓練模型檢測物體
    • 首先擷取模型權重
      wget https://pjreddie.com/media/files/yolov3.weights
                 
    • 然後運作目标檢測
      ./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
                 
      YOLOv3:訓練自己的資料(附優化與問題總結)

      這邊運作的時候很可能出現的一個問題就是,沒有出現bbox

      原因

      如果GPU = 1或CUDNN = 1,darknet将為每個層預先配置設定GPU虛拟記憶體,這取決于cfg檔案中的批量大小和子分區設定。

      對于訓練,批量大小表示将在疊代中對GPU執行多少圖檔,更大的值可以減少訓練時間,并且如果GPU在疊代中沒有足夠的記憶體,則子劃分可以将它們分組以防止存儲器大小限制問題。

      保持1檢測,因為這些設定主要用于訓練網絡。

      參考:

      https://github.com/pjreddie/darknet/issues/405

      解決方法:

      (1)檢測的時候令cudnn=0,

      (2)修改cfg檔案成batch=1,sub=1

指令簡介

先舉個例子

./darknet detector test cfg/coco.data cfg/yolov3.cfg yolov3.weights data/dog.jpg
           

darknet :一個可執行的程式,類似win下的exe

detector:是第一個參數,執行detector.c

test:detector.c裡面的一個函數test_detector(),用來測試圖檔

cfg/coco.data:"cfg/"是訓練的配置檔案所在路徑,coco.data是.data配置檔案名

cfg/yolov3.cfg:"cfg/"是訓練的配置檔案所在路徑,yolov3.cfg是.cfg配置檔案名

yolov3.weights:訓練好的模型,yolov3.weights在darknet根目錄

data/dog.jpg:“data/”是要測試圖檔所在路徑,dog.jpg是測試圖檔檔案名

  1. 測試
    • 測試圖檔

      指令格式

      ./darknet detector test <data_cfg> <test_cfg> <weights> <image_file>(可選)
                 
      如果沒有指定 <image_file> 就可以像這樣測試多張圖檔
      YOLOv3:訓練自己的資料(附優化與問題總結)

      <test_cfg>檔案中batch和subdivisions兩項須為1或者測試時不使用cudnn

      測試時還可以用-thresh調整置信度門檻值 ,例如隻顯示置信度在60%以上的 bbox

      ./darknet detector test cfg/coco.data cfg/yolov3.cfg yolov3.weights data/dog.jpg  -thresh .6
                 
    • 檢測視訊,需要用到demo函數(src/demo.c)
      ./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights test.avi
                 
    • 在攝像頭上測試,需要用到demo函數
      ./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights
                 
  2. 訓練模型
    ./darknet -i <gpu_id> detector train <data_cfg> <train_cfg> <weights>
               
    • 單GPU訓練
      ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
                 
    • 更換GPU訓練
      ./darknet detector -i 2 train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
                 
    • 多GPU訓練
      ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 -gpus 0,1,2,3
                 
    • CPU訓練
      ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 --nogpu
                 
  3. 模型評估recall,生成測試結果
    • 生成測試結果
      ./darknet detector valid <data_cfg> <test_cfg> <weights> <out_file>
                 

      (1)<test_cfg>檔案中batch和subdivisions兩項需為1。

      (2)測試結果生成在<data_cfg>的results指定的目錄下以<out_file>開頭的若幹檔案中,若<data_cfg>沒有指定results,那麼預設為darknet/results。

    • 計算recall(需要修改detectocr.c請參考第五部分優化與個性化——計算map與recall)
      /darknet detector recall <data_cfg> <test_cfg> <weights>
                 

      (1)<test_cfg>檔案中batch和subdivisions兩項需為1

      (2)RPs/Img、IOU、Recall都是到目前測試圖檔的均值(意義待續)

訓練自己的資料

  1. 首先下載下傳預訓練卷積權重
    wget https://pjreddie.com/media/files/darknet53.conv.74
               
  2. 其次标注資料集,生成yolo所需要的txt格式的labels
    • 推薦使用yolo_mark資料集标注工具,可以直接生成txt
    • 下載下傳yolo_mark
      git clone https://github.com/AlexeyAB/Yolo_mark.git
                 
    • 要在Linux上編譯,輸入:
      cd Yolo_mark
      cmake .
      make
      chmod +x linux_mark.sh
      ./linux_mark.sh
                 
    • 打開yolo_mark就是GUI的标注了,主要是修改data和names,具體請參考官網
  3. 修改配置檔案
    • 修改.data檔案
      classes= 4 (修改成自己訓練的種類數)
      train  = /home/user_name/darknet/data/train.txt   (修改成自己train.txt的路徑)
      valid  = /home/user_name/darknet/data/2007_test.txt (評估測試的圖檔的路徑,用于後面的評估測試)
      names = data/voc.names                                  (修改成自己的類别名的路徑)
      backup = backup                                         (訓練的權重所存放的路徑)
      results = results                            (評估測試結果存放路徑,也可以自己定義)
                 
    • 修改.names這個比較簡單,修改自己的類别就可以

      例如我的檔案是yolov3.names

    YOLOv3:訓練自己的資料(附優化與問題總結)
    • 修改.cfg檔案
    • 關于cfg修改,以4類目标檢測為例,主要有以下幾處調整(藍色标出),#表示注釋,根據訓練和測試,自行修改,修改說明:

      A:filters的計算和聚類數目分布有關

      yolov3:filters=(classes + 5)x3

      yolov2: filters=(classes + 5)x5。

      B.classes就是類别數量

      C.如果想修改預設anchors數值,可以使用k-means;

      D.如果顯存夠大,則可以開啟多尺度訓練令random=1,不過loss會有點震蕩;

      給出一份我的cfg以供參考,傳送門
      [net]

      Testing

      batch=1

      subdivisions=1

      Training

      batch=64

      subdivisions=8

      ......

      [convolutional]

      size=1

      stride=1

      pad=1

      filters=27###75

      activation=linear

      [yolo]

      mask = 6,7,8

      anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326

      classes=4###20

      num=9

      jitter=.3

      ignore_thresh = .5

      truth_thresh = 1

      random=0###1

      mask = 3,4,5

      mask = 0,1,2

    • 給一份标注好的資料集,有需要的可以參考目錄和格式.百度網盤戳我

      提取碼: j1zi

  4. 開始訓練
    ./darknet detector train cfg/yolov3.data cfg/yolov3.cfg  darknet53.conv.74
               
    YOLOv3:訓練自己的資料(附優化與問題總結)

    avg:平均loss,越小越好,

    rate:學習率,預設為0.001,可以根據自己的訓練情況調整,一般為0.01,0.003,0.001等

    訓練參數說明

    Region xx: cfg檔案中yolo-layer的索引,82是大物體層,94是中物體層,106小物體層;

    Avg IOU: 目前疊代中,預測的box與标注的box的平均交并比,越大越好,期望數值為1;

    Class: 标注物體的分類準确率,越大越好,期望數值為1;

    obj: 越大越好,期望數值為1;

    No obj: 越小越好;

    .5R: 以IOU=0.5為門檻值時候的recall; recall = 檢出的正樣本/實際的正樣本

    0.75R: 以IOU=0.75為門檻值時候的recall;

    count: 正樣本數目。

優化與個性化問題

這邊的更改之後都需要make clean後再make。如果改了之後發現沒變化,請第一時間看看是不是沒有執行這一步。
  1. 什麼時候儲存模型,又要如何更改呢?

    疊代次數小于1000時,每100次儲存一次,大于1000時,每10000次儲存一次。

    自己可以根據需求進行更改,然後重新編譯即可[ 先 make clean ,然後再 make]

    代碼位置: examples/detector.c line 138

    YOLOv3:訓練自己的資料(附優化與問題總結)
  2. 如何在圖像上添加置信度?

    可以看github的修改記錄,也可以看下面的修改,修改傳送門

    修改src/image.c檔案draw_detections()函數,代碼修改如下:

    int i,j;
     
        for(i = 0; i < num; ++i){
            char labelstr[4096] = {0};
    		char s1[]={"  "};//為了name與置信度之間加空格
            int class = -1;
    	    char possible[5];//存放檢測的置信值
            for(j = 0; j < classes; ++j){
    	    sprintf(possible,"%.2f",dets[i].prob[j]);//置信值截取小數點後兩位賦給possible
                if (dets[i].prob[j] > thresh){
                    if (class < 0) {
                        strcat(labelstr, names[j]);
    					strcat(labelstr,s1); //加空格
    		            strcat(labelstr, possible);//标簽中加入置信值
                        class = j;
                    } else {
                        strcat(labelstr, ", ");
                        strcat(labelstr, names[j]);
    					strcat(labelstr,s1);//加空格
    		            strcat(labelstr, possible);//标簽中加入置信值
                    }
                    printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100);
                }
            }
           
               
  3. 如何縮小标簽大小?

    src/image.c的draw_detections()函數中,get_label函數調用的參數,修改成合适的大小

    YOLOv3:訓練自己的資料(附優化與問題總結)
    效果圖:
    YOLOv3:訓練自己的資料(附優化與問題總結)
  4. 如何添加中文标簽?
    • 首先制作中文标簽圖檔,

      在darknet/data/labels裡面有制作腳本make_labels.py。修改以下就可以用了

      例如我的是四類,我就修改成

      # -*- coding: utf-8 -*-  
      
      import os   
      l=["行人","前部","側邊","後部"]
      def make_labels(s): 
          i = 0 
          for word in l:   
              os.system("convert -fill black -background white -bordercolor white -border 4  -font /usr/share/fonts/truetype/arphic/ukai.ttc -pointsize %d label:\"%s\" \"cn_%d_%d.png\""%(s,word,i,s/12-1)) 
            i = i + 1 
      for i in [12,24,36,48,60,72,84,96]:
          make_labels(i) 
                 
      運作腳本
      python make_labels.py
                 
      就可以在labels裡面看到自己制作的圖檔标簽
      YOLOv3:訓練自己的資料(附優化與問題總結)
    • 其次修改src/image.c的代碼

      具體請看github的修改,傳送門,修改完之後,make clean && make.

      測試以下,如圖,Bingo!!!

      YOLOv3:訓練自己的資料(附優化與問題總結)
  5. 如何批量檢測?

    可以看我github的修改,也可以按照下面的修改。修改傳送門

    • 首先在添加一個擷取圖檔名字的函數:
    #include "darknet.h"
    #include <opencv2/highgui/highgui.hpp>
    static int coco_ids[]={1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90};
    
    char *GetFilename(char *p)
    { 
       static char name[20]={""};
       char *q = strrchr(p,'/') + 1;
       strncpy(name,q,10);//後面的10是你自己圖檔名的長度,可修改
       return name;
    
    }
               
    • 替換examples/detector.c 中的test_detector函數
    void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen)
    {
        list *options = read_data_cfg(datacfg);
        char *name_list = option_find_str(options, "names", "data/names.list");
        char **names = get_labels(name_list);
     
        image **alphabet = load_alphabet();
        network *net = load_network(cfgfile, weightfile, 0);
        set_batch_network(net, 1);
        srand(2222222);
        double time;
        char buff[256];
        char *input = buff;
        float nms=.45;
        int i=0;
        while(1){
            if(filename){
                strncpy(input, filename, 256);
                image im = load_image_color(input,0,0);
                image sized = letterbox_image(im, net->w, net->h);
                layer l = net->layers[net->n-1];
     
     
                float *X = sized.data;
                time=what_time_is_it_now();
                network_predict(net, X);
                printf("%s: Predicted in %f seconds.\n", input, what_time_is_it_now()-time);
                int nboxes = 0;
                detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);
                if (nms) do_nms_sort(dets, nboxes, l.classes, nms);
                    draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);
                    free_detections(dets, nboxes);
                if(outfile)
                 {
                    save_image(im, outfile);
                 }
                else{
                    save_image(im, "predictions");
    #ifdef OPENCV
                    cvNamedWindow("predictions", CV_WINDOW_NORMAL); 
                    if(fullscreen){
                    cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
                    }
                    show_image(im, "predictions",0);
                    cvWaitKey(0);
                    cvDestroyAllWindows();
    #endif
                }
                free_image(im);
                free_image(sized);
                if (filename) break;
             } 
            else {
                printf("Enter Image Path: ");
                fflush(stdout);
                input = fgets(input, 256, stdin);
                if(!input) return;
                strtok(input, "\n");
       
                list *plist = get_paths(input);
                char **paths = (char **)list_to_array(plist);
                 printf("Start Testing!\n");
                int m = plist->size;
                if(access("/home/lzm/data/test_folder/darknet/car_person/out",0)==-1)//"/homelzm/......"修改成自己要儲存圖檔的的路徑
                {
                  if (mkdir("/home/lzm/data/test_folder/darknet/car_person/out",0777))//"/homelzm/......"修改成自己要儲存圖檔的的路徑
                   {
                     printf("creat folder failed!!!");
                   }
                }
                for(i = 0; i < m; ++i){
                 char *path = paths[i];
                 image im = load_image_color(path,0,0);
                 image sized = letterbox_image(im, net->w, net->h);
            //image sized = resize_image(im, net->w, net->h);
            //image sized2 = resize_max(im, net->w);
            //image sized = crop_image(sized2, -((net->w - sized2.w)/2), -((net->h - sized2.h)/2), net->w, net->h);
            //resize_network(net, sized.w, sized.h);
            layer l = net->layers[net->n-1];
     
     
            float *X = sized.data;
            time=what_time_is_it_now();
            network_predict(net, X);
            printf("Try Very Hard:");
            printf("%s: Predicted in %f seconds.\n", path, what_time_is_it_now()-time);
            int nboxes = 0;
            detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);
            //printf("%d\n", nboxes);
            //if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
            if (nms) do_nms_sort(dets, nboxes, l.classes, nms);
            draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);
            free_detections(dets, nboxes);
            if(outfile){
                save_image(im, outfile);
            }
            else{
                 
                 char b[2048];
                sprintf(b,"/home/lzm/data/test_folder/darknet/car_person/out/%s",GetFilename(path));//"/homelzm/......"修改成自己要儲存圖檔的的路徑
                
                save_image(im, b);
                printf("OJBK!\n",GetFilename(path));
    #ifdef OPENCV
                cvNamedWindow("predictions", CV_WINDOW_NORMAL); 
                if(fullscreen){
                    cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
                }
                show_image(im, "predictions",0);
                cvWaitKey(0);
                cvDestroyAllWindows();
    #endif
            }
     
            free_image(im);
            free_image(sized);
            if (filename) break;
            }
          }
        }
    }
               
    make clean後再make然後批量測試即可,輸入的路徑為那些圖檔路徑的txt。
    YOLOv3:訓練自己的資料(附優化與問題總結)
  6. 如何輸入檢測結果文本,計算recall,map計算
    • ./darknet detector valid <data_cfg> <test_cfg> <weights> <out_file>
                 

      <test_cfg>檔案中batch和subdivisions兩項必須為1。

      結果生成在<data_cfg>的results指定的目錄下以<out_file>開頭的若幹檔案中,若<data_cfg>沒有指定results,那麼預設為<darknet_root>/results。

      如果出現以下問題,則說明在.data檔案裡面沒有指定valid的路徑,添加即可

      Couldn't open file: data/train.list
                 
      YOLOv3:訓練自己的資料(附優化與問題總結)
    • 計算recall

      修改examples/detector.c的validate_detector_recall函數,修改傳送門

      首先将validate_detector_recall函數定義和調用修改成

      void validate_detector_recall(char *datacfg, char *cfgfile, char *weightfile)
      validate_detector_recall(datacfg, cfg, weights);
                 
      list *plist = get_paths("data/coco_val_5k.list");
      char **paths = (char **)list_to_array(plist);
          修改成
      list *options = read_data_cfg(datacfg);
      char *valid_images = option_find_str(options, "valid", "data/train.list");
      list *plist = get_paths(valid_images);
      char **paths = (char **)list_to_array(plist);
                 
      修改完老規矩,make clean後再make
      ./darknet detector recall <data_cfg>  <test_cfg>  <weights>
                 
      YOLOv3:訓練自己的資料(附優化與問題總結)
    • 計算map
      • 先制作xml檔案,由于yolo需要的是txt标注,是以之前沒有制作xml的。現在利用一個腳本txt2xml.py進行轉換,txt2xml.py傳送門。

        在python3的環境下運作,就可以得到xml檔案

      • 計算mAP

        借助py-faster-rcnn下的voc_eval.py計算,voc_eval.py傳送門

        然後建立一個all_map.py(顧名思義是計算所有類的ap,平均一下就是map)的計算腳本,這邊提供一個例子,根據注釋修改即可。all_map.py傳送門

        python2 all_map.py即可看到ap

        若不想讓他輸出這麼多rec什麼,可以修改voc.eval.py的傳回值隻傳回ap既可

        若要輸出結果就直接在指令後面加一個 > ap_res.txt 即可

        若有需求重複計算map,需要删除生成的annots.pkl

        YOLOv3:訓練自己的資料(附優化與問題總結)
  7. 背景運作并儲存訓練日志

    nohup 執行指令 > 輸出檔案 2>&1 &

    例如

    nohup ./darknet detector train head_detector/head.data head_detector/head.cfg head_detector/backup/head.backup -gpus 0,1 > train.log 2>&1 &
               
  8. Loss和IOU可視化

    用上面生成的train.log來可視化,代碼在github,可以根據注釋修改,安裝pandas等相關子產品即可

    代碼傳送門

    我的訓練初步效果

    YOLOv3:訓練自己的資料(附優化與問題總結)
    YOLOv3:訓練自己的資料(附優化與問題總結)
  9. 使用python接口進行批量檢測并儲存測試結果和分割圖檔

    隻需要将python接口中的檔案路徑替換成自己的,具體看github代碼注釋,代碼傳送門

    python darknet.py
               
    測試結果目錄樹類似以下。若出現子產品報錯等,則安裝相應子產品即可。
    YOLOv3:訓練自己的資料(附優化與問題總結)

可能出現的問題

  1. 出現大量的-nan

    有三個可能

    1. 可能cfg檔案的batch=1,subdivisions=1
    2. 可能batch太小了
    3. 訓練的圖檔中沒有遍布大中小三個層
    解決方案
    1. 修改成Training狀态的參數batch=64, subdicisions=16
    YOLOv3:訓練自己的資料(附優化與問題總結)
    1. 在顯存允許的情況下,增加batch
    2. 第三種情況屬于正常,因為YOLOv3從三個scale上提取了特征,且不同尺度選取了不同尺度的的檢測框,比如你在訓練小目标,那麼region82和region94出現nan就正常。
  2. CUDA: out of memory
    darknet: ./src/cuda.c:36: check_error: Assertion `0' failed.
               
    YOLOv3:訓練自己的資料(附優化與問題總結)

    原因:顯存不夠

    解決方案:關閉random,如果還是不行,可以适當調小batch(32或者16)

  3. 訓練了很多次itertation,但是還是什麼都沒有檢測出來?
    1. 檢測的時候需要batch和sub不是1 ;
    2. 如果調低thresh後還沒有出現bbox,那麼就是訓練的時候可能batch和sub都是1,這樣訓練的模 型幾乎不能用
    1. 測試的時候.cfg檔案中令 (batch=1,subdivisions=1);
    2. ​訓練的時候将.cfg檔案修改成(batch=64, subdicisions=16)
  4. cfg修改問題?
    darknet: ./src/parser.c:348: parse_region: Assertion `l.outputs == params.inputs' failed
               

    原因:filters修改錯誤,沒有對上classes;

    解決方案:修改filters

    YOLOv2: filters =(classes+5)x5

    YOLOv3: filters =(classes+5)x3

  5. can't open file :train.txt or can't load image ?
    Couldn't open file:/home/lzm/test_folder/darknet/car_person/labels/jam6_0116.txt
               
    YOLOv3:訓練自己的資料(附優化與問題總結)

    原因:一般來說是train.txt的檔案格式問題;

    解決方案:把train.txt的換行格式修改成unix換行格式,另外圖檔路徑要正确,參考博文

  6. Segmentation fault (core dumped),訓練幾百次intertation後分段錯誤
    1. 開了尺度訓練即cfg中random=1,顯存不足;
    2. 标注的box裡面出現x,y=0的情況;
    1. 關閉尺度訓練,random=0;
    2. 修改坐标讓x,y>0; 參考issue
  7. detector recall的時候出現大量nan
    YOLOv3:訓練自己的資料(附優化與問題總結)
    原因:detector.c代碼中的bug,k的最大值必須由nbox确定,而不是
    YOLOv3:訓練自己的資料(附優化與問題總結)
    解決方案:修改examples/detector.c的代碼,修改參考
  8. 為了批量檢測圖檔,修改detector.c後,編譯出現問題
    error: too few arguments to function ‘show_image’
    show_image(im, "predictions");
               
    YOLOv3:訓練自己的資料(附優化與問題總結)

    原因:show_image函數少了一個參數,有些代碼是比較舊的。

    解決方案:修改報錯的那一行

    show_image(im, "predictions");
    改成
    show_image(im, "predictions",0);
               
  9. error: ‘CV_WINDOW_NORMAL’ undeclared (first use in this function)
    cvNamedWindow("predictions", CV_WINDOW_NORMAL);
               
    YOLOv3:訓練自己的資料(附優化與問題總結)

    原因:原因連結,darknet.h還少了opencv的一個頭檔案,#include <opencv2/highgui/highgui.hpp>

    解決方案:在報錯的程式中加上即可

    #include "darknet.h"
    #include <opencv2/highgui/highgui.hpp>
               
  10. 使用txt2xml.py生成xml時候的問題

    問題issue

'encoding' is an invalid keyword argument for this function
           

原因:在Python 2中,open()函數不帶編碼參數(第三個參數是緩沖選項)

解決方法:有兩種

  1. import io

    open改成 io.open

  2. 使用python3

References

  • https://pjreddie.com/darknet/
  • https://blog.csdn.net/Pattorio/article/details/80051988
  • https://blog.csdn.net/lilai619/article/details/79695109
  • https://blog.csdn.net/mieleizhi0522/article/details/79989754
  • https://blog.csdn.net/cgt19910923/article/details/80524173
  • https://github.com/AlexeyAB/darknet/blob/master/README.md
    YOLOv3:訓練自己的資料(附優化與問題總結)