Repository navigation
Fix loading nested-Sequential .h5 exports under Keras 3 - #14
Merged
Merged
Conversation
Contributor
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| Python | Sep 12, 2026 6:19p.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
Some Teachable Machine .h5 exports save the model as a Sequential wrapping nested Sequential/Functional submodels: Sequential > [InputLayer, Sequential > [InputLayer, Functional MobileNet backbone, GlobalAveragePooling2D], Sequential > [InputLayer, Dense, Dense]]. Keras 3's legacy H5 loader mis-rebuilds that shape: when the Functional backbone is called as a plain layer inside Sequential.build(), its single output tensor comes back wrapped in a length-1 Python list instead of the bare tensor. The next layer then treats that list as two separate inputs, failing with e.g. "expects 1 input(s), but it received 2 input tensors" -- which _load_model previously mislabeled as a generic "Model file not found" FileNotFoundError. _load_model now falls back to _load_nested_sequential_h5 when the normal load_model() call fails. It flattens the nested Sequential wrappers out of the model_config JSON (keeping only the first InputLayer, leaving any Functional submodels intact), rebuilds the graph via the Functional API (unwrapping the length-1 list after calling a Functional submodel), and restores weights by matching each leaf layer's name against the H5 file's saved weight paths directly, since Keras's built-in weight loader expects the now-discarded nested-Sequential grouping those paths were saved under. Models that already load fine via load_model() are unaffected.
MeqdadDev
force-pushed
the
fix/nested-sequential-h5-load
branch
from
September 12, 2026 18:12
c607500 to
2f28433
Compare
Adds a docstring to the nested weight-path walk() helper (the one genuine new-code finding DeepSource flagged on PR #14) and drops the redundant `(object)` base class, a no-op cleanup that's free to fix. The remaining findings the check reports (missing docstrings on _load_labels/_get_image_classification/_preprocess_image, long docstring lines, etc.) predate this branch entirely -- confirmed via git blame, they're from 2023-2024 commits -- so they're left alone as pre-existing repo debt rather than scope-creeping this bugfix PR.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Some Teachable Machine
.h5exports save the model as aSequentialwrapping nestedSequential/Functionalsubmodels:Keras 3's legacy H5 loader mis-rebuilds this shape: when the Functional backbone is called as a plain layer inside
Sequential.build(), its single output tensor comes back wrapped in a length-1 Python list instead of the bare tensor. The next layer then treats that list as two separate inputs, failing with:ValueError: ... expects 1 input(s), but it received 2 input tensors._load_modelpreviously caught this as a generic exception and re-raised it as a misleadingFileNotFoundError: Model file not found, hiding the real cause.Fix:
_load_modelnow falls back to a new_load_nested_sequential_h5when the normalload_model()call fails. It:Flattens the nested
Sequentialwrappers out of themodel_configJSON (keeping only the firstInputLayer, leaving anyFunctionalsubmodels intact).Rebuilds the graph via the Functional API, unwrapping the length-1 list after calling a
Functionalsubmodel.Restores weights by matching each leaf layer's name against the H5 file's saved weight paths directly, since Keras's built-in weight loader expects the now-discarded nested-Sequential grouping those paths were originally saved under.
Models that already load fine via
load_model()are unaffected — this is a pure fallback path.Tested against a real Teachable Machine MobileNetV2 export (2 classes) that previously failed with the error above; it now loads and classifies correctly.