华为NPU开发流程点滴

华为NPU开发流程点滴
NPU/CPU集成操作流程图介绍了App使用HUAWEI HiAI DDK的集成流程。
 
IR在线模型构建
IR在线模型构建通过IR单算子根据原始模型中的关系级联,配置权重数据,构建IR模型网络。
离线模型转换
离线模型转换需要将Caffe、TensorFlow、ONNX、MindSpore模型转换为HUAWEI HiAI平台支持的模型格式,并可以按需进行AIPP操作、量化操作,使用场景及方法如下:
  • AIPP操作
AIPP用于在硬件上完成图像预处理,包括改变图像尺寸、色域转换(转换图像格式)、减均值/乘系数(改变图像像素),运用后可避免重新训练匹配推理计算平台需要的数据格式,只通过AIPP参数配置或者在软件层面上调用AIPP接口即可完成适配,同时由于硬件专用,可以获得较好的推理性能收益,具体操作可参见AIPP模型转换
  • 量化操作
量化是一种可以把FP32模型转化为低bit模型的操作,以节约网络存储空间、降低传输时延以及提高运算执行效率,量化操作可参见量化模型转换
App集成
App集成流程包含模型预处理、加载模型、运行模型、模型后处理。
NPU算子库动态升级介绍
计算能力增强功能开放前,NPU算子库的功能、性能优化需要随EMUI升级发布,周期较长,用户无法及时享受到NPU算子库更新带来的功能、性能提升。HiAI DDK V520计算能力增强功能支持NPU算子库动态升级能力。可以使用户及时享受到NPU算子库更新带来的功能、性能提升。NPU算子库动态升级能力工作原理如下:
  1. 更新NPU算子库。
NPU算子库托管于华为云侧服务器,系统预置的computecapability APP检测到NPU算子库存在新版本后,系统同时在设备充电、连接WiFi、灭屏状态下从云端自动更新。
  1. 模型编译。
模型编译时,若满足计算能力增强功能使能,调用computecapability APP提供的服务,可完成模型编译,不再依赖系统中预置的NPU算子库。若不满足计算能力增强功能使能,继续使用系统提供的NPU算子库。
  1. 模型加载、运行。
模型编译完成后,模型的加载、运行使用ROM侧资源。
 
NPU算子库动态升级集成
  1. 配置编译依赖maven仓。
  1. buildscript {
  2.     repositories {
  3.         jcenter{
  4.             url "https://jcenter.bintray.com/"
  5.         }
  6.         google()
  7.     }
  8.     dependencies {
  9.         classpath 'com.android.tools.build:gradle:4.1.1'
  10. 10.     }
11. }
  1. 12.  
  2. 13.  
14. allprojects {
  1. 15.     repositories {
  2. 16.         google()
  3. 17.         jcenter()
  4. 18.         maven {url 'https://developer.huawei.com/repo/'}
  5. 19.     }
20. }
  1. 21.  
  2. 22.  
23. task clean(type: Delete) {
  1. 24.     delete rootProject.buildDir
25. }
  1. 添加编译依赖computecapability aar。
    1. dependencies {
    2.     implementation fileTree(dir: 'libs', include: ['*.jar'])
    3.     implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    4.     implementation 'com.huawei.hiai.hiai-engine:huawei-hiai-computecapability:11.0.2.300'
    5.     implementation 'com.android.support:recyclerview-v7:27.1.1'
    6.     implementation 'com.android.support:cardview-v7:27.1.1'
    7.     implementation 'com.android.support:appcompat-v7:27.1.1'
    8. }
    9. (可选)配置混淆脚本。
在应用级根目录下打开混淆配置文件“proguard-rules.pro”,加入排除computecapability aar的混淆配置脚本。
  1. -keep class com.huawei.hiai.computecapability.IComputeCapabilityDynamicLoader {*;}
  2. -keep class com.huawei.hiai.computecapability.ComputeCapabilityDynamicClient {*;}
  3. 添加头文件。
    1. #include "cloud_service.h"
    2. 使能计算能力增强接口Enable
调用CloudService::Enable(void* jniEnv, void* context),在模型管家Init接口前调用该接口。
该接口第一个参数为应用层JNIEnv,第二个参数为应用层Context对象。若是深度学习框架集成,这两个参数可以传递void*,由应用层开发人员透传。
  1. shared_ptr<AiModelMngerClient> client = make_shared<AiModelMngerClient>();
  2. if (!client ) {
  3.     LOGE("Create model manager client fail.");
  4.     return;
  5. }
  6. 6.   jobject
context
 =
env
->NewGlobalRef(
jcontext
);
  1. 7.   bool
ret
 = CloudService::Enable(
env
,
context
);
  1. if (!ret) {
  2.     LOGW("Cloud service enable failed. Will run as normal mode.");
10. }
11. ret = client->Init(nullptr);
JNIEnv参数直接传递JNI函数接口中的JNIEnv即可,以Demo为例详述Context的参数传递过程。
  1. Java层创建native接口时,添加Java层Context入参。
    1. 1.   public static native ArrayList<ModelInfo> loadModelSync(ArrayList<ModelInfo> modelInfo,
Context context
);
    1. Java层在Activity中调用该native接口,传入this。
  1. @Override
  2. protected ArrayList<ModelInfo> loadModel(ArrayList<ModelInfo> modelInfo) {
  3. 3.       return ModelManager.loadModelSync(modelInfo,
this
);
  1. }
  2. env->DeleteGlobalRef(context);
  3. 添加编译脚本SO依赖。
  4. include $(CLEAR_VARS)
    1. LOCAL_MODULE    := hiai_enhance
    2. LOCAL_SRC_FILES := $(DDK_LIB_PATH)/libhiai_enhance.so
    3. include $(PREBUILT_SHARED_LIBRARY)
    4.  
    5.  
    6. LOCAL_SHARED_LIBRARIES :=  hiai \
    7. 10.                            hiai_enhance \
    8. 验证计算能力增强使能。
    1. JNI层传递Context,创建JNI层jobject context对象的全局引用,使用完成后,需要释放掉。
    • OMG方式
      • App集成必选的so:libhiai.so、libhiai_enhance.so。
      • App集成可选的so:libai_fmk_dnnacl.so、libhiai_hcl_model_runtime.so、libcpucl.so、libhiai_ir_infershape.so。
    • IR在线方式
      • App集成必选的so:libhiai.so、libhiai_ir.so、libhiai_enhance.so,libhiai_ir_build.so。
      • App集成可选的so:libai_fmk_dnnacl.so、libhiai_hcl_model_runtime.so、libcpucl.so、libhiai_ir_infershape.so。
步骤4 Enable接口返回值为true,表示可以使能计算能力增强功能。若返回false,表示不使能计算能力增强功能。
打印Log:“CloudCollabCompile start”表示使用端云协同-NPU算子库动态升级的能力做模型编译。
  1. dnnacl_graph_optimizer.cpp PreGraphSaveOptimize(265)::"[dnnacl] optimize romVersion: 100.510.010.029!"
  2. dnnacl_graph_optimizer.cpp PreGraphSaveOptimize(273)::"PreGraphSaveOptimize success !"
  3. seq_block_mem_assigner.cpp AssignMemory(568)::"out, workspace, reuse, concat size:[1,0,0,0]"
  4. model_memory_assign.cpp MemoryCalculate(394)::" ModelMemAssign::sub graph max memory_size is 618496!"
  5. seq_block_mem_assigner.cpp AssignMemory(568)::"out, workspace, reuse, concat size:[3,0,1,0]"
  6. model_memory_assign.cpp MemoryCalculate(407)::" ModelMemAssign::main graph memory_size is 622592!"
  7. dnnacl_graph_compiler.cpp CloudCollabCompile(153)::"CloudCollabCompilestart."
  8. (可选)获取计算能力增强版本号。
需要在步骤2中使用最新版本computecapability aar。Enable接口返回值为true后,调用CloudService::GetComputeCapabilityVersion()来获取计算能力增强版本号。
  1. string versionName=CloudService::GetComputeCapabilityVersion();
注意
当调用HiAI接口失败时,App集成开发者需要回退到自己的通用流程中处理。
NPU算子库动态升级目前支持kirin 9000芯片的手机,且需要安装HarmonyOS系统。
支持Android API 19及以上版本。
建议使用NPU算子库动态升级功能的用户,模型使用最新omg工具生成。
编译JNI
推理模型编译JNI时,以NPU场景为例,打包APK时需要打包libhiai.so、libhiai_ir.so、 libhiai_model_compatible.so(可选)、libhiai_enhance.so(可选) 和libhiaijni.so,其中libhiaijni.so为Demo的JNI打包二进制。
编写CMakeLists.txt 以及npu_demo.cmake
以非异构运行在NPU上的场景为例:
CMakeLists.txt 如下:
  1. # Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
  2. cmake_minimum_required(VERSION 3.6.0)
  3. # please select the cmake file based on the type of device you want to run the APK.
  4. # NPU:npu_demo.cmake
  5. # CPU:cpu_demo.cmake
  6.  
  7.  
  8. include(npu_demo.cmake)
  9. #include(cpu_demo.cmake)
对应的npu_demo.cmake实现如下:
  1. # Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
  2.  
  3.  
  4. PROJECT(hiaijni LANGUAGES C CXX)
  5. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  6. set(CMAKE_CXX_STANDARD 11)
  7.  
  8.  
  9. include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni)
10. set(jnilibs ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI})
  1. 11.  
  2. 12.  
13. #Create libhiai.so
14. add_library(hiai SHARED IMPORTED)
15. set_target_properties(hiai PROPERTIES IMPORTED_LOCATION
  1. 16.         ${jnilibs}/libhiai.so)
  2. 17.  
  3. 18.  
19. #Create libhiai_ir.so
20. add_library(hiai_ir SHARED IMPORTED )
21. set_target_properties(hiai_ir PROPERTIES IMPORTED_LOCATION
  1. 22.         ${jnilibs}/libhiai_ir.so)
  2. 23.  
  3. 24.  
25. #Create libhiai_ir_build.so
26. add_library(hiai_ir_build SHARED IMPORTED )
27. set_target_properties(hiai_ir_build PROPERTIES IMPORTED_LOCATION
  1. 28.         ${jnilibs}/libhiai_ir_build.so)
  2. 29.  
  3. 30.  
31. #Create libhiai_model_compatible.so
32. add_library(hiai_model_compatible SHARED IMPORTED )
33. set_target_properties(hiai_model_compatible PROPERTIES IMPORTED_LOCATION
  1. 34.         ${jnilibs}/libhiai_model_compatible.so)
  2. 35.  
  3. 36.  
37. #Create libhiai_enhance.so
38. add_library(hiai_enhance SHARED IMPORTED )
39. set_target_properties(hiai_enhance PROPERTIES IMPORTED_LOCATION
  1. 40.         ${jnilibs}/libhiai_enhance.so)
  2. 41.  
  3. 42.  
43. link_directories(${jnilibs})
  1. 44.  
  2. 45.  
46. # hiaijni link library
  1. 47.  
  2. 48.  
49. # npu inference
50. if (${MODULE} STREQUAL "inference")
  1. 51.     add_library(hiaijni SHARED
  2. 52.             ../${MODULE_NAME}/src/main/jni/ClassifyJni.cpp
  3. 53.             ../${MODULE_NAME}/src/main/jni/Utils.cpp
  4. 54.             )
  5. 55.     target_link_libraries(hiaijni LINK_PUBLIC
  6. 56.             hiai
  7. 57.             hiai_ir
  8. 58.             hiai_model_compatible
  9. 59.             hiai_enhance
  10. 60.             android
  11. 61.             log)
62. endif()
  1. 63.  
  2. 64.  
65. # npu irmodulebuild
66. if (${MODULE} STREQUAL "irmodelbuild" )
  1. 67.     add_library(hiaijni SHARED
  2. 68.             ../${MODULE_NAME}/src/main/jni/ClassifyJni.cpp
  3. 69.             ../${MODULE_NAME}/src/main/jni/CreateModel.cpp
  4. 70.             ../${MODULE_NAME}/src/main/jni/Utils.cpp
  5. 71.             )
  6. 72.     target_link_libraries(hiaijni LINK_PUBLIC
  7. 73.             hiai
  8. 74.             hiai_ir
  9. 75.             hiai_ir_build
  10. 76.             hiai_model_compatible
  11. 77.             hiai_enhance
  12. 78.             android
  13. 79.             log)
80. endif()
  1. 81.  
  2. 82.  
83. set_target_properties(hiaijni PROPERTIES LINKER_LANGUAGE CXX)
拷贝DDK so到资源库
根据项目工程,将HiAI DDK的.so文件拷贝到项目对应位置。HiAI Demo中,将.so文件放置在“app/libs”目录下。
  • 如果在NPU上执行模型推理,App在模型预处理过程中集成libhiai.so、libhiai_ir.so、libhiai_model_compatible.so(可选)。
 
  • 如果使用ROM侧异构场景,App在模型预处理过程中集成libhiai.so、libhiai_ir.so、libhiai_model_compatible.so(可选),具体请参见异构调优工具
 
  • 如果使用DDK侧异构场景,App在模型预处理过程中集成libhiai.so、libhiai_ir.so、libhiai_ir_infershape.so、libcpucl.so、libhiai_ir_build.so、libhiai_ir_build_aipp.so(可选)、libhiai_hcl_model_runtime.so、libhiai_c_def.so、libai_fmk_dnnacl.so。
 
  • 如果使用模型保存场景,需要集成libhiai_ir.so和libhiai_debugger.so,使构建的模型保存到本地便于调试。
编辑build.gradle文件
指定ndk编译C++文件,在“/src/build.gradle”文件中,添加ndk编译信息如下。
 

序号

算子

含义

1

Activation

激活函数,支持模式:Abs、Sigmoid、Tanh、ELU、PReLU、Relu、Relu6

2

Add

计算2个输入的和

3

Argmax

返回输入的最大值对应的索引序号

4

BatchMatmul

在Batch维度上,对切片进行Matmul运算

5

BatchNorm

对输入在batch上做归一化计算

6

BatchToSpaceND

将批处理数据重新排列到空间数据块中,然后进行裁剪

7

BiasAdd

计算输入tensor和1个常量输入的和

8

BNInference

对输入在batch上做归一化计算,同BatchNorm

9

Cast

转换输入的数据类型

10

Ceil

向上取整

11

ClipByValue

将一个张量中的数值限制在[min, max]范围之内

12

Concat

数据按维度拼接

13

Convolution

卷积

14

ConvolutionDepthwise

深度卷积

15

ConvTranspose

转置卷积,计算过程约等于卷积的反向计算

16

Cos

计算cos

17

Crop

截取

18

CropAndResize

从输入图像张量中提取作物并调整它们的大小

19

Deconvolution

反卷积

20

DepthToSpace

Channel维通过空间块转移至Height、Width维的一种数据重排

21

Eltwise

按元素操作层(求和、乘积、最大值)

22

Erf

误差函数(也称为高斯误差函数)

23

Equal

判断第一个输入是否等于第二个输入

24

Exp

计算并返回给定输入、给定参数的EXP值

25

ExpandDims

对输入shape进行补维

26

Expm1

计算y = exp(x) - 1

27

Fill

使用输入的维度和值信息,生成一个tensor

28

Flatten

保留输入的第一个维度,把第一个维度包含的每个子张量展开成一个行向量

29

FlattenV2

将输入张量扁平化为二维矩阵,可选起始和终止axis

30

Floor

向下取整

31

FloorDiv

x / y 向下取整

32

FloorMod

返回除法元素的余数

33

FullConnection

全连接

34

Gather

根据indices对输入tensor的第axis根轴获取合成新的tensor

35

GatherNd

将输入张量的切片聚合成具有indices指定维度的新张量

36

Gemm

使用输入张量、一组学习的权重计算内积,并添加偏差

37

Greater

判断第一个输入是否大于第二个输入

38

GreaterEqual

按元素返回(x1 >= x2)真值

39

InstanceNorm

对输入在像素上做归一化计算

40

Interp

插值层

41

LeakyRelu

激活函数

42

LayerNorm

层归一化函数

43

Less

按元素返回(x1 < x2)真值

44

LessEqual

比较两个输入张量每个元素是否满足小于等于的关系

45

Log

计算每个元素的自然对数

46

Log1p

计算(1+x)元素的自然对数

47

LogicalAnd

对两个输入张量做逻辑与运算

48

LogicalNot

对输入张量做逻辑非运算

49

LogicalOr

对两个输入张量做逻辑或运算

50

LRN

局部响应标准化,用于防止数据过度拟合

51

Matmul

对2个输入做矩阵乘法运算

52

Maximum

计算2个输入中的较大值

53

MirrorPad

根据指定的paddings来填充一个tensor

54

Minimum

返回两个输入中小的元素

55

Mul

计算2个输入的乘积

56

Neg

对每个元素值取反

57

NotEqual

比较两个输入张量每个元素是否满足不等于的关系

58

NonMaxSuppressionV3D

按分数降序选择边界框的子集

59

NonMaxSuppressionV6

选出与上一个框有较高的交叉点重叠率(IOU)的框

60

OneHot

将input转化为独热类型数据输出

61

Pack

将多个输入堆叠拼接成1个

62

Pad

对输入做shape填充

63

PadV2

对输入做shape填充,填充值为constant_values

64

Permute

对输入做维度转置操作

65

Pooling

池化层

66

Pow

计算幂

67

Power

计算y =(scale*x+shift)^ power

68

Prelu

执行参数ReLU,生成一个输出数据(Tensor)

69

PriorBox

在特征图的每个位置生成默认框

70

RealDiv

根据实际数据类型,按元素返回x1/x2

71

ReducelogsumExp

沿着axis指定的轴计算log(sum(exp(elements)))

72

ReduceMax

计算张量沿着某一维度的和,可以在求最大值后降维

73

ReduceMean

计算一个张量中各维元素的均值

74

ReduceMin

计算张量沿着某一维度的和,可以在求最小值后降维

75

ReduceProd

计算一个张量各个维度上元素的乘积

76

ReduceSum

计算张量沿着某一维度的和,可以在求和后降维

77

Reduction

算张量沿着某一维度的和/绝对值和/平方和/均值和,降维运算

78

Reorg

实现数据重排,调整tensor维度。以stride为步长对Channel、Height、Width放大或缩小

79

Reshape

改变输入维度

80

ResizeBilinear

将图片尺寸调整到指定的大小

81

ResizeNearestNeighbor

使用最近邻差值算法进行图像大小调整

82

ResizeNearestNeighborV2

使用最近邻插值将图像大小调整为size大小

83

Rint

查找并返回最接近“x”的整数,如果结果在两个可表示值之间选择偶数表示

84

Round

将张量的值按元素四舍五入到最接近的整数

85

Rsqrt

计算每个元素的平方根的倒数

86

Scale

out=alpha*Input+beta

87

ScatterUpdate

将稀疏更新应用于变量引用

88

Select

根据条件选择输出

89

ShuffleChannel

对输入以Channel维度按照group做数据分组重排

90

ShuffleChannelV2

在axis维度做Shuffle运算

91

Sin

计算sin

92

Slice

从输入张量中提取切片,此操作从由begin指定位置开始的张量input中提取一个尺寸size的切片

93

Softmax

归一化逻辑函数

94

SpaceToBatchND

重新排列的空间数据块成批

95

SpaceToDepth

通过空间快将Height、Width维的数据转移至Channel维的一种数据重排

96

SparseToDense

将稀疏表示转换为密集张量

97

Split

沿指定维度将输入张量平均分割成一个张量列表

98

SplitV

沿指定轴将输入张量分割为num_split个新的张量

99

Sqrt

计算输入张量的平方根

100

Square

计算每个元素的平方

101

Squeeze

从输入张量的shape中删除一个值为1的维度

102

SSDDetectionOutput

SSD网络的一个算子,它的作用是根据priorbox和其对应的偏移量、评分信息来生成检测目标的数量和候选框

103

StrideSlice

对输入从起始位置到结束位置按照步长截取数据

104

StrideSliceV2

提取张量的一个分段切片

105

Sub

计算2个输入的差

106

Tan

计算Tan

107

Threshold

如果该值大于阈值,则输出为1;否则,输出为0

108

Tile

平铺给定矩阵

109

TopK

提取最后一个维度的k个最大值

110

TruncateDiv

按元素返回x / y,对结果小数部分截断保留整数部分,截断表示负数将小数量向零舍入

111

Unpack

Pack的逆操作,将数据沿axis轴进行分组,输出N个数据维度相同,维度数减1

112

Xlogy

返回x乘以log(y)

 
 
参考文献链接
https://developer.huawei.com/consumer/cn/doc/hiai-Guides/introduction-0000001051486804
posted @ 2024-04-28 03:43  吴建明wujianming  阅读(234)  评论(0编辑  收藏  举报