공급업체 확장 프로그램

Android 10에 도입된 Neural Networks API(NNAPI) 공급업체 확장 프로그램에는 공급업체에서 정의한 연산 및 데이터 유형이 모여 있습니다. NN HAL 1.2 이상을 실행하는 기기에서는 드라이버가 상응하는 공급업체 확장 프로그램을 지원하여 맞춤 하드웨어 가속 연산을 제공할 수 있습니다. 공급업체 확장 프로그램은 기존 연산의 동작을 수정하지 않습니다.

공급업체 확장 프로그램은 Android 10에서 지원 중단된 OEM 연산 및 데이터 유형에 관한 좀 더 구조화된 대안을 제공합니다. 자세한 내용은 OEM 연산 및 데이터 유형을 참고하세요.

확장 프로그램 사용 허용 목록

공급업체 확장 프로그램은 명시적으로 지정된 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이 아닌 값이 있으면 연산 또는 피연산자 유형이 확장 프로그램 유형입니다. 값이 0인 경우에는 연산 또는 피연산자 유형이 확장 프로그램 유형이 아닙니다.

접두사를 확장 프로그램 이름에 매핑하려면 model.extensionNameToPrefix에서 검색합니다. 접두사에서 확장 프로그램 이름으로의 매핑은 주어진 모델의 일대일 대응관계(bijection)입니다. 여러 접두사 값이 다른 모델의 동일한 확장 프로그램 이름과 일치할 수 있습니다.

드라이버가 확장 프로그램 연산과 데이터 유형을 확인해야 하는 이유는 NNAPI 런타임으로 특정 확장 프로그램 연산과 데이터 유형을 확인할 수 없기 때문입니다.

확장 프로그램 피연산자는 런타임에서 임의 크기의 원시 데이터 blob으로 간주되는 operand.extraParams.extension에 연결된 데이터가 있을 수 있습니다.

OEM 연산 및 데이터 유형

NNAPI에는 기기 제조업체에서 드라이버와 관련된 맞춤 기능을 제공할 수 있게 해주는 OEM 연산 및 OEM 데이터 유형이 포함되어 있습니다. 이러한 연산 및 데이터 유형은 OEM 애플리케이션에 의해서만 사용됩니다. OEM 연산과 데이터 유형의 의미 체계는 OEM과 관련이 있으며 언제든지 변경될 수 있습니다. OEM 연산과 데이터 유형은 OperationType::OEM_OPERATION, OperandType::OEM, OperandType::TENSOR_OEM_BYTE를 사용하여 인코딩됩니다.