在C++平臺上部署PyTorch模型流程+踩坑實錄

重磅乾貨,第一時間送達

導讀

最近因為工作需要,要把pytorch的模型部署到c++平臺上,基本過程主要參照官網的教學示例,期間發現了不少坑,特此記錄。

1.模型轉換

libtorch不依賴於python,python訓練的模型,需要轉換為script model才能由libtorch載入,並進行推理。在這一步官網提供了兩種方法:

方法一:Tracing

缺點是如果模型中存在控制流比如if-else語句,一組輸入只能遍歷一個分支,這種情況下就沒辦法完整的把模型資訊記錄下來。

方法二:Scripting

直接在Torch指令碼中編寫模型並相應地註釋模型,透過編譯模組,將其轉換為。示例如下:

forward方法會被預設編譯,forward中被呼叫的方法也會按照被呼叫的順序被編譯

如果想要方法不被編譯,可使用

@torch.jit.ignore

(https://pytorch。org/docs/master/generated/torch。jit。ignore。html#torch。jit。ignore)

或者

@torch.jit.unused

(https://pytorch。org/docs/master/generated/torch。jit。unused。html#torch。jit。unused)

在這一步遇到好多坑,主要原因可歸為一下兩點

1. 不支援的操作

TorchScript支援的操作是python的子集,大部分torch中用到的操作都可以找到對應實現,但也存在一些尷尬的不支援操作,詳細列表可見https://pytorch。org/docs/master/jit_unsupported。html#jit-unsupported,下面列一些我自己遇到的操作:

1)引數/返回值不支援可變個數,例如

或者

2)各種iteration操作

eg1。

可以改成:

eg2。

eg3。

3)不支援的語句

eg1。 不支援continue

eg2。 不支援try-catch

eg3。 不支援with語句

4)其他常見op/module

eg1。 torch。autograd。Variable

解決:使用torch。ones/torch。randn等初始化+。float()/。long()等指定資料型別。

eg2。 torch。Tensor/torch。LongTensor etc。

解決:同上

eg3。 requires_grad引數只在torch。tensor中支援,torch。ones/torch。zeros等不可用

eg4。 tensor。numpy()

eg5。 tensor。bool()

解決:tensor。bool()用tensor>0代替

eg6。 self。seg_emb(seg_fea_ids)。to(embeds。device)

解決:需要轉gpu的地方顯示呼叫。cuda()

總之一句話:除了原生python和pytorch以外的庫,比如numpy什麼的能不用就不用,儘量用pytorch的各種API。

2。 指定資料型別

1)屬性,大部分的成員資料型別可以根據值來推斷,空的列表/字典則需要預先指定

2)常量,使用Final關鍵字

3)變數。預設是tensor型別且不可變,所以非tensor型別必須要指明

方法三:Tracing and Scriptin混合

一種是在trace模型中呼叫script,適合模型中只有一小部分需要用到控制流的情況,使用例項如下:

另一種情況是在script module中用tracing生成子模組,對於一些存在script module不支援的python feature的layer,就可以把相關layer封裝起來,用trace記錄相關layer流,其他layer不用修改。使用示例如下:

2.儲存序列化模型

如果上一步的坑都踩完,那麼模型儲存就非常簡單了,只需要呼叫save並傳遞一個檔名即可,需要注意的是如果想要在gpu上訓練模型,在cpu上做inference,一定要在模型save之前轉化,再就是記得呼叫model。eval(),形如

3.C++load訓練好的模型

要在C ++中載入序列化的PyTorch模型,必須依賴於PyTorch C ++ API(也稱為LibTorch)。libtorch的安裝非常簡單,只需要在pytorch官網(https://pytorch。org/)下載對應版本,解壓即可。會得到一個結構如下的資料夾。

然後就可以構建應用程式了,一個簡單的示例目錄結構如下:

example-app。cpp和CMakeLists。txt的示例程式碼分別如下:

至此,就可以執行以下命令從資料夾中構建應用程式啦:

其中/path/to/libtorch是之前下載後的libtorch資料夾所在的路徑。這一步如果順利能夠看到編譯完成100%的提示,下一步執行編譯生成的可執行檔案,會看到“ok”的輸出,可喜可賀!

4.執行Script Module

終於到最後一步啦!下面只需要按照構建輸入傳給模型,執行forward就可以得到輸出啦。一個簡單的示例如下:

前兩行建立一個的向量,並新增單個輸入。 使用建立輸入張量,等效於C ++ API中的。然後,執行的方法,透過呼叫將返回的IValue值轉換為張量。C++對torch的各種操作還是比較友好的,透過torch::或者後加_的方法都可以找到對應實現,例如

最後check一下確保c++端的輸出和pytorch是一致的就大功告成啦~

踩了無數坑,薅掉了無數頭髮,很多東西也是自己一點點摸索的,如果有錯誤歡迎指正!

參考資料

PyTorch C++ API - PyTorch master document

Torch Script - PyTorch master documentation

下載1:OpenCV-Contrib擴充套件模組中文版教程

下載2:Python視覺實戰專案52講

下載3:OpenCV實戰專案20講

交流群