【Vitis AI】FPGA设备使用PyTorch 运行 ResNet18获得10000fps
来自 Vitis AI 库的 PyTorch 中的 ResNet18
- 版本:Vitis AI 3.5 with Pytorch 1.13.1
- 支持:ZCU102, ZCU102, VCK190, VEK280, Alveo V70
1 简介
1.1 理由
在本深度学习 (DL) 教程中,您将使用一个公共领域的卷积神经网络 (CNN),如 ResNet18,并通过 Vitis AI 3.5 堆栈在 FPGA 设备上运行 DL 推理;该应用程序对图像中“汽车对象”的不同颜色进行分类。
尽管 ResNet18 已经在 PyTorch 框架中的 ImageNet 数据集上进行了训练,但您将使用 Kaggle Vehicle Color Recognition 数据集(简称 VCoR)重新训练它。
1.2 Vitis AI 流程
假设您已经训练了 CNN 并拥有其原始模型,通常是一个扩展名为.h5
的 HDF5 文件,您将按照以下步骤将该 CNN 部署到 FPGA 目标板:
-
(可选)运行 Model Inspector 检查原始模型是否与 AMD Deep Processor Unit (DPU) 架构兼容,该架构在目标板上可用(如果不是,您必须修改 CNN 并重新训练它)。
-
运行 Model Quantization 过程,从原始 32 位浮点 CNN 生成一个 8 位定点(简称“int8”)模型。如果您应用所谓的Post-Training Quantization (PTQ),这将是一个单步操作,否则您需要使用 Quantization-Aware-Training (QAT) 重新训练(或者更确切地说“微调”)CNN。
-
(可选)在 Vitis AI 环境(在主机桌面上运行)上使用 int8 模型运行推理,以检查预测准确性:如果差异不可忽略(例如,大于 5%),您可以将 PTQ 替换为 QAT 重新进行量化。
-
在 int8 模型上运行 Model Compilation 过程,为目标板的 DPU IP 软核生成
.xmodel
微代码。 -
使用带有 Vitis AI RunTime (VART) API 的 C++ 或 Python 代码编译在目标板的 ARM CPU(与 DPU 紧密耦合)上运行的应用程序。
基于此,您将能够测量推理性能,包括目标板上的平均预测准确性和每秒帧数 (fps) 吞吐量。
本文档中报告的所有命令也收集在 run_all.sh 脚本中。
2 前提条件
以下是您在开始本教程的实际内容之前需要具备和执行的内容。
-
熟悉 DL 原理。
-
在运行任何脚本之前,请从头到尾准确阅读此 README.md 文件。
-
具有 Ubuntu >= 18.04.5 的主机 PC(并且可能具有 GPU 支持以运行 CNN 训练)。
-
从 www.github.com/Xilinx 网站克隆 Vitis AI 3.5 堆栈的整个存储库。
-
准确阅读 Vitis AI User 3.5 Guide 1414(简称 UG1414)。
-
准确阅读 Vitis AI 3.5 在线文档。特别是,请注意主机 PC 和目标板的安装和设置说明,建议您构建一个基于 GPU 的带有 TF2 的 Docker 镜像。
-
AMD 目标板,例如:
- Zynq® UltraScale+™ MPSoC ZCU102,或
- Zynq® UltraScale+™ MPSoC ZCU104,或
- Versal AI Core 系列 VCK190,或
- Versal AI Edge 系列 VEK280,或
- Alveo V70 AI 加速器。
-
包含 Kaggle 图像数据集的
archive.zip
文件,如第 4 VCoR 数据集 节中所述。 -
大小为 179507695 字节的 模型动物园
pt_vehicle-color-classification_3.5.zip
存档,如第 [5.1 从 Vitis AI 模型动物园获取 ResNet18](#51-get-resnet18 -from-vitis-ai-model-zoo) 节中所述。
2.1 工作目录
在本文档的后续部分中,假设您已在文件系统中的某个位置安装了 Vitis AI 3.5(简称VAI3.5
),这将是您的工作目录${WRK_DIR}
,例如export WRK_DIR=/media/danieleb/DATA/VAI3.5
。您还在${WRK_DIR}
下创建了一个名为tutorials
的文件夹,并将本教程复制到该文件夹并将其重命名为PyTorch-ResNet18
。使用命令tree -d -L 2
,您应该看到以下目录:
${WRK_DIR} # 您的 Vitis AI 3.5 工作目录
├── bck
├── board_setup
│ ├── v70
│ └── vek280
├── demos
├── docker
│ ├── common
│ ├── conda
│ └── dockerfiles
├── docs
│ ├── docs
│ ├── _downloads
│ ├── doxygen
│ ├── _images
│ ├── _sources
│ └── _static
├── docsrc
│ ├── build
│ └── source
├── dpu
├── examples
│ ├── custom_operator
│ ├── ofa
│ ├── OnBoard
│ ├── vai_library
│ ├── vai_optimizer
│ ├── vai_profiler
│ ├── vai_quantizer
│ ├── vai_runtime
│ ├── waa
│ └── wego
├── model_zoo
│ ├── images
│ └── model-list
├── src
│ ├── AKS
│ ├── vai_library
│ ├── vai_optimizer
│ ├── vai_petalinux_recipes
│ ├── vai_quantizer
│ └── vai_runtime
├── third_party
│ ├── tflite
│ └── tvm
└── tutorials # 由您创建
├── PyTorch-ResNet18 # 本教程
├── TF2-Vitis-AI-Optimizer
2.2 Dos 到 Unix 的转换
如果您在脚本执行期间遇到一些奇怪的错误,您必须仅使用 dos2unix 实用程序处理所有 *.sh
shell 脚本和 python *.py
脚本一次。
在这种情况下,请从 Ubuntu 主机 PC(在 Vitis AI Docker 镜像之外)运行以下命令:
sudo apt-get install dos2unix
cd ${WRK_DIR}/tutorials/PyTorch-ResNet18 #您的存储库目录
for file in $(find . -name "*.sh"); do
dos2unix ${file}
done
for file in $(find . -name "*.py"); do
dos2unix ${file}
done
for file in $(find . -name "*.c*"); do
dos2unix ${file}
done
for file in $(find . -name "*.h*"); do
dos2unix ${file}
done
这些操作已经包含在 run_all.sh 脚本中,该脚本由 run_all.sh 脚本启动,该脚本收集了本文档其余部分中显示的所有命令。
强烈建议您熟悉 run_all.sh 脚本,以便了解它所做的一切,最终了解主机计算机上的整个 Vitis AI 流程。
3 Docker 工具镜像
您必须了解有关 Docker 的一些知识,以便在您的主机 PC 环境上顺利运行 Vitis AI。
3.1 构建镜像
从 Vitis AI 3.5 存储库中,运行以下命令:
cd ${WRK_DIR}
cd docker
./docker_build.sh -t gpu -f pytoch
该过程完成后,使用命令docker images
,您应该看到类似以下内容:
仓库 TAG 镜像 ID 创建时间 大小
xilinx/vitis-ai-pytorch-gpu 3.5.0.001-b56bcce50 3c5d174a1807 27 小时前 21.4GB
3.2 启动 Docker 镜像
要使用 Vitis AI 工具启动 Docker 容器,请从 ${WRK_DIR}
文件夹执行以下命令:
cd ${WRK_DIR} # 您现在位于 Vitis_AI 子文件夹中
./docker_run.sh xilinx/vitis-ai-pytorch-gpu:latest
conda activate vitis-ai-pytorch
cd /workspace/tutorials/
cd PyTorch-ResNet18 # 您的当前目录
请注意,该容器将共享文件夹 /workspace
与您从中启动上述命令的主机 PC 的文件系统映射。
此共享文件夹使您能够将文件从主机 PC 传输到 Docker 容器,反之亦然。
Docker 容器没有任何图形编辑器,因此建议您使用两个终端并指向同一个文件夹,在一个终端中使用 Docker 容器命令,在另一个终端中打开您喜欢的任何图形编辑器。
如果您需要添加一些其他软件包,例如randaugment
和torchsummary
:
sudo su
conda activate vitis-ai-pytorch
pip install randaugment
pip install torchsummary
#exit
然后记住从不同的终端(除了您正在运行 Docker 镜像的第一个终端之外的第二个终端)永久保存修改后的 Docker 镜像,
方法是启动以下命令:
$ sudo docker ps -l
$ sudo docker commit -m"COMMENT" CONTAINER_ID DOCKER_IMAGE
您应该看到类似以下内容:
$ sudo docker ps -l
容器 ID 镜像 命令 创建时间
8626279e926e xilinx/vitis-ai-pytorch-gpu:3.5.0.001-b56bcce50 "/opt/nvidia/nvidia_…" 6 小时前
$ sudo docker commit -m"pyt new_package" 8626279e926e xilinx/vitis-ai-pytorch-gpu:3.5.0.001-b56bcce50
3.3 需要了解的事项
- 如果您“无法连接到 unix:/var/d9f942cdf7de xilinx/vitis-ai-tensorflow2-gpu:3.5.0.001-b56bcce50 run/docker.sock 处的 Docker 守护程序。Docker 守护程序是否正在运行?”,只需启动以下命令:
sudo systemctl restart docker
- 请注意,到目前为止,Docker 没有自动垃圾回收系统。您可以使用此命令执行手动垃圾回收:
docker rmi -f $(docker images -f "dangling=true" -q)
- 为了清理 Docker 消耗的(通常是大量)空间,请查看此帖子:Docker Overlay2 清理。以下命令非常有效(尤其是最后一个命令):
docker system df
docker image prune --all
docker system prune --all
4 VCoR 数据集
本教程中采用的数据集是 Kaggle Vehicle Color Recognition,简称 VCoR。
您可以在 Vitis AI 模型动物园在线表格 中找到它的超链接。
此数据集由要分类的 15 类颜色(用于汽车)组成。它包含大小为 224x224x3 的标记 RGB 图像,并且是为论文开发的
-
Panetta, Karen, Landry Kezebou, Victor Oludare, James Intriligator, and Sos Agaian. 2021. “人工智能在基于文本的车辆搜索、识别和交通视频中的持续定位中的应用” AI 2, no. 4: 684-704. https://doi.org/10.3390/ai2040041
-
开放访问:https://www.mdpi.com/2673-2688/2/4/41
在 Docker 容器之外,从 VCoR 网站下载约 602MB 的archive.zip
文件,然后将其解压缩到build/dataset/vcor
文件夹中
(如 run_all.sh 脚本中所示):
cd ${WRK_DIR}/tutorials/PyTorch-ResNet18/files
# 您必须已经下载了 zip 存档
unzip ./archive.zip -d ./build/dataset/vcor/
5 使用 ResNet18 进行车辆颜色分类
以下小节中显示的所有命令都可在 run_all.sh 脚本中使用,通常使用以下命令调用:
source ./run_all main_vocr
5.1 从 Vitis AI 模型动物园获取 ResNet18
您必须下载此 model.yaml 文件中报告的 ResNet18 的 pt_vehicle-color-classification_3.5.zip
存档。
正如文件名所说,此类 CNN 已经在输入大小为 224x224 的 RGB 图像上进行了训练
并且它需要每次图像计算 3.64GOP。
从 Docker 镜像中,解压缩 pt_vehicle-color-classification_3.5.zip
存档到files
文件夹中
并清理一些文件/文件夹,执行以下操作(已在 run_all.sh 脚本中提供):
cd ${WRK_DIR} # 您现在位于 Vitis_AI 子文件夹中
# 进入 Docker 镜像
./docker_run.sh xilinx/vitis-ai-pytorch-gpu:latest
# 激活环境
conda activate vitis-ai-pytorch
# 转到教程目录
cd /workspace/tutorials/
cd PyTorch-ResNet18/files # 您的当前目录
# 您必须已经下载了存档
unzip pt_vehicle-color-classification_3.5.zip
# 清理一些文件/文件夹
cd pt_vehicle-color-classification_3.5
rm -rf code data *.md *.txt *.sh
cd ..
您将获得 files/pt_vehicle-color-classification_3.5/
文件夹,您可以在其中分别在子文件夹 float
和quant
中找到预训练的浮点模型和量化模型。您可以忽略并删除所有其他子文件夹。
本教程中应用的 ResNet18 CNN 旨在识别输入图像中汽车车辆的颜色。
在实际应用中,输入图像通常包含多个车辆,或者存在许多区域作为背景,
因此通常与对象检测 CNN 一起使用,这意味着首先
使用对象检测网络检测车辆区域,并根据对象检测网络的输出边界框切割原始图像,然后将裁剪后的图像发送到网络进行分类。在pt_vehicle-color-classification_3.5
中
您可以使用 YoloV3 CNN 检测 VCoR 数据集中的汽车,并使用裁剪后的图像构建新数据集来训练和测试模型。
如果您的输入图像包含很少的背景,或者您的 CNN 没有与对象检测 CNN 结合使用,则可以跳过此步骤
(这确实是本教程中所做的)。
此车辆颜色模型属于 Vitis AI 库“分类”示例:
-
模型名称为
chen_color_resnet18_pt
,这使得它不明显它实际上是车辆颜色分类。 -
这是 汽车颜色列表。由于有 15 种颜色,因此也有 15 个要分类的类别。
-
DPU 输出将是具有 15 个类别的 分类结果数据结构。然后,ARM CPU 将使用此类输出张量来计算函数
SoftMax
和相关的Top-5
预测准确性。
5.2 训练
如果您想从头开始在 VCoR 数据集上训练 ResNet18 CNN,只需启动脚本 run_train.sh(这已经从 run_all.sh 脚本中完成):
cd ${WRK_DIR} # 您现在位于 Vitis_AI 子文件夹中
# 进入 Docker 镜像
./docker_run.sh xilinx/vitis-ai-pytorch-gpu:latest
# 激活环境
conda activate vitis-ai-pytorch
# 转到教程目录
cd /workspace/tutorials/
cd PyTorch-ResNet18/files # 您的当前目录
bash -x ./scripts/run_train.sh main_vcor
您应该看到类似以下内容:
. . .
Train Epoch: 29 [0/7267 (0%)] Loss: 0.004103
Train Epoch: 29 [5120/7267 (71%)] Loss: 0.006058
Test set: Average loss: 0.4320, Accuracy: 1380/1550 (89.032%)
. . .
classes: ['beige', 'black', 'blue', 'brown', 'gold', 'green', 'grey', 'orange', 'pink', 'purple', 'red', 'silver', 'tan', 'white', 'yellow']
Test set: Average loss: 0.4320, Accuracy: 1380/1550 (89.032%)
请注意,当您在 train.py 和 test.py 文件中使用 ToTensor()
类时,PyTorch 自动将所有图像转换为 [0,1]
范围。
图像应该采用 RGB 格式,而不是 BGR 格式(通常由 OpenCV 库采用)
5.3 量化
如果您想从头开始量化浮点 ResNet18 CNN,只需启动脚本 run_quant.sh(这已经从 run_all.sh 脚本中完成)。
您应该看到类似以下内容:
. . .
[VAIQ_NOTE]: =>正在进行权重均衡化...
[VAIQ_NOTE]: =>已生成可量化模块。(quantized/ResNet.py)
[VAIQ_NOTE]: =>获取带有量化的模块。
Test set: Average loss: 0.4234, Accuracy: 1376/1550 (88.774%)
. . .
[VAIQ_NOTE]: =>成功将“ResNet_0”转换为 xmodel。(quantized/ResNet_0_int.xmodel)
[VAIQ_NOTE]: 已生成 ResNet_int.pt。(quantized/ResNet_int.pt)
[VAIQ_NOTE]: 已生成 ResNet_int.onnx。(quantized/ResNet_int.onnx)
5.4 编译目标 DPU
然后必须针对目标板的 DPU 架构编译量化的 CNN,使用脚本 run_compile.sh(这已经从 run_all.sh 脚本中完成)。
您应该看到类似以下内容:
-----------------------------------------
为 VCK190 编译模型..
-----------------------------------------
[UNILOG][INFO] 编译模式:dpu
[UNILOG][INFO] 调试模式:null
[UNILOG][INFO] 目标架构:DPUCVDX8G_ISA3_C32B6
[UNILOG][INFO] 图名称:ResNet_0,具有操作数:171
[UNILOG][INFO] 开始编译...
[UNILOG][INFO] 设备子图总数 3,DPU 子图数 1
[UNILOG][INFO] 编译完成。
[UNILOG][INFO] 元 json 已保存到“/workspace/tutorials/PyTorch-ResNet18/files/./build/compiled_vck190/meta.json”
[UNILOG][INFO] 编译后的 xmodel 已保存到“/workspace/tutorials/PyTorch-ResNet18/files/./build/compiled_vck190/vck190_ResNet_0_int.xmodel.xmodel”
[UNILOG][INFO] 编译后的 xmodel 的 md5sum 为 df3f607e0b632e90d4c8eafdd174bef8,并且已保存到“/workspace/tutorials/PyTorch-ResNet18/files/./build/compiled_vck190/md5sum.txt”
**************************************************
5.5 在目标板上运行
仅针对 VEK280 板显示以 fps 为单位测量的吞吐量,仅作为参考。对于其他板,结果可能会因不同的 DPU 架构和相关的批处理大小 (BS) 而有很大差异。
以下小节中说明的所有命令都在脚本 run_all_vcor_target.sh 中,它们通过启动命令 run_all_target.sh xxxyyy
直接应用于目标板 xxxyyy
(即 zcu102、vck190、v70、vek280 等),这涉及 run_all_target.sh 更高级别的脚本。
5.5.1 多线程 C++ 应用程序代码
在目标板的嵌入式 ARM CPU 上运行的 C++ 应用程序是在 main_int8.cc 文件中编写的。请注意,输入图像在进入 DPU 之前以与训练期间预处理的完全相同的方式进行预处理,即:
-
RGB 图像格式(而不是 BGR);
-
像素范围 [0, 255] 被归一化为数据范围 [0,1]
以下是相关的 C++ 代码片段:
Mat image = imread(baseImagePath + images[n + i]);
/*图像预处理*/
Mat image2 = cv::Mat(inHeight, inWidth, CV_8SC3);
resize(image, image2, Size(inHeight, inWidth), 0, 0, INTER_NEAREST);
for (int h = 0; h < inHeight; h++)
{
for (int w = 0; w < inWidth; w++)
{
for (int c = 0; c < 3; c++)
{
//在 RGB 模式下
imageInputs[i*inSize+h*inWidth*3+w*3+2-c] = (int8_t)( (image2.at<Vec3b>(h, w)[c]/255.0f)*input_scale );
}
}
}
请注意,DPU API 应用 OpenCV 函数来读取图像文件(无论是png
还是jpg
还是任何格式),因此图像被视为 BGR 而不是本机 RGB。本教程中完成的所有训练和推理步骤都将图像视为 RGB,这对于上述 C++ 归一化例程也是如此。
5.5.2 运行时执行
可以直接在目标上编译应用程序(除了将其编译到主机计算机环境中之外),这是可能的并且很简单。
事实上,这就是脚本 run_all_vcor_target.sh 在目标上启动时所做的。
打开您的目标板并与来自 Ubuntu 的 putty
终端或来自您的 Windows 主机 PC 的 TeraTerm
终端建立串行通信。
确保您具有以太网点对点电缆连接,并具有正确的 IP 地址,以启用 ssh
通信,以便使用来自 Ubuntu 的 scp
或来自 Windows 主机 PC 的 pscp.exe
快速将文件传输到目标板。例如,您可以将目标板的 IP 地址设置为 192.168.1.217
,而主机 PC 为 192.168.1.140
。
创建 build/target_vek280
(或 build/target_vck190
等)文件夹的 tar
文件后,将其从主机 PC 复制到目标板。例如,在 Ubuntu PC 的情况下,使用以下命令:
scp target_vek280.tar root@192.168.1.217:~/
从目标板终端,运行以下命令(在 VEK280 的情况下):
tar -xvf target_vek280.tar
cd target_vek280
bash -x ./run_all_target.sh vek280
基于 VART C++ API 的应用程序使用 build_app.sh 脚本构建,最后针对每个 CNN 启动,通过 Python 脚本 check_runtime_top5_vcor.py 检查有效的 top-5 分类准确性,该脚本是从
vcor_performance.sh 脚本中启动的。
请注意,测试图像已使用 generate_target_test_images.py 脚本正确准备
以便将类名附加到图像文件名,从而启用使用 check_runtime_top5_vcor.py
检查预测准确性。
5.5.3 DPU 性能
在 VEK280 板上,以 fps 为单位测量的纯 DPU 性能(不包括 CPU 任务)为:
-
使用 1 个线程时为 ~4804 fps,
-
使用 3 个线程时为 ~10099 fps。
预测准确性为:
...
预测的总图像数 300
top1 错误预测数 41
top1 正确预测数 259
top5 错误预测数 4
top5 正确预测数 296
top1 准确性 = 0.86
top5 准确性 = 0.99
...