赞
踩
下载源代码: YOLOv3 项目源代码
也可以直接输入命令进行下载
git clone https://github.com/pjreddie/darknet
终端进入 darknet 文件夹下
cd darknet
如果使用 CPU 直接编译源代码,无须修改 Makefile 文件。
如果使用GPU训练,需要修改 Makefile
GPU=1
CUDNN=1
OPENCV=1
NVCC=/usr/local/cuda-9.0/bin/nvcc
ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda/include/
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand
endif
修改后为:
ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda-9.0/include/
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda-9.0/lib64 -lcuda -lcudart -lcublas -lcurand
endif
# ARCH= -gencode arch=compute_30,code=sm_30 \
# -gencode arch=compute_35,code=sm_35 \
# -gencode arch=compute_50,code=[sm_50,compute_50] \
# -gencode arch=compute_52,code=[sm_52,compute_52]
# -gencode arch=compute_20,code=[sm_20,sm_21] \ This one is deprecated?
# This is what I use, uncomment if you know your arch and want to specify
ARCH = -gencode arch=compute_61,code=sm_61
最后进行编译
sudo make
如果出现如下输出,说明已经编译过了且你没有作任何改动。已经有了 libdarknet.a 和 libdarknet.so 文件。
make: Nothing to be done for 'all'.
首先下载权重文件,并运行 demo,测试得到的结果 predictions.jpg
会以图片的形式保存在 darknet 文件夹下
# 下载权重文件
wget https://pjreddie.com/media/files/yolov3.weights
# 测试 dog 图片
./darknet detect cfg/yolov3.cfg yolov3.weights /data/monn/darknet/data/dog.jpg
/data/monn/darknet/data/dog.jpg
是要进行测试的图片路径输出结果:
layer filters size input output
0 conv 32 3 x 3 / 1 608 x 608 x 3 -> 608 x 608 x 32 0.639 BFLOPs
1 conv 64 3 x 3 / 2 608 x 608 x 32 -> 304 x 304 x 64 3.407 BFLOPs
2 conv 32 1 x 1 / 1 304 x 304 x 64 -> 304 x 304 x 32 0.379 BFLOPs
3 conv 64 3 x 3 / 1 304 x 304 x 32 -> 304 x 304 x 64 3.407 BFLOPs
...
105 conv 255 1 x 1 / 1 76 x 76 x 256 -> 76 x 76 x 255 0.754 BFLOPs
106 yolo
Loading weights from yolov3.weights...Done!
/data/monn/darknet/data/dog.jpg: Predicted in 26.420837 seconds.
dog: 100%
truck: 92%
bicycle: 99%
VOC 数据集下载地址:PASCAL VOC
在 darknet 目录下新建 voc 文件夹,终端进入 voc 目录下,执行如下命令,下载 PASCAL VOC 数据集。打算用 2007 和 2012 一起进行训练,所以都要下载哈。
wget https://pjreddie.com/media/files/VOCtrainval_11-May-2012.tar
wget https://pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar
wget https://pjreddie.com/media/files/VOCtest_06-Nov-2007.tar
或者可以直接下载好以后放在 voc 文件夹下,速度比较快。
下载完成后对数据集进行解压:
tar xf VOCtrainval_11-May-2012.tar
tar xf VOCtrainval_06-Nov-2007.tar
tar xf VOCtest_06-Nov-2007.tar
然后 voc 文件夹下就会出现 VOCdevkit/ 目录:
VOCdevkit 下包含了 VOC 2007 和 2012 两部分:PASCAL VOC 数据集格式见:
darknet 要求为图片数据集生成 txt 格式的标签文件,标签文件每行的格式为:
<object-class> <x> <y> <width> <height>
voc_label.py
即可。下载 voc_label.py 到 VOCdevkit 同级目录下,并执行 voc_label.py 文件:(其实darknet/scripts/目录下也有voc_lable.py)
wget https://pjreddie.com/media/files/voc_label.py
python voc_label.py
然后就生成了一些标签文件,在 labels 中:
下面的 txt 文件就是为 VOC 数据集生成的标签文件:
此时,在 voc 文件夹下你能看到以下文件:
ls
2007_test.txt VOCdevkit
2007_train.txt voc_label.py
2007_val.txt VOCtest_06-Nov-2007.tar
2012_train.txt VOCtrainval_06-Nov-2007.tar
2012_val.txt VOCtrainval_11-May-2012.tar
cat 2007_train.txt 2007_val.txt 2012_*.txt > train.txt
修改 cfg/voc.data 文件,将 < path-to-voc > 修改为存放 VOC 数据集的文件目录,比如:/data/monn/darknet/VOC
classes= 20
train = <path-to-voc>/train.txt
valid = <path-to-voc>/2007_test.txt
names = data/voc.names
backup = backup
修改之后:
classes= 20
train = /data/monn/darknet/VOC/train.txt
valid = /data/monn/darknet/VOC/2007_test.txt
names = data/voc.names
backup = backup
修改 cfg/yolov3-voc.cfg 配置文件:
[net]
# Testing
# batch=1
# subdivisions=1
# Training
batch=64 # 每轮迭代抽取 64 张图片
subdivisions=16 # 每轮迭代包含了 16 组
我们要使用在 ImageNet 上预训练的权重数据。进入 darknet 目录下,下载权重文件:
wget https://pjreddie.com/media/files/darknet53.conv.74
开始训练模型:
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
训练结束后,将得到的 weights 文件拷贝到 darknet/weights 文件夹下,运行命令以检测图片:
./darknet detector test <data_cfg> <test_cfg> <weights> <image_file>
具体地:
# 输出一张检测结果图,显示画出的检测框、类别和置信度
./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg weights/yolov3.weights data/dog.jpg
生成预测结果可以参考如下命令:
./darknet detector valid <data_cfg> <test_cfg> <weights> <out_file>
在开始训练后,提示错误:
这是因为配置文件 Makefile 中配置的 GPU 架构和本机 GPU 型号不一致导致的。
# ARCH= -gencode arch=compute_30,code=sm_30 \
# -gencode arch=compute_35,code=sm_35 \
# -gencode arch=compute_50,code=[sm_50,compute_50] \
# -gencode arch=compute_52,code=[sm_52,compute_52]
# -gencode arch=compute_20,code=[sm_20,sm_21] \ This one is deprecated?
# This is what I use, uncomment if you know your arch and want to specify
ARCH= -gencode arch=compute_61,code=sm_61
compute_30 表示显卡的计算能力是 3.0,几款主流 GPU 的 compute capability 列表:
所以 Tesla K80 对应 compute_30,Tesla K40c 对应compute_35,Titan X 对应compute_52,根据自己的GPU型号的计算能力进行配置,例如这里用的 GTX 1080 配置为:
ARCH= -gencode arch=compute_61,code=compute_61 \
重新编译即可。
darknet 文件夹下本来就有 backup,但是仍然会出错。这里把原本的 backup 文件夹删掉,新建一个一摸一样的,问题解决。(不知道是为了啥)
第一次训练已经跑了20000次,第二天想用 log 日志接着训练,结果就开始报错。
对照了好几遍路径、文件名,都是对的,最后还是翻墙出去看到了有一个人和我同样的问题,原来是 windows 和 linux 系统的文件编码不一样,导致无法打开。折腾了一天…
解决:将 voc.data
删掉,在终端用 vim 重新写一个 voc.data
,内容都不用变,保存退出后重新训练即可。
看一下 /data/monn/darknet/cfg/ 文件夹下的 yolov3-voc.cfg
配置文件:
[net]
# Testing
# batch=1
# subdivisions=1
# Training
batch=64 # 每轮迭代抽取 batch 张图片
subdivisions=16 # 将一个 batch 分成 subdivisions 个子 batch
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 # 学习率变动因子
Region 82 Avg IOU:
Region 94 Avg IOU:
Region 106 Avg IOU:
三个尺度上预测不同大小的框:82 卷积层为最大的预测尺度,使用较大的 mask,但是可以预测出较小的物体;94 卷积层为中间的预测尺度,使用中等的 mask;106 卷积层为最小的预测尺度,使用较小的 mask,可以预测出较大的物体。
快输出:
Region 82 Avg IOU: 0.798032, Class: 0.559781, Obj: 0.515851, No Obj: 0.006533, .5R: 1.000000, .75R: 1.000000, count: 2
批输出:
2706: 1.350835, 1.386559 avg, 0.001000 rate, 3.323842 seconds, 173184 images
仍然在 /data/monn/darknet/cfg/ 文件夹下的 yolov3-voc.cfg
配置文件:
[convolutional]
batch_normalize=1 # 是否做BN
filters=32 # 输出特征图的数量
size=3 # 卷积核的尺寸
stride=1 # 做卷积运算的步长
pad=1 # 如果pad为0,padding由 padding参数指定;如果pad为1,padding大小为size/2,padding应该是对输入图像左边缘拓展的像素数量
activation=leaky # 激活函数的类型
[yolo]
mask = 0,1,2 # 预测 anchor boxes
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=20 # 网络需要识别的物体类别数
num=9 # 每个 grid cell 预测 9 个 box
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1
当有多块 GPU 时,如果需要选择不同的 GPU 进行训练,参考以下命令:(0 或 1 表示 GPU 的序号)
./darknet detector -i 0 train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
如果要用多个 GPU 同时训练,参考以下命令(在最后加上 gpu 的编号即可):
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 -gpus 0,1,2,3
在测试时没有 bbox 和 predict 结果,有两种可能:
- 训练发散了
判断:训练时看一下 loss 和 avg loss,如果这两个值不断变大到几百就说明训练发散,可以通过降低学习率和提高batch数量解决这个问题。
- 训练不充分
判断:在 test 时设置 threshold,darknet 默认是 0.25,可以逐渐降低这个值看看效果。
./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_20000.weights data/dog.jpg -thresh 0.05
在训练 20000 次时,阈值为 0.25,只能检测到 car:
Loading weights from backup/yolov3-voc_20000.weights...Done!
data/dog.jpg: Predicted in 0.041438 seconds.
car: 83%
设置 threshold = 0.13 时,误检出 bird:
Loading weights from backup/yolov3-voc_20000.weights...Done!
data/dog.jpg: Predicted in 0.021404 seconds.
car: 83%
bird: 14%
可以将最后一次的模型(weights)作为预训练模型继续训练,比如在第 20000 次训练的基础上继续:
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_20000.weights
如果已经训练完成,想要在 final_weights 的基础上继续训练,可以修改 cfg 配置文件的 max_batches。
在 ./darknet/examples/
的 detector.c
下修改权重保存间隔,这里默认是迭代小于 1000 次时每 100 次保存一下模型,迭代大于 1000 次以后,每训练 10000 次才会保存一下。可以根据自己需要进行修改。
训练结束后要检测各种指标(比如 loss)是否达到了期望的数值。如果没有,要分析为什么,可视化这些参数可以帮我们分析问题。
可视化中间参数需要用到训练时保存的 log 文件:在原始训练命令上进行修改如下:(路径根据自己的实际路径进行修改)
先在 darknet 下建立 visualization 目录,后执行训练命令:
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 2>1 | tee visualization/train_yolov3.log
保存 log 时会生成文件:train_yolov3.log
extravt_log.py
log 日志文件中保存了训练过程中的所有输出信息(包括快输出和批输出),所以在绘制曲线之前,需要先使用 extravt_log.py
解析 loss 行和 iou 行 得到两个 txt 文件。
# coding=utf-8
# 该文件用来提取训练log,去除不可解析的log后使log文件格式化,生成新的log文件供可视化工具绘图
import inspect
import os
import random
import sys
def extract_log(log_file, new_log_file, key_word):
with open(log_file, 'r') as f:
with open(new_log_file, 'w') as train_log:
#f = open(log_file)
#train_log = open(new_log_file, 'w')
for line in f:
# 去除多gpu的同步log
if 'Syncing' in line:
continue
# 去除除零错误的log
if 'nan' in line:
continue
if key_word in line:
train_log.write(line)
f.close()
train_log.close()
extract_log('train_yolov3.log','train_log_loss.txt','images')
extract_log('train_yolov3.log','train_log_iou.txt','IOU')
#!/usr/bin/python
#coding=utf-8
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 根据log_loss.txt的行数修改lines, 修改训练时的迭代起始次数(start_ite)和结束次数(end_ite)。
lines = 27180
start_ite = 0 # train_log_loss.txt里面的最小迭代次数
end_ite = 27180 # train_log_loss.txt里面的最大迭代次数
step = 10 # 跳行数,决定画图的稠密程度
igore = 1000 # 当开始的loss较大时,忽略前igore次迭代,注意这里是迭代次数
y_ticks = [0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3] # 纵坐标的值
data_path = 'train_log_loss.txt' # log_loss的路径。
result_path = 'avg_loss' # 保存结果的路径。
names = ['loss', 'avg', 'rate', 'seconds', 'images']
result = pd.read_csv(data_path, skiprows=[x for x in range(lines) if (x<lines*1.0/((end_ite - start_ite)*1.0)*igore or x%step!=9)], error_bad_lines=\
False, names=names)
result.head()
for name in names:
result[name] = result[name].str.split(' ').str.get(1)
result.head()
result.tail()
for name in names:
result[name] = pd.to_numeric(result[name])
result.dtypes
print(result['avg'].values)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
x_num = len(result['avg'].values)
tmp = (end_ite-start_ite - igore)/(x_num*1.0)
x = []
for i in range(x_num):
x.append(i*tmp + start_ite + igore)
#print(x)
print('total = %d\n' %x_num)
print('start = %d, end = %d\n' %(x[0], x[-1]))
ax.plot(x, result['avg'].values, label='avg_loss')
#ax.plot(result['loss'].values, label='loss')
plt.yticks(y_ticks)#如果不想自己设置纵坐标,可以注释掉。
plt.grid()
ax.legend(loc = 'best')
ax.set_title('The loss curves')
ax.set_xlabel('batches')
fig.savefig(result_path)
#fig.savefig('loss')
#!/usr/bin/python
#coding=utf-8
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#根据log_iou修改行数
lines = 952812
step = 5000
start_ite = 0
end_ite = 50200
igore = 1000
data_path = 'train_log_iou.txt' #train_log_iou的路径。
result_path = 'avg_iou' #保存结果的路径。
names = ['Region Avg IOU', 'Class', 'Obj', 'No Obj', '.5_Recall', '.7_Recall', 'count']
#result = pd.read_csv('log_iou.txt', skiprows=[x for x in range(lines) if (x%10==0 or x%10==9)]\
result = pd.read_csv(data_path, skiprows=[x for x in range(lines) if (x<lines*1.0/((end_ite - start_ite)*1.0)*igore or x%step!=0)]\
, error_bad_lines=False, names=names)
result.head()
for name in names:
result[name] = result[name].str.split(': ').str.get(1)
result.head()
result.tail()
for name in names:
result[name] = pd.to_numeric(result[name])
result.dtypes
x_num = len(result['Region Avg IOU'].values)
tmp = (end_ite-start_ite - igore)/(x_num*1.0)
x = []
for i in range(x_num):
x.append(i*tmp + start_ite + igore)
#print(x)
print('total = %d\n' %x_num)
print('start = %d, end = %d\n' %(x[0], x[-1]))
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(x, result['Region Avg IOU'].values, label='Region Avg IOU')
#ax.plot(result['Avg Recall'].values, label='Avg Recall')
plt.grid()
ax.legend(loc='best')
ax.set_title('The Region Avg IOU curves')
ax.set_xlabel('batches')
fig.savefig(result_path)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。