git clone https://github.com/pjreddie/darknet
cd darknet
因为在用此工程的过程中,对源码进行了修改,可以直接从下边路径copy一份,进行编译即可。
略,哈哈哈哈,理解万岁。
GPU=1 #如果使用GPU设置为1,CPU设置为0
CUDNN=1 #如果使用CUDNN设置为1,否则为0
OPENCV=0 #如果调用摄像头或者测试视频文件,还需要设置OPENCV为1,否则为0
OPENMP=0 #如果使用OPENMP设置为1,否则为0
DEBUG=0 #如果使用DEBUG设置为1,否则为0
…
CC=gcc
NVCC=/usr/local/cuda-8.0/bin/nvcc #NVCC=nvcc 修改为自己的路径
AR=ar
...
ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda-8.0/include/ #修改为自己的路径
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda-8.0/lib64 -lcuda -lcudart -lcublas -lcurand #修改为自己的路径
保存完成后,在此路径下执行make,如果出现如下错误:
错误一:
Loadingweights from yolo.weights...Done!
CUDA Error:invalid device function
darknet: ./src/cuda.c:21: check_error: Assertion `0' failed.
Aborted (core dumped)
这是因为配置文件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_52,code=compute_52
CUDA官方说明文档:
http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#virtual-architecture-feature-list
错误二:
如果出现/usr/bin/ld: 找不到 -lippicv;找不到nvcc
请参考:
https://blog.csdn.net/tmosk/article/details/76578082
然后重新编译即可
(1)准备的训练数据集文件夹的结构:
按下列文件夹结构,将训练数据集放到各个文件夹下面。
VOCdevkit #数据集名称(一般为项目名称)
—VOC2007 #数据集子类名称(可以将搜集的不同的数据集以此种形式放置)
——Annotations #xml文件
——JPEGImages #图片文件
(2)生成yolo训练和测试所需要的数据集。
生成训练集和评估集的脚本在如下路径:
略,哈哈哈哈,理解万岁。
CreatTrainNameList.py脚本为生成训练数据集的脚本,CreatValidNameList.py脚本为生成评估集数据集的脚本,对脚本进行如下修改(以训练集为例):
if __name__ == "__main__":
#========================================================================
sets=[( VOC2007, 'train'),( VOC2012,'train')] #(VOC2007,VOC2012两个数据集生成train数据集)
classes = ["Person_HS"] #检测的类别
parse = argparse.ArgumentParser()
parse.add_argument("--input_path", type=str,default=' VOCdevkit', help="...") #项目名称
# =======================================================================
生成后的数据集的文件结果如下:
VOCdevkit
—VOC2007
——Annotations
——ImageSets
———Main
————train.txt /val.txt #训练集和评估集在一起时,两个文档共存(一般选择分开)。
——JPEGImages
VOCdevkit_train.txt / VOCdevkit_val.txt #yolov3训练和评估时的索引文档。
如果你选用基础网络为darknet53的yolov3时可以采用如下的预训练模型
wget https://pjreddie.com/media/files/darknet53.conv.74
classes= 2 #classes为训练样本集的类别总数(不包含背景)
train = /home/user/darknet/ VOCdevkit _train.txt #train的路径为训练样本集所在的路径
valid = /home/user/darknet/ VOCdevkit _val.txt #valid的路径为验证样本集所在的路径
names = data/voc.names #names的路径为data/voc.names文件所在的路径
backup = backup #模型的保存路径(文件夹必须保证在darknet文件夹下面已存在)
注:
如果觉得每次都得创建模型保存路径的文件夹费事,请按照下图对源码(example\detector.c)进行修改:
#include "darknet.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
...
void train_detector(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear )
{
list *options = read_data_cfg(datacfg);
char *train_images = option_find_str(options, "train", "data/train.list");
char *backup_directory = option_find_str(options, "backup", "/backup/");
if(-1==access(backup_directory,00)){
mkdir(backup_directory,S_IRWXU);}
...
}
Class1
Class2
关于cfg修改,以10类目标检测为例,主要有以下几处调整(蓝色标出):
[net]
# Testing ### 测试模式
# batch=1
# subdivisions=1
# Training ### 训练模式,每次前向的图片数目 = batch/subdivisions
batch=64
subdivisions=16
width=416 ### 网络的输入宽、高、通道数(可以直接修改,但是要修改网络避免深层网络的浪费)
height=416 ### 宽高比的比例可依据输入图像的尺寸进行设置(但需要为32的倍数)
channels=3
momentum=0.9 ### 动量: DeepLearning1中最优化方法中的动量参数,这个值影响着梯度下降到最优值得速度。 详情参考:https://blog.csdn.net/qq_33270279/article/details/102796812
decay=0.0005 ### 权重衰减:权重衰减正则项,防止过拟合.每一次学习的过程中,将学习后的参数按照固定比例进行降低,为了防止过拟合,decay参数越大对过拟合的抑制能力越强。
angle=0 ###旋转角度(单位:度)
saturation = 1.5 ### 饱和度
exposure = 1.5 ### 曝光度
hue=.1 ### 色调
learning_rate=0.001 ### 学习率的调整参考 :https://blog.csdn.net/qq_33485434/article/details/80452941
burn_in=1000 ### 模型预热,小于1000batch时采用(0-learning_rate)递增的方式。
max_batches = 50200 ### 迭代次数
policy=steps ### 学习率策略: 这个是学习率调整的策略,有policy: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](https://nanfei.ink/2018/01/23/YOLOv2调参总结/#more)
steps=40000,45000 ### 学习率变动步长
scales=.1,.1 ### 学习率变动因子
[convolutional]
batch_normalize=1 ### BN
filters=32 ### 卷积核数目
size=3 ### 卷积核尺寸
stride=1 ### 卷积核步长
pad=1 ### pad
activation=leaky ###激活函数的类型:linear(线性:不做改变)relu(值>0时保持不变,小于零时置零)leaky(值>0时保持不变,小于零时*0.1)
......
[convolutional]
size=1
stride=1
pad=1
filters=45 #每一个[region/yolo]层前的最后一个卷积层中的 filters=(classes+cords+1)*anchors_num,其中anchors_num 是该层mask的数量。如果没有maskanchors_num=num。coords+1:论文中的tx,ty,tw,th,to
activation=linear
[yolo]
mask = 6,7,8 #预测anchors[]中的后三个较大尺寸的anchor。因为其感受野较大
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
#(width, height)通过聚类脚本计算样本宽高
classes=2 #类别
num=9 #如果在配置文件中anchors的数量大于num时,仅使用前num个,小于时内存越界。
jitter=.3 #利用数据抖动产生更多数据, jitter就是crop的参数, jitter=.3,就是在0~0.3中进行crop(具体的裁剪方式请参考src/data.c中的load_data_detection()函数)
ignore_thresh = .5 #参数解释:ignore_thresh 指得是参与计算的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。 实验结果:AP=0.5121(有明显下降)
truth_thresh = 1
random=0 #1,如果显存很小,将random设置为0,关闭多尺度训练;
......
[yolo]
mask = 3,4,5 #预测anchors[]中的中间三个较小尺寸的anchor。因为其感受野适中
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=2
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=0
......
[yolo]
mask = 0,1,2 #预测anchors[]中的前三个较小尺寸的anchor。因为其感受野较小
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=2
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=0
#可以添加没有标注框的图片和其空的txt文件,作为negative数据(生成训练数据集的脚本已经支持,只需要添加图片即可)
#可以在第一个[yolo]层之前的倒数第二个[convolutional]层末尾添加 stopbackward=1,以此提升训练速度
#即使在用416*416训练完之后,也可以在cfg文件中设置较大的width和height,增加网络对图像的分辨率,从而更可能检测出图像中的小目标,而不需要重新训练。
原始的yolov3的多尺度训练如下:
random如果为1,每次迭代图片大小随机从320到608,步长为32,如果为0,每次训练大小与输入大小一致。
当修改训练的分辨率时(支持宽高比不为1:1),需要修改尺度变化的范围,所以需要修改训练函数(example/detector.c)train_detector()和run_detector()如下所示:
void train_detector(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear )
{
...
int iDataWidth = net->w;
int iDataHeight = net->h;
int imgs = net->batch * net->subdivisions * ngpus;
printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay);
data train, buffer;
layer l = net->layers[net->n - 1];
int classes = l.classes;
float jitter = l.jitter;
list *plist = get_paths(train_images);
//int N = plist->size;
char **paths = (char **)list_to_array(plist);
load_args args = get_base_args(net);
args.coords = l.coords;
args.paths = paths;
args.n = imgs;
args.m = plist->size;
args.classes = classes;
args.jitter = jitter;
args.num_boxes = l.max_boxes;
args.d = &buffer;
args.type = DETECTION_DATA;
//args.type = INSTANCE_DATA;
args.threads = 64;
pthread_t load_thread = load_data(args);
double time;
int count = 0;
//while(i*imgs < N*120){
while(get_current_batch(net) < net->max_batches){
if(l.random && count++%10 == 0){
printf("Resizing\n");
int iRand = rand() % 10;
int dim_W = (iRand + iDataWidth / 32 - 2) * 32;
float fRatio = dim_W * 1.0 / iDataWidth;
//int dim_H = (iRand + iDataHeight / 32 - 2) * 32;
int dim_H = (int)(iDataHeight * fRatio);
if (get_current_batch(net)+200 > net->max_batches){
dim_W = (iDataWidth / 32 + 5) * 32;
fRatio = dim_W * 1.0 / iDataWidth;
//dim_H = (iDataHeight / 32 + 5) * 32;
dim_H = (int)(iDataHeight * fRatio);
}
int delt_H=dim_H % 32;
if(delt_H>16) dim_H=dim_H+32-delt_H;
else dim_H=dim_H-delt_H;
printf("dim_W:%d\n", dim_W);
printf("dim_H:%d\n", dim_H);
args.w = dim_W;
args.h = dim_H;
pthread_join(load_thread, 0);
train = buffer;
free_data(train);
load_thread = load_data(args);
#pragma omp parallel for
for(i = 0; i < ngpus; ++i){
resize_network(nets[i], dim_W, dim_H);
//printf("resize_network Done!");
}
net = nets[0];
}
...
}
So for one GPU, the relevant portion of the .cfg
file would be:
learning_rate=0.001
burn_in=1000
And for two GPUs, the relevant portion of the .cfg
file would be:
learning_rate=0.0005
burn_in=2000
And for four GPUs, the relevant portion of the .cfg
file would be:
learning_rate=0.00025
burn_in=4000
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 -gpus 0,1 2>1 | tee train_yolov3.log
voc.data:数据集以及输出模型的保存路径
yolov3-voc.cfg:网络参数和网络结构文档
darknet53.conv.74:预训练模型(当训练中断后,也可加载“模型名.backup”继续训练)
2>1 | tee train_yolov3.log:保存的日志名称。
https://blog.csdn.net/zsl091125/article/details/85010833 (参考)
(1)测试图片
测试单张图片:
./darknet detector test
测试时还可以用-thresh和-hier选项指定对应参数。
(2)生成预测结果
./darknet detector valid
结果生成在
对源码进行了修改可以用-thresh:
detector 中的valid函数不支持-thresh置信度阈值的设置,修改example/detector.c如下所示,并重新编译。
void validate_detector(char *datacfg, char *cfgfile, char *weightfile, char *outfile, float _thresh)
{
……
//float thresh = .005;
float thresh = _thresh;
float nms = .45;
………
}
void run_detector(int argc, char **argv)
{
………
else if(0==strcmp(argv[2], "valid")) validate_detector(datacfg, cfg, weights,outfile, thresh);
……………
}
(3)计算recall(执行这个命令需要修改detector.c文件,修改信息请参考“detector.c修改”)
./darknet detector recall
输出在stderr里,重定向时请注意。
RPs/Img、IOU、Recall都是到当前测试图片的均值。
detector.c中对目录处理有错误,可以参照validate_detector对validate_detector_recall最开始几行的处理进行修改。
(还未实现,实现过程可参考:https://blog.csdn.net/mieleizhi0522/article/details/79989754 )
(4)测试视频
相应的参数请查看官网或者darknet/example/detector.c源码
./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights
(5)python接口测试(主要应用在评估工具的编写)
darknet/example/detector.py:无opencv编译时,运行。(修改相应的路径即可运行)
darknet/example/detector-scipy-opencv.py:有OpenCV编译,运行此脚本。(原脚本存在问题,需要修改,修改方法如下),包括两点:(上传的工程中,此部分已经做了修改)
a、/darknet/python/darknet.py 中,增加如下函数:
def detect_np(net, meta, im, thresh=.5, hier_thresh=.5, nms=.45):
num = c_int(0)
pnum = pointer(num)
predict_image(net, im)
dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, None, 0, pnum)
num = pnum[0]
if (nms): do_nms_obj(dets, num, meta.classes, nms)
res = []
for j in range(num):
for i in range(meta.classes):
if dets[j].prob[i] > 0:
b = dets[j].bbox
res.append((meta.names[i], dets[j].prob[i], (b.x, b.y, b.w, b.h)))
res = sorted(res, key=lambda x: -x[1])
#free_image(im)
free_detections(dets, num)
print 'detect_np over'
return res
b、detector-scipy-opencv.py中,‘#OpenCV’下边的代码全部删掉,改为如下代码
# OpenCV
arr = cv2.imread('../data/dog.jpg')
im = array_to_image(arr)
dn.rgbgr_image(im)
r = dn.detect_np(net, meta, im)
print r
(1)待标注样本数据集准备与训练数据集的准备基本相同,区别:没有标注文件夹。如下所示:
按下列文件夹结构,将预标注数据集放到各个文件夹下面。
VOCdevkit #数据集名称(一般为项目名称)
—VOC2007 #数据集子类名称(可以将搜集的不同的数据集以此种形式放置)
——JPEGImages #图片文件
(2)生成yolo测试(预标注跟测试评估一致)所需要的数据集。
CreatValidNameList.py脚本为生成评估集数据集的脚本:
#-*- coding: utf-8 -*-
#######################################
# 本程序用于yolov3生成测试评估的数据集#
#######################################
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
import sys,argparse
def checkpath(path):
if not os.path.exists(path):
print("错误的路径: {}".format(path))
sys.exit()
def makepath(path):
if not os.path.exists(path):
os.makedirs(path)
if __name__ == "__main__":
# ==================================================================================================================
sets=[('VOC100', 'val')]
parse = argparse.ArgumentParser()
parse.add_argument("--input_path", type=str,default='Person', help="...")
# ==================================================================================================================
flags, unparsed = parse.parse_known_args(sys.argv[1:])
input_path = flags.input_path
checkpath(input_path)
for year, image_set in sets:
output_path = input_path+'/%s/ImageSets/Main'%(year)
makepath(output_path)
path = os.path.join(input_path, '%s'%(year), 'JPEGImages')
imgs = os.listdir(path)
save_path = os.path.join(output_path, 'val.txt')
fw = open(save_path, 'w')
for img in imgs:
print(img)
img_name = img.split('.')[0]
fw.write(img_name + '\n')
fw.close()
wd = getcwd()
list_file = open(input_path+'_val.txt', 'a')
for year, image_set in sets:
image_ids = open(input_path+'/%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split('\n')
for image_id in image_ids:
list_file.write('%s/'%(wd)+input_path+'/%s/JPEGImages/%s.jpg\n'%(year, image_id))
list_file.close()
生成后的数据集的文件结果如下:
VOCdevkit
—VOC2007
——ImageSets
———Main
————val.txt
——JPEGImages
VOCdevkit_val.txt #yolov3评估时的索引文档。
(3)生成comp4_det_test_命名.txt(在result中)
./darknet detector valid cfg/voc.data cfg/yolo-voc.cfg backup/yolo-voc.weights –thresh 0.05
voc.data文件里修改如下:
classes= 2 #classes为训练样本集的类别总数
valid = /home/user/darknet/ VOCdevkit _val.txt #valid的路径为评估样本集所在的路径
names = data/voc.names #names的路径为data/voc.names文件所在的路径
(4)用Yolov3ResultToxml.py脚本生成所需的xml文件。
Yolov3ResultToxml.py路径如下:
略,哈哈哈哈,理解万岁。
#-*-coding:utf-8-*-
#######################################
# 本程序用于yolo评估模型:将生成的目标框信息(txt)转换为xml文件#
#######################################
...
if __name__ == "__main__":
#=======================================================================
parse = argparse.ArgumentParser()
parse.add_argument("--txt_path", type=str, default='xxx.txt',
help="yoloV3生成的txt")
parse.add_argument("--img_path", type=str, default='.../JPEGImages/', help="图像路径")
parse.add_argument("--output_path", type=str, default='xxx2.txt',
help="保存的新txt")
parse.add_argument("--save_path", type=str, default='xxx',
help="生成xml的保存路径")
#==========================================================================
#xml文件配置
config = {
'roi_size': [1920, 1080, 3], # 图片大小
'roi_num': 0, # 生成xml数 应等于图片数
'obj_name': ['Car','Person_HS'], # 此脚本未用
'count': 0, # 此脚本未用
}
#===========================================================================
...
(5)生成的xml与测试集gt_xml通过评估工具进行评估(AutoAssessTool工具)。
略,哈哈哈哈,理解万岁。
略,哈哈哈哈,理解万岁。
1.准备评估集(与训练集有相同的样本分布,最少为训练集的一半)
VOCdevkit #数据集名称(一般为项目名称)
—VOC2007 #数据集子类名称(可以将搜集的不同的数据集以此种形式放置)
——Annotations #xml文件
——JPEGImages #图片文件
2.评估集检测
略,哈哈哈哈,理解万岁。
运行环境:python3+cv2,其他环境由yolov3以及ssd编译环境而定。
##################################################
#目标检测,并生成xml,目前支持yolov3和ssd
#Author : 小楞
#Created : 2019-09-06
#备注:用于样本的与标注以及测试集的评估(与AutoAssessTool工具配合使用)##################################################
...
if __name__ == "__main__":
# ==============修改输入参数=====================
parse = argparse.ArgumentParser()
parse.add_argument("--input_path", type=str, default='.../value_data',
help="评估数据集路径,下边包含一个或多个样本集")
parse.add_argument("--model_path", type=str, default='.../models',
help="保存模型的文件夹,其中每个模型又是一个文件夹,一个模型文件包括:.weights .cfg .names .data")
parse.add_argument("--output_path", type=str, default='.../detetct_xml_result',
help="检测结果保存在xml文件中")
parse.add_argument("--thresh", type=float,default=0.05, help="置信度阈值")
# ==========修改输入参数end=======================
3.模型自动评估
略,哈哈哈哈,理解万岁。
##################################################
# File Name: autotest.py
# Author: 小楞
# Created: 2019.8.30
# Description: 自动模型评测系统
##################################################
...
if __name__ == '__main__':
tester = Tester(
output_path='..\\Annotations',#检测出的xml
gt_path='...\\Annotations',#标注的xml
log_path='example.txt', save_log=True, pr_steps=0.01,#配置参数
readin=True, readin_paths=['compare.txt'])#带比较的评估结果
tester.test()
4.评估结果详细分析:
会分别保存误检和漏检结果,具体问题具体分析。
略,哈哈哈哈,理解万岁。
##################################################
#根据生成的评测样本的xml结果与gt_xml比较,得到误检和漏检。
#Author : 小楞
#Created : 2019-09-06
#备注:支持多模型同时评测,接受dete_xml_generate工具生成的结果。##################################################
...
# =================修改输入参数=============================================
parse = argparse.ArgumentParser()
parse.add_argument("--gt_path", type=str, default='...\\TestSets',help="评估数据集gt路径,下边包含一个或多个Set,每个set下包括JPEG和XML文件夹")
parse.add_argument("--det_path", type=str, default='...\\detetct_xml_generate_result',help="评估数据集det路径,下边包含一个或多个Set,每个set下包括JPEG和XML文件夹")
parse.add_argument("--model_path", type=str, default='...\\models',help="模型路径,第二层文件夹,运行一次只能评估一个模型")
parse.add_argument("--output_path", type=str,default='...\\positive_samples_evaluation_false',help="错误检测结果保存路径")
parse.add_argument("--conf_thresh", type=float, default=0.05, help="置信度阈值")
parse.add_argument("--pos_overlap", type=float, default=0.3, help="IOU阈值")
# ===============修改输入参数end============================================
5.板卡芯片结果评估:
略,哈哈哈哈,理解万岁。
##################################################
#板卡芯片检测结果txt准换成xml用于评估
#Author : 小楞
#Created : 2019-09-07
##################################################
...
# ==========修改输入参数=============================================
parse = argparse.ArgumentParser()
parse.add_argument("--Ori_TxtPath", type=str, default='...\\resulttest', help="原始txt文件路径")
parse.add_argument("--Det_AnnoPath", type=str,default='...\\resulttest_xml',help="xml文件路径")
# =========修改输入参数end============================================
用autotest.py进行评估。
1.纯负样本的获取:
网络爬图工具:略,哈哈哈哈,理解万岁。
Crawl_Pictures.py
names.txt
在names.txt中写入需要下载图片的关键词即可,需要设置需要下载图片的数量如下:
C:\soft\Anaconda3\python.exe .../Crawl_Pictures.py
请输入每类图片的下载数量 500
正在检测图片总数,请稍等.....
经过检测山路类图片共有1020张
找到关键词:山路的图片,即将开始下载图片...
正在下载第1张图片,图片地址:http://www.fotosay.com/userimages/blogimages/2010/1026/zndnn/10521382527b.jpg
2.负样本评估:
负样本检测脚本:略,哈哈哈哈,理解万岁。
##################################################
#纯负样本评估,输出各个模型不同类别的误检情况。
#Author : 小楞
#Created : 2019-09-06
#备注:支持yolov3和ssd,支持多模型同时对比评测。##################################################
...
# =============修改输入参数=============================================
parse = argparse.ArgumentParser()
parse.add_argument("--img_path", type=str, default='.../Bg_img',help="负样本图片路径")
parse.add_argument("--model_path", type=str, default='.../models',help="保存模型的文件夹,其中每个模型又是一个文件夹")
parse.add_argument("--conf_thresh", type=float, default=0.2, help="置信度阈值")
parse.add_argument("--is_save_false", type=int, default=1, help="1表示保存檢測錯誤的結果")
parse.add_argument("--output_path", type=str, default=".../neg_img_result",help="结果保存路径")
parse.add_argument("--isresize", type=int, default=0, help="是否将原图片填补为宽高比1:1的图片")
# ===========修改输入参数end============================================
负样本结果分析:略,哈哈哈哈,理解万岁。
##################################################
#分析负样本评估打印日志,输出不同模型误检情况的柱状图
#Author : 小楞
#Created : 2019-09-06
#关键词:二维字典,二维并列柱状图
##################################################
...
if __name__ == "__main__":
# ================修改输入参数=============================================
configs = {
'Neg_ResultPath': '...\\neg_result.txt',
'label_list': ['class1', 'class2'],
}
# ==============修改输入参数end============================================
Loss可视化:
IOU可视化:
参考:
https://blog.csdn.net/cgt19910923/article/details/80783614
修改好的脚本文件如下路径:
略,哈哈哈哈,理解万岁。
因为.cfg文件中定义batch=64,每个batch分为8组,所以打印8组信息。每组包含三个yolo层的训练信息。
Region xx: cfg文件中yolo-layer的索引;
**Avg IOU:**当前迭代中,预测的box与标注的box的平均交并比,越大越好,期望数值为1;
Class: 标注物体的分类准确率,越大越好,期望数值为1;
obj: 越大越好,期望数值为1;
No obj: 越小越好;
.5R: 以IOU=0.5为阈值时候的recall; recall = 检出的正样本/实际的正样本
0.75R: 以IOU=0.75为阈值时候的recall;
**count:**正样本数目。
7634: 指示当前训练的迭代次数;
1.077007: 是总体的 Loss(损失);
0.980247 avg: 是平均 Loss, 这个数值应该越低越好, 一般来说, 一旦这个数值低于 0.060730 avg 就可以终止训练了;
0.001000 rate: 代表当前的学习率, 是在.cfg文件中定义的;
7.004570 seconds: 表示当前批次训练花费的总时间;
488576 images: 这一行最后的这个数值是 979864ngpus 的大小, 表示到目前为止, 参与训练的
如果出现上图-nan的情况,不要慌:只是说明本组图像中在此尺度下的特征图中检测不到正样本而已。
目的:
解决训练过程中cpu吃紧的问题。
解决办法:
detector-nolog.c替换darknet\examples\detector.c
yolo_layer-nolog.c替换darknet\src\yolo_layer.c
(文件在:略,哈哈哈哈,理解万岁。)
重新make即可。
A:保存模型出错?
一般是 .data 文件中指定的文件夹无法创建,导致模型保存时出错。自己手动创建即可。
B:模型什么时候保存?如何更改
迭代次数小于1000时,每100次保存一次,大于1000时,每10000次保存一次。
自己可以根据需求进行更改,然后重新编译即可[ 先 make clean ,然后再 make]。
代码位置: examples/detector.c line 138
C:使用预训练模型直接保存问题
darknet53.conv.74作为预训练权重文件,因为只包含卷积层,所以可以从头开始训练。
xxx.weights作为预权重文件训练,因为包含所有层,相当于恢复快照训练,会从已经保存的迭代次数往下训练。如果cfg中迭代次数没改,所以不会继续训练,直接保存结束。
问题:
原因:
配置文件的编码问题,将文件格式改为unix格式。(在linux下,拷贝的文件来自windows)
解决:
在vim下 输入 :set ff=unix
保存退出。
进行编译,训练时会出现如下报错:
原因:未知(代码中有除0操作,出现段错误,但是其他服务器未出现此错误)
暂时解决方案:
拷贝编译好的框架直接使用即可:scp -r host@…: …/darknet ./
其他问题请参考如下链接:
https://blog.csdn.net/lilai619/article/details/79695109
DBL: 如图1左下角所示,也就是代码中的Darknetconv2d_BN_Leaky,是yolo_v3的基本组件。就是卷积+BN+Leaky relu。对于v3来说,BN和leaky relu已经是和卷积层不可分离的部分了(最后一层卷积除外),共同构成了最小组件。
resn**:**n代表数字,有res1,res2, … ,res8等等,表示这个res_block里含有多少个res_unit。这是yolo_v3的大组件,yolo_v3开始借鉴了ResNet的残差结构,使用这种结构可以让网络结构更深(从v2的darknet-19上升到v3的darknet-53,前者没有残差结构)。对于res_block的解释,可以在图1的右下角直观看到,其基本组件也是DBL。
concat**:**张量拼接。将darknet中间层和后面的某一层的上采样进行拼接。拼接的操作和残差层add的操作是不一样的,拼接会扩充张量的维度,而add只是直接相加不会导致张量维度的改变。
整个v3结构里面,是没有池化层和全连接层的。前向传播过程中,张量的尺寸变换是通过改变卷积核的步长来实现的,比如stride=(2, 2),这就等于将图像边长缩小了一半(即面积缩小到原来的1/4)。在yolo_v2中,要经历5次缩小,即1/32。输入为416x416,则输出为13x13(416/32=13)。yolo_v3也和v2一样,backbone都会将输出特征图缩小到输入的1/32。所以,通常都要求输入图片是32的倍数。
\1. 主干网络裁剪
(1) 减少res基础结构个数。
(2) 减少通道数。(2的指数倍)
(3) 减少降维的倍率。
(4) 改变降维方式:卷积降维改为pooling(在裁剪的最终网络中,每修改一处,效率提升10个点左右,但是效果有所下降。仅供参考)
\2. 降低分辨率
必须为降维倍率的整数倍,最好为基数倍(这样回归的框会比较准)。
\3. Yolo层以及anchor的裁剪
在尺度变换不是很大的应用场景,可以减少anchor的数量,以及yolo的层数。
yolo v3输出了3个不同尺度的feature map,如上图所示的y1, y2, y3。这也是v3论文中提到的为数不多的改进点:predictions across scales
这个借鉴了FPN(feature pyramid networks),采用多尺度来对不同size的目标进行检测,越精细的grid cell就可以检测出越精细的物体。
y1,y2和y3的深度都是255,边长的规律是13:26:52
对于COCO类别而言,有80个种类,所以每个box应该对每个种类都输出一个概率。yolo v3设定的是每个网格单元预测3个box,所以每个box需要有(x, y, w, h, confidence)五个基本参数,然后还要有80个类别的概率。所以3*(5 + 80) = 255。这个255就是这么来的。
优化关注点:
第一点, 9个anchor会被三个输出张量平分的。根据大中小三种size各自取自己的anchor。
第二点,作者使用了logistic回归来对每个anchor包围的内容进行了一个目标性评分(objectness score)。根据目标性评分来选择anchor prior进行predict,而不是所有anchor prior都会有输出。
layer filters size input output
0 conv 16 3 x 3 / 1 224 x 224 x 3 -> 224 x 224 x 16 0.043 BFLOPs
1 max 2 x 2 / 2 224 x 224 x 16 -> 112 x 112 x 16
2 conv 32 3 x 3 / 1 112 x 112 x 16 -> 112 x 112 x 32 0.116 BFLOPs
3 max 2 x 2 / 2 112 x 112 x 32 -> 56 x 56 x 32
4 conv 64 3 x 3 / 1 56 x 56 x 32 -> 56 x 56 x 64 0.116 BFLOPs
5 conv 128 3 x 3 / 2 56 x 56 x 64 -> 28 x 28 x 128 0.116 BFLOPs
6 conv 64 1 x 1 / 1 28 x 28 x 128 -> 28 x 28 x 64 0.013 BFLOPs
7 conv 128 3 x 3 / 1 28 x 28 x 64 -> 28 x 28 x 128 0.116 BFLOPs
8 res 5 28 x 28 x 128 -> 28 x 28 x 128
9 conv 64 1 x 1 / 1 28 x 28 x 128 -> 28 x 28 x 64 0.013 BFLOPs
10 conv 128 3 x 3 / 1 28 x 28 x 64 -> 28 x 28 x 128 0.116 BFLOPs
11 res 8 28 x 28 x 128 -> 28 x 28 x 128
12 conv 256 3 x 3 / 2 28 x 28 x 128 -> 14 x 14 x 256 0.116 BFLOPs
13 conv 128 1 x 1 / 1 14 x 14 x 256 -> 14 x 14 x 128 0.013 BFLOPs
14 conv 256 3 x 3 / 1 14 x 14 x 128 -> 14 x 14 x 256 0.116 BFLOPs
15 res 12 14 x 14 x 256 -> 14 x 14 x 256
16 conv 128 1 x 1 / 1 14 x 14 x 256 -> 14 x 14 x 128 0.013 BFLOPs
17 conv 256 3 x 3 / 1 14 x 14 x 128 -> 14 x 14 x 256 0.116 BFLOPs
18 res 15 14 x 14 x 256 -> 14 x 14 x 256
19 conv 128 1 x 1 / 1 14 x 14 x 256 -> 14 x 14 x 128 0.013 BFLOPs
20 conv 256 3 x 3 / 1 14 x 14 x 128 -> 14 x 14 x 256 0.116 BFLOPs
21 conv 18 1 x 1 / 1 14 x 14 x 256 -> 14 x 14 x 18 0.002 BFLOPs
22 yolo
23 route 19
24 conv 128 1 x 1 / 1 14 x 14 x 128 -> 14 x 14 x 128 0.006 BFLOPs
25 upsample 2x 14 x 14 x 128 -> 28 x 28 x 128
26 route 25 11
27 conv 64 1 x 1 / 1 28 x 28 x 256 -> 28 x 28 x 64 0.026 BFLOPs
28 conv 128 3 x 3 / 1 28 x 28 x 64 -> 28 x 28 x 128 0.116 BFLOPs
29 conv 18 1 x 1 / 1 28 x 28 x 128 -> 28 x 28 18 0.004 BFLOPs
30 yolo
支持基于yolov3原始网络的网络设计和裁剪,主要功能有参数设定,通道裁剪,以及结构的裁剪设计。
工具路径:
略,哈哈哈哈,理解万岁。
Yolov3_Net_Design.py配置文件信息如下:
##################################################
#自动裁剪yolov3网络
#Author : 小楞
#Created : 2019-08-19
#注:目前支持yolov3网络的通道裁剪,参数设定, 结构裁剪。
#问题: 配置文件的编码问题,将文件格式改为unix格式。(在linux下,拷贝的文件来自windows)
#解决: 在vim下 输入 :set ff=unix
#参考:网络裁剪算力与裁剪方式减少比值之间的关系
# 1.算力减少比例/像素点个数减少比例:1
# 2.算力减少比例/通道数裁剪比例:1.7
# 3.算力减少比例/res模块减少数:0.02-0.03
##################################################
...
# 设置配置参数configs = {
'classNum':22,#检测的类别数
'yoloNum':3,#yolo层的个数,暂不支持修改
'width':608,
'height':352,#输入分辨率
'angle':5,#样本旋转
'random':1,#是否开启多尺度训练
'ignore_thresh':.5,#参与loss计算的Iou阈值,参考0.5-0.7,标注质量(框的位置)较差时选用0.5较好。
'filt_ReduceRatio': 0.75,#通道缩减比例
'base_Module':{'res1':0,'res2':0,'res8_1':0,'res8_2':0,'res4':0},#建议裁剪res8_1和res8_2;参考值:'res1':1,'res2':2,'res8_1':8,'res8_2':8,'res4':4
'anchors': 'anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326',
'ori_CfgDir': "...\yolov3_ori.cfg",
'det_CfgDir': "...\\yolov3_manual.cfg",}
按照如上结构进行网络参数配置,即可自动生成所需网络。
Yolov3_Compute_Ops.py配置文件:
##################################################
#计算yolov3算力
#Author : 小楞
#Created : 2019-09-03
#注:需要darknet框架输出的单层算力值
#结论:网络裁剪算力与裁剪方式减少比值之间的关系
# 1.算力减少比例/像素点个数减少比例:1
# 2.算力减少比例/通道数裁剪比例:1.7
# 3.算力减少比例/res模块减少数:0.020.03
##################################################
...
# 设置配置参数
configs = {'ori_CfgDir': "...\\darknet_yolov3net.txt",}
darknet_yolov3net.txt为darknet训练时,打印的网络结构:
yolov3
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
4 res 1 304 x 304 x 64 -> 304 x 304 x 64
...
82 yolo
83 route 79
84 conv 256 1 x 1 / 1 19 x 19 x 512 -> 19 x 19 x 256 0.095 BFLOPs
85 upsample 2x 19 x 19 x 256 -> 38 x 38 x 256
86 route 85 61
...
94 yolo
95 route 91
96 conv 128 1 x 1 / 1 38 x 38 x 256 -> 38 x 38 x 128 0.095 BFLOPs
97 upsample 2x 38 x 38 x 128 -> 76 x 76 x 128
98 route 97 36
...
106 yolo
在工程应用中,yolo的部署方式有很多种,但是由于各种原因,选择了caffe环境部署。所以需要将yolo的模型以及网络文件转换为caffe格式。
想要转化为Caffe框架,就要先了解yolov3的网络结构,如下图。
如果有运行过darknet应该会很熟悉,这是darknet运行成功后打印log信息,这里面包含了yolo网络结构的一些信息。yolov3与v2相比,网络结构中加入了残差(shortcut层),并且引入了上采样(upsample层),并为了将采样后的特征图进行融合引入了拼接(route层),最后融合的特征图以三个不同的大小131375,262675,525275输入给yolo层最后得到目标的位置及分类信息,加上卷积层convolution,这些便是yolov3的网络基本构造。因此只要我们如果在Caffe中找到对应的层按照相应的进行构造就能够使用Caffe实现yolov3了。
卷积层不说,yolov3中的shortcut层可以用eltwise替代,route层可以用concat替代,而upsample层和yolo层则需要自己实现,并添加到Caffe中即可。upsample层主要完成了上采样的工作,这里不细说。本文主要讲一下yolo层如何实现,上图中的YOLO Detection即为yolo层的所在位置,接收三种不同大小的特征图,并完成对特征图的解析,得到物体的位置和类别信息。所以其实yolo层主要起到了解析特征并输出检测结果的作用,这一过程我们完全可以在外部实现而无需加入到网络结构当中,也就是说我们无需将实现的yolo层加入到Caffe当中去。
下面这部分将着重讲一下如何实现从darknet向caffe的转换,首先这一过程要感谢chenyingpeng提供的代码。
参考(https://blog.csdn.net/Chen_yingpeng/article/details/80692018)。
1.加入upsample层并编译Caffe
upsample层的代码在(https://pan.baidu.com/s/13GpoYoqKSCeFX0m0ves_fQ#list/path=%2F 提取码:bwrd)
其中的upsample_layer.hpp放入include/caffe/layers下面;upsample_layer.cpp与upsample_layer.cu放在src/caffe/layers下面。
修改相应的caffe.proto文件,src/caffe/proto/caffe.proto中的LayerParameter的最后一行加入加入:
message LayerParameter {
.....
optional UpsampleParameter upsample_param = 149;
}
注意149为新层的ID号,该ID号请根据个人的caffe.proto文件指定即可。
然后再caffe.proto中添加upsample层的参数:
message UpsampleParameter{
optional int32 scale = 1 [default = 1];
}
紧接着重新编译Caffe,这样就完成了在Caffe中添加upsample层。
上面说过转换到Caffe后只包含推理过程,因此我们需要将训练好的模型(.cfg)和权重文件(.weights)转换到对应Caffe下的.proto和.caffemodel,代码可以借鉴github上的模型转换工具(https://github.com/marvis/pytorch-caffe-darknet-convert )。该工具需要pytorch支持请自行百度安装安装。
且该工具应用于Yolov2,因为我们在Caffe中加入了相应的upsample层并且yolov3和v2的网络结构有变化,因此需要替换相应的darknet2caffe.py,代码在这里(略,哈哈哈哈,理解万岁。)。
yolov3-darknet2caffe.py脚本需要修改caffe路径,如下图:
至此我们的准备工作就结束了,这样通过Caffe我们就能得到相应的blobs,这些blobs里包含的信息和darknet输入给yolo层的信息是一样的。接下来我们将展示如何转换模型:
把要转的yolov3的模型(yolov3.weights)和模型文件(yolov3.cfg(测试网络))copy到pytorch-caffe-darknet-convert目录下;
执行如下命令:
python yolov3_darknet2caffe.py yolov3.cfg yolov3.weights yolov3.prototxt yolov3.caffemodel
上面转换后的模型不包含yolo层,需要在部署的环境中自己实现yolo层的编写(不要担心,当然是已经做好了,直接用就行)。需要注意的是:yolo层中包含许多所使用模型信息(种类、anchor信息),需要与所训模型匹配(也不用担心找不到相关的参数:相关的参数在生成的.proto文件最后边)。
因此我们转换到Caffe框架下的yolov3也仅能实现推理过程,具体的训练还需要通过darknet来完成。
报错如下:
运行darknet2caffe.py后生成了.prototxt文件 没能生成.caffemodel文件,报错如下:
unknow layer type yolo [libprotobuf ERROR google/protobuf/text_format.cc:274] Error parsing text-format caffe.NetParameter: 2622:20: Message type "caffe.LayerParameter" has no field named "upsample_param". WARNING: Logging before InitGoogleLogging() is written to STDERR F0831 16:30:17.545022 32644 upgrade_proto.cpp:79] Check failed: ReadProtoFromTextFile(param_file, param) Failed to parse NetParameter file: yolov3.prototxt *** Check failure stack trace: *** 已放弃 (核心已转储)
解决办法:
Caffe编译有问题,重新按要求编译caffe。(如果出现在caffe路径下不能进行编译,请在build路径下进行编译。)
make clean
make all
make pycaffe
git clone https://github.com/xiangweizeng/darknet2ncnn.git
其中,darknet与ncnn需要单独下载,并放置该目录文件夹中。
遇到并解决的问题
报错:/usr/bin/ld: 找不到 -lippicv
collect2: error: ld returned 1 exit status
解决办法:
cp /home/username/opencv-3.1.0/3rdparty/ippicv/unpack/ippicv_lnx/lib/intel64/liboppicv.a
至 /usr/local/lib
参考博客:https://blog.csdn.net/dengshuai_super/article/details/51895120
环境搭建之后,可以进行模型转换,有以下几种形式:
./darknet2ncnn data/yolov3-Person3-416_PileG_Digt.cfg data/yolov3-Person3-416_PileG_Digt.weights example/zoo/yolov3-Pile_Digt.param example/zoo/yolov3-Pile_Digt.bin
./convert_verify data/yolov3-Person3-416_PileG_Digt.cfg data/yolov3-Person3-416_PileG_Digt.weights example/zoo/yolov3-Pile_Digt.param example/zoo/yolov3-Pile_Digt.bin example/data/14.jpg
./classifier zoo/yolov3-Pile_Digt.param zoo/yolov3-Pile_Digt.bin data/14.jpg data/PileDigt.names
./yolo zoo/yolov3-Pile_Digt.param zoo/yolov3-Pile_Digt.bin data/14.jpg data/PileDigt.names
详细介绍可参考下载文件中的README.md文件