Android에서 TensorFlow 실행하기

Google은 기계 학습을 구현하기 위해 Android에서 사용할 수있는 TensorFlow라는 라이브러리를 오픈 소스로 제공합니다. TensorFlow는 Google에서 제공하는 Machine Intelligence 용 오픈 소스 소프트웨어 라이브러리입니다.

인터넷을 많이 검색했지만 Android 용 TensorFlow를 만드는 간단한 방법이나 간단한 예제를 찾지 못했습니다. 알려진 정보를 토대로 잘 조합하여 빌드 할 수 있게 되었습니다. 이런 과정들을 다른사람들도 쉽게 이해 할 수 있도록 공유하기위해 글을 쓰게 되었습니다.

이 글은 이미 기계 학습에 익숙하고 기계 학습을위한 모델 구축 방법을 알고있는 사람을 위해 작성된 글입니다. (예제에서는 사전 훈련 된 모델을 사용합니다). 기계 학습을 하기위한 벙법은 이미 많이 널려있으니 참고하시길 바랍니다.

Android용 TensorFlow 빌드하기

몇가지 알아두어야할 사항:

  • TensorFlow의 핵심은 C++로 작성되었습니다.
  • 안드로이드 용으로 빌드하려면 JNI(Java Native Interface)를 사용하여 loadModel, getPredictions 등과 같은 C++ 함수를 호출해야합니다.
  • JAVA API를 호출하여 작업을 쉽게사용 하기위해 C++ 컴파일 된 파일 인 .so (공유 객체) 파일과 네이티브 C++을 호출 할 JAVA API로 구성된 jar 파일을 갖습니다.
  • jar (자바 API)와 .so (C++ 컴파일 된) 파일이 필요합니다.
  • 사전 훈련 된 모델 파일과 분류를 위한 라벨 파일이 필요합니다.

개체를 탐지 할 수 있는 예제를 만들어 보겠습니다.

먼저 jar와 .so 파일을 만들어 봅시다.
TensorFlow를 JAVA기반의 안드로이드에서 작동 하기위해 so파일로 빌드하고 interface역할을 하기위해 jar이 필요합니다.

 

필요한 툴:

  • Android SDK 설치(Android Studio에 포함되어 있습니다.)
  • NDK를 설치합니다.
  • Bazel을 설치합니다. (Bazel은 TensorFlow의 기본 구축 시스템입니다.)

git clone –recurse-submodules https://github.com/tensorflow/tensorflow.git
참고 : –recurse-submodules명령을 통해 서브 모듈도 함께 내려받아야 합니다.

복제한 TensorFlow의 루트 디렉토리에서 WORKSPACE 파일을 찾아 편집합니다.

# Uncomment and update the paths in these entries to build the Android demo.
#android_sdk_repository(
# name = "androidsdk",
# api_level = 23,
# build_tools_version = "25.0.1",
# # Replace with path to Android SDK on your system
# path = "<PATH_TO_SDK>",
#)
#
#android_ndk_repository(
# name="androidndk",
# path="<PATH_TO_NDK>",
# api_level=14)

위에서 설치한 SDK, NDK경로를 지정합니다.

android_sdk_repository(
name = "androidsdk",
api_level = 23,
build_tools_version = "25.0.1",
# Replace with path to Android SDK on your system
path = "/Android/sdk/",
)
android_ndk_repository(
name="androidndk",
path="/android-ndk-r13/",
api_level=14)

.so 파일 빌드:

bazel build -c opt //tensorflow/contrib/android:libtensorflow_inference.so \
--crosstool_top=//external:android/crosstool \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchain \
--cpu=armeabi-v7a

armeabi-v7a를 타겟으로 빌드를 합니다.

빌드 후 아래 위치로 so파일이 생성됩니다.
bazel-bin/tensorflow/contrib/android/libtensorflow_inference.so

JAVA 빌드:

bazel build //tensorflow/contrib/android:android_tensorflow_inference_java

빌드후 아래 위치에 jar파일이 생성됩니다.
bazel-bin/tensorflow/contrib/android/libandroid_tensorflow_inference_java.jar

 

이제 jar와 .so 파일을 모두 가지고 있습니다. 이미 .so 파일과 jar 파일을 모두 빌드 했으므로 아래 프로젝트에서 직접 사용할 수 있습니다.

그러나 미리 훈련 된 모델과 레이블 파일이 필요합니다.

여기에서는 주어진 이미지에서 객체 감지를 수행하는 Google의 사전 훈련 된 모델을 사용합니다. (모델 다운로드)

다운받은 zip파일의 압축을 풀면 imagenet_comp_graph_label_strings.txt (객체의 레이블) 및 tensorflow_inception_graph.pb (사전 학습된 모델)가 생성됩니다.

 

Android Studio에서 Android 샘플 프로젝트를 만듭니다.

imagenet_comp_graph_label_strings.txt 및 tensorflow_inception_graph.pb를 assets 폴더에 넣습니다.
libandroid_tensorflow_inference_java.jar를 libs 폴더에 넣고 마우스 오른쪽 버튼을 클릭하여 라이브러리로 추가하십시오.

위에서 빌드한 jar파일과 so파일을 추가 합니다.

compile files('libs/libandroid_tensorflow_inference_java.jar’)

기본 디렉토리에 jniLibs 폴더를 만들고 libtensorflow_inference.so를 jniLibs/armeabi-v7a/ 폴더에 넣습니다.

 

이제 TensorFlow Java API를 호출 할 수 있습니다.

TensorFlow Java API는 TensorFlowInferenceface 클래스를 통해 필요한 모든 메소드를 제공합니다. TensorFlow Java API를 호출하여 모델경로를 지정하고, 예측을 얻기위해 이미지를 입력해보세요!

아래 주소를 통해 설명된 완벽한 예제 소스를 볼 수 있습니다.
https://github.com/MindorksOpenSource/AndroidTensorFlowMachineLearningExample

 

원문: https://blog.mindorks.com/android-tensorflow-machine-learning-example-ff0e9b2654cc#.thihnty8c

이와 별개로 Caffe를 안드로이드에서 작동하는 방법도 있습니다.
https://github.com/kmshack/TrafficLightsDetector-Android

안드로이드에서 Java와 NDK(C/C++) 성능에 대해서..

안드로이드 어플리케이션 개발을 위해 JAVA를 쓴다. 하지만 안드로이드에서 C/C++을 쓸수 있도록 구글에서는 NDK라는 것을 만들었다. 그렇다면 NDK(C/C++)에서 JAVA보다 얼마만큼의 성능을 가지는가에 대한 궁금증이 생겼다.

이런 궁금증을 해결 하기위해 용어먼저 설명 하겠다.

NDK

안드로이드는 JAVA를 기반으로 어플리케이션을 만들수 있는데, 기존 C/C++로 된 라이브러리를 JAVA로 재 개발 해야 되는 문제점들을 위해 구글에서 네이티브코드로 만들 수 있는 방법을 제공한 개발킷이다.

JNI

JAVA에서 NDK의 C/C++을 연결 해주는 인터페이스 

그렇다면 수많은 연산을 통해 JAVA와 NDK의 성능을 테스트 해보겠다.

JAVA VS C

똑같은 연산을 하는 JAVA와 C코드를 준비 한다.

 

public int calc() {
    int count = 3;
    int i = 0;
    for (i = 0; i < 100000000; ++i) {
        count = count * 2 / 3 + 5 - 1;
    }

    return count;
}

jint Java_com_example_ndkspeedtest_MainActivity_calc( JNIEnv* env, jobject this)
{
    int count = 3;
    int i = 0;
    for ( i = 0; i < 100000000; ++i )
    {
        count = count * 2 / 3 + 5 - 1;
    }

    return count;
}

JAVA코드와 C코드의 각각 성능 측정을 하였다.

07-20 01:09:20.066: E/Test(19435): Java : 3377, ret : 10

07-20 01:09:22.716: E/Test(19435): native : 2650, ret : 10

—————————————————————-

20%

07-20 01:09:30.326: E/Test(19435): Java : 3366, ret : 10

07-20 01:09:32.966: E/Test(19435): native : 2640, ret : 10

—————————————————————-

27%

07-20 01:09:38.551: E/Test(19435): Java : 3315, ret : 10

07-20 01:09:41.196: E/Test(19435): native : 2645, ret : 10

—————————————————————-

25%

07-20 01:09:47.626: E/Test(19435): Java : 3373, ret : 10

07-20 01:09:50.301: E/Test(19435): native : 2671, ret : 10

—————————————————————-

27%

07-20 01:09:59.601: E/Test(19435): Java : 3362, ret : 10

07-20 01:10:02.276: E/Test(19435): native : 2672, ret : 10

—————————————————————-

25%
평균 C가 JAVA에 비해 25% 빠른 성능을 보여주고 있다. 
단순 연산만으로 측정한 값이며, 그래픽작업등 더 많은 연산의 경우 C가 더욱 우수한 성능을 보여줄 것으로 예상된다.

– powered by hoon038