TensorFlow Split and LRN operator support for Alexnet Model#1572
TensorFlow Split and LRN operator support for Alexnet Model#1572Dayananda-V wants to merge 16 commits intoapache:masterfrom
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.
LRN is already implemented earlier. Any reason for this modify ?
There was a problem hiding this comment.
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.
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.
@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.
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.
Can you share details of use case where convolution weights not in params ?
There was a problem hiding this comment.
This case is observe while running AlexNet model.
There was a problem hiding this comment.
Is weights output of another node in runtime?
Can u share serialised tensor flow graph?
There was a problem hiding this comment.
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.
Suggest to use transforms instead of ignores & extras.
There was a problem hiding this comment.
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.
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.
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.
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.
as address to review comment, we need nested try block to handle fancy node name convention logic.
There was a problem hiding this comment.
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.
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.
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.
Split returns the split result with list so comparing with List content result.
There was a problem hiding this comment.
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.
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.
Pls add a case for -ve axis
There was a problem hiding this comment.
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.
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.
| 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.
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.
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.
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.
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.
Can run one time by passing list of tensors to run method.
There was a problem hiding this comment.
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.
#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.
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.