• 【CANN训练营笔记】OrangePI AIPro 体验手写体识别模型训练与推理


    CANN 简介

    当我们谈到香橙派AIPro的时候,总会把她和昇腾生态关联起来,因为在昇腾芯片的加持下,这款开发板有着出色的算力,被众多开发者追捧。而谈到昇腾芯片,我们不得不提上层的 AI 异构计算架构 CANN。

    异构计算架构CANN(Compute Architecture for Neural Networks)是华为针对AI场景推出的异构计算架构,向上支持多种AI框架,包括MindSpore、PyTorch、TensorFlow等,向下服务AI处理器与编程,发挥承上启下的关键作用,是提升昇腾AI处理器计算效率的关键平台。同时针对多样化应用场景,提供多层次编程接口,支持用户快速构建基于昇腾平台的AI应用和业务。

    从上面的CANN 逻辑架构图中,我们可以看到 CANN 起到了一个重要的承上启下的作用,为下层的昇腾 AI 算力资源提供了统一的编程接口去调度,对上层适配了主流的 AI 框架,使得整个 AI 开发体系更加强壮。整个 CANN 的关键功能特性包括:

    • 应用开发:简单来说,就是提供统一的API框架,实现对所有资源的调用。

    • 模型开发:提供了模型迁移工具,支持将 PyTorch、TensorFlow等开源框架网络模型快速迁移到昇腾平台。

    • 算子开发:提供了超过 1400 个硬件亲和的高性能算子,覆盖主流 AI 框架的算子加速需求,还支持自定义算子开发。

    今天,我们的实践内容主要集中在应用开发和模型开发,我们训练一个手写体识别模型并进行推理。

    MNIST & EMNIST 数据集介绍

    MNIST数据

    • MNIST数据集(Modified National Institute of Standards and Technology database)是一个用来训练各种图像处理系统的二进制图像数据集,广泛应用于机器学习中的训练和测试。

    • 该数据集是从NIST的两个手写数字数据集:Special Database 3 和Special Database 1中分别取出部分图像,并经过一些图像处理后得到的。

    • MNIST数据集共有70000张图像,其中训练集60000张,测试集10000张。所有图像都是28×28的灰度图像,每张图像包含一个手写数字。

    EMNIST数据集

    • EMNIST全称为Extended MNIST,是NIST Special Database 19的扩展数据集,包含了字母和数字,比MNIST的内容更多。

    • EMNIST数据集主要分为以下几类:By_Class、By_Merge、Balanced、Digits、Letters和MNIST。

    • By_Class和By_Merge共有814255张图片,Balanced类有131600张图片,Digits类有28000张图片,Letters类有145600张图片,MNIST类有70000张图片。

    • 这些数据集的处理方式跟MNIST保持一致,也是为了方便已经熟悉MNIST的开发者去使用。

    Pytorch 训练及迁移

    Pytorch 训练基本流程是怎么样的?首先,我们训练的输入是我们的数据集,它包含了我们的图片和标签,标签就是表明图片的真实值是多少,比如说图片画的是0,那它的标签就是0;然后,我们对数据集进行处理,把它封装进 DataSet 的数据格式里, 主要用来下载数据集以及对数据集进行预处理,如 Resize、归一化等等,这些操作使用得当可以提升训练效果并加速训练,DataLoader 定义如何取数据,一个重要的参数是 batchsize ,它决定了我们每次训练他一次会取多少张图片, 比如 batchsize = 4 ,那么一次训练会取四张图片出来进行训练;接着就是前向传播,也就是让这个输入经过整个模型得到一个推理的结果,通过损失函数计算 loss 值,使用反向传播、权重更新来迭代模型,权重更新会用到优化器,也就是决定了我们权重更新是如何更新的,因此这里比较重要的参数就是我们的学习率,也就是 learning ratelr),它决定了每一次训练权重更新的幅度是大还是小;最后就是训练结束保持模型。以 mnist 数据集为例,当我们设置epochs 为 5 、batchsize为 64 时,这意味着我们将遍历整个训练集 5 次。在每个 “epoch” 中,我们都会遍历所有的批次(每个批次包含 64 个样本),对每个批次进行前向传播、计算损失、反向传播和更新权重。这样,模型就可以在每个 “epoch” 中学习到数据集中的信息,并逐步改进其预测性能。在每个 “epoch” 结束时,我们打印出平均损失,以便观察模型的学习过程。

    综合上述,在 Pytorch 训练过程中,我们可以适当调整三个重要参数:batchsizeepochLR除此之外,我们还可以尝试修改损失函数优化器来调教模型。

    将基于PyTorch的训练脚本迁移到昇腾AI处理器上进行训练,目前有以下3种方式:自动迁移(推荐)、工具迁移、手工迁移,且迁移前要保证该脚本能在GPU、CPU上运行。推荐用户使用最简单的自动迁移方式。

    • 自动迁移

    PyTorch 1.11.0版本及以上使用

     
    
    1. import torch
    2. import torch_npu
    3. .....
    4. from torch_npu.contrib import transfer_to_npu

    • 工具迁移

        工具迁移推荐使用命令行方式: msFmkTransplt

        具体参考:

    https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/80RC1alpha003/devaids/auxiliarydevtool/atlasfmkt_16_0015.html

    • 手工迁移

      • 单卡迁移

        • 导入NPU相关库

        • 迁移适配GPU的模型脚本,指定NPU作为训练设备

        • 替换CUDA接口:将训练脚本中的CUDA接口替换为NPU接口,例如CUDA接口、模型、损失函数、数据集等迁移到NPU上

      • 多卡迁移

        • 除单卡迁移包含的3个修改要点外,在分布式场景下,还需要切换通信方式,直接修改init_process_group的值。

    手写体识别模型训练与推理

    环境介绍

    开发板:OrangePi AI Pro 8G

    NPU:Ascend 310B4

    CANN:7.0

    Pytorch:2.1.0 + torchvision 0.16.0 + torch-npu 2.1.0rc1

    代码获取

    本次实践代码来源于 Ascend 社区的仓库:https://gitee.com/ascend/EdgeAndRobotics/tree/master/Samples/HandWritingTrainAndInfer

     
    
    1. git clone https://gitee.com/ascend/EdgeAndRobotics
    2. cd EdgeAndRobotics/Samples/HandWritingTrainAndInfer

    依赖安装

    1. # torch_npu由于需要源码编译,速度可能较慢,本样例提供 python3.9,torch2.1版本的torch_npu whl包
    2. wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/wanzutao/torch_npu-2.1.0rc1-cp39-cp39-linux_aarch64.whl
    3. # 使用pip命令安装
    4. pip3 install torch_npu-2.1.0rc1-cp39-cp39-linux_aarch64.whl
    5. # 安装其他依赖
    6. pip3 install -r requirements.txt # PyTorch2.1版本

    模型训练(CPU)

    python main-cpu.py

    模型训练(NPU)

    python main.py

    我们从本案例看看 Pytroch 模型迁移训练,主要改动如下:

    • 导入NPU相关库,

      1. import torch
      2. import torch_npu
      3. # 此处还导入 AMP 模块,训练时自动混合精度
      4. from torch_npu.npu import amp

    • 迁移适配GPU的模型脚本,指定NPU作为训练设备

      1. device = torch.device('npu:0')
      2. #...
      3. imgs = imgs.to(device) # 把img数据放到指定NPU上
      4. labels = labels.to(device) # 把label数据放到指定NPU上
      5. #...
      6. #...
      7. # 把模型放到指定NPU上
      8. model = CNN().to(device)
      9. # 定义损失函数
      10. loss_func = nn.CrossEntropyLoss().to(device)
      11. #...

    • 开启AMP进行训练

       
      1. for batch_idx,(imgs, labels) in enumerate(train_dataloader):
      2. data_time.update(time.time() - end)
      3. # 把img数据放到指定NPU上
      4. imgs = imgs.to(device)
      5. # 把label数据放到指定NPU上
      6. labels = labels.to(device)
      7. # 设置amp
      8. with amp.autocast():
      9. # 前向计算
      10. outputs = model(imgs)
      11. # 损失函数计算
      12. loss = criterion(outputs, labels)
      13. optimizer.zero_grad()
      14. # 进行反向传播前后的loss缩放、参数更新
      15. # loss缩放并反向转播
      16. scaler.scale(loss).backward()
      17. # 更新参数(自动unscaling)
      18. scaler.step(optimizer)
      19. # 基于动态Loss Scale更新loss_scaling系数
      20. scaler.update()
      21. #...
      22. scaler = amp.GradScaler() # 在模型、优化器定义之后,定义GradScaler
      23. #...

    训练完成之后我们分别得到了 `mnist.pt` 和 `mnist-npu.pt` 。接着我们可以使用训练出来的模型进行推理,虽然我们可以直接用来推理,但业界常规的操作都是将 `.pt --> .onnx`,也许是 ONNX(Open Neural Network Exchange)是更具开放性的模型格式,它允许模型在不同的深度学习框架之间进行转换,更易于支持不同的硬件平台。

    在线推理

    首先将 pt 模型导出为 onnx :

    1. import torch
    2. import torch.nn as nn
    3. # CNN类定义
    4. class CNN(nn.Module):
    5. def __init__(self):
    6. super(CNN, self).__init__()
    7. self.net = nn.Sequential(
    8. nn.Conv2d(in_channels = 1, out_channels = 16,
    9. kernel_size = (3, 3),
    10. stride = (1, 1),
    11. padding = 1),
    12. nn.MaxPool2d(kernel_size = 2),
    13. nn.Conv2d(16, 32, 3, 1, 1),
    14. nn.MaxPool2d(2),
    15. nn.Flatten(),
    16. nn.Linear(32*7*7, 16),
    17. nn.ReLU(),
    18. nn.Linear(16, 10)
    19. )
    20. def forward(self, x):
    21. return self.net(x)
    22. device = torch.device('cpu')
    23. # 加载训练的pt模型,如 mnist.pt
    24. model = torch.load("./mnist.pt",map_location=device)
    25. # 切换评估模式
    26. model.eval()
    27. # 指定输入输出节点名称
    28. input_names = ['input']
    29. output_names = ['output']
    30. # 构造输入
    31. x = torch.randn(1,1,28,28,requires_grad=True)
    32. # onnx导出
    33. torch.onnx.export(model, x, 'mnist.onnx', input_names=input_names, output_names=output_names, verbose='True')

    执行 `python3 export.py `, 在当前路径下生成 mnist.onnx 模型

    执行在线推理:

    1. # 下载测试图片,也可自行准备
    2. cd data
    3. wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/wanzutao/mnist/8.jpg
    4. # 执行推理在线
    5. cd ../onnxInfer/
    6. python3 infer.py

    推理成功返回:

    1. [inferssession_time:326 pictures/s] [output:8]

    离线推理

    先进行模型转换 `onnx -> om`: 使用 ATC 模型转换工具将模型转换为昇腾AI处理器能识别的模型(*.om)。

     
    
    1. # 获取测试图片数据
    2. cd omInfer/data
    3. wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/wanzutao/mnist/8.jpg
    4. # 拷贝模型
    5. cd ../model
    6. cp ../../mnist.onnx ./
    7. # 获取AIPP配置文件
    8. wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/wanzutao/mnist/ecs/aipp.cfg
    9. # 模型转换
    10. atc --model=mnist.onnx --framework=5 --insert_op_conf=aipp.cfg --output=mnist --soc_version=Ascend310B4

    执行离线推理

    1. # 编译源码
    2. cd ../scripts
    3. bash sample_build.sh
    4. # 离线推理
    5. bash sample_run.sh

    执行成功输出:

    value[1.000000] output[8]

    EMNIST 训练介绍

    最后我们简单过一下 EMNIST 数据集训练,与 MNIST 不同点在于:

    • 加入了验证集,且数据下载时添加 ` split="letters"`

    • 神经网络有稍微调整:

    1. class CNN(nn.Module):
    2. def __init__(self):
    3. super(CNN, self).__init__()
    4. self.net = nn.Sequential(
    5. nn.Conv2d(in_channels = 1, out_channels = 16,
    6. kernel_size = (5, 5),
    7. stride = (1, 1),
    8. padding = 2),
    9. nn.ReLU(),
    10. nn.MaxPool2d(kernel_size = 2),
    11. nn.Conv2d(16, 32, 5, 1, 2),
    12. nn.ReLU(),
    13. nn.MaxPool2d(2),
    14. nn.Flatten(),
    15. nn.Linear(32*7*7, 27) # 索引从 1 开始,因此 26 个字母是 27
    16. )
    17. def forward(self, x):
    18. return self.net(x)

  • 相关阅读:
    Unity3D PRO 3D游戏制作系列教程第四课:认识菜单二
    核酸检测多少人为一组混检合适?
    SpringBoot集成-阿里云对象存储OSS
    重新认识下JVM级别的本地缓存框架Guava Cache——优秀从何而来
    华为OD机考--TVL解码--GPU算力--猴子爬台阶--两个数组前K对最小和--勾股数C++实现
    Linux 网络编程 广播/组播
    Spring5学习笔记03--Bean的生命周期
    数据仓库为什么要分层建设?每一层的作用是什么?
    二叉树与递归的相爱相杀
    杭电oj 2044 一只小蜜蜂,C语言
  • 原文地址:https://blog.csdn.net/sinat_34365157/article/details/137408404