Firebase Summit에서 발표된 모든 내용을 살펴보고 Firebase로 앱을 빠르게 개발하고 안심하고 앱을 실행하는 방법을 알아보세요. 자세히 알아보기

Android에서 맞춤형 TensorFlow Lite 모델 사용

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

앱에서 맞춤 TensorFlow Lite 모델을 사용하는 경우 Firebase ML을 사용하여 모델을 배포할 수 있습니다. Firebase를 사용하여 모델을 배포하면 앱의 새 버전을 출시하지 않고도 앱의 초기 다운로드 크기를 줄이고 앱의 ML 모델을 업데이트할 수 있습니다. 또한 원격 구성 및 A/B 테스팅을 통해 다양한 사용자 집합에 다양한 모델을 동적으로 제공할 수 있습니다.

TensorFlow Lite 모델

TensorFlow Lite 모델은 모바일 장치에서 실행하도록 최적화된 ML 모델입니다. TensorFlow Lite 모델을 가져오려면:

시작하기 전에

  1. 아직 Android 프로젝트에 Firebase를 추가 하지 않았다면 추가합니다.
  2. 모듈(앱 수준) Gradle 파일 (일반적으로 <project>/<app-module>/build.gradle )에서 Firebase ML 모델 다운로더 Android 라이브러리에 대한 종속성을 추가합니다. Firebase Android BoM 을 사용하여 라이브러리 버전 관리를 제어하는 ​​것이 좋습니다.

    또한 Firebase ML 모델 다운로더 설정의 일부로 TensorFlow Lite SDK를 앱에 추가해야 합니다.

    Java

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:31.1.0')
    
        // Add the dependency for the Firebase ML model downloader library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-modeldownloader'
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }

    Firebase Android BoM 을 사용하면 앱에서 항상 호환되는 Firebase Android 라이브러리 버전을 사용합니다.

    (또는) BoM을 사용 하지 않고 Firebase 라이브러리 종속성을 추가합니다.

    Firebase BoM을 사용하지 않기로 선택한 경우 종속성 줄에 각 Firebase 라이브러리 버전을 지정해야 합니다.

    앱에서 여러 Firebase 라이브러리를 사용하는 경우 모든 버전이 호환되도록 BoM을 사용하여 라이브러리 버전을 관리하는 것이 좋습니다.

    dependencies {
        // Add the dependency for the Firebase ML model downloader library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-modeldownloader:24.1.1'
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }

    Kotlin+KTX

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:31.1.0')
    
        // Add the dependency for the Firebase ML model downloader library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-modeldownloader-ktx'
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }

    Firebase Android BoM 을 사용하면 앱에서 항상 호환되는 Firebase Android 라이브러리 버전을 사용합니다.

    (또는) BoM을 사용 하지 않고 Firebase 라이브러리 종속성을 추가합니다.

    Firebase BoM을 사용하지 않기로 선택한 경우 종속성 줄에 각 Firebase 라이브러리 버전을 지정해야 합니다.

    앱에서 여러 Firebase 라이브러리를 사용하는 경우 모든 버전이 호환되도록 BoM을 사용하여 라이브러리 버전을 관리하는 것이 좋습니다.

    dependencies {
        // Add the dependency for the Firebase ML model downloader library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-modeldownloader-ktx:24.1.1'
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }
  3. 앱의 매니페스트에서 INTERNET 권한이 필요하다고 선언합니다.
    <uses-permission android:name="android.permission.INTERNET" />

1. 모델 배포

Firebase 콘솔 또는 Firebase Admin Python 및 Node.js SDK를 사용하여 맞춤 TensorFlow 모델을 배포합니다. 사용자 지정 모델 배포 및 관리를 참조하십시오.

Firebase 프로젝트에 맞춤 모델을 추가한 후 지정한 이름을 사용하여 앱에서 모델을 참조할 수 있습니다. 언제든지 getModel() 을 호출하여 새 TensorFlow Lite 모델을 배포하고 새 모델을 사용자의 기기에 다운로드할 수 있습니다(아래 참조).

2. 모델을 기기에 다운로드하고 TensorFlow Lite 인터프리터를 초기화합니다.

앱에서 TensorFlow Lite 모델을 사용하려면 먼저 Firebase ML SDK를 사용하여 최신 버전의 모델을 기기에 다운로드하세요. 그런 다음 모델로 TensorFlow Lite 인터프리터를 인스턴스화합니다.

모델 다운로드를 시작하려면 모델 다운로더의 getModel() 메서드를 호출하여 모델을 업로드할 때 할당한 이름, 항상 최신 모델을 다운로드할지 여부, 다운로드를 허용할 조건을 지정합니다.

세 가지 다운로드 동작 중에서 선택할 수 있습니다.

다운로드 유형 설명
LOCAL_MODEL 장치에서 로컬 모델을 가져옵니다. 사용 가능한 로컬 모델이 없으면 LATEST_MODEL 처럼 작동합니다. 모델 업데이트 확인에 관심이 없는 경우 이 다운로드 유형을 사용하십시오. 예를 들어 원격 구성을 사용하여 모델 이름을 검색하고 항상 새 이름으로 모델을 업로드합니다(권장).
LOCAL_MODEL_UPDATE_IN_BACKGROUND 장치에서 로컬 모델을 가져오고 백그라운드에서 모델 업데이트를 시작합니다. 사용 가능한 로컬 모델이 없으면 LATEST_MODEL 처럼 작동합니다.
최신_모델 최신 모델을 받으십시오. 로컬 모델이 최신 버전이면 로컬 모델을 반환합니다. 그렇지 않으면 최신 모델을 다운로드하십시오. 이 동작은 최신 버전이 다운로드될 때까지 차단됩니다(권장하지 않음). 명시적으로 최신 버전이 필요한 경우에만 이 동작을 사용하십시오.

모델이 다운로드되었음을 확인할 때까지 모델 관련 기능(예: UI 일부를 회색으로 표시하거나 숨기기)을 비활성화해야 합니다.

Java

CustomModelDownloadConditions conditions = new CustomModelDownloadConditions.Builder()
    .requireWifi()  // Also possible: .requireCharging() and .requireDeviceIdle()
    .build();
FirebaseModelDownloader.getInstance()
    .getModel("your_model", DownloadType.LOCAL_MODEL_UPDATE_IN_BACKGROUND, conditions)
    .addOnSuccessListener(new OnSuccessListener<CustomModel>() {
      @Override
      public void onSuccess(CustomModel model) {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.

        // The CustomModel object contains the local path of the model file,
        // which you can use to instantiate a TensorFlow Lite interpreter.
        File modelFile = model.getFile();
        if (modelFile != null) {
            interpreter = new Interpreter(modelFile);
        }
      }
    });

Kotlin+KTX

val conditions = CustomModelDownloadConditions.Builder()
        .requireWifi()  // Also possible: .requireCharging() and .requireDeviceIdle()
        .build()
FirebaseModelDownloader.getInstance()
        .getModel("your_model", DownloadType.LOCAL_MODEL_UPDATE_IN_BACKGROUND,
            conditions)
        .addOnSuccessListener { model: CustomModel? ->
            // Download complete. Depending on your app, you could enable the ML
            // feature, or switch from the local model to the remote model, etc.

            // The CustomModel object contains the local path of the model file,
            // which you can use to instantiate a TensorFlow Lite interpreter.
            val modelFile = model?.file
            if (modelFile != null) {
                interpreter = Interpreter(modelFile)
            }
        }

많은 앱이 초기화 코드에서 다운로드 작업을 시작하지만 모델을 사용하기 전에 언제든지 시작할 수 있습니다.

3. 입력 데이터에 대한 추론 수행

모델의 입력 및 출력 모양 가져오기

TensorFlow Lite 모델 인터프리터는 입력으로 하나 이상의 다차원 배열을 출력으로 생성합니다. 이러한 배열에는 byte , int , long 또는 float 값이 포함됩니다. 데이터를 모델에 전달하거나 그 결과를 사용하려면 먼저 모델이 사용하는 배열의 수와 차원("모양")을 알아야 합니다.

모델을 직접 구축했거나 모델의 입력 및 출력 형식이 문서화되어 있는 경우 이 정보가 이미 있을 수 있습니다. 모델 입력 및 출력의 모양과 데이터 유형을 모르는 경우 TensorFlow Lite 인터프리터를 사용하여 모델을 검사할 수 있습니다. 예를 들어:

파이썬

import tensorflow as tf

interpreter = tf.lite.Interpreter(model_path="your_model.tflite")
interpreter.allocate_tensors()

# Print input shape and type
inputs = interpreter.get_input_details()
print('{} input(s):'.format(len(inputs)))
for i in range(0, len(inputs)):
    print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype']))

# Print output shape and type
outputs = interpreter.get_output_details()
print('\n{} output(s):'.format(len(outputs)))
for i in range(0, len(outputs)):
    print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))

출력 예:

1 input(s):
[  1 224 224   3] <class 'numpy.float32'>

1 output(s):
[1 1000] <class 'numpy.float32'>

인터프리터 실행

모델의 입력 및 출력 형식을 결정한 후 입력 데이터를 가져오고 모델에 적합한 모양의 입력을 얻는 데 필요한 데이터에 대한 변환을 수행합니다.

예를 들어 입력 모양이 [1 224 224 3] 부동 소수점 값인 이미지 분류 모델이 있는 경우 다음 예제와 같이 Bitmap 객체에서 입력 ByteBuffer 를 생성할 수 있습니다.

Java

Bitmap bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true);
ByteBuffer input = ByteBuffer.allocateDirect(224 * 224 * 3 * 4).order(ByteOrder.nativeOrder());
for (int y = 0; y < 224; y++) {
    for (int x = 0; x < 224; x++) {
        int px = bitmap.getPixel(x, y);

        // Get channel values from the pixel value.
        int r = Color.red(px);
        int g = Color.green(px);
        int b = Color.blue(px);

        // Normalize channel values to [-1.0, 1.0]. This requirement depends
        // on the model. For example, some models might require values to be
        // normalized to the range [0.0, 1.0] instead.
        float rf = (r - 127) / 255.0f;
        float gf = (g - 127) / 255.0f;
        float bf = (b - 127) / 255.0f;

        input.putFloat(rf);
        input.putFloat(gf);
        input.putFloat(bf);
    }
}

Kotlin+KTX

val bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true)
val input = ByteBuffer.allocateDirect(224*224*3*4).order(ByteOrder.nativeOrder())
for (y in 0 until 224) {
    for (x in 0 until 224) {
        val px = bitmap.getPixel(x, y)

        // Get channel values from the pixel value.
        val r = Color.red(px)
        val g = Color.green(px)
        val b = Color.blue(px)

        // Normalize channel values to [-1.0, 1.0]. This requirement depends on the model.
        // For example, some models might require values to be normalized to the range
        // [0.0, 1.0] instead.
        val rf = (r - 127) / 255f
        val gf = (g - 127) / 255f
        val bf = (b - 127) / 255f

        input.putFloat(rf)
        input.putFloat(gf)
        input.putFloat(bf)
    }
}

그런 다음 모델의 출력을 포함할 만큼 충분히 큰 ByteBuffer 를 할당하고 입력 버퍼와 출력 버퍼를 TensorFlow Lite 인터프리터의 run() 메서드에 전달합니다. 예를 들어, [1 1000] 부동 소수점 값의 출력 모양:

Java

int bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE;
ByteBuffer modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder());
interpreter.run(input, modelOutput);

Kotlin+KTX

val bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE
val modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder())
interpreter?.run(input, modelOutput)

출력을 사용하는 방법은 사용 중인 모델에 따라 다릅니다.

예를 들어 분류를 수행하는 경우 다음 단계로 결과의 인덱스를 해당 레이블이 나타내는 레이블에 매핑할 수 있습니다.

Java

modelOutput.rewind();
FloatBuffer probabilities = modelOutput.asFloatBuffer();
try {
    BufferedReader reader = new BufferedReader(
            new InputStreamReader(getAssets().open("custom_labels.txt")));
    for (int i = 0; i < probabilities.capacity(); i++) {
        String label = reader.readLine();
        float probability = probabilities.get(i);
        Log.i(TAG, String.format("%s: %1.4f", label, probability));
    }
} catch (IOException e) {
    // File not found?
}

Kotlin+KTX

modelOutput.rewind()
val probabilities = modelOutput.asFloatBuffer()
try {
    val reader = BufferedReader(
            InputStreamReader(assets.open("custom_labels.txt")))
    for (i in probabilities.capacity()) {
        val label: String = reader.readLine()
        val probability = probabilities.get(i)
        println("$label: $probability")
    }
} catch (e: IOException) {
    // File not found?
}

부록: 모델 보안

TensorFlow Lite 모델을 Firebase ML에 제공하는 방법에 관계없이 Firebase ML은 표준 직렬화된 protobuf 형식으로 로컬 저장소에 저장합니다.

이론적으로 이것은 누구나 모델을 복사할 수 있음을 의미합니다. 그러나 실제로 대부분의 모델은 애플리케이션에 따라 다르고 최적화로 인해 난독화되어 경쟁업체가 코드를 분해하고 재사용하는 것과 유사한 위험이 있습니다. 그럼에도 불구하고 앱에서 사용자 지정 모델을 사용하기 전에 이러한 위험을 알고 있어야 합니다.

Android API 레벨 21(Lollipop) 이상에서는 자동 백업에서 제외된 디렉터리에 모델을 다운로드합니다.

Android API 레벨 20 이하에서 모델은 앱 전용 내부 저장소의 com.google.firebase.ml.custom.models 라는 디렉토리에 다운로드됩니다. BackupAgent 를 사용하여 파일 백업을 활성화한 경우 이 디렉터리를 제외하도록 선택할 수 있습니다.