mlbundle 0.1.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mlbundle might be problematic. Click here for more details.
- mlbundle-0.1.0a1/LICENSE +21 -0
- mlbundle-0.1.0a1/PKG-INFO +347 -0
- mlbundle-0.1.0a1/README.md +297 -0
- mlbundle-0.1.0a1/pyproject.toml +254 -0
- mlbundle-0.1.0a1/pyproject.toml.orig +214 -0
- mlbundle-0.1.0a1/src/mlbundle/__init__.py +82 -0
- mlbundle-0.1.0a1/src/mlbundle/_base.py +19 -0
- mlbundle-0.1.0a1/src/mlbundle/_canonical_work.py +67 -0
- mlbundle-0.1.0a1/src/mlbundle/_catboost/__init__.py +159 -0
- mlbundle-0.1.0a1/src/mlbundle/_catboost/budget.py +22 -0
- mlbundle-0.1.0a1/src/mlbundle/_catboost/flatbuffer.py +104 -0
- mlbundle-0.1.0a1/src/mlbundle/_catboost/native.py +244 -0
- mlbundle-0.1.0a1/src/mlbundle/_catboost/prediction.py +67 -0
- mlbundle-0.1.0a1/src/mlbundle/_catboost/scope.py +109 -0
- mlbundle-0.1.0a1/src/mlbundle/_catboost/state.py +62 -0
- mlbundle-0.1.0a1/src/mlbundle/_composite/__init__.py +1 -0
- mlbundle-0.1.0a1/src/mlbundle/_composite/claims.py +133 -0
- mlbundle-0.1.0a1/src/mlbundle/_composite/io.py +158 -0
- mlbundle-0.1.0a1/src/mlbundle/_composite/prediction.py +77 -0
- mlbundle-0.1.0a1/src/mlbundle/_container_rules.py +73 -0
- mlbundle-0.1.0a1/src/mlbundle/_export_manifest.py +78 -0
- mlbundle-0.1.0a1/src/mlbundle/_native_io.py +53 -0
- mlbundle-0.1.0a1/src/mlbundle/_onnx/__init__.py +115 -0
- mlbundle-0.1.0a1/src/mlbundle/_onnx/_wire_runs.py +103 -0
- mlbundle-0.1.0a1/src/mlbundle/_onnx/description.py +252 -0
- mlbundle-0.1.0a1/src/mlbundle/_onnx/prediction.py +136 -0
- mlbundle-0.1.0a1/src/mlbundle/_onnx/protobuf.py +111 -0
- mlbundle-0.1.0a1/src/mlbundle/_onnx/session.py +87 -0
- mlbundle-0.1.0a1/src/mlbundle/_onnx/signatures.py +63 -0
- mlbundle-0.1.0a1/src/mlbundle/_onnx/validation.py +164 -0
- mlbundle-0.1.0a1/src/mlbundle/_pipeline_contract.py +104 -0
- mlbundle-0.1.0a1/src/mlbundle/_prediction_input.py +63 -0
- mlbundle-0.1.0a1/src/mlbundle/_schema_fields.py +68 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/__init__.py +183 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/_width_checks.py +59 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/archive.py +32 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/arrays.py +150 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/column_width.py +198 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/consistency.py +127 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/dtypes.py +41 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/encoder_width.py +123 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/fitted.py +113 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/labels.py +52 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/nodes.py +75 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/parameters.py +94 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/payload.py +234 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/pipeline_scope.py +141 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/polynomial_names.py +31 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/prediction.py +119 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/prediction_state.py +61 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/references.py +110 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/serialization.py +80 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/state.py +144 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/state_fields.json +228 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/structure.py +265 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/transform_names.py +111 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/transform_width.py +270 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/transformers.py +69 -0
- mlbundle-0.1.0a1/src/mlbundle/_sklearn/trusted.py +89 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/__init__.py +262 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/arrays.py +47 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/categories.py +109 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/native.py +193 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/prediction.py +59 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/snapshots.py +41 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/state.py +147 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/structure.py +47 -0
- mlbundle-0.1.0a1/src/mlbundle/_xgboost/ubjson.py +119 -0
- mlbundle-0.1.0a1/src/mlbundle/_zip_directory.py +173 -0
- mlbundle-0.1.0a1/src/mlbundle/_zip_members.py +116 -0
- mlbundle-0.1.0a1/src/mlbundle/adapters.py +33 -0
- mlbundle-0.1.0a1/src/mlbundle/canonical.py +290 -0
- mlbundle-0.1.0a1/src/mlbundle/claims.py +124 -0
- mlbundle-0.1.0a1/src/mlbundle/cli.py +193 -0
- mlbundle-0.1.0a1/src/mlbundle/container.py +93 -0
- mlbundle-0.1.0a1/src/mlbundle/contract.py +55 -0
- mlbundle-0.1.0a1/src/mlbundle/declarations.py +68 -0
- mlbundle-0.1.0a1/src/mlbundle/environment.py +85 -0
- mlbundle-0.1.0a1/src/mlbundle/errors.py +92 -0
- mlbundle-0.1.0a1/src/mlbundle/inspection.py +30 -0
- mlbundle-0.1.0a1/src/mlbundle/integrity.py +30 -0
- mlbundle-0.1.0a1/src/mlbundle/limits.py +38 -0
- mlbundle-0.1.0a1/src/mlbundle/manifest.py +222 -0
- mlbundle-0.1.0a1/src/mlbundle/model_io.py +151 -0
- mlbundle-0.1.0a1/src/mlbundle/py.typed +0 -0
- mlbundle-0.1.0a1/src/mlbundle/registry.py +103 -0
- mlbundle-0.1.0a1/src/mlbundle/results.py +68 -0
- mlbundle-0.1.0a1/src/mlbundle/samples.py +71 -0
- mlbundle-0.1.0a1/src/mlbundle/schema.py +145 -0
- mlbundle-0.1.0a1/src/mlbundle/training.py +104 -0
- mlbundle-0.1.0a1/src/mlbundle/writing.py +256 -0
mlbundle-0.1.0a1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pablo Ulloa Santín
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mlbundle
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Pickle-free, verifiable model bundles with a manifest, schema and structure.
|
|
5
|
+
Keywords: machine-learning,mlops,model-serialization,model-packaging,reproducibility,security,scikit-learn,xgboost,catboost,onnx,skops
|
|
6
|
+
Author: Pablo Ulloa Santín
|
|
7
|
+
Author-email: Pablo Ulloa Santín <ulloasantinpablo2@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Dist: pydantic>=2.13.4,<3
|
|
23
|
+
Requires-Dist: scikit-learn>=1.6 ; extra == 'all'
|
|
24
|
+
Requires-Dist: skops>=0.16.0 ; extra == 'all'
|
|
25
|
+
Requires-Dist: xgboost>=3.4.1 ; extra == 'all'
|
|
26
|
+
Requires-Dist: catboost>=1.2.10 ; extra == 'all'
|
|
27
|
+
Requires-Dist: onnx>=1.23.0 ; extra == 'all'
|
|
28
|
+
Requires-Dist: onnxruntime>=1.30.0 ; extra == 'all'
|
|
29
|
+
Requires-Dist: mlschema>=0.2.1,<0.3 ; python_full_version == '3.14.*' and extra == 'all'
|
|
30
|
+
Requires-Dist: catboost>=1.2.10 ; extra == 'catboost'
|
|
31
|
+
Requires-Dist: onnx>=1.23.0 ; extra == 'onnx'
|
|
32
|
+
Requires-Dist: onnxruntime>=1.30.0 ; extra == 'onnx'
|
|
33
|
+
Requires-Dist: mlschema>=0.2.1,<0.3 ; python_full_version == '3.14.*' and extra == 'schema'
|
|
34
|
+
Requires-Dist: scikit-learn>=1.6 ; extra == 'sklearn'
|
|
35
|
+
Requires-Dist: skops>=0.16.0 ; extra == 'sklearn'
|
|
36
|
+
Requires-Dist: xgboost>=3.4.1 ; extra == 'xgboost'
|
|
37
|
+
Requires-Dist: scikit-learn>=1.2 ; extra == 'xgboost'
|
|
38
|
+
Requires-Python: >=3.14
|
|
39
|
+
Project-URL: Homepage, https://github.com/UlloaSP/mlbundle
|
|
40
|
+
Project-URL: Repository, https://github.com/UlloaSP/mlbundle
|
|
41
|
+
Project-URL: Issues, https://github.com/UlloaSP/mlbundle/issues
|
|
42
|
+
Project-URL: Documentation, https://github.com/UlloaSP/mlbundle/blob/main/README.md
|
|
43
|
+
Provides-Extra: all
|
|
44
|
+
Provides-Extra: catboost
|
|
45
|
+
Provides-Extra: onnx
|
|
46
|
+
Provides-Extra: schema
|
|
47
|
+
Provides-Extra: sklearn
|
|
48
|
+
Provides-Extra: xgboost
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# mlbundle
|
|
52
|
+
|
|
53
|
+
Package a trained model, manifest, locally inferred schema and structure in one `.mlbundle` file.
|
|
54
|
+
Export on the training machine and validate/load in the consuming application with the same Python library.
|
|
55
|
+
|
|
56
|
+
mlbundle replaces three gaps in separate model/DataFrame uploads:
|
|
57
|
+
|
|
58
|
+
- **Pickle code execution:** closed adapters use skops, native XGBoost UBJSON,
|
|
59
|
+
CatBoost CBM or an existing ONNX file, without pickle/joblib/cloudpickle.
|
|
60
|
+
- **Missing training versions:** the manifest records Python, OS, architecture
|
|
61
|
+
and installed package versions alongside model and feature metadata.
|
|
62
|
+
- **Training data leaving the machine:** schema inference runs locally;
|
|
63
|
+
training rows are included only when explicitly requested as samples.
|
|
64
|
+
|
|
65
|
+
**Pre-alpha v0.1.0a1, published to reserve the PyPI name.** Format version 1 is
|
|
66
|
+
not frozen; bundles may need re-export. The security design has not had an
|
|
67
|
+
independent review: do not accept bundles from untrusted parties.
|
|
68
|
+
Requires **Python >=3.14**; tested on Windows/Linux. Schema requires Python 3.14.
|
|
69
|
+
|
|
70
|
+
## Install and try scikit-learn
|
|
71
|
+
|
|
72
|
+
Install the pre-release with
|
|
73
|
+
`python -m pip install --pre "mlbundle[sklearn]"`.
|
|
74
|
+
Replace `[sklearn]` with `[xgboost]`, `[catboost]`, `[onnx]`, `[schema]` or `[all]` for the
|
|
75
|
+
corresponding dependencies. Combine extras as `[sklearn,schema]` for local
|
|
76
|
+
DataFrame inference. Omit the brackets for a base install that can inspect
|
|
77
|
+
every bundle without model libraries. Load/verify needs the matching model extra.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from sklearn.tree import DecisionTreeClassifier
|
|
81
|
+
from mlbundle import export, inspect, load
|
|
82
|
+
|
|
83
|
+
rows = [[0.0, 1.0], [1.0, 0.0], [0.1, 0.9], [0.9, 0.1]]
|
|
84
|
+
model = DecisionTreeClassifier(random_state=7).fit(rows, ["cold", "warm"] * 2)
|
|
85
|
+
export(model, "trained.mlbundle", feature_names=["temperature", "humidity"])
|
|
86
|
+
bundle = load("trained.mlbundle")
|
|
87
|
+
assert isinstance(bundle.native_model, DecisionTreeClassifier)
|
|
88
|
+
assert bundle.native_model.predict(rows).tolist() == model.predict(rows).tolist()
|
|
89
|
+
assert bundle.class_labels == [["cold", "warm"]]
|
|
90
|
+
print(bundle.predict_proba([[0.0, 1.0]])) # [[1.0, 0.0]]
|
|
91
|
+
assert inspect("trained.mlbundle").manifest.model.library == "sklearn"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`export` returns metadata and requires `overwrite=True` for existing destinations.
|
|
95
|
+
`load` accepts paths, bytes or streams. Classifiers use `bundle.predict_proba`;
|
|
96
|
+
regressors use `bundle.predict`. Results are plain lists: output-major classifier
|
|
97
|
+
labels/probabilities, row-major regression values. `native_model` supports native
|
|
98
|
+
inference/explanations; mutating it invalidates the metadata verification.
|
|
99
|
+
|
|
100
|
+
Native names win; otherwise origin is `declared`, `dataframe`, or `generated` (`feature_1` through `feature_N`).
|
|
101
|
+
`target_name=` and `metadata=` remain author declarations.
|
|
102
|
+
|
|
103
|
+
## XGBoost
|
|
104
|
+
|
|
105
|
+
Install the `xgboost` extra. Train on encoded indices and supply the original
|
|
106
|
+
labels in probability-column order, nested even for one output:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from xgboost import XGBClassifier
|
|
110
|
+
from mlbundle import export, load
|
|
111
|
+
|
|
112
|
+
rows = [[0.0, 1.0], [1.0, 0.0], [0.1, 0.9], [0.9, 0.1]]
|
|
113
|
+
model = XGBClassifier(n_estimators=3, max_depth=2, n_jobs=1).fit(rows, [0, 1, 0, 1])
|
|
114
|
+
export(model, "xgboost.mlbundle", class_labels=[["cold", "warm"]])
|
|
115
|
+
bundle = load("xgboost.mlbundle")
|
|
116
|
+
assert isinstance(bundle.native_model, XGBClassifier)
|
|
117
|
+
assert bundle.native_model.predict(rows).tolist() == model.predict(rows).tolist()
|
|
118
|
+
assert bundle.predict_proba(rows) == model.predict_proba(rows).tolist()
|
|
119
|
+
assert bundle.class_labels == [["cold", "warm"]]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Labels/settings bind to a native **copy**. Load verifies consistency, not history.
|
|
123
|
+
Omitted labels use encoded indices/an existing binding; native predict stays encoded.
|
|
124
|
+
Standalone sklearn, CatBoost and regressors reject `class_labels=`; composites
|
|
125
|
+
delegate labels to the final native adapter.
|
|
126
|
+
|
|
127
|
+
Only recoverable native configuration is recorded; learning rate, max depth,
|
|
128
|
+
seed and thread count do not survive native XGBoost I/O and are not reconstructed.
|
|
129
|
+
|
|
130
|
+
## CatBoost
|
|
131
|
+
|
|
132
|
+
Install the `catboost` extra (>=1.2.10; includes matplotlib/plotly). Numeric,
|
|
133
|
+
single-target symmetric classifiers/regressors use native CBM and native labels:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from catboost import CatBoostClassifier
|
|
137
|
+
from mlbundle import export, load
|
|
138
|
+
|
|
139
|
+
rows = [[0.0, 1.0], [1.0, 0.0], [0.1, 0.9], [0.9, 0.1]]
|
|
140
|
+
model = CatBoostClassifier(
|
|
141
|
+
iterations=3, depth=2, verbose=False, allow_writing_files=False
|
|
142
|
+
).fit(rows, ["cold", "warm"] * 2)
|
|
143
|
+
export(model, "catboost.mlbundle")
|
|
144
|
+
bundle = load("catboost.mlbundle")
|
|
145
|
+
assert bundle.class_labels == [["cold", "warm"]]
|
|
146
|
+
assert bundle.predict_proba(rows) == model.predict_proba(rows).tolist()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`class_labels=`, categorical/text/embedding features,
|
|
150
|
+
Depthwise/Lossguide, custom Python objectives/metrics and other losses are rejected.
|
|
151
|
+
Classifier losses: Logloss, CrossEntropy, MultiClass. Regressors: RMSE, MAE,
|
|
152
|
+
Quantile, Huber, Poisson, MAPE. Complete trees/borders/NaN handling/importances
|
|
153
|
+
are verified on load. [The CatBoost contract](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-catboost.md) details
|
|
154
|
+
wrapper recovery, native-parser limits and the private in-memory serializer.
|
|
155
|
+
|
|
156
|
+
## Composite pipelines
|
|
157
|
+
|
|
158
|
+
Install `[sklearn,xgboost]` or `[sklearn,catboost]`. Export the fitted Pipeline
|
|
159
|
+
as it is; transformers use skops and the final estimator uses its native adapter:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from sklearn.pipeline import make_pipeline
|
|
163
|
+
from sklearn.preprocessing import StandardScaler
|
|
164
|
+
from xgboost import XGBClassifier
|
|
165
|
+
from mlbundle import export, load
|
|
166
|
+
|
|
167
|
+
rows = [[0.0, 1.0], [1.0, 0.0], [0.1, 0.9], [0.9, 0.1]]
|
|
168
|
+
model = make_pipeline(StandardScaler(), XGBClassifier(n_estimators=3, n_jobs=1))
|
|
169
|
+
model.fit(rows, [0, 1, 0, 1])
|
|
170
|
+
export(model, "pipeline.mlbundle", class_labels=[["cold", "warm"]])
|
|
171
|
+
bundle = load("pipeline.mlbundle")
|
|
172
|
+
assert bundle.predict_proba(rows) == model.predict_proba(rows).tolist()
|
|
173
|
+
assert bundle.native_model.predict(rows).tolist() == model.predict(rows).tolist()
|
|
174
|
+
assert bundle.class_labels == [["cold", "warm"]]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Load rebuilds a real Pipeline after verifying both parts. Bundle probability labels
|
|
178
|
+
use XGBoost's bound originals; native classes/predict stay encoded. CatBoost keeps
|
|
179
|
+
native labels. Prefixes include scalers, PCA, SimpleImputer (indicators/empty columns),
|
|
180
|
+
KBinsDiscretizer, PolynomialFeatures, univariate selectors, OneHotEncoder/OrdinalEncoder,
|
|
181
|
+
identity/closed NumPy FunctionTransformer, nested native-free Pipeline and ColumnTransformer.
|
|
182
|
+
Each needs a fitted, data-free width proof. Unknown widths, FeatureUnion/KNNImputer,
|
|
183
|
+
slice/callable/scalar selectors, caching, metadata routing and other native placements
|
|
184
|
+
remain rejected. Step names/outer options are declarations; trees address transformed
|
|
185
|
+
features, without original-feature attribution. Schema/dtypes/samples describe original
|
|
186
|
+
inputs; six members permit schema plus explicit samples. See [the pipeline contract](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-pipeline.md).
|
|
187
|
+
|
|
188
|
+
## Existing ONNX files
|
|
189
|
+
|
|
190
|
+
Install the `onnx` extra. This example assumes your existing `trained.onnx` is
|
|
191
|
+
a self-contained tabular classifier with two numeric features and two probability
|
|
192
|
+
columns. Labels on a label-free graph are count-checked declarations:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from pathlib import Path
|
|
196
|
+
from mlbundle import export, load
|
|
197
|
+
|
|
198
|
+
original = Path("trained.onnx").read_bytes()
|
|
199
|
+
export("trained.onnx", "onnx.mlbundle", class_labels=[["cold", "warm"]])
|
|
200
|
+
bundle = load("onnx.mlbundle")
|
|
201
|
+
assert bundle.native_model.data == original
|
|
202
|
+
assert bundle.native_model.session.get_providers() == ["CPUExecutionProvider"]
|
|
203
|
+
assert bundle.class_labels == [["cold", "warm"]]
|
|
204
|
+
print(bundle.predict_proba([[0.0, 0.0]]))
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Paths and bytes are preserved unchanged; a ModelProto is serialized once.
|
|
208
|
+
There is no conversion. `native_model` contains the original `data` and CPU
|
|
209
|
+
`session`. Native graph labels are verified; supplied labels must agree with
|
|
210
|
+
them. Without native/supplied labels, encoded indices are generated. ONNX has
|
|
211
|
+
no visualization structure. See [the exact ONNX contract](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-onnx.md).
|
|
212
|
+
|
|
213
|
+
## Local schema and explicit samples
|
|
214
|
+
|
|
215
|
+
Install a model extra plus `schema` on Python 3.14. Supply a nonempty pandas
|
|
216
|
+
DataFrame with string feature names in model input order. A named target must
|
|
217
|
+
exist and is excluded; without `target_name`, the frame contains features only.
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
import pandas as pd
|
|
221
|
+
from sklearn.tree import DecisionTreeClassifier
|
|
222
|
+
from mlbundle import export, inspect, load
|
|
223
|
+
|
|
224
|
+
training = pd.DataFrame(
|
|
225
|
+
[[0.0, 1.0, "cold"], [1.0, 0.0, "warm"]] * 2,
|
|
226
|
+
columns=["temperature", "humidity", "target"],
|
|
227
|
+
)
|
|
228
|
+
features = training.drop(columns="target")
|
|
229
|
+
model = DecisionTreeClassifier(random_state=7).fit(features, training["target"])
|
|
230
|
+
export(model, "schema.mlbundle", training_data=training, target_name="target")
|
|
231
|
+
view = inspect("schema.mlbundle")
|
|
232
|
+
assert view.manifest.features.dtypes == ["float64", "float64"]
|
|
233
|
+
assert view.schema["reports"][0]["labels"] == ["cold", "warm"]
|
|
234
|
+
assert view.samples is None
|
|
235
|
+
assert load("schema.mlbundle").schema == view.schema
|
|
236
|
+
# Samples are real data: explicitly include the first two feature rows.
|
|
237
|
+
export(model, "sampled.mlbundle", training_data=features, sample_rows=2)
|
|
238
|
+
assert inspect("sampled.mlbundle").samples == features.head(2).to_dict("records")
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`schema.json` contains a schema version, locally inferred fields and one report
|
|
242
|
+
per target output. `color__red`/`color__blue` binary columns can become one
|
|
243
|
+
category field using mlschema's `__` convention. Frame names must match native
|
|
244
|
+
names in order; `feature_names=` cannot rename/reorder a supplied frame.
|
|
245
|
+
Training dtypes are declarations, separate from verified model dtypes. Inspect/load
|
|
246
|
+
validate schema mappings, reports and sample keys without pandas or mlschema.
|
|
247
|
+
|
|
248
|
+
**Data leaving the machine:** names/dtypes, field kinds/nullability, dtype-based
|
|
249
|
+
steps, categories (including unused), one-hot suffixes, pair-axis metadata and
|
|
250
|
+
class labels. No inferred ranges, means, counts or arbitrary text/example rows.
|
|
251
|
+
Names/categories and learned models/structure can reveal training information.
|
|
252
|
+
Explicit `sample_rows=N` includes the first N feature rows, without target/index;
|
|
253
|
+
N must be positive and within the frame. See [schema and samples](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-schema.md).
|
|
254
|
+
|
|
255
|
+
## Inspect, load and the CLI
|
|
256
|
+
|
|
257
|
+
`inspect` validates archive layout, limits, manifest, hashes and schema/sample
|
|
258
|
+
declarations **without importing any model library**. It cannot attest that
|
|
259
|
+
metadata or structure matches the model. `load` additionally deserializes through
|
|
260
|
+
the closed adapter and recomputes native claims and structure before returning
|
|
261
|
+
the model and predictor. Export checks the same native round trip before writing.
|
|
262
|
+
|
|
263
|
+
```shell
|
|
264
|
+
mlbundle inspect trained.mlbundle
|
|
265
|
+
mlbundle inspect trained.mlbundle --json
|
|
266
|
+
mlbundle verify trained.mlbundle
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Inspection prints metadata, members/hashes and declarations. Human values escape
|
|
270
|
+
terminal controls and truncate at 512 characters; `--json` is lossless ASCII-escaped
|
|
271
|
+
manifest JSON. `verify` requires the model extra. Errors are one bounded stderr
|
|
272
|
+
line without bundle content/traceback. Exit codes: **0** success, **1** bundle or
|
|
273
|
+
dependency rejection (`MLBundleError`), **2** usage/I/O failure.
|
|
274
|
+
|
|
275
|
+
## Supported models and limits
|
|
276
|
+
|
|
277
|
+
- **Sklearn:** fitted classifiers exposing `predict_proba` and regressors with
|
|
278
|
+
recoverable feature/output counts. Tested families include trees/ensembles,
|
|
279
|
+
linear/regularized/robust/GLM models, SVMs, PLSRegression, naive Bayes, LDA/QDA,
|
|
280
|
+
dense brute-force neighbors, NearestCentroid, MLP and MultiOutput/RegressorChain.
|
|
281
|
+
SimpleImputer/KNNImputer, scalers, KBinsDiscretizer, categorical encoders,
|
|
282
|
+
univariate selectors and closed NumPy FunctionTransformer functions round-trip,
|
|
283
|
+
including nested ColumnTransformer pipelines; see [the exact lists](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-state.md).
|
|
284
|
+
All-sklearn Pipeline structure stays null; composite trees expose the inner boundary. See
|
|
285
|
+
[tested configurations](https://github.com/UlloaSP/mlbundle/blob/main/tests/test_sklearn_families.py) and [core round trips/rejections](https://github.com/UlloaSP/mlbundle/blob/main/tests/test_sklearn.py).
|
|
286
|
+
- **Sklearn rejections:** custom estimators/subclasses, functions/types outside the
|
|
287
|
+
closed lists (including lambdas and user functions), compound fitted dtypes, classifiers without
|
|
288
|
+
probabilities, multilabel/ClassifierChain layouts, unsupported native placements,
|
|
289
|
+
and untrusted nested storage such as fitted sparse neighbor arrays.
|
|
290
|
+
HistGradientBoosting uses untrusted `TreePredictor` storage and is rejected.
|
|
291
|
+
- **Sklearn versions:** the closed class/attribute catalog is audited/tested
|
|
292
|
+
against **1.9.1 with skops 0.16.0**, not every version allowed by `>=1.6`.
|
|
293
|
+
Unknown state fails; export/load sklearn versions must match. Configurations
|
|
294
|
+
must satisfy the catalog and round trip; see [state ownership](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-state.md).
|
|
295
|
+
- **XGBoost:** exact XGBClassifier/XGBRegressor, one target, native gbtree
|
|
296
|
+
(including XGBoost 3.4.1 dart normalized to gbtree). Closed probability/regression
|
|
297
|
+
objectives; raw boosters, RF wrappers, subclasses, ranking/survival/custom
|
|
298
|
+
objectives and unsupported storage are rejected. At most 100,000 trees/depth 14,
|
|
299
|
+
with complete state/dumps; see [the mapping](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-adapters.md#implemented-xgboost-mapping).
|
|
300
|
+
- **ONNX:** fixed-width rank-two numeric inputs; one regression/probability target,
|
|
301
|
+
optional native labels and dynamic/one-row batches. Unsupported layouts,
|
|
302
|
+
external data/custom domains/functions and runtime/training configuration fail
|
|
303
|
+
before CPU sessions. Tested with ONNX 1.23.0/Runtime 1.30.0; see [ONNX bounds](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-onnx.md).
|
|
304
|
+
|
|
305
|
+
Archive ceilings: **6 members**, **512 MiB** archive/compressed/member bytes,
|
|
306
|
+
**1 GiB** aggregate uncompressed bytes, ratio **200**, JSON depth **32**.
|
|
307
|
+
Manifest: **8 MiB**; structure: **256 MiB**; schema/samples: **16 MiB each**.
|
|
308
|
+
Features/classes: at most **100,000**; outputs: **1,024** (XGBoost/CatBoost/ONNX: one).
|
|
309
|
+
Callers may only lower `ArchiveLimits`. Native [state](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-state.md)/[ONNX](https://github.com/UlloaSP/mlbundle/blob/main/docs/format-onnx.md)
|
|
310
|
+
bounds do not cap process memory, prediction rows or CPU.
|
|
311
|
+
|
|
312
|
+
## Bundle contents and security
|
|
313
|
+
|
|
314
|
+
Every bundle contains `manifest.json` and one native member, or the declared
|
|
315
|
+
`model.skops` + `model.ubj`/`model.cbm` composite pair. Single-step composites use only the leaf. Optional members are `schema.json`, `structure.json`
|
|
316
|
+
(required when the adapter extracts structure) and explicitly requested
|
|
317
|
+
`samples.json`. No training DataFrame or arbitrary files are included.
|
|
318
|
+
The manifest records identity/time/writer, model parameters/counts/state,
|
|
319
|
+
ordered features/dtypes/origin, target labels/name, environment, member
|
|
320
|
+
SHA-256/sizes, references and free-form user metadata. The normative
|
|
321
|
+
[format specification](https://github.com/UlloaSP/mlbundle/blob/main/docs/format.md) owns these records and verification rules.
|
|
322
|
+
|
|
323
|
+
Treat bundles as untrusted. The reader validates ZIP layout, allowed members,
|
|
324
|
+
limits, strict JSON/manifest and hashes; adapters use closed trust/loading paths
|
|
325
|
+
and recompute claims. **Hashes detect corruption, not deliberate tampering**;
|
|
326
|
+
there is no signing or proof of authorship, training history, quality or fairness.
|
|
327
|
+
Schema, samples and training-environment declarations are not authenticated.
|
|
328
|
+
Native parsers/inference require process isolation, memory/CPU/row limits and
|
|
329
|
+
maintained dependencies. See [SECURITY.md](https://github.com/UlloaSP/mlbundle/blob/main/SECURITY.md) for guarantees and reporting.
|
|
330
|
+
|
|
331
|
+
## Development
|
|
332
|
+
|
|
333
|
+
From the root: `uv python install 3.14`, `uv sync --locked --all-extras`,
|
|
334
|
+
`uv run pre-commit install`. Default dev installs every extra; base: `uv sync --no-dev`.
|
|
335
|
+
Pydantic v2 is the sole core dependency; model/schema imports are lazy. Schema uses
|
|
336
|
+
mlschema `>=0.2.1,<0.3` on 3.14 only (0.2.1 excludes 3.15). `uv.lock` fixes tested
|
|
337
|
+
versions; dependency edits require `uv sync` and committing the updated lockfile.
|
|
338
|
+
|
|
339
|
+
Run `uv run ruff check .`, `uv run ruff format --check .`, `uv run pyright`,
|
|
340
|
+
`uv run pytest` and `uv build`. Tests execute every fenced README example and
|
|
341
|
+
write terminal/XML coverage with an 80% branch-inclusive minimum.
|
|
342
|
+
CI checks lint/types and tests the same built wheel on Ubuntu/Windows, including
|
|
343
|
+
clean base installs. The owner, or an agent on the owner's explicit request for
|
|
344
|
+
that version, runs manual [publishing](https://github.com/UlloaSP/mlbundle/blob/main/docs/publishing.md) from `main` with `pypi_env` approval. Read [AGENTS.md](https://github.com/UlloaSP/mlbundle/blob/main/AGENTS.md).
|
|
345
|
+
Agents never run or approve it on their own initiative or add publish paths, tokens or secrets.
|
|
346
|
+
Releases, version tags, conversions, per-model environments, signing and additional
|
|
347
|
+
model libraries remain outside scope. The project is MIT licensed.
|