RCNN系列我是大概看過的,還寫過:RCNN系列,但是這種location和classification分開的思路,要達到實時的話我的硬件條件肯定是不可能的。YOLOV3我是在TX2上跑過的:YOLOV3-TX2跑起來,而且YOLO是有簡化版本的模型的,對于簡單應用應該是夠了。
因為以前跑過,整體的流程走下來還算比較順利,比起SSD來說,訓練時要修改的代碼也比較少,可能留給犯錯的概率就少一些。
我分以下幾個部分:
- 1. YOLO系列簡介。
- 2. 編譯環(huán)境準備。
- 3. 訓練配置。
1. YOLOV3系列簡介。
1.1:簡介。
- 作者:Joseph Redmon
- YOLO主頁:YOLO
- 作者github: https://github.com/pjreddie
- 模型簡介:You only look once (YOLO) is a state-of-the-art, real-time object detection system. On a Pascal Titan X it processes images at 30 FPS and has a mAP of 57.9% on COCO test-dev.
和其他模型的對比:(總結起來一句話:比它快的準確率沒有它高,準確率比它高的沒有他快)
| Model | Train | Test | mAP | FLOPS | FPS | Cfg | Weights |
|---|---|---|---|---|---|---|---|
| SSD300 | COCO trainval | test-dev | 41.2 | - | 46 | link | |
| SSD500 | COCO trainval | test-dev | 46.5 | - | 19 | link | |
| YOLOv2 608x608 | COCO trainval | test-dev | 48.1 | 62.94 Bn | 40 | cfg | weights |
| Tiny YOLO | COCO trainval | test-dev | 23.7 | 5.41 Bn | 244 | cfg | weights |
| SSD321 | COCO trainval | test-dev | 45.4 | - | 16 | link | |
| DSSD321 | COCO trainval | test-dev | 46.1 | - | 12 | link | |
| R-FCN | COCO trainval | test-dev | 51.9 | - | 12 | link | |
| SSD513 | COCO trainval | test-dev | 50.4 | - | 8 | link | |
| DSSD513 | COCO trainval | test-dev | 53.3 | - | 6 | link | |
| FPN FRCN | COCO trainval | test-dev | 59.1 | - | 6 | link | |
| Retinanet-50-500 | COCO trainval | test-dev | 50.9 | - | 14 | link | |
| Retinanet-101-500 | COCO trainval | test-dev | 53.1 | - | 11 | link | |
| Retinanet-101-800 | COCO trainval | test-dev | 57.5 | - | 5 | link | |
| YOLOv3-320 | COCO trainval | test-dev | 51.5 | 38.97 Bn | 45 | cfg | weights |
| YOLOv3-416 | COCO trainval | test-dev | 55.3 | 65.86 Bn | 35 | cfg | weights |
| YOLOv3-608 | COCO trainval | test-dev | 57.9 | 140.69 Bn | 20 | cfg | weights |
| YOLOv3-tiny | COCO trainval | test-dev | 33.1 | 5.56 Bn | 220 | cfg | weights |
| YOLOv3-spp | COCO trainval | test-dev | 60.6 | 141.45 Bn | 20 | cfg | weights |
1.2.YOLO原理。
論文我正在看,等我看完了再寫這一部分,但是因為這個模型的訓練和檢測框架都是端到端的,所以即使 不了解中間的細節(jié)也是可以訓練和檢測的。
2. 編譯環(huán)境準備。
相較于其他模型來說,darknet的編譯環(huán)境是最簡單的。因為他并沒有用時下比較流行的深度學習框架來寫,而是作者自己用C擼了一個框架:darknet。這個框架是用C和CUDA-C來寫的(這代碼能力就只有仰望的份了),所以裝了ubuntu系統(tǒng)裝好以后就可以用了,非常簡單。
如果需要(一般肯定是需要的)GPU加速的話,那么需要安裝NVIIDA的那一套東西(驅動,CUDA,CUDNN),如果需要測試視頻和顯示視頻或者照片的話,那么需要安裝opencv。
所以是不是很簡單?
我自己的電腦是win10+ubuntu的雙系統(tǒng)。win10下darknet也是可以配置的,但是為了簡單和適應linux系統(tǒng)我這次還是選擇了ubuntu系統(tǒng),中間安裝opencv的時候空間不夠了,我手賤想從win10下面分出來一點空間來給ubuntu的時候,出了岔子,ubunut系統(tǒng)崩掉了,一氣之下我把C盤清空了,兩個系統(tǒng)都重裝了。
裝系統(tǒng)的話就不多說了,win10下裝ubuntu的話大概以下幾個步驟:
- ①從win的系統(tǒng)盤右鍵壓縮出來50G(有的話可以再多點)的未分配的空間。(可能是綠色或者黑色的,不是很影響)
- ②制作ubuntu的u盤啟動盤,用軟碟通就可以,這個很簡單。
- ③重啟,進bios,從u盤啟動,順便把電腦的網(wǎng)斷了(拔掉網(wǎng)線或者關閉wifi),然后安裝,不要選擇安裝更新,并且自己分配空間,就從剛才從win10上壓縮的未分配空間來分配。
然后基本就可以了,如果遇到什么問題,勤百度,這寫坑基本上都有。
至于安裝NVIDIA的那一套,網(wǎng)上也是有很多教程,我也是踩了很多坑,現(xiàn)在也無法截圖寫教程了,就不說了,這一套更新挺快的,并不是越新越好(越適合自己的項目),注意各個版本需要匹配。
opencv的編譯寫一下,因為我正在弄。
- ①安裝一些依賴環(huán)境:
sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev
sudo apt-get install –assume-yes libopencv-dev libdc1394-22 libdc1394-22-dev libjpeg-dev libpng12-dev libtiff5-dev libjasper-dev libavcodec-dev libavformat-dev libswscale-dev libxine2-dev libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev libv4l-dev libtbb-dev libqt4-dev libfaac-dev libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev x264 v4l-utils unzip
sudo apt-get install ffmpeg libopencv-dev libgtk-3-dev python-numpy python3-numpy libdc1394-22 libdc1394-22-dev libjpeg-dev libpng12-dev libtiff5-dev libjasper-dev libavcodec-dev libavformat-dev libswscale-dev libxine2-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libv4l-dev libtbb-dev qtbase5-dev libfaac-dev libmp3lame-dev libopencore-amrnb-dev
選擇的地方全部選擇yes,然后等待安裝完成就可以了。
- ② 下載opencv源碼,這個自己想辦法吧,網(wǎng)絡好的話還是比較快的。然后解壓。
- ③ 配置cmake編譯。
cd opencv-3.4.1 #解壓的文件
mkdir build #創(chuàng)建build文件夾
cd build #進入build文件夾
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D WITH_V4L=ON -D WITH_QT=ON -D WITH_OPENGL=ON -D WITH_GTK=ON -D WITH_GTK_2_X=ON -D CUDA_NVCC_FLAGS="-D_FORCE_INLINES" ..
#配置cmake
sudo make #編譯
編譯還是挺慢的,我跑了步回來還沒好,耐心等著就可以了。
然后
sudo make install #這個sudo是必須的,要不會因為權限問題產(chǎn)生錯誤
這樣就可以了,有的教程寫這里需要添加動態(tài)連接什么的,我倒沒有這么做,就直接可以用了。
- ④測試。
mkdir opencv_cpp #建立一個文件夾:
gedit lena.cpp #建立測試文件
gedit CMakelist.txt #建立CMakeLists文件
cmake . #編譯頭文件和源文件
make #生成可執(zhí)行文件
CMakelist.txt 里面寫的是:
cmake_minimum_required(VERSION 2.8)
project( DisplayImage )
find_package( OpenCV REQUIRED )
add_executable( LENA lena.cpp )
target_link_libraries( LENA ${OpenCV_LIBS} )
cpp文件里寫的是:
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv )
{ if ( argc != 2 )
{ printf("usage: DisplayImage.out <Image_Path>\n");
return -1; }
Mat image;
image = imread( argv[1], 1 );
if ( !image.data )
{ printf("No image data \n"); return -1; }
namedWindow("Display Image", WINDOW_AUTOSIZE );
imshow("Display Image", image);
waitKey(0);
return 0;
}
然后找一張照片命名lena.jpg,放到lena.cpp的同級目錄下。
最后執(zhí)行:
./LENA lena.jpg 看到顯示照片的話就說明沒有問題了。
c++版本的opencv這里就可以用了,但是沒有好用的編輯器,還是不如win下面VS寫起來好用。
3.訓練配置。
這里訓練使用VOC2007的數(shù)據(jù)格式,這個數(shù)據(jù)格式在前面的文章中已經(jīng)做了介紹:VOC制作數(shù)據(jù)集

其中
Annotations中存的是打好的標簽,xml格式。一個典型的標簽長這樣:

里面包含了文件名,路徑,尺寸,通道以及標注的目標以及目標的位置和尺寸。
在JPEGImages文件夾里面是所有的原始照片。

imageSets文件夾里面是我們分配的訓練集和驗證集的個數(shù):這個我們可以用代碼來生成,代碼給在下面,改的別人的。這個代碼的主要作用就是生成main文件夾下的TXT文件。
import os
import random
trainval_percent = 1 # trainval數(shù)據(jù)集占所有數(shù)據(jù)的比例
train_percent = 0.9 # train數(shù)據(jù)集占trainval數(shù)據(jù)的比例
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets\Main'
total_xml = os.listdir(xmlfilepath)
num=len(total_xml)
list=range(num)
tv=int(num*trainval_percent)
tr=int(tv*train_percent)
trainval= random.sample(list,tv)
train=random.sample(trainval,tr)
ftrainval = open('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w')
for i in list:
name=total_xml[i][:-4]+'\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest .close()
好了,下面有幾個步驟:
3.1. 生成labels標簽和train文件地址索引。
VOC的格式就差不多是上面那些了,但是YOLO要求的標簽需要是歸一化以后的坐標以及尺寸,所以需要生成這樣的訓練標簽,以及,用哪些照片來訓練是需要把它們的目錄索引寫在文本文件中的。
參考代碼如下:
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
#sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
sets=[('2007','train')]
#修改成自己的訓練文件,我們只用了2007的train,所以這里這么改
#classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
classes = ["DJI"]
#修改成自己的類別,我這里只有一類,所以這樣寫。
#classes=[str(i) for i in range(1)]
#這個函數(shù)就是為了獲得歸一化坐標的。
def convert(size, box):
#print(size[0])
dw = 1./size[0]
dh = 1./size[1]
x = (box[0] + box[1])/2.0
y = (box[2] + box[3])/2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x*dw
w = w*dw
y = y*dh
h = h*dh
return (x,y,w,h)
def convert_annotation(year, image_id):
in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))
out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, image_id), 'w')
tree=ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
bb = convert((w,h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
wd = getcwd()
for year, image_set in sets:
print(year)
print(image_set)
if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)):
os.makedirs('VOCdevkit/VOC%s/labels/'%(year))
image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
list_file = open('%s_%s.txt'%(year, image_set), 'w')
for image_id in image_ids:
list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))
convert_annotation(year, image_id)
list_file.close()
這樣會在labels文件夾下面生成每個圖像的標注信息,我隨表找了一個,如下:

darknet根文件夾下面會生成2007_train.txt文件,這個文件里面寫的就是所有待訓練圖片的目錄信息,如圖:

3.2. 下載預訓練權重。
這個主要是卷積部分的預訓練權重,使用這個權重可以節(jié)省訓練時間,直接輸入這個命令下載,或者去darknet官網(wǎng)上下載都是可以的。
wget https://pjreddie.com/media/files/darknet53.conv.74
3.3. 修改cfg/voc.data
這個主要是使用VOC格式的數(shù)據(jù)進行訓練的時候的一些路徑設置:
classes= 1 #類別
train =/home/zhxing/darknet/2007_train.txt #訓練圖片路徑存儲的txt
valid = /home/zhxing/darknet/2007_test.txt #測試圖片路徑存儲的txt
names = data/voc.names #voc.names,這個里面存的是名稱,下面修改
backup = backup #存放訓練好的模型的位置,這個文件夾在darknet下自己新建一個就可以了
3.4. 新建backup文件夾。
3.5. 修改data/voc.names
就是把對應的名稱來修改,我是只有用一類,所以就這樣了,這里寫的其實和標注的時候那個data文件夾下的txt里面寫的是一樣的內容。

3.6. 修改cfg/yolov3-voc.cfg
最后是來修改這個文件,這個文件存的就是yolov3的網(wǎng)絡結構,darknet這個框架中cfg里面不同的配置文件對應著不同的網(wǎng)絡結構,用來訓練和檢測都是在cfg文件里進行定義的,所以這里我們是要做一些修改的。主要修改的部分是與類別相關的,還挺長的,我把需要修改的用中文注釋一下。
[net]
# Testing
#batch=1
#subdivisions=1
# 改成訓練模式,batch和subdivisions修改一下,這個關系到GPU內存是否夠用。
Training #訓練模式打開
batch=32
subdivisions=16
# 每次前向的圖片數(shù)目=batch/subdivisons
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1
learning_rate=0.001
burn_in=1000
max_batches = 50200
policy=steps
steps=40000,45000
scales=.1,.1
[convolutional]
batch_normalize=1
filters=32
size=3
stride=1
pad=1
activation=leaky
# Downsample
[convolutional]
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=32
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
# Downsample
[convolutional]
batch_normalize=1
filters=128
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=64
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=64
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
# Downsample
[convolutional]
batch_normalize=1
filters=256
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
# Downsample
[convolutional]
batch_normalize=1
filters=512
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
# Downsample
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
######################
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=1024
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=1024
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=1024
activation=leaky
[convolutional]
size=1
stride=1
pad=1
filters=18
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=1 #類別
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1 #是否關閉多尺度訓練
[route]
layers = -4
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[upsample]
stride=2
[route]
layers = -1, 61
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky
[convolutional]
size=1
stride=1
pad=1
filters=18 # 這里改成3×(類別+4+1),這個看文章就是到是怎么回事了,是每個grid生成的向量的維數(shù)
activation=linear
[yolo]
mask = 3,4,5
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=1 #類別數(shù)
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1
[route]
layers = -4
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[upsample]
stride=2
[route]
layers = -1, 36
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky
[convolutional]
size=1
stride=1
pad=1
filters=18 #修改,3×(類別+4+1)
activation=linear
[yolo]
mask = 0,1,2
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=1 #修改,類別
num=9
jitter=.3
ignore_thresh = .5 #閾值
truth_thresh = 1
random=1 #是否進行多尺度訓練
需要注意,修改cfg文件是不需要重新編譯源碼的,所以非常方便,可以根據(jù)自己的需求進行修改,需要修改的地方基本都在命名未[yolo]的結構下面,對照著改一下就可以了,下面就可以開始訓練了。
3.7. 開始訓練。
輸入以下命令,開始訓練。
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
會進行權重加載然后訓練:

參數(shù)意義:
Region xx: cfg文件中yolo-layer的索引.
AVE IOU: 當前迭代中,預測的box和標注的box的平均交并比,越大越好,最大是1.
class: 標注物體的分類準確率,越大越好,期望值是1.
obj: 越大越好,期望值為1.
no obj:越小越好,期望值為0.
.5R: IOU=0.5時的召回率,召回率=檢測出的正樣本/實際的正樣本
0.75R: 和上面的一樣,閾值為0.75
count: 正樣本數(shù)目.
每一個batch結束之后,會顯示當前batch的損失和平均損失:

開頭為數(shù)字的那一行,意義分別是:
batch 總損失 平均損失 學習率 rate 消耗時間 使用圖片
默認的是每小于1000的時候每100個batch保存一次模型,大于1000的時候每10000個batch保存一次模型,這個可以根據(jù)自己的需求修改,代碼在examples/detector.c line 138:
save_weights(net, buff);
}
if(i%1000==0 || (i < 1000 && i%100 == 0)){
//需要修改保存模型的batch的話修改這里,我已經(jīng)改過了,大于1000的話每1000個batch存一次模型.
#ifdef GPU
if(ngpus != 1) sync_nets(nets, ngpus, 0);
#endif
char buff[256];
sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i);
save_weights(net, buff);
}
free_data(train);
}
#ifdef GPU
基本上就是這些了,訓練需要結束的話關掉終端就可以了(一般看損失降到差不多或者召回率比較高的時候),然后可以加載訓練好的模型來檢測。
3.8. 檢測.
./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_final.weights 01.jpg
把weights文件和jpg文件換成自己的就可以了。
4. 其他。
基本的訓練流程就是這樣了,還有些其他的:比如如何批量測試保存圖片,測試保存視頻,保存loss信息等一些列問題,有的我已經(jīng)解決了,有的還沒有,這一篇太龐雜了,打算新開一篇了。
我自己做的無人機檢測,第一次搞的數(shù)據(jù)量感覺有點少,最后的結果也非常一般,這一次數(shù)據(jù)量擴充了5倍,剛剛標注完,所以重新走了一遍訓練的流程,順便記錄下來,希望一切順利!這一篇就到此結束了。