ใช้โมเดล TensorFlow Lite เพื่อการอนุมานด้วย ML Kit บน Android

คุณสามารถใช้ ML Kit เพื่อทำการอนุมานบนอุปกรณ์ด้วยโมเดล TensorFlow Lite

API นี้ต้องใช้ Android SDK ระดับ 16 (Jelly Bean) ขึ้นไป

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

  1. เพิ่ม Firebase ลงในโปรเจ็กต์ Android หากยังไม่ได้ดำเนินการ
  2. เพิ่มทรัพยากร Dependency สำหรับคลัง ML Kit สำหรับ Android ลงในไฟล์ Gradle ของโมดูล (ระดับแอป) (โดยปกติจะเป็น app/build.gradle)
    apply plugin: 'com.android.application'
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-model-interpreter:22.0.3'
    }
  3. แปลงโมเดล TensorFlow ที่ต้องการใช้เป็นรูปแบบ TensorFlow Lite ดู TOCO: ตัวแปลงการเพิ่มประสิทธิภาพ TensorFlow Lite

โฮสต์หรือรวมโมเดล

คุณต้องทำให้โมเดลพร้อมใช้งานสำหรับ ML Kit ก่อนจึงจะใช้โมเดล TensorFlow Lite เพื่อการอนุมานในแอปได้ ML Kit สามารถใช้โมเดล TensorFlow Lite ที่โฮสต์จากระยะไกลโดยใช้ Firebase, มาพร้อมกับไบนารีของแอป หรือทั้ง 2 อย่าง

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

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

การรวมโมเดลไว้ในแอปช่วยให้มั่นใจได้ว่าฟีเจอร์ ML ของแอปจะยังคงทํางานได้เมื่อโมเดลที่โฮสต์ใน Firebase ไม่พร้อมใช้งาน

โฮสต์โมเดลใน Firebase

วิธีโฮสต์โมเดล TensorFlow Lite ใน Firebase

  1. ในส่วน ML Kit ของคอนโซล Firebase ให้คลิกแท็บกําหนดเอง
  2. คลิกเพิ่มรูปแบบที่กําหนดเอง (หรือเพิ่มรูปแบบอื่น)
  3. ระบุชื่อที่จะใช้ระบุโมเดลในโปรเจ็กต์ Firebase จากนั้นอัปโหลดไฟล์โมเดล TensorFlow Lite (มักจะลงท้ายด้วย .tflite หรือ .lite)
  4. ประกาศในไฟล์ Manifest ของแอปว่าจำเป็นต้องมีสิทธิ์ INTERNET
    <uses-permission android:name="android.permission.INTERNET" />

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

รวมโมเดลกับแอป

หากต้องการรวมโมเดล TensorFlow Lite กับแอป ให้คัดลอกไฟล์โมเดล (โดยปกติจะลงท้ายด้วย .tflite หรือ .lite) ไปยังโฟลเดอร์ assets/ ของแอป (คุณอาจต้องสร้างโฟลเดอร์ก่อนโดยคลิกขวาที่โฟลเดอร์ app/ แล้วคลิกใหม่ > โฟลเดอร์ > โฟลเดอร์ชิ้นงาน)

จากนั้นเพิ่มข้อมูลต่อไปนี้ลงในไฟล์ build.gradle ของแอปเพื่อให้แน่ใจว่า Gradle จะไม่บีบอัดโมเดลเมื่อสร้างแอป

android {

    // ...

    aaptOptions {
        noCompress "tflite"  // Your model's file extension: "tflite", "lite", etc.
    }
}

ไฟล์โมเดลจะรวมอยู่ในแพ็กเกจแอปและพร้อมใช้งานสำหรับ ML Kit เป็นชิ้นงานดิบ

โหลดโมเดล

หากต้องการใช้โมเดล TensorFlow Lite ในแอป ให้เริ่มด้วยการกําหนดค่า ML Kit ด้วยตําแหน่งที่มีโมเดลอยู่ ซึ่งได้แก่ ระยะไกลโดยใช้ Firebase, ในที่จัดเก็บในเครื่อง หรือทั้ง 2 อย่าง หากคุณระบุทั้งโมเดลในเครื่องและโมเดลระยะไกล คุณจะใช้โมเดลระยะไกลได้หากมี และถอยกลับไปใช้โมเดลที่เก็บไว้ในเครื่องหากโมเดลระยะไกลไม่พร้อมใช้งาน

กำหนดค่ารูปแบบที่โฮสต์โดย Firebase

หากคุณโฮสต์โมเดลด้วย Firebase ให้สร้างFirebaseCustomRemoteModel ออบเจ็กต์โดยระบุชื่อที่คุณกำหนดให้กับโมเดลเมื่ออัปโหลด

Java

FirebaseCustomRemoteModel remoteModel =
        new FirebaseCustomRemoteModel.Builder("your_model").build();

Kotlin

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()

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

Java

FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
        .requireWifi()
        .build();
FirebaseModelManager.getInstance().download(remoteModel, conditions)
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                // Success.
            }
        });

Kotlin

val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Success.
    }

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

กำหนดค่าโมเดลในเครื่อง

หากคุณรวมโมเดลไว้ในแอป ให้สร้างออบเจ็กต์ FirebaseCustomLocalModel โดยระบุชื่อไฟล์ของโมเดล TensorFlow Lite ดังนี้

Java

FirebaseCustomLocalModel localModel = new FirebaseCustomLocalModel.Builder()
        .setAssetFilePath("your_model.tflite")
        .build();

Kotlin

val localModel = FirebaseCustomLocalModel.Builder()
    .setAssetFilePath("your_model.tflite")
    .build()

สร้างโปรแกรมแปลภาษาจากโมเดล

หลังจากกําหนดค่าแหล่งที่มาของโมเดลแล้ว ให้สร้างFirebaseModelInterpreter ออบเจ็กต์จากแหล่งที่มาแหล่งใดแหล่งหนึ่ง

หากคุณมีเพียงโมเดลที่รวมอยู่ในเครื่อง ให้สร้างตัวแปรแปลจากออบเจ็กต์ FirebaseCustomLocalModel ดังนี้

Java

FirebaseModelInterpreter interpreter;
try {
    FirebaseModelInterpreterOptions options =
            new FirebaseModelInterpreterOptions.Builder(localModel).build();
    interpreter = FirebaseModelInterpreter.getInstance(options);
} catch (FirebaseMLException e) {
    // ...
}

Kotlin

val options = FirebaseModelInterpreterOptions.Builder(localModel).build()
val interpreter = FirebaseModelInterpreter.getInstance(options)

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

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

Java

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener(new OnSuccessListener<Boolean>() {
            @Override
            public void onSuccess(Boolean isDownloaded) {
                FirebaseModelInterpreterOptions options;
                if (isDownloaded) {
                    options = new FirebaseModelInterpreterOptions.Builder(remoteModel).build();
                } else {
                    options = new FirebaseModelInterpreterOptions.Builder(localModel).build();
                }
                FirebaseModelInterpreter interpreter = FirebaseModelInterpreter.getInstance(options);
                // ...
            }
        });

Kotlin

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded -> 
    val options =
        if (isDownloaded) {
            FirebaseModelInterpreterOptions.Builder(remoteModel).build()
        } else {
            FirebaseModelInterpreterOptions.Builder(localModel).build()
        }
    val interpreter = FirebaseModelInterpreter.getInstance(options)
}

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

Java

FirebaseModelManager.getInstance().download(remoteModel, conditions)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void v) {
              // Download complete. Depending on your app, you could enable
              // the ML feature, or switch from the local model to the remote
              // model, etc.
            }
        });

Kotlin

FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.
    }

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

ขั้นตอนถัดไป ให้กําหนดค่ารูปแบบอินพุตและเอาต์พุตของโปรแกรมตีความโมเดล

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

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

import tensorflow as tf

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

# Print input shape and type
print(interpreter.get_input_details()[0]['shape'])  # Example: [1 224 224 3]
print(interpreter.get_input_details()[0]['dtype'])  # Example: <class 'numpy.float32'>

# Print output shape and type
print(interpreter.get_output_details()[0]['shape'])  # Example: [1 1000]
print(interpreter.get_output_details()[0]['dtype'])  # Example: <class 'numpy.float32'>

หลังจากกําหนดรูปแบบอินพุตและเอาต์พุตของโมเดลแล้ว คุณสามารถกําหนดค่าโปรแกรมตีความโมเดลของแอปได้โดยการสร้างออบเจ็กต์ FirebaseModelInputOutputOptions

เช่น โมเดลการจัดประเภทรูปภาพแบบทศนิยมอาจรับค่า float Nx224x224x3 เป็นอินพุต ซึ่งแสดงกลุ่มรูปภาพ N 224x224 3 ช่อง (RGB) และสร้างเอาต์พุตเป็นรายการค่า float 1,000 รายการ โดยแต่ละรายการแสดงถึงความน่าจะเป็นที่รูปภาพจะอยู่ในหมวดหมู่ใดหมวดหมู่หนึ่งจาก 1,000 หมวดหมู่ที่โมเดลคาดการณ์

สําหรับโมเดลดังกล่าว คุณจะต้องกําหนดค่าอินพุตและเอาต์พุตของโปรแกรมแปลโมเดลดังที่แสดงด้านล่าง

Java

FirebaseModelInputOutputOptions inputOutputOptions =
        new FirebaseModelInputOutputOptions.Builder()
                .setInputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 224, 224, 3})
                .setOutputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 5})
                .build();

Kotlin

val inputOutputOptions = FirebaseModelInputOutputOptions.Builder()
        .setInputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 224, 224, 3))
        .setOutputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 5))
        .build()

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

สุดท้าย หากต้องการใช้การอนุมานโดยใช้โมเดล ให้รับข้อมูลอินพุตและดำเนินการเปลี่ยนรูปแบบข้อมูลที่จำเป็นเพื่อให้ได้อาร์เรย์อินพุตที่มีรูปร่างเหมาะสมกับโมเดล

เช่น หากคุณมีโมเดลการจัดประเภทรูปภาพที่มีรูปร่างอินพุตเป็นค่าทศนิยม [1 224 224 3] คุณอาจสร้างอาร์เรย์อินพุตจากออบเจ็กต์ Bitmap ดังที่แสดงในตัวอย่างต่อไปนี้

Java

Bitmap bitmap = getYourInputImage();
bitmap = Bitmap.createScaledBitmap(bitmap, 224, 224, true);

int batchNum = 0;
float[][][][] input = new float[1][224][224][3];
for (int x = 0; x < 224; x++) {
    for (int y = 0; y < 224; y++) {
        int pixel = bitmap.getPixel(x, y);
        // Normalize channel values to [-1.0, 1.0]. This requirement varies by
        // model. For example, some models might require values to be normalized
        // to the range [0.0, 1.0] instead.
        input[batchNum][x][y][0] = (Color.red(pixel) - 127) / 128.0f;
        input[batchNum][x][y][1] = (Color.green(pixel) - 127) / 128.0f;
        input[batchNum][x][y][2] = (Color.blue(pixel) - 127) / 128.0f;
    }
}

Kotlin

val bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true)

val batchNum = 0
val input = Array(1) { Array(224) { Array(224) { FloatArray(3) } } }
for (x in 0..223) {
    for (y in 0..223) {
        val pixel = bitmap.getPixel(x, y)
        // Normalize channel values to [-1.0, 1.0]. This requirement varies by
        // model. For example, some models might require values to be normalized
        // to the range [0.0, 1.0] instead.
        input[batchNum][x][y][0] = (Color.red(pixel) - 127) / 255.0f
        input[batchNum][x][y][1] = (Color.green(pixel) - 127) / 255.0f
        input[batchNum][x][y][2] = (Color.blue(pixel) - 127) / 255.0f
    }
}

จากนั้นสร้างออบเจ็กต์ FirebaseModelInputs ด้วยข้อมูลอินพุต แล้วส่งออบเจ็กต์ดังกล่าวและข้อกำหนดอินพุตและเอาต์พุตของโมเดลไปยังเมธอด run ของโปรแกรมตีความโมเดล ดังนี้

Java

FirebaseModelInputs inputs = new FirebaseModelInputs.Builder()
        .add(input)  // add() as many input arrays as your model requires
        .build();
firebaseInterpreter.run(inputs, inputOutputOptions)
        .addOnSuccessListener(
                new OnSuccessListener<FirebaseModelOutputs>() {
                    @Override
                    public void onSuccess(FirebaseModelOutputs result) {
                        // ...
                    }
                })
        .addOnFailureListener(
                new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        // Task failed with an exception
                        // ...
                    }
                });

Kotlin

val inputs = FirebaseModelInputs.Builder()
        .add(input) // add() as many input arrays as your model requires
        .build()
firebaseInterpreter.run(inputs, inputOutputOptions)
        .addOnSuccessListener { result ->
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

หากการเรียกใช้สำเร็จ คุณจะได้รับเอาต์พุตโดยการเรียกใช้เมธอด getOutput() ของออบเจ็กต์ที่ส่งไปยัง Listener ของผลลัพธ์ เช่น

Java

float[][] output = result.getOutput(0);
float[] probabilities = output[0];

Kotlin

val output = result.getOutput<Array<FloatArray>>(0)
val probabilities = output[0]

วิธีใช้เอาต์พุตจะขึ้นอยู่กับรุ่นที่คุณใช้

ตัวอย่างเช่น หากคุณกำลังทำการแยกประเภท ขั้นตอนถัดไปอาจเป็นการกำหนดดัชนีของผลลัพธ์ให้กับป้ายกำกับที่แสดงถึงผลลัพธ์นั้น

Java

BufferedReader reader = new BufferedReader(
        new InputStreamReader(getAssets().open("retrained_labels.txt")));
for (int i = 0; i < probabilities.length; i++) {
    String label = reader.readLine();
    Log.i("MLKit", String.format("%s: %1.4f", label, probabilities[i]));
}

Kotlin

val reader = BufferedReader(
        InputStreamReader(assets.open("retrained_labels.txt")))
for (i in probabilities.indices) {
    val label = reader.readLine()
    Log.i("MLKit", String.format("%s: %1.4f", label, probabilities[i]))
}

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

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

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

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

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