本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
🍊作者简介:秃头小苏,致力于用最通俗的语言描述问题🍊专栏推荐:深度学习网络原理与实战
🍊近期目标:写好专栏的每一篇文章
🍊支持小苏:点赞👍🏼、收藏⭐、留言📩
深度学习模型部署篇------从0部署深度学习分类模型(一)
写在前面
Hello,大家好,我是小苏👦🏽👦🏽👦🏽
在过往的博客中,我为大家介绍过物体分类、目标检测、语言分割、缺陷检测等等深度学习模型,感兴趣的可以点击☞☞☞进入我的主页了解详情。🍄🍄🍄今天准备来给大家介绍介绍我们如何来部署我们训练的深度学习模型,当然啦,这篇部署的是分类模型,所以希望大家对物体分类有一定的了解,不熟悉的可以先看一下下面几篇博客:
准备好了吗,让我们一起从0部署深度学习分类模型叭~~~🥂🥂🥂
模型部署概述
首先来谈谈到底什么是模型部署呢?其实模型部署就是将我们训练的深度学习模型应用到生产环境中。这里大家可能就有一个问题了,我们之前训练的分类网络难道不能用于生产环境嘛,为什么还要部署模型呢?🍭🍭🍭在我看来,其实我们之前训练的模型理论上是可以用的,但是一方面在生产环境中往往对模型的速度提出更高的要求,直接使用训练好的模型往往速度慢,难以满足实际需求;另一方面我们训练模型时往往使用不同的框架,如Pytorch、TensorFlow、Caffe等等,这些框架需要特定的环境依赖,配置复杂,往往不适合在实际生产环境中安装。🍚🍚🍚
总之,如果我们直接将我们训练的模型应用于生产环境,会存在环境配置复杂、推理速度较慢的问题 ,因此我们需要对我们的模型进行部署。
下图展示了我们从数据采集到最终应用于生产环境的过程,首先我们会收集数据,然后选择合适的模型对我们的数据进行训练,并进行测试评估。其实到这里都是我们之前掌握的知识,也就是训练我们的模型。当我们训练好我们的模型后,会对我们的模型进行部署,使其能够应用于生产环境当中。🥗🥗🥗
模型部署流程
上文对什么是模型部署以及为什么要进行模型部署进行概述,下面就来谈谈模型部署的通用流程。模型部署常见的流程为"深度学习框架-->中间表示-->推理引擎"
。我们用下图来解释一下这个流程:首先深度学习框架大家应该很熟悉了,如Pytorch、TensorFlow等等,图中以Pytorch为例,我们先使用Pytorch定义了一个网络架构,如resnet,然后将我们采集的数据喂入网络进行训练,得到权重文件;之后我们会将刚刚得到的权重文件转换成一种中间格式,如图中的ONNX【这个最常见】;最后我们会使用推理引擎【有TensorRT、ONNX RUNTIME、OpenVINO等等】把刚刚得到的中间表示转换成特定的格式,之后就可以在硬件平台高效的运行模型。
这里大家只要知道这个流程就行,后面我们会结合代码帮助大家理解。代码部分将按照下图流程进行,即采用Pytorch框架,ONNX中间表示,ONNX RUNTIME推理引擎,在本地PC上进行模型部署。
模型部署代码解析
这节将以部署一个分类模型为例为大家介绍,让我们一起来看看叭~~~🍵🍵🍵
安装环境
先给再大家提个醒,大家在安装环境的时候务必要把系统代理关上喔,不然会报错滴。🌸🌸🌸
-
安装深度学习框架Pytorch
pythonpip3 install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu113
-
安装中间表示ONNX
pythonpip install onnx -i https://pypi.tuna.tsinghua.edu.cn/simple
-
安装推理引擎ONNX Runtime
pythonpip install onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/simple
-
安装其它依赖包
pythonpip install numpy pandas matplotlib tqdm opencv-python pillow -i https://pypi.tuna.tsinghua.edu.cn/simple
配置环境建议大家使用Conda创建一个新的虚拟环境。🌱🌱🌱
-
验证安装是否成功
pythonimport torch import onnx import onnxruntime as ort print('PyTorch 版本', torch.__version__) print('ONNX 版本', onnx.__version__) print('ONNX Runtime 版本', ort.__version__)
安装成功回打印处对应包的版本,我的结果如下:
模型训练
安装好环境后,我们就可以使用Pytorch模型来训练我们的数据了,这个过程我在之前的博客中都有介绍,这里即不赘述了,自己也偷个懒,使用官方在ImageNet上训练的resnet18模型,我们可以直接下载模型权重:
python
# 导入相关包
import torch
from torchvision import models
# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
model = models.resnet18(pretrained=True)
model = model.eval().to(device)
如果我们打印一下model,就会发现其就是resnet18的模型。【太长了,这里就展示一部分了】
将训练模型转换为中间表示ONNX
上一步我们已经得到了一个训练模型model,下面我们将其转换成ONNX中间格式。
首先我们要先构造一个输入图像的Tensor:
python
x = torch.randn(1, 3, 256, 256).to(device)
然后我们就可以使用下面的代码将模型model转换成ONNX格式:
python
with torch.no_grad(): #推理
torch.onnx.export(
model, # 要转换的模型
x, # 模型的任意一组输入
'resnet18_imagenet.onnx', # 导出的 ONNX 文件名
opset_version=11, # ONNX 算子集版本
input_names=['input'], # 输入 Tensor 的名称(自己起名字)
output_names=['output'] # 输出 Tensor 的名称(自己起名字)
)
大家肯定也发现了, torch.onnx.export
就是将模型转换成ONNX格式的函数。其中model表示要转换的pytorch模型;x是模型的任意一组输入,注意这个输入的shape要和模型输入相匹配;'resnet18_imagenet.onnx'是我们要到处的ONNX的文件名;opset_version表示算子集版本,不同的版本会更新一些新的算子; input_names和output_names是输入输出的名称,先给大家打个预防针,这里的输入输出名称要和我们运行推理引擎时的保持一致。🍦🍦🍦
上述代码运行成功后,会生成一个resnet18_imagenet.onnx
文件:
大家注意到我这有个图标了嘛,这是Netron,一个可视化模型的工具,大家也可以下一个试试,可以看看模型的结果,双击点开来看看,如下图所示:
好叭,这个图太长了,我展示出来是想要大家看看这个开头的input
和output
,这两个就是我们在 torch.onnx.export
函数中输入输出的tensor名称,不信的话你在torch.onnx.export
中修改一下,得到的ONNX结果也会发生变化。🍀🍀🍀
运行推理引擎
先来说说推理引擎是干什么的,通俗的说它是用来加速我们模型推理的,方便部署。🍑🍑🍑
上一步我们已经得到了中间表示的.onnx格式的模型,下面我们将来用推理引擎ONNX Runtime来运行一下.onnx模型。
首先我们先导入onnxruntime包,然后用InferenceSession方法来载入onnx模型,获取Onnx Runtime推理器。
python
import onnxruntime
ort_session = onnxruntime.InferenceSession('resnet18_imagenet.onnx')
接着我们需要有一个输入数据,我从网上下载了一张橘子图片作为输入,但是大家思考一下,我们能直接把下载的图片作为输入嘛,显然是不能的,因为我们输入的要求是[3,256,256]大小的图片,因此需要对输入的橘子做一些预处理操作,使其满足输入要求。🥗🥗🥗
python
img_path = 'orange.jpg' #输入图片路径
# 用 pillow 载入
from PIL import Image
img_pil = Image.open(img_path)
可以先来看一下图片和其尺寸:
因为图像是PIL格式的,通道数为RGB格式表示图像有3个通道,即图片的shape为[3,310,163]。因为其不满足输入[3,256,256]的要求,故需要对图像进行预处理,如下:
python
from torchvision import transforms
# 测试集图像预处理-RCTN:缩放裁剪、转 Tensor、归一化
test_transform = transforms.Compose([transforms.Resize(256),
transforms.CenterCrop(256),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
input_img = test_transform(img_pil) #将橘子图像送入预处理函数当中
这个预处理不用我过多介绍了叭,是非常常见的,主要就是把图像变成指定的[3,256,256]大小,并将其变成tensor格式并归一化。【对Totensor和Normalize不熟悉的可以看我的这篇博客】🌸🌸🌸
我们看看归一化之后图片的shape:
已经变成[3,256,256],但是在输入模型之前呢,我们需要加一个batch维度:
python
input_tensor = input_img.unsqueeze(0).numpy()
添加后的shape如下:
到这里我们已经有了输入input_tensor,也构建了一个推理器ort_session
,这样我们就能使用推理器中的run
方法进行模型推理了,代码如下:
python
# ONNX Runtime 输入
ort_inputs = {'input': input_tensor}
# ONNX Runtime 输出
pred_logits = ort_session.run(['output'], ort_inputs)[0]
pred_logits = torch.tensor(pred_logits)
注意这里我们把input_tensor先构建成一个字典,该字典的键input要和 torch.onnx.export
函数中输入名称一致。ort_session.run
方法的参数有两个,第一个是输出张量名称,也就是output,这个也和我们前面 torch.onnx.export
函数中输出名称一致。第二个参数就是我们输入值的字典。我们可以来看看pred_logits
的shape:
这就是将我们的数据放入到模型中推理得到的结果,这个1000是ImageNet的分类类别,也就是模型最后的全连接层神经元个数为1000,如图:
接着我们就可以由这个结果来预测出我们输入的图片属于哪一类物体的概率,这些就和正常的图像分类预测一致了,如下:
python
# 对 logit 分数做 softmax 运算,得到置信度概率
pred_softmax = F.softmax(pred_logits, dim=1)
# 取置信度最高的前 n 个结果
n = 3
top_n = torch.topk(pred_softmax, n)
# 预测类别
pred_ids = top_n.indices.numpy()[0]
# 预测置信度
confs = top_n.values.numpy()[0]
# 载入类别 ID 和 类别名称 对应关系
df = pd.read_csv('imagenet_class_index.csv') 这个要下载ImageNet的类别索引对照表
idx_to_labels = {}
for idx, row in df.iterrows():
#idx_to_labels[row['ID']] = row['class'] # 英文
idx_to_labels[row['ID']] = row['Chinese'] # 中文
# 分别用英文和中文打印预测结果
for i in range(n):
class_name = idx_to_labels[pred_ids[i]] # 获取类别名称
confidence = confs[i] * 100 # 获取置信度
text = '{:<20} {:>.3f}'.format(class_name, confidence)
print(text)
我们可以看下
可以看出橘子的概率还是蛮高滴。🌰🌰🌰
小结
整个分类模型部署的代码到这里就结束啦,我们在来总结一下,主要分为三步:
- 训练分类模型,得到模型权重文件
- 将模型权重文件转换成ONNX中间表示格式
- 使用ONNX Runtime推理引擎进行推理
其实整个过程下来,你会发现针对分类模型来说部署还是比较简单的,大家可以快去试试叭。🍚🍚🍚
总结
好啦,这部分就先为大家介绍到这里了,大家先消化消化。这节我只展示了预测单张图片的案例,也只为大家梳理了模型部署的过程,但是是否使用模型部署,对推理速度的影响并没有体现,我们将在下一节为大家叙述,并且会实现一个自己的分类模型,敬请期待叭~~~🍍🍍🍍
参考文献
如若文章对你有所帮助,那就🛴🛴🛴