Skip to content

Latest commit

 

History

History
 
 

prune

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

运行该示例前请安装Paddle1.6或更高版本

检测模型卷积通道剪裁示例

概述

该示例使用PaddleSlim提供的卷积通道剪裁压缩策略对检测库中的模型进行压缩。 在阅读该示例前,建议您先了解以下内容:

配置文件说明

关于配置文件如何编写您可以参考:

其中,配置文件中的pruned_params需要根据当前模型的网络结构特点设置,它用来指定要裁剪的parameters.

这里以MobileNetV1-YoloV3模型为例,其卷积可以三种:主干网络中的普通卷积,主干网络中的depthwise convolutionyolo block里的普通卷积。PaddleSlim暂时无法对depthwise convolution直接进行剪裁, 因为depthwise convolutionchannel的变化会同时影响到前后的卷积层。我们这里只对主干网络中的普通卷积和yolo block里的普通卷积做裁剪。

通过以下方式可视化模型结构:

from paddle.fluid.framework import IrGraph
from paddle.fluid import core

graph = IrGraph(core.Graph(train_prog.desc), for_test=True)
marked_nodes = set()
for op in graph.all_op_nodes():
    print(op.name())
    if op.name().find('conv') > -1:
        marked_nodes.add(op)
graph.draw('.', 'forward', marked_nodes)

该示例中MobileNetV1-YoloV3模型结构的可视化结果:MobileNetV1-YoloV3.pdf

同时通过以下命令观察目标卷积层的参数(parameters)的名称和shape:

for param in fluid.default_main_program().global_block().all_parameters():
    if 'weights' in param.name:
        print(param.name, param.shape)

从可视化结果,我们可以排除后续会做concat的卷积层,最终得到如下要裁剪的参数名称:

conv2_1_sep_weights
conv2_2_sep_weights
conv3_1_sep_weights
conv4_1_sep_weights
conv5_1_sep_weights
conv5_2_sep_weights
conv5_3_sep_weights
conv5_4_sep_weights
conv5_5_sep_weights
conv5_6_sep_weights
yolo_block.0.0.0.conv.weights
yolo_block.0.0.1.conv.weights
yolo_block.0.1.0.conv.weights
yolo_block.0.1.1.conv.weights
yolo_block.1.0.0.conv.weights
yolo_block.1.0.1.conv.weights
yolo_block.1.1.0.conv.weights
yolo_block.1.1.1.conv.weights
yolo_block.1.2.conv.weights
yolo_block.2.0.0.conv.weights
yolo_block.2.0.1.conv.weights
yolo_block.2.1.1.conv.weights
yolo_block.2.2.conv.weights
yolo_block.2.tip.conv.weights
(conv2_1_sep_weights)|(conv2_2_sep_weights)|(conv3_1_sep_weights)|(conv4_1_sep_weights)|(conv5_1_sep_weights)|(conv5_2_sep_weights)|(conv5_3_sep_weights)|(conv5_4_sep_weights)|(conv5_5_sep_weights)|(conv5_6_sep_weights)|(yolo_block.0.0.0.conv.weights)|(yolo_block.0.0.1.conv.weights)|(yolo_block.0.1.0.conv.weights)|(yolo_block.0.1.1.conv.weights)|(yolo_block.1.0.0.conv.weights)|(yolo_block.1.0.1.conv.weights)|(yolo_block.1.1.0.conv.weights)|(yolo_block.1.1.1.conv.weights)|(yolo_block.1.2.conv.weights)|(yolo_block.2.0.0.conv.weights)|(yolo_block.2.0.1.conv.weights)|(yolo_block.2.1.1.conv.weights)|(yolo_block.2.2.conv.weights)|(yolo_block.2.tip.conv.weights)

综上,我们将MobileNetV2配置文件中的pruned_params设置为以下正则表达式:

(conv2_1_sep_weights)|(conv2_2_sep_weights)|(conv3_1_sep_weights)|(conv4_1_sep_weights)|(conv5_1_sep_weights)|(conv5_2_sep_weights)|(conv5_3_sep_weights)|(conv5_4_sep_weights)|(conv5_5_sep_weights)|(conv5_6_sep_weights)|(yolo_block.0.0.0.conv.weights)|(yolo_block.0.0.1.conv.weights)|(yolo_block.0.1.0.conv.weights)|(yolo_block.0.1.1.conv.weights)|(yolo_block.1.0.0.conv.weights)|(yolo_block.1.0.1.conv.weights)|(yolo_block.1.1.0.conv.weights)|(yolo_block.1.1.1.conv.weights)|(yolo_block.1.2.conv.weights)|(yolo_block.2.0.0.conv.weights)|(yolo_block.2.0.1.conv.weights)|(yolo_block.2.1.1.conv.weights)|(yolo_block.2.2.conv.weights)|(yolo_block.2.tip.conv.weights)

我们可以用上述操作观察其它检测模型的参数名称规律,然后设置合适的正则表达式来剪裁合适的参数。

训练

根据PaddleDetection/tools/train.py编写压缩脚本compress.py。 在该脚本中定义了Compressor对象,用于执行压缩任务。

执行示例

step1: 设置gpu卡

export CUDA_VISIBLE_DEVICES=0

step2: 开始训练

使用PaddleDetection提供的配置文件在用8卡进行训练:

python compress.py \
    -s yolov3_mobilenet_v1_slim.yaml \
    -c ../../configs/yolov3_mobilenet_v1_voc.yml \
    -o max_iters=258 \
    YoloTrainFeed.batch_size=64 \
    -d "../../dataset/voc"

通过命令行覆盖设置max_iters选项,因为PaddleDetection中训练是以batch为单位迭代的,并没有涉及epoch的概念,但是PaddleSlim需要知道当前训练进行到第几个epoch, 所以需要将max_iters设置为一个epoch内的batch的数量。

如果要调整训练卡数,需要调整配置文件yolov3_mobilenet_v1_voc.yml中的以下参数:

  • max_iters: 一个epoch中batch的数量,需要设置为total_num / batch_size, 其中total_num为训练样本总数量,batch_size为多卡上总的batch size.
  • YoloTrainFeed.batch_size: 当使用DataLoader时,表示单张卡上的batch size; 当使用普通reader时,则表示多卡上的总的batch_sizebatch_size受限于显存大小。
  • LeaningRate.base_lr: 根据多卡的总batch_size调整base_lr,两者大小正相关,可以简单的按比例进行调整。
  • LearningRate.schedulers.PiecewiseDecay.milestones: 请根据batch size的变化对其调整。
  • LearningRate.schedulers.PiecewiseDecay.LinearWarmup.steps: 请根据batch size的变化对其进行调整。

以下为4卡训练示例,通过命令行覆盖yolov3_mobilenet_v1_voc.yml中的参数:

python compress.py \
    -s yolov3_mobilenet_v1_slim.yaml \
    -c ../../configs/yolov3_mobilenet_v1_voc.yml \
    -o max_iters=258 \
    YoloTrainFeed.batch_size=64 \
    -d "../../dataset/voc"

以下为2卡训练示例,受显存所制,单卡batch_size不变,总batch_size减小,base_lr减小,一个epoch内batch数量增加,同时需要调整学习率相关参数,如下:

python compress.py \
    -s yolov3_mobilenet_v1_slim.yaml \
    -c ../../configs/yolov3_mobilenet_v1_voc.yml \
    -o max_iters=516 \
    LeaningRate.base_lr=0.005 \
    YoloTrainFeed.batch_size=32 \
    LearningRate.schedulers='[!PiecewiseDecay {gamma: 0.1, milestones: [110000, 124000]}, !LinearWarmup {start_factor: 0., steps: 2000}]' \
    -d "../../dataset/voc"

通过python compress.py --help查看可配置参数。 通过python ../../tools/configure.py ${option_name} help查看如何通过命令行覆盖配置文件yolov3_mobilenet_v1_voc.yml中的参数。

保存断点(checkpoint)

如果在配置文件中设置了checkpoint_path, 则在压缩任务执行过程中会自动保存断点,当任务异常中断时, 重启任务会自动从checkpoint_path路径下按数字顺序加载最新的checkpoint文件。如果不想让重启的任务从断点恢复, 需要修改配置文件中的checkpoint_path,或者将checkpoint_path路径下文件清空。

注意:配置文件中的信息不会保存在断点中,重启前对配置文件的修改将会生效。

评估

如果在配置文件中设置了checkpoint_path,则每个epoch会保存一个压缩后的用于评估的模型, 该模型会保存在${checkpoint_path}/${epoch_id}/eval_model/路径下,包含__model____params__两个文件。 其中,__model__用于保存模型结构信息,__params__用于保存参数(parameters)信息。

如果不需要保存评估模型,可以在定义Compressor对象时,将save_eval_model选项设置为False(默认为True)。

运行命令为:

python ../eval.py \
    --model_path ${checkpoint_path}/${epoch_id}/eval_model/ \
    --model_name __model__ \
    --params_name __params__ \
    -c ../../configs/yolov3_mobilenet_v1_voc.yml \
    -d "../../dataset/voc"

预测

如果在配置文件中设置了checkpoint_path,并且在定义Compressor对象时指定了prune_infer_model选项,则每个epoch都会 保存一个inference model。该模型是通过删除eval_program中多余的operators而得到的。

该模型会保存在${checkpoint_path}/${epoch_id}/eval_model/路径下,包含__model__.infer__params__两个文件。 其中,__model__.infer用于保存模型结构信息,__params__用于保存参数(parameters)信息。

更多关于prune_infer_model选项的介绍,请参考:Compressor介绍

python预测

在脚本PaddleDetection/tools/infer.py中展示了如何使用fluid python API加载使用预测模型进行预测。

运行命令为:

python ../infer.py \
    --model_path ${checkpoint_path}/${epoch_id}/eval_model/ \
    --model_name __model__.infer \
    --params_name __params__ \
    -c ../../configs/yolov3_mobilenet_v1_voc.yml \
    --infer_dir ../../demo

PaddleLite

该示例中产出的预测(inference)模型可以直接用PaddleLite进行加载使用。 关于PaddleLite如何使用,请参考:PaddleLite使用文档

示例结果

当前release的结果并非超参调优后的最好结果,仅做示例参考,后续我们会优化当前结果。

MobileNetV1-YOLO-V3

FLOPS Box AP model_size Paddle Fluid inference time(ms) Paddle Lite inference time(ms)
baseline 76.2 93M - -
-50% 69.48 51M - -

FAQ