Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
Bilinear upsampling documentation and test
Browse files Browse the repository at this point in the history
  • Loading branch information
vandanavk committed Feb 14, 2019
1 parent df5310b commit 8427a97
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 17 deletions.
2 changes: 0 additions & 2 deletions python/mxnet/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,6 @@ def build_param_doc(arg_names, arg_types, arg_descs, remove_dup=True):
for key, type_info, desc in zip(arg_names, arg_types, arg_descs):
if key in param_keys and remove_dup:
continue
if key == 'num_args':
continue
param_keys.add(key)
ret = '%s : %s' % (key, type_info)
if len(desc) != 0:
Expand Down
14 changes: 8 additions & 6 deletions src/operator/nn/upsampling-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,26 @@ struct UpSamplingParam : public dmlc::Parameter<UpSamplingParam> {
.set_range(1, 1000)
.describe("Up sampling scale");
DMLC_DECLARE_FIELD(num_filter)
.describe("Input filter. Only used by bilinear sample_type.")
.describe("Input filter. Only used by bilinear sample_type. "
"Since bilinear upsampling uses deconvolution, num_filters "
"is set to the number of channels.")
.set_default(0);
DMLC_DECLARE_FIELD(sample_type)
.add_enum("nearest", up_enum::kNearest)
.add_enum("bilinear", up_enum::kBilinear)
.describe("upsampling method");
DMLC_DECLARE_FIELD(num_args).set_default(1)
.describe("Number of inputs to be upsampled. For nearest neighbor "
"upsampling, this can be 1-N; the size of output will be"
"(scale*h_0,scale*w_0) and all other inputs will be upsampled to the"
"same size. For bilinear upsampling this must be 2; 1 input and 1 weight.");
DMLC_DECLARE_FIELD(multi_input_mode)
.add_enum("concat", up_enum::kConcat)
.add_enum("sum", up_enum::kSum)
.set_default(up_enum::kConcat)
.describe("How to handle multiple input. concat means concatenate upsampled "
"images along the channel dimension. sum means add all images together, "
"only available for nearest neighbor upsampling.");
DMLC_DECLARE_FIELD(num_args).set_lower_bound(1)
.describe("Number of inputs to be upsampled. For nearest neighbor "
"upsampling, this can be 1-N; the size of output will be"
"(scale*h_0,scale*w_0) and all other inputs will be upsampled to the"
"same size. For bilinear upsampling this must be 2; 1 input and 1 weight.");
DMLC_DECLARE_FIELD(workspace).set_default(512).set_range(0, 8192)
.describe("Tmp workspace for deconvolution (MB)");
}
Expand Down
4 changes: 3 additions & 1 deletion src/operator/nn/upsampling.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ struct UpSamplingGrad {
DMLC_REGISTER_PARAMETER(UpSamplingParam);

NNVM_REGISTER_OP(UpSampling)
.describe("Performs nearest neighbor/bilinear up sampling to inputs.")
.describe("Performs nearest neighbor/bilinear up sampling to inputs. "
"Bilinear upsampling makes use of deconvolution. Therefore, "
"provide 2 inputs - data and weights. ")
.set_num_inputs([](const NodeAttrs& attrs) {
const UpSamplingParam& params = nnvm::get<UpSamplingParam>(attrs.parsed);
return params.sample_type == up_enum::kNearest ? params.num_args : 2;
Expand Down
68 changes: 60 additions & 8 deletions tests/python/unittest/test_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1491,17 +1491,55 @@ def check_nearest_upsampling_with_shape(shapes, scale, root_scale):
assert_allclose(arr[name].asnumpy()*root_scale**2*scale**(2*k), arr_grad[name].asnumpy(), rtol=1e-4)


def check_bilinear_upsampling_with_shape(shapes, scale, root_scale):
arr = {'arg_%d'%i: mx.random.uniform(-10.0, 10.0, shape, ctx=mx.cpu()).copyto(default_context()) for i, shape in zip(range(len(shapes)), shapes)}
arr_grad = {'arg_%d'%i: mx.nd.zeros(shape) for i, shape in zip(range(len(shapes)), shapes)}

up = mx.sym.UpSampling(*[mx.sym.Variable('arg_%d'%i) for i in range(len(shapes))], sample_type='bilinear', scale=root_scale)
def check_bilinear_upsampling_with_shape(data_shape, weight_shape, scale, root_scale, num_filter):
def py_bilinear_resize(x, outputHeight, outputWidth):
batch, channel, inputHeight, inputWidth = x.shape
if outputHeight == inputHeight and outputWidth == inputWidth:
return x
y = np.empty([batch, channel, outputHeight, outputWidth])
rheight = 1.0 * (inputHeight - 1) / (outputHeight - 1) if outputHeight > 1 else 0.0
rwidth = 1.0 * (inputWidth - 1) / (outputWidth - 1) if outputWidth > 1 else 0.0
for h2 in range(outputHeight):
h1r = 1.0 * h2 * rheight
h1 = int(np.floor(h1r))
h1lambda = h1r - h1
h1p = 1 if h1 < (inputHeight - 1) else 0
for w2 in range(outputWidth):
w1r = 1.0 * w2 * rwidth
w1 = int(np.floor(w1r))
w1lambda = w1r - w1
w1p = 1 if w1 < (inputHeight - 1) else 0
for b in range(batch):
for c in range(channel):
y[b][c][h2][w2] = (1-h1lambda)*((1-w1lambda)*x[b][c][h1][w1] + \
w1lambda*x[b][c][h1][w1+w1p]) + \
h1lambda*((1-w1lambda)*x[b][c][h1+h1p][w1] + \
w1lambda*x[b][c][h1+h1p][w1+w1p])
return y
def _init_bilinear(arr):
weight = np.zeros(np.prod(arr.shape), dtype='float32')
shape = arr.shape
f = np.ceil(shape[3] / 2.)
c = (2 * f - 1 - f % 2) / (2. * f)
for i in range(np.prod(shape)):
x = i % shape[3]
y = (i // shape[3]) % shape[2]
weight[i] = (1 - abs(x / f - c)) * (1 - abs(y / f - c))
arr[:] = weight.reshape(shape)
return arr
arr = {'data': mx.random.uniform(-10.0, 10.0, data_shape, ctx=mx.cpu()).copyto(default_context()),
'weight': mx.nd.array(_init_bilinear(mx.ndarray.empty(weight_shape).asnumpy()))}

up = mx.sym.UpSampling(mx.sym.Variable('data'),
mx.sym.Variable('weight'), sample_type='bilinear', scale=root_scale,
num_filter=num_filter, num_args=2)
arg_shapes, out_shapes, _ = up.infer_shape(data=data_shape)
arr_grad = [mx.nd.empty(s) for s in arg_shapes]
exe = up.bind(default_context(), args=arr, args_grad=arr_grad)
exe.forward(is_train=True)
out = exe.outputs[0].asnumpy()
exe.backward(exe.outputs)
for k in range(len(shapes)):
name = 'arg_%d'%k
assert_allclose(arr[name].asnumpy()*root_scale**2*scale**(2*k), arr_grad[name].asnumpy(), rtol=1e-4)
assert_allclose(out, py_bilinear_resize(arr['data'].asnumpy(), data_shape[2]*root_scale, data_shape[3]*root_scale), rtol=1e-4)


@with_seed()
Expand All @@ -1514,6 +1552,20 @@ def test_nearest_upsampling():
check_nearest_upsampling_with_shape(shapes, scale, root_scale)


@with_seed()
def test_bilinear_upsampling():
for root_scale in [2,3]:
for scale in [1,2,3]:
for num_filter in [1,2,3]:
for base in [1,2,3]:
# bilinear upsampling takes only 1 data and 1 weight
# multi input mode is not applicable
dimension = base*root_scale*scale
kernel = 2 * root_scale - root_scale % 2
data_shape = (1, num_filter, dimension, dimension)
weight_shape = (num_filter, 1, kernel, kernel)
check_bilinear_upsampling_with_shape(data_shape, weight_shape, scale, root_scale, num_filter)

@with_seed()
def test_batchnorm_training():
def check_batchnorm_training(stype):
Expand Down

0 comments on commit 8427a97

Please sign in to comment.