深度學(xué)習(xí)模型怎么部署?

我們在構(gòu)建完成深度學(xué)習(xí)的模型之后逗堵,往往需要將模型部署到我們需要的設(shè)備上力喷,例如:電腦刽漂、手機(jī)、邊緣設(shè)備等

一弟孟、通常部署的設(shè)備

  • PC/服務(wù)端:pytorch/C++
  • 手機(jī)端(Android/IOS):NCNN框架(CPU推理)贝咙、tflite
  • IOT設(shè)備:NVIDIA JETSON 系列(Linux,tensorRT)拂募、瑞芯微(Android)庭猩、海思(鴻蒙)
  • HTTP部署:Flask + pytorch/C++

模型推理框架之間的轉(zhuǎn)換(pytorch / tensorflow):onnx

二乌奇、模型部署方案

一個模型格式轉(zhuǎn)換的網(wǎng)站:一鍵轉(zhuǎn)換 Caffe, ONNX, TensorFlow 到 NCNN, MNN, Tengine (convertmodel.com)

(1) torchscript ——讓其他語言調(diào)用pytorch模型。

那么python語言編寫的代碼怎么被其它語言調(diào)用呢眯娱?我們需要將模型轉(zhuǎn)換成torchScript的格式礁苗。這里以C++為例,官方教程:Loading a TorchScript Model in C++ — PyTorch Tutorials 2.0.1+cu117 documentation

網(wǎng)站中有詳細(xì)的教程(這里僅將代碼搬運過來):

  • 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設(shè)備上

官網(wǎng)給出的解決方案:Android | PyTorch
徙缴、iOS | PyTorch

  • step1: 模型準(zhǔn)備
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

詳細(xì)教程見官網(wǎng)

(3)ncnn 框架

ncnn是騰訊開發(fā)的一個為手機(jī)端極致優(yōu)化的高性能神經(jīng)網(wǎng)絡(luò)前向計算框架试伙,無第三方依賴,跨平臺于样。ncnn目前已在騰訊多款應(yīng)用中使用疏叨,如 QQ,Qzone穿剖,微信蚤蔓,天天P圖等。ncnn主要基于C++和caffe糊余,ncnn項目地址見:https://github.com/Tencent/ncnn秀又。ncnn是使用CPU進(jìn)行推理,其支持超多操作平臺贬芥、并支持大部分的CNN網(wǎng)絡(luò)吐辙,非常值得試試。

(4)onnx

ONNX 是一種基于numpy的用于表示機(jī)器學(xué)習(xí)的開放格式 模型蘸劈。ONNX 定義了一組通用運算符(機(jī)器學(xué)習(xí)和深度學(xué)習(xí)模型的構(gòu)建基塊)和通用文件格式昏苏,使 AI 開發(fā)人員能夠使用具有各種框架、工具威沫、運行時和編譯器的模型贤惯。其支持大部分神經(jīng)網(wǎng)絡(luò)框架。

  • 我們使用onnx進(jìn)行不同模型之間的轉(zhuǎn)換時棒掠,模型的精度會有一定的下降孵构。

如何將pytorch打包成onnx格式?
首先需要安裝onnx和onnxruntime
onnx安裝命令:pip install onnx
onnx-cpu安裝命令:pip install onnxruntime
onnx-gpu安裝命令:pip install onnxruntime-gpu這里需要注意版本問題句柠,官方參考:NVIDIA - CUDA | onnxruntime

轉(zhuǎn)換僅需一行代碼:

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)
  • 常用參數(shù):
    1. model:torch.nn.model 要導(dǎo)出的模型
    2. args:tuple or tensor 模型的輸入?yún)?shù)浦译。注意tuple的最后參數(shù)為dict要小心,詳見pytorch文檔溯职。輸入?yún)?shù)只需滿足shape正確,為什么要輸入?yún)?shù)呢帽哑?因為后面torch.jit.trace要用到谜酒,先按下不表。
    3. f:file object or string 轉(zhuǎn)換輸出的模型的位置妻枕,如'yolov4.onnx'
    4. export_params:bool,default=True僻族,true表示導(dǎo)出trained model粘驰,false則untrained model。默認(rèn)即可
    5. verbose:bool,default=False述么,true表示打印調(diào)試信息
    6. input_names:list of string蝌数,default=None,指定輸入節(jié)點名稱
    7. output_names:list of string度秘,default=None顶伞,指定輸出節(jié)點名稱
    8. do_constant_folding:bool,default=True,是否使用常量折疊剑梳,默認(rèn)即可
    9. dynamic_axes:dict<string, dict<int, string>> or dict<string, list(int)>,default=None唆貌,有時模型的輸入輸出是可變的,如RNN垢乙,或者輸入輸出圖片的batch是可變的锨咙,這時我們通過dynamic_axes來指定輸入tensor的哪些參數(shù)可變。
    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)

轉(zhuǎn)換完之后蓖宦,檢查并運行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]))

需要進(jìn)行精度驗證,如果精度不達(dá)標(biāo)油猫,則可能需要進(jìn)行精度對齊:

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 

# 構(gòu)建模型稠茂,并載入權(quán)重向量
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])
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市情妖,隨后出現(xiàn)的幾起案子睬关,更是在濱河造成了極大的恐慌,老刑警劉巖毡证,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件电爹,死亡現(xiàn)場離奇詭異,居然都是意外死亡料睛,警方通過查閱死者的電腦和手機(jī)丐箩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恤煞,“玉大人屎勘,你說我怎么就攤上這事【影牵” “怎么了概漱?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長喜喂。 經(jīng)常有香客問我瓤摧,道長竿裂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任照弥,我火速辦了婚禮腻异,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘这揣。我一直安慰自己悔常,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布曾沈。 她就那樣靜靜地躺著这嚣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪塞俱。 梳的紋絲不亂的頭發(fā)上姐帚,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音障涯,去河邊找鬼罐旗。 笑死,一個胖子當(dāng)著我的面吹牛唯蝶,可吹牛的內(nèi)容都是我干的九秀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粘我,長吁一口氣:“原來是場噩夢啊……” “哼鼓蜒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起征字,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤都弹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后匙姜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畅厢,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年氮昧,在試婚紗的時候發(fā)現(xiàn)自己被綠了框杜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡袖肥,死狀恐怖咪辱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情昭伸,我是刑警寧澤梧乘,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站庐杨,受9級特大地震影響选调,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灵份,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一仁堪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧填渠,春花似錦弦聂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至枪眉,卻和暖如春捺檬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贸铜。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工堡纬, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒿秦。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓烤镐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親棍鳖。 傳聞我的和親對象是個殘疾皇子炮叶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容