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