वेंडर एक्सटेंशन

Android 10 में पेश किए गए, Neural Networks API (NNAPI) वेंडर एक्सटेंशन, वेंडर की ओर से तय किए गए ऑपरेशनों और डेटा टाइप के कलेक्शन होते हैं. NN HAL 1.2 या इसके बाद के वर्शन पर काम करने वाले डिवाइसों पर, ड्राइवर वेंडर एक्सटेंशन के साथ काम करके, हार्डवेयर से तेज़ी लाने की सुविधा वाली कस्टम कार्रवाइयां कर सकते हैं. वेंडर एक्सटेंशन, मौजूदा कार्रवाइयों के व्यवहार में बदलाव नहीं करते.

वेंडर एक्सटेंशन, ओईएम ऑपरेशन और डेटा टाइप का ज़्यादा स्ट्रक्चर्ड विकल्प देते हैं. इन्हें Android 10 में बंद कर दिया गया था. ज़्यादा जानकारी के लिए, ओईएम ऑपरेशन और डेटा टाइप लेख पढ़ें.

एक्सटेंशन के इस्तेमाल की अनुमति वाली सूची

वेंडर एक्सटेंशन का इस्तेमाल सिर्फ़ उन Android ऐप्लिकेशन और नेटिव बाइनरी के लिए किया जा सकता है जिनके बारे में साफ़ तौर पर बताया गया है. ये ऐप्लिकेशन और बाइनरी, /product, /vendor, /odm, और /data पार्टीशन पर मौजूद होने चाहिए. /system पार्टीशन पर मौजूद ऐप्लिकेशन और नेटिव बाइनरी, वेंडर एक्सटेंशन का इस्तेमाल नहीं कर सकते.

NNAPI वेंडर एक्सटेंशन का इस्तेमाल करने की अनुमति वाले Android ऐप्लिकेशन और बाइनरी की सूची, /vendor/etc/nnapi_extensions_app_allowlist में सेव की जाती है. फ़ाइल की हर लाइन में एक नई एंट्री होती है. कोई एंट्री, नेटिव बाइनरी पाथ हो सकती है. इसके पहले स्लैश (/) लगा होता है. उदाहरण के लिए, /data/foo. इसके अलावा, यह Android ऐप्लिकेशन पैकेज का नाम भी हो सकता है. उदाहरण के लिए, com.foo.bar.

अनुमति वाली सूची, NNAPI रनटाइम शेयर की गई लाइब्रेरी से लागू की जाती है. यह लाइब्रेरी, गलती से इस्तेमाल होने से बचाती है. हालांकि, यह किसी ऐसे ऐप्लिकेशन को नहीं रोक सकती जो जान-बूझकर NNAPI ड्राइवर HAL इंटरफ़ेस का इस्तेमाल कर रहा हो.

वेंडर एक्सटेंशन की परिभाषा

वेंडर, एक्सटेंशन की परिभाषा वाली हेडर फ़ाइल बनाता है और उसे मैनेज करता है. एक्सटेंशन की परिभाषा का पूरा उदाहरण, 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 में मौजूद डेटा से जुड़ा हो सकता है. रनटाइम इसे किसी भी साइज़ के रॉ डेटा के तौर पर इस्तेमाल करता है.

ओईएम ऑपरेशन और डेटा टाइप

NNAPI में OEM ऑपरेशन और OEM डेटा टाइप होते हैं. इनकी मदद से, डिवाइस बनाने वाली कंपनियां ड्राइवर के हिसाब से काम करने वाली कस्टम सुविधाएं उपलब्ध करा सकती हैं. इन ऑपरेशन और डेटा टाइप का इस्तेमाल सिर्फ़ ओईएम ऐप्लिकेशन करते हैं. ओईएम के ऑपरेशन और डेटा टाइप के सिमैंटिक, ओईएम के हिसाब से तय होते हैं. इनमें कभी भी बदलाव किया जा सकता है. ओईएम के ऑपरेशन और डेटा टाइप को OperationType::OEM_OPERATION, OperandType::OEM, और OperandType::TENSOR_OEM_BYTE का इस्तेमाल करके एन्कोड किया जाता है.