PyTorch Author:louwill Machine Learning Lab 引言 PyTorch作為一款端到端的深度學(xué)習(xí)框架,在1.0版本之后已具備較好的生產(chǎn)環(huán)境部署條件。除了在web端撰寫(xiě)REST API進(jìn)行部署之外(參考),軟件端的部署也有廣泛需求。尤其是最近發(fā)布的1.5版本,提供了更為穩(wěn)定的C++前端API。 工業(yè)界與學(xué)術(shù)界最大的區(qū)別在于工業(yè)界的模型需要落地部署,學(xué)界更多的是關(guān)心模型的精度要求,而不太在意模型的部署性能。一般來(lái)說(shuō),我們用深度學(xué)習(xí)框架訓(xùn)練出一個(gè)模型之后,使用Python就足以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的推理演示了。但在生產(chǎn)環(huán)境下,Python的可移植性和速度性能遠(yuǎn)不如C++。所以對(duì)于深度學(xué)習(xí)算法工程師而言,Python通常用來(lái)做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的方式來(lái)構(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)建TorchScript traced_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ì)算圖屬性:
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) 代碼屬性:
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è)模型都保存到硬盤(pán)上了,并且經(jīng)過(guò)這種方式保存下來(lái)的模型可以加載到其他其他語(yǔ)言環(huán)境中。 TorchScript的另一種實(shí)現(xiàn)方式是Script的方式,可以算是對(duì)Tracing方式的一種補(bǔ)充。當(dāng)模型代碼中含有if或者for-loop等控制流程序時(shí),使用Tracing方式是無(wú)效的,這時(shí)候可以采用Script方式來(lái)進(jìn)行實(shí)現(xiàn)TorchScript。實(shí)現(xiàn)方法跟Tracing差異不大,關(guān)鍵在于把jit.tracing換成jit.script方法,示例如下。
除了Tracing和Script之外,我們也可以混合使用這兩種方式,這里不做詳述。總之,TorchScript為我們提供了一種表示形式,可以對(duì)代碼進(jìn)行編譯器優(yōu)化以提供更有效的執(zhí)行。 libtorch 在Python環(huán)境下對(duì)訓(xùn)練好的模型進(jìn)行轉(zhuǎn)換之后,我們需要C++環(huán)境下的PyTorch來(lái)讀取模型并進(jìn)行編譯部署。這種C++環(huán)境下的PyTorch就是libtorch。因?yàn)閘ibtorch通常用來(lái)作為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 torch import torchvision.models as models vgg16 = 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) 輸出為:
然后切換到C++環(huán)境,編寫(xiě)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ù)編寫(xiě)test.cpp代碼如下:
編譯test.cpp并執(zhí)行,輸出如下。對(duì)比Python環(huán)境下的的運(yùn)行結(jié)果,可以發(fā)現(xiàn)基本是一致的,這也說(shuō)明當(dāng)前環(huán)境下libtorch安裝沒(méi)有問(wèn)題。 ok -0.8297, -35.6048, 12.4823 [Variable[CUDAFloatType]{1,3}] 完整部署流程 通過(guò)前面對(duì)TorchScript和libtorch的描述,其實(shí)我們已經(jīng)基本將PyTorch的C++部署已經(jīng)基本講到了,這里我們?cè)賮?lái)完整的理一下整個(gè)流程?;贑++的PyTorch模型部署流程如下。 第一步: 通過(guò)torch.jit.trace方法將PyTorch模型轉(zhuǎn)換為T(mén)orchScript,示例如下:
第二步: 將TorchScript序列化為.pt模型文件。 traced_script_module.save('traced_resnet_model.pt') 第三步: 在C++中導(dǎo)入序列化之后的TorchScript模型,為此我們需要分別編寫(xiě)包含調(diào)用程序的cpp文件、配置和編譯用的CMakeLists.txt文件。CMakeLists.txt文件示例內(nèi)容如下:
包含模型調(diào)用程序的example-app.cpp示例編碼如下: #include <torch/script.h> // torch頭文件. #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 { // 反序列化:導(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è)文件編寫(xiě)完成之后便可對(duì)其執(zhí)行編譯:
第四步: 給example-app.cpp添加模型推理代碼并執(zhí)行: std::vector<torch::jit::IValue> inputs;inputs.push_back(torch::ones({1, 3, 224, 224})); // 執(zhí)行推理并將模型轉(zhuǎn)化為T(mén)ensor output = module.forward(inputs).toTensor();std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << '\n'; 以上便是C++中部署PyTorch模型的全過(guò)程,相關(guān)教程可參考PyTorch官方: https://pytorch.org/tutorials/ 參考資料: https://pytorch.org/tutorials/ https://pytorch.org/features/ |
|
來(lái)自: aideshizhe0 > 《數(shù)據(jù)挖掘》