אפשר להשתמש ב-ML Kit כדי לבצע הסקת מסקנות במכשיר באמצעות מודל TensorFlow Lite.
ל-API הזה נדרשת Android SDK ברמה 16 (Jelly Bean) ואילך.
לפני שמתחילים
- אם עדיין לא עשיתם זאת, מוסיפים את Firebase לפרויקט Android.
- הוספת יחסי התלות של ספריות 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 Optimizing Converter.
אירוח או אריזה של המודל
לפני שתוכלו להשתמש במודל TensorFlow Lite לצורך הסקת מסקנות באפליקציה שלכם, חייבים להפוך את המודל לזמין ל-ML Kit. ב-ML Kit אפשר להשתמש במודלים של TensorFlow Lite שמתארחים מרחוק באמצעות Firebase, במודלים שמצורפים לקובץ הבינארי של האפליקציה או בשניהם.
אירוח מודל ב-Firebase מאפשר לכם לעדכן את המודל בלי לפרסם גרסת אפליקציה חדשה, ולהשתמש ב-Remote Config וב-A/B Testing כדי להציג דינמית מודלים שונים לקבוצות שונות של משתמשים.
אם תבחרו לספק את המודל רק על ידי אירוח שלו ב-Firebase, ולא ב-Firebase לצרף אותו לאפליקציה שלך, אפשר להקטין את גודל ההורדה הראשוני של האפליקציה. עם זאת, חשוב לזכור שאם המודל לא נכלל בחבילה עם האפליקציה שלכם, שקשורה למודלים לא יהיו זמינים עד שהאפליקציה תוריד את בפעם הראשונה.
שילוב המודל עם האפליקציה מאפשר לוודא שיש בה תכונות ללמידת מכונה ימשיכו לפעול כשהמודל שמתארח ב-Firebase לא זמין.
אירוח מודלים ב-Firebase
כדי לארח את מודל TensorFlow Lite ב-Firebase:
- בקטע ML Kit במסוף Firebase, לוחצים על את הכרטיסייה בהתאמה אישית.
- לוחצים על הוספת מודל מותאם אישית (או על הוספת מודל נוסף).
- מציינים שם שישמש לזיהוי המודל ב-Firebase
ולאחר מכן להעלות את קובץ המודל TensorFlow Lite (בדרך כלל מסתיים ב
.tflite
או.lite
). - במניפסט של האפליקציה, צריך להצהיר שנדרשת הרשאת 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, מרחוק, אחסון מקומי, או את שניהם. אם מציינים מודל מקומי וגם מודל מרוחק, אפשר להשתמש במודל המרוחק אם הוא זמין, ולעבור למודל המאוחסן באופן מקומי אם המודל המרוחק לא זמין.הגדרת מודל שמתארח ב-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)
}
אם יש לך רק מודל שמתארח מרחוק, עליך להשבית את התכונה שקשורה למודלים
פונקציונליות - לדוגמה, הצגה באפור או הסתרה של חלק מממשק המשתמש - עד
מוודאים שבוצעה הורדה של המודל. אפשר לעשות זאת על ידי צירוף listen
ל-method 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 מקבל כקלט ומפיק פלט אחד או יותר
מערכים רב-ממדיים. המערכים האלה מכילים את byte
,
int
, long
או float
. צריך
להגדיר את ערכת ML Kit במספר והמאפיינים ("הצורה") של המערכים
של המודל.
אם אתם לא יודעים מה הצורה וסוג הנתונים של הקלט והפלט של המודל, אתם יכולים להשתמש ברכיב התרגום של TensorFlow Lite ב-Python כדי לבחון את המודל שלכם. לדוגמה:
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
.
לדוגמה, מודל סיווג תמונות עם נקודה צפה (floating-point) עשוי לקחת כקלט
Nמערך x224x224x3 של ערכי float
, שמייצגים קבוצה של
N תמונות תלת-ערוציות (RGB) בגודל 224x224, ומפיקות כפלט רשימה של
1,000 ערכים של float
, שכל אחד מהם מייצג את הסבירות שהתמונה שייכת אליו
אחת מ-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+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 // ... }
אם הקריאה מסתיימת בהצלחה, אפשר לקבל את הפלט על ידי קריאה ל-method 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 מאחסן אותם בפורמט פרוטוב טורי סטנדרטי ב- אחסון מקומי.
בתיאוריה, המשמעות היא שכל אחד יכול להעתיק את המודל שלכם. אבל, לפעמים בפועל, רוב המודלים הם ספציפיים לאפליקציה ומעורפלים (obfuscation) והסיכון הזה דומה לזה של מתחרים פירוק שימוש חוזר בקוד. עם זאת, חשוב להיות מודעים לסיכון זה לפני השימוש מודל מותאם אישית באפליקציה.
ב-Android API ברמה 21 (Lollipop) ואילך, הורדת המודל מתבצעת ספרייה שהיא לא נכלל בגיבוי האוטומטי.
ב-Android API ברמה 20 ואילך, המערכת מורידה את המודל לספרייה
בשם com.google.firebase.ml.custom.models
במצב פרטי באפליקציה
ואחסון פנימי. אם הפעלתם את הגיבוי של הקבצים באמצעות BackupAgent
, תוכלו להחריג את התיקייה הזו.