目录

  • 前言
  • 1 探索AI CPU与标准CPU之间的差异
  • 1.1 npu-smi info
  • 1.2 npu-smi info -t cpu-num-cfg
  • 2.AI Demo
  • 2.1 运行 Jupyter Lab
  • 2.2 运行yolov5 demo
  • 3.还原为脚本
  • 导入代码依赖
  • 4.如何通过HTTP被调用
  • 4.1 创建文件 http_api.py
  • 4.2 安装依赖
  • 4.3 运行
  • 4.4 接口测试
  • 5 总结与反思

前言

在进行AI相关的测试之前,我已经对OrangePi AIpro进行了初步的嵌入式操作评估。现在,我将转向AI功能的探索,以揭示华为昇腾芯片的独特优势。

1 探索AI CPU与标准CPU之间的差异

在开始这一部分之前,请确保您已经使用串行调试接口或SSH远程登录到OrangePi AIpro。登录信息如下:用户名"HwHiAiUser",密码"Mind@123"。同时,请将串口的波特率设置为115200,以确保通讯顺畅。

1.1 npu-smi info

npu-smi info

(base) HwHiAiUser@orangepiaipro:~$ npu-smi info
+--------------------------------------------------------------------------------------------------------+
| npu-smi 23.0.0                                   Version: 23.0.0                                       |
+-------------------------------+-----------------+------------------------------------------------------+
| NPU     Name                  | Health          | Power(W)     Temp(C)           Hugepages-Usage(page) |
| Chip    Device                | Bus-Id          | AICore(%)    Memory-Usage(MB)                        |
+===============================+=================+======================================================+
| 0       310B4                 | Alarm           | 0.0          45                15    / 15            |
| 0       0                     | NA              | 0            1564 / 7545                             |
+===============================+=================+======================================================+
(base) HwHiAiUser@orangepiaipro:~$

1.2 npu-smi info -t cpu-num-cfg

说实话,我也不太会看。只知道是8G,7545表示8GB内存。然后默认4核中,3个核是普通CPU,1个是AI CPU,平时AI CPU占用率是0%的。

这个操作可以查看到目前CPU情况。

(base) HwHiAiUser@orangepiaipro:~$ npu-smi info -t cpu-num-cfg -i 0 -c 0
        Current AI CPU number          : 1
        Current control CPU number     : 3
        Current data CPU number        : 0
(base) HwHiAiUser@orangepiaipro:~$

这个是查看利用率。

(base) HwHiAiUser@orangepiaipro:~$ npu-smi info -t usages -i 0 -c 0
        Memory Capacity(MB)            : 7545
        Memory Usage Rate(%)           : 21
        Hugepages Total(page)          : 15
        Hugepages Usage Rate(%)        : 100
        Aicore Usage Rate(%)           : 0
        Aicpu Usage Rate(%)            : 0
        Ctrlcpu Usage Rate(%)          : 2
        Memory Bandwidth Usage Rate(%) : 1
(base) HwHiAiUser@orangepiaipro:~$

然后参考说明书,这个指令还可以设置AI CPU为普通CPU,不过我暂时不操作了。

2.AI Demo

看用户手册,这里的demo是需要先运行Jupyter Lab的。这距离实际部署又差了一段距离。不过对于新手还是比较友好的,多了图形界面。

2.1 运行 Jupyter Lab

运行这个 Jupyter需要命令行执行:

cd ~/samples/notebooks
./start_notebook.sh

在这里插入图片描述

看这里的链接,是无法通过局域网访问的。
我想要通过局域网访问:

jupyter lab --ip 192.168.123.179  --port 5678 --allow-root --no-browser

注意这里192.168.123.179是OrangePi AIpro的ip,和windows电脑处于同一个路由器下。

在这里插入图片描述

电脑可以访问了:

在这里插入图片描述

2.2 运行yolov5 demo

我也不是很熟悉Jupyter,最开始是在百度Ai Studio类似的工具中看到的,按播放可以运行一个个代码块。
打开 yolov5下的main.ipynb。这个格式的文件都需要用Jupyter打开才可以的,不能直接用python运行的。不便于部署,但是便于入门,也可以带有很多注释。带有注释就弥补了很多python库缺少文档的缺点。c/c++的开源库,往往编程基础扎实,也能输出文档。python的库没那么多规矩。

在这里插入图片描述

打开 main.ipynb后,点击>>重新运行。

在这里插入图片描述

随后等待一段时间,就会有视频输出了。这个demo未修改下,是处理video的。

在这里插入图片描述

Jupyter不错,可以显示处理结果。图片和视频都可以。

根据用户手册,这个demo还可以处理图像。修改第4个代码块中的infer_mode,修改为image,再次执行。

在这里插入图片描述

会输出结果:

在这里插入图片描述

到这里就运行了第一个demo。本demo其实还可以调用摄像头进行识别。不过我暂时未接入摄像头。
其余的demo用同样的方法就可以执行。目前有这几个demo,只要适当修改,就可以直接应用于特定场景当中。

在这里插入图片描述

demo还是做得挺完善的,有图像视频处理、文本、图像HDR、语音分类等。其实实际部署到板卡的AI模型,难点在于基本都是在英伟达的GPU中训练,然后再经过模型转换后放到板子上运行的。我以前在电脑中训练过微小模型(LeNet),用的是Tensorflow+Keras,部署到英伟达的GPU、Jetson都很方便,但是要移植到别的AI板卡,就卡在自己不会算子这里。

3.还原为脚本

我还是偏爱脚本,后续可以直接命令行运行。
切换目录:

cd ~/samples/notebooks/01-yolov5

创建文件:

vim main.py

添加内容:

导入代码依赖

import cv2
import numpy as np
import ipywidgets as widgets
from IPython.display import display
import torch
from skvideo.io import vreader, FFmpegWriter
import IPython.display
from ais_bench.infer.interface import InferSession
 
from det_utils import letterbox, scale_coords, nms
 
def preprocess_image(image, cfg, bgr2rgb=True):
    """图片预处理"""
    img, scale_ratio, pad_size = letterbox(image, new_shape=cfg['input_shape'])
    if bgr2rgb:
        img = img[:, :, ::-1]
    img = img.transpose(2, 0, 1)  # HWC2CHW
    img = np.ascontiguousarray(img, dtype=np.float32)
    return img, scale_ratio, pad_size
 
 
def draw_bbox(bbox, img0, color, wt, names):
    """在图片上画预测框"""
    det_result_str = ''
    for idx, class_id in enumerate(bbox[:, 5]):
        if float(bbox[idx][4] < float(0.05)):
            continue
        img0 = cv2.rectangle(img0, (int(bbox[idx][0]), int(bbox[idx][1])), (int(bbox[idx][2]), int(bbox[idx][3])),
                             color, wt)
        img0 = cv2.putText(img0, str(idx) + ' ' + names[int(class_id)], (int(bbox[idx][0]), int(bbox[idx][1] + 16)),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
        img0 = cv2.putText(img0, '{:.4f}'.format(bbox[idx][4]), (int(bbox[idx][0]), int(bbox[idx][1] + 32)),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
        det_result_str += '{} {} {} {} {} {}\n'.format(
            names[bbox[idx][5]], str(bbox[idx][4]), bbox[idx][0], bbox[idx][1], bbox[idx][2], bbox[idx][3])
    return img0
 
 
def get_labels_from_txt(path):
    """从txt文件获取图片标签"""
    labels_dict = dict()
    with open(path) as f:
        for cat_id, label in enumerate(f.readlines()):
            labels_dict[cat_id] = label.strip()
    return labels_dict
 
 
def draw_prediction(pred, image, labels):
    """在图片上画出预测框并进行可视化展示"""
    imgbox = widgets.Image(format='jpg', height=720, width=1280)
    img_dw = draw_bbox(pred, image, (0, 255, 0), 2, labels)
    imgbox.value = cv2.imencode('.jpg', img_dw)[1].tobytes()
    display(imgbox)
 
 
def infer_image(img_path, model, class_names, cfg):
    """图片推理"""
    # 图片载入
    image = cv2.imread(img_path)
    # 数据预处理
    img, scale_ratio, pad_size = preprocess_image(image, cfg)
    # 模型推理
    output = model.infer([img])[0]
 
    output = torch.tensor(output)
    # 非极大值抑制后处理
    boxout = nms(output, conf_thres=cfg["conf_thres"], iou_thres=cfg["iou_thres"])
    pred_all = boxout[0].numpy()
    # 预测坐标转换
    scale_coords(cfg['input_shape'], pred_all[:, :4], image.shape, ratio_pad=(scale_ratio, pad_size))
    # 图片预测结果可视化
    draw_prediction(pred_all, image, class_names)
 
 
def infer_frame_with_vis(image, model, labels_dict, cfg, bgr2rgb=True):
    # 数据预处理
    img, scale_ratio, pad_size = preprocess_image(image, cfg, bgr2rgb)
    # 模型推理
    output = model.infer([img])[0]
 
    output = torch.tensor(output)
    # 非极大值抑制后处理
    boxout = nms(output, conf_thres=cfg["conf_thres"], iou_thres=cfg["iou_thres"])
    pred_all = boxout[0].numpy()
    # 预测坐标转换
    scale_coords(cfg['input_shape'], pred_all[:, :4], image.shape, ratio_pad=(scale_ratio, pad_size))
    # 图片预测结果可视化
    img_vis = draw_bbox(pred_all, image, (0, 255, 0), 2, labels_dict)
    return img_vis
 
 
def img2bytes(image):
    """将图片转换为字节码"""
    return bytes(cv2.imencode('.jpg', image)[1])
 
 
def infer_video(video_path, model, labels_dict, cfg):
    """视频推理"""
    image_widget = widgets.Image(format='jpeg', width=800, height=600)
    display(image_widget)
 
    # 读入视频
    cap = cv2.VideoCapture(video_path)
    while True:
        ret, img_frame = cap.read()
        if not ret:
            break
        # 对视频帧进行推理
        image_pred = infer_frame_with_vis(img_frame, model, labels_dict, cfg, bgr2rgb=True)
        image_widget.value = img2bytes(image_pred)
 
 
def infer_camera(model, labels_dict, cfg):
    """外设摄像头实时推理"""
    def find_camera_index():
        max_index_to_check = 10  # Maximum index to check for camera
 
        for index in range(max_index_to_check):
            cap = cv2.VideoCapture(index)
            if cap.read()[0]:
                cap.release()
                return index
 
        # If no camera is found
        raise ValueError("No camera found.")
 
    # 获取摄像头
    camera_index = find_camera_index()
    cap = cv2.VideoCapture(camera_index)
    # 初始化可视化对象
    image_widget = widgets.Image(format='jpeg', width=1280, height=720)
    display(image_widget)
    while True:
        # 对摄像头每一帧进行推理和可视化
        _, img_frame = cap.read()
        image_pred = infer_frame_with_vis(img_frame, model, labels_dict, cfg)
        image_widget.value = img2bytes(image_pred)
        
cfg = {
    'conf_thres': 0.4,  # 模型置信度阈值,阈值越低,得到的预测框越多
    'iou_thres': 0.5,  # IOU阈值,高于这个阈值的重叠预测框会被过滤掉
    'input_shape': [640, 640],  # 模型输入尺寸
}
 
model_path = 'yolo.om'
label_path = './coco_names.txt'
# 初始化推理模型
model = InferSession(0, model_path)
labels_dict = get_labels_from_txt(label_path)
 
 
infer_mode = 'image'
 
if infer_mode == 'image':
    img_path = 'world_cup.jpg'
    infer_image(img_path, model, labels_dict, cfg)
elif infer_mode == 'camera':
    infer_camera(model, labels_dict, cfg)
elif infer_mode == 'video':
    video_path = 'racing.mp4'
    infer_video(video_path, model, labels_dict, cfg)

运行测试:

python3 main.py

在这里插入图片描述

这里用脚本调用AI处理器,比上面的Jupyter更利于实际部署。图形界面给学习上手较好,linux服务器还不一定有图形界面。当服务器是后台服务器,没有前端技术,就会只有命令行执行程序。下文还给设备添加HTTP接口,实现命令行被局域网访问。但是如果要执行特定代码,还需要继续开发。

4.如何通过HTTP被调用

假如本板卡不是个大脑,只是个辅助性的AI。那么本板卡需要开发个接口API,给外部调用。虽然SSH也是可以远程执行脚本,不过还是HTTP接口的API比较友好。HTTP形式的,也利于跨平台跨设备进行互相通信。

接下来演示基于python+flask实现的http接口的hello world。

4.1 创建文件 http_api.py

from flask import Flask
 
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False  # 禁止中文转义
 
@app.route('/HelloWorld')
def hello_world():
    return "Hello World!"
 
if __name__ == "__main__":
    app.run(host="0.0.0.0")

4.2 安装依赖

python3 -m pip install flask

4.3 运行

python3 http_api.py

4.4 接口测试

浏览器打开 192.168.123.179:5000/HelloWorld

在这里插入图片描述

用postman测试http接口:

在这里插入图片描述

这里用的是Get方式。后续还可以开发HTTP POST接口用以数据交互。比如可以post图像文件到板卡,然后板子用python调用AI处理器进行图像处理。不过本文暂时没有做到那么多。

5 总结与反思

我对华为昇腾的AI架构和算子并不十分了解,目前仅处于尝试运行演示程序的阶段,并未深入到AI开发的核心领域。本文通过使用npu-smi命令,我得以一窥昇腾CPU的独特之处——它能够将CPU核心配置为普通或AI专用,这一功能颇具特色。在参考了用户手册之后,我成功运行了第一个AI演示程序。体验中,我发现该开发板的计算能力相当可观,尤其是在处理视频时,效率显著,远快于我以往使用OpenCV在CPU模式下处理同样时长视频的经历。这表明昇腾在图像和视频处理方面的AI性能相当出色。

此外,文章还提供了在开发板上实现HTTP接口的"Hello World"示例,突显了AI开发板需要一个易于调用的接口,以便将小型AI功能集成到更大的系统中。尽管我目前对AI领域的理解还比较浅显,但通过这些基础的实践,我对昇腾AI板卡的潜力和应用前景有了初步的认识。

Logo

昇腾万里,让智能无所不及

更多推荐