Mxnet(symbol和Gluon)并行测试

我们都知道,在训练过程中,一个batch通常包含多个训练图像,而在测试阶段,通常的做法是每次只测试一幅图像,如果我们想通过图像旋转、翻转等方法对于一幅图像进行多种输入测试时,最常想到的就是变换完之后,分别输入网络中。除此之外,还可以把多张同尺寸的图像拼成一个batch,直接一次性得到多张图像的预测结果。并行预测在某些并发较高的场景中能够显著提高系统的吞吐量。下面分别以分类模型为例,介绍symbol和Gluon接口实现方式,symbol接口实现方式很久没测试过,最近一直使用Gluon,不知symbol是否有变动。

1. symbol接口实现

通常我们加载训练好的模型和初始化的时候,需要指定data_shape,而一般测试的data_shape都是1*3*m*n,表示测试输入是一张图像。然后我们将一幅图像处理成网络可以输入的batch形式,就可以通过forward(),进行预测。其实,多个输入也只需要修改这几个地方,首先,我们修改模型加载和初始化的地方:

sym, arg_params, aux_params = mx.model.load_checkpoint('vgg', 10)
mod = mx.mod.Module(symbol=sym, context=mx.gpu(3), label_names=None)
mod.bind(for_training=False, data_shapes=[('data', (4,3,224,224))],label_shapes=mod._label_shapes)
mod.set_params(arg_params, aux_params, allow_missing=True)

我们只需要把data_shape,按照我们的输入修改,比如我们想一次输入4张图像,那么这里就可以写成(4,3,224,224),接下来我们修改生成batch的类:

class OneDataBatch():
    def __init__(self,img): 
        self.data = [mx.nd.array(img)]
        self.label = None
        self.provide_label = None
        self.provide_data = [("data",(4,3,224,224))]

接下来,我们将一张256的图像固定crop成4张224,然后将四张图拼成一个batch,并行输入网络进行测试:

for i in {8, 24}:
   for j in {8, 24}:
       im_crop = img[:, j : j + 224, i : i + 224]
       normed_img_list.append(normed_img)
im_batch = OneDataBatch(normed_img_list)
mod.forward(im_batch)
prob = mod.get_outputs()[0].asnumpy())

除了固定位置crop,我们还可以mxnet中的随机crop函数:

 for i in range(0, 4):
    cropped_im, rect  = mx.image.random_crop(mx.nd.array(im_resize), (224, 224))
    im_crop = im[:, rect[0] : rect[0] + rect[2], rect[1] : rect[1] + rect[3]]
    normed_img_list.append(normed_img)

2. Gluon接口实现

Gluon的实现更加简单,不需要复杂的配置,只要网络支持,直接将数据拼成B*3*h*w的batch就可以直接传入网络:

import mxnet as mx
from gluoncv.data.transforms.image import ten_crop, resize_short_within

transform_fn = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))
    ])

img = mx.image.imread('1.jpg')
img = resize_short_within(img, short=224)
img_to_test = ten_crop(img_tree, size=(224, 224)) #crop and flip , 10*3*224*224
img_to_test = transform_fn(img_to_test)
prob = net(img_to_test)

如果是想测试多少图像,只需要使用concat函数,在B这一维度(第0维)将图像拼起来传入网络:

#img1:1*3*224*224;img2:1*3*224*224
from mxnet import nd
img_to_test = nd.concat(img1, img2, dim=0) #2*3*224*224
output = net(img_to_test)

Gluoncv中的分类模型基本都支持并行测试,而且检测模型Yolov3也支持,实际测试发现,并行测试在GPU使用率不高时,确实能提升单张图像的平均预测时间。当然并行跑两张的耗时肯定比串行跑一张的总耗时多,但是比串行跑两张的总耗时少,所以可以根据硬件水平和具体任务中等待处理的个数,合理安排并行数量。请求少时,通过翻转crop并行测试提升精度,请求多时,通过并行多张提升速度。并行测试外加在之前文章中提到的使用卷积进行预处理,能够更快,否则CPU的预处理速度会限制并行速度。

下面简单介绍Gluoncv 中的Yolov3并行测试的方法,假如我们有多少图像,而且尺寸一样(不一样的情况没测,感觉应该不行吧),不需要翻转,只需要拼接起来进行测试,class_IDs等输出的第0维便表明了是哪张图像索引。

from gluoncv import data, model_zoo
net = model_zoo.get_model('yolo3_darknet53_voc', pretrained=True)
net.collect_params().reset_ctx(mx.gpu(0))
a, img = data.transforms.presets.yolo.load_test(im1_path, short=512) #1*3*512*512
b, img = data.transforms.presets.yolo.load_test(im2_path, short=512) #1*3*512*512
img_to_test = nd.concat(a, b, dim=0)
img_to_test = img_to_test .as_in_context(mx.gpu(0))
class_IDs, scores, bounding_boxs = net(img_to_test)

假如我们有一张图像,想通过翻转进行测试提高准确率,可以通过如下方式:

from gluoncv import data, model_zoo
from mxnet import nd
net = model_zoo.get_model('yolo3_darknet53_voc', pretrained=True)
net.collect_params().reset_ctx(mx.gpu(0))

a, img = data.transforms.presets.yolo.load_test(im1_path, short=512) #1*3*512*512
#分别进行水平、垂直、对角线
x_flip_2 = nd.flip(data=x, axis=2)
x_flip_3 = nd.flip(data=x, axis=3)
x_flip_4 = nd.flip(data=x_flip_2, axis=3)

img_to_test = nd.concat(a, x_flip_2, x_flip_3, x_flip_4, dim=0)
img_to_test = img_to_test.as_in_context(mx.gpu(0))
class_IDs, scores, bounding_boxs = net(img_to_test)

最后三个输出的第0维分别存着4组数据,别忘了把box在flip回来(gluoncv/data/transforms/box.py中有对box进行flip的函数,可以import),可能还要再经过一个nms删掉重复的box。

from gluoncv.data.transforms import bbox as tbox
box_after = tbbox.flip(box_ori, (w, h), flip_x=True)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值