คุณสามารถใช้ ML Kit เพื่อทำการอนุมานบนอุปกรณ์ด้วยโมเดล TensorFlow Lite
API นี้ต้องใช้ Android SDK ระดับ 16 (Jelly Bean) ขึ้นไป
ก่อนเริ่มต้น
- เพิ่ม Firebase ลงในโปรเจ็กต์ Android หากยังไม่ได้ดำเนินการ
- เพิ่มทรัพยากร 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' }
- แปลงโมเดล 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
- ในส่วน ML Kit ของคอนโซล Firebase ให้คลิกแท็บกําหนดเอง
- คลิกเพิ่มรูปแบบที่กําหนดเอง (หรือเพิ่มรูปแบบอื่น)
- ระบุชื่อที่จะใช้ระบุโมเดลในโปรเจ็กต์ Firebase จากนั้นอัปโหลดไฟล์โมเดล TensorFlow Lite (มักจะลงท้ายด้วย
.tflite
หรือ.lite
) - ประกาศในไฟล์ 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
คุณอาจเลือกยกเว้นไดเรกทอรีนี้