השתמש בדגם TensorFlow Lite להסקת מסקנות עם ערכת ML באנדרואיד

אתה יכול להשתמש בערכת ML כדי לבצע הסקת מסקנות במכשיר עם דגם TensorFlow Lite .

API זה דורש Android SDK ברמה 16 (Jelly Bean) או חדש יותר.

לפני שאתה מתחיל

  1. אם עדיין לא עשית זאת, הוסף את Firebase לפרויקט Android שלך .
  2. הוסף את התלות של ספריות אנדרואיד של ML Kit לקובץ 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 Optimizing Converter .

ארח או אגד את הדגם שלך

לפני שתוכל להשתמש במודל 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. במניפסט של האפליקציה שלך, הצהיר כי נדרשת הרשאת אינטרנט:
    <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 עם המיקומים שבהם הדגם שלך זמין: מרחוק באמצעות 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)
}

אם יש לך רק דגם שמתארח מרחוק, עליך להשבית את הפונקציונליות הקשורה לדגם - לדוגמה, לאפור או להסתיר חלק מהממשק שלך - עד שתאשר שהדגם הורד. אתה יכול לעשות זאת על ידי חיבור מאזין לשיטת 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 עם המספר והממדים ("צורה") של המערכים שהמודל שלך משתמש בו.

אם אינך יודע את הצורה וסוג הנתונים של הקלט והפלט של המודל שלך, אתה יכול להשתמש במתורגמן 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 .

לדוגמה, מודל סיווג תמונה של נקודה צפה עשוי לקבל כקלט מערך N x224x224x3 של float , המייצגים אצווה של N 224x224 תמונות תלת-ערוצים (RGB), ולהפיק כפלט רשימה של 1000 ערכי float , כל אחד מייצג את סבירות שהתמונה היא חברה באחת מ-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 מאחסנת אותם בפורמט פרוטובוף סידורי סטנדרטי באחסון מקומי.

בתיאוריה, זה אומר שכל אחד יכול להעתיק את המודל שלך. עם זאת, בפועל, רוב הדגמים הם כל כך ספציפיים ליישום ומעורפלים על ידי אופטימיזציות שהסיכון דומה לזה של מתחרים שמפרקים ומשתמשים מחדש בקוד שלך. עם זאת, עליך להיות מודע לסיכון זה לפני שאתה משתמש במודל מותאם אישית באפליקציה שלך.

ב-Android API רמה 21 (Lollipop) ואילך, הדגם מורד לספרייה שאינה נכללת בגיבוי אוטומטי .

ב-Android API רמה 20 ומעלה, הדגם יורדת לספרייה בשם com.google.firebase.ml.custom.models באחסון פנימי פרטי לאפליקציה. אם הפעלת גיבוי קבצים באמצעות BackupAgent , ייתכן שתבחר לא לכלול את הספרייה הזו.