基于PaddleClas2.2的奥特曼识别,从数据训练到利用PaddleLite2.9框架将模型部署到树莓派4b 64位(调用python api进行图片&视频流识别)

网上PaddleClass2.2文章很少,都是2.1,但是2.2和2.1的配置还是有些区别的,而且看了网上很多关于paddle lite树莓派相关教程都是修改cc文件,然后./run.sh。但是没有直接调用python api的教程,更有甚至利用python使用os.system('./run.sh')进行调用,实在难受。因此有了这篇文章来记录一下。

本项目主要是基于PaddleClas2.2的奥特曼识别,从数据训练到利用PaddleLite2.9框架将模型部署到树莓派4b 64位,然后在树莓派上利用paddlelite框架的python api进行模型转换和图片&视频流对奥特曼的识别。


目录

1.数据处理

2.模型训练

3.模型评估

4.模型转换

5.树莓派部署


1.数据处理

我们利用百度的aistudio白嫖算力进行数据训练。

首先是数据解压处理

# 数据解压
!unzip -oq /home/aistudio/data/data104203/aoteman.zip

aoteman文件夹结构如下

aoteman
├── dijia
│   ├── 001.jpg
│   ├── 002.jpg
│   ├── 003.jpg
│   ├── ······
├── jieke
│   ├── 001.jpg
│   ├── 002.jpg
│   ├── 003.jpg
│   ├── ······
├── saiwen
│   ├── 001.jpg
│   ├── 002.jpg
│   ├── 003.jpg
│   ├── ······
├── tailuo
│   ├── 001.jpg
│   ├── 002.jpg
│   ├── 003.jpg
│   ├── ······
├──predict_demo.jpg

由于代码执行警告太多很烦,我也先去掉

import warnings
warnings.filterwarnings("ignore")

数据集划分是个很烦的事,之前我也是自己def了n个函数划分然后写入txt,很麻烦,小白甚至觉得训练都没数据处理烦,这里我推荐jikuai库,可以一键划分生成txt,我们先安装一下。

!pip install jikuai -q

 那么接下来我们就开始git paddleclas了 ,并选择2.2分支

!git clone https://gitee.com/paddlepaddle/PaddleClas.git -b release/2.2

然后利用jiekuai进行数据划分

%cd ~/
from jikuai.dataset import Dataset
dataset = Dataset("aoteman") # 参数为数据集所在的位置,是分类目录的上一级目录
dataset.paddleclastxt(0.8) # 生成训练集和测试集列表,参数为两者划分的比例值。
!ls
# 2280348.ipynb  aoteman	data  eval.txt	PaddleClas  train.txt  work

我们把数据移到PaddleClas/dataset里,方便打包

!mv eval.txt aoteman/
!mv train.txt aoteman/
!mv aoteman PaddleClas/dataset/

2.模型训练

接下来是最重要的tran前配置了,不得不说PaddleClas比PaddleDetection配置要方便多了,全在一个yaml里

我们对 PaddleClas/ppcls/configs/quick_start/new_user/ShuffleNetV2_x0_25.yaml 进行修改设置

主要是以下几点:分类数、训练和验证的路径、图像尺寸、数据预处理、训练和预测的num_workers: 0才可以在aistudio跑通。

# global configs
Global:
  checkpoints: null
  # 预训练模型加载,这里不用了
  pretrained_model: null 
  # 模型保存地址
  output_dir: ./output/ 
  # 没有gpu就用cpu
  device: gpu 
  # 每几个轮次保存一次
  save_interval: 1
  eval_during_train: True
  # 每几个轮次验证一次
  eval_interval: 1
  # 训练600轮次
  epochs: 600
  # 每10步打印
  print_batch_step: 10
  # 可视化,这里不开启
  use_visualdl: False
  # 预测模型导出地址
  image_shape: [3, 224, 224]
  save_inference_dir: ./inference

# 模型结构
Arch:
  name: ShuffleNetV2_x0_25
  # 类别
  class_num: 4
 
# 训练/评估过程的损失函数配置 
Loss:
  Train:
    - CELoss:
        weight: 1.0
  Eval:
    - CELoss:
        weight: 1.0


Optimizer:
  name: Momentum
  momentum: 0.9
  lr:
    name: Cosine
    learning_rate: 0.0125
    warmup_epoch: 5
  regularizer:
    name: 'L2'
    coeff: 0.00001


# 用于训练和评估的数据加载配置
DataLoader:
  Train:
    dataset:
      name: ImageNetDataset
      # 文件根目录 因为train.txt格式是“aoteman/tailuo/072.jpg 3” 以便导入
      image_root: ./dataset
      # train划分地址
      cls_label_path: ./dataset/aoteman/train.txt
      # 数据预处理
      transform_ops:
        - DecodeImage:
            to_rgb: True
            channel_first: False
        - RandCropImage:
            size: 224
        - RandFlipImage:
            flip_code: 1
        - NormalizeImage:
            scale: 1.0/255.0
            mean: [0.485, 0.456, 0.406]
            std: [0.229, 0.224, 0.225]
            order: ''

    sampler:
      name: DistributedBatchSampler
      # 每一步16个数据加载
      batch_size: 16
      drop_last: False
      shuffle: True
    loader:
      num_workers: 0 # 这里一定要0才可以跑通aistudio
      use_shared_memory: True

  Eval:
    dataset: 
      name: ImageNetDataset
      image_root: ./dataset
      cls_label_path: ./dataset/aoteman/eval.txt
      transform_ops:
        - DecodeImage:
            to_rgb: True
            channel_first: False
        - ResizeImage:
            resize_short: 256
        - CropImage:
            size: 224
        - NormalizeImage:
            scale: 1.0/255.0
            mean: [0.485, 0.456, 0.406]
            std: [0.229, 0.224, 0.225]
            order: ''
    sampler:
      name: DistributedBatchSampler
      batch_size: 64
      drop_last: False
      shuffle: False
    loader:
      num_workers: 0
      use_shared_memory: True

Infer:
  infer_imgs: ./dataset/aoteman/predict_demo.jpg
  batch_size: 10
  transforms:
    - DecodeImage:
        to_rgb: True
        channel_first: False
    - ResizeImage:
        resize_short: 256
    - CropImage:
        size: 224
    - NormalizeImage:
        scale: 1.0/255.0
        mean: [0.485, 0.456, 0.406]
        std: [0.229, 0.224, 0.225]
        order: ''
    - ToCHWImage:
  PostProcess:
    name: Topk
    # 输出的可能性最高的前topk个
    topk: 4
    # 标签文件 需要自己新建文件
    class_id_map_file: ./dataset/aoteman/labels.txt
Metric:
  Train:
    - TopkAcc:
        topk: [1, 4]
  Eval:
    - TopkAcc:
        topk: [1, 4]

自己建个标签文件 ./dataset/label_list.txt

0 迪迦奥特曼  
1 杰克奥特曼  
2 赛文奥特曼  
3 泰罗奥特曼

配置搞定,那么接下来开始训练吧~

# 采用GPU训练
!export CUDA_VISIBLE_DEVICES=0
%cd ~/PaddleClas/
!python tools/train.py \
    -c ./ppcls/configs/quick_start/new_user/ShuffleNetV2_x0_25.yaml

训练结果一窥究竟

[2021/08/13 18:01:49] root INFO: [Train][Epoch 596/600][Iter: 0/28]lr: 0.00001, CELoss: 0.03720, loss: 0.03720, top1: 1.00000, top4: 1.00000, batch_cost: 0.30420s, reader_cost: 0.25090, ips: 52.59725 images/sec, eta: 0:00:42
[2021/08/13 18:01:51] root INFO: [Train][Epoch 596/600][Iter: 10/28]lr: 0.00001, CELoss: 0.13251, loss: 0.13251, top1: 0.96023, top4: 1.00000, batch_cost: 0.22338s, reader_cost: 0.17151, ips: 71.62723 images/sec, eta: 0:00:29
[2021/08/13 18:01:53] root INFO: [Train][Epoch 596/600][Iter: 20/28]lr: 0.00001, CELoss: 0.12071, loss: 0.12071, top1: 0.96131, top4: 1.00000, batch_cost: 0.21830s, reader_cost: 0.16640, ips: 73.29514 images/sec, eta: 0:00:26
[2021/08/13 18:01:55] root INFO: [Train][Epoch 596/600][Avg]CELoss: 0.11507, loss: 0.11507, top1: 0.96591, top4: 1.00000
[2021/08/13 18:01:56] root INFO: [Eval][Epoch 596][Iter: 0/2]CELoss: 0.30446, loss: 0.30446, top1: 0.92188, top4: 1.00000, batch_cost: 1.02158s, reader_cost: 0.99066, ips: 62.64828 images/sec
[2021/08/13 18:01:56] root INFO: [Eval][Epoch 596][Avg]CELoss: 0.31860, loss: 0.31860, top1: 0.90909, top4: 1.00000
[2021/08/13 18:01:56] root INFO: [Eval][Epoch 596][best metric: 0.9454545497894287]
[2021/08/13 18:01:56] root INFO: Already save model in ./output/ShuffleNetV2_x0_25/epoch_596
[2021/08/13 18:01:56] root INFO: Already save model in ./output/ShuffleNetV2_x0_25/latest

训练600epoch,基本上top1能稳定90-96%以上了。大家可以试下加载预训练模型,举个例子。

python3 tools/train.py \
    -c ./ppcls/configs/quick_start/MobileNetV3_large_x1_0.yaml \
    -o Arch.pretrained=True \
    -o Global.device=gpu

其中Arch.pretrained设置为True表示加载ImageNet的预训练模型,此外,Arch.pretrained也可以指定具体的模型权重文件的地址,使用时需要换成自己的预训练模型权重文件的路径。

3.模型评估

对我们模型评估一下 ,有90以上了,可以用了。

%cd ~/PaddleClas
!python3 tools/eval.py \
    -c ./ppcls/configs/quick_start/new_user/ShuffleNetV2_x0_25.yaml \
    -o Global.pretrained_model=output/ShuffleNetV2_x0_25/latest
[2021/08/14 10:30:57] root INFO: [Eval][Epoch 0][Iter: 0/2]CELoss: 0.31628, loss: 0.31628, top1: 0.92188, top4: 1.00000, batch_cost: 1.16294s, reader_cost: 1.12821, ips: 55.03274 images/sec
[2021/08/14 10:30:57] root INFO: [Eval][Epoch 0][Avg]CELoss: 0.33670, loss: 0.33670, top1: 0.90909, top4: 1.00000

我们用我们的模型来验证一下

!python3 tools/infer.py \
    -c ./ppcls/configs/quick_start/new_user/ShuffleNetV2_x0_25.yaml \
    -o Infer.infer_imgs=dataset/aoteman/predict_demo.jpg \
    -o Global.pretrained_model=output/ShuffleNetV2_x0_25/latest
[{'class_ids': [0, 1, 3, 2], 'scores': [1.0, 0.0, 0.0, 0.0], 'file_name': 'dataset/aoteman/predict_demo.jpg', 'label_names': ['迪迦奥特曼  ', '杰克奥特曼  ', '泰罗奥特曼', '赛文奥特曼  ']}]

验证推定100%是迪迦奥特曼,我们看下是不是迪迦奥特曼。 果然是哈~

基于PaddleClas2.2的奥特曼识别,从数据训练到利用PaddleLite2.9框架将模型部署到树莓派4b 64位(调用python api进行图片&视频流识别)_第1张图片

4.模型转换

最后要把模型导出inference模型,为什么又要导出新格式模型呢,原来模型不是可以用了吗?因为原来模型是包含正向和反向传播的,而我们部署上线是不需要反向传播的,因此我们导出预测专用的inference模型。

# 导出inference模型
!python3 tools/export_model.py \
    -c ./ppcls/configs/quick_start/new_user/ShuffleNetV2_x0_25.yaml \
    -o Global.pretrained_model=./output/ShuffleNetV2_x0_25/latest

模型导出到了我们.yaml里写的 PaddleClas/inference文件夹里里,我们将他下载上传到树莓派里,我们开始下一阶段。

5.树莓派部署

首先进行树莓派PaddleLite2.9源码编译,这大家也可以参考官方文档。

https://paddle-lite.readthedocs.io/zh/latest/source_compile/compile_linux.html

 我们先写个inference模型转nb模型的python代码

# https://paddlelite.paddlepaddle.org.cn/api_reference/python_api/opt.html
# 引用Paddlelite预测库
from paddlelite.lite import *

# 1. 创建opt实例
opt=Opt()
# 2. 指定输入模型地址
# 非combined形式
#opt.set_model_dir("./mobilenet_v1")
# conmbined形式,具体模型和参数名称,请根据实际修改
opt.set_model_file("./aoteman_inference/inference.pdmodel")
opt.set_param_file("./aoteman_inference/inference.pdiparams")
# 3. 指定转化类型: arm、x86、opencl、npu
opt.set_valid_places("arm")
# 4. 指定模型转化类型: naive_buffer、protobuf
opt.set_model_type("naive_buffer")
# 5. 动态离线量化
opt.set_quant_model(True)
opt.set_quant_type("QUANT_INT8")
# 6. 输出模型地址
opt.set_optimize_out("aoteman_model")
# 7. 执行模型优化
opt.run()

基于PaddleClas2.2的奥特曼识别,从数据训练到利用PaddleLite2.9框架将模型部署到树莓派4b 64位(调用python api进行图片&视频流识别)_第2张图片

 生成aoteman_model.nb模型,这样才可以被paddlelite调用。

然后我们建个predict.py利用python api进行模型调用和推断

from paddlelite.lite import *
import cv2
import numpy as np
import sys
import time
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw

# 加载模型
def create_predictor(model_dir):
    config = MobileConfig()
    config.set_model_from_file(model_dir)
    predictor = create_paddle_predictor(config)
    return predictor    

#图像归一化处理
def process_img(image, input_image_size):
    origin = image
    img = origin.resize(input_image_size, Image.BILINEAR)
    resized_img = img.copy()
    if img.mode != 'RGB':
      	img = img.convert('RGB')
    img = np.array(img).astype('float32').transpose((2, 0, 1))  # HWC to CHW
    img -= 127.5
    img *= 0.007843
    img = img[np.newaxis, :]
    return origin,img

# 预测
def predict(image, predictor, input_image_size):
    #输入数据处理
    input_tensor = predictor.get_input(0)
    input_tensor.resize([1, 3, input_image_size[0], input_image_size[1]])
    image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA))
    origin, img = process_img(image, input_image_size)
    image_data = np.array(img).flatten().tolist()
    input_tensor.set_float_data(image_data)
    #执行预测
    predictor.run()
    #获取输出
    output_tensor = predictor.get_output(0)
    print("output_tensor.float_data()[:] : ", output_tensor.float_data()[:])
    res = output_tensor.float_data()[:]
    return res

# 展示结果
def post_res(label_dict, res):
    print(max(res))
    target_index = res.index(max(res))
    print("结果是:" + "   " + label_dict[target_index])
    
if __name__ == '__main__':
    # 初始定义
    label_dict = {0:"dijia", 1:"jieke", 2:"saiwen", 3:"tailuo"}
    image = "./saiwen2.jpg"
    model_dir = "./trans/aoteman_model.nb"
    image_size = (224, 224)
    # 初始化
    predictor = create_predictor(model_dir)
    # 读入图片
    image = cv2.imread(image)
    # 预测
    res = predict(image, predictor, image_size)
    # 显示结果
    post_res(label_dict, res)
    cv2.imshow("image", image)
    cv2.waitKey()

 基于PaddleClas2.2的奥特曼识别,从数据训练到利用PaddleLite2.9框架将模型部署到树莓派4b 64位(调用python api进行图片&视频流识别)_第3张图片

网上随便找的赛文奥特曼被推断出来了~

 除了照片识别还不满足,我们利用摄像头视频流进行识别,建了个predict_camera.py如下

from paddlelite.lite import *
import cv2
import numpy as np
import sys
import time
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw

# 加载模型
def create_predictor(model_dir):
    config = MobileConfig()
    config.set_model_from_file(model_dir)
    predictor = create_paddle_predictor(config)
    return predictor    

#图像归一化处理
def process_img(image, input_image_size):
    origin = image
    img = origin.resize(input_image_size, Image.BILINEAR)
    resized_img = img.copy()
    if img.mode != 'RGB':
        img = img.convert('RGB')
    img = np.array(img).astype('float32').transpose((2, 0, 1))  # HWC to CHW
    img -= 127.5
    img *= 0.007843
    img = img[np.newaxis, :]
    return origin,img

# 预测
def predict(image, predictor, input_image_size):
    #输入数据处理
    input_tensor = predictor.get_input(0)
    input_tensor.resize([1, 3, input_image_size[0], input_image_size[1]])
    image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA))
    origin, img = process_img(image, input_image_size)
    image_data = np.array(img).flatten().tolist()
    input_tensor.set_float_data(image_data)
    #执行预测
    predictor.run()
    #获取输出
    output_tensor = predictor.get_output(0)
    # print("output_tensor.float_data()[:] : ", output_tensor.float_data()[:])
    res = output_tensor.float_data()[:]
    return res

# 展示结果
def post_res(label_dict, res):
    # print(max(res))
    target_index = res.index(max(res))
    # print("结果是:" + "   " + label_dict[target_index], "准确率为:",  max(res))
    print(max(res))
    return max(res), label_dict[target_index]
if __name__ == '__main__':
    # 初始定义
    label_dict = {0:"dijia", 1:"jieke", 2:"saiwen", 3:"tailuo"}
    model_dir = "./trans/aoteman_model.nb"
    image_size = (224, 224)
    # 初始化
    predictor = create_predictor(model_dir)
    
    cap = cv2.VideoCapture(0)
    while True:
        ret, frame = cap.read()
        # 预测
        print('Predict Start')
        time_start=time.time()
        res = predict(frame, predictor, image_size)
        confidence, label_result = post_res(label_dict, res)
        fps = 1/(time.time()-time_start)
        # 显示结果
        post_res(label_dict, res)
        if confidence > 0.5:
            cv2.putText(frame, '{}'.format(label_result), (20, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
            cv2.putText(frame, '{:.2f}'.format(confidence), (20, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
        cv2.putText(frame, 'fps:{:.2f}'.format(fps), (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
        cv2.imshow("frame", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            cv2.imwrite('out.png', frame)
            print("save one image done!")
            break
    print('Predict End')
    cap.release()
    cv2.destroyAllWindows()

基于PaddleClas2.2的奥特曼识别,从数据训练到利用PaddleLite2.9框架将模型部署到树莓派4b 64位(调用python api进行图片&视频流识别)_第4张图片

迪迦奥特曼被识别出来了!

需要图像数据或者opencv paddlelite编译需求,甚至镜像需要请私聊。

如下配置镜像,并开启jupyter开机自启,插件已经装全。
    OpenCV 4.5.1
    ncnn 20210124
    MNN 1.1.0
    Paddle-Lite 2.9
    TensorFlow-Lite 2.4.1
    TensorFLow 2.4.1
    TensorFlow Addons 0.13.0-dev
    Pytorch 1.8.0
    TorchVision 0.9.0

你可能感兴趣的:(Paddle,&,树莓派,paddlepaddle,python,opencv,人工智能,图像处理)