重磅乾貨,第一時間送達
導讀
最近因為工作需要,要把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講
交流群