ใช้โมเดล 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 เพิ่มประสิทธิภาพตัวแปลง

โฮสต์หรือรวมกลุ่มโมเดลของคุณ

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

เมื่อโฮสต์โมเดลใน 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. ให้ประกาศว่าต้องใช้สิทธิ์ INTERNET ในไฟล์ Manifest ของแอป โดยทำดังนี้
    <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+KTX

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+KTX

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+KTX

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+KTX

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+KTX

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 จนถึง คุณยืนยันว่าดาวน์โหลดโมเดลแล้ว คุณสามารถทำได้โดยการแนบ Listener ไปยังเมธอด 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+KTX

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

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

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

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+KTX

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+KTX

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+KTX

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() ของออบเจ็กต์ที่ส่งไปยังผู้ฟังที่ประสบความสำเร็จ เช่น

Java

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

Kotlin+KTX

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+KTX

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 จะจัดเก็บข้อมูลในรูปแบบ Protocolbuf แบบอนุกรมมาตรฐานใน พื้นที่เก็บข้อมูลในเครื่อง

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

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

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