基于国产AI编译器ICRAFT部署YOLOv5边缘端计算的实战案例

描述

 

1)背景介绍

1.1 边缘端部署

人工智能领域中各种算法模型的不断研究和改进。随着深度学习的兴起,包括卷积神经网络(CNN)、循环神经网络(RNN)、生成对抗网络(GAN)、transformer等在内的各种深度学习算法被广泛应用于图像识别、自然语言处理、语音识别等任务中。AI算法的作用是通过训练模型来实现自动化的数据分析、决策和预测,帮助人类解决复杂的问题。

为了将AI算法赋能社会生产生活的各个领域中,AI加速芯片和配套使用的AI算法编译器也逐渐发展成熟。本篇主要介绍在边缘设备部署AI算法的经验。边缘设备一般指智能手机、汽车电子、智能工业、农业物联网设备等。部署到边缘设备上的AI模型能够实时地对数据进行处理,可以在不需要云端服务器的情况下,提供快速的响应和处理能力。边缘端AI部署的优势主要体现在以下几个方面:

1. 实时性:边缘设备上部署的AI模型可以实时地对数据进行处理,不需要传输数据到云端服务器进行处理,能够大大减少数据传输和处理延迟。

2. 隐私保护:由于边缘设备上处理数据时不需要将数据传输到云端服务器,因此能够更好地保护用户隐私,减少因数据传输而带来的潜在安全风险。

3. 节约成本:边缘设备上的AI模型可以避免云端服务器的成本,能够有效地节约部署成本,尤其是在大规模部署时效果更为明显。

4. 灵活性:边缘设备上的AI模型可以与其他不同类型的设备相结合,构建出更为灵活、智能化的系统,例如智能家居、互联网医疗等。

1.2 AI加速芯片

为了在边缘端设备部署AI算法,往往需要专用AI加速芯片来加速算法计算。相比于通用的图形处理单元(GPU),用于边缘端的专用AI加速芯片具有以下优势:

- 高效性能:AI专用加速芯片采用了专门优化的计算架构,能够更高效地执行深度学习计算,提供更快的推理速度和更低的能耗。

- 低功耗:AI专用加速芯片在设计上注重节能,能够在相同计算能力下拥有更低的功耗,适合移动设备和嵌入式系统等对能耗要求较高的场景。

- 支持量化计算:AI专用加速芯片通常支持低精度的量化计算,能够在保持模型精度的同时减少模型的内存占用和计算量。

- 专注深度学习:由于AI专用芯片是专门为深度学习任务设计的,因此在深度学习计算方面有着更好的优化和支持,能够提供更好的用户体验和性能表现。

1.3 AI编译器

AI编译器是一种用于优化深度学习模型的工具,其主要功能是将高级的深度学习模型转化为底层硬件能够执行的低级指令序列,以提高模型的推理速度和效率。AI编译器可以针对特定的硬件平台进行优化,充分发挥硬件资源的性能潜力。 AI编译器的主要工作流程包括模型解析、优化、代码生成等过程。在模型解析阶段,AI编译器会解析深度学习模型的结构和参数,以便后续的优化工作。在优化阶段,AI编译器会进行各种优化技术,如张量融合、内存优化、量化计算等,以提高模型的性能和效率。最后,在代码生成阶段,AI编译器会将优化后的模型转化为特定硬件平台上的可执行指令序列,以实现高效的模型推理。

2)YOLOv5介绍

AI芯片

YOLOv5是一种目标检测算法,它是YOLO(You Only Look Once)系列模型的经典版本。YOLOv5通过使用深度卷积神经网络来实现实时、准确地检测图像或视频中的多个对象。

1. 高精度:YOLOv5在目标检测任务上取得了很好的性能,相较于之前版本的YOLO,YOLOv5在速度和准确率方面都有所提升。

2. 轻量级:YOLOv5采用了轻量级的网络结构设计,具有较少的参数量和计算量,适合在资源受限的环境下部署和应用。

3. 多尺度检测:YOLOv5引入了多尺度检测机制,可以在不同尺度下对目标进行检测,提高了对小目标和远距离目标的检测效果。

4. 强大的数据增强:YOLOv5采用了大量的数据增强技术,如随机缩放、随机扭曲等,来扩充训练集,提高模型的泛化能力和鲁棒性。

5. 简单易用:YOLOv5提供了简洁的代码和易于使用的接口,用户可以方便地进行训练、测试和部署。

3)ICRAFT编译器介绍 ICRAFT是一款轻量易用的纯国产自主研发AI编译器,其优势有:

- 易用性:安装环境简单,使用方便

- 扩展性强:支持用户自定义算子,以及算子后端

- 性能优秀:在同类产品中(相同算力后端芯片),推力性能以及模型精度处于上游水平

目前支持pytorch,tensorflow,paddle-paddle,darknet,caffe等主流框架所保存的模型转换。编译器将各环节组件打包成可执行程序,通过命令行调用,配合外部ini配置文件执行各个组件。如图所示,目前ICRAFT按功能层次划分了5个组件,分别是:

- 解析:将模型解析并用ICRAFT IR重新表达、序列化成新模型

- 优化:做图优化,减少计算量

- 量化:将模型参数量化

- 硬件适配:根据硬件情况进行计算图优化

- 指令生成:生成硬件指令

每个环节产生的中间模型都会被保存用于仿真验证。

AI芯片

此外,ICRAFT还提供了c/c++(未来还会提供python)的device和runtime库用于在硬件上部署编译后的模型。 下面我们就以yolov5为例,为大家介绍模型的编译部署流程。

4)基于ICRAFT部署YOLOv5的流程 部署流程分为3步:

1. 框架模型导出

2. 使用ICRAFT编译模型

3. 运行时工程搭建

下面结合pytorch框架下的yolov5分别介绍每一步操作:

3.1 框架模型导出  

框架模型导出目的:导出能够被ICRAFT编译的模型 框架模型导出背景:

1. pytorch保存的模型文件为.pt或.pth等格式。不同的保存方式会导致模型里面的内容不同。目前ICRAFT能够编译的pytorch模型需要是带有计算图的静态模型,因此需要用pytorch的`torch.jit.trace`保存出torchscript模型。

2. 由于ICRAFT的目标硬件是异构融合的芯片,包括npu、cpu、fpga等硬件资源,一个模型算法的不同计算部分由不同的后端执行。在ICRAFT2.x版本中,ICRAFT编译的部分是在npu上执行的部分,而模型的前后处理是在其他后端进行的,因此在导出模型时,需要把前后处理部分去掉。

了解以上背景后,我们来看看在yolov5这个算法。此算法导出模型时需要处理的有两点:

1)`export.py`已经提供了导出torchscript模型的方法,但是我们需要把`yolo.py`中的 `Detect`类中的一部分处理bbox的算子从模型中移出,处理方法就是,在导出模型时,修改detect类的forward函数:

   class Detect(nn.Module):
       stride = None  # strides computed during build
       onnx_dynamic = False  # ONNX export parameter
       export = False  # export mode
       trace = False
       ...
       def forward(self, x):
           z = []  # inference output
           for i in range(self.nl):
               x[i] = self.m[i](x[i])  # conv
               if not self.trace:
                   ...
           if self.trace:
               return x
在detetct类中加入trace的判断条件,在导出模型时设置为true,使这部分的计算流停止到最后一层卷积算子的计算,即`x[i] = self.m[i](x[i]) # conv`,之后立刻返回结果。当然,这只是一种操作办法,能达到相同的效果的方法都可以使用。

2)在yolov5早期版本中,算法起始部分使用了`focus`(有些算法里叫`reorg`)模块,这种操作在硬件上支持起来并不划算,为此我们可以用一个卷积来等效该操作: 为了方便理解,我们把weights用数字的方式展现:
   class Focus(nn.Module):
       device = torch.device("cpu")
       weights = torch.tensor([[[[1,0],[0,0]],[[0,0],[0,0]],[[0,0],[0,0]]],
                                 [[[0,0],[0,0]],[[1,0],[0,0]],[[0,0],[0,0]]],
                                 [[[0,0],[0,0]],[[0,0],[0,0]],[[1,0],[0,0]]],
   
                                 [[[0,0],[1,0]],[[0,0],[0,0]],[[0,0],[0,0]]],
                                 [[[0,0],[0,0]],[[0,0],[1,0]],[[0,0],[0,0]]],
                                 [[[0,0],[0,0]],[[0,0],[0,0]],[[0,0],[1,0]]],
   
                                 [[[0,1],[0,0]],[[0,0],[0,0]],[[0,0],[0,0]]],
                                 [[[0,0],[0,0]],[[0,1],[0,0]],[[0,0],[0,0]]],
                                 [[[0,0],[0,0]],[[0,0],[0,0]],[[0,1],[0,0]]],
       
                                 [[[0,0],[0,1]],[[0,0],[0,0]],[[0,0],[0,0]]],  
                                 [[[0,0],[0,0]],[[0,0],[0,1]],[[0,0],[0,0]]],
                                 [[[0,0],[0,0]],[[0,0],[0,0]],[[0,0],[0,1]]]],
                              dtype=torch.float32).to(device)
   
       def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
           super(Focus, self).__init__()
           self.conv = Conv(c1 * 4, c2, k, s, p, g, act)   
   
       def forward(self, x):  
           x = nn.functional.conv2d(x,self.weights,bias=None,stride=2)
           x = self.conv(x)
           return x
这个后加入的`nn.functional.conv2d`与`focus`中的若干`slice` ,`concat`计算等效,可以不用重训模型。

3.2 ICRAFT编译  

使用icraft编译模型十分方便,只需要准备好编译模型所需的若干文件后,在命令行执行:`icraft compile [配置文件路径]`即可一键编译。 需要准备的文件有3方面:

1. 第一步导出的模型

2. 配置文件

3. 量化校准集

配置文件说明: icraft2.x及之前版本,需要一份`.ini`格式的配置文件,下面以icraft2.2版本的yolov5配置文件为例展开说明:(涉及到具体某个组件的所有配置参数与说明,可以参考随ICRAFT外发的用户手册说明)
# 在icraft的使用中,此文件以“#”作为注释符,而不是默认的“;”


# config用来配置使用哪些内置的FPGA算子(后面简称硬算子)用于加速
# ImageMake的作用是加速输入图片的传入速率
# IcorePost的作用是为目标检测网络做阈值筛选,将合格的目标信息传出,加速输出速率
# customop_config是硬算子的配置文件,后面介绍
[config]
customop_on = ImageMake, IcorePost
customop_config = configs/customop/yolov5.ini


# parse section 用于配置解析组件的参数
[parse]
name = YoloV5s            # 编译成的json/raw模型名字
framework = pytorch          # 使用的框架
frame_version = 1.9          # pytorch的版本
input = 1, 640, 640, 3        # 输入的dims,4维输入要按照NHWC的顺序;其他维度与框架一致
input_format = NHWC          # 输入的layout,4维:NHWC;其他:FD
pre_method = resize          # 前处理方法,默认写resize即可
pre_scale = 255, 255, 255      # 输出归一化参数
pre_mean = 0, 0, 0          # 输出归一化参数
chann_swap = 2, 1, 0        # 图像输入,按照opencv读入可能需要做bgr转rgb
network = models/YoloV5/YoloV5s_640x640_traced.pt  # 第一步导出的模型地址
jr_path = json&raw/YoloV5s_BY/    # 编译后产生的中间模型保存路径
        
# optimize section 用于配置优化组件的参数
# 该组件只有少数情况需要特别配置,例如在某些图优化导致bug时,关闭某些优化pass
[optimize]
target = BUYI                    # 目标硬件
json = json&raw/YoloV5s_BY/YoloV5s_parsed.json    # 上一个组件产生的中间模型
raw = json&raw/YoloV5s_BY/YoloV5s_parsed.raw    # 上一个组件产生的中间模型
jr_path = json&raw/YoloV5s_BY/            # 编译后产生的中间模型保存路径
debug = false


# quantize section 用于配置量化组件的参数
[quantize]
forward_mode = image                 # 图片输入的方式
saturation = kld                  # 量化饱和点选取方式
per = tensor                    # 按层/通道的量化方式
forward_dir = images/coco              # 量化校准集图片文件夹路径
forward_list = images/coco.txt             # 量化校准集选取哪些图片的txt配置
batch = 1                      # 量化前向每次推理的图片数
bits = 8                      # 量化到多少bit
json = json&raw/YoloV5s_BY/YoloV5s_optimized.json   # 上一个组件产生的中间模型
raw = json&raw/YoloV5s_BY/YoloV5s_optimized.raw   # 上一个组件产生的中间模型
jr_path = json&raw/YoloV5s_BY/            # 编译后产生的中间模型保存路径  


# adapt section 用于配置硬件适配组件的参数
# 该组件与optmize组件类似都是会做一些图结构的等效修改
[adapt]  
target = BUYI                    
json = json&raw/YoloV5s_BY/YoloV5s_quantized.json 
raw = json&raw/YoloV5s_BY/YoloV5s_quantized.raw 
jr_path = json&raw/YoloV5s_BY/
debug = false


# generate section 用于配置指令生成组件的参数
[generate]
json = json&raw/YoloV5s_BY/YoloV5s_adapted.json
raw = json&raw/YoloV5s_BY/YoloV5s_adapted.raw
jr_path = json&raw/YoloV5s_BY/
log_path = ./logs/
qbits = 8                      # 与量化bit数保持一致  
rows = 4                      # 使用MPE核心数,默认即可
cols = 4                      # 使用MPE核心数,默认即可


# simulate section 用于配置仿真组件的参数
[simulate]  
target = BUYI                # 目标硬件
log_time = true
log_io = true
dump_ftmp = SFB                # 保存网络每个算子计算结果;SFB:浮点;SQB:定点
show = true                  # 少数分类网络可以直接查看结果
names = names/coco.names
json = json&raw/YoloV5s_BY/YoloV5s_BY.json  # 待仿真的中间模型路径
raw = json&raw/YoloV5s_BY/YoloV5s_BY.raw  # 待仿真的中间模型路径
image = images/coco/test_640x640.jpg    # 输入图片路径
  硬算子配置文件:
[IcorePost]
forward_dll = C:Icraft-CLIcustomopIcorePostIcorePost.dll
forward_so = /home/fmsh/ModelZoo/Deps/so/libcustom_IcorePost.so
quantized = true
# 需要修改之处
cmp_en = 1      # 是否做阈值比较
thr_f = 0.1     # 阈值 测精度时改为0.001
groups = 3      # 有几个输出head
anchor_num = 3    # 每个cell对应几个anchor;anchor free的情况配1
position = 5    # socre所在的位置;如果没有score,则配all,通过所有类别prob选取;


[ImageMake]
forward_dll = C:Icraft-CLIcustomopImageMakeImageMake.dll
forward_so = /home/fmsh/ModelZoo/Deps/so/libcustom_ImageMake.so
no_imkpad = 0
#mode = 1
quantized = true
 准备好以上文件后,执行编译命令,得到最终的`yolov5_BY.json`,`yolov5_BY.raw`,即可进行下一步。

3.3 运行时工程  

icraft提供了device 和运行时库,只需要新建一个c++工程依赖这些api,即可调用专用的AI硬件执行模型推理。

主要运行时api介绍:
# 打开设备
auto device = icraft::open(url.data());
# 传入json和raw文件,构造网络
auto network_ptr = std::make_shared(jsonPath.data(), rawPath.data());
# 构造runtime
icraft::Runtime runtime(network_ptr, device);
# 前处理
...
# 执行前向
# 输入需要是RutimeTensor的数据结构,只需要了解其构造方式即可自行做好前处理后传入
auto result_tensor = runtime_view->forward({ img_tensor });
# 得到的result_tensor也是RutimeTensor的数据结构,继续做后处理即可
# 后处理
完成运行时工程的编写后,即可在片上系统或交叉编译环境编译出可执行程序。然后在片上执行,即可得到推理结果。

AI芯片

 






审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分