一般地,当我们在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