本文首先將會(huì)對(duì)Vitis統(tǒng)一軟件平臺(tái)和Vitsi AI進(jìn)行簡(jiǎn)單介紹,然后介紹如何在KV260上部署DPU鏡像,最后在KV260 DPU鏡像上運(yùn)行Vitis AI自帶的圖像分類示例。通過(guò)本文,你將會(huì)對(duì)Vitis軟件平臺(tái)、Vitsi AI架構(gòu)有初步認(rèn)識(shí),并知道如何在KV260上快速體驗(yàn)Vitsi AI圖像分類示例程序。
01一、背景簡(jiǎn)介
開(kāi)始本文的實(shí)操環(huán)節(jié)之前,這里我先介紹一些背景知識(shí),分別是Vitis統(tǒng)一軟件平臺(tái)和Vitis AI。
1.1 Vitis 統(tǒng)一軟件平臺(tái)簡(jiǎn)介
來(lái)自Xilinx官網(wǎng)的簡(jiǎn)介,Vitis 統(tǒng)一軟件平臺(tái)包括:
全面的內(nèi)核開(kāi)發(fā)套件,可無(wú)縫構(gòu)建加速應(yīng)用
完整的硬件加速開(kāi)源庫(kù),針對(duì) AMD FPGA 和 Versal 自適應(yīng) SoC 硬件平臺(tái)進(jìn)行了優(yōu)化
插入特定領(lǐng)域的開(kāi)發(fā)環(huán)境,可直接在熟悉的更高層次框架中進(jìn)行開(kāi)發(fā)
不斷發(fā)展的硬件加速合作伙伴庫(kù)和預(yù)建應(yīng)用生態(tài)系統(tǒng)
Vitis Model Composer 是一款基于模型的設(shè)計(jì)工具,不僅可在 MathWorks MATLAB 和 Simulink 環(huán)境中實(shí)現(xiàn)快速設(shè)計(jì)探索與驗(yàn)證 ,而且還可加速 AMD 器件的生產(chǎn)進(jìn)程。
Vitis Networking P4 允許創(chuàng)建軟定義網(wǎng)絡(luò)。VitisNetP4 數(shù)據(jù)平面構(gòu)建器生成的系統(tǒng)可以針對(duì)從簡(jiǎn)單的數(shù)據(jù)包分類到復(fù)雜的數(shù)據(jù)包編輯的各種數(shù)據(jù)包處理功能進(jìn)行編程。
來(lái)自官網(wǎng)的Vitis統(tǒng)一軟件平臺(tái)架構(gòu)圖:

從官網(wǎng)的介紹頁(yè)面我們也可以看到,Vitis 統(tǒng)一軟件平臺(tái)包括如下組件:
Vitis AI
Vitis 視頻分析SDK
Vitis 庫(kù)
Vitis HLS
Vitis Model Composer
今天我們將會(huì)重點(diǎn)介紹Vitis AI。
1.2 Vitsi AI簡(jiǎn)介
本節(jié)內(nèi)容主要參考了自官方github.io文檔。
AMD Vitis AI 是一個(gè)集成開(kāi)發(fā)環(huán)境,可用于加速 AMD 平臺(tái)上的 AI 推理。該工具鏈提供優(yōu)化的IP、工具、庫(kù)、模型以及資源,例如示例設(shè)計(jì)和教程,可在整個(gè)開(kāi)發(fā)過(guò)程中為用戶提供幫助。它在設(shè)計(jì)時(shí)充分考慮了高效率和易用性,在 AMD 自適應(yīng) SoC 和 Alveo 數(shù)據(jù)中心加速卡上釋放了 AI 加速的全部潛力。
Vitis AI 解決方案由三個(gè)主要組件組成:
深度學(xué)習(xí)處理器單元 (DPU),用于優(yōu)化 ML 模型推理的硬件引擎。
模型開(kāi)發(fā)工具,用于為 DPU 編譯和優(yōu)化 ML 模型。
模型部署庫(kù)和 API,用于從軟件應(yīng)用程序在 DPU 引擎上集成和執(zhí)行 ML 模型。
Vitis AI 解決方案的打包和交付方式如下:
AMD 開(kāi)放下載:集成 DPU 的預(yù)構(gòu)建目標(biāo)映像(以下簡(jiǎn)稱“DPU鏡像”)
Vitis AI Docker容器:模型開(kāi)發(fā)工具
Vitis AI github 存儲(chǔ)庫(kù):模型部署庫(kù)、設(shè)置腳本、示例和參考設(shè)計(jì)
02二、部署DPU鏡像到KV260
2.1 下載DPU鏡像
在KV260開(kāi)發(fā)板上正式體驗(yàn)Vitis AI之前,需要將上一節(jié)中提到的DPU鏡像下載下來(lái)并燒錄到SD上。
支持KV260的最新DPU鏡像下載鏈接:https://china.xilinx.com/member/forms/download/design-license-xef.html?filename=xilinx-kv260-dpu-v2022.2-v3.0.0.img.gz
2.2 寫(xiě)入DPU鏡像到SD卡
下載完成后,解壓壓縮包,通過(guò)Rufus將解壓的wic文件寫(xiě)入SD卡。使用Rufus選擇文件時(shí),需要注意將右側(cè)的默認(rèn)文件類型修改為全部文件,否則默認(rèn)不支持wic文件:

寫(xiě)入過(guò)程顯示進(jìn)度:

2.3 啟動(dòng)DPU鏡像系統(tǒng)
完成DPU鏡像寫(xiě)入SD卡后,將SD卡讀卡器從PC移除后,將SD卡插入到開(kāi)發(fā)板,插好串口線,打開(kāi)串口終端,波特率設(shè)置為115200,就可以準(zhǔn)備上電開(kāi)機(jī)了。
啟動(dòng)之后會(huì)自動(dòng)登錄root賬號(hào)(默認(rèn)密碼為root):

插上網(wǎng)線的話,啟動(dòng)后還可以看到輸出了dashborad訪問(wèn)鏈接:

根據(jù)ifconfig查看的IP地址,瀏覽器訪問(wèn)dashboard鏈接,可以看到實(shí)時(shí)狀態(tài)監(jiān)控:

03三、運(yùn)行Vitsi AI圖像分類示例
3.1 DPU鏡像自帶的一些文件介紹
DPU鏡像默認(rèn)帶有SSH服務(wù),并且是開(kāi)機(jī)啟動(dòng)的,因此可以使用MobaXterm的SSH客戶端通過(guò)網(wǎng)絡(luò)登錄KV260,如下圖所示:

MobaXterm的SSH客戶端時(shí)帶有X11-forwarding功能的,支持將遠(yuǎn)程程序界面通過(guò)SSH協(xié)議顯示在本地。
登錄系統(tǒng)后,可以看到,/home/root目錄下已經(jīng)有了兩個(gè)目錄。
使用tree命令,可以看到Vitis-AI目錄結(jié)構(gòu):

接下來(lái)我們將會(huì)嘗試運(yùn)行vai_runtime下的resnet50示例程序,我們先看看這個(gè)目錄下的文件結(jié)構(gòu):

里面有文件的作用分別為:
build.sh,編譯腳本,里面包含編譯src/main.cc的命令
resnet50,已經(jīng)編譯好的可執(zhí)行程序,由src/main.cc編譯生成
readme,說(shuō)明文件
words.txt,分類標(biāo)簽
src/main.cc,示例程序源碼
DPU鏡像默認(rèn)已經(jīng)安裝了gcc,直接運(yùn)行build.sh就可以編譯src/main.cc,并生成resnet50可執(zhí)行文件。可以嘗試將resnet50可執(zhí)行文件刪除掉,再運(yùn)行build.sh腳本,觀察是否重新生成了resnet50。

3.2 下載resnet50測(cè)試圖片
通過(guò)以下命令,下載并解壓resnet50測(cè)試圖片:
cd ~ wget https://china.xilinx.com/bin/public/openDownload?filename=vitis_ai_runtime_r3.0.0_image_video.tar.gz -O vitis_ai_runtime_r3.0.0_image_video.tar.gz mkdir vitis_ai_runtime_r3.0.0_image_video tar -C vitis_ai_runtime_r3.0.0_image_video -xzvf vitis_ai_runtime_r3.0.0_image_video.tar.gz
(左右移動(dòng)查看全部?jī)?nèi)容)
3.3 運(yùn)行resnet50示例程序
接下來(lái),通過(guò)如下命令,運(yùn)行resnet50示例程序:
cd ~/Vitis-AI/examples/vai_runtime/resnet50 ./resnet50 /usr/share/vitis_ai_library/models/resnet50/resnet50.xmodel
(左右移動(dòng)查看全部?jī)?nèi)容)
運(yùn)行結(jié)果如下:

報(bào)錯(cuò)說(shuō)../images目錄找不到。
創(chuàng)建../images目錄,并將剛剛下載的resnet50測(cè)試圖片拷貝到該目錄中:
mkdir -v ../images cp -vr ~/vitis_ai_runtime_r3.0.0_image_video/images/* ../images/
(左右移動(dòng)查看全部?jī)?nèi)容)
運(yùn)行輸出如下:

再次運(yùn)行resnet50示例程序:

成功識(shí)別了。
命令行第二個(gè)參數(shù) /usr/share/vitis_ai_library/models/resnet50/resnet50.xmodel 是resnet50的DPU模型文件,該文件在DPU鏡像中已經(jīng)有了,因此不需要手動(dòng)下載。
3.4 使用金魚(yú)圖片進(jìn)行測(cè)試
words.txt 文件中是resnet50識(shí)別結(jié)果的分類標(biāo)簽,可以看到前面幾行中包含金魚(yú)(goldfish)分類:

因此,可以找一個(gè)金魚(yú)圖片進(jìn)行測(cè)試。
隨便找了一張:
通過(guò)MobaXterm左側(cè)邊欄的上傳功能傳到開(kāi)發(fā)板上:

默認(rèn)上傳位置為HOME目錄(~)。
接下來(lái),將金魚(yú)圖片拷貝到../images目錄,并將原來(lái)的測(cè)試圖片刪除掉,重新運(yùn)行resnet50示例程序,可以看到成功識(shí)別了金魚(yú):

04四、示例程序源碼解讀
接下來(lái)我們看看resnet50目錄下的src/main.cc文件內(nèi)容。
4.2 main函數(shù)
首先是main函數(shù):
/** * [url=home.php?mod=space&uid=2666770]@Brief[/url] Entry for runing ResNet50 neural network * * [url=home.php?mod=space&uid=1902110]@NOTE[/url] Runner APIs prefixed with "dpu" are used to easily program & * deploy ResNet50 on DPU platform. * */ int main(int argc, char* argv[]) { // Check args if (argc != 2) { cout << "Usage of resnet50 demo: ./resnet50 [model_file]" << endl; ? ?return -1; ?} ?auto graph = xir::deserialize(argv[1]); ?auto subgraph = get_dpu_subgraph(graph.get()); ?CHECK_EQ(subgraph.size(), 1u) ? ? ?<< "resnet50 should have one and only one dpu subgraph."; ?LOG(INFO) << "create running for subgraph: " << subgraph[0]->get_name(); /*create runner*/ auto runner = vart::create_runner(subgraph[0], "run"); // ai::XdpuRunner* runner = new ai::XdpuRunner("./"); /*get in/out tensor*/ auto inputTensors = runner->get_input_tensors(); auto outputTensors = runner->get_output_tensors(); /*get in/out tensor shape*/ int inputCnt = inputTensors.size(); int outputCnt = outputTensors.size(); TensorShape inshapes[inputCnt]; TensorShape outshapes[outputCnt]; shapes.inTensorList = inshapes; shapes.outTensorList = outshapes; getTensorShape(runner.get(), &shapes, inputCnt, outputCnt); /*run with batch*/ runResnet50(runner.get()); return 0; }
(左右移動(dòng)查看全部?jī)?nèi)容)
其中:
auto graph = xir::deserialize(argv[1]); 用于加載模型
auto runner = vart::create_runner(subgraph[0], "run"); 用于創(chuàng)建Runner對(duì)象
auto inputTensors = runner->get_input_tensors(); 用于獲取輸入Tensor對(duì)象
auto outputTensors = runner->get_output_tensors(); 用于獲取輸出Tensor對(duì)象
最后的 runResnet50(runner.get()); 運(yùn)行模型
4.2 runRestnet50函數(shù)
接下來(lái)我們看看runReset50函數(shù):
/**
* @brief Run DPU Task for ResNet50
*
* [url=home.php?mod=space&uid=3142012]@param[/url] taskResnet50 - pointer to ResNet50 Task
*
* [url=home.php?mod=space&uid=1141835]@Return[/url] none
*/
void runResnet50(vart::Runner* runner) {
/* Mean value for ResNet50 specified in Caffe prototxt */
vector kinds, images;
/* Load all image names.*/
ListImages(baseImagePath, images);
if (images.size() == 0) {
cerr << "
Error: No images existing under " << baseImagePath << endl;
? ?return;
?}
?/* Load all kinds words.*/
?LoadWords(wordsPath + "words.txt", kinds);
?if (kinds.size() == 0) {
? ?cerr << "
Error: No words exist in file words.txt." << endl;
? ?return;
?}
?float mean[3] = {104, 107, 123};
?/* get in/out tensors and dims*/
?auto outputTensors = runner->get_output_tensors();
auto inputTensors = runner->get_input_tensors();
auto out_dims = outputTensors[0]->get_shape();
auto in_dims = inputTensors[0]->get_shape();
auto input_scale = get_input_scale(inputTensors[0]);
auto output_scale = get_output_scale(outputTensors[0]);
/*get shape info*/
int outSize = shapes.outTensorList[0].size;
int inSize = shapes.inTensorList[0].size;
int inHeight = shapes.inTensorList[0].height;
int inWidth = shapes.inTensorList[0].width;
int batchSize = in_dims[0];
std::vector> inputs, outputs;
vector imageList;
int8_t* imageInputs = new int8_t[inSize * batchSize];
float* softmax = new float[outSize];
int8_t* FCResult = new int8_t[batchSize * outSize];
std::vector inputsPtr, outputsPtr;
std::vector> batchTensors;
/*run with batch*/
for (unsigned int n = 0; n < images.size(); n += batchSize) {
? ?unsigned int runSize =
? ? ? ?(images.size() < (n + batchSize)) ? (images.size() - n) : batchSize;
? ?in_dims[0] = runSize;
? ?out_dims[0] = batchSize;
? ?for (unsigned int i = 0; i < runSize; i++) {
? ? ?Mat image = imread(baseImagePath + images[n + i]);
? ? ?/*image pre-process*/
? ? ?Mat image2; ?//= cv::Mat(inHeight, inWidth, CV_8SC3);
? ? ?resize(image, image2, Size(inHeight, inWidth), 0, 0);
? ? ?for (int h = 0; h < inHeight; h++) {
? ? ? ?for (int w = 0; w < inWidth; w++) {
? ? ? ? ?for (int c = 0; c < 3; c++) {
? ? ? ? ? ?imageInputs[i * inSize + h * inWidth * 3 + w * 3 + c] =
? ? ? ? ? ? ? ?(int8_t)((image2.at(h, w)[c] - mean[c]) * input_scale);
}
}
}
imageList.push_back(image);
}
/* in/out tensor refactory for batch inout/output */
batchTensors.push_back(std::shared_ptr(
xir::create(inputTensors[0]->get_name(), in_dims,
xir::DataType{xir::XINT, 8u})));
inputs.push_back(std::make_unique(
imageInputs, batchTensors.back().get()));
batchTensors.push_back(std::shared_ptr(
xir::create(outputTensors[0]->get_name(), out_dims,
xir::DataType{xir::XINT, 8u})));
outputs.push_back(std::make_unique(
FCResult, batchTensors.back().get()));
/*tensor buffer input/output */
inputsPtr.clear();
outputsPtr.clear();
inputsPtr.push_back(inputs[0].get());
outputsPtr.push_back(outputs[0].get());
/*run*/
auto job_id = runner->execute_async(inputsPtr, outputsPtr);
runner->wait(job_id.first, -1);
for (unsigned int i = 0; i < runSize; i++) {
? ? ?cout << "
Image : " << images[n + i] << endl;
? ? ?/* Calculate softmax on CPU and display TOP-5 classification results */
? ? ?CPUCalcSoftmax(&FCResult[i * outSize], outSize, softmax, output_scale);
? ? ?TopK(softmax, outSize, 5, kinds);
? ? ?/* Display the impage */
? ? ?bool quiet = (getenv("QUIET_RUN") != nullptr);
? ? ?if (!quiet) {
? ? ? ?cv::imshow("Classification of ResNet50", imageList[i]);
? ? ? ?cv::waitKey(10000);
? ? ?}
? ?}
? ?imageList.clear();
? ?inputs.clear();
? ?outputs.clear();
?}
?delete[] FCResult;
?delete[] imageInputs;
?delete[] softmax;
}
(左右移動(dòng)查看全部?jī)?nèi)容)
其中,關(guān)鍵代碼行如下:
ListImages(baseImagePath, images); 用于列出../images目錄中的圖片文件
LoadWords(wordsPath + "words.txt", kinds); 用于讀取words.txt中的分類標(biāo)簽
Mat image = imread(baseImagePath + images[n + i]); 用于讀取圖片
resize(image, image2, Size(inHeight, inWidth), 0, 0); 用于將圖片縮放為模型需要的尺寸
auto job_id = runner->execute_async(inputsPtr, outputsPtr); 開(kāi)始異步執(zhí)行模型推理
runner->wait(job_id.first, -1); 等待異步執(zhí)行完成
cv::imshow("Classification of ResNet50", imageList[i]); 顯示圖片
cv::waitKey(10000); 等待鍵盤按鍵10秒
好了本篇內(nèi)容就到這里了,感謝閱讀,下次再會(huì)。
審核編輯:湯梓紅
-
Xilinx
+關(guān)注
關(guān)注
73文章
2200瀏覽量
131129 -
AI
+關(guān)注
關(guān)注
91文章
39793瀏覽量
301381 -
程序
+關(guān)注
關(guān)注
117文章
3846瀏覽量
85238 -
圖像分類
+關(guān)注
關(guān)注
0文章
96瀏覽量
12489
原文標(biāo)題:【試用報(bào)告】如何在KV260上快速體驗(yàn)Vitsi AI圖像分類示例程序
文章出處:【微信號(hào):gh_9b9470648b3c,微信公眾號(hào):電子發(fā)燒友論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
[KV260視覺(jué)入門套件試用體驗(yàn)]+KV260 開(kāi)發(fā)套件使用初體驗(yàn)
【KV260視覺(jué)入門套件試用體驗(yàn)】運(yùn)行SmartCamera示例程序
【KV260視覺(jué)入門套件試用體驗(yàn)】KV260 開(kāi)發(fā)套件使用體驗(yàn)(二、接通攝像頭)
【KV260視覺(jué)入門套件試用體驗(yàn)】部署DPU鏡像并運(yùn)行Vitis AI圖像分類示例程序
【KV260視覺(jué)入門套件試用體驗(yàn)】KV260開(kāi)發(fā)板初使用
【KV260視覺(jué)入門套件試用體驗(yàn)】Vitis-AI加速的YOLOX視頻目標(biāo)檢測(cè)示例體驗(yàn)和原理解析
【KV260視覺(jué)入門套件試用體驗(yàn)】+03.SmartCamera人臉識(shí)別示例程序(zmj)
【KV260視覺(jué)入門套件試用體驗(yàn)】KV260開(kāi)箱及鏡像燒寫(xiě)
【KV260視覺(jué)入門套件試用體驗(yàn)】四、KV260 視覺(jué)入門套件和固件更新
Kria KV260上的動(dòng)態(tài)交通燈系統(tǒng)
使用Xilinx Kria KV260進(jìn)行AI火災(zāi)探測(cè)
Kria KV260 AI視覺(jué)套件入門
Vitis ai 1.4 KV260鏡像開(kāi)源
Kria KV260視覺(jué)AI入門套件用戶指南
如何在KV260上快速體驗(yàn)Vitsi AI圖像分類示例程序
評(píng)論