深度学习模型怎么部署?

我们在构建完成深度学习的模型之后,往往需要将模型部署到我们需要的设备上,例如:电脑、手机、边缘设备等

一、通常部署的设备

  • PC/服务端:pytorch/C++
  • 手机端(Android/IOS):NCNN框架(CPU推理)、tflite
  • IOT设备:NVIDIA JETSON 系列(Linux,tensorRT)、瑞芯微(Android)、海思(鸿蒙)
  • HTTP部署:Flask + pytorch/C++

模型推理框架之间的转换(pytorch / tensorflow):onnx

二、模型部署方案

一个模型格式转换的网站:一键转换 Caffe, ONNX, TensorFlow 到 NCNN, MNN, Tengine (convertmodel.com)

(1) torchscript ——让其他语言调用pytorch模型。

那么python语言编写的代码怎么被其它语言调用呢?我们需要将模型转换成torchScript的格式。这里以C++为例,官方教程:Loading a TorchScript Model in C++ — PyTorch Tutorials 2.0.1+cu117 documentation

网站中有详细的教程(这里仅将代码搬运过来):

  • step1: 打包模型
import torch
import torchvision

# An instance of your model.
model = torchvision.models.resnet18()

# An example input you would normally provide to your model's forward() method.
example = torch.rand(1, 3, 224, 224)

# Use torch.jit.trace to generate a torch.jit.ScriptModule via tracing.
traced_script_module = torch.jit.trace(model, example)

打包自定义模型

class MyModule(torch.nn.Module):
    def __init__(self, N, M):
        super(MyModule, self).__init__()
        self.weight = torch.nn.Parameter(torch.rand(N, M))

    def forward(self, input):
        if input.sum() > 0:
          output = self.weight.mv(input)
        else:
          output = self.weight + input
        return output

my_module = MyModule(10,20)
sm = torch.jit.script(my_module)
  • step2:保存模型文件
traced_script_module.save("traced_resnet_model.pt")
  • step3:在C++程序中使用模型
#include <torch/script.h> // One-stop header.

#include <iostream>
#include <memory>

int main(int argc, const char* argv[]) {
  if (argc != 2) {
    std::cerr << "usage: example-app <path-to-exported-script-module>\n";
    return -1;
  }


  torch::jit::script::Module module;
  try {
    // Deserialize the ScriptModule from a file using torch::jit::load().
    module = torch::jit::load(argv[1]);
  }
  catch (const c10::Error& e) {
    std::cerr << "error loading the model\n";
    return -1;
  }

  std::cout << "ok\n";
}
(2) 将模型部署到安卓或IOS设备上

官网给出的解决方案:Android | PyTorch
iOS | PyTorch

  • step1: 模型准备
import torch
import torchvision
from torch.utils.mobile_optimizer import optimize_for_mobile

model = torchvision.models.mobilenet_v2(pretrained=True)
model.eval()
example = torch.rand(1, 3, 224, 224)
traced_script_module = torch.jit.trace(model, example)
traced_script_module_optimized = optimize_for_mobile(traced_script_module)
traced_script_module_optimized._save_for_lite_interpreter("app/src/main/assets/model.ptl")
  • step2: 从Git上下载安卓示例APP
git clone https://github.com/pytorch/android-demo-app.git
cd HelloWorldApp

详细教程见官网

(3)ncnn 框架

ncnn是腾讯开发的一个为手机端极致优化的高性能神经网络前向计算框架,无第三方依赖,跨平台。ncnn目前已在腾讯多款应用中使用,如 QQ,Qzone,微信,天天P图等。ncnn主要基于C++和caffe,ncnn项目地址见:https://github.com/Tencent/ncnn。ncnn是使用CPU进行推理,其支持超多操作平台、并支持大部分的CNN网络,非常值得试试。

(4)onnx

ONNX 是一种基于numpy的用于表示机器学习的开放格式 模型。ONNX 定义了一组通用运算符(机器学习和深度学习模型的构建基块)和通用文件格式,使 AI 开发人员能够使用具有各种框架、工具、运行时和编译器的模型。其支持大部分神经网络框架。

  • 我们使用onnx进行不同模型之间的转换时,模型的精度会有一定的下降。

如何将pytorch打包成onnx格式?
首先需要安装onnx和onnxruntime
onnx安装命令:pip install onnx
onnx-cpu安装命令:pip install onnxruntime
onnx-gpu安装命令:pip install onnxruntime-gpu这里需要注意版本问题,官方参考:NVIDIA - CUDA | onnxruntime

转换仅需一行代码:

torch.onnx.export(model, args, f, export_params=True, verbose=False, input_names=None, 
output_names=None,do_constant_folding=True,dynamic_axes=None,opset_version=9)
  • 常用参数:
    1. model:torch.nn.model 要导出的模型
    2. args:tuple or tensor 模型的输入参数。注意tuple的最后参数为dict要小心,详见pytorch文档。输入参数只需满足shape正确,为什么要输入参数呢?因为后面torch.jit.trace要用到,先按下不表。
    3. f:file object or string 转换输出的模型的位置,如'yolov4.onnx'
    4. export_params:bool,default=True,true表示导出trained model,false则untrained model。默认即可
    5. verbose:bool,default=False,true表示打印调试信息
    6. input_names:list of string,default=None,指定输入节点名称
    7. output_names:list of string,default=None,指定输出节点名称
    8. do_constant_folding:bool,default=True,是否使用常量折叠,默认即可
    9. dynamic_axes:dict<string, dict<int, string>> or dict<string, list(int)>,default=None,有时模型的输入输出是可变的,如RNN,或者输入输出图片的batch是可变的,这时我们通过dynamic_axes来指定输入tensor的哪些参数可变。
    10. opset_version:int,default=9,指定onnx的opset版本,版本过低的话,不支持upsample等操作。

举个例子:

import torch 
from torch import nn 
from torchvision.models import resnet18

model = resnet18()
model.eval()

x = torch.randn((1,3,224,224),  requires_grad=True)

input_name = ["input"]
output_name = ["output"]

torch.onnx.export(model, x, "./resnet18.onnx", input_names=input_name, output_names=output_name, verbose=False)

转换完之后,检查并运行onnx模型。

import onnxruntime as ort 
import torch 
import onnx 
import numpy as np 

# 加载模型
model = onnx.load("./resnet18.onnx")
# 检查模型
onnx.checker.check_model(model)

# 1. 开启会话
session = ort.InferenceSession("./resnet18.onnx")
x = np.random.randn(1,3,224,224).astype(np.float32) # 输入的类型必须是numpy float32 

outputs = session.run(None, input_feed={"input" : x})
print(len(outputs[0][0]))

需要进行精度验证,如果精度不达标,则可能需要进行精度对齐:

import torch 
from torch import nn 
from torchvision.models import resnet18
from torchvision.models import ResNet18_Weights
import onnxruntime as ort 
import onnx 
import numpy as np 

# 构建模型,并载入权重向量
model1 = resnet18(weights = ResNet18_Weights.IMAGENET1K_V1)
model1.eval()

x1 = torch.randn((1,3,224,224),  requires_grad=True)
x2 = x1.detach().numpy().astype(np.float32)

input_name = ["input"]
output_name = ["output"]

# 保存模型
torch.onnx.export(model1, x1, "./resnet18.onnx", input_names=input_name, output_names=output_name, verbose=False)

# 加载模型
model2 = onnx.load("./resnet18.onnx")
# 检查模型
onnx.checker.check_model(model2)

# 开启会话
session = ort.InferenceSession("./resnet18.onnx")

output1 = model1(x1)
output2 = session.run(None, input_feed={"input" : x2})
print("torch: ", output1[0][0].item())
print("onnx: ", output2[0][0][0])
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容