New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CPP implementation of L2Norm and LRN ops #1157
Conversation
@sxjscience can you please do a round of code review? |
7ceb221
to
d351033
Compare
I have added nnvm frontend support for lrn and l2norm ops. Please review |
2070956
to
eca71e4
Compare
@tqchen, I am facing a build error only in the i386 environment with an unrelated source code. Could you please help for a clean build to see whether it is an environment issue? |
@kazum @merrymercy can you help review this PR? |
eca71e4
to
2e11f32
Compare
nnvm/include/nnvm/top/nn.h
Outdated
DMLC_DECLARE_FIELD(axis) | ||
.describe("input data layout channel axis"); | ||
DMLC_DECLARE_FIELD(alpha) | ||
.describe("alpha constant."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scaling parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
nnvm/include/nnvm/top/nn.h
Outdated
DMLC_DECLARE_FIELD(alpha) | ||
.describe("alpha constant."); | ||
DMLC_DECLARE_FIELD(beta) | ||
.describe("beta constant."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exponent number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
nnvm/include/nnvm/top/nn.h
Outdated
DMLC_DECLARE_FIELD(beta) | ||
.describe("beta constant."); | ||
DMLC_DECLARE_FIELD(bias) | ||
.describe("bias constant."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The offset parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
nnvm/python/nnvm/top/vision.py
Outdated
import tvm | ||
import topi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lint is suggesting this change.
offset to avoid dividing by 0. constant value | ||
|
||
alpha : float | ||
contant valie |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constant value
topi/include/topi/cuda/nn.h
Outdated
auto lrn = outs[0]; | ||
auto sqr_sum_up = lrn->op->InputTensors()[1]; | ||
auto sqr_sum = sqr_sum_up->op->InputTensors()[0]; | ||
auto set_pad = sqr_sum->op->InputTensors()[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to use concrete types:
https://github.com/dmlc/tvm/blob/master/docs/contribute/code_guide.rst#c-code-styles
topi/include/topi/nn.h
Outdated
@@ -1,7 +1,7 @@ | |||
/*! | |||
* Copyright (c) 2017 by Contributors | |||
* \brief NN op constructions | |||
* \file topi/nn.h | |||
* \file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* \file nn.h
?
topi/include/topi/nn/l2_norm.h
Outdated
using namespace tvm; | ||
|
||
/*! | ||
* \brief L2 normalization inference operator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove a trailing space.
std::string tag = kBroadcast) { | ||
CHECK_EQ(data->shape.size(), 4) << "LRN requires 4-D input"; | ||
assert(size % 2 == 1); | ||
assert(axis == 1 || axis == 3); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use CHECK_* macros here because assert() can be compiled out when we define NDEBUG.
2e11f32
to
776989a
Compare
@tqchen I am facing the same error again |
nnvm/include/nnvm/top/nn.h
Outdated
@@ -313,6 +313,41 @@ struct LayoutTransformParam : public dmlc::Parameter<LayoutTransformParam> { | |||
} | |||
}; | |||
|
|||
struct LrnParam : public dmlc::Parameter<LrnParam> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lrn-> LRN
nnvm/src/top/nn/nn.cc
Outdated
DMLC_REGISTER_PARAMETER(L2normParam); | ||
|
||
inline bool L2normInferShape(const nnvm::NodeAttrs& attrs, | ||
std::vector<TShape>* in_shape, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alignment
nnvm/src/top/nn/nn.cc
Outdated
return true; | ||
} | ||
|
||
NNVM_REGISTER_OP(l2norm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l2norm not a typical operator, a typical version os numpy.lingalg.norm, so I would recommend we do not add registration and support proper norm in a separate PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, it is good idea to have generalized norm operation. I will remove L2Norm from this PR and will raise another PR to have generalized norm operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I was confused by the name of the API, the current API is l2_normalize which performs the normalization, instead of calculating the norm.
Let us make the name clear in both TOPI and nnvm.
c.f. related tensorflow API https://www.tensorflow.org/api_docs/python/tf/nn/l2_normalize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
L2 norm operation can be used to perform l2 normalization. If we are planning to add generalized norm op, we can use the same to compute l2 normalization.
sqr_sum[i, j, k, l] = sum(a_np[i, j, k, sum_start:sum_end] * \ | ||
a_np[i, j, k, sum_start:sum_end]) | ||
|
||
for i in range(axis0): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use broadcasting semantics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tqchen , I have tried using broadcast operations further on this changes, but here the sum is doing based on window size and the window move across the axis. could you please elaborate your suggestion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, never mind, it is fine to keep this one as for loop
* | ||
* \return A schedule for the given ops. | ||
*/ | ||
inline Schedule schedule_l2norm(const Target &target, const Array<Tensor>& outs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove L2 norm for now and can do that in separate PR, support norm later.
@@ -365,6 +365,129 @@ def forward(x): | |||
inputs = [('x', (1, 3, 28, 28), x)] | |||
helper(y, inputs, dtype, forward) | |||
|
|||
def verify_lrn(n, c, h, w, size, axis, bias, alpha, beta): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def verify_lrn(ishape, size, axis, bias, alpha, beta)
and using ishape instead of dshape looks simpler.
offset to avoid dividing by 0. constant value | ||
|
||
alpha : float | ||
contant value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constant
radius = size // 2 | ||
sqr_sum = np.zeros(shape=a_np.shape).astype(a_np.dtype) | ||
sqr_sum_up = np.zeros(shape=a_np.shape).astype(a_np.dtype) | ||
lrn_out = np.zeros(shape=a_np.shape).astype(a_np.dtype) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line can be removed.
out_np = lrn_python(x_np, size, axis, bias, alpha, beta) | ||
np.testing.assert_allclose(out.asnumpy(), out_np, atol=1e-5, rtol=1e-5) | ||
|
||
def verify_l2norm(batch, channel, height, width, eps, axis): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def verify_l2norm(ishape, eps, axis)
looks simpler.
std::string tag = kBroadcast) { | ||
CHECK_EQ(data->shape.size(), 4) << "LRN requires 4-D input"; | ||
CHECK_EQ(size % 2, 1) << "size should be odd number"; | ||
CHECK_EQ((axis - 1) && (axis - 3), 0) << "axis should be 1 or 3 for NCHW and NHWC"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CHECK(axis == 1 || axis == 3)
l2norm_out : np.ndarray | ||
4-D with shape [batch, out_channel, out_height, out_width] | ||
""" | ||
batch, axis1, axis2, axis3 = a_np.shape |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
batch = a_np.shape[0]
looks simpler? We don't need axis[1-3].
batch, axis1, axis2, axis3 = a_np.shape | ||
sqr_sum = np.zeros(shape=(batch,)).astype(a_np.dtype) | ||
sqrt_sum = np.zeros(shape=(batch,)).astype(a_np.dtype) | ||
l2norm_out = np.zeros(shape=a_np.shape).astype(a_np.dtype) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be removed.
sqrt_sum = np.sqrt(np.maximum(np.broadcast_to(sqr_sum, a_np.shape), eps)) | ||
return np.divide(a_np, sqrt_sum) | ||
|
||
def verify_l2norm(n, c, h, w, eps, axis=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest def verify_l2norm(shape, eps, axis=None)
.
@@ -0,0 +1,101 @@ | |||
"""Test code for LRN""" | |||
import os |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be removed.
radius = size // 2 | ||
sqr_sum = np.zeros(shape=a_np.shape).astype(a_np.dtype) | ||
sqr_sum_up = np.zeros(shape=a_np.shape).astype(a_np.dtype) | ||
lrn_out = np.zeros(shape=a_np.shape).astype(a_np.dtype) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove.
nnvm/src/top/nn/nn.cc
Outdated
return true; | ||
} | ||
|
||
NNVM_REGISTER_OP(l2norm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I was confused by the name of the API, the current API is l2_normalize which performs the normalization, instead of calculating the norm.
Let us make the name clear in both TOPI and nnvm.
c.f. related tensorflow API https://www.tensorflow.org/api_docs/python/tf/nn/l2_normalize
OK, please fix the comments, remove l2norm or add l2_normalize to this PR and let us aim to prioritize and bring this in. This code review has been hanging for a bit long |
@PariksheetPinjari909 can you act on the comments? Rebase is needed |
776989a
to
fa22f79
Compare
@tqchen Thanks, i was about to commit, then i saw rebase is needed. I have made the l2normalize naming to avoid future confusion. Pls review. |
@kazum Pls review |
nnvm/src/top/nn/nn.cc
Outdated
.set_num_outputs(1) | ||
.set_attr<FInferShape>("FInferShape", L2normalizeInferShape) | ||
.set_attr<FInferType>("FInferType", ElemwiseType<1, 1>) | ||
.set_support_level(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need FCorrectLayout attribute to for correct layout pass.
nnvm/python/nnvm/top/nn.py
Outdated
|
||
reg.register_pattern("lrn", OpPattern.OUT_ELEMWISE_FUSABLE) | ||
|
||
@reg.register_compute("l2normalize") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use l2_normalize(with underscore), to be consistent with tensorflow API
nnvm/python/nnvm/top/nn.py
Outdated
with tvm.target.create(target): | ||
return topi.generic.schedule_lrn(outs) | ||
|
||
reg.register_pattern("lrn", OpPattern.OUT_ELEMWISE_FUSABLE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we confirm if lrn is OUT_ELEMWISE_FUSABLE. We need to add a testcase, lrn followed by relu, and confirm if the test pass on GPU
topi/include/topi/nn/l2_normalize.h
Outdated
* | ||
* \return A Tensor whose op member is the l2 normalization operation | ||
*/ | ||
inline Tensor l2normalize_instance(const Tensor& data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l2_normalize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does instance mean in here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instance name was given with respect to mxnet l2_normalize function, but now we are supporting l2_normalize in all axes so no need to keep the instance name. I will remove it. Thanks for pointing out.
nnvm/python/nnvm/top/nn.py
Outdated
with tvm.target.create(target): | ||
return topi.generic.schedule_l2normalize(outs) | ||
|
||
reg.register_pattern("l2normalize", OpPattern.OUT_ELEMWISE_FUSABLE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we want to mark it as OUT_ELEMWISE_FUSABLE, confirm with a testcase of op + elemwise operator so that it generate testcase for used ops
l2normalize_out : np.ndarray | ||
4-D with shape [batch, out_channel, out_height, out_width] | ||
""" | ||
batch = a_np.shape[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line can be removed.
sqr_sum = np.zeros(shape=(batch,)).astype(a_np.dtype) | ||
sqrt_sum = np.zeros(shape=(batch,)).astype(a_np.dtype) | ||
l2norm_out = np.zeros(shape=a_np.shape).astype(a_np.dtype) | ||
batch = a_np.shape[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
l2normalize_out : np.ndarray | ||
4-D with shape [batch, out_channel, out_height, out_width] | ||
""" | ||
batch = a_np.shape[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
f60cb88
to
91b6025
Compare
nnvm/src/top/nn/nn.cc
Outdated
|
||
NNVM_REGISTER_OP(lrn) | ||
.describe(R"code(LRN layer)code" NNVM_ADD_FILELINE) | ||
.add_argument("data", "4D Tesndor", "Input data.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"4D Tensor"
nnvm/src/top/nn/nn.cc
Outdated
|
||
NNVM_REGISTER_OP(l2_normalize) | ||
.describe(R"code(L2NORMALIZE layer)code" NNVM_ADD_FILELINE) | ||
.add_argument("data", "4D Tesndor", "Input data.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"4D Tensor"
axis0, axis1, axis2, axis3 = a_np.shape | ||
radius = size // 2 | ||
sqr_sum = np.zeros(shape=a_np.shape).astype(a_np.dtype) | ||
def sum_dot_values(i, j, k, l): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from itertools import product
for i, j, k, l in product(*[range(_axis) for _axis in a_np.shape]):
and we can remove the nested loop below. I think this cleanup is matter of taste, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it looks nicer now. Thanks
91b6025
to
e91e6d1
Compare
dtype = "float32" | ||
x_np = np.random.uniform(size=ishape).astype(dtype) | ||
|
||
def l2_normalize_python(a_np, eps, axis=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move this function to topi.testing.
dtype = "float32" | ||
x_np = np.random.uniform(size=ishape).astype(dtype) | ||
|
||
def lrn_python(a_np, size, axis, bias, alpha, beta): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move this function to topi.testing
Some final comments and should be approved from my side, await @kazum 's comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good from my side, thanks!
@PariksheetPinjari909 please act on my final comments |
e91e6d1
to
c2ca9c2
Compare
@tqchen all reviews are handled now. Pls check. |
Thanks! This is merged! |
This PR has CPP implementation of LRN and L2Norm. It also redirect LRN and L2Norm python ops to CPP ops.