裁剪预测库方法

Paddle-Lite支持根据模型裁剪预测库功能。Paddle-Lite的一般编译会将所有已注册的operator打包到预测库中,造成库文件体积膨胀;裁剪预测库能针对具体的模型,只打包优化后该模型需要的operator,有效降低预测库文件大小。

效果展示(Tiny_publish Android动态预测库体积)

测试模型 裁剪开关 libpaddle_lite_jni.so 转化后模型中的OP
mobilenetv1(armv8) 裁剪前–build_tailor=OFF 1.5M feed,etch,conv2d,depthwise_conv2d,fc,fpool2d,softmax
mobilenetv1(armv8) 裁剪后–build_tailor=ON 788K feed,etch,conv2d,depthwise_conv2d,fc,fpool2d,softmax
mobilenetv2(armv8) 裁剪前–build_tailor=OFF 1.5M feed,fetch,conv2d,depthwise_conv2d,elementwise_add,fc,pool2d,relu6,softmax
mobilenetv2(armv8) 裁剪后–build_tailor=ON 912K feed,fetch,conv2d,depthwise_conv2d,elementwise_add,fc,pool2d,relu6,softmax
inceptionv4(armv7) 裁剪前–build_tailor=OFF 938K feed,fetch,concat,conv2d,dropout,fc,pool2d,softmax
inceptionv4(armv7) 裁剪后–build_tailor=ON 512K feed,fetch,concat,conv2d,dropout,fc,pool2d,softmax
yolov3(armv7) 裁剪前–build_tailor=OFF 938K feed,fetch,concat,conv2d,depthwise_conv2d,multiclass_nms,nearest_interp,transpose2,yolo_box
yolov3(armv7) 裁剪后–build_tailor=ON 516K feed,fetch,concat,conv2d,depthwise_conv2d,multiclass_nms,nearest_interp,transpose2,yolo_box

实现过程:

1、转化模型时记录优化后模型信息

说明:使用model_optimize_tool转化模型时,选择 --record_tailoring_info =true 会将优化后模型的OP和kernel信息保存到输出文件夹,这些信息将用于编译裁剪后的动态库。 注意:需要使用Paddle-Lite 最新版本(release/v2.0.0之后)代码编译出的model_optimize_tool 例如:

./model_optimize_tool     --model_dir=./mobilenet_v1     --optimize_out_type=naive_buffer     --optimize_out=mobilenet_v1NB     --record_tailoring_info =true     --valid_targets=arm

效果:优化后模型使用的OP和kernel信息被保存在 mobilenet_v1NB文件夹中的隐藏文件里了

2、根据模型信息编译裁剪后的预测库

说明:编译Paddle-Lite时选择--build_tailor=ON ,并且用 –-opt_model_dir= 指定优化后的模型的地址 例如:

./lite/tools/build.sh   --arm_os=android   --arm_abi=armv7   --arm_lang=gcc   --android_stl=c++_static   --build_extra=ON --build_tailor=ON --opt_model_dir=../mobilenet_v1NB full_publish

注意:上面命令中的../mobilenet_v1NB是第1步得到的转化模型的输出路径

效果:编译出来的动态库文件变小,且可以运行优化后的模型。

编译出的C++预测库文件位于 :

build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/cxx/lib/

编译出的Java预测库文件位于:

build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/java/so/

3、运行裁剪后的预测库文件

注意:基于某一模型裁剪出的预测库只能支持优化工具转化后的该模型,例如根据mobilenetV1裁剪出的 full_api预测库只能运行以protobuf格式转化出的模型mobilenetV1_opt_nb, 裁剪出的light_api预测库只能运行以naive_buffer格式转化出的模型mobilenetV1_opt_nb, 运行其他模型可能会出现segementation fault:undifined op or kernel。 模型转化方法参考:使用model_optimize_tool转化模型)。

示例1:使用裁剪后的light_api预测库运行mobilenetv1

1、执行第二步编译后,light_api的C++ 示例位于

/Paddle-Lite/build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/demo/cxx/mobile_light

输入make命令执行编译可编译出可执行文件mobilenetv1_light_api

2、使用adb将mobilenetV1_NB模型和mobilenetv1_light_api传到手机后执行demo:

./mobilenetv1_light_api --model_dir=./mobilenetV1_NB

注意:mobilenetV1_NB是用mobilenetV1模型转化的naive_buffer格式模型(不需要设置 --record_tailoring_info =true,转化流程参考:使用model_optimize_tool转化模型)。

示例2:使用裁剪后的full_api预测库运行mobilenetv1

1、执行第二步编译后,full_api的C++ 示例位于

/Paddle-Lite/build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/demo/cxx/mobile_light

替换mobilenetv1_full_api.cc代码内容:

#include <gflags/gflags.h>
#include <stdio.h>
#include <vector>
#include "paddle_api.h"          // NOLINT
#include "paddle_use_kernels.h"  // NOLINT
#include "paddle_use_ops.h"      // NOLINT
#include "paddle_use_passes.h"   // NOLINT

using namespace paddle::lite_api;  // NOLINT

DEFINE_string(model_dir, "", "Model dir path.");

int64_t ShapeProduction(const shape_t& shape) {
  int64_t res = 1;
  for (auto i : shape) res *= i;
  return res;
}

void RunModel() {
  // 1. Set CxxConfig
  CxxConfig config;
  config.set_model_file(FLAGS_model_dir + "model");
  config.set_param_file(FLAGS_model_dir + "params");

  std::vector<Place> valid_places{Place{TARGET(kARM), PRECISION(kFloat)}};
  config.set_valid_places(valid_places);

  // 2. Create PaddlePredictor by CxxConfig
  std::shared_ptr<PaddlePredictor> predictor =
      CreatePaddlePredictor<CxxConfig>(config);

  // 3. Prepare input data
  std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
  input_tensor->Resize(shape_t({1, 3, 224, 224}));
  auto* data = input_tensor->mutable_data<float>();
  for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
    data[i] = 1;
  }

  // 4. Run predictor
  predictor->Run();

  // 5. Get output
  std::unique_ptr<const Tensor> output_tensor(
      std::move(predictor->GetOutput(0)));
  printf("Output dim: %d\n", output_tensor->shape()[1]);
  for (int i = 0; i < ShapeProduction(output_tensor->shape()); i += 100) {
    printf("Output[%d]: %f\n", i, output_tensor->data<float>()[i]);
  }
}

int main(int argc, char** argv) {
  google::ParseCommandLineFlags(&argc, &argv, true);
  RunModel();
  return 0;
}

2、使用adb将mobilenetV1_PB模型和mobilenetv1_full_api传到手机后执行demo:

./mobilenetv1_full_api --model_dir=./mobilenetV1_PB

注意:mobilenetV1_PB是用mobilenetV1模型转化的protobuf格式模型(不需要设置 --record_tailoring_info =true,转化流程参考:使用model_optimize_tool转化模型)。