Raspberry Pi 4 上的实时推理 (30 fps!¶
创建时间: Feb 08, 2022 |上次更新时间:2024 年 1 月 16 日 |上次验证: Nov 05, 2024
作者: Tristan Rice
PyTorch 对 Raspberry Pi 4 提供开箱即用的支持。本教程将指导 您将了解如何设置 Raspberry Pi 4 以运行 PyTorch 并运行 MobileNet v2 在 CPU 上实时 (30 fps+) 分类模型。
这一切都是使用 Raspberry Pi 4 Model B 4GB 测试的,但应该适用于 2GB 变体以及性能降低的 3B 上。
![https://user-images.githubusercontent.com/909104/153093710-bc736b6f-69d9-4a50-a3e8-9f2b2c9e04fd.gif](https://user-images.githubusercontent.com/909104/153093710-bc736b6f-69d9-4a50-a3e8-9f2b2c9e04fd.gif)
先决条件¶
要学习本教程,您需要一个 Raspberry Pi 4、一个摄像头等等 其他标准附件。
散热器和风扇(可选但推荐)
5V 3A USB-C 电源
SD 卡(至少 8GB)
SD 卡读/写器
Raspberry Pi 4 设置¶
PyTorch 仅提供 Arm 64 位 (aarch64) 的 pip 包,因此您需要在 Raspberry Pi 上安装 64 位版本的操作系统
您可以从 https://downloads.raspberrypi.org/raspios_arm64/images/ 下载最新的 arm64 Raspberry Pi 操作系统,并通过 rpi-imager 进行安装。
32 位 Raspberry Pi OS 将不起作用。
![https://user-images.githubusercontent.com/909104/152866212-36ce29b1-aba6-4924-8ae6-0a283f1fca14.gif](https://user-images.githubusercontent.com/909104/152866212-36ce29b1-aba6-4924-8ae6-0a283f1fca14.gif)
安装至少需要几分钟,具体取决于您的互联网速度和 SD 卡速度。完成后,它应该看起来像:
![https://user-images.githubusercontent.com/909104/152867425-c005cff0-5f3f-47f1-922d-e0bbb541cd25.png](https://user-images.githubusercontent.com/909104/152867425-c005cff0-5f3f-47f1-922d-e0bbb541cd25.png)
是时候将 SD 卡放入 Raspberry Pi 中,连接相机并启动它了。
![https://user-images.githubusercontent.com/909104/152869862-c239c980-b089-4bd5-84eb-0a1e5cf22df2.png](https://user-images.githubusercontent.com/909104/152869862-c239c980-b089-4bd5-84eb-0a1e5cf22df2.png)
启动并完成初始设置后,您需要编辑文件以启用相机。/boot/config.txt
# This enables the extended features such as the camera.
start_x=1
# This needs to be at least 128M for the camera processing, if it's bigger you can just leave it as is.
gpu_mem=128
# You need to commment/remove the existing camera_auto_detect line since this causes issues with OpenCV/V4L2 capture.
#camera_auto_detect=1
然后重新启动。重新启动后,video4linux2 设备应该存在。/dev/video0
安装 PyTorch 和 OpenCV¶
PyTorch 和我们需要的所有其他库都有 ARM 64 位/aarch64 变体,因此您只需通过 pip 安装它们,并让它像任何其他 Linux 系统一样工作。
$ pip install torch torchvision torchaudio
$ pip install opencv-python
$ pip install numpy --upgrade
![https://user-images.githubusercontent.com/909104/152874260-95a7a8bd-0f9b-438a-9c0b-5b67729e233f.png](https://user-images.githubusercontent.com/909104/152874260-95a7a8bd-0f9b-438a-9c0b-5b67729e233f.png)
我们现在可以检查所有内容是否都已正确安装:
$ python -c "import torch; print(torch.__version__)"
![https://user-images.githubusercontent.com/909104/152874271-d7057c2d-80fd-4761-aed4-df6c8b7aa99f.png](https://user-images.githubusercontent.com/909104/152874271-d7057c2d-80fd-4761-aed4-df6c8b7aa99f.png)
视频捕获¶
对于视频捕获,我们将使用 OpenCV 来流式传输视频帧
而不是更常见的 .picamera 在 64 位上不可用
Raspberry Pi 操作系统,它比 OpenCV 慢得多。OpenCV 直接访问设备以抓取帧。picamera
/dev/video0
我们使用的模型 (MobileNetV2) 接受的图像大小,因此我们
可以直接从 OpenCV 以 36fps 请求。我们的目标是 30fps
model 的 Alpha 模型,但我们请求的帧率略高于此帧率,因此始终存在
足够的帧数。224x224
import cv2
from PIL import Image
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 224)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 224)
cap.set(cv2.CAP_PROP_FPS, 36)
OpenCV 在 BGR 中返回一个数组,因此我们需要读取并执行一些
随机播放以使其转换为预期的 RGB 格式。numpy
ret, image = cap.read()
# convert opencv output from BGR to RGB
image = image[:, :, [2, 1, 0]]
此数据读取和处理大约需要 。3.5 ms
图像预处理¶
我们需要获取帧并将它们转换为模型期望的格式。这与您在任何具有标准 torchvision 转换的机器上所做的处理相同。
from torchvision import transforms
preprocess = transforms.Compose([
# convert the frame to a CHW torch tensor for training
transforms.ToTensor(),
# normalize the colors to the range that mobilenet_v2/3 expect
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
input_tensor = preprocess(image)
# The model can handle multiple images simultaneously so we need to add an
# empty dimension for the batch.
# [3, 224, 224] -> [1, 3, 224, 224]
input_batch = input_tensor.unsqueeze(0)
型号选择¶
您可以选择多种型号来使用不同的性能
特性。并非所有模型都提供预训练变体,因此对于
测试目的,您应该选择一个可以的,但如果您训练和量化
您自己的模型,您可以使用其中任何一个。qnnpack
我们用于本教程,因为它具有良好的性能和
准确性。mobilenet_v2
Raspberry Pi 4 基准测试结果:
型 |
FPS |
总时间(毫秒/帧) |
模型时间 (ms/frame) |
qnnpack 预训练 |
---|---|---|---|---|
mobilenet_v2 |
33.7 |
29.7 |
26.4 |
真 |
mobilenet_v3_large |
29.3 |
34.1 |
30.7 |
真 |
resnet18 |
9.2 |
109.0 |
100.3 |
假 |
resnet50 |
4.3 |
233.9 |
225.2 |
假 |
resnext101_32x8d |
1.1 |
892.5 |
885.3 |
假 |
inception_v3 |
4.9 |
204.1 |
195.5 |
假 |
谷歌网 |
7.4 |
135.3 |
132.0 |
假 |
shufflenet_v2_x0_5 |
46.7 |
21.4 |
18.2 |
假 |
shufflenet_v2_x1_0 |
24.4 |
41.0 |
37.7 |
假 |
shufflenet_v2_x1_5 |
16.8 |
59.6 |
56.3 |
假 |
shufflenet_v2_x2_0 |
11.6 |
86.3 |
82.7 |
假 |
MobileNetV2:量化和 JIT¶
为了获得最佳性能,我们需要一个量化和融合的模型。量化
意味着它使用 int8 进行计算,这比
标准的 float32 数学。Fused 表示已连续操作
在可能的情况下融合在一起,形成性能更高的版本。常见事物
Like activations () 可以合并到 () 之前的图层中
在推理期间。ReLU
Conv2d
pytorch 的 aarch64 版本需要使用该引擎。qnnpack
import torch
torch.backends.quantized.engine = 'qnnpack'
在此示例中,我们将使用 torchvision 提供的开箱即用的 MobileNetV2 的预量化和融合版本。
from torchvision import models
net = models.quantization.mobilenet_v2(pretrained=True, quantize=True)
然后,我们希望对模型进行 jit 以减少 Python 开销并融合任何运算。Jit 给了我们 ~30fps 而不是没有它的 ~20fps。
net = torch.jit.script(net)
把它放在一起¶
我们现在可以将所有部分放在一起并运行它:
import time
import torch
import numpy as np
from torchvision import models, transforms
import cv2
from PIL import Image
torch.backends.quantized.engine = 'qnnpack'
cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 224)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 224)
cap.set(cv2.CAP_PROP_FPS, 36)
preprocess = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
net = models.quantization.mobilenet_v2(pretrained=True, quantize=True)
# jit model to take it from ~20fps to ~30fps
net = torch.jit.script(net)
started = time.time()
last_logged = time.time()
frame_count = 0
with torch.no_grad():
while True:
# read frame
ret, image = cap.read()
if not ret:
raise RuntimeError("failed to read frame")
# convert opencv output from BGR to RGB
image = image[:, :, [2, 1, 0]]
permuted = image
# preprocess
input_tensor = preprocess(image)
# create a mini-batch as expected by the model
input_batch = input_tensor.unsqueeze(0)
# run model
output = net(input_batch)
# do something with output ...
# log model performance
frame_count += 1
now = time.time()
if now - last_logged > 1:
print(f"{frame_count / (now-last_logged)} fps")
last_logged = now
frame_count = 0
运行它显示我们悬停在 ~30 fps。
![https://user-images.githubusercontent.com/909104/152892609-7d115705-3ec9-4f8d-beed-a51711503a32.png](https://user-images.githubusercontent.com/909104/152892609-7d115705-3ec9-4f8d-beed-a51711503a32.png)
这是 Raspberry Pi OS 中的所有默认设置。如果您禁用了 UI 而默认情况下启用的所有其他后台服务则更多 高性能且稳定。
如果我们检查一下,我们会看到我们的利用率几乎是 100%。htop
![https://user-images.githubusercontent.com/909104/152892630-f094b84b-19ba-48f6-8632-1b954abc59c7.png](https://user-images.githubusercontent.com/909104/152892630-f094b84b-19ba-48f6-8632-1b954abc59c7.png)
为了验证它是否端到端工作,我们可以计算 类,并使用 ImageNet 类标签打印检测结果。
top = list(enumerate(output[0].softmax(dim=0)))
top.sort(key=lambda x: x[1], reverse=True)
for idx, val in top[:10]:
print(f"{val.item()*100:.2f}% {classes[idx]}")
mobilenet_v3_large
实时运行:
![https://user-images.githubusercontent.com/909104/153093710-bc736b6f-69d9-4a50-a3e8-9f2b2c9e04fd.gif](https://user-images.githubusercontent.com/909104/153093710-bc736b6f-69d9-4a50-a3e8-9f2b2c9e04fd.gif)
检测到橙子:
![https://user-images.githubusercontent.com/909104/153092153-d9c08dfe-105b-408a-8e1e-295da8a78c19.jpg](https://user-images.githubusercontent.com/909104/153092153-d9c08dfe-105b-408a-8e1e-295da8a78c19.jpg)
检测马克杯:
![https://user-images.githubusercontent.com/909104/153092155-4b90002f-a0f3-4267-8d70-e713e7b4d5a0.jpg](https://user-images.githubusercontent.com/909104/153092155-4b90002f-a0f3-4267-8d70-e713e7b4d5a0.jpg)
疑难解答:性能¶
默认情况下,PyTorch 将使用所有可用的内核。如果您有任何内容 在 Raspberry Pi 的后台运行可能会导致与 模型推理导致延迟峰值。为了缓解这种情况,您可以减少 线程数,这将在较小的性能下减少峰值延迟 罚款。
torch.set_num_threads(2)
for using instead of 将最佳情况延迟增加到 from,但消除了
延迟峰值 。shufflenet_v2_x1_5
2 threads
4 threads
72 ms
60 ms
128 ms
后续步骤¶
您可以创建自己的模型或微调现有模型。如果您微调 TorchVision.models.quantized 中的一个模型已经为您完成了大部分融合和量化的工作,因此您可以 直接在 Raspberry Pi 上部署,性能良好。
查看更多:
Quantization 有关如何量化和融合模型的更多信息。
Transfer Learning 教程,了解如何使用迁移学习将预先存在的模型微调到数据集。