darknet53的网络结构笔记

本网络结构从gluoncv/model_zoo/yolo/darknet.py调试得到 

darknet layers =  [1,   2,    8,    8,    4]
darknet channels = [64, 128, 256, 512, 1024]

--------------------------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================================
Input (1, 3, 512, 703) 0
Conv2D-1 (1, 32, 512, 703) 864
BatchNorm-2 (1, 32, 512, 703) 128
LeakyReLU-3 (1, 32, 512, 703) 0
_conv2d 1
Conv2D-4 (1, 64, 256, 352) 18432
BatchNorm-5 (1, 64, 256, 352) 256
LeakyReLU-6 (1, 64, 256, 352) 0
_conv2d 2
Conv2D-7 (1, 32, 256, 352) 2048
BatchNorm-8 (1, 32, 256, 352) 128
LeakyReLU-9 (1, 32, 256, 352) 0
Conv2D-10 (1, 64, 256, 352) 18432
BatchNorm-11 (1, 64, 256, 352) 256
LeakyReLU-12 (1, 64, 256, 352) 0
DarknetBasicBlockV3-13 (1, 64, 256, 352) 0
DarknetBasicBlockV3 3
Conv2D-14 (1, 128, 128, 176) 73728
BatchNorm-15 (1, 128, 128, 176) 512
LeakyReLU-16 (1, 128, 128, 176) 0
_conv2d 4
Conv2D-17 (1, 64, 128, 176) 8192
BatchNorm-18 (1, 64, 128, 176) 256
LeakyReLU-19 (1, 64, 128, 176) 0
Conv2D-20 (1, 128, 128, 176) 73728
BatchNorm-21 (1, 128, 128, 176) 512
LeakyReLU-22 (1, 128, 128, 176) 0
DarknetBasicBlockV3-23 (1, 128, 128, 176) 0
DarknetBasicBlockV3 5
Conv2D-24 (1, 64, 128, 176) 8192
BatchNorm-25 (1, 64, 128, 176) 256
LeakyReLU-26 (1, 64, 128, 176) 0
Conv2D-27 (1, 128, 128, 176) 73728
BatchNorm-28 (1, 128, 128, 176) 512
LeakyReLU-29 (1, 128, 128, 176) 0
DarknetBasicBlockV3-30 (1, 128, 128, 176) 0
DarknetBasicBlockV3 6
Conv2D-31 (1, 256, 64, 88) 294912
BatchNorm-32 (1, 256, 64, 88) 1024
LeakyReLU-33 (1, 256, 64, 88) 0
_conv2d 7
Conv2D-34 (1, 128, 64, 88) 32768
BatchNorm-35 (1, 128, 64, 88) 512
LeakyReLU-36 (1, 128, 64, 88) 0
Conv2D-37 (1, 256, 64, 88) 294912
BatchNorm-38 (1, 256, 64, 88) 1024
LeakyReLU-39 (1, 256, 64, 88) 0
DarknetBasicBlockV3-40 (1, 256, 64, 88) 0
DarknetBasicBlockV3 8
Conv2D-41 (1, 128, 64, 88) 32768
BatchNorm-42 (1, 128, 64, 88) 512
LeakyReLU-43 (1, 128, 64, 88) 0
Conv2D-44 (1, 256, 64, 88) 294912
BatchNorm-45 (1, 256, 64, 88) 1024
LeakyReLU-46 (1, 256, 64, 88) 0
DarknetBasicBlockV3-47 (1, 256, 64, 88) 0
DarknetBasicBlockV3 9
Conv2D-48 (1, 128, 64, 88) 32768
BatchNorm-49 (1, 128, 64, 88) 512
LeakyReLU-50 (1, 128, 64, 88) 0
Conv2D-51 (1, 256, 64, 88) 294912
BatchNorm-52 (1, 256, 64, 88) 1024
LeakyReLU-53 (1, 256, 64, 88) 0
DarknetBasicBlockV3-54 (1, 256, 64, 88) 0
DarknetBasicBlockV3 10
Conv2D-55 (1, 128, 64, 88) 32768
BatchNorm-56 (1, 128, 64, 88) 512
LeakyReLU-57 (1, 128, 64, 88) 0
Conv2D-58 (1, 256, 64, 88) 294912
BatchNorm-59 (1, 256, 64, 88) 1024
LeakyReLU-60 (1, 256, 64, 88) 0
DarknetBasicBlockV3-61 (1, 256, 64, 88) 0
DarknetBasicBlockV3 11
Conv2D-62 (1, 128, 64, 88) 32768
BatchNorm-63 (1, 128, 64, 88) 512
LeakyReLU-64 (1, 128, 64, 88) 0
Conv2D-65 (1, 256, 64, 88) 294912
BatchNorm-66 (1, 256, 64, 88) 1024
LeakyReLU-67 (1, 256, 64, 88) 0
DarknetBasicBlockV3-68 (1, 256, 64, 88) 0
DarknetBasicBlockV3 12
Conv2D-69 (1, 128, 64, 88) 32768
BatchNorm-70 (1, 128, 64, 88) 512
LeakyReLU-71 (1, 128, 64, 88) 0
Conv2D-72 (1, 256, 64, 88) 294912
BatchNorm-73 (1, 256, 64, 88) 1024
LeakyReLU-74 (1, 256, 64, 88) 0
DarknetBasicBlockV3-75 (1, 256, 64, 88) 0
DarknetBasicBlockV3 13
Conv2D-76 (1, 128, 64, 88) 32768
BatchNorm-77 (1, 128, 64, 88) 512
LeakyReLU-78 (1, 128, 64, 88) 0
Conv2D-79 (1, 256, 64, 88) 294912
BatchNorm-80 (1, 256, 64, 88) 1024
LeakyReLU-81 (1, 256, 64, 88) 0
DarknetBasicBlockV3-82 (1, 256, 64, 88) 0
DarknetBasicBlockV3 14
Conv2D-83 (1, 128, 64, 88) 32768
BatchNorm-84 (1, 128, 64, 88) 512
LeakyReLU-85 (1, 128, 64, 88) 0
Conv2D-86 (1, 256, 64, 88) 294912
BatchNorm-87 (1, 256, 64, 88) 1024
LeakyReLU-88 (1, 256, 64, 88) 0
DarknetBasicBlockV3-89 (1, 256, 64, 88) 0
DarknetBasicBlockV3 15
---------------------------------------------------------------------------------------------------------
Conv2D-90 (1, 512, 32, 44) 1179648
BatchNorm-91 (1, 512, 32, 44) 2048
LeakyReLU-92 (1, 512, 32, 44) 0
_conv2d 16
Conv2D-93 (1, 256, 32, 44) 131072
BatchNorm-94 (1, 256, 32, 44) 1024
LeakyReLU-95 (1, 256, 32, 44) 0
Conv2D-96 (1, 512, 32, 44) 1179648
BatchNorm-97 (1, 512, 32, 44) 2048
LeakyReLU-98 (1, 512, 32, 44) 0
DarknetBasicBlockV3-99 (1, 512, 32, 44) 0
DarknetBasicBlockV3 17
Conv2D-100 (1, 256, 32, 44) 131072
BatchNorm-101 (1, 256, 32, 44) 1024
LeakyReLU-102 (1, 256, 32, 44) 0
Conv2D-103 (1, 512, 32, 44) 1179648
BatchNorm-104 (1, 512, 32, 44) 2048
LeakyReLU-105 (1, 512, 32, 44) 0
DarknetBasicBlockV3-106 (1, 512, 32, 44) 0
DarknetBasicBlockV3 18
Conv2D-107 (1, 256, 32, 44) 131072
BatchNorm-108 (1, 256, 32, 44) 1024
LeakyReLU-109 (1, 256, 32, 44) 0
Conv2D-110 (1, 512, 32, 44) 1179648
BatchNorm-111 (1, 512, 32, 44) 2048
LeakyReLU-112 (1, 512, 32, 44) 0
DarknetBasicBlockV3-113 (1, 512, 32, 44) 0
DarknetBasicBlockV3 19
Conv2D-114 (1, 256, 32, 44) 131072
BatchNorm-115 (1, 256, 32, 44) 1024
LeakyReLU-116 (1, 256, 32, 44) 0
Conv2D-117 (1, 512, 32, 44) 1179648
BatchNorm-118 (1, 512, 32, 44) 2048
LeakyReLU-119 (1, 512, 32, 44) 0
DarknetBasicBlockV3-120 (1, 512, 32, 44) 0
DarknetBasicBlockV3 20
Conv2D-121 (1, 256, 32, 44) 131072
BatchNorm-122 (1, 256, 32, 44) 1024
LeakyReLU-123 (1, 256, 32, 44) 0
Conv2D-124 (1, 512, 32, 44) 1179648
BatchNorm-125 (1, 512, 32, 44) 2048
LeakyReLU-126 (1, 512, 32, 44) 0
DarknetBasicBlockV3-127 (1, 512, 32, 44) 0
DarknetBasicBlockV3 21
Conv2D-128 (1, 256, 32, 44) 131072
BatchNorm-129 (1, 256, 32, 44) 1024
LeakyReLU-130 (1, 256, 32, 44) 0
Conv2D-131 (1, 512, 32, 44) 1179648
BatchNorm-132 (1, 512, 32, 44) 2048
LeakyReLU-133 (1, 512, 32, 44) 0
DarknetBasicBlockV3-134 (1, 512, 32, 44) 0
DarknetBasicBlockV3 22
Conv2D-135 (1, 256, 32, 44) 131072
BatchNorm-136 (1, 256, 32, 44) 1024
LeakyReLU-137 (1, 256, 32, 44) 0
Conv2D-138 (1, 512, 32, 44) 1179648
BatchNorm-139 (1, 512, 32, 44) 2048
LeakyReLU-140 (1, 512, 32, 44) 0
DarknetBasicBlockV3-141 (1, 512, 32, 44) 0
DarknetBasicBlockV3 23
Conv2D-142 (1, 256, 32, 44) 131072
BatchNorm-143 (1, 256, 32, 44) 1024
LeakyReLU-144 (1, 256, 32, 44) 0
Conv2D-145 (1, 512, 32, 44) 1179648
BatchNorm-146 (1, 512, 32, 44) 2048
LeakyReLU-147 (1, 512, 32, 44) 0
DarknetBasicBlockV3-148 (1, 512, 32, 44) 0
DarknetBasicBlockV3 24
---------------------------------------------------------------------------------------------------------
Conv2D-149 (1, 1024, 16, 22) 4718592
BatchNorm-150 (1, 1024, 16, 22) 4096
LeakyReLU-151 (1, 1024, 16, 22) 0
_conv2d 25
Conv2D-152 (1, 512, 16, 22) 524288
BatchNorm-153 (1, 512, 16, 22) 2048
LeakyReLU-154 (1, 512, 16, 22) 0
Conv2D-155 (1, 1024, 16, 22) 4718592
BatchNorm-156 (1, 1024, 16, 22) 4096
LeakyReLU-157 (1, 1024, 16, 22) 0
DarknetBasicBlockV3-158 (1, 1024, 16, 22) 0
DarknetBasicBlockV3 26
Conv2D-159 (1, 512, 16, 22) 524288
BatchNorm-160 (1, 512, 16, 22) 2048
LeakyReLU-161 (1, 512, 16, 22) 0
Conv2D-162 (1, 1024, 16, 22) 4718592
BatchNorm-163 (1, 1024, 16, 22) 4096
LeakyReLU-164 (1, 1024, 16, 22) 0
DarknetBasicBlockV3-165 (1, 1024, 16, 22) 0
DarknetBasicBlockV3 27
Conv2D-166 (1, 512, 16, 22) 524288
BatchNorm-167 (1, 512, 16, 22) 2048
LeakyReLU-168 (1, 512, 16, 22) 0
Conv2D-169 (1, 1024, 16, 22) 4718592
BatchNorm-170 (1, 1024, 16, 22) 4096
LeakyReLU-171 (1, 1024, 16, 22) 0
DarknetBasicBlockV3-172 (1, 1024, 16, 22) 0
DarknetBasicBlockV3 28
Conv2D-173 (1, 512, 16, 22) 524288
BatchNorm-174 (1, 512, 16, 22) 2048
LeakyReLU-175 (1, 512, 16, 22) 0
Conv2D-176 (1, 1024, 16, 22) 4718592
BatchNorm-177 (1, 1024, 16, 22) 4096
LeakyReLU-178 (1, 1024, 16, 22) 0
DarknetBasicBlockV3-179 (1, 1024, 16, 22) 0
DarknetBasicBlockV3 29
---------------------------------------------------------------------------------------------------------
Dense-180 (1, 1000) 1025000
DarknetV3-181 (1, 1000) 0
================================================================================
  • 优点
    Darknet是一个比较小众的深度学习框架,没有社区,主要靠作者团队维护,所以推广较弱,用的人不多。而且由于维护人员有限,功能也不如tensorflow等框架那么强大,但是该框架还是有一些独有的优点:
    1.易于安装:在makefile里面选择自己需要的附加项(cuda,cudnn,opencv等)直接make即可,几分钟完成安装;
    2.没有任何依赖项:整个框架都用C语言进行编写,可以不依赖任何库,连opencv作者都编写了可以对其进行替代的函数;
    3.结构明晰,源代码查看、修改方便:其框架的基础文件都在src文件夹,而定义的一些检测、分类函数则在example文件夹,可根据需要直接对源代码进行查看和修改;
    4.友好python接口:虽然darknet使用c语言进行编写,但是也提供了python的接口,通过python函数,能够使用python直接对训练好的.weight格式的模型进行调用;
    5.易于移植:该框架部署到机器本地十分简单,且可以根据机器情况,使用cpu和gpu,特别是检测识别任务的本地端部署,darknet会显得异常方便。
  • 代码结构
    下图是darknet源代码下载解压后文件夹的分布情况:
  • 在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d

  • 1.cfg文件夹内是一些模型的架构,每个cfg文件类似与caffe的prototxt文件,通过该文件定义的整个模型的架构
    2.data文件夹内放置了一些label文件,如coco9k的类别名等,和一些样例图(该文件夹主要为演示用,或者是直接训练coco等对应数据集时有用,如果要用自己的数据自行训练,该文件夹内的东西都不是我们需要的)
    3.src文件夹内全是最底层的框架定义文件,所有层的定义等最基本的函数全部在该文件夹内,可以理解为该文件夹就是框架的源码;
    4.examples文件夹是更为高层的一些函数,如检测函数,识别函数等,这些函数直接调用了底层的函数,我们经常使用的就是example中的函数;
    5.include文件夹,顾名思义,存放头文件的地方;
    6.python文件夹里是使用python对模型的调用方法,基本都在darknet.py中。当然,要实现python的调用,​​还需要用到darknet的动态库libdarknet.so​​,这个动态库稍后再介绍;
    7.scripts文件夹中是一些脚本,如下载coco数据集,将voc格式的数据集转换为训练所需格式的脚本等
    8.除了license文件,剩下的就是Makefile文件,如下图,在问价开头有一些选项,把你需要使用的选项设为1即可
  • 在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_02

  • 安装
    1.点开Makefile,将需要的选项设置为1,如图,使用GPU和CUDNN
  • 在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_03

  • 2.打开终端,进入到darknet文件夹根目录,输入make,开始编译
    3.几分钟后编译完成,文件夹中会多出一些文件夹和文件,obj文件中存放了编译过程中的.o文件,其他的几个空文件夹也不需要太大关注,这里最重要的就是三个:名为darknet的exe文件,名为libdarknet.a的静态链接库和名为libdarknet.so的动态链接库。如果直接在本地进行模型调用尝试,可以直接运行darknet这个exe文件,如果需要移植调用,则需要用到libdarknet.so这个动态链接库,这个动态链接库中只包含了src文件夹中定义的框架基础函数,没有包含examples中的高层函数,所以调用过程中需要自己去定义检测函数
  • 检测
    运行如下代码
./darknet detector test data/detect.data data/yolov3.cfg data/yolov3.weight

 

其中./darknet表示运行编译生成的darknet.exe文件,darknet.exe首先调用example文件夹下的darknet.c,该文件中的main函数需要预定义参数,detector即为预定义参数,如下代码

else if (0 == strcmp(argv[1], "detector")){
run_detector(argc, argv);

由‘detector’转而调用run_detector,run_detector存在于example文件夹下的detector.c中,再根据预定义参数,确定是调用检测函数,训练函数还是验证函数:

if(0==strcmp(argv[2], "test")) test_detector(datacfg, cfg, weights, filename, thresh, hier_thresh, outfile, fullscreen);
else if(0==strcmp(argv[2], "train")) train_detector(datacfg, cfg, weights, gpus, ngpus, clear);
else if(0==strcmp(argv[2], "valid")) validate_detector(datacfg, cfg, weights, outfile);
else if(0==strcmp(argv[2], "valid2")) validate_detector_flip(datacfg, cfg, weights, outfile);
else if(0==strcmp(argv[2], "recall")) validate_detector_recall(cfg, weights);
else if(0==strcmp(argv[2], "demo"))

其中test表示检测,train表示训练,valid表示验证,recall表示测试其召回率,demo为调用摄像头的实时检测

命令最后的三个参数表示运行需要的文件,.data文件记录了模型检测的类别,类名文件等,如下:

classes= 1
train = /media/seven/yolov3/data/plate2/train.list
#valid = data/coco_val_5k.list
names = data/plate/plate.names
backup = /media/seven/yolov3/data/plate2/models
#eval=coco

class表示检测类别,train为训练中需要用到的训练数据的列表,valid为验证集列表,names为检测类别的名称,backup为训练中用到的存放训练模型的路径

.cfg文件定义了模型结构,而.weight文件为调用的模型权重文件

运行以上命令,会在终端得到如下提示:

Enter Image Path:

 

直接在终端输入图像的路径,就可以对该图像进行检测,并在darknet的根目录生成名为predictions.png的检测结果,如图:

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_04

  • 分类
    分类和检测类似,调用命令如下:
./darknet classifier predict classify.data classify.cfg classify.weights

 

与检测同理,./darknet运行darknet.exe,并调用example中的darknet.c文件,通过classfier调用classifier.c中的run_classifier函数:

else if (0 == strcmp(argv[1], "classifier")){
run_classifier(argc, argv);

并通过predict进而调用predict_classifier函数:

if(0==strcmp(argv[2], "predict")) predict_classifier(data, cfg, weights, filename, top);
else if(0==strcmp(argv[2], "fout")) file_output_classifier(data, cfg, weights, filename);
else if(0==strcmp(argv[2], "try")) try_classifier(data, cfg, weights, filename, atoi(layer_s));
else if(0==strcmp(argv[2], "train")) train_classifier(data, cfg, weights, gpus, ngpus, clear);
else if(0==strcmp(argv[2], "demo")) demo_classifier(data, cfg, weights, cam_index, filename);
...

 

而classify.data,classify.cfg,classify.weights分别表示分类对应的.data文件,模型定义cfg文件和模型权重.weights文件。

  • 训练
  1. 检测模型的训练:
    (1)数据准备:
    首先,你需要将数据的groundtruth转化为darknet需要的格式,如果你的gt为voc格式的xml,可以通过如下脚本进行转换
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
classes = ["plate"]#类别改为自己需要检测的所有类别
def convert(size, box):
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(image_id):
in_file = open(xml_path)#与图片对应的xml文件所在的地址
out_file = open(txt_save_path,'w') #与此xml对应的转换后的txt,这个txt的保存完整路径
tree=ET.parse(in_file)
root = tree.getroot()
size = root.find('size') #访问size标签的数据
w = int(size.find('width').text)#读取size标签中宽度的数据
h = int(size.find('height').text)#读取size标签中高度的数据

for obj in root.iter('object'):
cls = obj.find('name').text
if cls not in classes :#or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox') #访问boundbox标签的数据并进行处理,都按yolo自带的代码来,没有改动
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')

 

上面的代码需要自行设置xml_path和txt_save_path

从上面的代码可以看到,对于object的位置x_min,x_max,y_min,y_max,先求得其中心点坐标center_x,center_y以及位置框的长宽width_rect,height_rect,再将这四个值分别除以长宽以将数据归一化,如果不是voc格式的数据可以按照这样的思路进行类似处理

如果是voc格式的数据可以参照我之前的博客进行一步一步的处理​​darknet用自己的数据进行训练​​ 按照上面的流程,每张图像都生成了对应的txt文件来保存其归一化后的位置信息,如下图对应生成的txt如下:

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_05

0 0.250925925926 0.576388888889 0.1 0.0263888888889
0 0.485185185185 0.578125 0.0685185185185 0.0201388888889

 

图中共有车牌两个,每行保存一个车牌的信息,第一个0表示检测object的label,因为我只有一类,所以都是0
后面的四位即为归一化后的中心点坐标和位置框的长和宽
最后将图像和对应txt的文件名统一,并拷贝到同一个文件夹(a.jpg对应的txt为a.txt),如图:

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_06


注意,txt和对应jpg文件的名称除了最后.jpg,.txt的后缀不一样,其他的必须完全一样,且需要保存在同一文件夹,训练过程中会直接将a.jpg的名称替换为a.txt来寻找该图像对应的gt。对应的gt文件也不一定必须是txt格式,如果不是txt格式可以去源码中将这部分代码进行修改,将.jpg替换为你需要的格式后缀(2).data文件准备

前面已经贴过.data的图,训练的时候必须的项目有“class”,“train”,“backup”。“names”最好也设置上,方便以后调用。

“class”表示你要检测的类别个数,如检测类别为20则​​class=20​​ “backup”表示训练过程中的缓存和保存的模型。训练过程中,会在该路径下生成一个后缀为.backup的文件,该文件每100个step将模型更新一遍,以防止训练忽然终端而没有保存模型。并且,训练保存的模型也会存在该路径下。默认情况下,每10000step还是多少(记不太清了,我自己修改过)会保存一个模型,命名为yolov3_迭代次数.weights,最终训练完成还会保存一个yolov3_final.weights。这些模型都会保存在backup路径下

“names”为保存检测object名称的路径,names=plate.names

“train”为你训练集的list路径,如train=data/trainlist.txt,trainlist.txt中保存了所有训练集图像的路径,如下图

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_07


可以通过如下命令直接生成该文件:

find image_path -name \*.jpg > trainlist.txt

 

image_path为你数据集的路径

(3).cfg文件准备
如果要调用yolo v3,可以直接使用cfg文件夹下的yolov3.cfg,但是需要做如下几个修改:
首先,将最上方的

# Testing
batch=1
subdivisions=1
# Training
# batch=64
# subdivisions=16

修改为

# Testing
#batch=1
#subdivisions=1
# Training
batch=64
subdivisions=16

其中batch表示batchsize,而subdivisions是为了解决想要大batchsize而显存又不够的情况,每次代码只读取batchsize/subdivisions 个图像,如图中设置为64/16=4,但是会将16次的结果也就是64张图的结果,作为一个batch来统一处理
(调用的时候再将testing部分解除注释,并将train部分注释)

然后,根据自己检测的类别,将每个​​[yolo]​​​(共有三个​​[yolo]​​​) 下方的classes修改为自己需要检测的类别,如果只检测一类则classes=1
然后将每个​​​[yolo]​​​ 上方的第一个filters的值进行修改,计算方式为​​(5+classes)*3​​,如果classes为1,则为18,修改前后的对比:

[convolutional]
size=1
stride=1
pad=1
filters=255
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=80
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1
[convolutional]
size=1
stride=1
pad=1
filters=18
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

图中的random表示论文中提及的resize network来增强网络的适应性,如果显存足够,最好设置为1,如果显存不够,也可以将其设置为0,即不进行network resizing

(4)weights文件准备
如果你使用的是作者提供的cfg模型结构,如yolov3,那么可以到其官网去下载预训练模型来初始化模型参数,这样可以加快收敛。当然,你也可以不使用预训练模型进行训练

(5)开始训练
如果使用预训练模型则使用如下命令

./darknet detector train data/detect.data data/yolov3.cfg data/yolov3.weight

 

否则,使用

./darknet detectortrain data/detect.data data/yolov3.cfg

命令类似与之前检测的调用命令,只是将test变为了train

  1. 分类模型的训练
    (1)数据准备
    和检测不一样,分类的gt只需要一个label即可,不再需要位置框的信息,所以不再需要单独的txt文件来保存gt,而是直接将label在图像名称上进行体现。所以需要对图像名称进行重命名,命名规则为:
    (图片序号)_(标签).(图片格式),如1_numzero.jpg
    需要注意的是:
    1.label不区分大小写,即 numzero和NumZero是一样的效果
    2.label之间不能有包含关系,如ji和jin,不能出现这样的形式,可改为ji1和jin

(2).data文件准备
和检测类似:

classes=65
train = data/char/train.list
labels = data/char/labels.txt
backup = backup/
top=2

其中top不是在训练中使用,而是在分类调用时使用,表示输出Top个最高的可能值

(3).cfg文件准备
可以从cfg文件夹中选择,也可以自行定义

(4).weights文件
同上面的检测

(5)开始训练
使用命令:

./darknet classifier train cfg/cifar.data cfg/cifar_small.cfg (xxx.weights)

YoloV3

YoloV3作为最经典的一步法的模型之一,在精度中等的情况下并保证了很快的速度。

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_08

YoloV3框架

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_09


与大部分网络一样,首先是特征提取层。YoloV3采用了DarkNet53。YoloV3在其基础上得到了13×13的特征图m,再将其上采样并与DarkNet53中间得到的特征图合并得到26×26,同理再将26×26上采样得到52*52的特征图。 至于通道数为什么是255,d这不是随意取的,而是((4+1+80)×3),这将在下面解释。

Yolo系列的核心思想就是特征图中的每一个网格都会去预测一个物体。也就是最后得到的13×13,共169个网格,这169个网格都会分别去预测它对应网格的物体属于哪一个。而预测物体会得到boundingBox4个坐标,1个边框置信度以及coco数据集80个类别的概率(4+1+80)。至于为什么要×3呢,这归咎与anchor的存在(这将在后面说明)。 至此,一张图片经过DarkNet网络,得到了13×13×255,26×26×255,52×52×255金字塔特征图。后面所有的位置,种类预测也都是基于这3个不同大小的特征图。

anchor

如果没有anchor的话,直接这13×13中的1个特征像素对应原图32个像素(因为下采样了32倍),这样的话,32个像素肯定不能包含整个物体,而是某物体的一部分,最后再将所有同一类d别的的像素进行合并。这种方法,我个人觉得1是精度不高,2是训练困难。cce

anchor相当于把这32个像素的位置放大了。为了尽可能让anchor尽可能的准确包含物体,yoloV3为每个网格提供了3种先验框。这样总共3层特征图,聚类出3×3种尺寸的先验框(不同的数据集会稍有不同)。在COCO数据集这9个先验框是:

[(10x13),(16x30),(33x23)]—对应52×52网格;

[(30x61),(62x45),(59x119)]—对应26×26网格;

[(116x90),(156x198),(373x326)]—对应13×13网格。

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_10


红色框是1313中的每一个网格。例如坐标为(10,5),那么它对应原图位置为(1032,532)像素点的位置。这样我们再以这个点为中心,找出附近[(116x90),(156x198),(373x326)]3个框的位置。例如第一个anchor对应的框为(1032-116/2,5*32-90/2,116,90)…遇到边界置0就好了。这样每一个网格就负责原图这3个位置的物体位置,类别预测,而这里最终预测的物体位置也是相对于anchor的位置(最后可还原至原图)。

同理,另外26×26,52×52的特征图也是这样预测物体位置的。

损失函数

网络输出需要经过decode解码得到bbox的pred预测输出

return tensor of shape [batch_size, output_size, output_size, anchor_per_scale, 5 + num_classes]
contains (x, y, w, h, score, probability)
"""# 获得输入数据的形状conv_shape =tf.shape(conv_output)batch_size =conv_shape[0]output_size =conv_shape[1]# 每个gred预测3个box(只有包含box中心的grep才进行预测)anchor_per_scale =len(anchors)conv_output =tf.reshape(conv_output,(batch_size,output_size,output_size,anchor_per_scale,5+self.num_class))# 获得每个gred预测的box中心坐标的偏移值,conv_raw_dxdy =conv_output[:,:,:,:,0:2]# 表示高宽的缩放比conv_raw_dwdh =conv_output[:,:,:,:,2:4]# 会的box预测每个类别的置信度conv_raw_conf =conv_output[:,:,:,:,4:5]# 每种类别的概率conv_raw_prob =conv_output[:,:,:,:,5:]# 对没一列进进行标号,假设output_size = 5得到如下# [0 0 0 0 0]# [1 1 1 1 1]# [2 2 2 2 2]# [3 3 3 3 3]# [4 4 4 4 4]]y =tf.tile(tf.range(output_size,dtype=tf.int32)[:,tf.newaxis],[1,output_size])# 对没一列进进行标号,假设output_size = 5得到如下# [[0 1 2 3 4]# [0 1 2 3 4]# [0 1 2 3 4]# [0 1 2 3 4]# [0 1 2 3 4]]x =tf.tile(tf.range(output_size,dtype=tf.int32)[tf.newaxis,:],[output_size,1])# 获得每个gred的坐标,如:# [(0,0),(0,1),(0,2),(0,3),(0,4)]# ......# ......# [(4,0),(4,1),(4,2),(4,3),(4,4)]xy_grid =tf.concat([x[:,:,tf.newaxis],y[:,:,tf.newaxis]],axis=-1)# 再增加一个batch_size与anchor_per_scale的维度,最终形成的xy_grid包含每个batch_size,每个张图片gred的坐标,及3个预测的anchor_per_scalexy_grid =tf.tile(xy_grid[tf.newaxis,:,:,tf.newaxis,:],[batch_size,1,1,anchor_per_scale,1])xy_grid =tf.cast(xy_grid,tf.float32)# conv_raw_dxdy相对于中心点偏移值,* stride复原到原图像中pred_xy =(tf.sigmoid(conv_raw_dxdy)+xy_grid)*stride # conv_raw_dwdh,表示高宽的缩放比pred_wh =(tf.exp(conv_raw_dwdh)*anchors)*stride # 进行合并pred_xywh =tf.concat([pred_xy,pred_wh],axis=-1)# 置信度pred_conf =tf.sigmoid(conv_raw_conf)# 类别概率pred_prob =tf.sigmoid(conv_raw_prob)returntf.concat([pred_xywh,pred_conf,pred_prob],axis=-1)```
损失函数是由boundingBox,类别,置信度组成。

```j
def loss_layer(self, conv, pred, label, bboxes, anchors, stride):
print(conv.shape)
print(pred.shape)
print(label.shape)
print(bboxes.shape)
conv_shape = tf.shape(conv)
batch_size = conv_shape[0]
output_size = conv_shape[1]
input_size = stride * output_size
conv = tf.reshape(conv, (batch_size, output_size, output_size,
self.anchor_per_scale, 5 + self.num_class))
conv_raw_conf = conv[:, :, :, :, 4:5] #没有通过anchors计算的置信度,即没有经过decode的原始网络的输出
conv_raw_prob = conv[:, :, :, :, 5:] #没有通过anchors计算的类别概率

pred_xywh = pred[:, :, :, :, 0:4] #通过anchors decode计算得到的中心坐标以及长宽
pred_conf = pred[:, :, :, :, 4:5] #通过anchors计算的类别的置信度

label_xywh = label[:, :, :, :, 0:4] # 每个cell真实3个box对应的中心坐标以及长框
respond_bbox = label[:, :, :, :, 4:5] # 每个cell真实3个box的置信度
label_prob = label[:, :, :, :, 5:] # 每个cell真实3个box种类的概率

giou = tf.expand_dims(self.bbox_giou(pred_xywh, label_xywh), axis=-1)
print('giou', giou.shape)
input_size = tf.cast(input_size, tf.float32)

bbox_loss_scale = 2.0 - 1.0 * label_xywh[:, :, :, :, 2:3] * label_xywh[:, :, :, :, 3:4] / (input_size ** 2)
giou_loss = respond_bbox * bbox_loss_scale * (1- giou)

iou = self.bbox_iou(pred_xywh[:, :, :, :, np.newaxis, :], bboxes[:, np.newaxis, np.newaxis, np.newaxis, :, :])
max_iou = tf.expand_dims(tf.reduce_max(iou, axis=-1), axis=-1)

respond_bgd = (1.0 - respond_bbox) * tf.cast( max_iou < self.iou_loss_thresh, tf.float32 )

conf_focal = self.focal(respond_bbox, pred_conf)

conf_loss = conf_focal * (
respond_bbox * tf.nn.sigmoid_cross_entropy_with_logits(labels=respond_bbox, logits=conv_raw_conf)
+
respond_bgd * tf.nn.sigmoid_cross_entropy_with_logits(labels=respond_bbox, logits=conv_raw_conf)
)

prob_loss = respond_bbox * tf.nn.sigmoid_cross_entropy_with_logits(labels=label_prob, logits=conv_raw_prob)

giou_loss = tf.reduce_mean(tf.reduce_sum(giou_loss, axis=[1,2,3,4]))
conf_loss = tf.reduce_mean(tf.reduce_sum(conf_loss, axis=[1,2,3,4]))
prob_loss = tf.reduce_mean(tf.reduce_sum(prob_loss, axis=[1,2,3,4]))

return giou_loss, conf_loss, prob_loss

中心坐标损失

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_11


x,y为label框的中心坐标,上面带符号的为预测中心坐标的位置。I为该位置是否是背景,是为0,否则为1。S×S为特征图网格的大小,分别为13,26,52。B为anchor的数量,为3。

宽高坐标损失

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_12

置信度损失

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_13

分类损失

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_14

这里的j,没有进行求和。因此3个anchor只取IOU>阈值且最大的那个anchor求损失,另外2个不需要。 z

yoloV3损失函数

在学习yolo之前,有必要学习下darknet的网络结构darknet53的网络结构笔记_2d_15