Skip to content

Conversation

@Dayananda-V
Copy link
Contributor

  1. Tensorflow input parse logic updated
  2. LRN bugfix
  3. Split operator added
  4. Input shapes parse logic updated
    To support alexnet on tensorflow(https://www.cs.toronto.edu/~guerzhoy/tf_alexnet/)

Thanks for contributing to TVM! Please refer to guideline https://docs.tvm.ai/contribute/ for useful information and tips. After the pull request is submitted, please request code reviews from others in the community.

1. Tensorflow input parse logic updated
2. LRN bugfix
3. Split operator added
4. Input shapes parse logic updated
To support alexnet on tensorflow(https://www.cs.toronto.edu/~guerzhoy/tf_alexnet/)
1. Compilation issue fix
2. '_input_shapes' handle fix across operator frontend
@Dayananda-V
Copy link
Contributor Author

This PR adds Split, LRN operator and shape parse logic,
@srkreddy1238 @yzhliu Could you please review?

attr_new['alpha'] = attr.get('alpha', 1) * size
attr_new['beta'] = attr.get('beta', 0.5)
return AttrCvt(op_name='lrn')(new_inputs, attr_new)
depth_radius = attr.get('depth_radius', 2)
Copy link
Contributor

Choose a reason for hiding this comment

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

LRN is already implemented earlier. Any reason for this modify ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously implemented LRN was wrong as per TF formula and input tensor is never pass to operator, this leak observe after run the AlexNet model

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that was a bug passing empty inputs new_inputs = []
Removing new_inputs and using inputs instead should work for alexnet as well.
Keep the remaining implementation default value handling.

@siju-samuel can you confirm the depth_radius default value as 5 which results size=11 ? or is it a typo !!

   depth_radius = attr.get('depth_radius', 5)
   size = (depth_radius * 2) + 1

Test case was passed as the input was taken from params instead.
#1546 should fix this issue by replacing all const inputs to Placeholders.

Copy link
Member

Choose a reason for hiding this comment

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

@srkreddy1238 as per tensorflow, the default value of depth_radius is 5. So the size will become 11.
https://www.tensorflow.org/api_docs/python/tf/nn/local_response_normalization

if attr['data_format'] == 'NHWC':
in_h = input_shapes[0][1]
in_w = input_shapes[0][2]
in_h = input_shapes[1]
Copy link
Contributor

Choose a reason for hiding this comment

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

input_shapes -> input_shape


# Extract kernel shape from params
conv_param_weights = params[inputs[1].list_output_names()[0]]
if inputs[1] in attr['_input_shapes']:
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you share details of use case where convolution weights not in params ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This case is observe while running AlexNet model.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is weights output of another node in runtime?

Can u share serialised tensor flow graph?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fine tuned Alexnet can be found here for the above reference

pop_node = inputs.pop(0)
axis = params[pop_node.list_output_names()[0]].asnumpy()[0]
return AttrCvt(op_name="split",
ignores=['num_split'],
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest to use transforms instead of ignores & extras.

Copy link
Member

Choose a reason for hiding this comment

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

Have you reached consensus the changes here is good?

input_sym = self._nodes[node_input_key].__getitem__(slot_num)
except NNVMError:
# TODO: Fancy node name invalid slot neglect
input_sym = self._nodes[node_input_key].__getitem__(0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you share details on this fall back?
I see slot_num is initialized to 0 above already.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some of Layer implementation in frontend(LSTM, Ptb) this assign fancy node name (eg. Model/RNN/cell_0/RnnCell:6 ), in this case 6 is slot updated as per our logic and only one node will be present, in this fancy naming convention we need to parse the default(0) node.

input_shapes[input_sym] = self._output_shapes[
node_input_key].__getitem__(slot_num)
attr['_input_shapes'] = input_shapes
except KeyError:
Copy link
Contributor

Choose a reason for hiding this comment

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

This except is for the same statement below where the self._nodes doesn't have key node_input_key
input_sym = self._nodes[node_input_key].__getitem__(slot_num)

Suggest to make one try block with multiple except instead of nested.

Copy link
Contributor Author

@Dayananda-V Dayananda-V Aug 9, 2018

Choose a reason for hiding this comment

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

as address to review comment, we need nested try block to handle fancy node name convention logic.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's the same statement which cause 2 exceptions in two different cases, one try on input_sym = self._nodes[node_input_key].__getitem__(slot_num) with two except should work. Pls check.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

First try block we are trying to access the slot base input and after caught NNVMError we are trying to access default(0) location input, in this case if we caught error should be thrown to user.

tf_output = run_tf_graph(sess, [np_data], ['in_data:0'], 'split:0')
tvm_output = run_tvm_graph(final_graph_def, [np_data], ['in_data'],
tf_output.shape, dtype)
np.testing.assert_allclose(tf_output, tvm_output, atol=1e-5, rtol=1e-5)
Copy link
Contributor

Choose a reason for hiding this comment

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

Split results in multiple outputs, hence suggest to enhance run methods to return multiple outputs and compare the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Split returns the split result with list so comparing with List content result.

Copy link
Contributor

Choose a reason for hiding this comment

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

run_tf_graph is not designed to result a list. Also we pass 'split:0' to return only first result.
run_tvm_graph can return list if output_shape, output_dtypes are lists.

Please check.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah missed scenario, will work on it.

def test_forward_split():
'''test split operator'''
_test_split((2, 3), 2, axis=0)
_test_split((6, 3), 3, axis=0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Pls add a case for -ve axis

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Split axis should be range (0, rank(value)). so negative axis is not added


def _split():
def _impl(inputs, attr, params):
pop_node = inputs.pop(0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Ref. Split
tensorflow has two variations for split as num_split and size_split.
Please handle if possible or raise exception accordingly.

If handling size_split please handle num attribute if possible or raise exception accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Split had num_split attribute and SplitV had size_splits attribute. As of this PR supported Split operator

if inputs[1] in attr['_input_shapes']:
conv_param_weights = tuple(attr['_input_shapes'][inputs[1]])
else:
conv_param_weights = params[inputs[1].list_output_names()[0]].shape
Copy link
Contributor

Choose a reason for hiding this comment

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

conv_param_weights -> weights_shape (it's no more a param hence.)

attr_new['alpha'] = attr.get('alpha', 1) * size
attr_new['beta'] = attr.get('beta', 0.5)
return AttrCvt(op_name='lrn')(new_inputs, attr_new)
depth_radius = attr.get('depth_radius', 2)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that was a bug passing empty inputs new_inputs = []
Removing new_inputs and using inputs instead should work for alexnet as well.
Keep the remaining implementation default value handling.

@siju-samuel can you confirm the depth_radius default value as 5 which results size=11 ? or is it a typo !!

   depth_radius = attr.get('depth_radius', 5)
   size = (depth_radius * 2) + 1

Test case was passed as the input was taken from params instead.
#1546 should fix this issue by replacing all const inputs to Placeholders.

input_shapes[input_sym] = self._output_shapes[
node_input_key].__getitem__(slot_num)
attr['_input_shapes'] = input_shapes
except KeyError:
Copy link
Contributor

Choose a reason for hiding this comment

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

It's the same statement which cause 2 exceptions in two different cases, one try on input_sym = self._nodes[node_input_key].__getitem__(slot_num) with two except should work. Pls check.

1. Review comment reworked.
tf_output = run_tf_graph(sess, [np_data], ['in_data:0'], 'split:0')
tvm_output = run_tvm_graph(final_graph_def, [np_data], ['in_data'],
tf_output.shape, dtype)
np.testing.assert_allclose(tf_output, tvm_output, atol=1e-5, rtol=1e-5)
Copy link
Contributor

Choose a reason for hiding this comment

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

run_tf_graph is not designed to result a list. Also we pass 'split:0' to return only first result.
run_tvm_graph can return list if output_shape, output_dtypes are lists.

Please check.

1. run_tf_graph handle for multiple output node names support.
2. Split test case review comment reworked.
@Dayananda-V
Copy link
Contributor Author

thanks @srkreddy1238.

Reworked for accepted review comments. Welcome more review from @yzhliu

output_data = sess.run(tensor, input_dict)
if isinstance(output_node, list):
output_list = []
for node_name in output_node:
Copy link
Contributor

Choose a reason for hiding this comment

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

Can run one time by passing list of tensors to run method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Address the problem with update rework solution.

@yzhliu yzhliu added status: review in progress status: need update need update based on feedbacks labels Aug 13, 2018
@yzhliu yzhliu self-assigned this Aug 20, 2018
@yzhliu
Copy link
Member

yzhliu commented Aug 26, 2018

@dayanandasiet Can you confirm the attr implement (where @srkreddy1238 comment 'Suggest to use transforms instead of ignores & extras') is good? Otherwise LGTM. Thanks.

bias=bias,
name="local_response_normalization")
np_data = np.random.uniform(size=ip_shape).astype(dtype)
with tf.Session() as sess:
Copy link
Contributor

Choose a reason for hiding this comment

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

#1546 adds compare_tf_with_tvm which does comparing tf and tvm.

Suggest to use it.

#######################################################################
# LRN (Local Response Normalization)
# ----------------------------------
def _test_lrn(ip_shape, depth_radius=2, alpha=1e-05, beta=0.75, bias=1.0):
Copy link
Contributor

Choose a reason for hiding this comment

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

Ref. we don't need to explicitly set the default values. TF can handle them if not set.

Suggest to ignore to allow TF default values play role here.

@yuruofeifei
Copy link
Contributor

Hi @dayanandasiet ,
Do you have any update on this PR?

@yzhliu
Copy link
Member

yzhliu commented Sep 18, 2018

Any update @dayanandasiet ?

@yuruofeifei
Copy link
Contributor

Hi @dayanandasiet , do you have any update on this PR?
I'm currently working on TF convertor which will need split operator.

@Dayananda-V
Copy link
Contributor Author

Will fix review comment and push this changes with new PR. As of now closing this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants