在modelarts上部署mask-rcnn模型
最近老山完成了对mask-rcnn在modelarts上的部署,部署模型来自于这个项目。部署的过程大体和我的上篇文章使用modelarts部署bert命名实体识别模型相似,许多细节也不在赘述。这篇文章主要介绍下大体的思路、遇到的问题和解决方案,文章结尾会附录运行需要的程序。
部署思路
生成savedModel
原模型是使用tensorflow做backend的keras模型。源程序中的keras模型又被封装在MaskRCNN类中。我们要先取出被封装的keras模型,在源程序提供的demo.ipynb第三步后,我们提取出keras模型
# Create model object in inference mode.
model = modellib.MaskRCNN(mode="inference", model_dir=MODEL_DIR, config=config)
# Load weights trained on MS-COCO
model.load_weights(COCO_MODEL_PATH, by_name=True)
# 前面来自于demo.ipynb
model = model.keras_model
之后就可以把keras模型变成tensorflow savedModel模型了,代码详见在modelarts上部署backend为TensorFlow的keras模型
customize_service.py书写
这仍然是工作最主要的部分,我们依然找到预测的源程序MaskRCNN.detect函数,我们把detect程序分成前处理+预测+后处理三段,做以下工作:
把前处理和后处理分别写成子函数,把预测部分去掉,保留类的结构,尽可能减少代码更改量;
前处理和后处理的数据交换通过类属性传递;
把无关程序(主要是train部分程序)尽可能去掉,以减少代码调试量。
class MaskRCNN:
# .....
def detect(self, images, verbose=0):
# some code...
if verbose:
log("molded_images", molded_images)
log("image_metas", image_metas)
log("anchors", anchors)
# 在此之前是前处理
# 预测
detections, _, _, mrcnn_mask, _, _, _ =\
self.keras_model.predict([molded_images, image_metas, anchors], verbose=0)
# 后处理程序
results = []
for i, image in enumerate(images):
# ...
})
return results
遇到的问题
输出张量大小限制
模型输出张量(定义可见模型部署介绍)有30多M,超过了modelarts目前Tensorflow Serving的4M的限制,因此还需要对savedModel的模型进行更改,首先观察模型输出
0 mrcnn_detection shape:(1, 100, 6) size:600 dtype:float32
1 mrcnn_class shape:(1, 1000, 81) size:81000 dtype:float32
2 mrcnn_bbox shape:(1, 1000, 81, 4) size:324000 dtype:float32
3 mrcnn_mask shape:(1, 100, 28, 28, 81) size:6350400 dtype:float32
4 ROI shape:(1, 1000, 4) size:4000 dtype:float32
5 rpn_class shape:(1, 261888, 2) size:523776 dtype:float32
6 rpn_bbox shape:(1, 261888, 4) size:1047552 dtype:float32
由于输出的张量只有mrcnn_detection和mrcnn_mask两个应用于后处理,输出过大的原因在于mrcnn_mask。我们通过对后处理源程序的观察和运行结果发现,mrcnn_mask几个维度的参数意义如下
维度 | 值 | 意义 |
---|---|---|
0 | 1 | batch size |
1 | 100 | 检测物体的数目,上限是100 |
2~3 | (28, 28) | 物体的mask,被压缩成28*28的网格,数据是float32 |
4 | 81 | 物体的类别id |
mrcnn_detection的几个维度的意义:
维度 | 值 | 意义 |
---|---|---|
0 | 1 | batch size |
1 | 100 | 检测物体的数目,上限是100 |
2 | 6 | 包括top, left, bottom, right, classid, score,如果classid是0,说明是不关心的部分 |
我们可以从2个方面去精简输出张量:
检测物体的数目不需要必须凑成100个,我们以实际输出的数目作为准,去掉classid是0的值,这个对mrcnn_mask和mrcnn_detection都做精简;
通过这种方式,我们不能减少输出的上限,但对大部分情况,输出的张量都能减少到原来的10%左右;
我们关系的不是所有类别的mask,只关系被检测出物体的类别id下的mask;换言之,我们对于每个被检测的物体,可以在mrcnn_detection上找到他的classid,然后在mrcnn_mask物体类别id中,我们只需输出该id下的mask即可;
通过这种方式,输出张量可以固定缩减成原来的1/81;
由于4M限制是对于模型输出的,因此更改也只能通过更改savedModel来实现。也因此只能使用tensorflow的一定底层操作。换言之,要把源程序中用python后处理的部分程序需要改写成tf.Operation。也许是考虑了自动求导的原因,tensorflow的张量操作虽然很多,但各个功能都弱的一逼,下面是老山的实现片段
# 前面程序是读入已保存的savedModel模型,读入输出张量y1-y7
detections = y1
mrcnn_masks = y4
# 把detections的classid列取出
detections = detections[0]
classes = tf.cast(detections[:, 4], tf.int32)
# 把classid做成一个是否为0的mask: boolean list
zero = tf.constant(0, tf.int32)
mask = tf.not_equal(classes, zero)
# 使用mask把detensions,classid精简,range_trim存储了剩下的id号
detection_trim = tf.boolean_mask(detections, mask)
classes_trim = tf.boolean_mask(classes, mask)
range_trim = tf.boolean_mask(tf.range(classes.shape[0], dtype=tf.int32), mask)
# 构造一个[index, classid]对的list,长度是已经精简完的classid,命名为stack
stack = tf.stack([range_trim, classes_trim], axis = 1)
mrcnn_masks = mrcnn_masks[0]
# 通过transpose把stack的两列弄到前面去
mrcnn_masks = tf.transpose(mrcnn_masks, perm = [0,3,1,2])
# 使用stack来精简mrcnn_masks_trim
mrcnn_masks_trim = tf.gather_nd(mrcnn_masks, stack)
# 后续是保存模型
# 详细可见附件
模型的输出张量改动之后,后处理模块的程序也需要做相应的调整。
输出结果的压缩
由于modelarts源程序的后处理的输出包括rois,masks, class_ids, scores等量,其中mask是每个识别物体在原图上的mask(换句话说,就是原图尺寸的boolean矩阵),如果直接变成json的话,原先的占1bit的True/False就会变成文字,下载数据量达到几十M,时间太长。为此,我们需要对输出的masks进行压缩。
老山尝试了这2类算法:
PNG压缩
from PIL import Image
import base64
def mask2str(mask):
# 变成图片
image = Image.fromarray(mask.astype('uint8')*255, 'L').convert('1')
# 把图片输出到buffer里
buffered = BytesIO()
image.save(buffered, format='PNG')
# 使用base64变成文本
base_bytes = base64.b64encode(buffered.getvalue())
base_str = base_bytes.decode('utf-8')
return base_str
2.自建索引压缩
import numpy as np
import base64
import lzma
def mask2str(mask):
# 把mask打成一维,寻找值发生变化的索引序列
mask_flatten = mask.reshape(-1)
mask_flatten_toright= np.insert(mask_flatten, 0, False)[:-1]
diff = np.where(mask_flatten!=mask_flatten_toright)[0]
counts = np.diff(diff, prepend=0).astype('int32')
# 使用lzma压缩
counts_bytes = bytes(counts)
lzma_bytes = lzma.compress(counts_bytes)
# 使用base64变成文本
base64_bytes = base64.b64encode(lzma_bytes)
base64_str = base64_bytes.decode('utf-8')
return base64_str
索引压缩的想法是mask通常是在False背景下的True图,对于每行我们其实只要知道True的起始index和末了index就好,或者更抽象一步的说,图中交替的存在大量的连续False和True序列,那我们只要把他们的长度值变成list即可(默认第一个序列是False,哪怕长度是0)。这样的话,记录长度的好处在于比记录位置值更小,更便于压缩。为此,我们干脆的,把图片拉成一维,然后记录交替False、True序列的长度,再用lzma压缩。
压缩结果是使用索引只有PNG压缩大小的45%左右。
生成图片
源程序使用matplotlib画图,不好保存,老山用PIL重写了这段程序。
附件结构
附件结构如下,部署使用的模型大家自行在github上下载,然后在notebook上使用modify.py生成savedModel模型。预测中修改get_x_auth_token.py获取x_auth_token后,更改x_auth_token.py,然后下载图片数据,然后便可以使用predict预测了。
root
├── deploy # 部署相关文件
│ ├── coco.py
│ ├── compression_.py
│ ├── config_.py
│ ├── config.py
│ ├── customize_service.py
│ ├── image_util.py
│ ├── model_util.py
│ └── surround.py
├── modify.py # 用于notebook生成savedModel模型
└── predict # 用于预测
├── compression_.py
├── get_x_auth_token.py
├── new_visualize.py
├── predict.py
└── x_auth_token.py
如果觉得老山的文章不错,不妨点击下关注。
作者:山找海味
在modelarts上部署mask-rcnn模型的更多相关文章
- 在modelarts上部署backend为TensorFlow的keras模型
最近老山在研究在modelarts上部署mask-rcnn,源代码提供的是keras模型.我们可以将keras转化成savedModel模型,在TensorFlow Serving上部署,可参考老山的 ...
- Windows上配置Mask R-CNN及运行示例demo.ipynb
最近做项目需要用到Mask R-CNN,于是花了几天时间配置.简单跑通代码,踩了很多坑,写下来分享给大家. 首先贴上官方Mask R-CNN的Github地址:https://github.com/m ...
- 终极指南:构建用于检测汽车损坏的Mask R-CNN模型(附Python演练)
介绍 计算机视觉领域的应用继续令人惊叹着.从检测视频中的目标到计算人群中的人数,计算机视觉似乎没有无法克服的挑战. 这篇文章的目的是建立一个自定义Mask R-CNN模型,可以检测汽车上的损坏区域(参 ...
- [代码解析]Mask R-CNN介绍与实现(转)
文章来源 DFann 版权声明:如果你觉得写的还可以,可以考虑打赏一下.转载请联系. https://blog.csdn.net/u011974639/article/details/78483779 ...
- Mask R-CNN用于目标检测和分割代码实现
Mask R-CNN用于目标检测和分割代码实现 Mask R-CNN for object detection and instance segmentation on Keras and Tenso ...
- 手把手教你使用LabVIEW实现Mask R-CNN图像实例分割
前言 前面给大家介绍了使用LabVIEW工具包实现图像分类,目标检测,今天我们来看一下如何使用LabVIEW实现Mask R-CNN图像实例分割. 一.什么是图像实例分割? 图像实例分割(Instan ...
- 谷歌大脑提出:基于NAS的目标检测模型NAS-FPN,超越Mask R-CNN
谷歌大脑提出:基于NAS的目标检测模型NAS-FPN,超越Mask R-CNN 朱晓霞发表于目标检测和深度学习订阅 235 广告关闭 11.11 智慧上云 云服务器企业新用户优先购,享双11同等价格 ...
- Tensorflow 模型线上部署
获取源码,请移步笔者的github: tensorflow-serving-tutorial 由于python的灵活性和完备的生态库,使得其成为实现.验证ML算法的不二之选.但是工业界要将模型部署到生 ...
- LabVIEW+OpenVINO在CPU上部署新冠肺炎检测模型实战
前言 之前博客:[YOLOv5]LabVIEW+OpenVINO让你的YOLOv5在CPU上飞起来给大家介绍了在LabVIEW上使用openvino加速推理,在CPU上也能感受丝滑的实时物体识别.那我 ...
随机推荐
- CSPS模拟 57
rank4大众rank T1 天空龙 让他自由翱翔吧 T2 巨神兵 对于n=10的测试点本可以打出非常优秀的分层状压 但是没有打出来,因为对拓扑图理解不够深刻,纠结于指回的边,实际上只关注伸出的边就可 ...
- 三分钟学会Redis在.NET Core中做缓存中间件
大家好,今天给大家说明如何在.NET Core中使用Redis,我们在想要辩论程序的好与坏,都想需要一个可视化工具,我经常使用的是一位国内大牛开发的免费工具,其Github地址为: https://g ...
- 原生JS实现call,apply,bind函数
1. 前言 使用原生JS实现call和apply函数,充分了解其内部原理.call和apply都是为了解决改变this的指向.作用都相同,只是传参的方式不同.除了第一个参数外,call可以接受一个参数 ...
- 使用Typescript重构axios(十九)——请求取消功能:实现第二种使用方式
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- python学习之【第十五篇】:Python中的常用模块之time模块
1.前言 在Python中,对时间的表示或操作通常要使用到time模块.本篇博文就来记录一下time模块中常用的几种时间表示转换方法. 2. 三种时间表示形式 2.1 时间戳 从1970年1月1日零点 ...
- 大数据之路day02_1--运算符
运算符这一节主要是介绍算数运算符.赋值运算符.比较运算符.逻辑运算符.三元运算符.接下来一一介绍. 1.算数运算符 ++ 和 -- 的用法 例如:a++ 和 ++a的区别 %的应用场景(取模其实就是取 ...
- JavaScript BOM学习
Mirror王宇阳 2019年11月13日 [首发] 数日没有更新博文了,觉得不好意思了!这不是,整理了一下JavaScript的一下BOM笔记资料,今天贡献出来!(HTML DOM也会随后整理发表) ...
- python基础-匿名函数和内置函数
匿名函数和内置函数 匿名函数:没有名字,使用一次即被收回,加括号就可以运行的函数. 语法:lambda 参数:返回值 使用方式: 将匿名函数赋值给变量,给匿名函数一个名字,使用这个变量来调用(还不如用 ...
- .NET Core 3.0 单元测试与 Asp.Net Core 3.0 集成测试
单元测试与集成测试 测试必要性说明 相信大家在看到单元测试与集成测试这个标题时,会有很多感慨,我们无数次的在实践中提到要做单元测试.集成测试,但是大多数项目都没有做或者仅建了项目文件.这里有客观原因, ...
- centos6官网镜像dvd1和dvd2的解释