MXNet编译至Android

来源:互联网 发布:智阳网络有哪些职位 编辑:程序博客网 时间:2024/06/04 17:48

MXNet版本更新很快,网上别人编译好的动态链接库很快就过时了,经常会出现某些方法不支持或者某个符号无法读取,这时就需要自己动手编译动态连接库so文件。这个地方无论是中文资料还是英文资料都很少,我折腾了两天才搞定,现在把我的方法记录一下。

工具

android-ndk-r13b
Ubutnu 14.04

过程

准备工作

在GitHub下载好OpenBLAS和MXNet的源代码,将OpenBLAS和MXNet放在同一个目录下面。我使用的MXNet版本号是0.7。

将NDK中工具链独立出来

cd /zhao/android-ndk-r13b/build/toolspython make_standalone_toolchain.py --arch arm --api 21 --install-dir /zhao/android-toolchain

这样会把NDK中api21的工具链独立出来,api21对应的是Android5.0。其中cd命令和--install-dir根据自己实际情况进行更改。这一步的效果是,我们可以在/zhao目录下面生成一个android-toolchain的文件夹,里面就是api21对应的ndk工具。

编译OpenBLAS

cd OpenBLASmake cleanexport PATH=$PATH:/zhao/android-toolchain/binmake TARGET=ARMV7 HOSTCC=gcc CC=arm-linux-androideabi-gcc NOFORTRAN=1

其中环境变量的路径名根据自己实际情况修改。成功的标志是提示已经生成了libopenblas_armv7p-r0.2.20.dev.a文件。

编译MXNet

这一步是最容易出错的一步。

我们先进入到/mxnet/amalgamation路径下。修改一下Makefile文件,在文件前面加上以下语句:

export SYS_ROOT=/zhao/android-toolchain/sysrootexport INCLUDE=/zhao/android-toolchain/incde/c++/4.9.xexport CXX=/zhao/android-toolchain/bin/arm-linux-androideabi-g++export CC=/zhao/android-toolchain/bin/arm-linux-androideabi-gcc

具体的路径可以根据自己实际情况修改。

随后在终端中使用make进行编译:

make cleanmake ANDROID=1

这时会出现很多warning信息,这些都可以忽略,只需要注意error信息即可。如果错误提示缺少jni.h这样的头文件,那么就是环境变量设置有问题,需要仔细检查环境变量的设置。

这一步正常进行的标志是生成了mxnet_predict-all.cc文件,但是仍会报错,提示找不到mkl相关文件,比如:

jni/../mxnet_predict-all.cc:42:30: fatal error: mxnet/mkl_memory.h: No such file or directory

这是因为mxnet_predict-all.cc找不到头文件,这些头文件在android上并不需要。打开mxnet_predict-all.cc文件,注释掉所有和mkl相关的头文件,如果还出错,继续注释出错头文件。还有omp.h也需要注释掉,这个头文件错误比较隐蔽。

最终生成的jni_libmxnet_predict.so文件大小约为7M,重命名为libmxnet_predict.so就可以放到android工程里了。

Hack

由于amalgamation很久没有更新了,遇到一些问题也不足为怪。我遇到的一个问题是softmax_activation操作符找不到,这时就需要自己Hack了。

定位错误

打开mxnet_predict-all.cc文件,发现里面没有softmax_activation相关代码,由此可以判断错误的原因是没有加载softmax_activation.cc源代码。

这样就需要简单阅读makefile文件,寻找生成mxnet_predict-all.cc的那一步:

mxnet_predict-all.cc:  mxnet_predict0.d mxnet_predict0.cc    @echo "Generating amalgamation to " $@    python ./amalgamation.py $+ $@ $(MIN) $(ANDROID)

这段代码的大概意思是说:要生成mxnet_predict-all.cc需要两个文件mxnet_predict0.dmxnet_predict0.cc,生成的方法是使用amalgamation.py。我先怀疑是不是amalgamation.py中间加载过程出错,我把加载的文件名称保存下来就可以判断了。在amalgamation.py中加入代码:

sources = get_sources(sys.argv[1])with open("name.txt",'w') as f:    f.write(str(sources))

所有加载的文件名称都保存在name.txt中,打开发现根本没有softmax_activation文件的影子。为什么别的文件都能加载,但softmax_activation加载不进来呢,于是我把传入amalgamation.py的输入参数打印下来:

['./amalgamation.py', 'mxnet_predict0.d', 'mxnet_predict0.cc', 'mxnet_predict-all.cc', '0', '1']

四个参数就打印出来,结合对源代码的分析,所有加载文件的目录应该是由mxnet_predict0.d文件决定的。打开mxnet_predict0.d'文件,发现下面全是要加载的源代码路径,由此可以判断mxnet_predict-all.cc中的所有加载代码路径是由mxnet_predict0.d决定的。可是初始情况下并没有mxnet_predict0.d这个文件,再次打开makefile:

mxnet_predict0.d: mxnet_predict0.cc    ${CXX} ${CFLAGS} -MD -MF $@ \    -I ${MXNET_ROOT}/ -I ${MXNET_ROOT}/mshadow/ -I ${MXNET_ROOT}/dmlc-core/include \    -I ${MXNET_ROOT}/include \    -D__MIN__=$(MIN) -c $+    rm mxnet_predict0.o

mxnet_predict0.d文件是由mxnet_predict0.cc生成的,那么这个mxnet_predict0.cc里面是什么呢?里面全是一些头文件:

#include "src/ndarray/ndarray_function.cc"#include "src/ndarray/ndarray.cc"#include "src/engine/engine.cc"#include "src/engine/naive_engine.cc"#include "src/symbol/graph_executor.cc"#include "src/symbol/graph_memory_allocator.cc"#include "src/symbol/static_graph.cc"

由此可以判断,mxnet_predict-all.cc中内容是由mxnet_predict0.cc决定的,如果需要加载softmax_activation,就需要加入相关的头文件,只需要加上一行:

#include "src/operator/softmax_activation.cc"

再次编译一下,发现softmax_activation.cc已经加载进去了,最终在Android上测试成功。

0 0
原创粉丝点击