[STEP] Recursive composite and native serialization - #52
Conversation
fkiraly
left a comment
There was a problem hiding this comment.
Very very nice!
I mostly agree, one point I am unsure about is how to handle multiple applicable serialization formats. We should think about it - the current save allows, for instance, to pick pickle or cloudpickle.
@fkiraly I think the |
fkiraly
left a comment
There was a problem hiding this comment.
ok, understood - the serialization_format is the baseline for metadata and base types, from which special serializers deviate. This also means though that nodes cannot pick their own serialization format. Perhaps something to keep in mind for a future design.
Replaces the flat-archive-plus-global-manifest design with the serialization-node format specified in STEP 27, "Recursive composite and native serialization" (sktime/enhancement-proposals#52), which explicitly rejected the previous approach. Archive format - the archive root is the root object's own node; the `root/` wrapper and the flat `components/` directory are gone - `manifest.json` is removed entirely, along with the `_format` and `_version` sidecars; each node now carries its own `_metadata`, `_artifacts/index.json` and `_components/index.json`, so no global index can drift out of sync with the directory tree - `_components/` is recursive: each child is a full node that may hold its own artifacts and children - `_metadata` is a versioned mapping of format version, class, and serialization format; it is always readable with plain pickle, so a reader can determine a node's format before it knows the serializer Component references - children are referenced by pickle persistent IDs rather than encoded attribute paths, via a Pickler subclass recognising children through the shared skbase base-object protocol - this fixes three defects in the previous walker: children held in dict attributes were silently absorbed into the parent, custom and immutable containers were rebuilt by type and mangled, and a child referenced twice was restored as two distinct objects - component IDs are opaque and node-local, and a node resolves them only through its own index Native artifacts - adds `_artifacts/` with the pretrained, keras, lightning_checkpoint and torch_state_dict backends, matching sktime#10453 so archives are readable across packages - adds the `serialization:skip` and `serialization:native_artifacts` tags, with classification ordered skip, artifacts, components, then `_obj`; an attribute carrying both tags raises Safety and compatibility - saving never mutates the source object, restoring removed attributes even on partial failure - ownership cycles and cross-branch aliases raise clear, archive-relative errors instead of recursing forever; self-references round-trip via the pickle memo - component paths escaping their own node are rejected - an unsupported format version is rejected up front - readers still accept legacy bare-class `_metadata`, legacy in-memory tuples, and minimal nodes - in-memory save now matches disk: a leaf stays lightweight pickle bytes, a composite becomes an in-memory zip of the same layout Other - moves the serialization API into a private `_SerializationMixin`, keeping the loose `load` thin - replaces the `joblib` format with `cloudpickle`, per STEP 27 and sktime; cloudpickle support is what motivates storing the class object in `_metadata` rather than a qualified name - drops the orphaned `capability:pred_int` tag, which was unrelated to serialization and referenced nowhere - adds skpro/base/tests/test_serialize.py covering the archive layout, container shapes, identity, unsupported graphs, tags, and compatibility Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the flat-archive-plus-global-manifest design with the serialization-node format specified in STEP 27, "Recursive composite and native serialization" (sktime/enhancement-proposals#52), which explicitly rejected the previous approach. Archive format - the archive root is the root object's own node; the `root/` wrapper and the flat `components/` directory are gone - `manifest.json` is removed entirely, along with the `_format` and `_version` sidecars; each node now carries its own `_metadata`, `_artifacts/index.json` and `_components/index.json`, so no global index can drift out of sync with the directory tree - `_components/` is recursive: each child is a full node that may hold its own artifacts and children - `_metadata` is a versioned mapping of format version, class, and serialization format; it is always readable with plain pickle, so a reader can determine a node's format before it knows the serializer Component references - children are referenced by pickle persistent IDs rather than encoded attribute paths, via a Pickler subclass recognising children through the shared skbase base-object protocol - this fixes three defects in the previous walker: children held in dict attributes were silently absorbed into the parent, custom and immutable containers were rebuilt by type and mangled, and a child referenced twice was restored as two distinct objects - component IDs are opaque and node-local, and a node resolves them only through its own index Native artifacts - adds `_artifacts/` with the pretrained, keras, lightning_checkpoint and torch_state_dict backends, matching sktime#10453 so archives are readable across packages - adds the `serialization:skip` and `serialization:native_artifacts` tags, with classification ordered skip, artifacts, components, then `_obj`; an attribute carrying both tags raises Safety and compatibility - saving never mutates the source object, restoring removed attributes even on partial failure - ownership cycles and cross-branch aliases raise clear, archive-relative errors instead of recursing forever; self-references round-trip via the pickle memo - component paths escaping their own node are rejected - an unsupported format version is rejected up front - readers still accept legacy bare-class `_metadata`, legacy in-memory tuples, and minimal nodes - in-memory save now matches disk: a leaf stays lightweight pickle bytes, a composite becomes an in-memory zip of the same layout Other - moves the serialization API into a private `_SerializationMixin`, keeping the loose `load` thin - replaces the `joblib` format with `cloudpickle`, per STEP 27 and sktime; cloudpickle support is what motivates storing the class object in `_metadata` rather than a qualified name - drops the orphaned `capability:pred_int` tag, which was unrelated to serialization and referenced nowhere - adds skpro/base/tests/test_serialize.py covering the archive layout, container shapes, identity, unsupported graphs, tags, and compatibility
Replaces the flat-archive-plus-global-manifest design with the serialization-node format specified in STEP 27, "Recursive composite and native serialization" (sktime/enhancement-proposals#52), which explicitly rejected the previous approach. Archive format - the archive root is the root object's own node; the `root/` wrapper and the flat `components/` directory are gone - `manifest.json` is removed entirely, along with the `_format` and `_version` sidecars; each node now carries its own `_metadata`, `_artifacts/index.json` and `_components/index.json`, so no global index can drift out of sync with the directory tree - `_components/` is recursive: each child is a full node that may hold its own artifacts and children - `_metadata` is a versioned mapping of format version, class, and serialization format; it is always readable with plain pickle, so a reader can determine a node's format before it knows the serializer Component references - children are referenced by pickle persistent IDs rather than encoded attribute paths, via a Pickler subclass recognising children through the shared skbase base-object protocol - this fixes three defects in the previous walker: children held in dict attributes were silently absorbed into the parent, custom and immutable containers were rebuilt by type and mangled, and a child referenced twice was restored as two distinct objects - component IDs are opaque and node-local, and a node resolves them only through its own index Native artifacts - adds `_artifacts/` with the pretrained, keras, lightning_checkpoint and torch_state_dict backends, matching sktime#10453 so archives are readable across packages - adds the `serialization:skip` and `serialization:native_artifacts` tags, with classification ordered skip, artifacts, components, then `_obj`; an attribute carrying both tags raises Safety and compatibility - saving never mutates the source object, restoring removed attributes even on partial failure - ownership cycles and cross-branch aliases raise clear, archive-relative errors instead of recursing forever; self-references round-trip via the pickle memo - component paths escaping their own node are rejected - an unsupported format version is rejected up front - readers still accept legacy bare-class `_metadata`, legacy in-memory tuples, and minimal nodes - in-memory save now matches disk: a leaf stays lightweight pickle bytes, a composite becomes an in-memory zip of the same layout Other - moves the serialization API into a private `_SerializationMixin`, keeping the loose `load` thin - replaces the `joblib` format with `cloudpickle`, per STEP 27 and sktime; cloudpickle support is what motivates storing the class object in `_metadata` rather than a qualified name - drops the orphaned `capability:pred_int` tag, which was unrelated to serialization and referenced nowhere - adds skpro/base/tests/test_serialize.py covering the archive layout, container shapes, identity, unsupported graphs, tags, and compatibility
This pull request adds a design document for a unified serialization format across
sktimeandskpro. It combines recursive serialization of composite estimators with framework-native serialization for deep-learning and foundation models, while preserving the existingsave/loadAPI.The proposal supersedes STEP 20 and specifies the archive layout, component and artifact handling, backward compatibility, implementation plan, and testing requirements.
Related discussions:
sktimeestimators withskprosktime#10582