-
Notifications
You must be signed in to change notification settings - Fork 3.8k
TensorFlow Split and LRN operator support for Alexnet Model #1572
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
Conversation
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
1. Pylint issue fix
1. Comment refactored
|
This PR adds Split, LRN operator and shape parse logic, |
| 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) |
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 is already implemented earlier. Any reason for this modify ?
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.
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
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 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.
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.
@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] |
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.
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']: |
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 you share details of use case where convolution weights not in params ?
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 case is observe while running AlexNet model.
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.
Is weights output of another node in runtime?
Can u share serialised tensor flow graph?
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.
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'], |
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.
Suggest to use transforms instead of ignores & extras.
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.
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) |
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 you share details on this fall back?
I see slot_num is initialized to 0 above already.
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.
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: |
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 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.
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.
as address to review comment, we need nested try block to handle fancy node name convention logic.
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.
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.
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.
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) |
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.
Split results in multiple outputs, hence suggest to enhance run methods to return multiple outputs and compare the same.
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.
Split returns the split result with list so comparing with List content result.
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.
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.
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.
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) |
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.
Pls add a case for -ve 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.
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) |
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.
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.
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 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 |
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.
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) |
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 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: |
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.
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) |
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.
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.
|
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: |
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 run one time by passing list of tensors to run method.
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.
Address the problem with update rework solution.
1. Tensorflow fetch output handle for list of node output as per review comments.
… into tf_lrn_split_fix
|
@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: |
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.
#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): |
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.
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.
|
Hi @dayanandasiet , |
|
Any update @dayanandasiet ? |
|
Hi @dayanandasiet , do you have any update on this PR? |
|
Will fix review comment and push this changes with new PR. As of now closing this PR. |
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.