目錄
1. 簡介
2. 代碼解析
3. 全部代碼展示
4. 總結
1. 簡介
本文以?Resnet50 為例,展示使用 PYNQ 調用 DPU 運行 Resnet50 網絡的詳細過程,并對其中關鍵代碼做出解釋。
PYNQ是一個針對Xilinx Zynq平臺的Python開發框架,它允許開發者使用Python語言和庫來利用Zynq的高效計算資源,使用 PYNQ 可以非常方便地處理各種與 Zynq 相關的計算任務,包括調用 DPU 進行推理。
Resnet50
一種深度卷積神經網絡(CNN),它由50層構成。這種網絡特別設計用于圖像識別任務,并且在2015年的ImageNet大規模視覺識別挑戰賽(ILSVRC)中取得了勝利。Resnet50的“殘差學習”能力使得它可以通過添加更多的層來提高準確性,而不會導致訓練難度增加或準確性下降。
這個網絡的核心是“殘差塊”,它允許數據在網絡的多個層之間直接傳遞,從而解決了深度網絡訓練中的“退化問題”。這種設計使得即使是非常深的網絡也能有效地訓練,并且隨著網絡深度的增加,性能也能得到提升。
2. 代碼解析
- 硬件和模型加載
overlay = DpuOverlay("dpu.bit")
overlay.load_model("dpu_resnet50.xmodel")
首先,加載一個名為 dpu.bit 的 FPGA 比特流到 Zynq 設備上。DpuOverlay 是 PYNQ 庫中用于管理 FPGA 上的疊加層(overlay)的一個類。?
然后加載一個名為 dpu_resnet50.xmodel 的深度學習模型到已經配置好的 DPU 上。load_model 是 DpuOverlay 類的一個方法,它用于加載編譯后的深度學習模型文件。這里的 dpu_resnet50.xmodel 是一個已經被轉換和優化以適應 DPU執行的深度學習模型文件。
- runner類,來自VART的方法
dpu = overlay.runner # runner類,來自VART的方法
inputTensors = dpu.get_input_tensors() # 返回是單個元素的列表
outputTensors = dpu.get_output_tensors() # 即[xir.Tensor]
VART(Vitis AI Runtime)是Xilinx提供的一套運行時庫,用于在Xilinx平臺上執行深度學習模型推理。
dpu = overlay.runner,這行代碼通過訪問overlay對象的runner屬性,獲取了一個VART運行時的實例。
- 獲取dimensions?
# 元組tuple,類似于列表list,但不可更改;dims -> dimensions
shapeIn = tuple(inputTensors[0].dims) # 元組(1, 224, 224, 3)
shapeOut = tuple(outputTensors[0].dims) # (1, 1, 1, 1000)
- 計算輸出數據大小
# get_data_size()方法返回輸出張量的總大小,除以輸入張量的第一維大小(即batch size),可以得到單個輸出張量的大小。outputSize為1000
outputSize = int(outputTensors[0].get_data_size() / shapeIn[0])
- ?構建一維陣列,dtype=f64
softmax = np.empty(outputSize)
- 形狀shape創建內存數據陣列;order="C"行優先存儲,"F"列優先存儲?
output_data = [np.empty(shapeOut, dtype=np.float32, order="C")]
input_data = [np.empty(shapeIn, dtype=np.float32, order="C")]
- 為 input_data 中第一個元素設置別名 image
image = input_data[0]
- 圖像預處理?
preprocessed = preprocess_fn(cv2.imread(os.path.join(image_folder, original_images[image_index])))
- 格式轉換,切片操作
image[0,...] = preprocessed.reshape(shapeIn[1:])
- 執行異步推理作業,并等待結果返回
job_id = dpu.execute_async(input_data, output_data)
dpu.wait(job_id)
job_id = dpu.execute_async(input_data, output_data),這行代碼調用execute_async方法來異步啟動一個深度學習模型的推理任務。這個方法接收兩個參數:input_data和output_data,分別代表模型的輸入數據和用于接收模型輸出結果的容器。input_data應該與模型的輸入張量格式匹配,而output_data則應該是足夠大以容納預期的輸出結果的容器。
execute_async方法立即返回一個job_id,這是一個標識符,用于追蹤異步執行的推理任務。此時,推理任務已經在DPU上啟動,但該方法不會阻塞調用線程等待任務完成。這允許CPU繼續執行其他任務,而不必等待DPU完成推理。
dpu.wait(job_id),這行代碼調用wait方法,并傳入之前execute_async方法返回的job_id,以等待對應的推理任務完成。如果推理任務已經完成,wait方法將立即返回;如果推理任務尚未完成,wait方法將阻塞調用線程,直到任務完成。這確保了在繼續進行任何依賴于推理結果的操作之前,推理任務已經成功完成。
- ?轉化為一維向量
# 轉化為一維向量,放入temp列表中,此時temp形狀為(1,1,1000)
temp = [j.reshape(1, outputSize) for j in output_data]
- 計算每個元素的指數
softmax = calculate_softmax(temp[0][0])
- 計算最大值所在的index標簽?
print("Classification: {}".format(predict_label(softmax)))
- 顯示圖像?
if display:display_image = cv2.imread(os.path.join(image_folder, original_images[image_index]))_, ax = plt.subplots(1)_ = ax.imshow(cv2.cvtColor(display_image, cv2.COLOR_BGR2RGB))
_ = ax.imshow(cv2.cvtColor(display_image, cv2.COLOR_BGR2RGB))
# 短橫線"_"用作一個變量名,臨時變量,一種書寫習慣
3. 全部代碼展示
以下代碼演示了使用PYNQ和DPU進行深度學習推理的全部過程,從圖像預處理、數據加載、模型推理到結果展示,為圖像分類任務提供了一個完整的流程:
import os
import time
import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inlinefrom pynq_dpu import DpuOverlay
overlay = DpuOverlay("dpu.bit")overlay.load_model("dpu_resnet50.xmodel")_R_MEAN = 123.68
_G_MEAN = 116.78
_B_MEAN = 103.94MEANS = [_B_MEAN,_G_MEAN,_R_MEAN]def resize_shortest_edge(image, size):H, W = image.shape[:2]if H >= W:nW = sizenH = int(float(H)/W * size)else:nH = sizenW = int(float(W)/H * size)return cv2.resize(image,(nW,nH))def mean_image_subtraction(image, means):B, G, R = cv2.split(image)B = B - means[0]G = G - means[1]R = R - means[2]image = cv2.merge([R, G, B])return imagedef BGR2RGB(image):B, G, R = cv2.split(image)image = cv2.merge([R, G, B])return imagedef central_crop(image, crop_height, crop_width):image_height = image.shape[0]image_width = image.shape[1]offset_height = (image_height - crop_height) // 2offset_width = (image_width - crop_width) // 2return image[offset_height:offset_height + crop_height, offset_width:offset_width + crop_width, :]def normalize(image):image=image/256.0image=image-0.5image=image*2return imagedef preprocess_fn(image, crop_height = 224, crop_width = 224):image = resize_shortest_edge(image, 256)image = mean_image_subtraction(image, MEANS)image = central_crop(image, crop_height, crop_width)return imagedef calculate_softmax(data):result = np.exp(data)return resultdef predict_label(softmax):with open("img/words.txt", "r") as f:lines = f.readlines()return lines[np.argmax(softmax)-1]image_folder = 'img'
original_images = [i for i in os.listdir(image_folder) if i.endswith("JPEG")]
total_images = len(original_images)dpu = overlay.runnerinputTensors = dpu.get_input_tensors()
outputTensors = dpu.get_output_tensors()shapeIn = tuple(inputTensors[0].dims)
shapeOut = tuple(outputTensors[0].dims)
outputSize = int(outputTensors[0].get_data_size() / shapeIn[0])softmax = np.empty(outputSize)output_data = [np.empty(shapeOut, dtype=np.float32, order="C")]
input_data = [np.empty(shapeIn, dtype=np.float32, order="C")]
image = input_data[0]def run(image_index, display=False):preprocessed = preprocess_fn(cv2.imread(os.path.join(image_folder, original_images[image_index])))image[0,...] = preprocessed.reshape(shapeIn[1:])job_id = dpu.execute_async(input_data, output_data)dpu.wait(job_id)temp = [j.reshape(1, outputSize) for j in output_data]softmax = calculate_softmax(temp[0][0])if display:display_image = cv2.imread(os.path.join(image_folder, original_images[image_index]))_, ax = plt.subplots(1)_ = ax.imshow(cv2.cvtColor(display_image, cv2.COLOR_BGR2RGB))print("Classification: {}".format(predict_label(softmax)))run(1, display=True)
代碼的主要步驟如下:
- 環境配置與模型加載:首先,導入所需的Python庫,包括os、time、numpy、cv2(OpenCV庫)、matplotlib(用于圖像顯示)等,并加載DPU疊加層和預訓練的深度學習模型(dpu_resnet50.xmodel)。
- 圖像預處理:定義了幾個預處理函數來準備圖像數據以供模型使用。這些函數包括:
- resize_shortest_edge:調整圖像大小,使得其最短邊為指定的尺寸,同時保持原始的寬高比。
- mean_image_subtraction:執行均值減法,用于圖像歸一化,減去圖像中每個通道的平均值。
- BGR2RGB:將圖像從BGR格式轉換為RGB格式,因為OpenCV默認讀入圖像為BGR格式,而大多數模型使用RGB。
- central_crop:從圖像中心裁剪指定大小的區域。
- normalize:將圖像數據歸一化到[-1, 1]的范圍內。
- preprocess_fn:將上述預處理步驟組合起來,為模型準備圖像數據。
- 模型預測:圖像預處理后,使用DPU執行預測。首先,讀取輸入和輸出張量的形狀,準備好輸入數據的容器。然后,對指定的圖像進行預處理并將其加載到輸入數據容器中。通過DPU執行異步推理,并等待結果。使用calculate_softmax函數計算輸出數據的softmax,以獲得每個類別的預測概率。
- 結果展示:定義predict_label函數,它根據softmax預測結果,從一個包含類別標簽的文件中選擇并返回最可能的類別標簽。如果display參數設為True,該函數還會顯示原圖像及其預測類別。
- 執行預測:最后,選擇一個圖像文件并調用run函數來執行上述預測流程,并可選擇是否顯示圖像及其分類標簽。
4. 總結
在這個總結中,我們探討了Resnet50,這是一個由50層構成的深度卷積神經網絡,它在圖像識別任務中表現出色。通過“殘差學習”的創新設計,Resnet50解決了深度網絡訓練中的退化問題,使得網絡能夠通過增加更多的層來提高性能,而不會增加訓練難度。我們還分析了如何在Xilinx Zynq平臺上使用VART運行Resnet50模型的代碼,包括模型加載、數據預處理、異步推理和結果分類。這個過程展示了如何利用Zynq芯片的強大功能,將深度學習應用于邊緣計算,為各種行業,特別是高級駕駛輔助系統(ADAS)等應用,提供了新的可能性。這個例子不僅展示了深度學習在實際應用中的潛力,也突顯了Zynq芯片在處理復雜計算任務時的高效性和靈活性。