一般地,當我們在python框架(eg:pytorch,tensorflow等)中訓練好模型,需要部署到C/C++環境,有以下方案:
- CPU方案:Libtorch、OpenCV-DNN、OpenVINO、ONNX(有個runtime可以調)
- GPU方案:TensorRT、OpenCV-DNN(需要重新編譯,帶上CUDA)
注:OpenCV、OpenVINO都是intel的開源框架庫,OpenCV的DNN模塊其實調用的也就是OpenVINO,另外OpenvVINO在硬件加速方面使用了intel自家CPU的集成顯卡。
模型部署的時候,我們僅需要實現數據處理、前向傳播就行,不需要去管反向傳播。網絡架構是一定要讀懂的,舉個例子:
假設現有大小為32 x 32
的圖片樣本,輸入樣本的channels
為1,該圖片可能屬於10個類中的某一類。CNN框架定義如下:
1 class CNN(nn.Module): 2 def __init__(self): 3 nn.Model.__init__(self) 4 5 self.conv1 = nn.Conv2d(1, 6, 5) # 輸入通道數為1,輸出通道數為6 6 self.conv2 = nn.Conv2d(6, 16, 5) # 輸入通道數為6,輸出通道數為16 7 self.fc1 = nn.Linear(5 * 5 * 16, 120) 8 self.fc2 = nn.Linear(120, 84) 9 self.fc3 = nn.Linear(84, 10) 10 11 def forward(self,x): 12 # 輸入x -> conv1 -> relu -> 2x2窗口的最大池化 13 x = self.conv1(x) 14 x = F.relu(x) 15 x = F.max_pool2d(x, 2) 16 # 輸入x -> conv2 -> relu -> 2x2窗口的最大池化 17 x = self.conv2(x) 18 x = F.relu(x) 19 x = F.max_pool2d(x, 2) 20 # view函數將張量x變形成一維向量形式,總特征數不變,為全連接層做准備 21 x = x.view(x.size()[0], -1) 22 x = F.relu(self.fc1(x)) 23 x = F.relu(self.fc2(x)) 24 x = self.fc3(x) 25 return x
要用libtorch實現上述python代碼,首先得弄個清楚網絡中每一層的數據維度變換,如下圖,我都注釋了:
1 # 詳細注釋 2 # reference:https://www.jianshu.com/p/45a26d278473 3 #************************************************************************************************ 4 class CNN(nn.Module): 5 def __init__(self): 6 nn.Model.__init__(self) 7 8 self.conv1 = nn.Conv2d(1, 6, 5) # 輸入通道數為1,輸出通道數為6 9 self.conv2 = nn.Conv2d(6, 16, 5) # 輸入通道數為6,輸出通道數為16 10 self.fc1 = nn.Linear(5 * 5 * 16, 120) 11 self.fc2 = nn.Linear(120, 84) 12 self.fc3 = nn.Linear(84, 10) 13 14 #網絡整體結構:[conv + relu + pooling] * 2 + FC * 3 15 #原始輸入樣本的大小:32 x 32 x 1 16 def forward(self,x): 17 # 第一次卷積:使用6個大小為5 x 5的卷積核,故卷積核的規模為(5 x 5) x 6; 18 #卷積操作的stride參數默認值為1 x 1,32 - 5 + 1 = 28,並且使用ReLU對 19 #第一次卷積后的結果進行非線性處理,輸出大小為28 x 28 x 6; 20 x = self.conv1(x) 21 22 # 第一次卷積后池化:kernel_size為2 x 2,輸出大小變為14 x 14 x 6; 23 x = F.relu(x) 24 x = F.max_pool2d(x, 2) 25 26 # 第二次卷積:使用16個卷積核,故卷積核的規模為(5 x 5 x 6) x 16; 27 # 使用ReLU對第二次卷積后的結果進行非線性處理,14 - 5 + 1 = 10, 28 # 故輸出大小為10 x 10 x 16; 29 x = self.conv2(x) 30 31 # 第二次卷積后池化:kernel_size同樣為2 x 2,輸出大小變為5 x 5 x 16; 32 x = F.relu(x) 33 x = F.max_pool2d(x, 2) 34 35 # view函數將張量x變形成一維向量形式,總特征數不變,為全連接層做准備 36 x = x.view(x.size()[0], -1) 37 38 #第一次全連接:將上一步得到的結果鋪平成一維向量形式,5 x 5 x 16 = 400, 39 #即輸入大小為400 x 1,W大小為120 x 400,輸出大小為120 x 1; 40 x = F.relu(self.fc1(x)) 41 42 # 第二次全連接,W大小為84 x 120,輸入大小為120 x 1,輸出大小為84 x 1; 43 x = F.relu(self.fc2(x)) 44 45 # 第三次全連接:W大小為10 x 84,輸入大小為84 x 1,輸出大小為10 x 1,即分別預測為10 46 x = self.fc3(x) 47 return x
網絡結構圖如下,
對於其他網絡,在訓練完畢之后,一般的,也可以使用Netron可視化一下。
reference:
https://www.jianshu.com/p/45a26d278473