Yolo训练自己的数据集

Yolo训练自己的数据集

1
2
3
4
5
6
7
8
9
10
11
config:

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.09 Driver Version: 430.09 CUDA Version: 10.1 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 GeForce GTX 960M Off | 00000000:01:00.0 Off | N/A |
| N/A 52C P0 N/A / N/A | 0MiB / 2004MiB | 0% Default |
+-------------------------------+----------------------+----------------------+

错误递归逐渐忘记要干嘛。。。

yolo训练时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// cpu yolov3
xxxdeMacBook-Pro:darknet xxx$ ./darknet detect cfg/yolov3.cfg yolov3.weights 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
...
106 yolo
Loading weights from yolov3.weights...Done!
data/dog.jpg: Predicted in 18.616094 seconds.
bicycle: 99%
truck: 92%
dog: 100%
// cpu yolov2
xxxdeMacBook-Pro:darknet xxx$ ./darknet detect cfg/yolov2.cfg yolov2.weights 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
...
31 detection
mask_scale: Using default '1.000000'
Loading weights from yolov2.weights...Done!
data/dog.jpg: Predicted in 8.660725 seconds.
dog: 82%
truck: 64%
bicycle: 85%
// gpu yolov2, in another computer, but I didn't try cpu version
// at least 100x faster than cpu version
mei@mei-luo:~/Desktop/cv/darknet$ ./darknet detect cfg/yolov2.cfg yolov2.weights
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
...
31 detection
mask_scale: Using default '1.000000'
Loading weights from yolov2.weights...Done!
Enter Image Path: data/person.jpg
data/person.jpg: Predicted in 0.122470 seconds.
horse: 82%
dog: 86%
person: 86%

一、数据集预准备

VOC数据集目录结构如下:

1
2
3
4
5
6
7
├──VOC2019
├── Annotations
├── ImageSets
├── Layout
├── Main
└── Segmentation
└── JPEGImages

1.1 Annotations

存放VOC格式的xml文件,每一个xml对应一张图像,并且每个xml中存放的是标记的各个目标的位置和类别信息,命名通常与对应的原始图像一样

1.2 ImageSets

ImageSets我们只需要用到Main文件夹,这里面存放的是一些文本文件,通常为train.txt、test.txt等,该文本文件里面的内容是需要用来训练或测试的图像的名字(无后缀无路径)

1.3 JPEGImages

JPEGImages文件夹中放我们已按统一规则命名好的原始图像。

以此次我收集的关于泡面的数据集为例

数据来源:百度、京东、个人拍摄

1
2
3
4
5
6
7
8
9
10
├── JPEGImages
├── class_LR
├── LR00001.jpg
├── ...
├── class_XL
├── XL00001.jpg
├── ...
├── rename_voc.py
├── resize_pic.py
└── voc_spider.py

图片命名格式为<label_name><num(len=5)>.jpg,例如:LR00001.jpg

1.3.1 voc_spider.py

可以直接编译,爬取的图片存储在当前目录下。图片命名格式为<label_name><num(len=5)>.jpg,例如:LR00001.jpg

1.3.2 resize_pic.py

放缩图片大小,因为网络需要图片归一化,默认是处理成正方形的大小所以只需要输入一个数字,处理所在目录下的所有图片(递归式)。需要的图片尺寸大小应与yolo.cfg 中的大小一致。

1
2
3
4
5
6
7
8
9
# yolov2.cfg
[net]
# Testing
batch=1
subdivisions=1
...
width=608 !!!
height=608 !!!
...

1.3.3 rename_voc.py

寻找文件夹下命名为class_<label_name>的文件并根据序号重命名。这个主要是针对自己拍摄的照片进行处理时需要统一命名。

1.4 Conclusion

VOC数据集构建推荐步骤:

  1. 建立文件夹,目录结构如上述,建议命名为VOC+year,这样之后可以直接使用提供的voc_label.py
  2. JPEGImages下建立每个类的文件夹,我主要是为了方便区分,也不可以不分。
  3. 使用上述的py文件获取图片
  4. 最终所有图片都在JPEGImages文件夹下,不再另外区分。建议先做好这步!不然后期的xml文件夹还需要改动路径,不麻烦但是还是多了几步工作。

二、标记图像目标区域

跑opencv,报错了。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
xxxdeMacBook-Pro:main xxx$ python
Python 2.7.10 (default, Feb 22 2019, 21:17:52)
[GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.37.14)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/site-packages/cv2/__init__.py", line 89, in <module>
bootstrap()
File "/usr/local/lib/python2.7/site-packages/cv2/__init__.py", line 79, in bootstrap
import cv2
ImportError: dlopen(/usr/local/lib/python2.7/site-packages/cv2/python-2.7/cv2.so, 2): Library not loaded: /usr/local/opt/ffmpeg/lib/libavcodec.58.dylib
Referenced from: /usr/local/lib/libopencv_videoio.3.4.dylib
Reason: image not found
>>>

看了一下自己并没有安装 ffmpeg

1
xxxdeMacBook-Pro:main xxx$ brew install ffmpeg

我吹爆brew!原来尝试源码安装疯狂报错。。。

opencv能import了,然后numpy又报错了😭😭😭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
xxxdeMacBook-Pro:main xxx$ python
Python 2.7.10 (default, Feb 22 2019, 21:17:52)
[GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.37.14)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/site-packages/numpy/__init__.py", line 142, in <module>
from . import core
File "/usr/local/lib/python2.7/site-packages/numpy/core/__init__.py", line 71, in <module>
raise ImportError(msg)
ImportError:

IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE!

Importing the multiarray numpy extension module failed. Most
likely you are trying to import a failed build of numpy.
Here is how to proceed:
- If you're working with a numpy git repository, try `git clean -xdf`
(removes all files not under version control) and rebuild numpy.
- If you are simply trying to use the numpy version that you have installed:
your installation is broken - please reinstall numpy.
- If you have already reinstalled and that did not fix the problem, then:
1. Check that you are using the Python you expect (you're using /usr/bin/python),
and that you have no directories in your PATH or PYTHONPATH that can
interfere with the Python and numpy versions you're trying to use.
2. If (1) looks fine, you can open a new issue at
https://github.com/numpy/numpy/issues. Please include details on:
- how you installed Python
- how you installed numpy
- your operating system
- whether or not you have multiple versions of Python installed
- if you built from source, your compiler versions and ideally a build log

Note: this error has many possible causes, so please don't comment on
an existing issue about this - open a new one instead.

Original error was: No module named _multiarray_umath
xxxdeMacBook-Pro:main xxx$ sudo find /usr/local/lib/python* | grep numpy

看了一下自己文件里的numpy,发现在python3.7import是没有任何毛病的!干脆就把/usr/local/lib/python2.7/site-pacages/ 下的numpy删了,再测试了一下,ojbk👌

fine,我很ok的,我配环境配两天了,佛了,被环境工程劝退,又是头秃的一天啊!😃😃😃

推测一下出现这么多错可能是因为我同时存在了多个python的版本,有的包可能同时存在几个版本,容易冲突,这个标记的demo是在python2.7的环境下跑的,所以这些包我都尽量装在python2.7/site-pacages 目录下,使用pip指定目录安装

1
2
# pip install --target=<target_path> <pacakages>
xxxdeMacBook-Pro:main xxx$ pip install --target=/usr/local/lib/python2.7/site-pacages/ lxml

新的错,来自lxml,无法import etree,然鹅!python3.6下跑没有任何毛病= =

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
xxxdeMacBook-Pro:main xxx$ python
Python 2.7.10 (default, Feb 22 2019, 21:17:52)
[GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.37.14)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import lxml
>>> from lxml import etree
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name etree
>>>
xxxdeMacBook-Pro:main xxx$ python3
Python 3.6.8 (v3.6.8:3c6b436a57, Dec 24 2018, 02:04:31)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from lxml import etree
>>> etree.__version__
'4.3.3'
>>> exit()

注意:出错还有另外的可能是你的Python项目中有名称为lxml的文件(夹),就有可能导致这种错误。这种情况的话,把项目中对应的文件名称修改即可

曲线救国了

找到了另一个labelImg,好了!完美!

git:labelImg的git地址

1
2
3
4
5
6
# install lableImg in Mac OS X 10.14
pip3 install pyqt5 lxml # Install qt and lxml by pip

make qt5py3
python3 labelImg.py
python3 labelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE]

以下是xml的标准格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<annotation>
<folder>JPEGImages</folder>
<filename>LR00001.jpg</filename>
<path>/home/mei/Desktop/cv/darknet/scripts/VOCdevkit/NOODLE2019/JPEGImages/LR00001.jpg</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>608</width>
<height>608</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>luxiang</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>393</xmin>
<ymin>304</ymin>
<xmax>583</xmax>
<ymax>465</ymax>
</bndbox>
</object>
</annotation>

可能需要更改的地方:

  1. folder

    folder为最终跑模型的时候图片的放置位置。该命令运行在ubuntu 下

    1
    find -name '*.xml' |xargs perl -pi -e 's|<folder>class_LR|<folder>JPEGImages|g'
  2. filename

    可能导出的filename是没有文件后缀的,统一加上

    1
    find -name '*.xml' |xargs perl -pi -e 's|</filename>|.jpg</filename>|g'
  3. path

    path为图片在跑的模型上的绝对路径(因为我需要移植到另外一台电脑上跑)

    1
    find -name '*.xml' |xargs perl -pi -e 's|<path>$(your_original_pic_path)|<path>$(your_final_pic_path)|g'
  4. width, height

    有时候在Windows下用该工具label图像,可能会出现size那里的width和height都为0,如果在label之前已经归一化了图像大小那么就可以用下面两行命令来修改这个0值

    1
    2
    find -name '*.xml' |xargs perl -pi -e 's|0</width>|608</width>|g'
    find -name '*.xml' |xargs perl -pi -e 's|0</height>|608</height>|g'

三、用yolov2训练

3.1 生成相关文件

​ 按darknet的说明编译好后,接下来在darknet-master/scripts文件夹中新建文件夹VOCdevkit,然后将整个VOC2007文件夹都拷到VOCdevkit文件夹下。

​ 然后,需要利用scripts文件夹中的voc_label.py文件生成一系列训练文件和label,具体操作如下:

​ 首先需要修改voc_label.py中的代码,这里主要修改数据集名,以及类别信息,我的是VOC2019,并且所有样本用来训练,有两类目标,因此按如下设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...

#sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]

#classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]

sets=[('2019', 'train')]
classes = [ "luxiang", "xiangla"]

...

def convert_annotation(year, image_id):
in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))
#(如果使用的不是VOC而是自设置数据集名字,则这里需要修改)
out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, image_id), 'w')
#(同上)
...

for year, image_set in sets:
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()

修改好后在该目录下运行命令:python voc_label.py,之后则在文件夹script/VOCdevki/VOC2019下生成了文件夹labels

1
2
3
4
5
├──VOC2019
├── labels
├── LR00000.txt
├── LR00091.txt
├── ...

同时在scripts/下应该也生成了2019_train.txt这个文件,里面包含了所有训练样本的绝对路径。

❗️❗️❗️注意一下此时darknet/文件夹下是否也生成了train.txt 文件,如果有,检查一下是否为空,若为空就把 2019_train.txt 的内容移到 train.txt

3.2 配置文件修改

​ 做好了上述准备,就可以根据不同的网络设置(cfg文件)来训练了。在文件夹cfg中有很多cfg文件,应该跟caffe中的prototxt文件是一个意思。这里以yolov2.cfg‘为例,主要修改参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.
.
[convolutional]
size=1
stride=1
pad=1
filters=35
//修改最后一层卷积层核参数个数,计算公式是依旧自己数据的类别数filter=num×(classes + coords + 1)=5×(1+4+1)=30
activation=linear

[region]
anchors = 1.08,1.19,  3.42,4.41,  6.63,11.38,  9.42,5.11,  16.62,10.52
bias_match=1
classes=2 //类别数,本例为1类
coords=4
num=5
softmax=1
jitter=.2
rescore=1

object_scale=5
noobject_scale=1
class_scale=1
coord_scale=1

absolute=1
thresh = .6
random=1

​ 另外也可根据需要修改learning_rate、max_batches等参数。

​ 修改好了cfg文件之后,就需要修改两个文件,首先是data文件下的voc.names。打开voc.names文件可以看到有20类的名称,本例中只有一类,检测人,因此将原来所有内容清空,写上自己数据集对应的类名并保存。名字仍然用这个名字,如果喜欢用其他名字则请按一开始制作自己数据集的时候的名字来修改。

​ 接着需要修改cfg文件夹中的voc.data文件。也是按自己需求修改,我的修改之后是这样的画风:

  1. 1
    2
    3
    4
    5
    classes= 1  //类别数  
    train = /home/mei/Desktop/cv/darknet/scripts/2019_train.txt //训练样本的绝对路径文件,也就是上文2.1中最后生成的
    //valid = /home/pjreddie/data/voc/2007_test.txt //本例未用到
    names = data/voc.names //上一步修改的voc.names文件
    backup = backup/ //指示训练后生成的权重放在哪

修改后按原名保存最好,接下来就可以训练了。

3.3 运行训练

​ 上面完成了就可以命令训练了,可以在官网上找到一些预训练的模型作为参数初始值,也可以直接训练,训练命令为

1
$./darknet detector train ./cfg/voc.data cfg/tiny-yolo-voc.cfg

如果用官网的预训练模型darknet.conv.weights做初始化,则训练命令为

1
$./darknet detector train ./cfg/voc.data .cfg/tiny-yolo-voc.cfg darknet.conv.weights

不过我没试成功,加上这个模型直接就除了final,不知道啥情况。当然也可以用自己训练的模型做参数初始化,万一训练的时候被人终端了,可以再用训练好的模型接上去接着训练。

​ 训练过程中会根据迭代次数保存训练的权重模型,然后就可以拿来测试了,测试的命令同理:

1
./darknet detector test cfg/voc.data cfg/tiny-yolo-voc.cfg results/tiny-yolo-voc_6000.weights data/images.jpg

​ 这样就完成了整个流程。

给咱来个🍰,啾咪