实战检验:Orange Pi AIpro AI开发板的性能测试与使用体验

发布于:2024-07-14 ⋅ 阅读:(109) ⋅ 点赞:(0)


前言

Orange Pi AIpro,作为首款基于昇腾技术的AI开发板,它集成了高性能图形处理器,配备8GB/16GB LPDDR4X内存,支持外接存储模块,并具备双4K高清输出和强大的AI算力。

Orange Pi AIpro 简介

怀着迫不及待的打开了我们的Orange Pi AIpro,外观十分漂亮,映入眼帘的就是一个橙子的标志和为AI而生

在这里插入图片描述
整个板子的包裹性还是很好的,有一个盒子(里面都是海绵),板子外面还有一层塑料袋包裹着,大赞

在这里插入图片描述
整个板子的精密程度,以及它的美观程度都是很赞的,满满的科技感,爱不释手。
在这里插入图片描述

该开发板拥有丰富的接口,包括HDMI、GPIO、Type-C电源、M.2插槽、USB等,香橙派对应部分产品规格:

部件 参数
CPU 4核64位处理器+ AI处理器
GPU 集成图形处理器
AI算力 8-12TOPS算力
内存 LPDDR4X:8GB/16GB(可选),速率:3200Mbps
存储 SPI FLASH:32MB• SATA/NVME SSD(M.2接口2280);eMMC插槽:32GB/64GB/128GB/256GB(可选),eMMC5.1 HS400;TF插槽
WIFI+蓝牙 Wi-Fi 5双频2.4G和5G;BT4.2/BLE
以太网收发器 10/100/1000Mbps以太网
显示 2xHDMI2.0 Type-A TX 4K@60FPS;1x2 lane MIPI DSI via FPC connector
摄像头 2x2-lane MIPI CSI camera interface,兼容树莓派摄像头

我们可以官方给我们提供的接口详情图:
在这里插入图片描述
在这里插入图片描述
可以看到是堆料满满的一款产品,昇腾AI处理器是为了满足飞速发展的深度神经网络对芯片算力的需求,由华为公司在2018年推出的AI处理器,对整型数(INT8、INT4)或浮点数(FP16)提供了强大高效的计算力,在硬件结构上对深度神经网络做了优化,可以很高效率完成神经网络中的前向计算因此在智能终端领域有很大的应用前景。

Orange Pi AIpro 体验

将Linux镜像烧录到TF卡

打开香橙派官网:香橙派官网
在这里插入图片描述
选择我们对应的产品,我们可以根据自己的需求去下载官方的资料或者去下载官方的镜像,我们这里直接去下载ubuntu镜像
在这里插入图片描述
我们会跟着指示来到百度网盘,然后选择desktop的进行下载。
因为desktop镜像预装Linux桌面、CANN、AI 示例代码等,minimal镜像不包括上述内容
在这里插入图片描述
经过耐心等待,终于下载完成了
在这里插入图片描述

下载balenaEtcher:balenaEtcher官网,点击Download Etcher,
在这里插入图片描述
我们选择对应版本下载即可
在这里插入图片描述
我们将我们的TF卡插入读卡器中,接入电脑中,准备烧录
在这里插入图片描述
我们将我们下载好的balenaEtcher打开,选择聪文件烧录
在这里插入图片描述
我们选择好对应配置后,点击现在烧录!
在这里插入图片描述
当我们看到左边为紫色进度条时,证明我们正在烧录中
在这里插入图片描述
当我们看到左边为绿色进度条时,证明正在验证是否烧录成功中
在这里插入图片描述

YOLO识别视频中物体

我们首先打开连接上显示器,默认账号密码为:用户为HwHiAiUser,登录密码为Mind@123
在这里插入图片描述
我们右上角连接上WIFI

在这里插入图片描述
我们总共32G,操作系统预装了Ubuntu-22.04。系统本身加上预装的开发软件占用了大概17G的空间,剩余15G左右的空间。

HwHiAiUser@orangepiaipro:~$ cd samples
HwHiAiUser@orangepiaipro:~/samples$ ./start_notebook.sh

执行脚本之后,终端中会打印出Jupyter Lab的网址连接,我们将其复制去游览器打开即可
在这里插入图片描述
我们选择yolov5,然后选择main.ipynb,点击运行
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
我们运行完结果可以发现基本可以识别我们图片中的汽车,除了完全覆盖的没有识别到,也算正常。

我们在整个运行过程中,我们的风扇转速算不错了,散热效果比较好,拿起板子基本是一个常温的状态,没有发热的情况。
在这里插入图片描述

肺部CT识别

我们再来开发板运行一个项目:

import math
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.model_zoo as model_zoo
from torch.nn import init
import torch

__all__ = ['xception']

model_urls = {
    'xception': 'http://data.lip6.fr/cadene/pretrainedmodels/xception-43020ad28.pth'
}


class SeparableConv2d(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=1, stride=1, padding=0, dilation=1, bias=False):
        super(SeparableConv2d, self).__init__()

        self.conv1 = nn.Conv2d(in_channels, in_channels, kernel_size, stride, padding, dilation, groups=in_channels,
                               bias=bias)
        self.pointwise = nn.Conv2d(in_channels, out_channels, 1, 1, 0, 1, 1, bias=bias)

    def forward(self, x):
        x = self.conv1(x)
        x = self.pointwise(x)
        return x


class Block(nn.Module):
    def __init__(self, in_filters, out_filters, reps, strides=1, start_with_relu=True, grow_first=True):
        super(Block, self).__init__()

        if out_filters != in_filters or strides != 1:
            self.skip = nn.Conv2d(in_filters, out_filters, 1, stride=strides, bias=False)
            self.skipbn = nn.BatchNorm2d(out_filters)
        else:
            self.skip = None

        self.relu = nn.ReLU(inplace=True)
        rep = []

        filters = in_filters
        if grow_first:
            rep.append(self.relu)
            rep.append(SeparableConv2d(in_filters, out_filters, 3, stride=1, padding=1, bias=False))
            rep.append(nn.BatchNorm2d(out_filters))
            filters = out_filters

        for i in range(reps - 1):
            rep.append(self.relu)
            rep.append(SeparableConv2d(filters, filters, 3, stride=1, padding=1, bias=False))
            rep.append(nn.BatchNorm2d(filters))

        if not grow_first:
            rep.append(self.relu)
            rep.append(SeparableConv2d(in_filters, out_filters, 3, stride=1, padding=1, bias=False))
            rep.append(nn.BatchNorm2d(out_filters))

        if not start_with_relu:
            rep = rep[1:]
        else:
            rep[0] = nn.ReLU(inplace=False)

        if strides != 1:
            rep.append(nn.MaxPool2d(3, strides, 1))
        self.rep = nn.Sequential(*rep)

    def forward(self, inp):
        x = self.rep(inp)

        if self.skip is not None:
            skip = self.skip(inp)
            skip = self.skipbn(skip)
        else:
            skip = inp

        x += skip
        return x


class Xception(nn.Module):
    """
    Xception optimized for the ImageNet dataset, as specified in
    https://arxiv.org/pdf/1610.02357.pdf
    """

    def __init__(self, num_classes=38):
        """ Constructor
        Args:
            num_classes: number of classes
        """
        super(Xception, self).__init__()

        self.num_classes = num_classes

        self.conv1 = nn.Conv2d(3, 32, 3, 2, 0, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        self.relu = nn.ReLU(inplace=True)

        self.conv2 = nn.Conv2d(32, 64, 3, bias=False)
        self.bn2 = nn.BatchNorm2d(64)
        # do relu here

        self.block1 = Block(64, 128, 2, 2, start_with_relu=False, grow_first=True)
        self.block2 = Block(128, 256, 2, 2, start_with_relu=True, grow_first=True)
        self.block3 = Block(256, 728, 2, 2, start_with_relu=True, grow_first=True)

        self.block4 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True)
        self.block5 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True)
        self.block6 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True)
        self.block7 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True)

        self.block8 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True)
        self.block9 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True)
        self.block10 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True)
        self.block11 = Block(728, 728, 3, 1, start_with_relu=True, grow_first=True)

        self.block12 = Block(728, 1024, 2, 2, start_with_relu=True, grow_first=False)

        self.conv3 = SeparableConv2d(1024, 1536, 3, 1, 1)
        self.bn3 = nn.BatchNorm2d(1536)

        # do relu here
        self.conv4 = SeparableConv2d(1536, 2048, 3, 1, 1)
        self.bn4 = nn.BatchNorm2d(2048)

        self.fc = nn.Linear(2048, num_classes)

        # ------- init weights --------
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
        # -----------------------------

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)

        x = self.conv2(x)
        x = self.bn2(x)
        x = self.relu(x)

        x = self.block1(x)
        x = self.block2(x)
        x = self.block3(x)
        x = self.block4(x)
        x = self.block5(x)
        x = self.block6(x)
        x = self.block7(x)
        x = self.block8(x)
        x = self.block9(x)
        x = self.block10(x)
        x = self.block11(x)
        x = self.block12(x)

        x = self.conv3(x)
        x = self.bn3(x)
        x = self.relu(x)

        x = self.conv4(x)
        x = self.bn4(x)
        x = self.relu(x)

        x = F.adaptive_avg_pool2d(x, (1, 1))
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x


def xception(pretrained=False, **kwargs):
    """
    Construct Xception.
    """

    model = Xception(**kwargs)
    if pretrained:
        model.load_state_dict(model_zoo.load_url(model_urls['xception']))
    return model
import matplotlib.pyplot as plt
from PIL import Image
from torchvision.transforms import transforms
from model import xception
import torch
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif']=['SimHei']  #解决中文显示乱码问题
plt.rcParams['axes.unicode_minus']=False  #解决坐标轴负数的负号显示问题



data_transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Resize((224, 224)),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

img = Image.open(r"F:\SqueezeNet\dataset\test\PNEUMONIA\person1_virus_8.jpeg")
img = img.convert("RGB")
plt.imshow(img)
img = data_transform(img)
img = torch.unsqueeze(img, dim=0)
name = ['正常', '肺炎']
model_weight_path = "Xception_last.pth"
model = xception(num_classes=2)
model.load_state_dict(torch.load(model_weight_path))
model.eval()
with torch.no_grad():
    output = torch.squeeze(model(img))

    predict = torch.softmax(output, dim=0)
    # 获得最大可能性索引
    predict_cla = torch.argmax(predict).numpy()
    print('索引为', predict_cla)
print('预测结果为:{},置信度为: {}'.format(name[predict_cla], predict[predict_cla].item()))
plt.suptitle("预测结果为:{}".format(name[predict_cla]))
plt.show()

模型预测结果:
在这里插入图片描述

import torch
import torch.nn as nn

import torchvision.transforms as transforms
from torch.utils import data
import torch.optim as optim

from model import xception

from DataLoader import LoadData




# ================================= 下载图片、预处理图片、数据加载器 =================================

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
net = xception(num_classes=2).to(device=device)

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Resize((224, 224)),
     transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))])


train_data = LoadData(r"F:\SqueezeNet\Xception\train.txt", True)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=8, pin_memory=True,
                                           shuffle=True, num_workers=0)


loss_function = nn.CrossEntropyLoss()  # 定义损失函数
optimizer = torch.optim.Adam(net.parameters(), lr=0.001)

min_loss = 100
for epoch in range(2):  # loop over the dataset multiple times
    print('第{}轮训练开始'.format(epoch))
    running_loss = 0.0  # 累加训练过程中的损失
    for i, (inputs, labels) in enumerate(train_loader):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = inputs.to(device), labels.to(device)

        optimizer.zero_grad()  # 将历史损失梯度清零
        # forward + backward + optimize
        outputs = net(inputs)
        loss = loss_function(outputs, labels)
        loss.backward()  # 反向传播
        optimizer.step()  # 参数更新
        # print statistics
        running_loss += loss.item()
        if i % 50 == 49:  # print every 500 mini-batches
            print('进来了')
            #     with torch.no_grad(): # with是一个上下文管理器
            #         outputs = net(val_image)  # [batch, 10]
            predict_y = torch.max(outputs, dim=1)[1]
            accuracy = torch.eq(predict_y, labels).sum().item() / len(labels)
            print('[%d  %5d] train_loss: %.3f  test_accuracy: %.3f' %
                  (epoch + 1, i + 1, running_loss / 50, accuracy))




    print('Finished Training')

save_path = './Xception_last.pth'
torch.save(net.state_dict(), save_path)

训练可视化:
在这里插入图片描述
经过训练之后,我们的模型识别还是挺准的
在这里插入图片描述

Orange Pi AIpro 总结

通过个人的体验,我觉得Orange Pi AIpro 具有以下几个优势:

  • 接口丰富:除了标准的USB、HDMI、MIPI等接口外,还支持SATA/NVMe SSD 2280的M.2插槽,可外接32GB/64GB/128GB/256GB eMMC模块。
  • 支持多种操作系统:除了预装的Ubuntu系统外,还支持openEuler操作系统,满足不同的开发需求。
  • 昇腾AI技术路线:采用华为昇腾AI技术路线,具有高性能、低功耗的特点。

不足之处:

  • AI算力限制:虽然具有8TOPS和20TOPS两种AI算力版本,但对于更复杂的AI应用来说,可能还需要更强的算力支持。

Orange Pi AIpro 是一款硬件配置强大、接口丰富的高性能AI开发板。对于需要强大AI算力和丰富接口的用户来说,20T版本是一个不错的选择,尽管价格较高。而对于预算有限的用户,8T版本也是一个性价比较高的选择。