-
Notifications
You must be signed in to change notification settings - Fork 551
Add support for qkeras v3 #1473
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
base: main
Are you sure you want to change the base?
Changes from all commits
4b801ac
b04bbdd
54134ce
42e8b9b
a8131bd
bd309cc
8f4ff93
79654c8
2f80510
3bd75a7
939c0ab
b17d23a
3953f34
f556feb
53d600c
a4e9406
a477078
a1d1688
c843e38
5e0196c
e6676d7
5fe8b05
2cd5e30
9665a0e
2a20082
26a12a7
862ac93
eeaf804
b01c722
a6fac77
f4342b0
edbbfe7
a5388f6
3766914
77e52bd
49faba7
4febd2d
6cd82d7
346a2a5
70f01f5
9f0edd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from . import activation, layer, utils |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| from typing import Any | ||
|
|
||
| from hls4ml.converters.utils import IsolatedLayerReader | ||
|
|
||
| from ..core import KerasV3LayerHandler | ||
| from .utils import set_default_config | ||
|
|
||
|
|
||
| class QKerasQActivationHandler(KerasV3LayerHandler): | ||
| handles = ('qkeras.qlayers.QActivation', 'QActivation') | ||
|
Contributor
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. Why
Contributor
Author
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. I think Keras does not return 'keras.layers.Activation' during serialization.
Contributor
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. The layer seen by the parser is a python object, and no serialization is used in keras v3 parser though. |
||
|
|
||
| def handle( | ||
| self, | ||
| layer, | ||
| in_tensors, | ||
| out_tensors, | ||
| ) -> tuple[dict[str, Any], ...]: | ||
|
|
||
| config = layer.get_config() | ||
| layer_dict = {'config': config, 'class_name': layer.__class__.__name__} | ||
|
|
||
| reader = IsolatedLayerReader(layer) | ||
| input_shapes = [list(t.shape) for t in in_tensors] | ||
| input_names = [t.name for t in in_tensors] | ||
|
|
||
| from hls4ml.converters.keras_v2_to_hls import layer_handlers as v2_layer_handlers | ||
|
|
||
| v2_handler = v2_layer_handlers.get(layer.__class__.__name__) | ||
|
Contributor
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. If possible, let's avoid using the v2 parser as it shall be eventually removed at some point... No a hard requirement but would be nice if we can do so.
Contributor
Author
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. I agree but maybe thats something for the future when the v2 parser will be removed.
Contributor
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. I agree. We could revisit the depracation in the future. |
||
| if v2_handler is None: | ||
| raise ValueError(f'No v2 handler found for {layer.__class__.__name__}') | ||
|
|
||
| hls_conf, _ = v2_handler(layer_dict, input_names, input_shapes, reader) | ||
| hls_conf = set_default_config(hls_conf, self.default_config) | ||
|
|
||
| return (hls_conf,) | ||
|
makoeppel marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| from hls4ml.converters.utils import IsolatedLayerReader | ||
|
|
||
| from ..core import KerasV3LayerHandler | ||
| from .utils import set_default_config | ||
|
|
||
|
|
||
| class QKerasV3LayerHandler(KerasV3LayerHandler): | ||
| handles = ( | ||
| 'qkeras.qlayers.QDense', | ||
| 'qkeras.qconvolutional.QConv1D', | ||
| 'qkeras.qconvolutional.QConv2D', | ||
| 'qkeras.qconvolutional.QDepthwiseConv2D', | ||
| 'qkeras.qconv2d_batchnorm.QConv2DBatchnorm', | ||
| ) | ||
|
|
||
| def handle(self, layer, in_tensors, out_tensors): | ||
| config = layer.get_config() | ||
| layer_dict = {'config': config, 'class_name': layer.__class__.__name__} | ||
|
|
||
| reader = IsolatedLayerReader(layer) | ||
| input_shapes = [list(t.shape) for t in in_tensors] | ||
| input_names = [t.name for t in in_tensors] | ||
|
|
||
| from hls4ml.converters.keras_v2_to_hls import layer_handlers as v2_layer_handlers | ||
|
|
||
| v2_handler = v2_layer_handlers.get(layer.__class__.__name__) | ||
| if v2_handler is None: | ||
| raise ValueError(f'No v2 handler found for {layer.__class__.__name__}') | ||
|
|
||
| ret, _ = v2_handler(layer_dict, input_names, input_shapes, reader) | ||
| ret = set_default_config(ret, self.default_config) | ||
|
|
||
| activation = config.get('activation') | ||
|
Contributor
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. activation handling here is treated as a special case. The base class shall cover this already though...
Contributor
Author
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. I have now this layer.py which is more or less a base class. I can call it also _base.py if you want. |
||
| if activation not in (None, 'linear'): | ||
| from hls4ml.converters.keras.qkeras import get_activation_quantizer | ||
|
|
||
| activation_config = get_activation_quantizer(layer_dict, input_names) | ||
| intermediate_tensor_name = f'{out_tensors[0].name}_activation' | ||
| ret['output_keras_tensor_names'] = [intermediate_tensor_name] | ||
| activation_config.update( | ||
| { | ||
| 'name': f'{layer.name}_activation', | ||
| 'input_keras_tensor_names': [intermediate_tensor_name], | ||
| 'output_keras_tensor_names': [out_tensors[0].name], | ||
| } | ||
| ) | ||
| return ret, activation_config | ||
|
|
||
| return ret | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| def set_default_config(hls_conf, default_config): | ||
| for key, value in default_config.items(): | ||
| if key not in hls_conf.keys(): | ||
| hls_conf[key] = value | ||
| return hls_conf |
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.
Isn't garnet a specific example implemented in some version of qkeras v2?
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 should also work in qkeras v3.