• 텐서플로우로 이미지인식 모바일앱 만들어보기 - 5-1 (안드로이드용 예제 앱 실행해보기 - 기본예제 그대로) [머신러닝/딥러닝]
  • 공돌이
    조회 수: 74065, 2017.05.26 15:40:18
  • 지난시간에 그래프를 만들었는데요...

     

    이번시간에는 간략히 안드로이드용 앱의 구조를 살펴보기 위해 예제에서 제공하는 그래프를 다운로드받아서 사용해 보도록 하겠습니다.

    예제 앱은 잘 만든 앱이긴 하지만, 전체적인 안드로이드용 텐서플로우 적용앱의 구조를 설명하기에는 좀 복잡한 감이 있어서, 실행 테스트 후 다음시간에 좀 더 구조가 단순한 앱을 하나 만들어서 우리가 만든 그래프를 직접 올려보도록 하겠습니다.

     

    텐서플로우 github에서 다운로드 받은것을 보시면 잘 만들어진 안드로이드 예제가 있습니다.

     

    안드로이드용 예제코드는, 세개의 예제가 하나로 묶여 들어가 있습니다. (ImageDetector, ImageClassifier, Sytler)

    1. TF Classify: 구글 인셉션 모델(이미지인식)을 이용해서 카메라에 잡히는 물체를 실시간으로 인식하고, 결과(추측)를 화면에 오버레이해서 표시해주는 앱
    2. TF Detect: Scalable Object Detection using Deep Neural Networks 모델을 기반으로 카메라 프리뷰에 잡히는 사람을 실시간으로 추적
    3. TF Stylize:  A Learned Representation For Artistic Style 모델을 기반으로 카메라 프리뷰에 잡히는 이미지를 여러 예술가의 느낌으로 리스타일링

     

    안드로이드 텐서플로우 공식 예제 앱 돌려보기

     

    안드로이드에서 학습된 그래프를 사용해 입력받은 이미지를 인식하기 위해서 현재 순수하게 java로 만들어진 라이브러리는 없고, c++을 이용해서 만들어진 추론엔진을 가지고 와서 사용해야 합니다(아마 성능상의 이슈나 java에서 직접 처리하기 쉽지않은 부분들이 있겠죠?). 이를 위해서 텐서플로우에서는 c++라이브러리와 java에서 이 라이브러리를 사용할 수 있도록 JNI 라이브러리를 제공하고 있습니다.

     

    c++로 작성된 native library :libtensorflow_inference.so (하나 더 있는데, 데모를 위해서 만든거라, 나중에 자체 앱제작시에는 필요없으므로 설명은 생략합니다) 

    JNI library :  libandroid_tensorflow_inference_java.jar

     

    이를 준비하는 방법은 세가지가 있습니다

    a) JCenter에서 자동으로 다운로드 받는 방법( r1.2.0 프리뷰 버전부터 가능)

        : 제일 간단한데 얼마전 릴리즈된 1.2.0 프리뷰,rc0 버전에서 가능합니다. 지금 튜토리얼은 1.1.0 버전 기준으로 진행되던 건데, 내용이 약간 바뀌어서 이 튜토리얼 마무리하고 1.2프리뷰기준으로 좀 추가하려고 합니다. ㅠㅜ 

          이 옵션을 사용하면 소스에서 패키지 ( *.so 파일과 *.jar 파일)들을 빌드하는게 아니라 JCenter에서 패키지를 다운받게 됩니다. 아마 제일 빠르고 간단한 방법이겠지요.

          만약 github 소스를 1.2.0 버전을 받으셨다면 간단히 build.gradle 파일에서  nativeBuildSsystem 값만 none 으로 아래처럼 변경해주시면 됩니다.

       def nativeBuildSystem = 'none'

      만약 기존의 github 소스를 사용하고 계신거라면, build.gradle 의 dependency부분에 아래 의존성을 직접 추가해주시거나

     

    dependencies {
        ...
        compile 'org.tensorflow:tensorflow-android:1.2.0-rc0'
    }
    

     

    아니면 안드로이드스튜디오 메뉴에서 File -> Project Structure... 를 선택하시고 app -> dependencies 탭을 선택한 후 + 버튼을 누르시고

    Project Structure 2017-05-26 11-31-15.png

     

     

    표시되는 검색창에서 tensorflow 라고 치고 검색하시면 라이브러리가 나옵니다. (첫번째에 나오네요) 그걸 선택하셔도 됩니다.

     

    Choose Library Dependency 2017-05-26 11-31-59.png

     

    요기까지만 하시면 이제 바로 실행해보실 수 있습니다...^^ (빌드하면서 모델파일을 다운로드 받습니다)

     

    이렇게 하면 JNI 들을 빌드 따로 하지 않고 그냥 다운로드 받아버리니까 엄청 빨리 되겠죠. 

    다만, 기존 1.1.0 버전 안드로이드 소스를 가지고 하는 경우에는 버전이 바뀌면서 호출방식 등 변경된 부분이 있을수 있어서 오류 가능성이 있습니다. (이거는 제가 테스트해보고 따로 올릴게요) 1.2.0 버전 다운받으신 분들은 이 방법이 제일 쉽고 빠릅니다.

     

    이 방법의 유일한 단점은, 기존의 JNI 소스를 변경해서 사용하려고 하면 이 방법은 사용할 수 없고, 직접 다 빌드해야 한다는 점 정도겠지요.

    나중에 익숙해지면 직접빌드할 필요성이 있겠지만, 한동안은 그럴 필요성이 없을거예요.

     

    b) nightly build 되어있는거 가져다 쓰기 :

    a) 방법보다는 약간 수작업이 들어가지만 이것도 상당히 빠르고 간편합니다.a 방법에 비해 가지는 장점은, 최신의 변경사항이 거의 바로바로 반영된다는 점입니다. (나이틀리 빌드) 

    http://ci.tensorflow.org/view/Nightly/job/nightly-android/ 

     

    여기 가셔서 가장 last successful build 를 다운로드 받으시면 됩니다. (zip 파일로 압축해서 받으시면 간편하죠)

    nightly-android [Jenkins] 2017-05-26 11-44-28.png

     

    다운받으신 후 압축파일을 풀면 아래와 같은 폴더구조가 보이실겁니다.

    :Users:jpkim:Downloads:archive 4:out:native:libtensorflow_inference.so:armeabi-v7a 2017-05-26 11-50-08.png

     

    급하신 분은 여기 있는 tensorflow_demo.apk 파일을 직접 설치해서 바로 실행해보셔도 되지만, 구조를 알기 위해서 하나하나 넣어서 직접 apk를 만들어보겠습니다.

    텐서플로우 모바일인식을 위해 공통적으로 필요한 파일은 우선 libandroid_tensorflow_inference_java.jar (JNI interface 라이브러리) 파일과 libtensorflow_inference.so 파일입니다. 그리고 이 데모 앱을 실행하기 위해서는 (카메라에서 들어온 이미지 YUV 값을 RGB로 변환하는 등의 작업을 하는) libtensorflow_demo.so 파일도 필요하겠네요.

    *.so 파일은 해당 폴더를 보시면 아키텍쳐별로 서브폴더가 만들어져서 그 안에 각각 동일한 기능의 so 파일이 만들어져 있습니다.

    이 방법에 대한 자세한 설명은 다음번 튜토리얼에서 자세히 설명하고, 지금은 간단히 넘어가겠습니다.

    libs, jniLibs 폴더에 각각 넣어주고, dependency 설정해주면 됩니다.

     

    c) 소스로부터 직접 빌드하는 방법  :

    약간 복잡하고 시간이 오래걸립니다. 하지만, 나중에 뭔가 core 의 내용을 수정하고 싶다거나 할땐 이방법밖에 없겠죠. 나중에 기회가 되면 또 설명하겠지만, 현재버전의 안드로이드 라이브러리에서는 몇가지 지원되지 않는 것(Multicore, memmapped graph 등) 들이 있어서, 이거 해결하려면 jni쪽을 좀 수정해줘야 합니다...ㅠㅜ 

     

    직접 빌드하기 위해서는 우선 또 한두가지를 다운로드 받으셔야 합니다...^_^ 

     

    1) Android SDK Build tool API 버전 23 이상 (안드로이드 스튜디오를 쓰시면 거기 포함되어 있을테니 경로와 버전확인만 한번 하시면 될듯)

       ==> 별도로 다운로드받으시려면: 여기로

    2) Android NDK : 최신버전 말고 이전버전인 버전 12b 가 필요합니다. 아마 별도로 다운로드 받으셔야 할듯 (여기)

     

    Android SDK와 NDK 가 설치된 경로를 적어두시고...

    (저는 SDK는 안드로이드스튜디오에서 설치한 라이브러리폴더인 /Users/내아이디/Library/Android/sdk 에 있는걸 사용하고,

    NDK만 별도로 다운로드 받아서  /Users/내아이디/Library/Android 밑에 ndk-r12b 라는 폴더에 설치했습니다)

     

    우선 다운로드 받았던 github source folder (~/tensor/tensorflow) 밑에 있는 WORKSPACE 라는 파일을 열어주세요.

    터미널에서는 vi 나 nano 등 손에 익은걸로 여시고, 파인더를 통해서 여신다면 맥의 텍스트편집기 말고 plain text를 편집할 수 있는 에디터를 사용하셔야 합니다.

     

    열어서 보시면 아래처럼 안드로이드 SDK와 NDK 경로를 지정해주는 부분이 위쪽에 보이실겁니다

     

     

    # Uncomment and update the paths in these entries to build the Android demo.

     #android_sdk_repository(

     #    name = "androidsdk",

     #    api_level = 23,

     #    # Ensure that you have the build_tools_version below installed in the 

     #    # SDK manager as it updates periodically.

     #    build_tools_version = "25.0.2",

     #    # Replace with path to Android SDK on your system

     #    path = "<PATH_TO_SDK>",

     #)

     #

    # Android NDK r12b is recommended (higher may cause issues with Bazel)

     #android_ndk_repository(

     #    name="androidndk",

     #    path="<PATH_TO_NDK>",

     #    # This needs to be 14 or higher to compile TensorFlow. 

     #    # Note that the NDK version is not the API level.

     #    api_level=14)

     

     

    여기 주석으로 되어있는 부분을 주석해제하시고 아래와 같이 만들어주세요.  (*** 는 제 아이디 모자이크처리한겁니다...^^ 경로는 각자 환경에 맞게)

     

     # Uncomment and update the paths in these entries to build the Android demo.

     android_sdk_repository(

         name = "androidsdk",

         api_level = 23,

         # Ensure that you have the build_tools_version below installed in the 

         # SDK manager as it updates periodically.

         build_tools_version = "25.0.2",

         # Replace with path to Android SDK on your system

         path = "/Users/***/Library/Android/sdk",

     )

     #

     # Android NDK r12b is recommended (higher may cause issues with Bazel)

     android_ndk_repository(

         name="androidndk",

         path="/Users/***/Library/Android/ndk-r12b",

         # This needs to be 14 or higher to compile TensorFlow. 

         # Note that the NDK version is not the API level.

         api_level=14)

     

     

    여기까지 다 하셨다면,  이제 빌드 하시면 됩니다.

     

    몇가지 주의하실 점은,

    1) 현재 소스빌드 설정은 armeabi-v7 로만 빌드하도록 되어있습니다. 즉, 에뮬레이터에서 돌리면 오류나요...ㅠㅜ 실 기기에 설치해서 돌려보셔야 합니다.

    2) 그래프파일/라벨파일: 각 앱에서 사용할  .pb 파일과 .txt 파일은 빌드하면서 다운로드받도록 설정되어있습니다. 별도로 다운받거나 자기가 만든 파일로 바꿔치기하실 수도 있습니다만, 여기서는 생략할게요. 

     

    이제, 안드로이드 스튜디오에서 직접 빌드 혹은 (실기기 연결한 상태에서) Run 하시거나,

     

    터미널에서 WORKSPACE 파일이 있는 깃헙 소스 루트폴더 (튜토리얼대로 하셨다면 ~/tensor/tensorflow ) 로 가셔서 아래의 명령을 수행해 줍니다.

     

    bazel build -c opt //tensorflow/examples/android:tensorflow_demo

     

    시간이 꽤 걸리는 작업이니 커피한잔 드시고, 담배태우시면 담배도 한대 태우시고 잠시 노닥거리다가 오셔도 좋습니다.

     

    터미널로 빌드하셨다면, 깃헙 소스 루트 밑에 bazel-bin 폴더 (~/tensor/tensorflow/bazel-bin/tensorflow/examples/android/ ) 안에  APK 파일이 생성되어 있을테니

    아래의 명령어로 기기에 직접 설치하실 수 있습니다.

    adb install -r bazel-bin/tensorflow/examples/android/tensorflow_demo.apk

     

     

    기기를 살펴보시면 3개의 앱이 설치된 걸 확인하실 수 있습니다 (TFDetector, TFClassifier, TFStyler)

    각각 실행해서 가지고 놀아보시면 됩니다...^^

     

     

    자, 여기까지 했으면 기본적인거는 한번 해보신 셈인데요... 이제 슬슬 내가 따로 그래프 만들어서 나만의 앱을 만들고 싶어! 하시는 분들이 계실거라고 봅니다. (저도 그랬거든요)

     

    그거를 다음시간에 할텐데, 그 전에 잠시 앱에서 필요한 부분들의 구조를 잠시 살펴보고 넘어가겠습니다.

     

     

    jar 파일을 디컴파일해서 보시면 텐서플로우 inference(인식) 부분의 코어기능들이 JNI로 만들어져 있는것을 확인하실 수 있을겁니다.(몇개 빠져있어요 근데 ㅠㅜ)

    Tensor 환경 생성, inference 엔진에 데이터 피딩하기 등등... 복잡하고 저도 자세히 아는게 아니니 관심있는 분들은 한번씩 살펴보시고,

    (TensorflowInferenceInterface.java 부분이 직접 사용하는 핵심부분이니 요놈만 좀 살펴보셔도 좋아요)

     

    실제로 우리가 관심을 가져야 할 소스는 (다음번 튜토리얼에서 거의 그대로 가져다 쓸거임)

    인터페이스인 Classifier.java와 얘를 implement 한 TensorflowImageClassifier.java 입니다.

     

    TensorflowImageClassifier 를 이용해서, 좀 더 편하게 텐서플로우 인퍼런스인터페이스를 사용할 수가 있지요.

    그 중에 두개 메서드가 가장 중요합니다...ㅇㅇ

     

    create 메서드 : 텐서플로우 환경을 특정값으로 초기화하고, 모델로 쓸 그래프파일과 라벨파일을 메모리로 올려두는 역할을 합니다. 그래프파일의 용량이 큰 관계로, 호출할때는 executor를이용해서 비동기로 로드합니다. (예제기준)

    recognizeImage 메서드 : 핵심입니다. 비트맵(그림)을 입력값으로 받아서, 텐서가 연산할 수 있도록 float 값들의 행렬(일종의 패턴화된 숫자들)로 변환하여 텐서 객체에 feed 하고, 결과값( 라벨값과 확률치의 배열 - 여러개가 나올 수 있으므로)을 리턴하는 형태입니다.

     

    * 참고: 로직을 잘 보시면, 현재는 인셉션엔진(이미지인식)을 사용하는 거니까, 엔진에서 학습시 그래픽을 행렬로 변환하는 것과 동일한 방식으로 수치화->행렬화 해서 인식시키고 있지요? 즉, 다시말해서 다른 인식엔진(문자인식, 음성인식 등등)을 이용하면 (혹은 새로 만들면) 그 형태대로 수치화->행렬화 해주고 feed만 해주면 무엇이든 배우고 인식할 수 있게 되겠지요? 응용은 각자 익숙해진 후에 아이디어를 내서 알아서...^^

     

     

     

    이번시간은 여기까지입니다...휴

     

    여행갔다왔다가 다시 귀차니즘으로 차일피일 미루다가, 이제 다시 쓰네요. 그동안 1.2.0rc0 버전이 나와서 튜토리얼도 개정을 좀 해야할것 같아요 

    (아마, 기존거는 제목만 1.1.0 으로 수정하고 1.2.0 은 복붙애서 새로 쓰는 형태가 될듯)

     

     

     

    * 참고2: 위에꺼 따라해보기 엄청 귀찮으신 분들은 jenkins nightly에서 prebuilt된 apk 파일만 다운로드받아서 직접 실행해보셔도 됩니다만, 그럼 이 튜토리얼 보시는 의미가 별로 없겠지요...ㅎㅎ