物体检测yolo3算法 学习笔记(1)

举报
tengyun 发表于 2020/03/22 00:16:50 2020/03/22
【摘要】 YOLO作为一个one-stage目标检测算法,在速度和准确度上都有杰出的表现。而YOLO v3是YOLO的第3个版本(即YOLO、YOLO 9000、YOLO v3),检测效果,更准更强。

YOLO场景运用: YOLO作为一个one-stage目标检测算法,在速度和准确度上都有杰出的表现。

在ModelArts 实战营第四期中,我们学习使用了YOLO V3算法进行的物体检测训练和推理,这里对notebook代码的学习做个整理

准备数据

有很多开源的数据集可以用来进行目标检测任务的训练,如COCO数据集,PASCAL VOC数据集,BDD100K等,可以根据不同的需求和偏好进行选择。在获得数据集之后,需要对数据进行格式统一,然后便可以进行训练了。

本案例中使用的是COCO数据集,

解压后图片文件路径:data_path = "./coco/coco_data",

一、参数

1、标注信息

coco数据标注信息文件存储位置:

annotation_path = './coco/coco_train.txt'

格式如下:

图片的文件名 框的四个坐标(xmin,ymin,xmax,ymax,label_id)

923 168,131,289,270,6

400 1,64,636,477,8

... ...


2、coco类型

coco类型定义文件存储位置,

classes_path = './model_data/coco_classes.txt'

共有80个分类:

person

bicycle

car

motorbike

aeroplane

bus

train

truck

boat

traffic light

... ...


3、预测特征图(Prediction Feature Map)的anchor框(anchor box)集合

coco数据anchor值文件存储位置:

anchors_path = './model_data/yolo_anchors.txt'

  • 3个尺度(scale)的特征图,每个特征图3个anchor框,共9个框,从小到大排列;

  • 1 ~ 3是大尺度(52x52)特征图所使用的,4 ~ 6是中尺度(26x26),7 ~ 9是小尺度(13x13)

  • 大尺度特征图检测小物体,小尺度检测大物体;

  • 9个anchor来源于边界框(Bounding Box)的k-means聚类。

COCO的anchors,如下:

[[ 10.,  13.],       [ 16.,  30.],       [ 33.,  23.],       [ 30.,  61.],       [ 62.,  45.],       [ 59., 119.],       [116.,  90.],       [156., 198.],       [373., 326.]]


4、预训练模型

用于迁移学习(Transfer Learning)中的微调(Fine Tune),支持使用已训练完成的COCO模型参数,即:

预训练权重文件存储位置:

weights_path = "./model_data/yolo.h5"


# 模型文件存储位置

save_path = "./result/models/"


5、图片输入尺寸

默认为416x416。图片尺寸满足32的倍数,在DarkNet网络中,含有5次步长为2的降采样卷积(32=2^5),其中,卷积操作如下:

x = DarknetConv2D_BN_Leaky(num_filters, (3, 3), strides=(2, 2))(x)

在最底层时,特征图尺寸需要满足为奇数,如13,以保证中心点落在唯一框中。当为偶数时,则导致中心点落在中心的4个框中。


二、创建模型

创建YOLOv3的网络模型,输入:

  • input_shape:图片尺寸

  • anchors:9个anchor box;

  • num_classes:类别数;

  • weights_path:预训练模型的权重

实现如下:


# 初始化session

K.clear_session()

input_shape = (416,416)

image_input = Input(shape=(None, None, 3))

h, w = input_shape

# 对模型预测结果形状进行定义

y_true = [Input(shape=(h//{0:32, 1:16, 2:8}[l], w//{0:32, 1:16, 2:8}[l], num_anchors//3, num_classes+5))

         for l in range(3)]

# 构建YOLO模型结构

model_body = yolo_body(image_input, num_anchors//3, num_classes)

# 将YOLO权重文件加载进来,如果希望不加载预训练权重,从头开始训练的话,可以将这条语句删掉

model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)

# 定义YOLO损失函数

model_loss = Lambda(yolo_loss,

                   output_shape=(1,), name='yolo_loss',

                   arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(

   [*model_body.output, *y_true])

# 构建Model,为训练做准备

model = Model([model_body.input, *y_true], model_loss)


三、划分数据集

val_split = 0.1

with open(annotation_path) as f:

lines = f.readlines()

np.random.seed(10101)

np.random.shuffle(lines)

np.random.seed(None)

num_val = int(len(lines)*val_split)

num_train = len(lines) - num_val

样本洗牌(shuffle),将数据集拆分为10份,训练9份,验证1份。


四、第1阶段训练过程

第1阶段,冻结部分网络,训练底层参数,优化器使用Adam;

损失函数,直接使用模型的输出y_pred,忽略真值y_true;

代码如下:

model.compile(optimizer=Adam(lr=1e-4), loss=

# 使用定制的 yolo_loss Lambda层

{'yolo_loss': lambda y_true, y_pred: y_pred})  # 损失函数

对于损失函数yolo_loss,以及y_true和y_pred:

把y_true当成一个输入,构成多输入模型,把loss写成层(Lambda层),作为最后的输出。这样,构建模型的时候,就只需要将模型的输出(output)定义为loss即可。而编译(compile)的时候,直接将loss设置为y_pred,因为模型的输出就是loss,即y_pred就是loss,因而无视y_true。训练的时候,随便添加一个符合形状的y_true数组即可。

模型训练,使用数据生成包装器(data_generator_wrapper),按批次生成训练和验证数据。最终,模型model存储权重。实现如下:

# 开始训练

batch_size = 16

print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))

model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, data_path,anchors, num_classes),

steps_per_epoch=max(1, num_train//batch_size),

validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, data_path,anchors, num_classes),

validation_steps=max(1, num_val//batch_size),

epochs=5,

initial_epoch=0,

callbacks=[reduce_lr, early_stopping])

保存训练数据

import os

os.makedirs(save_path)

# 存储第1阶段的参数,再训练过程中,也通过回调存储

model.save_weights(log_dir + 'trained_weights_stage_1.h5')

在训练过程中,也会存储模型的参数,只存储权重(save_weights_only),只存储最优结果(save_best_only),每隔3个epoch存储一次(period),即:

checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',

monitor='val_loss', save_weights_only=True,

save_best_only=True, period=3)  # 只存储weights权重


五、第2阶段训练

第2阶段,使用第1阶段已训练的网络权重,继续训练:

将全部的参数都设置为可训练,而第1阶段则是冻结(freeze)部分参数;

优化器,仍是Adam,只是学习率(lr)有所下降,从1e-3减少至1e-4,细腻地学习最优参数;

损失函数,仍是只使用y_pred,忽略y_true。

for i in range(len(model.layers)):

model.layers[i].trainable = True

model.compile(optimizer=Adam(lr=1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred})

第2阶段的模型fit数据,与第1阶段类似,从第5个epoch开始,一直训练到第10个epoch,触发条件则提前终止。

额外增加了两个回调reduce_lr和early_stopping

# 定义callbacks方法

reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)

early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)

reduce_lr:当评价指标不在提升时,减少学习率,每次减少10%(factor),当学习率3次未减少(patience)时,终止训练。

early_stopping:验证集准确率,连续增加小于0(min_delta)时,持续10个epoch(patience),则终止训练。

batch_size = 16

print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))

model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, data_path,anchors, num_classes),

steps_per_epoch=max(1, num_train//batch_size),

validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, data_path,anchors, num_classes),

validation_steps=max(1, num_val//batch_size),

epochs=10,

initial_epoch=5,

callbacks=[reduce_lr, early_stopping])

至此,在第2阶段训练完成之后,输出的网络参数,就是最终的模型参数。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。