ใช้โมเดล TensorFlow Lite ที่กำหนดเองใน Android

หากแอปของคุณใช้โมเดล TensorFlow Lite ที่กำหนดเอง TensorFlow Lite คุณสามารถใช้ Firebase ML เพื่อทำให้โมเดลใช้งานได้ การทำให้โมเดลใช้งานได้ด้วย Firebase จะช่วยลดขนาดการดาวน์โหลดเริ่มต้นของแอป และอัปเดตโมเดล ML ของแอปได้โดยไม่ต้องเผยแพร่แอปเวอร์ชันใหม่ และด้วย Remote Config และ A/B Testing คุณสามารถแสดงโมเดลที่แตกต่างกันแบบไดนามิกแก่ผู้ใช้แต่ละกลุ่มได้

โมเดล TensorFlow Lite

โมเดล TensorFlow Lite คือโมเดล ML ที่ได้รับการปรับให้ทำงานบนอุปกรณ์เคลื่อนที่ วิธีรับโมเดล TensorFlow Lite

ก่อนเริ่มต้น

  1. หากยังไม่ได้เพิ่ม เพิ่ม Firebase ลงในโปรเจ็กต์ Android
  2. ในไฟล์ Gradle ของโมดูล (ระดับแอป) (โดยมากจะเป็นไฟล์ <project>/<app-module>/build.gradle.kts หรือ <project>/<app-module>/build.gradle) ให้เพิ่มทรัพยากร Dependency สำหรับไลบรารีตัวดาวน์โหลดโมเดล Firebase ML สำหรับ Android เราขอแนะนำให้ใช้ Firebase Android BoM เพื่อควบคุมการกำหนดเวอร์ชันของไลบรารี

    นอกจากนี้ คุณต้องเพิ่ม TensorFlow Lite SDK ลงในแอปด้วย ซึ่งเป็นส่วนหนึ่งของการตั้งค่าตัวดาวน์โหลดโมเดล Firebase ML

    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:34.13.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 เวอร์ชันที่เข้ากันได้อยู่เสมอ

    (ทางเลือก)  เพิ่มทรัพยากร Dependency ของไลบรารี Firebase โดยไม่ ใช้ BoM

    หากเลือกที่จะไม่ใช้ Firebase BoM คุณต้องระบุเวอร์ชันของไลบรารี Firebase แต่ละรายการ ในบรรทัดทรัพยากร Dependency

    โปรดทราบว่าหากคุณใช้ไลบรารี 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:26.0.2")
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation("org.tensorflow:tensorflow-lite:2.3.0")
    }
  3. ประกาศว่าต้องมีสิทธิ์ INTERNET ในไฟล์ Manifest ของแอป
    <uses-permission android:name="android.permission.INTERNET" />

1. ทำให้โมเดลใช้งานได้

ทำให้โมเดล TensorFlow ที่กำหนดเองใช้งานได้โดยใช้ Firebase คอนโซล หรือ Firebase Admin Python และ Node.js SDK ดูหัวข้อ ทำให้โมเดลที่กำหนดเองใช้งานได้และจัดการโมเดลที่กำหนดเอง

หลังจากเพิ่มโมเดลที่กำหนดเองลงในโปรเจ็กต์ Firebase แล้ว คุณจะอ้างอิงโมเดลในแอปโดยใช้ชื่อที่ระบุได้ คุณสามารถทำให้โมเดล TensorFlow Lite ใหม่ใช้งานได้และดาวน์โหลดโมเดลใหม่ลงในอุปกรณ์ของผู้ใช้ได้ทุกเมื่อโดย เรียกใช้ getModel() (ดูด้านล่าง)

2. ดาวน์โหลดโมเดลลงในอุปกรณ์และเริ่มต้นตัวตีความ TensorFlow Lite

หากต้องการใช้โมเดล TensorFlow Lite ในแอป ให้ใช้ Firebase ML SDK เพื่อดาวน์โหลดโมเดลเวอร์ชันล่าสุดลงในอุปกรณ์ก่อน จากนั้นสร้างอินสแตนซ์ตัวตีความ TensorFlow Lite ด้วยโมเดล

หากต้องการเริ่มดาวน์โหลดโมเดล ให้เรียกใช้เมธอด getModel() ของตัวดาวน์โหลดโมเดล โดยระบุชื่อที่คุณกำหนดให้กับโมเดลเมื่ออัปโหลด รวมถึงระบุว่าต้องการดาวน์โหลดโมเดลล่าสุดเสมอหรือไม่ และระบุเงื่อนไขที่คุณต้องการอนุญาตให้ดาวน์โหลด

คุณเลือกลักษณะการทำงานในการดาวน์โหลดได้ 3 แบบ ดังนี้

ประเภทการดาวน์โหลด คำอธิบาย
LOCAL_MODEL รับโมเดลในเครื่องจากอุปกรณ์ หากไม่มีโมเดลในเครื่องที่พร้อมใช้งาน ลักษณะการทำงานจะเป็นเหมือน LATEST_MODEL ใช้ประเภทการดาวน์โหลดนี้หากคุณไม่สนใจที่จะตรวจสอบการอัปเดตโมเดล เช่น คุณใช้การกำหนดค่าระยะไกลเพื่อดึงข้อมูลชื่อโมเดล และอัปโหลดโมเดลด้วยชื่อใหม่เสมอ (แนะนำ)
LOCAL_MODEL_UPDATE_IN_BACKGROUND รับโมเดลในเครื่องจากอุปกรณ์และเริ่มอัปเดตโมเดลในเบื้องหลัง หากไม่มีโมเดลในเครื่องที่พร้อมใช้งาน ลักษณะการทำงานจะเป็นเหมือน LATEST_MODEL
LATEST_MODEL รับโมเดลล่าสุด หากโมเดลในเครื่องเป็นเวอร์ชันล่าสุด ระบบจะแสดงโมเดลในเครื่อง หากไม่ใช่ ระบบจะดาวน์โหลดโมเดลล่าสุด ลักษณะการทำงานนี้จะบล็อกจนกว่าจะดาวน์โหลดเวอร์ชันล่าสุดเสร็จ (ไม่แนะนำ) ใช้ลักษณะการทำงานนี้เฉพาะในกรณีที่คุณต้องการเวอร์ชันล่าสุดอย่างชัดเจนเท่านั้น

คุณควรปิดใช้ฟังก์ชันการทำงานที่เกี่ยวข้องกับโมเดล เช่น ทำให้ส่วนหนึ่งของ UI เป็นสีเทาหรือซ่อนไว้ จนกว่าจะยืนยันว่าได้ดาวน์โหลดโมเดลแล้ว

Kotlin

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)
            }
        }

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);
        }
      }
    });

แอปจำนวนมากจะเริ่มงานดาวน์โหลดในโค้ดการเริ่มต้น แต่คุณสามารถทำได้ทุกเมื่อก่อนที่จะต้องใช้โมเดล

3. ทำการอนุมานข้อมูลอินพุต

รับรูปร่างอินพุตและเอาต์พุตของโมเดล

ตัวตีความโมเดล TensorFlow Lite รับอาร์เรย์หลายมิติอย่างน้อย 1 รายการเป็นอินพุตและสร้างอาร์เรย์หลายมิติอย่างน้อย 1 รายการเป็นเอาต์พุต อาร์เรย์เหล่านี้มีค่า byte, int, long, หรือ float คุณต้องทราบ จำนวนและมิติข้อมูล ("รูปร่าง") ของอาร์เรย์ที่โมเดลใช้ก่อนจึงจะส่งข้อมูลไปยังโมเดลหรือใช้ผลลัพธ์ของโมเดลได้

หากคุณสร้างโมเดลด้วยตนเอง หรือหากมีการบันทึกรูปแบบอินพุตและเอาต์พุตของโมเดลไว้ คุณอาจมีข้อมูลนี้อยู่แล้ว หากไม่ทราบ รูปร่างและประเภทข้อมูลของอินพุตและเอาต์พุตของโมเดล คุณสามารถใช้ ตัวตีความ TensorFlow Lite เพื่อตรวจสอบโมเดลได้ เช่น

Python

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] คุณสามารถสร้าง ByteBuffer อินพุตจากออบเจ็กต์ Bitmap ได้ดังที่แสดงในตัวอย่างต่อไปนี้

Kotlin

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)
    }
}

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);
    }
}

จากนั้นจัดสรร ByteBuffer ที่มีขนาดใหญ่พอที่จะเก็บเอาต์พุตของโมเดล และส่งบัฟเฟอร์อินพุตและบัฟเฟอร์เอาต์พุตไปยังเมธอด run() ของตัวตีความ TensorFlow Lite ตัวอย่างเช่น สำหรับรูปร่างเอาต์พุตเป็นค่าจุดลอยตัว [1 1000]

Kotlin

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

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

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?
}

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?
}

ภาคผนวก: ความปลอดภัยของโมเดล

ไม่ว่าคุณจะทำให้โมเดล TensorFlow Lite พร้อมใช้งานสำหรับ Firebase ML ด้วยวิธีใดก็ตาม Firebase ML จะจัดเก็บโมเดลในรูปแบบ protobuf ที่ซีเรียลไลซ์มาตรฐานไว้ใน พื้นที่เก็บข้อมูลในเครื่อง

ในทางทฤษฎี หมายความว่าทุกคนสามารถคัดลอกโมเดลของคุณได้ อย่างไรก็ตาม ในทางปฏิบัติ โมเดลส่วนใหญ่มีความเฉพาะเจาะจงกับแอปพลิเคชันและถูกทำให้สับสนโดยการเพิ่มประสิทธิภาพ ความเสี่ยงจึงคล้ายกับความเสี่ยงที่คู่แข่งจะแยกชิ้นส่วนและนำโค้ดของคุณไปใช้ซ้ำ อย่างไรก็ตาม คุณควรทราบถึงความเสี่ยงนี้ก่อนที่จะใช้โมเดลที่กำหนดเองในแอป

ใน Android ระดับ API 21 (Lollipop) ขึ้นไป ระบบจะดาวน์โหลดโมเดลลงในไดเรกทอรีที่ ยกเว้นจากการสำรองข้อมูลอัตโนมัติ

ใน Android ระดับ API 20 ลงไป ระบบจะดาวน์โหลดโมเดลลงในไดเรกทอรี ชื่อ com.google.firebase.ml.custom.models ในที่จัดเก็บข้อมูลภายในแบบส่วนตัวของแอป หากคุณเปิดใช้การสำรองข้อมูลไฟล์โดยใช้ BackupAgent, คุณอาจเลือกที่จะยกเว้นไดเรกทอรีนี้