forked from gmalivenko/onnx2keras
-
Notifications
You must be signed in to change notification settings - Fork 0
handling constants with a custom layer (mul) #179
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
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
170faa3
handling constants with a custom layer (mul)
f263a0a
modify mul to use lambda rather than custom layer
72113e0
fixed eager tensor and dtype errors
f540b7a
added kwargs
3ea820d
gitignore + convert eager tensor to numpy
eab9c68
deleted custom layer
539ba32
git ignore
185db00
added dtype conversion
11ca70c
elementwise sub with proper const handling
fcbdb4e
elementwise add const handling
caa9da9
up version
f065566
docstrings
8a3b717
padding
7a9a626
with padding fix
04f7e84
remove duplicated code
b3f182a
bugfix
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import numpy as np | ||
| import keras | ||
| import logging | ||
|
|
||
| from .utils import is_numpy, ensure_tf_type | ||
| from .tfops_funcs import tf_tensor_scatter_nd_update, tf_maximum, tf_minimum, tf_cast, tf_expand_dims, tf_repeat,\ | ||
| tf_equal, tf_where, tf_round, tf_sign, tf_abs, tf_math_mod, tf_bitwise_left_shift, tf_bitwise_right_shift,\ | ||
|
|
@@ -71,33 +72,71 @@ def convert_elementwise_add(node, params, layers, lambda_func, node_name, keras_ | |
| logger = logging.getLogger('onnx2keras.add') | ||
|
|
||
| if len(node.input) != 2: | ||
| raise AttributeError('Number of inputs is not equal 2 for element-wise layer') | ||
| raise AttributeError('Number of inputs is not equal to 2 for element-wise layer') | ||
|
|
||
| input_0 = layers[node.input[0]] | ||
| input_1 = layers[node.input[1]] | ||
|
|
||
| input_0_is_non_keras = is_numpy(input_0) or isinstance(input_0, EagerTensor) | ||
| input_1_is_non_keras = is_numpy(input_1) or isinstance(input_1, EagerTensor) | ||
| input_0_is_constant = is_numpy(input_0) or isinstance(input_0, EagerTensor) | ||
| input_1_is_constant = is_numpy(input_1) or isinstance(input_1, EagerTensor) | ||
|
|
||
| try: | ||
| if not input_0_is_non_keras and not input_1_is_non_keras: | ||
| to_add = input_1 | ||
| # We probably need to seperate two possibilities here. Currently we only deal with second option | ||
| # [Batch] + [Batch,1] -> [Batch,1] | ||
| # [Not-Batch] + [Not,Batch,1] -> [Not-batch, Not-batch] | ||
| if not input_0_is_constant and not input_1_is_constant: | ||
| # Both inputs are variables | ||
| if len(input_0.shape) != len(input_1.shape): | ||
| layers[node_name] = tf_add(input_0, to_add, tf_name=f"{params['cleaned_name']}_add") | ||
| # Use TensorFlow add to handle shape differences | ||
| layers[node_name] = tf_add(input_0, input_1, tf_name=f"{params['cleaned_name']}_add") | ||
| else: | ||
| layers[node_name] = keras.layers.Add(name=f"{params['cleaned_name']}_add")([input_0, to_add]) | ||
| # Use Keras Add layer | ||
| layers[node_name] = keras.layers.Add(name=f"{params['cleaned_name']}_add")([input_0, input_1]) | ||
| else: | ||
| raise ValueError('Operands are different.') | ||
| except (IndexError, ValueError): | ||
| logger.warning('Failed to use keras.layers.Add. Fallback to TF lambda.') | ||
| if input_0_is_non_keras: | ||
| layers[node_name] = input_1 + input_0 | ||
| logger.warning('Failed to use keras.layers.Add. Fallback to Lambda layer.') | ||
|
|
||
| if input_0_is_constant and not input_1_is_constant: | ||
| # input_0 is constant, input_1 is variable | ||
| constant_value = np.asarray(tf.cast(input_0, dtype=input_1.dtype)) | ||
| variable_input = input_1 | ||
|
|
||
| if np.all(constant_value == constant_value.flat[0]): | ||
| # Constant tensor has the same value throughout | ||
| const_val = constant_value.flat[0] | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x + const_val, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Embedding the constant tensor | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x + constant_value, | ||
| name=keras_name | ||
| )(variable_input) | ||
|
|
||
| elif not input_0_is_constant and input_1_is_constant: | ||
| # input_0 is variable, input_1 is constant | ||
| constant_value = np.asarray(tf.cast(input_1, dtype=input_0.dtype)) | ||
| variable_input = input_0 | ||
|
|
||
| if np.all(constant_value == constant_value.flat[0]): | ||
| # Constant tensor has the same value throughout | ||
| const_val = constant_value.flat[0] | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x + const_val, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Embedding the constant tensor | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x + constant_value, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Both inputs are constants; compute the result now | ||
| layers[node_name] = input_0 + input_1 | ||
|
|
||
|
|
||
|
|
||
| def convert_elementwise_mul(node, params, layers, lambda_func, node_name, keras_name): | ||
| """ | ||
| Convert element-wise mul. | ||
|
|
@@ -112,13 +151,14 @@ def convert_elementwise_mul(node, params, layers, lambda_func, node_name, keras_ | |
| logger = logging.getLogger('onnx2keras.mul') | ||
|
|
||
| if len(node.input) != 2: | ||
| raise AttributeError('Number of inputs is not equal 2 for element-wise layer') | ||
| raise AttributeError('Number of inputs is not equal to 2 for element-wise layer') | ||
|
|
||
| input_0 = layers[node.input[0]] | ||
| input_1 = layers[node.input[1]] | ||
|
|
||
| input_0_is_constant = is_numpy(input_0) or isinstance(input_0, EagerTensor) | ||
| input_1_is_constant = is_numpy(input_1) or isinstance(input_1, EagerTensor) | ||
|
|
||
| try: | ||
| if not input_0_is_constant and not input_1_is_constant: | ||
| mul = keras.layers.Multiply(name=f"{params['cleaned_name']}_mul") | ||
|
|
@@ -127,8 +167,48 @@ def convert_elementwise_mul(node, params, layers, lambda_func, node_name, keras_ | |
| raise ValueError('Operands are different.') | ||
|
|
||
| except (IndexError, ValueError): | ||
| logger.warning('Failed to use keras.layers.Multiply. Fallback to TF lambda.') | ||
| layers[node_name] = input_0 * input_1 | ||
| logger.warning('Failed to use keras.layers.Multiply. Fallback to Lambda layer.') | ||
|
|
||
| if input_0_is_constant and not input_1_is_constant: | ||
| # input_0 is constant, input_1 is variable | ||
| constant_value = np.asarray(tf.cast(input_0, dtype=input_1.dtype)) | ||
| variable_input = input_1 | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we end the if here? |
||
| if np.all(constant_value == constant_value.flat[0]): | ||
| # Constant tensor has the same value throughout | ||
| const_val = constant_value.flat[0] | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x * const_val, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Cannot avoid embedding the constant tensor | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x * constant_value, | ||
| name=keras_name | ||
| )(variable_input) | ||
|
|
||
| elif not input_0_is_constant and input_1_is_constant: | ||
| # input_0 is variable, input_1 is constant | ||
| constant_value = np.asarray(tf.cast(input_1, dtype=input_0.dtype)) | ||
| variable_input = input_0 | ||
|
|
||
| if np.all(constant_value == constant_value.flat[0]): | ||
| # Constant tensor has the same value throughout | ||
| const_val = constant_value.flat[0] | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x * const_val, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Cannot avoid embedding the constant tensor | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x * constant_value, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Both inputs are constants; compute the result now | ||
| layers[node_name] = input_0 * input_1 | ||
|
|
||
|
|
||
| def convert_elementwise_sub(node, params, layers, lambda_func, node_name, keras_name): | ||
|
|
@@ -149,24 +229,63 @@ def convert_elementwise_sub(node, params, layers, lambda_func, node_name, keras_ | |
|
|
||
| input_0 = layers[node.input[0]] | ||
| input_1 = layers[node.input[1]] | ||
| input_0_is_np = is_numpy(input_0) or isinstance(input_0, EagerTensor) | ||
| input_1_is_np = is_numpy(input_1) or isinstance(input_1, EagerTensor) | ||
|
|
||
| input_0_is_constant = is_numpy(input_0) or isinstance(input_0, EagerTensor) | ||
| input_1_is_constant = is_numpy(input_1) or isinstance(input_1, EagerTensor) | ||
|
|
||
| try: | ||
| if not input_0_is_np and not input_1_is_np: | ||
| if not input_0_is_constant and not input_1_is_constant: | ||
| sub = keras.layers.Subtract(name=f"{params['cleaned_name']}_sub") | ||
| layers[node_name] = sub([input_0, input_1]) | ||
| else: | ||
| raise ValueError('Operands are different.') | ||
|
|
||
| except (IndexError, ValueError): | ||
| logger.warning('Failed to use keras.layers.Subtract. Fallback to TF lambda.') | ||
| if input_0_is_np and not input_1_is_np: # constant - tensor does not parse well | ||
| layers[node_name] = - (input_1 - input_0) | ||
| logger.warning('Failed to use keras.layers.Subtract. Fallback to Lambda layer.') | ||
|
|
||
| if input_0_is_constant and not input_1_is_constant: | ||
| # input_0 is constant, input_1 is variable: constant - variable | ||
| constant_value = np.asarray(tf.cast(input_0, dtype=input_1.dtype)) | ||
| variable_input = input_1 | ||
|
|
||
| if np.all(constant_value == constant_value.flat[0]): | ||
| # Constant tensor has the same value throughout | ||
| const_val = constant_value.flat[0] | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: const_val - x, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Cannot avoid embedding the constant tensor | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: constant_value - x, | ||
| name=keras_name | ||
| )(variable_input) | ||
|
|
||
| elif not input_0_is_constant and input_1_is_constant: | ||
| # input_0 is variable, input_1 is constant: variable - constant | ||
| constant_value = np.asarray(tf.cast(input_1, dtype=input_0.dtype)) | ||
| variable_input = input_0 | ||
|
|
||
| if np.all(constant_value == constant_value.flat[0]): | ||
| # Constant tensor has the same value throughout | ||
| const_val = constant_value.flat[0] | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x - const_val, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Cannot avoid embedding the constant tensor | ||
| layers[node_name] = keras.layers.Lambda( | ||
| lambda x: x - constant_value, | ||
| name=keras_name | ||
| )(variable_input) | ||
| else: | ||
| # Both inputs are constants; compute the result now | ||
| layers[node_name] = input_0 - input_1 | ||
|
|
||
|
|
||
|
|
||
| def convert_min(node, params, layers, lambda_func, node_name, keras_name): | ||
| """ | ||
| Convert Min layer | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 end the if condition here and then take the next part and combine it to avoid code duplication?