Android에 Edge 배포 가이드

빌드 대상

이 가이드에서는 AutoML Vision Edge에서 만들어 내보낸 커스텀 TensorFlow Lite 모델을 다운로드합니다. 그런 다음 모델을 사용하여 꽃 이미지를 식별하는 미리 제작된 Android 앱을 실행합니다.

최종 제품 모바일 스크린샷
이미지 출처: 펠리페 베난시오, '어머니의 정원에서'(CC BY 2.0, 앱에 표시된 이미지)

목표

이 입문 내용에서는 코드를 사용해 다음을 수행하는 전반적인 과정을 둘러봅니다.

  • Android 앱에서 TFLite 인터프리터를 사용하여 선행 학습된 모델을 실행합니다.

시작하기 전에

AutoML Vision Edge에서 모델 학습

모델을 Edge 기기에 배포하려면 먼저 Edge 기기 모델 빠른 시작에 따라 AutoML Vision Edge에서 TF Lite 모델을 학습하고 내보내야 합니다.

빠른 시작을 완료한 후에 아래와 같이 학습된 모델 파일인 TF Lite 파일, 라벨 파일, 메타데이터 파일을 내보내야 합니다.

Cloud Storage TF Lite 모델 파일

TensorFlow 설치

가이드를 시작하기 전에 몇 가지 소프트웨어를 설치해야 합니다.

Python이 설치되어 있으면 다음 명령어를 실행하여 이 소프트웨어를 다운로드합니다.

pip install --upgrade  "tensorflow==1.7.*"
pip install PILLOW

이 프로세스에 문제가 발생하면 공식 TensorFlow 문서를 참조하세요.

Git 저장소 클론

명령줄에서 다음 명령어를 사용하여 Git 저장소를 클론합니다.

git clone https://github.com/googlecodelabs/tensorflow-for-poets-2

저장소 로컬 클론의 디렉터리로 이동합니다(tensorflow-for-poets-2 디렉터리). 이 디렉토리에서 다음 코드 샘플을 모두 실행합니다.

cd tensorflow-for-poets-2

Android 앱 설정

Android 스튜디오 설치

필요한 경우 로컬에서 Android Studio 3.0+를 설치합니다.

Android 스튜디오로 프로젝트 열기

다음 단계에 따라 Android 스튜디오로 프로젝트를 엽니다.

  1. Android 스튜디오 Android 스튜디오 시작 아이콘를 엽니다. 앱이 로드되면 다음 팝업에서 Android 스튜디오 프로젝트 열기 아이콘 'Open an existing Android Studio project(기존 Android 스튜디오 프로젝트 열기)'를 선택합니다.

    Android 스튜디오 프로젝트 열기 팝업

  2. 파일 선택기에서 작업 디렉터리의 tensorflow-for-poets-2/android/tflite를 선택합니다.

  3. 프로젝트를 처음 열면 Gradle 래퍼를 사용할지 묻는 'Gradle Sync(Gradle 동기화)' 팝업이 표시됩니다. 'OK(확인)'를 선택합니다.

    Android 스튜디오 프로젝트 열기 팝업

앱 테스트 실행

앱은 실제 Android 기기 또는 Android 스튜디오 에뮬레이터에서 실행할 수 있습니다.

Android 기기 설정

'개발자 모드'와 'USB 디버깅'을 활성화하지 않으면 Android 스튜디오의 앱을 스마트폰에 로드할 수 없습니다.

이 1회 설정 프로세스를 완료하려면 안내를 따르세요.

카메라 액세스로 에뮬레이터 설정(선택사항)

실제 Android 기기 대신 에뮬레이터를 사용할 경우 Android 스튜디오에서 간편하게 에뮬레이터를 설정할 수 있습니다.

이 앱은 카메라를 사용하므로 에뮬레이터의 카메라가 기본 테스트 패턴 대신 컴퓨터의 카메라를 사용하도록 설정하세요.

에뮬레이터의 카메라를 설정하려면 이 버튼 가상 기기 관리자 아이콘으로 액세스할 수 있는 서비스인 'Android Virtual Device Manager(Android Virtual Device 관리자)'에서 새 기기를 만들어야 합니다. 기본 AVDM 페이지에서 'Create Virtual Device(가상 기기 만들기)'를 선택합니다.

Android 스튜디오 가상 기기 만들기 옵션

그런 다음 가상 기기 설정의 마지막 페이지인 'Verify Configuration(구성 확인)' 페이지에서 'Show Advanced Settings(고급 설정 표시)'를 선택합니다.

Android 스튜디오 가상 기기 만들기 옵션

고급 설정이 표시되면 두 카메라 소스가 모두 호스트 컴퓨터의 웹캠을 사용하도록 설정할 수 있습니다.

Android 스튜디오 카메라 소스 선택 옵션

원래의 앱 실행

앱을 변경하기 전에 저장소와 함께 제공되는 버전을 실행합니다.

빌드 및 설치 프로세스를 시작하려면 Gradle 동기화를 실행합니다.

Gradle 동기화 아이콘

Gradle 동기화를 실행한 후 재생 Android 스튜디오 재생 아이콘을 선택합니다.

재생 버튼을 선택한 후 이 팝업에서 기기를 선택해야 합니다.

기기 선택 팝업 창

기기를 선택한 후 TensorFlow 데모가 카메라와 파일에 액세스할 수 있도록 허용해야 합니다.

카메라 액세스 허용 창

앱이 설치되었으면 앱 아이콘 Android 스튜디오 앱 아이콘을 클릭하여 앱을 실행합니다. 이 버전의 앱은 1,000개의 ImageNet 카테고리에 대해 선행 학습된 표준 MobileNet을 사용합니다.

다음과 같이 나타납니다.

테스트 앱 실행

맞춤설정된 앱 실행

기본 앱 설정은 재학습 없이 표준 MobileNet을 사용하여 이미지를 1,000개의 ImageNet 클래스 중 하나로 분류합니다.

이제 AutoML Vision Edge에서 만든 모델을 커스텀 이미지 카테고리에 사용하도록 앱을 수정합니다.

모델 파일을 프로젝트에 추가

데모 프로젝트는 android/tflite/app/src/main/assets/ 디렉터리에서 graph.litelabels.txt 파일을 검색하도록 구성되었습니다.

다음 명령어를 사용하여 원본 파일 2개를 원하는 버전으로 바꿉니다.

cp [Downloads]/model.tflite android/tflite/app/src/main/assets/graph.lite
cp [Downloads]/dict.txt  android/tflite/app/src/main/assets/labels.txt

앱 수정

이 앱은 부동 모델을 사용하지만 AutoML Vision Edge에서 만든 모델은 양자화된 모델입니다. 앱에서 양자화된 모델을 사용하도록 코드를 일부 변경합니다.

클래스 구성원 정의와 ImageClassifier 초기화 메서드에서 labelProbArrayfilterLabelProbArray의 데이터 유형을 부동(float)에서 바이트(byte)로 변경합니다.

private byte[][] labelProbArray = null;
private byte[][] filterLabelProbArray = null;

labelProbArray = new byte[1][labelList.size()];
filterLabelProbArray = new byte[FILTER_STAGES][labelList.size()];

ImageClassifier 초기화 메서드에서 int8 유형에 따라 imgData를 할당합니다.

    imgData =
        ByteBuffer.allocateDirect(
            DIM_BATCH_SIZE * DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y * DIM_PIXEL_SIZE);

printTopKLabels()에서 labelProbArray의 데이터 유형을 int8에서 부동으로 변환합니다.

  private String printTopKLabels() {
    for (int i = 0; i < labelList.size(); ++i) {
      sortedLabels.add(
          new AbstractMap.SimpleEntry<>(labelList.get(i), (float)labelProbArray[0][i]));
      if (sortedLabels.size() > RESULTS_TO_SHOW) {
        sortedLabels.poll();
      }
    }
    String textToShow = "";
    final int size = sortedLabels.size();
    for (int i = 0; i < size; ++i) {
      Map.Entry<String, Float> label = sortedLabels.poll();
      textToShow = String.format("\n%s: %4.2f",label.getKey(),label.getValue()) + textToShow;
    }
    return textToShow;
  }

앱 실행

빌드 시스템에서 파일을 찾을 수 있도록 Android 스튜디오에서 Gradle 동기화를 실행합니다.

Gradle 동기화 아이콘

Gradle 동기화를 실행한 다음 재생 Android 스튜디오 재생 아이콘을 선택하여 이전과 같이 빌드 및 설치 프로세스를 시작합니다.

다음과 같이 나타납니다.

최종 제품 모바일 스크린샷
이미지 출처: 펠리페 베난시오, '어머니의 정원에서'(CC BY 2.0, 앱에 표시된 이미지)

전원볼륨 다운 버튼을 동시에 누르면 스크린샷을 캡처할 수 있습니다.

카메라로 다양한 꽃 사진을 가리켜서 업데이트된 앱을 테스트하여 사진이 제대로 분류되는지 확인합니다.

작동 방식

이제 앱을 실행했으므로 TensorFlow Lite 관련 코드를 살펴보겠습니다.

TensorFlow-Android AAR

이 앱은 사전 컴파일된 TFLite Android 보관 파일(AAR)을 사용합니다. 이 AAR은 jcenter에서 호스팅됩니다.

모듈의 build.gradle 파일에 있는 다음 줄에는 프로젝트의 TensorFlow Bintray Maven 저장소에 있는 AAR의 최신 버전이 포함됩니다.

build.gradle

repositories {
    maven {
        url 'https://google.bintray.com/tensorflow'
    }
}

dependencies {
    // ...
    compile 'org.tensorflow:tensorflow-lite:+'
}

다음 블록을 사용하여 Android Asset Packaging Tool에 .lite 또는 .tflite 애셋을 압축하지 않도록 지시할 수 있습니다. 이 점이 중요한 이유는 .lite 파일의 메모리가 매핑되는데 파일이 압축되면 메모리 매핑이 작동하지 않기 때문입니다.

build.gradle

android {
    aaptOptions {
        noCompress "tflite"
        noCompress "lite"
    }
}

TFLite 자바 API 사용

TFLite에 대한 코드 상호작용은 모두 ImageClassifier.java에 포함됩니다.

설정

첫 번째로 살펴볼 블록은 ImageClassifier의 생성자입니다.

ImageClassifier.java

ImageClassifier(Activity activity) throws IOException {
    tflite = new Interpreter(loadModelFile(activity));
    labelList = loadLabelList(activity);
    imgData =
        ByteBuffer.allocateDirect(
            4 * DIM_BATCH_SIZE * DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y * DIM_PIXEL_SIZE);
    imgData.order(ByteOrder.nativeOrder());
    labelProbArray = new float[1][labelList.size()];
    Log.d(TAG, "Created a Tensorflow Lite Image Classifier.");
}

특히 중요한 몇 줄이 있습니다.

다음 줄은 TFLite 인터프리터를 생성합니다.

ImageClassifier.java

tflite = new Interpreter(loadModelFile(activity));

이 줄은 TFLite 인터프리터를 인스턴스화합니다. 이 인터프리터는 tf.Session(TFLite 외에 TensorFlow에 익숙한 사용자용)과 유사하게 작동합니다. 이 인터프리터에 모델이 포함된 MappedByteBuffer를 전달합니다. 로컬 함수 loadModelFile은 활동의 graph.lite 애셋 파일을 포함하는 MappedByteBuffer를 만듭니다.

다음 줄은 입력 데이터 버퍼를 만듭니다.

ImageClassifier.java

imgData = ByteBuffer.allocateDirect(
    4 * DIM_BATCH_SIZE * DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y * DIM_PIXEL_SIZE);

이 바이트 버퍼는 부동으로 변환되면 이미지 데이터를 포함하도록 크기가 조정됩니다. 인터프리터는 부동 배열을 바로 입력으로 사용할 수 있지만 ByteBuffer는 인터프리터에 있는 여분의 사본을 방지하므로 더 효율적입니다.

다음 줄은 라벨 목록을 로드하고 출력 버퍼를 생성합니다.

labelList = loadLabelList(activity);
//...
labelProbArray = new float[1][labelList.size()];

출력 버퍼는 모델이 출력 확률을 쓸 각 라벨에 하나의 요소를 포함하는 부동 배열입니다.

모델 실행

두 번째로 살펴볼 블록은 classifyFrame 메서드입니다. 이 메서드는 Bitmap을 입력으로 사용하고 앱에서 인쇄할 텍스트를 반환합니다.

ImageClassifier.java

String classifyFrame(Bitmap bitmap) {
 // ...
 convertBitmapToByteBuffer(bitmap);
 // ...
 tflite.run(imgData, labelProbArray);
 // ...
 String textToShow = printTopKLabels();
 // ...
}

이 메서드는 세 가지 작업을 수행합니다. 먼저 메서드는 입력 Bitmap을 변환하여 모델에 대한 입력으로 imgData ByteBuffer로 복사합니다. 그런 다음 인터프리터의 run 메서드를 호출하여 입력 버퍼와 출력 배열을 인수로 전달합니다. 인터프리터는 출력 배열의 값을 각 클래스에 대해 계산된 확률로 지정합니다. 입력 노드와 출력 노드는 앞에서 .lite 모델 파일을 생성한 toco 변환 단계의 인수로 정의됩니다.

다음 단계

이제 Edge 모델을 사용하여 Android 꽃 분류 앱의 둘러보기를 완료했습니다. 이미지 분류 앱을 수정하고 샘플 주석을 가져오기 전에 이 앱을 테스트했습니다. 그런 다음 TensorFlow Lite 관련 코드를 검사하여 기본 기능을 이해했습니다.

  • 공식 문서코드 저장소에서 TFLite 자세히 알아보기
  • 더 작은 패키지로 더 강력한 모델을 만들기 위해 이 데모 앱의 양자화 버전 사용해 보기
  • 음성 핫 워드 감지기와 스마트 답장의 기기별 버전을 포함하여 다른 TFLite 지원 모델 사용해보기
  • TensorFlow의 시작하기 문서에서 TensorFlow의 일반사항 자세히 알아보기