4. 模型训练
这一部分能够参见 附件 \train_yolov3.py,官网平台有更通用的训练代码(参见:https://gitee.com/mindspore/m…)。
4.1 导包
首先是导入包:
import time
from mindspore import context
from mindspore.common import set_seed
from mindspore.train.serialization import load_checkpoint, load_param_into_net
from mindspore import Tensor
from mindspore.nn.optim.momentum import Momentum
from mindspore import amp
from mindspore.train.loss_scale_manager import FixedLossScaleManager
from mindspore.train.callback import ModelCheckpoint, RunContext
from mindspore.train.callback import CheckpointConfig
以下包请见附件代码 train_yolov3.py
from utils.utils_yolov3 import AverageMeter, get_param_groups, keep_loss_fp32, load_yolov3_backbone
from model.yolo import YOLOV3DarkNet53, YoloWithLossCell
from utils.kaiming_init import default_recurisive_init
from dataset.voc2012_dataset import create_voc2012_dataset
from utils.lr_generator import get_lr
from yolov3_train_conf import TrainYOLOv3Conf
set_seed(1)
下面的包可能有些没有用但我忘删了。
附件中引入的代码:
AverageMeter:次要是用于治理训练过程中的损失值,每个迭代肯定次数给出这一段训练的损失平均值。
get_param_groups:次要是在配置优化器阶段应用。(keep_loss_fp32:我也不晓得它是干什么的)
load_yolov3_backbone:次要是将 Darknet53 骨干网络的预训练参数加载进来。
default_recurisive_init:应用 He 初始化,因为网络中大量应用 Relu。
get_lr:学习率应用余弦退火函数生成,这个函数会依据参数生成每一代训练的学习率。
TrainYOLOv3Conf:训练配置。
4.2 设置 context
而后设置 Context:
set context
context.set_context(mode=context.GRAPH_MODE,
device_target="GPU", save_graphs=False)
set_graph_kernel_context
context.set_context(enable_graph_kernel=True)
context.set_context(graph_kernel_flags=”–enable_parallel_fusion “
"--enable_trans_op_optimize"
"--disable_cluster_ops=ReduceMax,Reshape"
"--enable_expand_ops=Conv2D")
Set mempool block size for improving memory utilization, which will not take effect in GRAPH_MODE
if context.get_context(“mode”) == context.PYNATIVE_MODE:
context.set_context(mempool_block_size="31GB")
这外面我认为用途比拟大的是第一个 set_context,尤其是 device_target 参数决定了运行环境是 GPU。其它的参数我也不好评估它们的重要性(因为我也不太晓得是啥意思),大家能够参考官网。
4.3 初始化配置和网络
接下来是初始化训练配置对象和网络:
config
config = TrainYOLOv3Conf()
loss_meter = AverageMeter(‘loss’)
network
network = YOLOV3DarkNet53(is_training=config.data_training, config=config)
default_recurisive_init(network)
~ load_yolov3_learn(network, config.ckpt_path) # 用于迁徙 YOLOv3 模型时应用
load_yolov3_backbone(network, config.backbone_ckpt_path)
network = YoloWithLossCell(network, config=config)
AverageMeter 如前所述是用于治理训练过程中的损失值的。TrainYOLOv3Conf 生成的对象 config 中保留有所有的模型训练配置,包含这里的网络配置、前面的读数据集配置以及训练配置,大家能够在 附件 \yolov3_train_conf.py 中查看和编辑,TrainYOLOv3Conf 的定义和其中各字段的含意如下:
“”” 训练 YOLOv3 时的参数配置 ”””
class TrainYOLOv3Conf():
def __init__(self):
# ---- dataset ----
self.data_path = "VOCdevkit/VOC2012/" # 数据集门路,要求到有 Annotations 和 ImageSets 以及 JPEGImages 的目录下
self.data_usage = "my_person_train" # ImageSets/Main/ 中的文件名,不包含 txt 后缀,阐明读取哪些数据
self.data_training = True # 数据用于训练,训练时将采纳图像增强操作和 shulffle
self.num_classes = 1 # 只检测一个类即 person
self.class_to_idx = {}
self.anchor_scales = [[15, 38], # 9 个先验框大小
[34, 86],
[84, 127],
[51, 192],
[91, 257],
[173, 195],
[142, 319],
[221, 339],
[351, 365]]
self.jitter = 0.3 # 0.3 # 图像缩放裁剪,见 transform.py 中的_choose_candidate_by_constraints 函数
self.scale_sma = 0.7 # 0.25
self.scale_lar = 1.5 # 2
self.hue = 0.1 # 图像色彩随机变换。见 transform.py 中的 color_distortion 函数
self.saturation = 1.5
self.value = 1.5
self.batch_size = 48 # batch size
self.max_box = 32 # 训练时每张图像最多检测几个框
self.label_smooth = 0 # 用于损失函数中的一个设置
self.label_smooth_factor = 0.1
# ---- Model ----
self.out_channel = 3 * (5 + self.num_classes) # 输入通道数
self.keep_detect = True # 默认为 True
# ~ self.ckpt_path = "./yolo3v-280_36120.ckpt" # 迁徙训练模型
self.backbone_ckpt_path = "./ckpt/darknet53_wp.ckpt" # 加载预训练文件门路
# ---- Train ----
self.ignore_threshold = 0.7 # 用于训练时决定
self.max_epoch = 320 # 总训练的 epoch
self.pretrained_epoch_num = 0 # 曾经训练的 epoch
self.steps_per_epoch = -1 # 每个 epoch 有多少个 batch,这里在 train_yolov3.py 运行时再设置
# lr, use in get_lr()
self.lr = 0.001 # 初始学习率
self.warmup_epochs = 4
self.T_max = 320
self.eta_min = 0.0
# opt: momentum
self.momentum = 0.9 # use in Momentum init
self.loss_scale = 1024 # use in Momentum and FixedLossScaleManager
self.weight_decay = 0.016 # use in group_params
# ckpt
self.ckpt_interval = 10 # 隔多少 epoch 保留以下模型
self.save_ckpt_path = "train_ckpt/" # 保留 epoch 的门路
# log
self.log_interval = 50 # 训练多少 step 显示一下损失值和其它两头信息
网络初始化过程中,首先是生成了 YOLOV3DarkNet53,而后对整个网络进行 He 初始化,再加载骨干网络的参数进行笼罩(加载时会提醒有些模块没加载,不要缓和,还记得结构图中骨干网络前面的 3 条尾巴吗),最初再将它们放到 YoloWithLossCell 中。
一般来说,指标检测模型要应用迁徙训练。本我的项目的 YOLOv3 模型其实基本上是 copy 的官网的(ModelZoo,https://gitee.com/mindspore/m…),如果大家可能找到他人用大数据集预训练好的模型,能够间接迁徙。MindSpore 的模型参数采纳的是字典格局存储,键是字符串,其命名为你的模型 class 中的 字段. 下一级字段 …,若迁徙时零碎提醒抵触,能够依据本人的模型字段从新建一个新字典加载。若找不到预训练的模型,在附件中我提供了利用 Caltech-256 分类数据集对骨干网络进行预训练的办法和代码,本我的项目用着成果还行,要留神 Caltech-256 下好后外部其实有 257 个分类,要删掉最初一个。
4.4 加载数据集
dataset
voc2012_dat, data_size = create_voc2012_dataset(config, 2)
config.steps_per_epoch = int(data_size / config.batch_size)
print(“bath num in 1 epoch: “, config.steps_per_epoch)
4.5 生成 Momentum 优化器
opt
lr = get_lr(config.lr,
config.steps_per_epoch,
config.warmup_epochs,
config.max_epoch,
config.T_max,
config.eta_min,
config.pretrained_epoch_num)
opt = Momentum(params=get_param_groups(network),
learning_rate=Tensor(lr),
momentum=config.momentum,
weight_decay=config.weight_decay,
loss_scale=config.loss_scale)
loss_scale_value = 1.0
loss_scale = FixedLossScaleManager(loss_scale_value, drop_overflow_update=False)
network = amp.build_train_network(network, optimizer=opt, loss_scale_manager=loss_scale,
level="O2", keep_batchnorm_fp32=False)
keep_loss_fp32(network)
其中,get_lt 的参数别离是:
config.lr:设置初始学习率。
config.steps_per_epoch:一个 epoch 中训练多少 steps,在数据集读取局部计算出该值。
warmup_epochs:代表学习率线性晋升阶段(从 config.eta_min 到 config.lr)的 epoch 数。
config.max_epoch:一共训练多少 epoch。
config.T_max:余弦函数的周期是多少 epoch。
config.eta_min:学习率最小值。
config.pretrained_epoch_num:曾经训练的 epoch 数。
4.6 设置训练过程中保留模型
ckpt
config.ckpt_interval = config.steps_per_epoch * config.ckpt_interval
print(“config.ckpt_interval: “,config.ckpt_interval,config.steps_per_epoch)
checkpoint save
ckpt_max_num = config.max_epoch * config.steps_per_epoch // config.ckpt_interval
ckpt_config = CheckpointConfig(save_checkpoint_steps=config.ckpt_interval,
keep_checkpoint_max=ckpt_max_num)
ckpt_cb = ModelCheckpoint(config=ckpt_config,
directory=config.save_ckpt_path,
prefix='yolov3')
cb_params = InternalCallbackParam()
cb_params.train_network = network
cb_params.epoch_num = ckpt_max_num
cb_params.cur_epoch_num = config.pretrained_epoch_num+1
run_context = RunContext(cb_params)
ckpt_cb.begin(run_context)
这里次要是设置保留模型的距离(config.ckpt_interval 在配置中原本保留的是多少 epoch 保留一下模型文件,这里乘以每个 epoch 的训练代数,得出每隔多少代保留模型),以及保留模型的门路(config.save_ckpt_path)和保留的文件名前缀。这里 InternalCallbackParam 类的定义请见 附件 \train_yolov3.py。
4.7 训练
train
old_progress = -1
p_epoch = config.pretrained_epoch_num
p_step = p_epoch*config.steps_per_epoch
t_end = time.time()
data_loader = voc2012_dat.create_dict_iterator(output_numpy=True, num_epochs=1)
for i, data in enumerate(data_loader):
images = data["image"]
input_shape = images.shape[2:4]
images = Tensor.from_numpy(images)
batch_y_true_0 = Tensor.from_numpy(data['bbox1'])
batch_y_true_1 = Tensor.from_numpy(data['bbox2'])
batch_y_true_2 = Tensor.from_numpy(data['bbox3'])
batch_gt_box0 = Tensor.from_numpy(data['gt_box1'])
batch_gt_box1 = Tensor.from_numpy(data['gt_box2'])
batch_gt_box2 = Tensor.from_numpy(data['gt_box3'])
loss = network(images, batch_y_true_0, batch_y_true_1, batch_y_true_2, batch_gt_box0, batch_gt_box1,
batch_gt_box2)
loss_meter.update(loss.asnumpy())
# ckpt progress
cb_params.cur_step_num = i + 1 # current step number
cb_params.batch_num = i + 2
ckpt_cb.step_end(run_context)
if i % config.log_interval == 0:
time_used = time.time() - t_end
epoch = int(i / config.steps_per_epoch)
per_step_time = time_used / config.log_interval * 1000
fps = config.batch_size * (i - old_progress) / time_used
print('epoch[{}], iter[{}], {}, {:.2f} imgs/sec, lr:{},'
'per step time:{}ms'.format(epoch+p_epoch, i+p_step, loss_meter, fps, lr, per_step_time))
t_end = time.time()
loss_meter.reset()
old_progress = i
if (i + 1) % config.steps_per_epoch == 0:
cb_params.cur_epoch_num += 1
print(“Train Finish”)
留神:训练过程中若超参数设置不好也可能导致模型训练成果不佳,例如训练的 epoch 设置得太小。
(未完,请见 YOLOv3 人体指标检测模型实现(四))