תוספי ספקים

תוספי ספקים של Neural Networks API (NNAPI), שהוצגו ב-Android 10, הם אוספים של פעולות וסוגי נתונים שהוגדרו על ידי הספק. במכשירים עם NN HAL 1.2 ואילך, מנהלי התקנים יכולים לבצע פעולות מותאמות אישית עם האצת חומרה באמצעות תמיכה בתוספי ספקים תואמים. תוספי ספקים לא משנים את ההתנהגות של פעולות קיימות.

תוספי ספקים מספקים חלופה מובנית יותר לתפעול ולסוגי נתונים של ה-OEM, שהוצאו משימוש ב-Android 10. מידע נוסף זמין במאמר פעולות וסוגי נתונים של ה-OEM.

רשימת היתרים לשימוש בתוספים

אפשר להשתמש בתוספי ספקים רק באפליקציות ל-Android ובקבצים הבינאריים המקוריים שצוינו באופן מפורש במחיצות /product, /vendor, /odm ו-/data. אפליקציות וקבצים בינאריים מקוריים שנמצאים במחיצה של /system לא יכולים להשתמש בתוספי ספקים.

רשימה של אפליקציות וקבצים בינאריים ל-Android שמורשים להשתמש בתוספי ספקים של NNAPI מאוחסנת ב-/vendor/etc/nnapi_extensions_app_allowlist. כל שורה בקובץ מכילה רשומה חדשה. רשומה יכולה להיות נתיב בינארי מקורי עם קו נטוי (/), לדוגמה, /data/foo או שם של חבילת אפליקציה ל-Android, למשל com.foo.bar.

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

הגדרת תוסף הספק

הספק יוצר ומתחזק קובץ כותרת עם הגדרת התוסף. דוגמה מלאה להגדרת תוסף מופיעה ב-example/fibonacci/FibonacciExtension.h.

לכל תוסף צריך להיות שם ייחודי שמתחיל בשם הדומיין ההפוך של הספק.

const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";

השם משמש כמרחב שמות לפעולות ולסוגי נתונים. ה-NNAPI משתמש בשם הזה כדי להבדיל בין תוספי הספקים.

הפעולות וסוגי הנתונים מוצהרות באופן דומה להצהרה של runtime/include/NeuralNetworks.h.

enum {
    /**
     * A custom scalar type.
     */
    EXAMPLE_SCALAR = 0,

    /**
     * A custom tensor type.
     *
     * Attached to this tensor is {@link ExampleTensorParams}.
     */
    EXAMPLE_TENSOR = 1,
};

enum {
    /**
     * Computes example function.
     *
     * Inputs:
     * * 0: A scalar of {@link EXAMPLE_SCALAR}.
     *
     * Outputs:
     * * 0: A tensor of {@link EXAMPLE_TENSOR}.
     */
    EXAMPLE_FUNCTION = 0,
};

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

התוספים יכולים גם להצהיר על מבנים מותאמים אישית שנלווים לאופרנדים של תוספים.

/**
 * Quantization parameters for {@link EXAMPLE_TENSOR}.
 */
typedef struct ExampleTensorParams {
    double scale;
    int64_t zeroPoint;
} ExampleTensorParams;

שימוש בתוספים בלקוחות NNAPI

הקובץ runtime/include/NeuralNetworksExtensions.h (C API) תומך בתוסף זמן ריצה. בקטע הזה מופיעה סקירה כללית של C API.

כדי לבדוק אם במכשיר יש תמיכה בתוסף, משתמשים ב-ANeuralNetworksDevice_getExtensionSupport.

bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
                                                   &isExtensionSupported),
         ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
    // The device supports the extension.
    ...
}

כדי לפתח מודל עם אופרנד תוסף, משתמשים ב-ANeuralNetworksModel_getExtensionOperandType כדי לקבל את סוג האופרנד ולהפעיל את הקריאה ANeuralNetworksModel_addOperand.

int32_t type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperandType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_TENSOR, &type),
         ANEURALNETWORKS_NO_ERROR);
ANeuralNetworksOperandType operandType{
        .type = type,
        .dimensionCount = dimensionCount,
        .dimensions = dimensions,
};
CHECK_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);

לחלופין, אפשר להשתמש ב-ANeuralNetworksModel_setOperandExtensionData כדי לשייך נתונים נוספים לאופרנד של תוסף.

ExampleTensorParams params{
        .scale = 0.5,
        .zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, &params, sizeof(params)),
         ANEURALNETWORKS_NO_ERROR);

כדי לבנות מודל עם פעולת תוסף, משתמשים ב-ANeuralNetworksModel_getExtensionOperationType כדי לקבל את סוג הפעולה ולקרוא ל-ANeuralNetworksModel_addOperation.

ANeuralNetworksOperationType type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperationType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_FUNCTION,
                                                        &type),
         ANEURALNETWORKS_NO_ERROR);
CHECK_EQ(ANeuralNetworksModel_addOperation(model, type, inputCount, inputs, outputCount, outputs),
         ANEURALNETWORKS_NO_ERROR);

הוספת תמיכה של תוסף למנהל התקן NNAPI

הנהגים מדווחים על תוספים נתמכים באמצעות method IDevice::getSupportedExtensions. הרשימה שמוחזרת צריכה להכיל רשומה שמתארת כל תוסף נתמך.

Extension {
    .name = EXAMPLE_EXTENSION_NAME,
    .operandTypes = {
        {
            .type = EXAMPLE_SCALAR,
            .isTensor = false,
            .byteSize = 8,
        },
        {
            .type = EXAMPLE_TENSOR,
            .isTensor = true,
            .byteSize = 8,
        },
    },
}

מתוך 32 הביטים שמשמשים לזיהוי סוגים ופעולות, הביטים הגבוהים של Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX הם הקידומת של התוסף, והביטים הנמוכים של Model::ExtensionTypeEncoding::LOW_BITS_TYPE מייצגים את הסוג או הפעולה של התוסף.

במהלך טיפול בפעולה או בסוג אופרנד, הנהג צריך לבדוק את קידומת התוסף. אם לקידומת התוסף יש ערך שאינו אפס, הפעולה או סוג האופרנד הם סוג של תוסף. אם הערך הוא 0, הפעולה או סוג האופרנד הם לא סוג של תוסף.

כדי למפות את התחילית לשם של תוסף, צריך לחפש אותה ב-model.extensionNameToPrefix. המיפוי מהקידומת לשם התוסף הוא התאמה של אחד לאחד (bijection) למודל נתון. ערכי קידומת שונים עשויים להתאים לאותו שם תוסף במודלים שונים.

מנהל ההתקן צריך לאמת את הפעולות וסוגי הנתונים של התוסף, כי זמן הריצה של NNAPI לא יכול לאמת פעולות מסוימות של תוסף וסוגי נתונים מסוימים.

לאופרנדים של תוספים יכולים להיות נתונים משויכים ב-operand.extraParams.extension, ולכן סביבת זמן הריצה מתייחסת אליו כ-blob של נתונים גולמיים בגודל שרירותי.

פעולות OEM (יצרן ציוד מקורי) וסוגי נתונים

ל-NNAPI יש פעולת OEM (יצרן ציוד מקורי) וסוגי נתונים של OEM (יצרן ציוד מקורי) כדי לאפשר ליצרני מכשירים לספק פונקציונליות מותאמת אישית ספציפית לנהג. סוגי הפעולות והנתונים האלה משמשים רק לאפליקציות של יצרני ציוד מקורי. הסמנטיקה של הפעולות וסוגי הנתונים של ה-OEM היא ספציפית ל-OEM, והיא עשויה להשתנות בכל שלב. הפעולה וסוגי הנתונים של ה-OEM מקודדים באמצעות OperationType::OEM_OPERATION, OperandType::OEM ו-OperandType::TENSOR_OEM_BYTE.