תוספי ספקים

תוספים של ספקים ל-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

הנהגים מדווחים על תוספים נתמכים באמצעות 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, והיא עשויה להשתנות בכל שלב. הפעולה וסוגי הנתונים של ה-OEM מקודדים באמצעות OperationType::OEM_OPERATION, OperandType::OEM ו-OperandType::TENSOR_OEM_BYTE.