一文带你学会在三大系统中使用OpenVINO部署飞桨模型
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情
本文介绍如何在在三大系统(Windows、Linux、MacOS)中使用OpenVINO部署飞桨模型。以10种猴子的分类为例,训练出飞桨模型,再使用OpenVINO对飞桨模型进行部署。本文目录结构如下:
- 模型训练
- 环境配置
- 模型验证
- 模型进阶使用
一、模型训练
模型训练部分为10种猴子的分类,数据示例如下图1所示。采用ResNet18作为分类模型。
- 知识点:ResNet残差网络介绍
ResNet(Residual Neural Network)由微软研究院的Kaiming He等人提出,通过使用ResNet Unit成功在ILSVRC2015比赛中取得冠军,在top5上的错误率为3.57%,同时参数量比VGGNet低,效果非常突出。ResNet的主要思想是在网络中增加了直连通道(恒等映射),即Residual Block,如下图2所示。
# 导入所需要的库
from sklearn.utils import shuffle
import os
import pandas as pd
import numpy as np
from PIL import Image
import paddle
import paddle.nn as nn
from paddle.io import Dataset
import paddle.vision.transforms as T
import paddle.nn.functional as F
from paddle.metric import Accuracy
import warnings
warnings.filterwarnings("ignore")
# 读取数据
train_images = pd.read_csv('data/data27098/train.csv')
# labelshuffling
def labelShuffling(dataFrame, groupByName = 'label'):
groupDataFrame = dataFrame.groupby(by=[groupByName])
labels = groupDataFrame.size()
print("length of label is ", len(labels))
maxNum = max(labels)
lst = pd.DataFrame()
for i in range(len(labels)):
print("Processing label :", i)
tmpGroupBy = groupDataFrame.get_group(i)
createdShuffleLabels = np.random.permutation(np.array(range(maxNum))) % labels[i]
print("Num of the label is : ", labels[i])
lst=lst.append(tmpGroupBy.iloc[createdShuffleLabels], ignore_index=True)
print("Done")
# lst.to_csv('test1.csv', index=False)
return lst
# 划分训练集和校验集
all_size = len(train_images)
# print(all_size)
train_size = int(all_size * 0.8)
train_image_list = train_images[:train_size]
val_image_list = train_images[train_size:]
df = labelShuffling(train_image_list)
df = shuffle(df)
train_image_path_list = df['filename'].values
label_list = df['label'].values
label_list = paddle.to_tensor(label_list, dtype='int64')
train_label_list = paddle.nn.functional.one_hot(label_list, num_classes=10)
val_image_path_list = val_image_list['filename'].values
val_label_list = val_image_list['label'].values
val_label_list = paddle.to_tensor(val_label_list, dtype='int64')
val_label_list = paddle.nn.functional.one_hot(val_label_list, num_classes=10)
# 定义数据预处理
data_transforms = T.Compose([
T.Resize(size=(224, 224)),
T.RandomHorizontalFlip(224),
T.RandomVerticalFlip(224),
T.RandomRotation(30),
T.Transpose(), # HWC -> CHW
T.Normalize(
mean=[0, 0, 0], # 归一化
std=[255, 255, 255],
to_rgb=True)
])
# 构建Dataset
class MyDataset(paddle.io.Dataset):
"""
步骤一:继承paddle.io.Dataset类
"""
def __init__(self, train_img_list, val_img_list,train_label_list,val_label_list, mode='train'):
"""
步骤二:实现构造函数,定义数据读取方式,划分训练和测试数据集
"""
super(MyDataset, self).__init__()
self.img = []
self.label = []
# 借助pandas读csv的库
self.train_images = train_img_list
self.test_images = val_img_list
self.train_label = train_label_list
self.test_label = val_label_list
if mode == 'train':
# 读train_images的数据
for img,la in zip(self.train_images, self.train_label):
self.img.append('data/data27098/train/'+img)
self.label.append(la)
else:
# 读test_images的数据
for img,la in zip(self.train_images, self.train_label):
self.img.append('data/data27098/train/'+img)
self.label.append(la)
def load_img(self, image_path):
# 实际使用时使用Pillow相关库进行图片读取即可,这里我们对数据先做个模拟
image = Image.open(image_path).convert('RGB')
return image
def __getitem__(self, index):
"""
步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
"""
image = self.load_img(self.img[index])
label = self.label[index]
# label = paddle.to_tensor(label)
return data_transforms(image), paddle.nn.functional.label_smooth(label)
def __len__(self):
"""
步骤四:实现__len__方法,返回数据集总数目
"""
return len(self.img)
#train_loader
train_dataset = MyDataset(train_img_list=train_image_path_list, val_img_list=val_image_path_list, train_label_list=train_label_list, val_label_list=val_label_list, mode='train')
train_loader = paddle.io.DataLoader(train_dataset, places=paddle.CPUPlace(), batch_size=32, shuffle=True, num_workers=0)
#val_loader
val_dataset = MyDataset(train_img_list=train_image_path_list, val_img_list=val_image_path_list, train_label_list=train_label_list, val_label_list=val_label_list, mode='test')
val_loader = paddle.io.DataLoader(train_dataset, places=paddle.CPUPlace(), batch_size=32, shuffle=True, num_workers=0)
from paddle.vision.models import resnet18
# 模型封装
model_res = resnet18(pretrained=False, num_classes=10, with_pool=True)
model = paddle.Model(model_res)
# 定义优化器
# scheduler = paddle.optimizer.lr.LinearWarmup(
# learning_rate=0.5, warmup_steps=20, start_lr=0, end_lr=0.5, verbose=True)
# optim = paddle.optimizer.SGD(learning_rate=scheduler, parameters=model.parameters())
optim = paddle.optimizer.Adam(learning_rate=3e-4, parameters=model.parameters())
# 配置模型
model.prepare(
optim,
paddle.nn.CrossEntropyLoss(soft_label=True),
Accuracy()
)
# model.load('work/Res2Net50_vd_26w_4s_pretrained.pdparams',skip_mismatch=True)
# 模型训练与评估
model.fit(train_loader,
val_loader,
log_freq=1,
epochs=2,
# callbacks=Callbk(write=write, iters=iters),
verbose=1,
)
The loss value printed in the log is the current step, and the metric is the average value of previous steps.
Epoch 1/2
step 31/31 [==============================] - loss: 1.9038 - acc: 0.3576 - 1s/step
Eval begin...
step 31/31 [==============================] - loss: 2.1364 - acc: 0.4273 - 1s/step
Eval samples: 990
Epoch 2/2
step 31/31 [==============================] - loss: 1.8375 - acc: 0.5172 - 1s/step
Eval begin...
step 31/31 [==============================] - loss: 1.5180 - acc: 0.5232 - 994ms/step
Eval samples: 990
# 保存模型参数
# model.save('Hapi_MyCNN') # save for training
model.save('Hapi_MyCNN1', False) # save for inference
二、环境配置(OpenVINO的安装)
OpenVINO简介
OpenVINO™ 工具套件是用于快速开发应用程序和解决方案,以解决各种任务(包括人类视觉模拟、自动语音识别、自然语言处理和推荐系统等)的综合工具套件。该工具套件基于最新一代的人工神经网络,包括卷积神经网络 (CNN)、递归网络和基于注意力的网络,可扩展跨英特尔® 硬件的计算机视觉和非视觉工作负载,从而最大限度地提高性能。它通过从边缘到云部署的高性能、人工智能和深度学习推理来为应用程序加速。
在最新的OpenVINO2022的版本中,将直接支持将Paddle的模型读取,不再需要将模型转换为ONNX格式,再转换为IR格式,通过与英特尔工程师的对接,直接读取Paddle模型对于推理时间与IR格式无任何差别。下面将为大家讲解如何在Ubuntu18.04中安装OpenVINO并跑通Paddle模型的Demo。
- Windows10下安装OpenVINO
在Windows10下的安装,需要有如下依赖:
- Microsoft Visual Studio* 2019 with MSBuild
- CMake 3.14 or higher 64-bit
- Python 3.6 - 3.8 64-bit
本教程默认依赖已经配置好。首先下载OpenVINO exe安装文件,按照官方文档中的步骤操作即可,需要注意的是,在使用的时候需要激活OpenVINO环境,因为每个人的环境不一样,这里采用了动态激活的方式,即在命令行模式下运行openvino_2021\bin\setupvars.bat命令激活环境,激活环境后即可。
- Linux&IOS下安装OpenVINO
在这两个系统中,本文采用源码编译的方式进行安装。
首先下载OpenVINO源代码,
git clone https://github.com/openvinotoolkit/openvino.git
cd openvino
git submodule update --init --recursive
- For Linux
chmod +x install_build_dependencies.sh
./install_build_dependencies.sh
pip install -r inference-engine/ie_bridges/python/src/requirements-dev.txt
- For Mac
#install Python Dependencies
pip install -r inference-engine/ie_bridges/python/src/requirements-dev.txt
Compile the source code with Python option enabled.
- 编译安装
OPENVINO_BASEDIR = `pwd`
mkdir build
cd build
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${OPENVINO_BASEDIR}/openvino_dist" \
-DPYTHON_EXECUTABLE=$(which python3) \
-DENABLE_MYRIAD=OFF \
-DENABLE_VPU=OFF \
-DENABLE_PYTHON=ON \
-DNGRAPH_PYTHON_BUILD_ENABLE=ON \
..
make -j$(nproc); make install
- 注意:需要更改为自己的路径,nproc许多指定几个线程,编译安装更快,Cmake版本要对应
三、模型验证(无需转ONNX)
本部分使用OpenVINO读取paddle模型,并对其进行验证。在最新的OpenVINO2022的版本中,将直接支持将Paddle的模型读取,不再需要将模型转换为ONNX格式,再转换为IR格式,通过与英特尔工程师的对接,直接读取Paddle模型对于推理时间与IR格式无任何差别,仅仅在初始化的时候会有一些微小区别。
import os
import sys
import numpy as np
import json
import cv2
from openvino.inference_engine import IENetwork, IECore, ExecutableNetwork
from IPython.display import Image
def image_preprocess_mobilenetv3(img_path):
img = cv2.imread(img_path)
img = cv2.resize(img, (224,224))
img = np.transpose(img, [2,0,1]) / 255
img = np.expand_dims(img, 0)
img_mean = np.array([0.485, 0.456,0.406]).reshape((3,1,1))
img_std = np.array([0.229, 0.224, 0.225]).reshape((3,1,1))
img -= img_mean
img /= img_std
return img.astype(np.float32)
# 输出预测结果
def top_k(result, topk=5):
result_detec = np.argmax(result[0])
print(result_detec)
ie = IECore()
net = ie.read_network(r"E:\openvino-paddlepaddle-demo-main\openvino-paddlepaddle-demo-main\model\Hapi_MyCNN1.pdmodel")
# net = ie.read_network(r"E:\openvino-paddlepaddle-demo-main\openvino-paddlepaddle-demo-main\model\test.onnx")
# net = ie.read_network(r"E:\openvino-paddlepaddle-demo-main\openvino-paddlepaddle-demo-main\model\test.xml")
filename = "monkey.jpg"
test_image = image_preprocess_mobilenetv3(filename)
# pdmodel might be dynamic shape, this will reshape based on the input
input_key = list(net.input_info.items())[0][0] # 'inputs'
net.reshape({input_key: test_image.shape})
#load the network on CPU
exec_net = ie.load_network(net, 'CPU')
assert isinstance(exec_net, ExecutableNetwork)
#perform the inference step
output = exec_net.infer({input_key: test_image})
result_ie = list(output.values())
#filter and print the top 5 results
top_k(result_ie)
Image(filename=filename)
四、模型进阶使用(模型转换)
此部分为进阶使用部分,大家可以根据自己的实际情况进行选择。
- 知识点:ONNX格式介绍
Open Neural Network Exchange(ONNX,开放神经网络交换)格式,是一个用于表示深度学习模型的标准,可使模型在不同框架之间进行转移。
也可以对ONNX的模型进行读取:
net = ie.read_network(r"E:\openvino-paddlepaddle-demo-main\openvino-paddlepaddle-demo-main\model\test.onnx")
# paddle2onnx
import os, time
import matplotlib.pyplot as plt
import paddle
from PIL import Image
import numpy as np
import pandas as pd
model_file_path="Hapi_MyCNN1"
model = paddle.jit.load(model_file_path)
paddle.onnx.export(model, 'work')
ONNX2IR
To convert an ONNX* model:
进入 <INSTALL_DIR>/deployment_tools/model_optimizer 文件夹内
使用 mo.py script 对模型进行转换。
python3 mo.py --input_model <INPUT_MODEL>.onnx --output_dir <OUTPUT_MODEL_DIR>
转换之后模型可以通过如下方式读取。
net = ie.read_network(r"E:\openvino-paddlepaddle-demo-main\openvino-paddlepaddle-demo-main\model\test.xml")
注意,路径需要改为自己的模型路径。
总结
本文主要为大家带来了使用OpenVINO部署飞桨模型的一些简单用法。在实际应用中,当模型训练结束后,上线部署时,就会遇到各种问题,比如,模型性能是否满足线上要求,模型如何嵌入到原有工程系统,推理线程的并发路数是否满足,这些问题决定着投入产出比。只有深入且准确的理解深度学习框架,才能更好的完成这些任务,满足上线要求。实OpenVINO是一个Pipeline工具集,同时可以兼容各种开源框架训练好的模型,拥有算法模型上线部署的各种能力,只要掌握了该工具,你可以轻松的将预训练模型在Intel的CPU上快速部署起来。此外,在最新的OpenVINO2022的版本中,将直接支持将Paddle的模型读取,不再需要将模型转换为ONNX格式,再转换为IR格式,通过与英特尔工程师的对接,直接读取Paddle模型对于推理时间与IR格式无任何差别。
参考资料: