在 Raspberry Pi 4 上進行即時推論 (30 fps!)¶
建立於:2022 年 2 月 08 日 | 最後更新:2024 年 1 月 16 日 | 最後驗證:2024 年 11 月 05 日
作者: Tristan Rice
PyTorch 提供了 Raspberry Pi 4 的開箱即用支援。 本教學將引導您如何在 Raspberry Pi 4 上設定 PyTorch,並在 CPU 上即時 (30 fps 以上) 執行 MobileNet v2 分類模型。
所有這些都使用 Raspberry Pi 4 Model B 4GB 進行測試,但也應該適用於 2GB 版本,以及在效能降低的情況下適用於 3B。

先決條件¶
要遵循本教學,您需要 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 OS,並透過 rpi-imager 安裝它。
32 位元 Raspberry Pi OS 無法運作。

安裝至少需要幾分鐘,具體取決於您的網際網路速度和 SD 卡速度。 完成後,它應該看起來像

是時候將您的 SD 卡放入 Raspberry Pi 中,連接相機並啟動它了。

啟動並完成初始設定後,您需要編輯 /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

我們現在可以檢查所有東西是否已正確安裝
$ python -c "import torch; print(torch.__version__)"

視訊擷取¶
對於視訊擷取,我們將使用 OpenCV 來串流視訊影格,而不是更常見的 picamera
。 picamera 在 64 位元 Raspberry Pi OS 上不可用,而且比 OpenCV 慢得多。 OpenCV 直接存取 /dev/video0
裝置以抓取影格。
我們使用的模型 (MobileNetV2) 採用 224x224
的影像尺寸,因此我們可以以 36fps 直接從 OpenCV 請求它。 我們針對模型的目標是 30fps,但我們請求比這略高的影格率,以便始終有足夠的影格。
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 傳回 numpy
陣列,因此我們需要讀取並進行一些調整才能將其轉換為預期的 RGB 格式。
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/影格) |
模型時間 (ms/影格) |
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 |
否 |
googlenet |
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 數學運算效能更高。 融合意味著連續的操作已融合在一起,成為更高效能的版本 (如果可能的話)。 通常,諸如激活函數 (ReLU
) 之類的東西可以在推論期間合併到之前的層 (Conv2d
) 中。
pytorch 的 aarch64 版本需要使用 qnnpack
引擎。
import torch
torch.backends.quantized.engine = 'qnnpack'
在這個範例中,我們會使用一個預量化且融合過的 MobileNetV2 版本,這個版本由 torchvision 直接提供。
from torchvision import models
net = models.quantization.mobilenet_v2(pretrained=True, quantize=True)
接著,我們會使用 JIT 編譯模型,以減少 Python 的額外負擔並融合所有運算。JIT 編譯後的模型可以達到約 30fps,而沒有 JIT 編譯則只有約 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。

這是使用 Raspberry Pi OS 中所有預設設定的結果。如果您停用了 UI 和所有預設啟用的其他背景服務,效能會更高且更穩定。
如果我們檢查 htop
,我們會看到 CPU 使用率幾乎達到 100%。

為了驗證整個流程是否正常運作,我們可以計算各個類別的機率,並使用 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
正在即時運行。

偵測到一個橘子。

偵測到一個馬克杯。

疑難排解:效能¶
預設情況下,PyTorch 會使用所有可用的核心。 如果 Raspberry Pi 上有任何背景程式在運行,可能會導致模型推論發生衝突,進而導致延遲峰值。 為了減輕這種情況,您可以減少執行緒的數量,這會以較小的效能損失為代價,降低峰值延遲。
torch.set_num_threads(2)
對於 shufflenet_v2_x1_5
,使用 2 threads
而不是 4 threads
會將最佳情況下的延遲從 60 ms
增加到 72 ms
,但消除了 128 ms
的延遲峰值。
下一步¶
您可以建立自己的模型或微調現有模型。 如果您在 torchvision.models.quantized 中的模型上進行微調,大部分融合和量化的工作已經為您完成,因此您可以直接部署並在 Raspberry Pi 上獲得良好的效能。
參閱更多