上篇blog说了数据集制作的问题,然后?就开始train了。

一 参数含义

先理解下output的参数代表的含义吧。随便截了一个输出

yolox训练的训练权重 yolo训练loss值要到多少_Desktop

每一个batch都会输出一行信息。

6274:迭代次数

0.479518:当前batch的loss

0.625265 :avg loss,是平均Loss,这个数值应该越低越好,一般来说,一旦这个数值低于0.060730 avg就可以终止训练了。

rate  代表当前的学习率,是在.cfg文件中定义的。

seconds: 表示当前批次训练花费的总时间。

images: 这一行最后的这个数值是6274*64的大小,表示到目前为止,参与训练的图片的总量。

 

Region Avg IOU: 表示在当前subdivision内的图片的平均IOU,代表预测的矩形框和真实目标的交集与并集之比. 
Class: 标注物体分类的正确率,期望该值趋近于1。 
Obj: 越接近1越好。 
No Obj: 期望该值越来越小,但不为零。 
count: count后的值是所有的当前subdivision图片中包含正样本的图片的数量。
 

后面显示了所有训练图片的一个批次(batch),批次大小的划分根据我们在 .cfg 文件中设置的subdivisions参数。在我使用的 .cfg 文件中 batch = 64 ,subdivision = 16,所以在训练输出中,训练迭代包含了16组,每组又包含了4张图片,跟设定的batch和subdivision的值一致。

(注: 也就是说每轮迭代会从所有训练集里随机抽取 batch = 64 个样本参与训练,所有这些 batch 个样本又被均分为 subdivision = 16次送入网络参与训练,以减轻内存占用的压力)

二 train bug

第一个让我无奈的bug。。刚开始train什么都没干,过个几秒就出来了。

Segmentation fault(core dump)

(下述core dump的含义:操作系统把程序当掉时的内存内容 dump 出来(现在通常是写在一个叫 core 的 file 里面),让我们或是 debugger 做为参考。这个动作就叫作 core dump。)

懵逼半天,retry了n次 都是基本2-3分钟卡断一次,没办法只好找bug啦。关键还没有人遇到这个,因为段错误范围太大了,没报错具体信息,只好先找找能不能找到错误log。

使用core与gdb进行调试,先让系统生成core文件啦,然后加载gdb:

gdb ./darknet ./core

Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Core was generated by `./darknet detector train cfg/coco.data cfg/yolov3.cfg darknet53.conv.74 -gpus 0'.

Program terminated with signal SIGSEGV, Segmentation fault.

#0 0x000000000048f6b5 in get_yolo_box (x=0x412026c0, biases=0xc3bd70, n=8, index=1645163760, 

   i=15999984, j=15999984, lw=16, lh=16, w=512, h=512, stride=256) at ./src/yolo_layer.c:86

86      b.x = (i + x[index + 0*stride]) / lw;

[Current thread is 1 (Thread 0x7f0695be7700 (LWP 23910))]

我就看到了这些,一看这么大的下标肯定是哪里出问题了,可能是divide zero了。 在github上darknet的issue上找这个问题,有个老哥给了一个方案,查看labels中是否存在有zero值。

yolox训练的训练权重 yolo训练loss值要到多少_yolox训练的训练权重_02

然后无奈。因为我的json文件转换的代码是不可能出现0值的,于是就去迭代找bbox中是否有空值。妈耶果不其然:

yolox训练的训练权重 yolo训练loss值要到多少_yolox训练的训练权重_03

有16张图片bbox都为0,我去查看了对应的jpg,是背景图,进行了标注但是没有任何bbox的信息,yolov3的训练是不支持这样的图片的。

剔除了这16张图片对应的jpg和lables信息,重新生成了train.txt文件后,就可以正常训练了。

三 训练过程可视化

yolo会输出中间训练过程的值,但是我们想得到更全面的信息比如loss跟iou曲线的话,需要写一些脚本进行可视化。

参考blog 首先是在训练的过程的命令 要将训练输出的内容重定向到一个文件中 

./darknet detector train cfg/coco.data cfg/yolov3.cfg backup/yolov3.backup -gpus 0,1,2,3 2>1 | tee -a train.txt

注意输出的内容在stderr中,重定向中需要注意。之后就生成了对应的train的log文件。我是从5000次开始保存开启了多尺度训练,到50000次的时候大概有250M的log。文件,然后通过下面的代码去除nan值生成对应的loss.txt跟Iou.txt

#!/usr/bin/python
# 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:
            for line in f:
                # 去除多GPU的同步log;去除除零错误的log
                if ('Syncing' in line) or ('nan' in line):
                    continue
                if key_word in line:
                    train_log.write(line)
    f.close()
    train_log.close()


extract_log('/Users/zhangzhenghao/Desktop/train_yolov3.txt', '/Users/zhangzhenghao/Desktop/log_loss.txt', 'images')
extract_log('/Users/zhangzhenghao/Desktop/train_yolov3.txt', '/Users/zhangzhenghao/Desktop/log_iou.txt', 'IOU')

下图对应自己的log文件所在location,然后再通过下面两个代码进行图的绘制。

loss:

#!/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 = 4500
start_ite = 5201  # log_loss.txt里面的最小迭代次数
end_ite = 50200  # log_loss.txt里面的最大迭代次数
step = 10  # 跳行数,决定画图的稠密程度
igore = 0  # 当开始的loss较大时,你需要忽略前igore次迭代,注意这里是迭代次数

y_ticks = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1]  # 纵坐标的值,可以自己设置。
data_path = '/Users/zhangzhenghao/Desktop/log_loss.txt'  # log_loss的路径。
result_path = '/Users/zhangzhenghao/Desktop/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')

IOU:

#!/usr/bin/python
# coding=utf-8

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 根据log_iou修改行数
lines = 1736397
step = 5000
start_ite = 0
end_ite = 300000
igore = 1000
data_path = '/Users/zhangzhenghao/Desktop/log_iou.txt'  # log_loss的路径。
result_path = '/Users/zhangzhenghao/Desktop/Region_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)

对应的结果:我的是从5000次到5w次的迭代:

yolox训练的训练权重 yolo训练loss值要到多少_python_04

yolox训练的训练权重 yolo训练loss值要到多少_迭代_05

四 关于模型的保存

源码中:

迭代次数小于1000时,每100次保存一次,大于1000时,每10000次保存一次。

自己可以根据需求进行更改,然后重新编译即可[ 先 make clean ,然后再 make]。

代码位置: examples/detector.c line 138

yolox训练的训练权重 yolo训练loss值要到多少_Desktop_06

这里如果迭代次数多的话建议大家5000次保存一次result,我就出现了4万次迭代的mAP比5万次的好,中间是不是有更好的就不清楚了。