Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions python/tvm/relay/frontend/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,30 @@ def get_int_tuple(self, key, default=RequiredAttr()):
raise AttributeError("Required attribute {} not found.".format(key))
return default

def get_float_tuple(self, key, default=RequiredAttr()):
"""Get float tuple attribute

Parameters
----------
key : str
The attribute key

default : float
The default value.

Returns
-------
value : The result
"""

if key in self.attrs:
tshape = self.attrs[key]
return tuple(float(x.strip()) for x in
tshape.strip('()[]').split(','))
if isinstance(default, RequiredAttr):
raise AttributeError("Required attribute {} not found.".format(key))
return default

def get_tuple_tuple_int(self, key, default=RequiredAttr()):
"""Get int list attribute

Expand Down
32 changes: 30 additions & 2 deletions python/tvm/relay/frontend/mxnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,33 @@ def _mx_lrn(inputs, attrs):
return _op.nn.lrn(inputs[0], **new_attrs)


def _mx_multibox_prior(inputs, attrs):
new_attrs = {}
new_attrs["sizes"] = attrs.get_float_tuple("sizes", (1.0, ))
new_attrs["steps"] = attrs.get_float_tuple("steps", (-1.0, -1.0))
new_attrs["offsets"] = attrs.get_float_tuple("offsets", (0.5, 0.5))
new_attrs["ratios"] = attrs.get_float_tuple("ratios", (1.0, ))
new_attrs["clip"] = attrs.get_bool("clip", False)
return _op.vision.multibox_prior(inputs[0], **new_attrs)


def _mx_multibox_detection(inputs, attrs):
new_attrs0 = {}
new_attrs0["clip"] = attrs.get_bool("clip", True)
new_attrs0["threshold"] = attrs.get_float("threshold", 0.01)
new_attrs0["variances"] = attrs.get_float_tuple("variances", (0.1, 0.1,
0.2, 0.2))

new_attrs1 = {}
new_attrs1["overlap_threshold"] = attrs.get_float("nms_threshold", 0.5)
new_attrs1["force_suppress"] = attrs.get_bool("force_suppress", False)
new_attrs1["topk"] = attrs.get_int("nms_topk", -1)

ret = _op.vision.multibox_transform_loc(inputs[0], inputs[1],
inputs[2], **new_attrs0)
return _op.vision.nms(ret[0], ret[1], **new_attrs1)


# Note: due to attribute conversion constraint
# ops in the identity set must be attribute free
_identity_list = [
Expand Down Expand Up @@ -327,13 +354,14 @@ def _mx_lrn(inputs, attrs):
"LeakyReLU" : _mx_leaky_relu,
"SoftmaxOutput" : _mx_softmax_output,
"SoftmaxActivation" : _mx_softmax_activation,
# vision
"_contrib_MultiBoxPrior" : _mx_multibox_prior,
"_contrib_MultiBoxDetection" : _mx_multibox_detection,
# List of missing operators that are present in NNVMv1
# TODO(tvm-tvm): support all operators.
#
# "broadcast_to",
# "gather_nd",
# "_contrib_MultiBoxPrior" : _rename("multibox_prior"),
# "_contrib_MultiBoxDetection" : _contrib_multibox_detection,
# "Crop" : _crop_like,

}
Expand Down
1 change: 1 addition & 0 deletions python/tvm/relay/op/vision/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

from .multibox import *
from .nms import *
from . import _multibox
77 changes: 77 additions & 0 deletions python/tvm/relay/op/vision/_multibox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# pylint: disable=invalid-name, unused-argument
"""Definition of vision ops"""
from __future__ import absolute_import

import topi
from topi.util import get_const_int, get_const_float, get_float_tuple
from .. import op as reg
from ..op import OpPattern


@reg.register_schedule("vision.multibox_prior")
def schedule_multibox_prior(_, outs, target):
"""Schedule definition of multibox_prior"""
with target:
return topi.generic.schedule_multibox_prior(outs)


@reg.register_compute("vision.multibox_prior")
def compute_multibox_prior(attrs, inputs, _, target):
"""Compute definition of multibox_prior"""
sizes = get_float_tuple(attrs.sizes)
ratios = get_float_tuple(attrs.ratios)
steps = get_float_tuple(attrs.steps)
offsets = get_float_tuple(attrs.offsets)
clip = bool(get_const_int(attrs.clip))
return [
topi.vision.ssd.multibox_prior(inputs[0], sizes, ratios, steps,
offsets, clip)
]


reg.register_pattern("vision.multibox_prior", OpPattern.OPAQUE)


# multibox_transform_loc
@reg.register_schedule("vision.multibox_transform_loc")
def schedule_multibox_transform_loc(_, outs, target):
"""Schedule definition of multibox_detection"""
with target:
return topi.generic.schedule_multibox_transform_loc(outs)


@reg.register_compute("vision.multibox_transform_loc")
def compute_multibox_transform_loc(attrs, inputs, _, target):
"""Compute definition of multibox_detection"""
clip = bool(get_const_int(attrs.clip))
threshold = get_const_float(attrs.threshold)
variances = get_float_tuple(attrs.variances)
return topi.vision.ssd.multibox_transform_loc(
inputs[0], inputs[1], inputs[2], clip, threshold, variances)


reg.register_pattern("vision.multibox_transform_loc", OpPattern.OPAQUE)
reg.register_pattern("vision.multibox_detection", OpPattern.OPAQUE)


# non-maximum suppression
@reg.register_schedule("vision.nms")
def schedule_nms(_, outs, target):
"""Schedule definition of nms"""
with target:
return topi.generic.schedule_nms(outs)


@reg.register_compute("vision.nms")
def compute_nms(attrs, inputs, _, target):
"""Compute definition of nms"""
overlap_threshold = get_const_float(attrs.overlap_threshold)
force_suppress = bool(get_const_int(attrs.force_suppress))
topk = get_const_int(attrs.topk)
return [
topi.vision.nms(inputs[0], inputs[1], overlap_threshold,
force_suppress, topk)
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed?

Copy link
Member Author

@zhiics zhiics Dec 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed because fcompute needs to receive an Array of Tensor when returning to c++. We can probably refactor the packfunc conversion a little bit to accept both Array and Tensor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NNVM automatically wraps result in Array:
https://github.com/dmlc/tvm/blob/e5d92e1b96c1fbc175be741f522c030f2d91a613/nnvm/src/compiler/packed_func_ext.cc#L99-L103

But Relay does not:
https://github.com/dmlc/tvm/blob/e5d92e1b96c1fbc175be741f522c030f2d91a613/src/relay/ir/op.cc#L129-L135

Relay doc asks for schedule function that returns list. So this is indeed complying with existing Relay interface.



reg.register_pattern("vision.nms", OpPattern.OPAQUE)
12 changes: 7 additions & 5 deletions python/tvm/relay/op/vision/multibox.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Multibox operations."""
from __future__ import absolute_import as _abs
from . import _make
from ...expr import TupleWrapper

def multibox_prior(data,
sizes=(1.0,),
Expand Down Expand Up @@ -43,7 +44,7 @@ def multibox_transform_loc(cls_prob,
anchor,
clip=True,
threshold=0.01,
variance=(0.1, 0.1, 0.2, 0.2)):
variances=(0.1, 0.1, 0.2, 0.2)):
"""Location transformation for multibox detection

Parameters
Expand All @@ -63,12 +64,13 @@ def multibox_transform_loc(cls_prob,
threshold : double, optional
Threshold to be a positive prediction.

variance : Tuple of float, optional
Variances to be decoded from box regression output.
variances : Tuple of float, optional
variances to be decoded from box regression output.

Returns
-------
ret : tuple of tvm.relay.Expr
"""
return _make.multibox_transform_loc(cls_prob, loc_pred, anchor, clip,
threshold, variance)
return TupleWrapper(_make.multibox_transform_loc(cls_prob, loc_pred,
anchor, clip, threshold,
variances), 2)
Loading