机器学习——yolov3模型解析(包含模型和权重)
下⾯的对模型代码的解析是转载的,yolo3的模型--⽤keras写的,是我从github上下载的,带训练好的权重。
关于视频检测和图⽚检测的预测部份,我没有实验。只跑了train.py部分,我跑的是不是tiny,是3个输出的model。
分割线
******************************************************************************************************************************************************
V3的改进:
1.使⽤多标签分类;⽤多个独⽴的逻辑分类器替换softmax函数,以计算输⼊属于特定标签的可能性
2. 使⽤逻辑回归预测每个边界框的⽬标性得分,改变了计算代价函数的⽅式
3.使⽤3种不同的尺度进⾏融合预测
*********************************************************************************************************************************************
⼀. 基于keras的tiny-yolov3
1.1 tiny-yolov3 的⽹络结构,主⼲⽹络采⽤⼀个7层conv+max⽹络提取特征(和darknet19类似),嫁接⽹络采⽤的是13*13、26*26的分辨率探测⽹络,精度⽐较低。共24层:
1.2训练过程如下:
1).主函数main(): 加载annotation_path,classes_path,anchors_path, 图⽚尺⼨input_shape
2).第⼀阶段: create_model(input_shape, anchors, len(class_names) ) #构建模型以及是否预加载模型参数
-定义y_true形状是[(13, 13, 2, class+5), (26, 26, 2, class+5)]#len(anchors)//3=2
-model_body = tiny_yolo_body(image_input, num_anchors//3, num_classes)# 构建CNN⽹络,返回Model(input,[y1,y2])
--使⽤compse函数构建CNN⽹络,其中y1,y2构成两个不同尺度feature组
--返回定义Model,输⼊为inputs,输出为[y1,y2], return Model(inputs, [y1,y2])
-如果预加载训练模型:加载权重参数best_weights0.h5,是否冻结,是则将前⾯层参数冻结,否则全部都优化
-构建model_loss=Lambda(yolo_loss, output_shape,arguments)([*model_body.output, *y_true]),
--其中yolo_loss:要实现的函数,仅接受⼀个变量即上⼀层的输出,output_shape函数应该返回的值的shape
--损失层Lambda的输⼊是已有模型的输出model_body.output和真值y_true,输出是1个值,即损失值。
--yolo_loss除了接收Lambda层的输⼊model_body.output和y_true,还接收锚框anchors、类别数和过滤阈值3个参数。
-构建完整的算法模型model=Model([model_body.input, *y_true], model_loss)
--把model_body.input和y_true作为输⼊层,输出层为model_loss层
--model_body.input是任意(?)个(416,416,3)的图⽚;y_true是已标注数据所转换的真值结构
-
-把⽬标当成⼀个输⼊构成多输⼊模型,把loss写成⼀个层作为最后的输出,搭建模型的时候,只需将模型的output定义为loss,⽽compile的时候直接将loss设置为y_pred(因为模型的输出就是loss,所以y_pred就是loss)
-返回model ,完成算法模型model的构建
**总结: 构建模型Model([model_body.input, *y_true], model_loss),其中inputs=[model_body.input, *y_true], outputs=model_loss.
3).第⼆阶段:train(model, annotation_path, input_shape, anchors, len(class_names), log_dir)#使⽤构建的模型继续训练
-使⽤第⼀阶段已完成的⽹络权重继续训练
-编译创建好的模型modelpile(optimizer='adam', loss={'yolo_loss': lambda y_true, y_pred: y_pred})
--优化器Adam,模型compile时传递的是⾃定义的loss,把loss写成⼀个层融合到model⾥后,y_pred就是loss。
⾃定义损失函数yolo_loss规定要以y_true, y_pred为参数。
-可视化tensorboard = TensorBoard(log_dir=log_dir)
-定义检查模型权重参数checkpoint=ModelCheckpoint("best_weights.h5",monitor="val_loss",mode='min'...period=1)
--设置成当验证数据集的分类损失减⼩时保存⽹络权重(mode=min),每隔⼀定步数记录最⼤值
-定义回调函数callback_lists=[tensorboard,checkpoint]实时监测训练动态,并能根据训练情况及时对模型采取⼀定的措施
--在调⽤fit时传⼊模型的⼀个对象,它在训练过程中的不同时间点都会被模型调⽤。
--可以访问关于模型状态和性能的所有可⽤数据,还可以采取⾏动:中断训练\保存模型\加载⼀组不同的权重或者改变模型状态
-定义batch_size、训练和验证的⽐例val_split
-读取annotation⽂件保存在lines中并随机打乱秩序np.random.shuffle(lines)
-开始训练model.fit_generator(generator,steps_perepoch,epochs,verbose,callbacks,validation_data,v
alidation_steps,initial) --逐个⽣成数据的batch并进⾏训练,⽣成器与模型将并⾏执⾏以提⾼效率,⽣成器将⽆限在数据集上循环
--generator⽣成器函数,值为data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes)返回结果
---data_generator()#循环输出固定批次数batch_size=16的图⽚数据image_data和标注框数据box_data
----数据的总⾏数是n=len(annotation)
----数据的总⾏数是n=len(annotation)
----在第0次时将数据洗牌shuffle(annotation)
----在每batch_size循环中调⽤get_random_data解析第i⾏annotation⽣成image和box,添加⾄各⾃image/box_data中
-------get_random_data调整输⼊的每个图⽚尺⼨image_data,取图⽚中标注框取前20个调整图⽚后box_data
----索引值递增i+1,当完成n个⼀轮之后即循环⼀轮,重新将i置0,再次调⽤shuffle洗牌数据
----将image_data和box_data都转换为np数组
----⽣成真值y_true=preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes)
-----将真实坐标转化为yolo需要输⼊的坐标真值y_true,维度是[[batchsize,13,13,2,6],[batchsize, 26,26,2,6]]
rectangle函数opencv-----grid_shapes=[[13,13],[26,26]]
-----在每个box_data中,只取长宽⼤于0的数据,然后取anchor与这些数据最接近的best_anchor
-----y_true最后⼀维度的6位中0/1位是中⼼点xy,范围0-13/26,2/3位是宽⾼wh,范围是0-1,4位是置信度1/0,5位是类别
0/1
----返回[image_data, *y_true], np.zeros(batch_size)
--steps_per_epoch当⽣成器返回steps_per_epoch次数据时计⼀个epoch结束,执⾏下⼀个epoch
--epochs:整数,数据迭代的轮数
--verbose:⽇志显⽰,0为不在标准输出流输出⽇志信息,1为输出进度条记录,2为每个epoch输出⼀⾏记录
--validation_data⽣成验证集的⽣成器,上⽂的generator是⽣成训练集⽣成器
--validation_steps指定验证集的⽣成器返回次数
--initial_epoch: 从该参数指定的epoch开始训练,在继续之前的训练时有⽤
--callbacks=callback_lists回调函数是⼀组在训练特定阶段调⽤的函数集,⽤来观察训练过程中⽹络内部的状态和统计信息,也在这⼀步保存模型,以后直接载⼊模型与训练数据即可开始训练或者识别
-model.save_weights()保存模型权重,只保存模型的权重,可以看作是保存模型的⼀部分(本程序没有被调⽤)
**总结:编译模型modelpile(optimizer='adam', loss={'yolo_loss': lambda y_true, y_pred: y_pred})
**** 训练模型model.fit_generator(generator,epochs,callbacks,validation_data,...)
**** 保存训练模型权重⽂件 best_weights.h5,提供给检测过程使⽤
1.3 检测过程如下:
1.定义相关参数
-定义参数解析器parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)
-添加参数model,anchors,classes,gpu_num,image
-将变量以标签-值的字典形式存⼊args字典FLAGS = parser.parse_args()
2.开始检测detect_frame(YOLO(**vars(FLAGS)))
-使⽤OpenCV打开USB相机cap = cv2.VideoCapture(0),ret, frame = ad()
-OpenCV转换成PIL.Image格式 Image.fromarray(frame[...,::-1])
-开始对每帧进⾏检测yolo.detect_frame(image)并返回边框boxes,置信度score和类别名称
--⾸先确定定义的输⼊图⽚尺⼨是32的倍数assert model_image_size[0]%32 == 0
-
-boxed_image = letterbox_image改变图⽚尺⼨为[416,416]#这⾥不改变图⽚⽐例,较⼩的⽤灰⾊填充
--加载参数并运⾏self.sess.run([output],feed_dict={})其中output=self.boxes, self.scores, self.classes
---self.boxes, self.scores, self.classes = ate()#构建检测模型,下载模型数据
----lo_model = load_model##载⼊模型参数⽂件
----boxes, scores, classes=yolo_eval()#使⽤yolo模型进⾏图⽚的检测,进⾏坐标转化和nms处理
-----yolo_eval()完成预测逻辑的封装
-----yolo_eval(yolo_outputs,anchors,num_classes,image_shape,max_boxes,score_threshold,iou_threshold)
-----yolo_outputs:YOLO模型的输出,2个尺度,13/26,最后1维是预测值=2×(5+1)=12,为[(?,13,13,12),(?,26,26,12)]
-----max_boxes:图中最⼤的检测框数,20个
-----score_threshold:框置信度阈值,⼩于阈值的框被删除,需要的框较多则调低阈值,需要的框较少
则调⾼阈值;
-----iou_threshold:同类别框的IoU阈值,⼤于阈值的重叠框被删除,重叠物体较多则调⾼阈值,重叠物体较少则调低阈值;
-----在两个尺度中调⽤yolo_boxes_and_scores(),提取框_boxes和置信度_box_scores,将2层的框数据放⼊列表boxes和box_scores,再拼接展平concatenate,输出的数据就是适合输⼊图⽚的所有的框和置信度
-----mask过滤⼩于置信度阈值的框,只保留⼤于置信度的框,mask掩码
-----max_boxes_tensor每张图⽚的最⼤检测框数,max_boxes是20
-----通过掩码mask和类别c,筛选框class_boxes和置信度class_box_scores;
-----通过NMS⾮极⼤值抑制_max_suppression(class_boxes, class_box_scores, max_boxes_tensor,
iou_threshold=iou_threshold),筛选出框boxes的NMS索引nms_index,把同⼀分类重合度过⾼的候选框给筛选掉;
-----根据索引,选择gather输出的框class_boxes和置信class_box_scores度,再⽣成类别信息classes
-----将多个类别的数据组合,⽣成最终的检测数据框并返回
---feed_dict={lo_model.input: image_data,self.input_image_shape: [image.size[1], image.size[0]]...}传⼊图数据、图⽚
---feed_dict={lo_model.input: image_data,self.input_image_shape: [image.size[1], image.size[0]]...}传⼊图数据、图⽚尺⼨等参数
-绘制检测框和类别angle(),cv2.putText()
-关闭session: yolo.close_session()
*********************************************************************************************************************************************
上图左边是⼀个Darknet-53模型结构,⽹络中有53个Convolutional层。算上所有的层是108层。其中Residual层是输⼊层与输出层叠加起来作为输出层,叠加后的特征图作为新的输⼊输⼊到下⼀层。YOLO主体是由许多这种残差模块组成,减⼩了梯度爆炸的风险,加强了⽹络的学习能⼒。输⼊图⽚⼤⼩是416X416,右边预测的三个特征层scale1,scale2,scale3⼤⼩分别是52×52,26×26, 13×13。都是
奇数使得⽹格会有个中⼼位置。同时YOLO输出为3个尺度,每个尺度之间还有联系。⽐如说,13×13这个尺度输出⽤于检测⼤型⽬标,对应的26×26为中型的,52×52⽤于检测⼩型⽬标。⽣成预测层的三层的最后⼀层都是Conv2d。
2.2 ⽬标边界框的预测(??)
YOLOv3⽹络在三个特征图中分别通过(4+1+c)*k个⼤⼩为1*1的卷积核进⾏卷积预测,k为预设边界框的个数(默认取3),c为预测⽬标的类别数,其中4k个参数负责预测⽬标边界框的偏移量,k个参数负责预测⽬标边界框内包含⽬标的概率,ck个参数负责预测这k个预设边界框对应c个⽬标类别的概率。。。。。预设边界框的中⼼坐标固定在⼀个cell当中,作者说这样能够加快⽹络收敛....没懂?
假设根据聚类得到的anchors⽂件内容为54,315, 85,28, 112,38, 142,57, 272,129, 351,178, 381,98, 410,211, 504,129,三个预测层的特征图⼤⼩以及每个特征图上预设边界框的尺⼨如下表:
特征图层 特征图⼤⼩ 预设边界框尺⼨ 预设边界框数量
图层1 13x13 (381,98);(410,211); (504,129) 13x13x3
图层2 26x26 (142,57); (272,129; (351,178) 26x26x3
图层3 52x52 (54,315); (85,28); (112,38) 52x52x3
2.3损失函数计算(??)
YOLOv3的损失函数主要分为三个部分:⽬标定位偏移量损失,⽬标置信度损失以及⽬标分类损失,其中是平衡系数。
⽬标置信度可以理解为预测⽬标矩形框内存在⽬标的概率,⽬标置信度损失采⽤的是⼆值交叉熵损失,其中,表⽰预测⽬标边界框i中是否真实存在⽬标,0表⽰不存在,1表⽰存在。表⽰预测⽬标矩形框i内是否存在⽬标的Sigmoid概率(将预测值通过sigmoid函数得到)。
⽬标定位损失采⽤的是真实偏差值与预测偏差值差的平⽅和,其中表⽰预测矩形框坐标偏移量(注意⽹络预测的是偏移量,不是直接预测坐标),表⽰与之匹配的GTbox与默认框之间的坐标偏移量,为预测的⽬标矩形框参数,为默认矩形框参数,为与之匹配的真实⽬标矩形框参数,这些参数都是映射在预测特征图上的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论