手机上的 AI - 在 Android/iOS 上运行 Caffe 深度学习框架

目录 开源

目前在云端基于各种深度学习框架的 AI 服务已经非常成熟,但最近的一些案例展示了在移动设备上直接运行深度神经网络模型能够带来很大的处理速度优势。比如 Facebook 在官方博客上发布的可在移动设备上进行实时视频风格转换的应用案例 “Delivering real-time AI in the palm of your hand”。其中提到 Caffe2go 框架加上优化后的网络模型,可以在 iPhone6S 上支持 20FPS 的视频风格转换。Google Tensorflow 也提供了 iOS 和 Android 的 example

Caffe 是一个知名的开源深度学习框架,在图像处理上有着非常广泛的应用。Caffe 本身基于 C++ 实现,有着非常简洁的代码结构,所以也有着很好的可移植性。早年也已经有了几个 github 项目实现了 Caffe 到 iOS/Android 平台的移植。但从我的角度来看,这些项目的编译依赖和编译过程都过于复杂,代码也不再更新,而且最终产出的产品包过大。caffe-compact 最接近我的思路,但是在两年前未完工就已经不更新了。

从我个人在 APP 产品上的经验来看,移植深度学习框架到 APP 中,不仅仅是能不能跑,跑不跑得快,还有个很重要的因素是包大小问题。因为一般用深度学习模型只是实现一个产品功能,不是整个产品。一个产品功能如果对 APP 包大小影响太大,很多 APP 产品都无法集成进去。我希望依赖库能尽量地精简,这样打包进 APP 的内容能尽量地少。所以我在春节期间在 github 上启动了一个 Caffe-Mobile 项目,将 Caffe 移植到 Android/iOS 上,并实现了以下目标:

NO_BACKWARD:手机的电量和计算能力都不允许进行模型训练,所以不如干脆移除所有的后向传播依赖代码,这样生成的库会更小,编译也更快。

最小的依赖。原始的 Caffe 依赖很多第三方库:protobuf, cblas, cuda, cudnn, gflags, glog, lmdb, leveldb, boost, opencv 等。但事实上很多依赖都是没必要的:cuda/cudnn 仅支持 GPU, gflags 仅为了支持命令行工具,lmdb/leveldb 是为了在训练时更高效地读写数据,opencv 是为了处理输入图片,很多 boost 库都可以用 c++0x 库来替换。经过精简和修改部分代码,Caffe-Mobile 的第三方库依赖缩减到两个:protobuf 和 cblas。其中在 iOS 平台上,cblas 用 Accelerate Framework 中的 vecLib 实现;在 Android 平台上, cblas 用交叉编译的 OpenBLAS 实现。

相同的代码基,相同的编译方式。两个平台都采取先用 cmake 编译 Caffe 库(.a or .so),然后再用对应平台的 IDE 集成到 app 中。编译脚本使用同一个 CMakeList.txt,无需将库的编译也放到复杂的 IDE 环境中去完成。

可随 Caffe 代码更新。为了保证开发者能追随最新 Caffe 代码更新,我在修改代码时使用了预编译宏进行分支控制。这样进行 diff/patch 时,如果 Caffe 源码改动较大,merge 时开发者可以清楚地看到哪些地方被修改,是如何改的,更方便 merge 最新更新。

除了 Caffe 库外,在 Caffe-Mobile 项目中还提供了 Android/iOS 两个平台上的最简单的 APP 实现示例 CaffeSimple,展示了在手机上使用 Caffe example 里的 MNIST 示例(深度学习领域的 Hello World)训练出来的 LeNet 模型预测一个手写字符 “8” 图片的过程和结果。 Caffe-Mobile 项目的地址在:https://github.com/solrex/caffe-mobile 欢迎体验,感兴趣的同学们也可以帮忙 Star 下 :)

长按识别二维码关注《边际效应》
长按识别二维码关注《边际效应》

12 条评论

  • 东海
    2017-03-01

    大牛,在尝试文章中提及方法编译Android的时候遇到一个问题。使用armeabi-v7a with NEON.修改两个文件中对应脚本,make时候报错:
    oideabi-4.9/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: ../../../third_party/protobuf/lib/libprotobuf.a(arena.cc.o): incompatible target
    /home/ldh/androidNDK/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: ../../../third_party/protobuf/lib/libprotobuf.a(arenastring.cc.o): incompatible target
    /home/ldh/androidNDK/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: ../../../third_party/protobuf/lib/libprotobuf.a(generated_message_util.cc.o): incompatible target

    • 菜鸟
      2017-03-01

      build-protobuf-3.1.0.sh
      build-openblas.sh
      命令:
      cmake .. -DCMAKE_TOOLCHAIN_FILE=../third_party/android-cmake/android.toolchain.cmake \
      -DANDROID_NDK=$NDK_HOME \
      -DANDROID_ABI="armeabi-v7a with NEON" \
      -DANDROID_NATIVE_API_LEVEL=21 \
      -DTHIRD_PARTY=1

    • Solrex
      2017-03-24

      v7a 上有问题,可以运行,但是输出不准,我怀疑跟浮点数精度有关,不建议现在使用。
      至于你说的问题,在clean环境中应该能解决,但是你得去那两个 .sh 中,修改 target。

      • xueyedamo
        2017-05-10

        您好,您的程序在加载某些网络模型loadmodel的时候会crash,比如model是resnet,但是model是cifar10的时候能够成功运行

    • Solrex
      2017-04-28

      这个问题已经解决,请 pull 最新的代码。之前的问题主要是 OpenBLAS 不支持 float-abi=softfp 所致,现在是采用 OpenBLAS 的一个 softfp 分支来编译 armv7,而不是使用 release 版本。

  • 菜鸟
    2017-03-27

    谢谢!

  • lyk_ffl
    2017-05-09

    你好,使用./tools/build_android.sh时,出现错误
    CMake Error: The source directory "/home/relaybot/android/caffe-mobile/third_party/protobuf-3.1.0/protobuf-3.1.0/cmake" does not exist.
    该如何解呢?

    • Solrex
      2017-05-10

      应该是脚本下载 protobuf 的 tar 包的时候,没有下载下来,或者没有下载完整。你检查一下 third_party/protobuf-3.1.0.tar.gz 是否能正常解压?

  • 吕雪
    2017-06-14

    "_cblas_dcopy", referenced from:
    void caffe::caffe_cpu_scale(int, double, double const*, double*) in libcaffe.a(math_functions.cpp.o)
    "_cblas_dasum", referenced from:
    double caffe::caffe_cpu_asum(int, double const*) in libcaffe.a(math_functions.cpp.o)
    "_vvsqrt", referenced from:
    void caffe::caffe_sqrt(int, double const*, double*) in libcaffe.a(math_functions.cpp.o)
    "_vvsqrtf", referenced from:
    void caffe::caffe_sqrt(int, float const*, float*) in libcaffe.a(math_functions.cpp.o)
    "_vDSP_vabsD", referenced from:
    void caffe::caffe_abs(int, double const*, double*) in libcaffe.a(math_functions.cpp.o)
    "_vDSP_vsqD", referenced from:
    void caffe::caffe_sqr(int, double const*, double*) in libcaffe.a(math_functions.cpp.o)
    "_vDSP_vabs", referenced from:
    void caffe::caffe_abs(int, float const*, float*) in libcaffe.a(math_functions.cpp.o)
    "_vDSP_vsq", referenced from:
    void caffe::caffe_sqr(int, float const*, float*) in libcaffe.a(math_functions.cpp.o)
    "_vvexpf", referenced from:
    void caffe::caffe_exp(int, float const*, float*) in libcaffe.a(math_functions.cpp.o)

    xcode平台可以正常运行 但是打包成.a文件 再引用 就报这个错误了

  • mambaxie
    2017-07-20

    希望能联系上作者,有一些问题请教!

发表评论

电子邮件地址不会被公开。 必填项已用*标注