YOLO.V3-Darknet下的学习笔记
@wp20180927
【目录】
一、 安装Darknet(仅CPU下) 2
1.1在CPU下安装Darknet方式 2
1.2在GPU下安装Darknet方式 4
二、 YOLO.V3训练官网数据集(VOC数据集/COCO数据集) 4
2.1下载VOC数据集/COCO数据集 4
2.2下载预训练的模型(.weights文件) 8
三、 YOLO.V3训练自己的数据集(以3类别的为例) 8
3.1制作自己的VOC格式训练集 8
3.1.1图像(.jpg)进行重命名(00000X.jpg) 9
3.1.2制作图像的.xml文件 11
3.1.3将.xml文件转换成.txt文件【表述图像及框的四点坐标信息】 14
3.1.4将.jpg文件转换成.txt文件【训练、测试、验证数据集的路径】 15
3.2训练网络 17
3.3测试 19
3.4计算准确率 20
3.5问题汇集 20
四、 YOLO.V3的可视化 20
4.0格式化训练的日志文件log(用extract_log.py脚本) 21
4.1可视化训练日志中的loss(用train_loss_visualization.py脚本) 21
4.2可视化训练日志中的参数(用train_iou_visualization.py脚本) 24
【正文】
一、安装Darknet(仅CPU下)
安装环境:Ubuntu18.04
内存:3.9GiB,处理器:Intel® Core™2 Duo CPU E7500 @ 2.93GHz × 2
图形:Intel® Core™2 Duo CPU E7500 @ 2.93GHz × 2
GNOME:3.28.3
操作系统:64位
磁盘:492GB.
【说明:以下安装主要参考YOLO官网https://pjreddie.com/darknet/yolo/ 】
1.1在CPU下安装Darknet方式
官网安装:https://pjreddie.com/darknet/install/Darknet
安装相对caffe来说非常简单。Opencv和cuda作为选装的部分,可以不安装。推荐安装这两个,只有安装的cuda才能使用gpu。提示:只有安装了opencv才能调用摄像头进行实时检测。
Opencv和cuda的安装方法,可以参考安装caffe时的安装方法:可以是自己以前安装Caffe时安装Opencv的方法,也可以参考 http://blog.csdn.net/vvyuervv/article/details/60755653或者 https://blog.csdn.net/samylee/article/details/51684856/ 。
Darknet的安装简单来说就三个步骤:下载安装包+修改Makefile文件+编译。
(1)下载安装包:
使用命令行下载的命令:git clone https://github.com/pjreddie/darknet.git。
当然也可以直接输入网址进行下载: https://github.com/pjreddie/darknet.git
(2)修改Makefile文件:
下载之后打开文件夹里的Makefile文件,如果安装了opencv和cuda,则将Makefile文件中的GPU和OPENCV修改成1 (GPU=1,OPENCV=1)。如果没有安装opencv和cuda就不需要进行修改之后就是对安装包进行编译了。提示:安装opencv需要联网,耗时较长。
我自己使用CPU+opencv,所以修改CPU=0,opencv=1。
(3)编译make。
使用命令cd到文件根目录下make这样就可以了,没有安装opencv和cuda的情况下编译很快,安装了的编译时稍等一会儿。安装过caffe的都知道,编译过程中经常会出现错误。不过编译darknet时不用担心。
$ cd darknet
$ make
(4)测试YOLO-Darknet
测试yolov3.weights模型。将训练得到的weights文件拷贝到darknet/weights文件夹下面,执行语句:
$ wget https://pjreddie.com/media/files/yolov3.weights #下载模型
$ cd darknet
$ ./darknet detect cfg/yolov3-voc.cfg weights/yolov3.weights data/dog.jpg
也可以执行:
$ wget https://pjreddie.com/media/files/yolov3.weights #下载模型
$ cd darknet
$ ./darknet detect cfg/yolov3-voc.cfg weights/yolov3.weights
终端下,过一会儿会显示
Loading weights from weights/yolov3.weights...Done!
Enter Image Path: data/person.jpg
data/person.jpg: Predicted in 26.276781 seconds.
备注:这里可能会报错
./darknet: error while loading shared libraries: libopencv_core.so.2.4: cannot open shared object file: No such file or directory
处理方法:
在darknet目录下先meak clean清除掉原有编译文件,然后sudo ldconfig然后make一下后重新输入:
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
便可成功运行。
运行成功后会出现下面的图片,并在darknet目录下生成predictions.png文件
(5)训练测试YOLO-Darknet(这里是继续训练)
$ cd darknet
$ ./darknet detector train VOC/cfg/voc.data VOC/cfg/yolov3-voc.cfg VOC/darknet53.conv.74
1.2在GPU下安装Darknet方式
(1)下载安装包。(2)修改GPU=1。(3)make编译一下。
二、YOLO.V3训练官网数据集(VOC数据集/COCO数据集)
2.1下载VOC数据集/COCO数据集
下载数据集
新建文件夹VOC:/home/wp/darknet/VOC,在VOC文件夹下下载数据集:
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
tar xf VOCtrainval_11-May-2012.tar
tar xf VOCtrainval_06-Nov-2007.tar
tar xf VOCtest_06-Nov-2007.tar
请,参考官网例子https://pjreddie.com/darknet/yolo/ 下的”Training YOLO on VOC”
There will now be a VOCdevkit/ subdirectory with all the VOC training data in it.
Generate Labels for VOC
Now we need to generate the label files that Darknet uses. Darknet wants a .txt file for each image with a line for each ground truth object in the image that looks like:
<object-class> <x> <y> <width> <height>
Where x, y, width, and height are relative to the image's width and height. To generate these file we will run the voc_label.py script in Darknet's scripts/ directory. Let's just download it again because we are lazy.
$ wget https://pjreddie.com/media/files/voc_label.py
$ python voc_label.py
After a few minutes, this script will generate all of the requisite files. Mostly it generates a lot of label files in VOCdevkit/VOC2007/labels/ and VOCdevkit/VOC2012/labels/. In your directory you should see:
$ 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
The text files like 2007_train.txt list the image files for that year and image set. Darknet needs one text file with all of the images you want to train on. In this example, let's train with everything except the 2007 test set so that we can test our model. Run:
$ cat 2007_train.txt 2007_val.txt 2012_*.txt > train.txt
Now we have all the 2007 trainval and the 2012 trainval set in one big list. That's all we have to do for data setup!
Modify Cfg for Pascal Data
Now go to your Darknet directory. We have to change the cfg/voc.data config file to point to your data:
$ classes= 20
$ train = <path-to-voc>/train.txt
$ valid = <path-to-voc>2007_test.txt
$ names = data/voc.names
$ backup = backup
You should replace <path-to-voc> with the directory where you put the VOC data.
Download Pretrained Convolutional Weights
For training we use convolutional weights that are pre-trained on Imagenet. We use weights from the darknet53 model. You can just download the weights for the convolutional layers here (76 MB).
$ wget https://pjreddie.com/media/files/darknet53.conv.74
Train The Model
Now we can train! Run the command:
$ ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
自己的详细步骤如下:
(1)下载VOC数据集并解压
$ cd /home/wp/darknet/VOC
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
tar xf VOCtrainval_11-May-2012.tar
tar xf VOCtrainval_06-Nov-2007.tar
tar xf VOCtest_06-Nov-2007.tar
VOC数据集的文件夹详细介绍:
(2)制作.jpg数据集的.txt文件
$ cd darknet/VOC
$ wget https://pjreddie.com/media/files/voc_label.py
$ python voc_label.py
执行完后,生成2007_test.txt;2007_train.txt;2007_val.txt;2012_train.txt;2012_val.txt
(3) 修改 cfg/voc.data文件里的路径
classes= 20
train = /home/wp/darknet/VOC/train.txt
valid = /home/wp/darknet/VOC/2007_test.txt
names = /home/wp/darknet/VOC/data/voc.names
backup = /home/wp/darknet/backup
2.2下载预训练的模型(.weights文件)
(1)训练
1.在已训练好的模型上继续训练:
$ cd darknet
$ wget https://pjreddie.com/media/files/darknet53.conv.74 #先下载模型,再使用
$ ./darknet detector train VOC/cfg/voc.data VOC/cfg/yolov3-voc.cfg VOC/darknet53.conv.74
2.自己重新训练
为方便后面的可视化,这里最好保存训练的日志。
$ ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg 2>&1 | tee visualization/train_yolov3.log
说明:下载可以执行语句执行(wget+网址链接),也可以手动下载(到网页下载)。
三、YOLO.V3训练自己的数据集(以3类别的为例)
3.1制作自己的VOC格式训练集
核心思想:仿照VOC数据集的文件夹及其格式进行制作。
--VOC
--Annotations
--ImageSets
--Main
--Layout
--Segmentation
--JPEGImages
--SegmentationClass
--SegmentationObject
说明:这里面用到的文件夹是Annotation、ImageSets和JPEGImages。其中文件夹Annotation中主要存放xml文件,每一个xml对应一张图像,并且每个xml中存放的是标记的各个目标的位置和类别信息,命名通常与对应的原始图像一样;而ImageSets我们只需要用到Main文件夹,这里面存放的是一些文本文件,通常为train.txt、test.txt等,该文本文件里面的内容是需要用来训练或测试的图像的名字(无后缀无路径);JPEGImages文件夹中放我们已按统一规则命名好的原始图像。
因此,需要:
1.新建文件夹VOC2007(通常命名为这个,也可以用其他命名,但一定是名字+年份,例如MYDATA2016,无论叫什么后面都需要改相关代码匹配这里,本例中以VOC2007为例)
2.在VOC2007文件夹下新建三个文件夹Annotation、ImageSets和JPEGImages,并把准备好的自己的原始图像放在JPEGImages文件夹下
3.在ImageSets文件夹中,新建三个空文件夹Layout、Main、Segmentation,然后把写了训练或测试的图像的名字的文本拷到Main文件夹下,按目的命名,我这里所有图像用来训练,故而Main文件夹下只有train.txt文件。上面说的小代码运行后会生成该文件,把它拷进去即可。
3.1.1图像(.jpg)进行重命名(00000X.jpg)
方法:用python、C++、MATLAB等写一个重命名的脚本,批量修改。也可以借助美图秀秀、ACDsee工具修改。
结果:在/home/wp/darknet/wp_data/VOCdevkit/VOC2012/JPEGImages文件夹下产生一系列重命名的00000x.jpg图像。
可以参考网址https://blog.csdn.net/u011574296/article/details/72956446。
首先准备好自己的数据集,最好固定格式,此处以VOC为例,采用jpg格式的图像,在名字上最好使用像VOC一样类似000001.jpg、000002.jpg这样。
本文用的是images_rename_001.py,内容如下:
=====================================================
# -*- coding: utf-8 -*-
#!/usr/bin/python
import os
path = "/home/wp/darknet/wp_data/VOCdevkit/VOC2012/JPEGImages"
filelist = os.listdir(path) #该文件夹下所有的文件(包括文件夹)
count=0
for file in filelist:
print(file)
for file in filelist: #遍历所有文件
Olddir=os.path.join(path,file) #原来的文件路径
if os.path.isdir(Olddir): #如果是文件夹则跳过
continue
filename=os.path.splitext(file)[0] #文件名
filetype=os.path.splitext(file)[1] #文件扩展名
Newdir=os.path.join(path,str(count).zfill(6)+filetype) #用字符串函数zfill,以0补全所需位数
os.rename(Olddir,Newdir) #重命名
count+=1
====================================================
Matlab版本
%%
%图片保存路径为:
%E:\image\car
%E:\image\person
%car和person是保存车和行人的文件夹
%这些文件夹还可以有多个,
%放在image文件夹里就行
%该代码的作用是将图片名字改成000123.jpg这种形式
%%
clc;
clear;
maindir='E:\image\';
name_long=6; %图片名字的长度,如000123.jpg为6,最多9位,可修改
num_begin=1; %图像命名开始的数字如000123.jpg开始的话就是123
subdir = dir(maindir);
n=1;
for i = 1:length(subdir)
if ~strcmp(subdir(i).name ,'.') && ~strcmp(subdir(i).name,'..')
subsubdir = dir(strcat(maindir,subdir(i).name));
for j=1:length(subsubdir)
if ~strcmp(subsubdir(j).name ,'.') && ~strcmp(subsubdir(j).name,'..')
img=imread([maindir,subdir(i).name,'\',subsubdir(j).name]);
imshow(img);
str=num2str(num_begin,'%09d');
newname=strcat(str,'.jpg');
newname=newname(end-(name_long+3):end);
system(['rename ' [maindir,subdir(i).name,'\',subsubdir(j).name] ' ' newname]);
num_begin=num_begin+1;
fprintf('当前处理文件夹%s',subdir(i).name);
fprintf('已经处理%d张图片\n',n);
n=n+1;
pause(0.1);%可以将暂停去掉
end
end
end
end
==================================
3.1.2制作图像的.xml文件
方法:借助LabelImg工具,手动制作。说明:因为是一张张的手动制作,数据集大的话,需要一定的时间。
结果:在/home/wp/darknet/wp_data/VOCdevkit/VOC2012/Annotations文件夹下产生一系列与图像名称相对应的0000xx.xml文件。
可以参考网址https://blog.csdn.net/xunan003/article/details/78720189,来实现标记图像目标区域。
(1)下载labelImg-master.zip并解压
方式1:下载地址https://github.com/tzutalin/labelImg。
方式2:使用git命令
git clone https://github.com/tzutalin/labelImg
下载后是自动在home目录下解压好的,文件名为LabelImg,里面的内容同上图。
(2)labelImg安装
$ sudo apt-get install pyqt4-dev-tools #安装PyQt4
$ sudo pip install lxml #安装lxml,如果报错,可以试试下面语句
$ sudo apt-get install python-lxml #然后打开终端,进入LabelImg目录后使用make编译
不出错误的话,继续执行:
$ cd LabelImg
$ make all
(3)
labelImg使用
在labelImg目录下使用终端执行:
$ python labelImg.py
3.1快捷键
Ctrl + u 加载目录中的所有图像,鼠标点击Open dir同功能
Ctrl + r 更改默认注释目标目录(xml文件保存的地址)
Ctrl + s 保存
Ctrl + d 复制当前标签和矩形框
space 将当前图像标记为已验证
w 创建一个矩形框
d 下一张图片
a 上一张图片
del 删除选定的矩形框
Ctrl++ 放大
Ctrl-- 缩小
↑→↓← 键盘箭头移动选定的矩形框
3.2具体事项
想要修改图2中的标签类别内容(如默认的dog、person、cat等)则在主目录下data文件夹中的predefined_classes.txt文件中修改。
使用时,使用ctrl+u快捷键加载图片后,使用ctrl+r快捷键指定生成的xml文件的保存位置,然后开始按照类别将图片中的目标进行矩形框标注,每标注一个目标后软件自动弹出类别信息以供选择,在弹出的类别信息中选择对应的类别名称双击即可。当一张图片中各个类别所需要标注的目标全部标注后,点击保存按键或者使用ctrl+s快捷键保存就生成了相对应的xml位置信息文件,此时可以开始下一张图片的标注。
3.3注意
现在github上更新的最新版本可能在某些使用较久的老电脑上无法使用,原因不知,如遇到新版本无法使用,请首先考虑使用labelImg的老版本使用(老版本地址:http://pan.baidu.com/s/1b5qyke密码:jkz0),我们这里提供百度云地址。另外,老版本中在4当中提到的某些快捷键无法使用,请用鼠标点击使用相同功能。另外,如果.xml文件里filename中的文件名没有.jpg后缀,则需要统一加上后缀.jpg。只需一段命令即可find -name '*.xml' |xargs perl -pi -e 's|</filename>|.jpg</filename>|g' 。在对应目录下执行即可,这样就可以把后缀添上。这样就做按照VOC做好了我们的数据集,接下来就是放到算法中去训练跑起来。
(4)Labelimg窗口的使用方法:
• 修改默认的XML文件保存位置,可以用“Ctrl+R”,改为自定义位置,这里的路径不能包含中文,否则无法保存。
• 源码文件夹中使用notepad++打开data/predefined_classes.txt,可以修改默认类别,比如改成bus、car、building三个类别。
•“Open Dir”打开需要标注的样本图片文件夹,会自动打开第一张图片,开始进行标注
• 使用“Create RectBox”开始画框
• 完成一张图片后点击“Save”,此时XML文件已经保存到本地了。
• 点击“Next Image”转到下一张图片。
• 标注过程中可随时返回进行修改,后保存的文件会覆盖之前的。
• 完成标注后打开XML文件,发现确实和PASCAL VOC所用格式一样。
每个图片和标注得到的xml文件,JPEGImages文件夹里面的一个训练图片,对应Annotations里面的一个同名XML文件,一一对应,命名一致。
标注自己的图片的时候,类别名称请用小写字母,比如汽车使用car,不要用Car
pascal.py中读取.xml文件的类别标签的代码:
cls = self._class_to_ind[obj.find('name').text.lower().strip()]
写的只识别小写字母,如果你的标签含有大写字母,可能会出现KeyError的错误。
3.1.3将.xml文件转换成.txt文件【表述图像及框的四点坐标信息】
方法:写一个make_xml2txt.py脚本。
结果:/home/wp/darknet/wp_data/VOCdevkit/VOC2012/labels文件夹下产生与图像名称相对应的00000x.txt文件,记录了图像的类别+画框的位置坐标信息。
============make_xml2txt.py==================================
import os
import random
trainval_percent = 0.66
train_percent = 0.5
xmlfilepath = '/home/wp/darknet/wp_data/VOCdevkit/VOC2012/Annotations'
txtsavepath = '/home/wp/darknet/wp_data/VOCdevkit/VOC2012/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('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/ImageSets/Main/trainval.txt', 'w')
ftest = open('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/ImageSets/Main/test.txt', 'w')
ftrain = open('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/ImageSets/Main/train.txt', 'w')
fval = open('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/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.2制作图像的.xml文件】VS【3.1.3将.xml文件转换成.txt文件】
说明:YOLO-V3 XML和TXT标注文本解释。
【.xml】包含了图像名称、图像路径、图像size和深度、标记框的坐标信息
【.txt】包含了<object-class> <x> <y> <width> <height>
参考https://blog.csdn.net/qq_34806812/article/details/82355614
3.1.4将.jpg文件转换成.txt文件【训练、测试、验证数据集的路径】
方法:利用voc_label.py脚本,修改相应的信息即可为我们所用。
结果:生成了2012_train.txt、2012_test.txt、2012_val.txt等文件,由voc_label.py设置决定。其中xxx_*.txt记录的是图像的绝对路径。
=====================voc_label.py==or==make_jpg2txt.py=======
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=[('2012', 'train'), ('2012', 'val'), ('2012', 'test')] ###############
classes = ["person", "dog", "cat"] ########################
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('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/Annotations/%s.xml'%(image_id))
out_file = open('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/labels/%s.txt'%(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() #get current path
for image_set in sets:
if not os.path.exists('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/labels/'):
os.makedirs('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/labels/')
image_ids = open('/home/wp/darknet/wp_data/VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(image_set)).read().strip().split()
list_file = open('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/%s_%s.txt'%(image_set), 'w')
#list_file = open('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/voc_train.txt', 'w')
for image_id in image_ids:
list_file.write('/home/wp/darknet/wp_data/VOCdevkit/VOC2012/JPEGImages/%s.jpg\n'%(image_id))
convert_annotation(image_id)
list_file.close()
os.system("cat /home/wp/darknet/wp_data/VOCdevkit/VOC2012/2012_train.txt /home/wp/darknet/wp_data/VOCdevkit/VOC2012/2012_val.txt > /home/wp/darknet/wp_data/VOCdevkit/VOC2012/train.txt")
os.system("cat /home/wp/darknet/wp_data/VOCdevkit/VOC2012/2012_train.txt /home/wp/darknet/wp_data/VOCdevkit/VOC2012/2012_val.txt /home/wp/darknet/wp_data/VOCdevkit/VOC2012/2012_test.txt > /home/wp/darknet/wp_data/VOCdevkit/VOC2012/train_all.txt")
#os.system("cat 2012_train.txt 2012_val.txt > train.txt")
#os.system("cat 2012_train.txt 2012_val.txt 2012_test.txt > train_all.txt")
========================================================
3.2训练网络
首先,建立所需的配置文件voc.data、yolov3-voc.cfg、data/voc.names。
(1)建立cfg文件下的voc.data和yolov3-voc.cfg。建立data/voc.names。
可以参考voc数据集里的voc.data、yolov3-voc.cfg、voc.names,拷贝过来做修改。
(2)修改cfg文件下的voc.data和yolov3-voc.cfg.
第一步:修改voc.data
==================修改voc.data==================
classes= 3 #有几类就改成几类
train = /home/wp/darknet/wp_data/VOCdevkit/VOC2012/2012_train.txt
valid = /home/wp/darknet/wp_data/VOCdevkit/VOC2012/2012_test.txt
names = /home/wp/darknet/wp_data/data/voc.names
backup = /home/wp/darknet/wp_data/results
=============================================
第二步:修改yolov3-voc.cfg.
==================修改yolov3-voc.cfg.==================
注意:重点修改几点
- learning_rate、max_batches等参数.
- 训练阶段。注释掉testing,打开train。
[net]
# Testing
#batch=1
#subdivisions=1
# Training
batch=1 ####
subdivisions=1 ####
3.共三个yolo层都要改,yolo层中的class为类别数,每一个yolo层前的conv层中的filters =(类别+5)* 3,电脑内存不够的话设置random=0。
类似这样的位置3处。
[convolutional]
size=1
stride=1
pad=1
filters=24 ###75
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=3 ###20
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=0 ###1
=============================================
第三步:修改voc.names.
==================修改voc.names==================
person
dog
cat
改成自己的类别。也可以把类别设置成简单的1、2、3.....等。
1
2
3
=============================================
接着,开始训练。
在终端下开始执行:
$ cd darknet
$ ./darknet detector train wp_data/cfg/voc.data wp_data/cfg/yolov3-voc.cfg 2>&1 | tee wp_data/visualization/train_yolov3.log #保存训练日志
或者
$ ./darknet detector train wp_data/cfg/voc.data wp_data/cfg/yolov3-voc.cfg #不保存训练日志
或者
$ nohup ./darknet detector train cfg/voc_birds.data cfg/yolov3-voc-birds.cfg darknet53.conv.74 2>1 | tee visualization/train_yolov3_birds.log &
其中,
nohup $ 是防止因为ssh断开而中断服务器的进程(如果出现“找不到nohup文件”的错误,去掉命令中的“nohup ... &”)
2>1 | tee visualization/train_yolov3_birds.log 是为了保留训练中的log,为了后续绘制loss曲线。
训练完后的权重文件,weights应该保存到了darknet/backup下,其中保存的这个模型可以用于继续训练。(还没跑完过,待验证)
3.3测试
训练好后可以在backup看到权重文件,尝试test前要修改cfg文件,切换到test模式:
==================修改yolov3-voc.cfg.==================
测试阶段。注释掉train,打开testing。
[net]
# Testing
batch=1
subdivisions=1
# Training
# batch=1 ####
# subdivisions=1 ####
===================================================
开始测试:
$ ./darknet detector test cfg/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc.weights
执行后,测试集里的图片会一张张的显示出预测结果。
3.4计算准确率
darknet分类没有像caffe一样训练的同时日志文件输出测试集的准确率,但是提供了valid函数用于输出top-1准确率。
$ ./darknet classifier valid cfg/door.data cfg/darknet19.cfg classify_result/darknet19_18.weights
用相同的数据集对比caffe、pytorch框架,用近似的网络训练,darknet框架训练的平均loss更低,准确率更高,模型的泛化性也更好。对比caffe,没有第三方库,c也更方便移植到嵌入式平台。
3.5问题汇集
(1)Error: out of memory
原因:1. 可能是有人占用资源,查查后台进程
2. batch size过大,超出了显卡能够承受的范围。可以适当改小cfg文件中的batch, 同时让batch和subdivisions保持在一个比较合适的比例,每次传入的图片数量=进行forward propagation的图片数量=batch/subdivisions,进行backward propagation的图片数量=batch (我的理解是这样,如果不对欢迎指正)
解决:增大cfg文件中subdivisions,16、32或者64(最多为batch值)
subdivision:这个参数很有意思的,它会让你的每一个batch不是一下子都丢到网络里。而是分成subdivision对应数字的份数,一份一份的跑完后,在一起打包算作完成一次iteration。这样会降低对显存的占用情况。如果设置这个参数为1的话就是一次性把所有batch的图片都丢到网络里,如果为2的话就是一次丢一半。
(2)其他参考“Darknet YOLO 训练问题集锦”
网址:https://blog.csdn.net/Pattorio/article/details/80051988
四、YOLO.V3的可视化
Yolo.v3训练日志可视化,主要是loss和iou曲线的可视化,是查看训练效果的重要依据。等待训练结束后(有时还没等训练结束,模型就开始发散了),因此需要检测各项指标(如loss)是否达到了我们的预期期望值;如果没有,要分析为什么。
可视化训练过程的中间参数,可以帮助我们分析问题,可视化中间参数需要用到训练时保存的log文件(命令中的路径根据自己实际修改):
$ cd darknet
$ ./darknet detector train wp_data/cfg/voc.data wp_data/cfg/yolov3-voc.cfg 2>&1 | tee wp_data/visualization/train_yolov3.log
4.0格式化训练的日志文件log(用extract_log.py脚本)
在使用脚本绘制变化曲线之前,需要先使用extract_log.py脚本,格式化log,用生成的新的log文件供可视化工具绘图,格式化log的extract_log.py脚本如下(和生成的log文件同一目录):
====================================================
# 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')
====================================================
运行之后,会解析log文件的loss行和iou行得到两个txt文件.
4.1可视化训练日志中的loss(用train_loss_visualization.py脚本)
train_loss_visualization.py脚本如下(也是同一目录新建py文件):
====================================================
# coding=utf-8
#根据train_log_loss.txt行数修改lines行
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#%matplotlib inline
#lines =9873
lines=25100
result = pd.read_csv('train_log_loss.txt', skiprows=[x for x in range(lines)] ,error_bad_lines=False, names=['loss', 'avg', 'rate', 'seconds', 'images'])
result.head()
result['loss']=result['loss'].str.split(' ').str.get(1)
result['avg']=result['avg'].str.split(' ').str.get(1)
result['rate']=result['rate'].str.split(' ').str.get(1)
result['seconds']=result['seconds'].str.split(' ').str.get(1)
result['images']=result['images'].str.split(' ').str.get(1)
result.head()
result.tail()
#print(result.head())
# print(result.tail())import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#%matplotlib inline
lines =9873
result = pd.read_csv('train_log_loss.txt', skiprows=[x for x in range(lines) if ((x<1000))] ,error_bad_lines=False, names=['loss', 'avg', 'rate', 'seconds', 'images'])
result.head()
result['loss']=result['loss'].str.split(' ').str.get(1)
result['avg']=result['avg'].str.split(' ').str.get(1)
result['rate']=result['rate'].str.split(' ').str.get(1)
result['seconds']=result['seconds'].str.split(' ').str.get(1)
result['images']=result['images'].str.split(' ').str.get(1)
result.head()
result.tail()
#print(result.head())
# print(result.tail())
# print(result.dtypes)
print(result['loss'])
print(result['avg'])
print(result['rate'])
print(result['seconds'])
print(result['images'])
result['loss']=pd.to_numeric(result['loss'])
result['avg']=pd.to_numeric(result['avg'])
result['rate']=pd.to_numeric(result['rate'])
result['seconds']=pd.to_numeric(result['seconds'])
result['images']=pd.to_numeric(result['images'])
result.dtypes
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(result['avg'].values,label='avg_loss')
#ax.plot(result['loss'].values,label='loss')
ax.legend(loc='best')
ax.set_title('The loss curves')
ax.set_xlabel('batches')
fig.savefig('avg_loss')
#fig.savefig('loss')
# print(result.dtypes)
print(result['loss'])
print(result['avg'])
print(result['rate'])
print(result['seconds'])
print(result['images'])
result['loss']=pd.to_numeric(result['loss'])
result['avg']=pd.to_numeric(result['avg'])
result['rate']=pd.to_numeric(result['rate'])
result['seconds']=pd.to_numeric(result['seconds'])
result['images']=pd.to_numeric(result['images'])
result.dtypes
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(result['avg'].values,label='avg_loss')
#ax.plot(result['loss'].values,label='loss')
ax.legend(loc='best')
ax.set_title('The loss curves')
ax.set_xlabel('batches')
fig.savefig('avg_loss')
#fig.savefig('loss')
====================================================
修改train_loss_visualization.py中lines为train_log_loss.txt行数,并根据需要修改要跳过的行数:
skiprows=[x for x in range(lines) if ((x%10!=9) |(x<1000))]
运行train_loss_visualization.py会在脚本所在路径生成avg_loss.png。可以通过分析损失变化曲线,修改cfg中的学习率变化策略。
4.2可视化训练日志中的参数(用train_iou_visualization.py脚本)
可视化’Region Avg IOU’, ‘Class’, ‘Obj’, ‘No Obj’, ‘Avg Recall’,’count’这些参数可以使用脚本train_iou_visualization.py,使用方式和train_loss_visualization.py相同,train_iou_visualization.py脚本如下(#lines根据train_log_iou.txt的行数修改):
====================================================
# coding=utf-8
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#%matplotlib inline
lines = 122956 #根据train_log_iou.txt的行数修改
result = pd.read_csv('train_log_iou.txt', skiprows=[x for x in range(lines) if (x%10==0 or x%10==9) ] ,error_bad_lines=False, names=['Region Avg IOU', 'Class', 'Obj', 'No Obj', 'Avg Recall','count'])
result.head()
result['Region Avg IOU']=result['Region Avg IOU'].str.split(': ').str.get(1)
result['Class']=result['Class'].str.split(': ').str.get(1)
result['Obj']=result['Obj'].str.split(': ').str.get(1)
result['No Obj']=result['No Obj'].str.split(': ').str.get(1)
result['Avg Recall']=result['Avg Recall'].str.split(': ').str.get(1)
result['count']=result['count'].str.split(': ').str.get(1)
result.head()
result.tail()
# print(result.head())
# print(result.tail())
# print(result.dtypes)
print(result['Region Avg IOU'])
result['Region Avg IOU']=pd.to_numeric(result['Region Avg IOU'])
result['Class']=pd.to_numeric(result['Class'])
result['Obj']=pd.to_numeric(result['Obj'])
result['No Obj']=pd.to_numeric(result['No Obj'])
result['Avg Recall']=pd.to_numeric(result['Avg Recall'])
result['count']=pd.to_numeric(result['count'])
result.dtypes
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(result['Region Avg IOU'].values,label='Region Avg IOU')
# ax.plot(result['Class'].values,label='Class')
# ax.plot(result['Obj'].values,label='Obj')
# ax.plot(result['No Obj'].values,label='No Obj')
# ax.plot(result['Avg Recall'].values,label='Avg Recall')
# ax.plot(result['count'].values,label='count')
ax.legend(loc='best')
# ax.set_title('The Region Avg IOU curves')
ax.set_title('The Region Avg IOU curves')
ax.set_xlabel('batches')
# fig.savefig('Avg IOU')
fig.savefig('Region Avg IOU')
====================================================
运行train_iou_visualization.py会在脚本所在路径生成相应的曲线图。
---20180927第一次实战笔记
---后续根据实战在更新