在Android shell下运行PaddlePaddle
a17631544326 发布于2018-03-23 15:31 浏览:422 回复:0
0
收藏

我们可以在https://github.com/PaddlePaddle/Mobile/ 找到关于PaddlePaddle应用于移动端的demo和例子。这篇文章以Android shell下运行图像分类模型为例子来讲述如何入门PaddlePaddle移动端。

内容
PaddlePaddle训练移动端的分类模型
对PaddlePaddle进行Android 交叉编译
Android shell 下运行分类模型


PaddlePaddle训练移动端的分类模型
在Android shell下运行PaddlePaddle 模型,我们要准备一个适用于一个移动端的分类模型。Repo 下提供了适用于移动端的mobilenet模型,我们用这个模型来对花卉进行分类。
一,下载mobilenet配置文件

wget https://raw.githubusercontent.com/PaddlePaddle/Mobile/develop/models/standard_network/mobilenet.py

二,下载pre-trained 模型参数文件
在百度云上下载在imagenet上预训练的mobilenet模型参数 imagenet_pretrained_mobilenet.tar.gz
三,在imagenet模型上对flower102数据集进行微调(fine-tune)
拷贝以下代码,然后运行,会在每个epoch 后保存参数文件。可以点击此处 下载好我已经训练的模型参数。

import sys
import gzip

from paddle.trainer_config_helpers import *
import paddle.v2 as paddle
from mobilenet import mobile_net

# batch 大小是40
BATCH = 40

def main():
    datadim = 3 * 224 * 224
    classdim = 102

    #  采用gpu训练并使用第一块卡
    paddle.init(use_gpu=True, trainer_count=1, gpu_id=0)

    momentum_optimizer = paddle.optimizer.Momentum(
        momentum=0.9,
        regularization=paddle.optimizer.L2Regularization(rate=0.0005 * BATCH),
        learning_rate=0.001 / BATCH,
        learning_rate_schedule='constant')

    out = mobile_net(datadim, classdim, 1.0)

    lbl = paddle.layer.data(
        name="label", type=paddle.data_type.integer_value(classdim))
    cost = paddle.layer.classification_cost(input=out, label=lbl)

    # Create parameters
    parameters = paddle.parameters.create(cost)
    # 加载imagenet 预训练的模型参数
    with gzip.open('imagenet_pretrained_mobilenet.tar.gz', 'r') as f:
        fparameters = paddle.parameters.Parameters.from_tar(f)
    for param_name in fparameters.names():
        if param_name in parameters.names():
            parameters.set(param_name, fparameters.get(param_name))

    # End batch and end pass event handler
    def event_handler(event):
        if isinstance(event, paddle.event.EndIteration):
            if event.batch_id % 50 == 0:
                print "\nPass %d, Batch %d, Cost %f, %s" % (
                    event.pass_id, event.batch_id, event.cost, event.metrics)
            else:
                sys.stdout.write('.')
                sys.stdout.flush()
        if isinstance(event, paddle.event.EndPass):
            # save parameters
            with gzip.open('pruning_mobilenet_params_pass_%d.tar.gz' %
                           event.pass_id, 'w') as f:
                parameters.to_tar(f)

            result = trainer.test(
                reader=paddle.batch(
                    paddle.dataset.flowers.test(), batch_size=10),
                feeding={'image': 0,
                         'label': 1})
            print "\nTest with Pass %d, %s" % (event.pass_id, result.metrics)

    # Create trainer
    trainer = paddle.trainer.SGD(
        cost=cost, parameters=parameters, update_equation=momentum_optimizer)
    trainer.train(
        reader=paddle.batch(
            paddle.reader.shuffle(
                paddle.dataset.flowers.train(), buf_size=50000),
            batch_size=BATCH),
        num_passes=100,
        event_handler=event_handler,
        feeding={'image': 0,
                 'label': 1})


if __name__ == '__main__':
    main()

经过微调我们的分类精度可以达到98% 左右。 现在我们有了一个.py 文件,表示模型的配置文件, 还有一个.tar.gz文件,表示模型的参数文件, 这两个文件组成了唯一的一个模型。

Android 交叉编译PaddlePaddle
我们需要让PaddlePaddle运行在Android平台,需要在linux或者mac下编译出能在android或者ios平台下运行的PaddlePaddle库文件。这个过程为交叉编译。

Paddle repo下提供了关于如何在android平台下进行交叉编译PaddlePaddle:
在链接相关页面中提供了两种方式, 一种是通过docker的方式,一种基于自定义独立工具链编译方式, 这两种方式,我都进行了实验,个人比较倾向于自定义的方式,因为比较直接透明。具体的使用方式如下:

一, 下载 Android NDK

wget -q https://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip
unzip -q android-ndk-r14b-linux-x86_64.zip

假设当前目录为 $CURRENT_DIR

二, 自定义工具链(--install-dir 表示安装路径,根据自己的需求设置,假设安装路径为$TOOLCHAIN_PATH )

$CURRENT_DIR/android-ndk-r14b-linux-x86_64/build/tools/make-standalone-toolchain.sh \
        --arch=arm --platform=android-21 --install-dir=$TOOLCHAIN_PATH/v7_standalone_toolchain

$TOOLCHAIN_PATH/v7_standalone_toolchain目录下的内容为我们刚刚生成的工具链。

三, 交叉编译PaddlePaddle

git clone https://github.com/PaddlePaddle/Paddle.git 
cd Paddle
#  建立docker 镜像
mkdir install 
mkdir build 
cd build

cmake -DCMAKE_SYSTEM_NAME=Android \
      -DANDROID_STANDALONE_TOOLCHAIN=$TOOLCHAIN_PATH/v7_standalone_toolchain \
      -DANDROID_ABI=armeabi-v7a \
      -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 \
       -DANDROID_TOOLCHAIN=gcc  \
      ..

make -j `nproc`
make install

编译结束后,会在 install/lib 目录下生成动态库libpaddle_capi_shared.so, 这个动态库提供了模型程序调用PaddlePaddle的所有入口。

Android shell 下运行分类模型
一, 下载预测程序
该程序功能是用来测试模型的运行速度,主要包括加载模型,随机化输入,多次进行模型前向运算并统计时间,然后输出模型的平均前向运行时间。

wget https://raw.githubusercontent.com/PaddlePaddle/Mobile/develop/benchmark/tool/C/inference.cc

二, 将libpaddle_capi_shared.so copy至当前目录
三, 编译预测脚本

export PATH=$TOOLCHAIN_PATH/v7_standalone_toolchain/bin/:$PATH
arm-linux-androideabi-g++ inference.cc -L./ -lpaddle_capi_shared -o inference -pie -fPIE

我们可以看到,目录中多了一个inference 可运行的二进制文件。

四,将之前提到的模型配置文件.py 和模型参数文件.tar.gz融合成一个文件

from paddle.utils.merge_model import merge_v2_model

# import your network configuration
from mobilenet import mobile_net

net = mobile_net(3*224*224, 102, 1.0)
param_file = './mobilenet_flowers102.tar.gz'
output_file = './mobilenet.paddle'

merge_v2_model(net, param_file, output_file)

五,安装adb
linux 安装,查看教程
mac 安装,查看教程

adb 工具可以登陆android 手机的shell,让我们像使用linux shell一样来操作android。
adb安装之后,我们使用数据线将android手机和电脑链接。

六, Android shell下运行Paddle分类模型

adb push inference libpaddle_capi_shared.so mobilenet.paddle /sdcard/test_mobilenet
adb shell
cd /sdcard/test_mobilenet
export LD_LIBRARY_PATH=./
./inference --merged_model ./mobilenet.paddle --input_size 150528

其中input_size 表示模型输入的大小,即3 * 224 * 224 等于 150528
如果看到以下的log,说明程序运行成功:

可以看到,paddle初始化的时间是1.78015ms, 加载模型的时间是113.749ms, 模型前向的时间是337.754ms.


作者:X_Dragon
链接:https://www.jianshu.com/p/91053654da98
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收藏
点赞
0
个赞
TOP
切换版块