Day 16 - 进行影像辨识训练
在进行训练的过程中,我们可能因为某些关系而中断训练,可能是网路断线或是电脑断电等问题,这时候我们可以用以下指令重新开始训练,简单来说就是把原先 darknet53.conv.74 的权重,换成最新一个训练权重结果,而后面是记录整个训练过程的纪录档,因为是用 -a 选项,所以也是会继续附加上去。
cd /home/ec2-user/fishRecognition/fishsite/FishRecognition
../darknet/darknet detector train cfg/obj.data cfg/yolov3.cfg weights/yolov3.backup | tee -a trainRecord.txt
最终,我们训练了 6 千多次,大约花了 5 个小时,因为晚上训练了一段时间,隔天早上又接着训练,所以没有记录确切时间。最终平均损失值约为 0.26 左右,已经是官方建议停止训练的范围 (0.05 ~ 3)内。接着要进行测试,看看训练结果如何,测试图片时需要有 data 这个目录,似乎是因为辨识完成后画图时需要有些图片在这个目录内,所以需要把这个目录建立一个链结到目前的目录里
ln -s ../darknet/data .
# 利用训练后的权重来进行模型测试
../darknet/darknet detector test cfg/obj.data cfg/yolov3.cfg weights/yolov3.backup ./labels/02-frame-608x608-0090.jpg
../darknet/darknet detector test cfg/obj.data cfg/yolov3.cfg weights/yolov3.backup ./labels/00-frame-608x608-0030.jpg
../darknet/darknet detector test cfg/obj.data cfg/yolov3.cfg weights/yolov3.backup ./labels/01-frame-608x608-0030.jpg
图 1、辨识鱼类目录的结构
测试后的输出结果,结果非常准确,照片中所有的鱼都有找到,且置信度很高。
图 2、测试的输出结果
下图是针对 3 种观赏鱼来进行辨识的结果,辨识时间很短,也都能准确找出来。
图 3、针对 3 种观赏鱼分别测试的结果
对于这样的结果很满意,但是主要的原因是样本数太少,且太相近,因为是从同一个影片撷取出来的,但只能说本次专案的目标本来就不在提供一个精准的结果,而是展示一个过程,以及展示一个全端架构,每一个部分都可以进行修改跟优化。以下内容是观察整个训练过程,观察平均损失的变化以及 IoU (交集与联集比,Intersection over Union),简单来说平均损失可以视为猜错的量,数值越大表示模型越不准;IoU 是指模型所找出的方块框,与物件所在的方块框,如果等于 1,那就表示模型找到的方块框与物件所在的方块框完全重叠,就算是没有,也希望越大越好,当然,范围就是介于 0-1 之间,下图就是 IoU 的示意图,可以想像 A 方块框就是物件所在方块,B 方块框是模型猜测方块。
图 4、IoU 的示意图
以下三只程式的目的是要分析训验记录档,并将结果以图形方式呈现:
- extract_log.py:将 trainRecord.txt 的文字内容进行转换处理,并分别存成:train_log_loss.txt 和 train_log_iou.txt。
- train_iou_visualization.py:分析 train_log_loss.txt ,将平均损失的优化过程视觉化。
- train_loss_visualization.py:分析 train_log_iou.txt ,将交集与联集比 (IoU )的优化过程视觉化。
extract_log.py
#!/usr/bin/python
#coding=utf-8
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 的同步记录;去除除零错误的记录
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('trainRecord.txt', 'train_log_loss.txt', 'images')
extract_log('trainRecord.txt', 'train_log_iou.txt', 'IOU')
下图就是 YOLOV3 训练时的纪录档 (trainRecord.txt),上面的程式就是依照关键字 images 来形成 train_log_loss.txt 文档; IOU 来形成 train_log_iou.txt。
图 5、YOLOV3 训练时的纪录档
下图是每次叠代损失的文字记录档 train_log_loss.txt,共 6298 行。
图 6、每次叠代损失的文字记录档
下图是优化 IoU 的文字记录档 train_log_iou.txt ,共 106451 行。
图 7、优化 IoU 的文字记录档
train_iou_visualization.py
#!/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 = 6331
start_ite = 1 #log_loss.txt 里面的最小叠代次数
end_ite = 6298 #log_loss.txt里面的最大叠代次数
step = 30 # 跳行数,决定画图的稠密程度
igore = 0 # 当开始的 loss 较大时,你需要忽略前 igore 次叠代,注意这里是叠代次数
y_ticks = [1, 100, 200, 300, 500, 800] #纵坐标的值,可以自己设置。
data_path = 'train_log_loss.txt' #log_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['loss'].values)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
x_num = len(result['loss'].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['loss'].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('loss')
下图中显示平均损失数量收敛的很快,在前200多次的叠代中就收敛 1 左右。
图 8、训练过程的平均损失变化
当我们忽略前 1000 次的叠代时,得到下图,可以发现训练过程中是有发生震荡的
igore = 1000 # 当开始的 loss 较大时,你需要忽略前 igore 次叠代,注意这里是叠代次数
图 9、训练过程2000次后的平均损失变化
train_loss_visualization.py
#!/usr/bin/python
#coding=utf-8
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#根据log_iou修改行数
lines = 106451
step = 5000
start_ite = 100
end_ite = 10000
igore = 100
data_path = 'train_log_iou.txt'#log_loss的路径。
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('iou')
下图显示的是 iou 的变化情形
图 10、训练过程中 IoU 的变化
参考资料
- YOLO-V3可视化训练过程中的参数,绘制loss、IOU、avg Recall等的曲线图, https://blog.csdn.net/qq_34806812/article/details/81459982
- Python影像辨識筆記(九之四):可視化YOLOv3訓練過程中的loss、IOU、avg Recall等的曲線, https://yanwei-liu.medium.com/python影像辨識筆記-九之四-可視化yolov3訓練過程中的loss-iou-avg-recall等的曲線圖-ef3d36daa5ec