We realize a real-time object detection system with Vitis AI. To be specific, we use the AI model which regards the object as a point, correct the box by regression. The model can recognize the object in a small time. To deploy the model on the board, we prune and quantify the model also with knowledge distillation method to get a good accuracy. Finally we compile the model with Vitis-AI tool and get the xmodel which can be run on the board. We use the VART C++ interface to realize the program on the board, and both test the image and the video, which can reach 40 frame a second to recognize the video adas detection.
前期环境配置(docker启动Vitis-AI)docker环境配置参照Docker教程下载docker容器,docker的目的是,将所有需要用的环境集成到一个容器中,方便管理和移植。下载docker后,可将vitis-ai相关环境安装到docker容器中。
docker-安装.doc
VITIS-AI下载下载vitis-ai到文件夹下:
git clone https://github.com/Xilinx/Vitis-AI
如果有网络之类的报错可以去github网页下载
- 部署
进入vitis-ai目录:
cd Vitis-AI
运行VITIS-AI docker cpu部署脚本:
bash docker_run.sh
此过程是将vitis-ai所需要的环境下载到本地docker中,下载成功后可应用vitis-ai提供的相关环境。下载过程大概为2h左右,下载成功会出现以下图标:
- 运行
需要启动vitis-ai,则先进入vitis-ai文件夹下:
cd Vitis-AI
启动vitis-ai:
./docker_run.sh xilinx/vitis-ai-cpu:latest
该命令即启动docker,并拉取vitis-ai环境。
启动成功后可通过命令提示的conda指令,来访问不同框架的环境。
centernet模型生成实验环境准备激活相应环境,本centernet代码采用pytorch框架,因此需要相应pytorch环境和vitis-ai对于pytorch框架的功能环境。
在docker vitis-ai下,运行:
conda activate vitis-ai-pytorch
可激活pytorch以及vitis-ai所需要的环境。
下载centernet代码文件- 搭建centernet网络及工具文件
- 下载voc数据集,放入centernet文件夹中,用于训练等步骤。
- 生成数据集训练txt文件,目的是为代码提供数据地址方便代码解析数据集。代码原理为,遍历数据集中的图片,应用系统方法生成图片路径集合保存在文件中。
相应的生成txt的代码为【voc2centernet.py】【voc_annotation.py】,将代码中的voc数据集路径改为实际的数据集路径,分别运行:【voc2centernet.py】【voc_annotation.py】得到:
注意:修改python文件中的voc数据集路径为相应路径
- 训练
目的是多次迭代,产生拟合目标的权重值。
运行:python train.py
- 得到模型文件
训练的每个迭代需要一定时间,在logs中,将会保存每次迭代生成的权重文件。如果时间有限,可选择loss较小的一次训练结果作为最终结果
量化是为了减少存储大小,将原始的浮点权重舍弃一些精度并取整,并经过少量数据集进行再训练得到最终的定点数模型权重文件。量化程序代码为quantize.py
编写量化程序关键步骤:
- import相关量化包,vitis-ai提供的量化接口。
from pytorch_nndct.apis import torch_quantizer, dump_xmodel
- 加载模型,加载需要量化的模型及模型权重。
model = CenterNet_Resnet50(num_classes, pretrain=False).to(device)
model.load_state_dict(torch.load('float_model/centernet.pth', map_location='cpu'))
其中CenterNet_Resnet50为包含模型结构的模型类,.pth文件为训练好的浮点模型文件
- 创建量化器并量化
rand_in = torch.randn([batchsize, 3, 512, 512])#创建输入格式
quantizer = torch_quantizer(quant_mode, model, (rand_in), output_dir=quant_model)#创建量化器
quantized_model = quantizer.quant_model#量化模型
- 创建data_loader并评估模型(微调)
test_loader = DataLoader(test_dataset,
batch_size=batchsize,
shuffle=False, num_workers=8, pin_memory=True,
drop_last=True, collate_fn=centernet_dataset_collate)
quantized_model.eval()
- 生成量化后的模型
quantizer.export_xmodel(deploy_check=False, output_dir=quant_model)
注意:以上步骤中含有quant_mode参数,为量化方式,一般包含‘test’和‘calib’;传统上,需要先进行calib再进行test。calib即为迭代,用来更新定点参数,test为测试,输出最终定点文件。先进行calib,再进行test,因此quantize程序将运行2次。
Vitis-AI编译编译的目的是将原始模型文件转换为可以在DPU上运行的硬件可识别比特流,需先确定目标硬件平台后执行编译的指令。编译脚本文件为compile.sh
编写编译脚本文件流程:
- 确定ARCH和TARGET
本target为KV260
- 编译指令:
vai_c_xir \
- -xmodel quantized_model/CenterNet_Resnet50_int.xmodel \
- -arch $ARCH \
- -net_name CenterNet_${TARGET} \
- -output_dir compiled_model
修改量化文件目录及输出文件目录所需目录
- 运行脚本,将生成xmodel文件在compiled_model目录下。xmodel文件即为可以被硬件识别的二进制比特流。
在运行之前,确保KV260上安装了vitis-ai-library库及其他必要环境,很多接口在库中提供。
本项目在Vitis-AI1.3环境上部署运行。
测试代码编写测试代码编写关键流程:
测试代码在文件夹dpu/dpu_centerNet_cam中,包含源代码,编译脚本,函数模型文件和可执行文件
关键代码编写流程:
- xmodel文件导入:需要准备第2章所得到的xmodel文件,用xir提供的graph处理接口读取文件,即读取到模型的结构和参数:
g = xir.Graph.deserialize(xmodel)
- 筛选dpu子图,遍历子图的每一层,筛选出dpu处理的层,有些特殊的层vitis-ai尚未支持,因此这些层只能通过cpu进行处理
subgraphs = get_child_subgraph_dpu(g)
def get_child_subgraph_dpu(graph: "Graph") -> List["Subgraph"]:
assert graph is not None, "'graph' should not be None."
root_subgraph = graph.get_root_subgraph()
assert (root_subgraph is not None), "Failed to get root subgraph of input Graph object."
if root_subgraph.is_leaf:
return []
child_subgraphs = root_subgraph.toposort_child_subgraph()
assert child_subgraphs is not None and len(child_subgraphs) > 0
return [
cs
for cs in child_subgraphs
if cs.has_attr("device") and cs.get_attr("device").upper() == "DPU"
]
- 创建dpu运行器,为进行后续的操作创建一个类。
dpu = vart.Runner.create_runner(subgraphs[0], "run")
- 构建dpu运行输入输出框架,即构建一个大小符合数据输入输出要求的指针,保存输入输出数据。
inputTensors = dpu.get_input_tensors()
outputTensors = dpu.get_output_tensors()
得到inputTensor和outputTensor后,从其中获取输入输出的维度
input_ndim = tuple(inputTensors[0].dims)
output_ndim1 = tuple(outputTensors[0].dims)
output_ndim2 = tuple(outputTensors[1].dims)
output_ndim3 = tuple(outputTensors[2].dims)
构建输入文件和输出格式:
imageRun[0, ...] = img.reshape(input_ndim[1:])
outputData = [outputData1, outputData2, outputData3]
- 运行dpu,将输入inputData数据传入,所得的结果保存在outputData中。
job_id = dpu.execute_async(inputData, outputData)
dpu.wait(job_id)
此时得到的outputData即为神经网络输出的张量
- 后处理
centernet的输出有三个,分别为:
outputData[0]->heat map
得到分割小格每一格预测出的物体类别置信度,维度为128*128*20
outputData[1]->wide and height
得到每一格预测出的物体的宽高,维度为128*128*2
outputData[2]->offset
得到每一格预测出的物体的坐标偏移量,维度为128*128*2
针对这三个输出,分别进行处理
对于heat map:
处理流程为:sigmoid->maxpool->process
经过sigmoid和maxpool,可以得到与周围对比有物体的小格的置信度,在经过process,将这些小格与threshold对比,得到大于阈值的小格(位置)
对于宽高:
存储大于阈值小格的宽高,即为对应位置预测物体框的宽高
对于偏移:
将第一步得到的初选小格,加上预测便宜,得到最终预测物体的中心点坐标
运行将检测文件和xmodel文件拷备到KV260上,代码无误即可运行出结果
本demo是调用摄像头进行识别,可以将图片获取进行适当调整,得到需要的应用。
范雨,柳箐汶
Comments