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

หากแอปใช้โมเดล 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 เพื่อควบคุมการกำหนดเวอร์ชันของไลบรารี

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

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

    (วิธีอื่น)  เพิ่มไลบรารี 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:25.0.1")
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation("org.tensorflow:tensorflow-lite:2.3.0")
    }
    หากกำลังมองหาโมดูลไลบรารีสำหรับ Kotlin โดยเฉพาะ ตั้งแต่เดือนตุลาคม 2023 (Firebase BoM 32.5.0) เป็นต้นไป นักพัฒนาซอฟต์แวร์ทั้ง Kotlin และ Java จะใช้โมดูลไลบรารีหลักได้ (ดูรายละเอียดได้ในคําถามที่พบบ่อยเกี่ยวกับโครงการริเริ่มนี้)
  3. ประกาศในไฟล์ Manifest ของแอปว่าจำเป็นต้องมีสิทธิ์ INTERNET
    <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 รายการ อาร์เรย์เหล่านี้มีค่าเป็น 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 คุณอาจเลือกยกเว้นไดเรกทอรีนี้