תוספי ספקים

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

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

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

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

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

סוגי הנתונים והפעולות של OEM

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