-
Notifications
You must be signed in to change notification settings - Fork 2
paddle mobile编译
下面的编译过程要求在linux环境上进行,由于编译过程中会自动下载依赖包,所以机器要求能联网,OSX环境会出现各种奇怪的问题,暂时还没有解决。编译过程主要参考官方paddle mobile中相关的文档,但这些文档非常分散,而且很容易给读者造成困扰。本文结合实践将编译和部署的整个过程记录一下。
一般情况下,将代码编译成android平台的可执行程序可以使用Android NDK提供的ndk-build工具,ndk-build依赖jni文件夹,所以需要将代码放至jni目录下,另外还必须得有Android.mk和Application.mk这两个文件。Android.mk文件中定义代码目录、源文件、依赖包和编译链接的一些信息,Application.mk中可以指定编译的目标平台等信息。更改代码或目标平台都可能需要对Application.mk和Android.mk进行改动,甚至需要改jni文件和java工程。
我们可以通过构建独立的工具链来为了避免多处改动,不同的目标平台只需要指定不同平台的独立工具链即可,并且独立工具链可以结合cmake进行交叉编译。
- 首先下载Android NDK
wget https://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip
unzip android-ndk-r14b-linux-x86_64.zip
- 构建工具链
./android-ndk-r14b/build/tools/make-standalone-toolchain.sh --arch=arm --platform=android-9 --install-dir=/home/nlp/chenhoujiang/android-toolchain --toolchain=arm-linux-androideabi-4.9
参数解析:
--arch: 指定目标平台的CPU架构,比如arm/arm64/x86/mips。有些安卓机器使用的是64位的CPU可以使用arm64,一般arm v7(32位)架构的都用arm,64位也可以使用arm。
--platform:指定目标机器的最低API level,android-9代表的安卓版本为Android 2.3([参考官网](src https://developer.android.com/guide/topics/manifest/uses-sdk-element.html))。
--toolchain:指定工具链名称,会将指定的工具链从ndk目录下拷贝到构建的独立工具链中。支持的工具链可以到ndk目录的toolchains下查看。
##交叉编译paddle
下载paddle
git clone https://github.com/PaddlePaddle/Paddle.git Paddle
cd Paddle
在Paddle目录下新建文件build-android.sh,文件内容如下:
rm -rf build_android && mkdir build_android
cd build_android
cmake -DCMAKE_SYSTEM_NAME=Android \
-DANDROID_STANDALONE_TOOLCHAIN=/home/nlp/chenhoujiang/android-toolchain \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DANDROID_ABI="armeabi-v7a" \
-DANDROID_NATIVE_API_LEVEL=android-16 \
-DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 \
-DANDROID_ARM_NEON=ON \
-DANDROID_ARM_MODE=ON \
-DUSE_EIGEN_FOR_BLAS=ON \
-DCMAKE_INSTALL_PREFIX=./install \
-DWITH_C_API=ON \
-DWITH_SWIG_PY=OFF \
-DWITH_GPU=OFF \
-DWITH_TESTING=OFF \
-DWITH_PYTHON=OFF \
-DWITH_GOLANG=OFF \
-DGLIDE_INSTALL=OFF \
..
make -j10
make install
注意:
可能在cmake的过程中会报错"No CMAKE_Go_COMPILER could be found",这时需要手动设置一下go的路径,比如设置-DCMAKE_Go_COMPILER=/home/xxx/xxx/go/bin/go。
paddle mobile文档中说设置CMAKE_SYSTEM_NAME=Android会自动将WITH_PYTHON、WITH_AVX等设为OFF,实际上并没有,所以一开始我们编出来的静态库有100多M,动态库也有70多M。手动OFF一些选项后,最终在一台ubuntu的虚拟机上编译的paddle_capi_whole静态库在10M左右,另一台centos机器上编出来是13Mz左右。完整的编译命令如上所示。
安卓demo部分参考了dangbo的ncnn-mobile项目,使用paddle capi重写了jni文件,具体可以参考code中jni目录下的mobilenet_jni.cpp文件。
- 初始化paddle
// init paddle
if (paddle_init(0 , NULL) != kPD_NO_ERROR) {
__android_log_print(ANDROID_LOG_ERROR, "MobileNet", "init paddle failed");
}
- 加载模型参数
paddle_error ret = paddle_gradient_machine_create_for_inference_with_parameters(
&machine, mobilenet_bin.data(), len);
- 数据预处理
由于paddle卷积要求图片是BGR格式的,因此对于RGBA的输入需要做一次转换,并且对数据进行预处理。
// RGBA --> RGB
const float mean_vals[3] = {103.94, 116.78, 123.68};
const float norm_vals[3] = {0.017, 0.017, 0.017};
for (int i = 0; i < width * height; ++i) {
array_r[i] = (indata_t[2] - mean_vals[0]) * norm_vals[0];
array_g[i] = (indata_t[1] - mean_vals[1]) * norm_vals[1];
array_b[i] = (indata_t[0] - mean_vals[2]) * norm_vals[2];
indata_t += 4;
}
- 编译
可以使用ndk-build,也可以使用上面生成的独立工具链进行编译。使用ndk-build编译时需要将mobilenet_jni.cpp文件放在jni目录下,并在jni目录下写好对应的Android.mk和Application.mk文件,需要注意的是编译时需要全链接paddle的libpaddle_capi_whole.a库,最后在jni同级目录下执行ndk-build命令,没有出错的话编译好的文件应该已经在libs目录下了。如果使用独立工具链编译,可以写一个cmake文件(在code的jni目录下已经存在),编译时执行如下命令:
rm -rf build && mkdir build
cd build
cmake .. \
-DANDROID_ABI=armeabi-v7a \
-DANDROID_STANDALONE_TOOLCHAIN=/home/xxx/xxx/android-toolchain \
-DPADDLE_ROOT=/home/xxx/xxx/Paddle/build_android/install \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DBUILD_SHARED_LIBS=ON
make
再将编好的文件拷贝到libs目录下对应的ANDROID_ABI目录即可。
-
caffe2paddle
将caffe预训练的mobilenet模型转换成paddle模型,转换时要求已经安装好了python paddlepaddle和python caffe。 转换过程可以参考paddlepaddle关于caffe2paddle的说明。 -
合并模型
准备paddle mobilenet的模型结构文件,并将最后一层全连接层用1*1的卷积层替换,使用merge脚本将模型结构文件和模型参数合并成一个模型文件。详细过程可以参考paddlepaddle关于合并模型的说明。