这个现在太火了,蹭个热度写一下!###待完成的项目
官方代码
我这回打算详细分析一下,具体是根据这个tf版来写,因为这个项目还复现了trt版本!
需要weight的可以到我百度云里面下载:提取码:hwht
[net]
# Testing #测试模式
#batch=1
#subdivisions=1
# Training#训练模式 每次前向图片的数目=batch/subdivisions
batch=64###批大小.一个epoch要训练完训练集中的所有样本,样本总数记为num_train;故此可以确定迭代次数iteration=num_train/batch.
subdivisions=16
★★★ 在Darknet中,batch和sub是结合使用的,例如这儿的batch=64,sub=16表示训练的过程中将一次性加载64张图片进内存,然后分16次完成前向传播,意思是每次64/16=4张,前向传播的循环过程中累加loss求平均,待64张图片都完成前向传播后,再一次性后传更新参数
★★★ 调参经验:sub一般设置16(custom里面设置为16),不能太大或太小,且为8的倍数,其实也没啥硬性规定,看着舒服就好 batch的值可以根据显存占用情况动态调整,一次性加减sub大小即可,通常情况下batch越大越好,还需注意一点,在测试的时候batch和sub都设置为1,避免发生神秘错误!
width=608 ### input图像的宽
height=608 ### input图像的高
channels=3 ### input图像的通道数 3为RGB彩色图片,1为灰度图,4为RGBA图,A通道表示透明度
### 以上三个参数为输入图像的参数信息width和height,越小对小目标效果越好
### 对应的是输入图像的分辨率,从而影响precision,只可以设置成32的倍数
momentum=0.949### 冲量。DeepLearning1中最优化方法中的动量参数,这个值影响着梯度下降 到最优值得速度,冲量的建议配置为0.9
decay=0.0005### 权重衰减正则项,防止过拟合;等效于给误差函数添加一个惩罚项,常用的惩罚项是所有权重的平方乘以一个衰减常量之和
### 在每次迭代中,会基于角度、饱和度、曝光、色调产生新的训练图片
angle=0###数据增强参数,单位为度angle=5,就是生成新图片的时候随机旋转-5~5度
saturation = 1.5### 饱和度
exposure = 1.5### 曝光变化大小
hue=.1### 色调变化范围
learning_rate=0.00261### 初始学习率。训练发散的话可以降低学习率。学习遇到瓶颈,loss不变的话也可以减低学习率
★★★ 学习率调整一定不要太死,实际训练过程中根据loss的变化和其他指标动态调整,手动ctrl+c结束此次训练后,修改学习率,再加载刚才保存的模型继续训练即可完成手动调参,调整的依据是根据训练日志来,如果loss波动太大,说明学习率过大,适当减小,变为1/5,1/10均可,如果loss几乎不变,可能网络已经收敛或者陷入了局部极小,此时可以适当增大学习率,注意每次调整学习率后一定要训练久一点,调参是个细活慢慢琢磨
★★ 一点小说明:实际学习率与GPU的个数有关,例如你的学习率设置为0.001,如果你有4块GPU,那真实学习率为0.001/4
burn_in=1000### 在迭代次数小于burn_in时,其学习率的更新有一种方式,大于burn_in时,才采用policy的更新方式
max_batches = 500500### 最大迭代次数。训练达到max_batches后停止学习,修改为类别数量*2000
policy=steps### 学习策略,一般都是step这种步进式;还有constant, steps, exp, poly, step, sig, RANDOM,constant等方式
steps=400000,450000### steps和scale是设置学习率的变化,比如迭代到0.8*max_batches =400000次时,学习率衰减十倍,0.9*max_batches =450000次迭代时,学习率又会在前一个学习率的基础上衰减十倍
scales=.1,.1###意思就是在0-400000次iteration期间learning rate为原始0.1,0.000261;在400000-500000次iteration期间learning rate为当前值的0.1倍,为0.0000261.随着iteration增加,降低学习率可以使模型更有效的学习,也就是更好的降低train loss
#cutmix=1###cutmix相当于cutout+mixup的结合.mixup相当于是全图融合,cutout仅仅对图片进行增强,不改变label,而cutmix则是采用了cutout的局部融合思想,并且采用了mixup的混合label策略
mosaic=1###Yolov4论文提出的,属于cutmix的扩展,cutmix是两张图混合,而马赛克增强是4张图混合,好处非常明显是一张图相当于4张图,等价于batch增加了,可以显著减少训练需要的batch size大小
[convolutional]### 一层卷积层的配置说明
batch_normalize=1### 是否做BN:卷积层之后,激活层之前。BN层将数据归一化后,能够有效解决梯度消失与梯度爆炸问题
filters=32### 输出多少个特征图
size=3### 卷积核的尺寸
stride=1### 做卷积运算的步长
pad=1### 如果pad为0,padding由 padding参数指定。如果pad为1,padding大小为size/2
activation=mish###mish激活函数,为什么Mish表现这么好?
以上无边界(即正值可以达到任何高度)避免了由于封顶而导致的饱和。理论上对负值的轻微允许会有更好的梯度流,而不是像ReLU中那样的硬零边界。最后,可能也是最重要的,目前的想法是,平滑的激活函数允许更好的信息深入神经网络,得到更好的准确性和泛化。
# Downsample
[convolutional] ###下采样层的配置说明
batch_normalize=1
filters=64
size=3
stride=2###卷积核尺寸为3*3,配合padding且步长为2时,feature map变为原来的一半大小
pad=1
activation=mish
[route]
layers = -2###route layer层主要是把对应的层连接在一起;当layers只有一个值,代表route layer输出的是router layer - 2那一层layer的feature map;当layers有2个值时,代表route layer的输出为route layer -1和第route layer -1 layer的feature map在深度方向(z方向)连接起来
[shortcut]###把两个c h w都相同的两个层相加成一个相同c h w的层
from=-3###与前面的多少次进行融合,-3表示前面第三层
activation=linear###层次激活函数
[upsample]###通过双线性插值法将N*N的feature map变为(stride*N) * (stride*N)的feature map.模仿特征金字塔,生成多尺度feature map.加强小目标检测效果
stride=2
### SPP ######SPP层,内部采用不同大小的kernel size和stride实现不同感受野特征输出,然后route即可
[maxpool]
stride=1
size=5
[route]
layers=-2
[maxpool]
stride=1
size=9
[route]
layers=-4
[maxpool]
stride=1
size=13
[route]
layers=-1,-3,-5,-6
### End SPP ### ......
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
即上一层的特征图输入是13x13x512,然后三个分支分别是stride=1,kernel size为5,9,13,然后三个图拼接,得到13x13x2048的图,然后接一个1x1卷积,得到13x13x512的特征图,然后进行后续操作
......
[convolutional]###YOLO层前面一层卷积层配置说明
size=1
stride=1
pad=1
filters=255##filters=num.(预测框个数)*(classes+5),5的意义是4个坐标加一个置信率,论文中的tx,ty,tw,th,c,classes为类别数,COCO为80,num表示YOLO中每个cell预测的框的个数,YOLOV4中为3,3*(80+5)=255.自己使用时,此处的值一定要根据自己的数据集进行更改,例如你识别4个类,则:filters=3*(4+5)=27,三个fileters都需要修改,切记#
activation=linear###线性激活,也就是没用激活函数,原样输出
[yolo]###YOLO层配置说明
mask = 0,1,2###使用anchor的索引,0,1,2表示使用下面定义的anchors中的前三个anchor
anchors = 12, 16, 19, 36, 40, 28, 36, 75, 76, 55, 72, 146, 142, 110, 192, 243, 459, 401
classes=80###类别数目,对应上面的filters
num=9###每个grid cell总共预测几个box,和anchors的数量一致。当想要使用更多anchors时需要调大num(3*3)
jitter=.3###通过抖动增加噪声来抑制过拟合
ignore_thresh = .7### 参与计算的IOU阈值大小.当预测的检测框与ground true的IOU大于ignore_thresh的时候,参与loss的计算,否则,检测框的不参与损失计算目的是控制参与loss计算的检测框的规模,当ignore_thresh过于大,接近于1的时候,那么参与检测框回归loss的个数就会比较少,同时也容易造成过拟合;而如果ignore_thresh设置的过于小,那么参与计算的会数量规模就会很大。同时也容易在进行检测框回归的时候造成欠拟合
★ 参数设置:一般选取0.5-0.7之间的一个值,之前的计算基础都是小尺度(13*13)用的是0.7,(26*26)用的是0.5。这次先将0.5更改为0.7
random=1### random为1时会启用Multi-Scale Training,随机使用不同尺寸的图片进行训练,如果为0,每次训练大小与输入大小一致;是否随机确定最后的预测框,显存小可设置成0
truth_thresh = 1###
scale_x_y = 1.2
iou_thresh=0.213### 选定一个IOU阈值
cls_normalizer=1.0###class和confidence的权重参数,默认值1
iou_normalizer=0.07###box-iou的权重参数
iou_loss=ciou###x,y,w,h由mse损失函数换位box的ciou-loss,confidence和class用mse
nms_kind=greedynms###非极大值抑制(局部最大搜索),
beta_nms=0.6###需要设置权重阈值
机器 : Dell T5820服务器
GPU: Nvidia P4000
cuda: 10.1
cudnn: 7.5
关键库:
python detect.py --weights ./data/yolov4.weights --framework tf --size 608 --image ./data/kite.jpg
python save_model.py --weights ./data/yolov4.weights --output ./checkpoints/yolov4.tf --input_size 416 --model yolov4
python convert_trt.py --weights ./checkpoints/yolov4.tf --quantize_mode float16 --output ./checkpoints/yolov4-trt-fp16-416
python benchmarks.py --framework trt --weights ./checkpoints/yolov4-trt-fp16-416/
上述推理是将一张图片遍历1000次,然后计算出每次的推理时间,本人改写了一下 benchmarks.py,但是在 改
img_raw = tf.image.decode_image(open(FLAGS.image, 'rb').read(), channels=3)
这处时,不知到tf如何解码视屏,于是在加载每一帧时我都存为一张图片,有朋友知道如何优化这部分麻烦请告知我.
import numpy as np
import tensorflow as tf
import time
import cv2
from core.yolov4 import YOLOv4, YOLOv3_tiny, YOLOv3, decode
from absl import app, flags, logging
from absl.flags import FLAGS
from tensorflow.python.saved_model import tag_constants
from core import utils
from core.config import cfg
from tensorflow.compat.v1 import ConfigProto
from tensorflow.compat.v1 import InteractiveSession
flags.DEFINE_boolean('tiny', False, 'yolo or yolo-tiny')
flags.DEFINE_string('framework', 'tf', '(tf, tflite, trt')
flags.DEFINE_string('model', 'yolov4', 'yolov3 or yolov4')
flags.DEFINE_string('weights', './data/yolov4.weights', 'path to weights file')
flags.DEFINE_string('image', './data/Skate.mp4', 'path to input image')
flags.DEFINE_integer('size', 416, 'resize images to')
def main(_argv):
if FLAGS.tiny:
STRIDES = np.array(cfg.YOLO.STRIDES_TINY)
ANCHORS = utils.get_anchors(cfg.YOLO.ANCHORS_TINY, FLAGS.tiny)
else:
STRIDES = np.array(cfg.YOLO.STRIDES)
if FLAGS.model == 'yolov4':
ANCHORS = utils.get_anchors(cfg.YOLO.ANCHORS, FLAGS.tiny)
else:
ANCHORS = utils.get_anchors(cfg.YOLO.ANCHORS_V3, FLAGS.tiny)
NUM_CLASS = len(utils.read_class_names(cfg.YOLO.CLASSES))
XYSCALE = cfg.YOLO.XYSCALE
config = ConfigProto()
config.gpu_options.allow_growth = True
session = InteractiveSession(config=config)
input_size = FLAGS.size
physical_devices = tf.config.experimental.list_physical_devices('GPU')
if len(physical_devices) > 0:
tf.config.experimental.set_memory_growth(physical_devices[0], True)
if FLAGS.framework == 'tf':
input_layer = tf.keras.layers.Input([input_size, input_size, 3])
if FLAGS.tiny:
feature_maps = YOLOv3_tiny(input_layer, NUM_CLASS)
bbox_tensors = []
for i, fm in enumerate(feature_maps):
bbox_tensor = decode(fm, NUM_CLASS, i)
bbox_tensors.append(bbox_tensor)
model = tf.keras.Model(input_layer, bbox_tensors)
utils.load_weights_tiny(model, FLAGS.weights)
else:
if FLAGS.model == 'yolov3':
feature_maps = YOLOv3(input_layer, NUM_CLASS)
bbox_tensors = []
for i, fm in enumerate(feature_maps):
bbox_tensor = decode(fm, NUM_CLASS, i)
bbox_tensors.append(bbox_tensor)
model = tf.keras.Model(input_layer, bbox_tensors)
utils.load_weights_v3(model, FLAGS.weights)
elif FLAGS.model == 'yolov4':
feature_maps = YOLOv4(input_layer, NUM_CLASS)
bbox_tensors = []
for i, fm in enumerate(feature_maps):
bbox_tensor = decode(fm, NUM_CLASS, i)
bbox_tensors.append(bbox_tensor)
model = tf.keras.Model(input_layer, bbox_tensors)
utils.load_weights(model, FLAGS.weights)
elif FLAGS.framework == 'trt':
saved_model_loaded = tf.saved_model.load(FLAGS.weights, tags=[tag_constants.SERVING])
signature_keys = list(saved_model_loaded.signatures.keys())
print(signature_keys)
infer = saved_model_loaded.signatures['serving_default']
logging.info('weights loaded')
@tf.function
def run_model(x):
return model(x)
# Test the TensorFlow Lite model on random input data.
sum = 0
videoCapture = cv2.VideoCapture(FLAGS.image)
while 1:
ret, original_image = videoCapture.read()
#original_image = cv2.imread(FLAGS.image)
s_time = time.time()
tem_path = './temp/'+str(s_time)+'.jpg'
cv2.imwrite(tem_path,original_image)
original_image = cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB)
original_image_size = original_image.shape[:2]
image_data = utils.image_preporcess(np.copy(original_image), [FLAGS.size, FLAGS.size])
image_data = image_data[np.newaxis, ...].astype(np.float32)
#data_tensor = tf.convert_to_tensor(np_image)
#kkzssm = open(FLAGS.image, 'rb').read()
#print('kkzssm',type(kkzssm),np.shape(kkzssm))
img_raw = tf.image.decode_image(open(tem_path, 'rb').read(), channels=3)
img_raw = tf.expand_dims(img_raw, 0)
img_raw = tf.image.resize(img_raw, (FLAGS.size, FLAGS.size))
batched_input = tf.constant(image_data)
prev_time = time.time()
# pred_bbox = model.predict(image_data)
if FLAGS.framework == 'tf':
pred_bbox = []
result = run_model(image_data)
for value in result:
value = value.numpy()
pred_bbox.append(value)
if FLAGS.model == 'yolov4':
pred_bbox = utils.postprocess_bbbox(pred_bbox, ANCHORS, STRIDES, XYSCALE)
else:
pred_bbox = utils.postprocess_bbbox(pred_bbox, ANCHORS, STRIDES)
bboxes = utils.postprocess_boxes(pred_bbox, original_image_size, input_size, 0.25)
bboxes = utils.nms(bboxes, 0.213, method='nms')
elif FLAGS.framework == 'trt':
pred_bbox = []
result = infer(batched_input)
for key, value in result.items():
value = value.numpy()
pred_bbox.append(value)
if FLAGS.model == 'yolov4':
pred_bbox = utils.postprocess_bbbox(pred_bbox, ANCHORS, STRIDES, XYSCALE)
else:
pred_bbox = utils.postprocess_bbbox(pred_bbox, ANCHORS, STRIDES)
bboxes = utils.postprocess_boxes(pred_bbox, original_image_size, input_size, 0.25)
bboxes = utils.nms(bboxes, 0.213, method='nms')
# pred_bbox = pred_bbox.numpy()
curr_time = time.time()
exec_time = curr_time - prev_time
#if i == 0: continue
sum += (1 / exec_time)
info = "FPS: " + str(round((1 / exec_time), 1))
print(info)
if cv2.waitKey(100) & 0xff == ord('q'):
break
if __name__ == '__main__':
try:
app.run(main)
except SystemExit:
pass