上一节我们已经谈到了计算节点,但是即使是官方文档介绍里面相关内容也过于简略,我们使用Faster-RCNN代码中的新建节点为例,重新介绍一下新建节点的调用栈。

1、调用新建节点

参数分为三部分,op_type是节点名称,对应于辅助class的装饰器的输入;其他参数一部分传递给辅助class的初始化函数(这部分参数的虚参名和初始化函数的需参名要对应上),一部分直接作为一个list传给节点定义class的forward函数的in_data参数。

group = mx.symbol.Custom(rois=rois,                     # 2000*5的roi信息,CustomOpProp无此参数
gt_boxes=gt_boxes, # n*5的ground truth信息,n表示object数量,CustomOpProp无此参数
op_type='proposal_target', # <-----对应辅助节点装饰器参数
# CustomOp,CustomOpProp初始化参数
num_classes=num_classes, # num_classes是实际要分类的类别数加上背景类
batch_images=rcnn_batch_size, # 1
batch_rois=rcnn_batch_rois, # 128
fg_fraction=rcnn_fg_fraction, #s 0.25,正样本所占的比例
fg_overlap=rcnn_fg_overlap, # 0.5
box_stds=rcnn_bbox_stds # (0.1, 0.1, 0.2, 0.2)
)

2、建立辅助class:CustomOpProp

本部分的class方法参数固定,不可以随意修改

@mx.operator.register('proposal_target')  # <-----对应调用的op_type
class ProposalTargetProp(mx.operator.CustomOpProp):
def __init__(self, num_classes='21', batch_images='1', batch_rois='128', fg_fraction='0.25', # 接受上面调用的后5个参数
fg_overlap='0.5', box_stds='(0.1, 0.1, 0.2, 0.2)'):
super(ProposalTargetProp, self).__init__(need_top_grad=False) # <-----本节点是否需要后面的梯度
self._num_classes = int(num_classes) # num_classes是实际要分类的类别数加上背景类
self._batch_images = int(batch_images) # 1
self._batch_rois = int(batch_rois) # 128
self._fg_fraction = float(fg_fraction) # 0.25,正样本所占的比例
self._fg_overlap = float(fg_overlap) # 0.5
self._box_stds = tuple(np.fromstring(box_stds[1:-1], dtype=float, sep=',')) # (0.1, 0.1, 0.2, 0.2) def list_arguments(self):
return ['rois', 'gt_boxes'] # 向前传播需要的参数 def list_outputs(self):
return ['rois_output', 'label', 'bbox_target', 'bbox_weight'] # 向前传播输出参数名 def infer_shape(self, in_shape):
assert self._batch_rois % self._batch_images == 0, \
'BATCHIMAGES {} must devide BATCH_ROIS {}'.format(self._batch_images, self._batch_rois) rpn_rois_shape = in_shape[0]
gt_boxes_shape = in_shape[1] output_rois_shape = (self._batch_rois, 5)
label_shape = (self._batch_rois, )
bbox_target_shape = (self._batch_rois, self._num_classes * 4)
bbox_weight_shape = (self._batch_rois, self._num_classes * 4) return [rpn_rois_shape, gt_boxes_shape], \
[output_rois_shape, label_shape, bbox_target_shape, bbox_weight_shape] def create_operator(self, ctx, shapes, dtypes): # 返回初始化了的自定义节点类
return ProposalTargetOperator(self._num_classes, self._batch_images, self._batch_rois, self._fg_fraction,
self._fg_overlap, self._box_stds) def declare_backward_dependency(self, out_grad, in_data, out_data):
return []

__init__

初始化方法会首先从调用位置接收参数,调用位置的op_type参数用于指定选用哪个辅助class,然后其他参数优先传入本初的__init__方法,剩下的没有和本方法参数对应上的参数会做为节点class的forward方法的in_data参数。

下面一行表示本节点不需要接收后面层的梯度,对应到节点定义class的backward方法,out_grad参数就不应在函数体内调用了,in_grad(向前传播回去的梯度)计算完全依赖本层的参数。

super(ProposalTargetProp, self).__init__(need_top_grad=False)

list_arguments

使用list_arguments()方法返回时,输出并不直接就是上述代码list_arguments方法的return列表,实际上会将整个net结构截至的本节点为止,全部的variable变量名称输入。

对于本节点group,定义输入有两个:

rois=rois  # 2000*5的roi信息,CustomOpProp无此参数
gt_boxes=gt_boxes

其中gt_boxes变量本身是个variable(定义为gt_boxes = mx.symbol.Variable(name="gt_boxes")),但是rois为symbol,是之前网络的输出symbol,所以实际输出的arguments为gt_boxes本身,以及rois所依赖的全部variables,包含认为定义的占位符variable和网络层自带的参数variable。

['data', 'conv1_1_weight', 'conv1_1_bias', 'conv1_2_weight', 'conv1_2_bias', 'conv2_1_weight', 'conv2_1_bias', 'conv2_2_weight', 'conv2_2_bias', 'conv3_1_weight', 'conv3_1_bias', 'conv3_2_weight', 'conv3_2_bias', 'conv3_3_weight', 'conv3_3_bias', 'conv4_1_weight', 'conv4_1_bias', 'conv4_2_weight', 'conv4_2_bias', 'conv4_3_weight', 'conv4_3_bias', 'conv5_1_weight', 'conv5_1_bias', 'conv5_2_weight', 'conv5_2_bias', 'conv5_3_weight', 'conv5_3_bias', 'rpn_conv_3x3_weight', 'rpn_conv_3x3_bias', 'rpn_cls_score_weight', 'rpn_cls_score_bias', 'rpn_bbox_pred_weight', 'rpn_bbox_pred_bias', 'im_info',

'gt_boxes']

infer_shape

其输入参数in_shape就是list_arguments中return的那几个变量的shape,对于本例,就是rois和gtboxes的shape,本方法用于推断输出symbol和梯度symbol的shape是否正确。

3、实现节点class

节点class的初始化和调用部分的参数完全无关,是由辅助节点来进行传参调用的。但是其forward方法的in_data参数其值接收是从调用初进行的,in_data中的参数就是上面list_arguments方法的return结果(['rois', 'gt_boxes']),实际传参可以有空缺(例如第一小节可以删掉gt_boxes),缺省参数视为定义了一个Variable占位。

class ProposalTargetOperator(mx.operator.CustomOp):
def __init__(self, num_classes, batch_images, batch_rois, fg_fraction, fg_overlap, box_stds):
super(ProposalTargetOperator, self).__init__()
self._num_classes = num_classes # num_classes是实际要分类的类别数加上背景类
self._batch_images = batch_images # 1
self._batch_rois = batch_rois # 128
self._rois_per_image = int(batch_rois / batch_images)
self._fg_rois_per_image = int(round(fg_fraction * self._rois_per_image))
self._fg_overlap = fg_overlap # 0.5
self._box_stds = box_stds # (0.1, 0.1, 0.2, 0.2) def forward(self, is_train, req, in_data, out_data, aux):
"""Forward interface. Can override when creating new operators. Parameters
----------
is_train : bool
whether this is for training
req : list of str
how to assign to out_data. can be 'null', 'write', or 'add'.
You can optionally use self.assign(dst, req, src) to handle this.
in_data, out_data, aux: list of NDArrays
input, output, and auxiliary states for forward. See document for
corresponding arguments of Operator::Forward
"""
assert self._batch_images == in_data[1].shape[0], 'check batch size of gt_boxes' all_rois = in_data[0].asnumpy() # [2000, 5]
all_gt_boxes = in_data[1].asnumpy() # [n, 5] rois = np.empty((0, 5), dtype=np.float32)
labels = np.empty((0, ), dtype=np.float32)
bbox_targets = np.empty((0, 4 * self._num_classes), dtype=np.float32)
bbox_weights = np.empty((0, 4 * self._num_classes), dtype=np.float32)
for batch_idx in range(self._batch_images):
b_rois = all_rois[np.where(all_rois[:, 0] == batch_idx)[0]]
b_gt_boxes = all_gt_boxes[batch_idx]
b_gt_boxes = b_gt_boxes[np.where(b_gt_boxes[:, -1] > 0)[0]] # Include ground-truth boxes in the set of candidate rois
batch_pad = batch_idx * np.ones((b_gt_boxes.shape[0], 1), dtype=b_gt_boxes.dtype)
b_rois = np.vstack((b_rois, np.hstack((batch_pad, b_gt_boxes[:, :-1])))) b_rois, b_labels, b_bbox_targets, b_bbox_weights = \
sample_rois(b_rois, b_gt_boxes, num_classes=self._num_classes, rois_per_image=self._rois_per_image,
fg_rois_per_image=self._fg_rois_per_image, fg_overlap=self._fg_overlap, box_stds=self._box_stds) rois = np.vstack((rois, b_rois))
labels = np.hstack((labels, b_labels))
bbox_targets = np.vstack((bbox_targets, b_bbox_targets))
bbox_weights = np.vstack((bbox_weights, b_bbox_weights)) self.assign(out_data[0], req[0], rois)
self.assign(out_data[1], req[1], labels)
self.assign(out_data[2], req[2], bbox_targets)
self.assign(out_data[3], req[3], bbox_weights) def backward(self, req, out_grad, in_data, out_data, in_grad, aux):
"""Backward interface. Can override when creating new operators. Parameters
----------
req : list of str
how to assign to in_grad. can be 'null', 'write', or 'add'.
You can optionally use self.assign(dst, req, src) to handle this.
out_grad, in_data, out_data, in_grad, aux : list of NDArrays
input and output for backward. See document for
corresponding arguments of Operator::Backward
"""
self.assign(in_grad[0], req[0], 0)
self.assign(in_grad[1], req[1], 0)

介绍完辅助节点,本部分的介绍就不太多了,注意的就是向前向后两个方法没有返回值,使用assign来给symbol赋值,数量顺序要和辅助class的argument、output对应上,具体实现因为没有研究C++源码,没办法更详细介绍了,不过不影响使用(大概)。

另外,辅助节点class是会在python解释器里直接执行的,也就是说我们添加在函数体中的print什么的能够得到输出,但是在本class中,添加的中间输出不会被print出来,应该是建立符号图时被略去了(有关C++优化计算图的机理,李沐博士有介绍,不过我的C++功底不够,没有看过源码,仍旧觉得符号式编程的运行过程很神奇……),另外,我们在bind等操作做检查时,仅仅会运行辅助节点,不到真实的数据流入,这个class是不会运行乃至报错的,所以辅助节点的设计真的很重要。

『MXNet』第十二弹_再谈新建计算节点的更多相关文章

  1. 『PyTorch』第十二弹_nn.Module和nn.functional

    大部分nn中的层class都有nn.function对应,其区别是: nn.Module实现的layer是由class Layer(nn.Module)定义的特殊类,会自动提取可学习参数nn.Para ...

  2. 『MXNet』第十弹_物体检测SSD

    全流程地址 一.辅助API介绍 mxnet.image.ImageDetIter 图像检测迭代器, from mxnet import image from mxnet import nd data_ ...

  3. 『PyTorch』第十六弹_hook技术

    由于pytorch会自动舍弃图计算的中间结果,所以想要获取这些数值就需要使用钩子函数. 钩子函数包括Variable的钩子和nn.Module钩子,用法相似. 一.register_hook impo ...

  4. 『PyTorch』第十五弹_torch.nn.Module的属性设置&查询

    一.背景知识 python中两个属相相关方法 result = obj.name 会调用builtin函数getattr(obj,'name')查找对应属性,如果没有name属性则调用obj.__ge ...

  5. 『PyTorch』第十四弹_torch.nn.Module类属性

    nn.Module基类的构造函数: def __init__(self): self._parameters = OrderedDict() self._modules = OrderedDict() ...

  6. Openstack入门篇(十二)之neutron服务(计算节点)的部署与测试

    1.Neutron组件安装 [root@linux-node2 ~]# yum install -y openstack-neutron-linuxbridge ebtables ipset 2.配置 ...

  7. 『MXNet』专题汇总

    MXNet文档 MXNet官方教程 持久化模型 框架介绍 『MXNet』第一弹_基础架构及API 『MXNet』第二弹_Gluon构建模型 『MXNet』第三弹_Gluon模型参数 『MXNet』第四 ...

  8. 『MXNet』第四弹_Gluon自定义层

    一.不含参数层 通过继承Block自定义了一个将输入减掉均值的层:CenteredLayer类,并将层的计算放在forward函数里, from mxnet import nd, gluon from ...

  9. 『TensorFlow』SSD源码学习_其一:论文及开源项目文档介绍

    一.论文介绍 读论文系列:Object Detection ECCV2016 SSD 一句话概括:SSD就是关于类别的多尺度RPN网络 基本思路: 基础网络后接多层feature map 多层feat ...

随机推荐

  1. 【做题】arc070_f-HonestOrUnkind——交互+巧妙思维

    做的第一道交互题-- 首先,有解的一个必要条件是\(a>b\).否则,即当\(a<=b\)时,可以有\(a\)个unkind的人假装自己就是那\(a\)个honest的人.(彼此之间都说是 ...

  2. String comparison is too slow in R language

    ## String comparison is too slow in R language ## it will take 3 minutes, it is too slow date() strA ...

  3. P5091 【模板】欧拉定理

    思路 欧拉定理 当a与m互质时 \[ a^ {\phi (m)} \equiv 1 \ \ (mod\ m) \] 扩展欧拉定理 当a与m不互质且\(b\ge \phi(m)\)时, \[ a^b \ ...

  4. SPOJ 687 REPEATS - Repeats

    题意 给定字符串,求重复次数最多的连续重复子串 思路 后缀数组的神题 让我对着题解想了快1天 首先考虑一个暴力,枚举循环串的长度l,然后再枚举每个点i,用i和i+l匹配,如果匹配长度是L,这个循环串就 ...

  5. P3121 [USACO15FEB]审查(黄金)Censoring (Gold)

    吐槽 数据太水了吧,我AC自动机的trie建错了结果只是RE了两个点,还以为数组开小了改了好久 思路 看到多模板串,字符串匹配,且模板串总长度不长,就想到AC自动机 然后用栈维护当前的字符串位置,如果 ...

  6. 配置和运行 MatchNet CVPR 2015 MatchNet: Unifying Feature and Metric Learning for Patch-Based Matching

    配置和运行 MatchNet CVPR 2015 GitHub: https://github.com/hanxf/matchnet 最近一个同学在配置,测试这个网络,但是总是遇到各种问题. 我也尝试 ...

  7. 并发学习一、MPI初步认识

    学习参考地址:https://www.jianshu.com/p/2fd31665e816 编程使用的vs2015 社区版本(个人感觉比Vc6.0的丑界面看起来舒服多了) MPI基本函数 MPI调用借 ...

  8. HTML-CSS-JS-JQ常用知识点总结

    html:展示文件 标签:<html><head><title></title><meta><link><style> ...

  9. C#判断数据类型的简单示例代码

    ; Console.WriteLine( "i is an int? {0}",i.GetType()==typeof(int)); Console.WriteLine( &quo ...

  10. 后台返回数据判断是http还是后台本地图片 indexOf

    今天的笔记呢,记录一下 其实这个应该后台去判断的,但是因为某种原因,今天我们前台做一下判断 事情是这样的,后台返回我一个url  这个url有的http开头的 也有他后台本地的例如:/img/1.pn ...