<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于C++的PyTorch模型部署

          共 3943字,需瀏覽 8分鐘

           ·

          2022-01-14 21:18


          ??

          點(diǎn)擊上方小白學(xué)視覺”,選擇加"星標(biāo)"或“置頂

          重磅干貨,第一時(shí)間送達(dá)

          引言

          ?????

          ?????PyTorch作為一款端到端的深度學(xué)習(xí)框架,在1.0版本之后已具備較好的生產(chǎn)環(huán)境部署條件。除了在web端撰寫REST API進(jìn)行部署之外(參考),軟件端的部署也有廣泛需求。尤其是最近發(fā)布的1.5版本,提供了更為穩(wěn)定的C++前端API。


          ???? 工業(yè)界與學(xué)術(shù)界最大的區(qū)別在于工業(yè)界的模型需要落地部署,學(xué)界更多的是關(guān)心模型的精度要求,而不太在意模型的部署性能。一般來說,我們用深度學(xué)習(xí)框架訓(xùn)練出一個(gè)模型之后,使用Python就足以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的推理演示了。但在生產(chǎn)環(huán)境下,Python的可移植性和速度性能遠(yuǎn)不如C++。所以對(duì)于深度學(xué)習(xí)算法工程師而言,Python通常用來做idea的快速實(shí)現(xiàn)以及模型訓(xùn)練,而用C++作為模型的生產(chǎn)工具。目前PyTorch能夠完美的將二者結(jié)合在一起。實(shí)現(xiàn)PyTorch模型部署的核心技術(shù)組件就是TorchScript和libtorch。


          ???? 所以基于PyTorch的深度學(xué)習(xí)算法工程化流程大體如下圖所示:


          TorchScript

          ???? TorchScript可以視為PyTorch模型的一種中間表示,TorchScript表示的PyTorch模型可以直接在C++中進(jìn)行讀取。PyTorch在1.0版本之后都可以使用TorchScript的方式來構(gòu)建序列化的模型。TorchScript提供了Tracing和Script兩種應(yīng)用方式。


          ???? Tracing應(yīng)用示例如下:

          class?MyModel(torch.nn.Module):????def?__init__(self):????????super(MyModel,?self).__init__()        self.linear = torch.nn.Linear(4, 4)
          ????def?forward(self,?x,?h):????????new_h?=?torch.tanh(self.linear(x)?+?h) return new_h, new_h
          #?創(chuàng)建模型實(shí)例?my_model?=?MyModel()#?輸入示例x,?h?=?torch.rand(3,?4),?torch.rand(3,?4)#?torch.jit.trace方法對(duì)模型構(gòu)建TorchScripttraced_model?=?torch.jit.trace(my_model,?(x,?h))#?保存轉(zhuǎn)換后的模型traced_model.save('model.pt')


          ???? 在這段代碼中,我們先是定義了一個(gè)簡(jiǎn)單模型并創(chuàng)建模型實(shí)例,然后給定輸入示例,Tracing方法最關(guān)鍵的一步在于使用torch.jit.trace方法對(duì)模型進(jìn)行TorchScript轉(zhuǎn)化。我們可以獲得轉(zhuǎn)化后的traced_model對(duì)象獲得其計(jì)算圖屬性和代碼屬性。計(jì)算圖屬性:

          print(traced_model.graph)

          graph(%self.1?:?__torch__.torch.nn.modules.module.___torch_mangle_1.Module,??????%input?:?Float(3,?4),??????%h?:?Float(3,?4)):??%19?:?__torch__.torch.nn.modules.module.Module?=?prim::GetAttr[name="linear"](%self.1)??%21?:?Tensor?=?prim::CallMethod[name="forward"](%19,?%input)??%12?:?int?=?prim::Constant[value=1]()?#?/var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0??%13?:?Float(3,?4)?=?aten::add(%21,?%h,?%12)?#?/var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0??%14?:?Float(3,?4)?=?aten::tanh(%13)?#?/var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0??%15?:?(Float(3,?4),?Float(3,?4))?=?prim::TupleConstruct(%14,?%14)  return (%15)


          代碼屬性:

          print(traced_cell.code)

          def?forward(self,????input:?Tensor,????h:?Tensor)?->?Tuple[Tensor,?Tensor]:??_0?=?torch.add((self.linear).forward(input,?),?h,?alpha=1)??_1?=?torch.tanh(_0) return (_1, _1)


          ???? 這樣我們就可以將整個(gè)模型都保存到硬盤上了,并且經(jīng)過這種方式保存下來的模型可以加載到其他其他語(yǔ)言環(huán)境中。


          ???? TorchScript的另一種實(shí)現(xiàn)方式是Script的方式,可以算是對(duì)Tracing方式的一種補(bǔ)充。當(dāng)模型代碼中含有if或者for-loop等控制流程序時(shí),使用Tracing方式是無效的,這時(shí)候可以采用Script方式來進(jìn)行實(shí)現(xiàn)TorchScript。實(shí)現(xiàn)方法跟Tracing差異不大,關(guān)鍵在于把jit.tracing換成jit.script方法,示例如下。

          scripted_model?=?torch.jit.script(MyModel)scripted_model.save('model.pt')


          ???? 除了Tracing和Script之外,我們也可以混合使用這兩種方式,這里不做詳述。總之,TorchScript為我們提供了一種表示形式,可以對(duì)代碼進(jìn)行編譯器優(yōu)化以提供更有效的執(zhí)行。


          libtorch

          ???? 在Python環(huán)境下對(duì)訓(xùn)練好的模型進(jìn)行轉(zhuǎn)換之后,我們需要C++環(huán)境下的PyTorch來讀取模型并進(jìn)行編譯部署。這種C++環(huán)境下的PyTorch就是libtorch。因?yàn)閘ibtorch通常用來作為PyTorch模型的C++接口,libtorch也稱之為PyTorch的C++前端。


          ???? 我們可以直接從PyTorch官網(wǎng)下載已經(jīng)編譯好的libtorch安裝包,當(dāng)然也可以下載源碼自行進(jìn)行編譯。這里需要注意的是,安裝的libtorch版本要與Python環(huán)境下的PyTorch版本一致。

          ???? 安裝好libtorch后可簡(jiǎn)單測(cè)試下是否正常。比如我們用TorchScript轉(zhuǎn)換一個(gè)預(yù)訓(xùn)練模型,示例如下:

          import?torchimport?torchvision.models?as?modelsvgg16?=?models.vgg16()example?=?torch.rand(1,?3,?224,?224).cuda()?model?=?model.eval()traced_script_module?=?torch.jit.trace(model,?example)output?=?traced_script_module(torch.ones(1,3,224,224).cuda())traced_script_module.save('vgg16-trace.pt')print(output)

          輸出為:

          tensor([[?-0.8301,?-35.6095,?12.4716]],?device='cuda:0',        grad_fn=<AddBackward0>)


          ???? 然后切換到C++環(huán)境,編寫CmakeLists文件如下:

          cmake_minimum_required(VERSION?3.0.0?FATAL_ERROR)project(libtorch_test)find_package(Torch?REQUIRED)message(STATUS?"Pytorch?status:")message(STATUS?"libraries:?${TORCH_LIBRARIES}")add_executable(libtorch_test?test.cpp)target_link_libraries(libtorch_test?"${TORCH_LIBRARIES}")set_property(TARGET libtorch_test PROPERTY CXX_STANDARD 11)


          ???? 繼續(xù)編寫test.cpp代碼如下:

          #include?"torch/script.h"#include?"torch/torch.h"#include?#include?using?namespace?std;
          int?main(int?argc,?const?char*?argv[]){????if?(argc?!=?2)?{????????std::cerr?<"usage:?example-app?\n";????????return?-1; }
          ????//?讀取TorchScript轉(zhuǎn)化后的模型????torch::jit::script::Module?module;????try?{????????module?=?torch::jit::load(argv[1]); }
          ????catch?(const?c10::Error&?e)?{????????std::cerr?<"error?loading?the?model\n";????????return?-1; }
          ????module->to(at::kCUDA);????assert(module?!=?nullptr); std::cout << "ok\n";
          ????//?構(gòu)建示例輸入????std::vector?inputs; inputs.push_back(torch::ones({1, 3, 224, 224}).to(at::kCUDA));
          ????//?執(zhí)行模型推理并輸出tensor????at::Tensor?output?=?module->forward(inputs).toTensor(); std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << '\n';}


          ???? 編譯test.cpp并執(zhí)行,輸出如下。對(duì)比Python環(huán)境下的的運(yùn)行結(jié)果,可以發(fā)現(xiàn)基本是一致的,這也說明當(dāng)前環(huán)境下libtorch安裝沒有問題。

          ok-0.8297,?-35.6048,?12.4823[Variable[CUDAFloatType]{1,3}]

          完整部署流程

          ?????

          ????通過前面對(duì)TorchScript和libtorch的描述,其實(shí)我們已經(jīng)基本將PyTorch的C++部署已經(jīng)基本講到了,這里我們?cè)賮硗暾睦硪幌抡麄€(gè)流程。基于C++的PyTorch模型部署流程如下。


          第一步:

          ???? 通過torch.jit.trace方法將PyTorch模型轉(zhuǎn)換為TorchScript,示例如下:

          import?torchfrom?torchvision.models?import?resnet18model?=resnet18()example?=?torch.rand(1,?3,?224,?224)tracing.traced_script_module = torch.jit.trace(model, example)


          第二步:

          ???? 將TorchScript序列化為.pt模型文件。

          traced_script_module.save("traced_resnet_model.pt")


          第三步:

          ???? 在C++中導(dǎo)入序列化之后的TorchScript模型,為此我們需要分別編寫包含調(diào)用程序的cpp文件、配置和編譯用的CMakeLists.txt文件。CMakeLists.txt文件示例內(nèi)容如下:

          cmake_minimum_required(VERSION?3.0?FATAL_ERROR)project(custom_ops)find_package(Torch?REQUIRED)add_executable(example-app?example-app.cpp)target_link_libraries(example-app?"${TORCH_LIBRARIES}")set_property(TARGET example-app PROPERTY CXX_STANDARD 14)


          ???? 包含模型調(diào)用程序的example-app.cpp示例編碼如下:

          #include??//?torch頭文件.#include #include 
          int?main(int?argc,?const?char*?argv[])?{??if?(argc?!=?2)?{????std::cerr?<"usage:?example-app?\n";????return?-1; }
          ??torch::jit::script::Module?module;??try?{????//?反序列化:導(dǎo)入TorchScript模型????module?=?torch::jit::load(argv[1]); }
          ??catch?(const?c10::Error&?e)?{????std::cerr?<"error?loading?the?model\n";????return?-1; } std::cout << "ok\n";}


          ???? 兩個(gè)文件編寫完成之后便可對(duì)其執(zhí)行編譯:

          mkdir?example_testcd?example_testcmake?-DCMAKE_PREFIX_PATH=/path/to/libtorch?..cmake --example_test . --config Release


          第四步:

          給example-app.cpp添加模型推理代碼并執(zhí)行:

          std::vector?inputs;inputs.push_back(torch::ones({1,?3,?224,?224}));//?執(zhí)行推理并將模型轉(zhuǎn)化為Tensoroutput = module.forward(inputs).toTensor();std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << '\n';


          ???? 以上便是C++中部署PyTorch模型的全過程,相關(guān)教程可參考PyTorch官方:

          https://pytorch.org/tutorials/


          下載1:OpenCV-Contrib擴(kuò)展模塊中文版教程
          在「小白學(xué)視覺」公眾號(hào)后臺(tái)回復(fù):擴(kuò)展模塊中文教程即可下載全網(wǎng)第一份OpenCV擴(kuò)展模塊教程中文版,涵蓋擴(kuò)展模塊安裝、SFM算法、立體視覺、目標(biāo)跟蹤、生物視覺、超分辨率處理等二十多章內(nèi)容。

          下載2:Python視覺實(shí)戰(zhàn)項(xiàng)目52講
          小白學(xué)視覺公眾號(hào)后臺(tái)回復(fù):Python視覺實(shí)戰(zhàn)項(xiàng)目即可下載包括圖像分割、口罩檢測(cè)、車道線檢測(cè)、車輛計(jì)數(shù)、添加眼線、車牌識(shí)別、字符識(shí)別、情緒檢測(cè)、文本內(nèi)容提取、面部識(shí)別等31個(gè)視覺實(shí)戰(zhàn)項(xiàng)目,助力快速學(xué)校計(jì)算機(jī)視覺。

          下載3:OpenCV實(shí)戰(zhàn)項(xiàng)目20講
          小白學(xué)視覺公眾號(hào)后臺(tái)回復(fù):OpenCV實(shí)戰(zhàn)項(xiàng)目20講即可下載含有20個(gè)基于OpenCV實(shí)現(xiàn)20個(gè)實(shí)戰(zhàn)項(xiàng)目,實(shí)現(xiàn)OpenCV學(xué)習(xí)進(jìn)階。

          交流群


          歡迎加入公眾號(hào)讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器自動(dòng)駕駛、計(jì)算攝影、檢測(cè)、分割、識(shí)別、醫(yī)學(xué)影像、GAN算法競(jìng)賽等微信群(以后會(huì)逐漸細(xì)分),請(qǐng)掃描下面微信號(hào)加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三?+?上海交大?+?視覺SLAM“。請(qǐng)按照格式備注,否則不予通過。添加成功后會(huì)根據(jù)研究方向邀請(qǐng)進(jìn)入相關(guān)微信群。請(qǐng)勿在群內(nèi)發(fā)送廣告,否則會(huì)請(qǐng)出群,謝謝理解~


          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91嫩草欧美久久久九九九 | 人人爱人人模 | 国产毛片18水真多18精品 | 天天干女人网 | 欧美三级在线 |