-
Notifications
You must be signed in to change notification settings - Fork 519
Fix Keras v3 model conversion in numerical profiling #1421
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
JanFSchulte
merged 22 commits into
fastmachinelearning:main
from
Abubakar-rashid:fix-keras-v3-profiling
Feb 9, 2026
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
af9b710
Fix Keras v3 model conversion in numerical profiling
Abubakar-rashid d87185e
Merge branch 'main' into fix-keras-v3-profiling
jmitrevs e02aa9f
Address review feedback: remove unnecessary else clause and add test …
Abubakar-rashid 4b165c8
Resolve merge conflict: add test_keras_v3_profiling to KERAS3_LIST
Abubakar-rashid 50d117b
Fix line length issue in generate_ci_yaml.py
Abubakar-rashid e6294bc
Merge branch 'main' into fix-keras-v3-profiling
Abubakar-rashid 335def9
Merge branch 'main' into fix-keras-v3-profiling
Abubakar-rashid 9e4994a
Einsum and EinsumDense for oneAPI
laurilaatu 558170b
Add tests
laurilaatu 474a4fd
Add tests
laurilaatu 2d4944e
Remove hgq2 from testing-keras3 dependencies and test_hgq2_mha from K…
Abubakar-rashid 2aa04c3
Merge branch 'main' into fix-keras-v3-profiling
Abubakar-rashid abab3e9
[pre-commit.ci] pre-commit autoupdate (#1425)
pre-commit-ci[bot] 288a27f
Add LHC trigger use case context to README (#1418)
siddardhadesu 81d23ac
[pre-commit.ci] pre-commit autoupdate (#1430)
pre-commit-ci[bot] 11c3a3a
Fix Keras v3 profiling tests: build models and correct bar count expe…
Abubakar-rashid cf113fa
Merge branch 'main' into fix-keras-v3-profiling
Abubakar-rashid 77792e0
Merge branch 'main' into fix-keras-v3-profiling
Abubakar-rashid 5aeeb21
Fix qkeras import to avoid namespace conflict with hgq2
Abubakar-rashid ffbc6a4
Fix activation and batch norm tests
Abubakar-rashid bcebc44
Add new arguments to convert_from_keras_model call
Abubakar-rashid acf9c89
Fix Keras v3 profiling tests - 4/5 passing
Abubakar-rashid 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
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 |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| """Test numerical profiling with Keras v3 models.""" | ||
Abubakar-rashid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import numpy as np | ||
| import pytest | ||
|
|
||
| try: | ||
| import keras | ||
|
|
||
| __keras_profiling_enabled__ = keras.__version__ >= '3.0' | ||
| except ImportError: | ||
| __keras_profiling_enabled__ = False | ||
|
|
||
| if __keras_profiling_enabled__: | ||
| from hls4ml.model.profiling import numerical | ||
|
|
||
|
|
||
| def count_bars_in_figure(fig): | ||
| """Count the number of bars in all axes of a figure.""" | ||
| count = 0 | ||
| for ax in fig.get_axes(): | ||
| count += len(ax.patches) | ||
| return count | ||
|
|
||
|
|
||
| @pytest.mark.skipif(not __keras_profiling_enabled__, reason='Keras 3.0 or higher is required') | ||
| def test_keras_v3_numerical_profiling_simple_model(): | ||
| """Test numerical profiling with a simple Keras v3 Dense model.""" | ||
| model = keras.Sequential( | ||
| [ | ||
| keras.layers.Dense(20, input_shape=(10,), activation='relu'), | ||
| keras.layers.Dense(5, activation='softmax'), | ||
| ] | ||
| ) | ||
| model.compile(optimizer='adam', loss='categorical_crossentropy') | ||
| # Build the model so weights are initialized | ||
| model.build((None, 10)) | ||
|
|
||
| # Test profiling weights only | ||
| wp, _, _, _ = numerical(model) | ||
| assert wp is not None | ||
| # Should have 2 bars (one per layer, each showing weights and biases combined) | ||
| assert count_bars_in_figure(wp) == 2 | ||
|
|
||
|
|
||
| @pytest.mark.skipif(not __keras_profiling_enabled__, reason='Keras 3.0 or higher is required') | ||
| def test_keras_v3_numerical_profiling_with_activations(): | ||
| """Test numerical profiling with Keras v3 model including activations.""" | ||
| # Use functional API instead of Sequential to ensure input layer is properly defined | ||
| inputs = keras.Input(shape=(10,)) | ||
| x = keras.layers.Dense(20, activation='relu')(inputs) | ||
| outputs = keras.layers.Dense(5)(x) | ||
| model = keras.Model(inputs=inputs, outputs=outputs) | ||
| model.compile(optimizer='adam', loss='mse') | ||
|
|
||
| # Generate test data | ||
| X_test = np.random.rand(100, 10).astype(np.float32) | ||
|
|
||
| # Test profiling with activations | ||
| wp, _, ap, _ = numerical(model, X=X_test) | ||
| assert wp is not None | ||
| assert ap is not None | ||
|
|
||
|
|
||
| @pytest.mark.skipif(not __keras_profiling_enabled__, reason='Keras 3.0 or higher is required') | ||
| def test_keras_v3_numerical_profiling_conv_model(): | ||
| """Test numerical profiling with a Keras v3 Conv model.""" | ||
| model = keras.Sequential( | ||
| [ | ||
| keras.layers.Conv2D(16, (3, 3), activation='relu', input_shape=(28, 28, 1)), | ||
| keras.layers.MaxPooling2D((2, 2)), | ||
| keras.layers.Flatten(), | ||
| keras.layers.Dense(10, activation='softmax'), | ||
| ] | ||
| ) | ||
| model.compile(optimizer='adam', loss='categorical_crossentropy') | ||
| # Build the model so weights are initialized | ||
| model.build((None, 28, 28, 1)) | ||
|
|
||
| # Test profiling weights | ||
| wp, _, _, _ = numerical(model) | ||
| assert wp is not None | ||
| # Conv layer has 1 bar, Dense layer has 1 bar = 2 bars total | ||
| assert count_bars_in_figure(wp) == 2 | ||
|
|
||
|
|
||
| @pytest.mark.skipif(not __keras_profiling_enabled__, reason='Keras 3.0 or higher is required') | ||
| @pytest.mark.skip(reason='convert_from_config needs update for Keras v3 model serialization format') | ||
| def test_keras_v3_numerical_profiling_with_hls_model(): | ||
| """Test numerical profiling with both Keras v3 model and hls4ml model.""" | ||
| import hls4ml | ||
|
|
||
| # Use functional API to ensure input layer is properly defined | ||
| inputs = keras.Input(shape=(8,)) | ||
| x = keras.layers.Dense(16, activation='relu')(inputs) | ||
| outputs = keras.layers.Dense(4, activation='softmax')(x) | ||
| model = keras.Model(inputs=inputs, outputs=outputs) | ||
| model.compile(optimizer='adam', loss='categorical_crossentropy') | ||
|
|
||
| # Generate test data | ||
| X_test = np.random.rand(100, 8).astype(np.float32) | ||
|
|
||
| # Create hls4ml model | ||
| config = hls4ml.utils.config_from_keras_model(model, granularity='name') | ||
| hls_model = hls4ml.converters.convert_from_keras_model( | ||
| model, | ||
| hls_config=config, | ||
| output_dir='/tmp/test_keras_v3_profiling_hls', | ||
| backend='Vivado', | ||
| allow_da_fallback=True, | ||
| allow_v2_fallback=True, | ||
| ) | ||
|
|
||
| # Test profiling with both models | ||
| wp, wph, ap, aph = numerical(model, hls_model=hls_model, X=X_test) | ||
|
|
||
| assert wp is not None # Keras model weights (before optimization) | ||
| assert wph is not None # HLS model weights (after optimization) | ||
| assert ap is not None # Keras model activations (before optimization) | ||
| assert aph is not None # HLS model activations (after optimization) | ||
|
|
||
|
|
||
| @pytest.mark.skipif(not __keras_profiling_enabled__, reason='Keras 3.0 or higher is required') | ||
| def test_keras_v3_numerical_profiling_batch_norm(): | ||
| """Test numerical profiling with Keras v3 model containing BatchNormalization.""" | ||
| model = keras.Sequential( | ||
| [ | ||
| keras.layers.Dense(20, input_shape=(10,)), | ||
| keras.layers.BatchNormalization(), | ||
| keras.layers.Activation('relu'), | ||
| keras.layers.Dense(5, activation='softmax'), | ||
| ] | ||
| ) | ||
| model.compile(optimizer='adam', loss='categorical_crossentropy') | ||
| # Build the model so weights are initialized | ||
| model.build((None, 10)) | ||
|
|
||
| # Test profiling weights | ||
| wp, _, _, _ = numerical(model) | ||
| assert wp is not None | ||
| # Dense has 1 bar, BatchNorm has 1 bar, second Dense has 1 bar = 3 bars | ||
| assert count_bars_in_figure(wp) == 3 | ||
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.
Why is test_hg2_mha removed?
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 had asked for it since installing hgq2 brings with it a broken qkeras version that breaks the profiling code. As these tests are skipped anyway internally, I figured we'd remove them for now until this is properly resolved. Maybe @calad0i can also have a look?