From 0b08e1176ebc6d3e6fb0e450fa723f336a098eac Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Fri, 15 Dec 2017 13:42:51 +0900 Subject: [PATCH 01/12] add upsampling cpu op --- topi/python/topi/generic/nn.py | 5 ++- topi/python/topi/nn/__init__.py | 1 + topi/python/topi/nn/upsampling.py | 28 +++++++++++++++ topi/tests/python/test_topi_upsampling.py | 43 +++++++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 topi/python/topi/nn/upsampling.py create mode 100644 topi/tests/python/test_topi_upsampling.py diff --git a/topi/python/topi/generic/nn.py b/topi/python/topi/generic/nn.py index d606213a5270..903f6c60246f 100644 --- a/topi/python/topi/generic/nn.py +++ b/topi/python/topi/generic/nn.py @@ -177,7 +177,6 @@ def schedule_global_pool(outs): """ return _default_schedule(outs, False) - @tvm.target.generic_func def schedule_binarize_pack(outs): """Schedule for binarize_pack @@ -211,4 +210,8 @@ def schedule_binary_dense(outs): sch: Schedule The computation schedule for the op. """ + + +@tvm.target.generic_func +def schedule_upsampling(outs): return _default_schedule(outs, False) diff --git a/topi/python/topi/nn/__init__.py b/topi/python/topi/nn/__init__.py index 3cdf3122e78e..918f399f503c 100644 --- a/topi/python/topi/nn/__init__.py +++ b/topi/python/topi/nn/__init__.py @@ -14,3 +14,4 @@ from .softmax import * from .conv2d_transpose import * from .bnn import * +from .upsampling import * diff --git a/topi/python/topi/nn/upsampling.py b/topi/python/topi/nn/upsampling.py new file mode 100644 index 000000000000..96fc57e8499b --- /dev/null +++ b/topi/python/topi/nn/upsampling.py @@ -0,0 +1,28 @@ +"""TVM operator upsampling compute.""" +from __future__ import absolute_import +import tvm +from .pad import pad +from .util import get_pad_tuple +from .. import util +from .. import tag + + +def upsampling(data, scale): + """Only supports nearest neighbor for now. + + Parameters + ---------- + data : tvm.Tensor + 4-D with shape [batch, channel, in_height, in_width] + + scale: int + upsampling scaling factor + + """ + + batch, channel, height, width = data.shape + out_height = height * scale + out_width = width * scale + + return tvm.compute((batch, channel, out_height, out_width), \ + lambda n, c, h, w: data[n, c, h/scale, w/scale]) diff --git a/topi/tests/python/test_topi_upsampling.py b/topi/tests/python/test_topi_upsampling.py new file mode 100644 index 000000000000..794e581e42c0 --- /dev/null +++ b/topi/tests/python/test_topi_upsampling.py @@ -0,0 +1,43 @@ +"""Test code for upsampling""" +import numpy as np +import tvm +import topi +import math +from skimage.transform import resize + +def verify_upsampling(batch, in_channel, in_height, in_width, scale): + A = tvm.placeholder((batch, in_channel, in_height, in_width), name='A') + B = topi.nn.upsampling(A, scale) + out_shape = (batch, in_channel, in_height*scale, in_width*scale) + dtype = A.dtype + + a_np = np.random.uniform(size=(batch, in_channel, in_height, in_width)).astype(dtype) + b_np = np.zeros(out_shape, dtype=dtype) + for b in range(batch): + for c in range(in_channel): + b_np[b,c,:,:] = resize(a_np[b,c,:,:] , (in_height*scale, in_width*scale), order=0, mode="reflect") + + def check_device(device): + if not tvm.module.enabled(device): + print("Skip because %s is not enabled" % device) + return + print("Running on target: %s" % device) + with tvm.target.create(device): + s = topi.generic.schedule_upsampling(B) + ctx = tvm.context(device, 0) + a = tvm.nd.array(a_np, ctx) + b = tvm.nd.array(np.zeros(out_shape, dtype=dtype), ctx) + f = tvm.build(s, [A, B], device) + f(a, b) + + print("Diff:", np.sum(np.abs(b.asnumpy() - b_np))) + np.testing.assert_allclose(b.asnumpy(), b_np, rtol=1e-5) + + for device in ['llvm']: + check_device(device) + +def test_upsampling(): + verify_upsampling(8, 16, 32, 32, 2) + +if __name__ == "__main__": + test_upsampling() From 89748e934a2bf74b9793e396e9a887be58c881a4 Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Wed, 27 Dec 2017 17:52:51 +0900 Subject: [PATCH 02/12] add upsampling gpu schedule --- topi/python/topi/cuda/__init__.py | 1 + topi/python/topi/cuda/upsampling.py | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 topi/python/topi/cuda/upsampling.py diff --git a/topi/python/topi/cuda/__init__.py b/topi/python/topi/cuda/__init__.py index f829a9895fd2..444097711920 100644 --- a/topi/python/topi/cuda/__init__.py +++ b/topi/python/topi/cuda/__init__.py @@ -15,3 +15,4 @@ from .pooling import schedule_pool, schedule_global_pool from .conv2d_transpose_nchw import schedule_conv2d_transpose_nchw from .extern import schedule_extern +from .upsampling import schedule_upsampling diff --git a/topi/python/topi/cuda/upsampling.py b/topi/python/topi/cuda/upsampling.py new file mode 100644 index 000000000000..96b29d50e6b2 --- /dev/null +++ b/topi/python/topi/cuda/upsampling.py @@ -0,0 +1,9 @@ +# pylint: disable=invalid-name, unused-variable, +"""Schedule for upsampling operator""" +import tvm +from .. import generic +from .injective import schedule_injective + +@generic.schedule_upsampling.register(["cuda", "gpu"]) +def schedule_upsampling(outs): + return schedule_injective(outs) From 7fb86204afdd859f98d871a8fd58a1d5bf4b8352 Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Fri, 5 Jan 2018 23:10:31 +0900 Subject: [PATCH 03/12] add doc for upsampling op add more doc --- topi/python/topi/cuda/upsampling.py | 13 +++++++++++++ topi/python/topi/nn/upsampling.py | 8 ++++++-- topi/tests/python/test_topi_upsampling.py | 4 ++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/topi/python/topi/cuda/upsampling.py b/topi/python/topi/cuda/upsampling.py index 96b29d50e6b2..af37ea2cd1cd 100644 --- a/topi/python/topi/cuda/upsampling.py +++ b/topi/python/topi/cuda/upsampling.py @@ -6,4 +6,17 @@ @generic.schedule_upsampling.register(["cuda", "gpu"]) def schedule_upsampling(outs): + """Schedule for upsampling op. + + Parameters + ---------- + outs: Array of Tensor + The computation graph description of upsampling in the format + of an array of tensors. + + Returns + ------- + sch: Schedule + The computation schedule for the op. + """ return schedule_injective(outs) diff --git a/topi/python/topi/nn/upsampling.py b/topi/python/topi/nn/upsampling.py index 96fc57e8499b..4d49a5200de0 100644 --- a/topi/python/topi/nn/upsampling.py +++ b/topi/python/topi/nn/upsampling.py @@ -8,7 +8,8 @@ def upsampling(data, scale): - """Only supports nearest neighbor for now. + """Perform nearest neighbor upsampling on the data. + Bilinear upsampling is not supported. Parameters ---------- @@ -18,8 +19,11 @@ def upsampling(data, scale): scale: int upsampling scaling factor + Returns + ------- + output : tvm.Tensor + 4-D with shape [batch, channel, in_height*scale, in_width*scale] """ - batch, channel, height, width = data.shape out_height = height * scale out_width = width * scale diff --git a/topi/tests/python/test_topi_upsampling.py b/topi/tests/python/test_topi_upsampling.py index 794e581e42c0..0ac7f885dada 100644 --- a/topi/tests/python/test_topi_upsampling.py +++ b/topi/tests/python/test_topi_upsampling.py @@ -30,14 +30,14 @@ def check_device(device): f = tvm.build(s, [A, B], device) f(a, b) - print("Diff:", np.sum(np.abs(b.asnumpy() - b_np))) np.testing.assert_allclose(b.asnumpy(), b_np, rtol=1e-5) - for device in ['llvm']: + for device in ['llvm', 'cuda']: check_device(device) def test_upsampling(): verify_upsampling(8, 16, 32, 32, 2) + verify_upsampling(12, 32, 64, 64, 3) if __name__ == "__main__": test_upsampling() From 0028a1dd1df291d0b84ec6246b02eb5450d123ca Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Sat, 6 Jan 2018 16:50:55 +0900 Subject: [PATCH 04/12] cleanup upsampling test --- topi/python/topi/testing/__init__.py | 1 + topi/python/topi/testing/upsampling_python.py | 15 +++++++++++++++ topi/tests/python/test_topi_upsampling.py | 5 +---- 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 topi/python/topi/testing/upsampling_python.py diff --git a/topi/python/topi/testing/__init__.py b/topi/python/topi/testing/__init__.py index 3a43a04437a1..6a1b361e3097 100644 --- a/topi/python/topi/testing/__init__.py +++ b/topi/python/topi/testing/__init__.py @@ -10,3 +10,4 @@ from .depthwise_conv2d_python import depthwise_conv2d_python_nchw, depthwise_conv2d_python_nhwc from .dilate_python import dilate_python from .softmax_python import softmax_python, log_softmax_python +from .upsampling_python import upsampling_python diff --git a/topi/python/topi/testing/upsampling_python.py b/topi/python/topi/testing/upsampling_python.py new file mode 100644 index 000000000000..fbc83965c1dc --- /dev/null +++ b/topi/python/topi/testing/upsampling_python.py @@ -0,0 +1,15 @@ +# pylint: disable=invalid-name, line-too-long, unused-variable, too-many-locals +"""Upsampling in python""" +import numpy as np +import scipy.signal +from skimage.transform import resize + + +def upsampling_python(data, scale): + ishape = data.shape + oshape = (ishape[0], ishape[1], ishape[2]*scale, ishape[3]*scale) + output_np = np.zeros(oshape, dtype=data.dtype) + for b in range(oshape[0]): + for c in range(oshape[1]): + output_np[b,c,:,:] = resize(data[b,c,:,:] , (oshape[2], oshape[3]), order=0, mode="reflect") + return output_np diff --git a/topi/tests/python/test_topi_upsampling.py b/topi/tests/python/test_topi_upsampling.py index 0ac7f885dada..c3f613e86ef3 100644 --- a/topi/tests/python/test_topi_upsampling.py +++ b/topi/tests/python/test_topi_upsampling.py @@ -12,10 +12,7 @@ def verify_upsampling(batch, in_channel, in_height, in_width, scale): dtype = A.dtype a_np = np.random.uniform(size=(batch, in_channel, in_height, in_width)).astype(dtype) - b_np = np.zeros(out_shape, dtype=dtype) - for b in range(batch): - for c in range(in_channel): - b_np[b,c,:,:] = resize(a_np[b,c,:,:] , (in_height*scale, in_width*scale), order=0, mode="reflect") + b_np = topi.testing.upsampling_python(a_np, scale) def check_device(device): if not tvm.module.enabled(device): From e3d8b5ce20cc75d6f76d48348b39a9ab450bda7e Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Tue, 9 Jan 2018 11:37:29 +0900 Subject: [PATCH 05/12] add doc --- topi/python/topi/generic/nn.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/topi/python/topi/generic/nn.py b/topi/python/topi/generic/nn.py index 903f6c60246f..8c83a30fc1f1 100644 --- a/topi/python/topi/generic/nn.py +++ b/topi/python/topi/generic/nn.py @@ -214,4 +214,17 @@ def schedule_binary_dense(outs): @tvm.target.generic_func def schedule_upsampling(outs): + """Schedule for upsampling + + Parameters + ---------- + outs: Array of Tensor + The computation graph description of upsampling + in the format of an array of tensors. + + Returns + ------- + sch: Schedule + The computation schedule for the op. + """ return _default_schedule(outs, False) From e74e424fce52cf9fc35c82b6ba718fb01b1b0897 Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Wed, 10 Jan 2018 21:20:20 +0900 Subject: [PATCH 06/12] fix lint --- topi/python/topi/generic/nn.py | 1 + topi/python/topi/testing/upsampling_python.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/topi/python/topi/generic/nn.py b/topi/python/topi/generic/nn.py index 8c83a30fc1f1..39d61c1c4d4d 100644 --- a/topi/python/topi/generic/nn.py +++ b/topi/python/topi/generic/nn.py @@ -210,6 +210,7 @@ def schedule_binary_dense(outs): sch: Schedule The computation schedule for the op. """ + return _default_schedule(outs, False) @tvm.target.generic_func diff --git a/topi/python/topi/testing/upsampling_python.py b/topi/python/topi/testing/upsampling_python.py index fbc83965c1dc..db9079f0e8da 100644 --- a/topi/python/topi/testing/upsampling_python.py +++ b/topi/python/topi/testing/upsampling_python.py @@ -11,5 +11,5 @@ def upsampling_python(data, scale): output_np = np.zeros(oshape, dtype=data.dtype) for b in range(oshape[0]): for c in range(oshape[1]): - output_np[b,c,:,:] = resize(data[b,c,:,:] , (oshape[2], oshape[3]), order=0, mode="reflect") + output_np[b, c, :, :] = resize(data[b, c, :, :] , (oshape[2], oshape[3]), order=0, mode="reflect") return output_np From 24463d6fac8ba5469b170dcdd18cf7fa32c4e9b4 Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Wed, 10 Jan 2018 21:22:54 +0900 Subject: [PATCH 07/12] fix lint --- topi/python/topi/testing/upsampling_python.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/topi/python/topi/testing/upsampling_python.py b/topi/python/topi/testing/upsampling_python.py index db9079f0e8da..ff0e46384cdd 100644 --- a/topi/python/topi/testing/upsampling_python.py +++ b/topi/python/topi/testing/upsampling_python.py @@ -1,7 +1,6 @@ # pylint: disable=invalid-name, line-too-long, unused-variable, too-many-locals """Upsampling in python""" import numpy as np -import scipy.signal from skimage.transform import resize @@ -11,5 +10,5 @@ def upsampling_python(data, scale): output_np = np.zeros(oshape, dtype=data.dtype) for b in range(oshape[0]): for c in range(oshape[1]): - output_np[b, c, :, :] = resize(data[b, c, :, :] , (oshape[2], oshape[3]), order=0, mode="reflect") + output_np[b, c, :, :] = resize(data[b, c, :, :], (oshape[2], oshape[3]), order=0, mode="reflect") return output_np From 32efa106e65a2f5b601da41d51e2f3ba690665ec Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Wed, 10 Jan 2018 21:24:50 +0900 Subject: [PATCH 08/12] fix lint --- topi/python/topi/cuda/upsampling.py | 1 - 1 file changed, 1 deletion(-) diff --git a/topi/python/topi/cuda/upsampling.py b/topi/python/topi/cuda/upsampling.py index af37ea2cd1cd..634ec701d6ff 100644 --- a/topi/python/topi/cuda/upsampling.py +++ b/topi/python/topi/cuda/upsampling.py @@ -1,6 +1,5 @@ # pylint: disable=invalid-name, unused-variable, """Schedule for upsampling operator""" -import tvm from .. import generic from .injective import schedule_injective From b499515c619278f28aeadb7aeb77cd39557686d1 Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Wed, 10 Jan 2018 21:28:35 +0900 Subject: [PATCH 09/12] remove unused import --- topi/python/topi/nn/upsampling.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/topi/python/topi/nn/upsampling.py b/topi/python/topi/nn/upsampling.py index 4d49a5200de0..e1234741e286 100644 --- a/topi/python/topi/nn/upsampling.py +++ b/topi/python/topi/nn/upsampling.py @@ -1,10 +1,6 @@ """TVM operator upsampling compute.""" from __future__ import absolute_import import tvm -from .pad import pad -from .util import get_pad_tuple -from .. import util -from .. import tag def upsampling(data, scale): From beb681c5471b6a69dc2829ac5a41201e23a6348b Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Thu, 11 Jan 2018 09:28:49 +0900 Subject: [PATCH 10/12] remove skimage dependency --- topi/python/topi/testing/upsampling_python.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/topi/python/topi/testing/upsampling_python.py b/topi/python/topi/testing/upsampling_python.py index ff0e46384cdd..328c7a5a0bc1 100644 --- a/topi/python/topi/testing/upsampling_python.py +++ b/topi/python/topi/testing/upsampling_python.py @@ -1,8 +1,9 @@ # pylint: disable=invalid-name, line-too-long, unused-variable, too-many-locals """Upsampling in python""" import numpy as np -from skimage.transform import resize +def upsample_nearest(arr, scale): + return arr.repeat(scale, axis=0).repeat(scale, axis=1) def upsampling_python(data, scale): ishape = data.shape @@ -10,5 +11,5 @@ def upsampling_python(data, scale): output_np = np.zeros(oshape, dtype=data.dtype) for b in range(oshape[0]): for c in range(oshape[1]): - output_np[b, c, :, :] = resize(data[b, c, :, :], (oshape[2], oshape[3]), order=0, mode="reflect") + output_np[b, c, :, :] = upsample_nearest(data[b, c, :, :], scale) return output_np From dc4fff6fc1baefd7d882a985c5a6894ea67a4ced Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Thu, 11 Jan 2018 10:30:59 +0900 Subject: [PATCH 11/12] remove skimage import --- topi/tests/python/test_topi_upsampling.py | 1 - 1 file changed, 1 deletion(-) diff --git a/topi/tests/python/test_topi_upsampling.py b/topi/tests/python/test_topi_upsampling.py index c3f613e86ef3..9a9606736c1f 100644 --- a/topi/tests/python/test_topi_upsampling.py +++ b/topi/tests/python/test_topi_upsampling.py @@ -3,7 +3,6 @@ import tvm import topi import math -from skimage.transform import resize def verify_upsampling(batch, in_channel, in_height, in_width, scale): A = tvm.placeholder((batch, in_channel, in_height, in_width), name='A') From 84c33ee4b3245f4319db0dee54d16ac8e86322bb Mon Sep 17 00:00:00 2001 From: Masahiro Masuda Date: Thu, 11 Jan 2018 23:50:00 +0900 Subject: [PATCH 12/12] remove schedule_upsampling --- topi/python/topi/cuda/__init__.py | 1 - topi/python/topi/cuda/upsampling.py | 21 --------------------- topi/python/topi/generic/nn.py | 18 ------------------ topi/tests/python/test_topi_upsampling.py | 2 +- 4 files changed, 1 insertion(+), 41 deletions(-) delete mode 100644 topi/python/topi/cuda/upsampling.py diff --git a/topi/python/topi/cuda/__init__.py b/topi/python/topi/cuda/__init__.py index 444097711920..f829a9895fd2 100644 --- a/topi/python/topi/cuda/__init__.py +++ b/topi/python/topi/cuda/__init__.py @@ -15,4 +15,3 @@ from .pooling import schedule_pool, schedule_global_pool from .conv2d_transpose_nchw import schedule_conv2d_transpose_nchw from .extern import schedule_extern -from .upsampling import schedule_upsampling diff --git a/topi/python/topi/cuda/upsampling.py b/topi/python/topi/cuda/upsampling.py deleted file mode 100644 index 634ec701d6ff..000000000000 --- a/topi/python/topi/cuda/upsampling.py +++ /dev/null @@ -1,21 +0,0 @@ -# pylint: disable=invalid-name, unused-variable, -"""Schedule for upsampling operator""" -from .. import generic -from .injective import schedule_injective - -@generic.schedule_upsampling.register(["cuda", "gpu"]) -def schedule_upsampling(outs): - """Schedule for upsampling op. - - Parameters - ---------- - outs: Array of Tensor - The computation graph description of upsampling in the format - of an array of tensors. - - Returns - ------- - sch: Schedule - The computation schedule for the op. - """ - return schedule_injective(outs) diff --git a/topi/python/topi/generic/nn.py b/topi/python/topi/generic/nn.py index 39d61c1c4d4d..6f641e99f7dd 100644 --- a/topi/python/topi/generic/nn.py +++ b/topi/python/topi/generic/nn.py @@ -211,21 +211,3 @@ def schedule_binary_dense(outs): The computation schedule for the op. """ return _default_schedule(outs, False) - - -@tvm.target.generic_func -def schedule_upsampling(outs): - """Schedule for upsampling - - Parameters - ---------- - outs: Array of Tensor - The computation graph description of upsampling - in the format of an array of tensors. - - Returns - ------- - sch: Schedule - The computation schedule for the op. - """ - return _default_schedule(outs, False) diff --git a/topi/tests/python/test_topi_upsampling.py b/topi/tests/python/test_topi_upsampling.py index 9a9606736c1f..08b8f987694d 100644 --- a/topi/tests/python/test_topi_upsampling.py +++ b/topi/tests/python/test_topi_upsampling.py @@ -19,7 +19,7 @@ def check_device(device): return print("Running on target: %s" % device) with tvm.target.create(device): - s = topi.generic.schedule_upsampling(B) + s = topi.generic.schedule_injective(B) ctx = tvm.context(device, 0) a = tvm.nd.array(a_np, ctx) b = tvm.nd.array(np.zeros(out_shape, dtype=dtype), ctx)