Tiện ích của nhà cung cấp

Các tiện ích của nhà cung cấp Neural Networks API (NNAPI) được giới thiệu trong Android 10. Đây là tập hợp gồm các loại dữ liệu và thao tác do nhà cung cấp xác định. Trên các thiết bị chạy NN HAL 1.2 trở lên, trình điều khiển có thể cung cấp các thao tác tuỳ chỉnh được tăng tốc phần cứng bằng cách hỗ trợ các tiện ích tương ứng của nhà cung cấp. Tiện ích của nhà cung cấp không sửa đổi hành vi của các thao tác hiện có.

Tiện ích của nhà cung cấp cung cấp giải pháp thay thế có cấu trúc hơn cho các loại dữ liệu và thao tác của OEM (không dùng nữa trong Android 10). Để biết thêm thông tin, hãy xem bài viết Các loại dữ liệu và hoạt động của OEM.

Danh sách cho phép sử dụng tiện ích

Bạn chỉ có thể sử dụng tiện ích của nhà cung cấp cho các ứng dụng Android và tệp nhị phân gốc được chỉ định rõ ràng trên các phân vùng /product, /vendor, /odm/data. Các ứng dụng và tệp nhị phân gốc nằm trên phân vùng /system không thể sử dụng tiện ích của nhà cung cấp.

Danh sách các ứng dụng Android và tệp nhị phân được phép sử dụng tiện ích của nhà cung cấp NNAPI được lưu trữ trong /vendor/etc/nnapi_extensions_app_allowlist. Mỗi dòng của tệp chứa một mục mới. Mục nhập có thể là đường dẫn nhị phân gốc có tiền tố là dấu gạch chéo (/), ví dụ: /data/foo hoặc tên của một gói ứng dụng Android, chẳng hạn như com.foo.bar.

Danh sách cho phép được thực thi qua thư viện dùng chung môi trường thời gian chạy NNAPI. Thư viện này bảo vệ chống lại việc sử dụng ngoài ý muốn, nhưng không chống lại hành vi cố ý tránh né bằng cách một ứng dụng trực tiếp sử dụng giao diện HAL của trình điều khiển NNAPI.

Định nghĩa tiện ích của nhà cung cấp

Nhà cung cấp tạo và duy trì tệp tiêu đề có định nghĩa tiện ích. Bạn có thể xem ví dụ đầy đủ về định nghĩa tiện ích trong example/fibonacci/FibonacciExtension.h.

Mỗi tiện ích phải có một tên duy nhất bắt đầu bằng tên miền đảo ngược của nhà cung cấp.

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

Tên đóng vai trò là một không gian tên cho các thao tác và loại dữ liệu. NNAPI sử dụng tên này để phân biệt các phần mở rộng của nhà cung cấp.

Thao tác và kiểu dữ liệu được khai báo theo cách tương tự như trong 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,
};

Toán tử mở rộng có thể sử dụng bất kỳ loại toán hạng nào, bao gồm cả loại toán hạng không mở rộng và loại toán hạng của các phần mở rộng khác. Khi sử dụng loại toán hạng từ một tiện ích khác, trình điều khiển phải hỗ trợ tiện ích đó.

Tiện ích cũng có thể khai báo cấu trúc tuỳ chỉnh đi kèm với toán hạng mở rộng.

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

Sử dụng phần mở rộng trong các ứng dụng NNAPI

Tệp runtime/include/NeuralNetworksExtensions.h (API C) hỗ trợ tiện ích thời gian chạy. Phần này cung cấp thông tin tổng quan về C API.

Để kiểm tra xem thiết bị có hỗ trợ một tiện ích hay không, hãy sử dụng ANeuralNetworksDevice_getExtensionSupport.

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

Để xây dựng mô hình bằng toán hạng mở rộng, hãy sử dụng ANeuralNetworksModel_getExtensionOperandType để lấy loại toán hạng và gọi 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);

Bạn có thể sử dụng ANeuralNetworksModel_setOperandExtensionData để liên kết dữ liệu bổ sung với một toán hạng mở rộng (không bắt buộc).

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

Để xây dựng mô hình bằng thao tác mở rộng, hãy sử dụng ANeuralNetworksModel_getExtensionOperationType để lấy loại thao tác và gọi 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);

Thêm tính năng hỗ trợ tiện ích vào một trình điều khiển NNAPI

Trình điều khiển báo cáo các tiện ích được hỗ trợ thông qua phương thức IDevice::getSupportedExtensions. Danh sách được trả về phải chứa một mục nhập mô tả từng tiện ích được hỗ trợ.

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

Trong 32 bit dùng để xác định loại và thao tác, bit Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX cao là tiền tố của phần mở rộng và bit Model::ExtensionTypeEncoding::LOW_BITS_TYPE thấp đại diện cho loại hoặc hoạt động của tiện ích.

Khi xử lý một toán tử hoặc loại toán hạng, trình điều khiển phải kiểm tra tiền tố của phần mở rộng. Nếu tiền tố phần mở rộng có giá trị khác 0, thì toán tử hoặc loại toán hạng là một loại phần mở rộng. Nếu giá trị là 0, thì thao tác hoặc loại toán hạng không phải là loại tiện ích.

Để liên kết tiền tố với tên tiện ích, hãy tra cứu tiền tố trong model.extensionNameToPrefix. Việc liên kết từ tiền tố đến tên tiện ích là tương ứng một với một (một mối liên kết) đối với một mô hình nhất định. Các giá trị tiền tố khác nhau có thể tương ứng với cùng một tên tiện ích trong các mô hình khác nhau.

Trình điều khiển phải xác thực các thao tác mở rộng và các loại dữ liệu vì môi trường thời gian chạy NNAPI không thể xác thực các thao tác mở rộng và kiểu dữ liệu cụ thể.

Các toán hạng của phần mở rộng có thể có dữ liệu liên kết trong operand.extraParams.extension, mà thời gian chạy sẽ coi là một blob dữ liệu thô có kích thước tuỳ ý.

Loại dữ liệu và hoạt động của OEM

NNAPI có một toán tử OEM và các loại dữ liệu OEM để cho phép nhà sản xuất thiết bị cung cấp chức năng tuỳ chỉnh, dành riêng cho trình điều khiển. Những thao tác và loại dữ liệu này chỉ được các ứng dụng của Nhà sản xuất thiết bị gốc (OEM) sử dụng. Ngữ nghĩa của các thao tác OEM và các kiểu dữ liệu dành riêng cho OEM và có thể thay đổi bất cứ lúc nào. Hoạt động của OEM và các loại dữ liệu được mã hoá bằng OperationType::OEM_OPERATION, OperandType::OEMOperandType::TENSOR_OEM_BYTE.