赞
踩
最近在搞移动端的模型部署包括coreML IOS端的部署,tf-Lite Android端的部署。但是自己并不是前端开发工程师,所以移动端的代码只是修修改改,主要还是如何将模型转换到对应的格式并可以运行。
这篇主要说下YOLOv5s在Android端的部署,模型是官方最新的的pytorch转ONNX,然后自己再转tf-lite,中间需要自己去修改NMS相关部分的代码,遇到一堆坑。
效果录像
工程链接:https://github.com/ultralytics/yolov5
这一步主要是转onnx时确保pytorch模型不要有onnx不支持的算子,如果只是卷积操作基本问题不大,但是还是会有奇奇怪怪的问题需要去克服。
这一步主要就是几行代码问题,就用export.py 即可. 需要注意的是
model.model[-1].export = False 这里要改成 False 否则无法导出,主要还是版本问题,tf新版本有些是可以,但是会导致一个AddV2算子无法支持新版本只有tf2.1.1支持。但是tf2.1.1版本也会有其他问题需要改比如NMSV3不支持需要改成V4.以及算法中不要用类似省略号去张量的操作[…,:,:,3],tf-lite会报错,直接用:,:,:代替。还有tf-lite不支持超过4d维度的张量操作。需要修改yolo.py文件模型代码如下。实际上这里就是模型输出后NMS前导工作。
- def forward(self, x):
-
- # x = x.copy() # for profiling
-
- z = [] # inference output
-
- self.training |= self.export
-
- for i in range(self.nl):
-
- x[i] = self.m[i](x[i]) # conv
-
- bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
-
- if not self.training: # inference
-
- x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
-
- if self.grid[i].shape[2:4] != x[i].shape[2:4]:
-
- self.grid[i] = self._make_grid(nx, ny).to(x[i].device)
-
- y = x[i].sigmoid()
-
- y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy
-
- y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
-
- z.append(y.view(bs, -1, self.no))
-
- return x if self.training else (torch.cat(z, 1), x)
这个倒是很简单一行命令:onnx模型地址, 导出savedmodel位置
onnx-tf convert -i ./models/5.onnx -o ./model_new
注:如果你用的老版本onnx-tf 导出模型可能是冻结模型pb,我觉得还是用savedmodel最好,冻结模型pb已经用的少了。
这一块主要是先导入savedModel利用TF的tf.function()添加可追踪对象在,主要是添加两部分代码:
- 图片预处理代码
- def preprocess(image):
-
- image = image[:, :, ::-1]
-
- image = tf.transpose(image, (2, 0, 1))
-
- image = tf.cast(image, tf.float32)/255.0
-
- image = tf.expand_dims(image, axis=0)
-
- return image
-
- NMS算法的前后代码
- def mynms(inp):
-
- stride = [8., 16., 32.]
-
- anchor_grid = [[[[[10., 13.]]],
-
- [[[16., 30.]]],
-
- [[[33., 23.]]]],
-
- [[[[30., 61.]]],
-
- [[[62., 45.]]],
-
- [[[59., 119.]]]],
-
- [[[[116., 90.]]],
-
- [[[156., 198.]]],
-
- [[[373., 326.]]]]]
-
- z = []
-
- for i, one in enumerate(inp):
-
- layer_ = one
-
- ny, nx = layer_.shape[-2:]
-
- layer_ = tf.transpose(tf.reshape(layer_, [3, 85, ny, nx]), [0, 2, 3, 1])
-
- yv, xv = tf.meshgrid(tf.range(0, ny), tf.range(0, nx), indexing='ij')
-
- l_meshgrid = tf.cast(tf.reshape(tf.stack([xv, yv], 2), [1, ny, nx, 2]), tf.float32)
-
- layer_ = tf.sigmoid(layer_)
-
- cxcy = (layer_[:, :, :, 0:2] * 2. - 0.5 + l_meshgrid) * stride[i]
-
- wh = (layer_[:, :, :, 2:4] * 2) ** 2 * anchor_grid[i]
-
- layer_ = tf.concat([cxcy, wh, layer_[:, :, :, 4:]], axis=-1)
-
- layer_ = tf.reshape(layer_, [-1, 85])
-
- z.append(layer_)
-
- layers_out = tf.concat(z, axis=0)
-
- layers_out = tf.expand_dims(layers_out, 0)
-
- boxes = layers_out[0, :, :4]
-
- # cx, cy, w, h to x0 y0 x1 y1
-
- x0 = boxes[:, 0] - boxes[:, 2] / 2
-
- y0 = boxes[:, 1] - boxes[:, 3] / 2
-
- x1 = boxes[:, 0] + boxes[:, 2] / 2
-
- y1 = boxes[:, 1] + boxes[:, 3] / 2
-
- boxes = tf.stack((x0, y0, x1, y1), 1)
-
- conf = layers_out[0, :, 4]
-
- classes = layers_out[0, :, 5:]
-
- selected_indices, _ = tf.image.non_max_suppression_with_scores(boxes, conf, 30, 0.3)
-
- boxes = tf.gather(boxes, selected_indices, name="boxes")
-
- conf = tf.gather(conf, selected_indices, name="conf")
-
- classes = tf.gather(classes, selected_indices, name="classes")
-
- return boxes, conf, classes
这里需要注意就是GPU可能会显示不够需要内存自增长设置。
- import tensorflow as tf
-
- import numpy as np
-
- import predict_
-
- gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
-
- for gpu in gpus:
-
- tf.config.experimental.set_memory_growth(gpu, True)
-
- # input_arrays = ["input"]
-
- # output_arrays = ["boxes", 'conf', 'classes']
-
- # converter = tf.lite.TFLiteConverter.from_saved_model("/media/george/DATASETS1/pycharm_workspace/yolov5/tf2.1.1")
-
- converter = tf.lite.TFLiteConverter.from_saved_model("/media/george/DATASETS1/pycharm_workspace/ai_learning/onnx/pb_yolov5_meshgrid_nms")
-
- converter.target_spec.supported_ops = [
-
- tf.lite.OpsSet.TFLITE_BUILTINS, # enable TensorFlow Lite ops.
-
- ]
-
- converter.allow_custom_ops = True
-
- converter.experimental_new_converter = True
-
- # converter.optimizations = [tf.lite.Optimize.DEFAULT]
-
- # converter.target_spec.supported_types = [tf.float16]
-
- # converter.post_training_quantize = True
-
- tflite_model = converter.convert()
-
- open("y5s.tflite", "wb").write(tflite_model)
利用Android studio 加载tensorflow官方Android detection代码
https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection/android。
修改的地方也比较多,具体要看自己的模型流程是怎么样的,主要还是模型输入尺寸和输出参数是什么,修改对应的java代码即可。
如果哪里有说错或者有不清楚的可以留言.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。