modelzone-sdk 0.2.0.dev0__tar.gz → 0.3.0__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.
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/PKG-INFO +12 -9
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/README.md +8 -8
- modelzone_sdk-0.3.0/modelzone/__init__.py +19 -0
- modelzone_sdk-0.3.0/modelzone/azureml/__init__.py +27 -0
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/modelzone/azureml/backend.py +41 -66
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/modelzone/cli.py +51 -80
- modelzone_sdk-0.3.0/modelzone/core/__init__.py +8 -0
- modelzone_sdk-0.3.0/modelzone/core/constants.py +9 -0
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/modelzone/core/model.py +12 -14
- modelzone_sdk-0.3.0/modelzone/core/model_artifact.py +63 -0
- modelzone_sdk-0.3.0/modelzone/core/predict_context.py +41 -0
- modelzone_sdk-0.3.0/modelzone/core/protocols.py +25 -0
- modelzone_sdk-0.2.0.dev0/modelzone/training/context.py → modelzone_sdk-0.3.0/modelzone/core/training_context.py +27 -27
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/modelzone/core/training_result.py +3 -7
- modelzone_sdk-0.3.0/modelzone/project_config.py +73 -0
- modelzone_sdk-0.3.0/modelzone/training/__init__.py +19 -0
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/modelzone/training/backend.py +26 -72
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/modelzone/training/db.py +9 -9
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/pyproject.toml +13 -19
- modelzone_sdk-0.2.0.dev0/modelzone/__init__.py +0 -27
- modelzone_sdk-0.2.0.dev0/modelzone/azureml/__init__.py +0 -35
- modelzone_sdk-0.2.0.dev0/modelzone/core/__init__.py +0 -20
- modelzone_sdk-0.2.0.dev0/modelzone/core/constants.py +0 -6
- modelzone_sdk-0.2.0.dev0/modelzone/core/predict_context.py +0 -47
- modelzone_sdk-0.2.0.dev0/modelzone/core/record.py +0 -37
- modelzone_sdk-0.2.0.dev0/modelzone/core/trained_model.py +0 -86
- modelzone_sdk-0.2.0.dev0/modelzone/predict.py +0 -41
- modelzone_sdk-0.2.0.dev0/modelzone/training/__init__.py +0 -32
- {modelzone_sdk-0.2.0.dev0 → modelzone_sdk-0.3.0}/modelzone/training/delta_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modelzone-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Modelzone SDK – a slim model training and serving toolkit
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Author: Team Enigma
|
|
@@ -14,8 +14,11 @@ Provides-Extra: azureml
|
|
|
14
14
|
Provides-Extra: training
|
|
15
15
|
Requires-Dist: azure-ai-ml ; extra == "azureml"
|
|
16
16
|
Requires-Dist: azure-identity ; extra == "azureml"
|
|
17
|
+
Requires-Dist: deltalake (>=1) ; extra == "azureml"
|
|
17
18
|
Requires-Dist: deltalake (>=1) ; extra == "training"
|
|
18
19
|
Requires-Dist: mlflow (>=3) ; extra == "azureml"
|
|
20
|
+
Requires-Dist: pandas (<3) ; extra == "azureml"
|
|
21
|
+
Requires-Dist: pandas (<3) ; extra == "training"
|
|
19
22
|
Description-Content-Type: text/markdown
|
|
20
23
|
|
|
21
24
|
# Modelzone SDK
|
|
@@ -42,22 +45,22 @@ pip install modelzone-sdk[azureml]
|
|
|
42
45
|
|
|
43
46
|
## Quick start
|
|
44
47
|
|
|
45
|
-
Define a model by subclassing `
|
|
48
|
+
Define a model by subclassing `ModelDefinition` and implementing `train` and `predict`:
|
|
46
49
|
|
|
47
50
|
```python
|
|
48
|
-
from modelzone.core import
|
|
51
|
+
from modelzone.core import ModelDefinition, PredictContext, ModelArtifact
|
|
49
52
|
from modelzone.training import TrainingContext
|
|
50
53
|
|
|
51
54
|
|
|
52
|
-
class MyModel(
|
|
53
|
-
def train(self, ctx: TrainingContext) ->
|
|
55
|
+
class MyModel(ModelDefinition):
|
|
56
|
+
def train(self, ctx: TrainingContext) -> ModelArtifact:
|
|
54
57
|
ctx.print("Training started")
|
|
55
58
|
|
|
56
59
|
# … your training logic …
|
|
57
60
|
fitted = train_something(seed=ctx.seed)
|
|
58
61
|
|
|
59
62
|
ctx.log_metric("accuracy", 0.95)
|
|
60
|
-
return
|
|
63
|
+
return ModelArtifact(model=fitted, features=["feature_a", "feature_b"])
|
|
61
64
|
|
|
62
65
|
def predict(self, ctx: PredictContext):
|
|
63
66
|
df = ctx.db.query("input_table", ctx.time_interval)
|
|
@@ -91,10 +94,10 @@ result = backend.run(MyModel(), seed=42)
|
|
|
91
94
|
### Loading a trained model for prediction
|
|
92
95
|
|
|
93
96
|
```python
|
|
94
|
-
from modelzone.predict import
|
|
97
|
+
from modelzone.predict import load_model_artifact
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
print(
|
|
99
|
+
model_artifact = load_model_artifact(".model")
|
|
100
|
+
print(model_artifact.features)
|
|
98
101
|
```
|
|
99
102
|
|
|
100
103
|
## CLI
|
|
@@ -22,22 +22,22 @@ pip install modelzone-sdk[azureml]
|
|
|
22
22
|
|
|
23
23
|
## Quick start
|
|
24
24
|
|
|
25
|
-
Define a model by subclassing `
|
|
25
|
+
Define a model by subclassing `ModelDefinition` and implementing `train` and `predict`:
|
|
26
26
|
|
|
27
27
|
```python
|
|
28
|
-
from modelzone.core import
|
|
28
|
+
from modelzone.core import ModelDefinition, PredictContext, ModelArtifact
|
|
29
29
|
from modelzone.training import TrainingContext
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
class MyModel(
|
|
33
|
-
def train(self, ctx: TrainingContext) ->
|
|
32
|
+
class MyModel(ModelDefinition):
|
|
33
|
+
def train(self, ctx: TrainingContext) -> ModelArtifact:
|
|
34
34
|
ctx.print("Training started")
|
|
35
35
|
|
|
36
36
|
# … your training logic …
|
|
37
37
|
fitted = train_something(seed=ctx.seed)
|
|
38
38
|
|
|
39
39
|
ctx.log_metric("accuracy", 0.95)
|
|
40
|
-
return
|
|
40
|
+
return ModelArtifact(model=fitted, features=["feature_a", "feature_b"])
|
|
41
41
|
|
|
42
42
|
def predict(self, ctx: PredictContext):
|
|
43
43
|
df = ctx.db.query("input_table", ctx.time_interval)
|
|
@@ -71,10 +71,10 @@ result = backend.run(MyModel(), seed=42)
|
|
|
71
71
|
### Loading a trained model for prediction
|
|
72
72
|
|
|
73
73
|
```python
|
|
74
|
-
from modelzone.predict import
|
|
74
|
+
from modelzone.predict import load_model_artifact
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
print(
|
|
76
|
+
model_artifact = load_model_artifact(".model")
|
|
77
|
+
print(model_artifact.features)
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
## CLI
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Modelzone SDK – structured model training and serving.
|
|
2
|
+
|
|
3
|
+
Subpackages
|
|
4
|
+
-----------
|
|
5
|
+
- :mod:`modelzone.core` – ``ModelDefinition``, ``ModelArtifact``,
|
|
6
|
+
``TrainingResult`` and friends (zero extras, safe everywhere).
|
|
7
|
+
- :mod:`modelzone.training` – ``LocalBackend``, ``TrainingContext``,
|
|
8
|
+
``ModelzoneDatabase`` (training environment).
|
|
9
|
+
- :mod:`modelzone.azureml` – ``AzureMLBackend`` (requires ``azureml``
|
|
10
|
+
extra).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from modelzone import project_config # noqa: F401
|
|
14
|
+
from modelzone.core import ( # noqa: F401
|
|
15
|
+
ModelArtifact,
|
|
16
|
+
ModelDefinition,
|
|
17
|
+
PredictContext,
|
|
18
|
+
TrainingResult,
|
|
19
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Modelzone AzureML backend – tracked experiment runs (SDK v2 + MLflow).
|
|
2
|
+
|
|
3
|
+
Requires ``azure-ai-ml``, ``azure-identity``, and ``mlflow``.
|
|
4
|
+
Install with::
|
|
5
|
+
|
|
6
|
+
pip install modelzone-sdk[azureml]
|
|
7
|
+
|
|
8
|
+
Usage::
|
|
9
|
+
|
|
10
|
+
from modelzone.azureml import AzureMLBackend
|
|
11
|
+
|
|
12
|
+
backend = AzureMLBackend(
|
|
13
|
+
subscription_id="...",
|
|
14
|
+
resource_group="...",
|
|
15
|
+
workspace_name="...",
|
|
16
|
+
experiment_name="my_experiment",
|
|
17
|
+
)
|
|
18
|
+
result = backend.run(model, seed=42, db=db)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from modelzone.azureml.backend import AzureMLBackend # noqa: F401
|
|
23
|
+
except ImportError as e:
|
|
24
|
+
raise ImportError(
|
|
25
|
+
"AzureMLBackend requires the 'azureml' extra. "
|
|
26
|
+
"Install it with: pip install modelzone-sdk[azureml]"
|
|
27
|
+
) from e
|
|
@@ -17,12 +17,17 @@ import shutil
|
|
|
17
17
|
import tempfile
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
|
-
import datamazing.pandas as pdz
|
|
21
20
|
import requests
|
|
22
21
|
|
|
23
|
-
from modelzone.core
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
from modelzone.core import (
|
|
23
|
+
ModelArtifact,
|
|
24
|
+
ModelDefinition,
|
|
25
|
+
TrainingContext,
|
|
26
|
+
TrainingResult,
|
|
27
|
+
)
|
|
28
|
+
from modelzone.core.constants import TRAINED_MODEL_FILENAME
|
|
29
|
+
from modelzone.core.protocols import Database
|
|
30
|
+
from modelzone.training import Backend
|
|
26
31
|
|
|
27
32
|
try:
|
|
28
33
|
import mlflow
|
|
@@ -84,20 +89,12 @@ class AzureMLBackend(Backend):
|
|
|
84
89
|
self.resource_group = resource_group
|
|
85
90
|
self.workspace_name = workspace_name
|
|
86
91
|
self.experiment_name = experiment_name
|
|
87
|
-
self.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
credential = DefaultAzureCredential()
|
|
94
|
-
self._ml_client = MLClient(
|
|
95
|
-
credential=credential,
|
|
96
|
-
subscription_id=self.subscription_id,
|
|
97
|
-
resource_group_name=self.resource_group,
|
|
98
|
-
workspace_name=self.workspace_name,
|
|
99
|
-
)
|
|
100
|
-
return self._ml_client
|
|
92
|
+
self.ml_client = MLClient(
|
|
93
|
+
credential=DefaultAzureCredential(),
|
|
94
|
+
subscription_id=subscription_id,
|
|
95
|
+
resource_group_name=resource_group,
|
|
96
|
+
workspace_name=workspace_name,
|
|
97
|
+
)
|
|
101
98
|
|
|
102
99
|
def _configure_mlflow(self) -> None:
|
|
103
100
|
"""Point MLflow at the AzureML workspace tracking URI.
|
|
@@ -240,12 +237,12 @@ class AzureMLBackend(Backend):
|
|
|
240
237
|
|
|
241
238
|
def run(
|
|
242
239
|
self,
|
|
243
|
-
model:
|
|
240
|
+
model: ModelDefinition,
|
|
244
241
|
*,
|
|
245
242
|
seed: int | None = None,
|
|
246
243
|
params: dict[str, Any] | None = None,
|
|
247
244
|
tags: dict[str, str] | None = None,
|
|
248
|
-
db:
|
|
245
|
+
db: Database | None = None,
|
|
249
246
|
source_dir: str | None = None,
|
|
250
247
|
) -> TrainingResult:
|
|
251
248
|
"""Execute a training run tracked by AzureML via MLflow."""
|
|
@@ -283,8 +280,7 @@ class AzureMLBackend(Backend):
|
|
|
283
280
|
print(f"\n AzureML run: {portal_url}")
|
|
284
281
|
|
|
285
282
|
return TrainingResult(
|
|
286
|
-
|
|
287
|
-
record=result.record,
|
|
283
|
+
model_artifact=result.model_artifact,
|
|
288
284
|
run_id=run_id,
|
|
289
285
|
)
|
|
290
286
|
|
|
@@ -293,7 +289,7 @@ class AzureMLBackend(Backend):
|
|
|
293
289
|
run_dir: str,
|
|
294
290
|
seed: int,
|
|
295
291
|
params: dict[str, Any] | None,
|
|
296
|
-
db:
|
|
292
|
+
db: Database | None,
|
|
297
293
|
) -> _AzureMLTrainingContext:
|
|
298
294
|
"""Return an MLflow-aware context and log params/seed eagerly."""
|
|
299
295
|
if params:
|
|
@@ -328,16 +324,12 @@ class AzureMLBackend(Backend):
|
|
|
328
324
|
)
|
|
329
325
|
|
|
330
326
|
with open(os.path.join(local_path, TRAINED_MODEL_FILENAME), "rb") as f:
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
with open(os.path.join(local_path, RECORD_FILENAME), "rb") as f:
|
|
334
|
-
record: RunRecord = pickle.load(f) # noqa: S301
|
|
327
|
+
model_artifact: ModelArtifact = pickle.load(f) # noqa: S301
|
|
335
328
|
|
|
336
329
|
shutil.rmtree(download_dir)
|
|
337
330
|
|
|
338
331
|
return TrainingResult(
|
|
339
|
-
|
|
340
|
-
record=record,
|
|
332
|
+
model_artifact=model_artifact,
|
|
341
333
|
run_id=run_id,
|
|
342
334
|
)
|
|
343
335
|
|
|
@@ -348,7 +340,7 @@ class AzureMLBackend(Backend):
|
|
|
348
340
|
def register(
|
|
349
341
|
self,
|
|
350
342
|
run_id: str,
|
|
351
|
-
|
|
343
|
+
registered_name: str,
|
|
352
344
|
*,
|
|
353
345
|
description: str | None = None,
|
|
354
346
|
tags: dict[str, str] | None = None,
|
|
@@ -365,7 +357,7 @@ class AzureMLBackend(Backend):
|
|
|
365
357
|
Args:
|
|
366
358
|
run_id: The MLflow run ID whose ``run_output`` artifacts
|
|
367
359
|
will back the registered model.
|
|
368
|
-
|
|
360
|
+
registered_name: Name for the model in the AzureML Model Registry.
|
|
369
361
|
description: Optional human-readable description.
|
|
370
362
|
tags: Optional key/value tags stored on the model version.
|
|
371
363
|
|
|
@@ -379,14 +371,14 @@ class AzureMLBackend(Backend):
|
|
|
379
371
|
merged_tags.update(tags)
|
|
380
372
|
|
|
381
373
|
try:
|
|
382
|
-
existing = self.ml_client.models.list(name=
|
|
374
|
+
existing = self.ml_client.models.list(name=registered_name)
|
|
383
375
|
versions = [int(m.version) for m in existing if m.version.isdigit()]
|
|
384
376
|
except Exception:
|
|
385
377
|
versions = []
|
|
386
378
|
version = str(max(versions) + 1) if versions else "1"
|
|
387
379
|
|
|
388
380
|
model = AzureMLModel(
|
|
389
|
-
name=
|
|
381
|
+
name=registered_name,
|
|
390
382
|
version=version,
|
|
391
383
|
path=f"azureml://jobs/{run_id}/outputs/artifacts/run_output",
|
|
392
384
|
type=AssetTypes.CUSTOM_MODEL,
|
|
@@ -398,67 +390,51 @@ class AzureMLBackend(Backend):
|
|
|
398
390
|
|
|
399
391
|
def download_registered(
|
|
400
392
|
self,
|
|
401
|
-
|
|
402
|
-
version: str
|
|
393
|
+
registered_name: str,
|
|
394
|
+
version: str,
|
|
403
395
|
) -> tuple[TrainingResult, str, str]:
|
|
404
396
|
"""Download a registered model, returning the result and the local path.
|
|
405
397
|
|
|
406
398
|
The local path points to the downloaded model directory which
|
|
407
|
-
contains ``trained_model.pkl``, ``
|
|
399
|
+
contains ``trained_model.pkl``, ``run_info.json``, and ``code/``.
|
|
408
400
|
This is useful at build time to export the model into a
|
|
409
401
|
:class:`~modelzone.training.LocalBackend` via
|
|
410
402
|
:meth:`~modelzone.training.LocalBackend.save`.
|
|
411
403
|
|
|
412
404
|
Args:
|
|
413
|
-
|
|
414
|
-
version: Version to download.
|
|
415
|
-
version is used.
|
|
405
|
+
registered_name: Name of the model in the AzureML Model Registry.
|
|
406
|
+
version: Version to download.
|
|
416
407
|
|
|
417
408
|
Returns:
|
|
418
|
-
A tuple of ``(TrainingResult, model_dir_path)``.
|
|
409
|
+
A tuple of ``(TrainingResult, model_dir_path, download_dir)``.
|
|
419
410
|
"""
|
|
420
|
-
if version is None:
|
|
421
|
-
try:
|
|
422
|
-
existing = self.ml_client.models.list(name=model_name)
|
|
423
|
-
versions = [m for m in existing if m.version.isdigit()]
|
|
424
|
-
except Exception:
|
|
425
|
-
versions = []
|
|
426
|
-
if not versions:
|
|
427
|
-
raise ValueError(f"No versions found for model '{model_name}'")
|
|
428
|
-
latest = max(versions, key=lambda m: int(m.version))
|
|
429
|
-
version = latest.version
|
|
430
|
-
|
|
431
411
|
download_dir = tempfile.mkdtemp(prefix="modelzone_registered_")
|
|
432
412
|
self.ml_client.models.download(
|
|
433
|
-
name=
|
|
413
|
+
name=registered_name,
|
|
434
414
|
version=version,
|
|
435
415
|
download_path=download_dir,
|
|
436
416
|
)
|
|
437
417
|
|
|
438
|
-
local_path = os.path.join(download_dir,
|
|
418
|
+
local_path = os.path.join(download_dir, registered_name, "run_output")
|
|
439
419
|
|
|
440
420
|
with open(os.path.join(local_path, TRAINED_MODEL_FILENAME), "rb") as f:
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
with open(os.path.join(local_path, RECORD_FILENAME), "rb") as f:
|
|
444
|
-
record: RunRecord = pickle.load(f) # noqa: S301
|
|
421
|
+
model_artifact: ModelArtifact = pickle.load(f) # noqa: S301
|
|
445
422
|
|
|
446
423
|
run_id = ""
|
|
447
|
-
model_entity = self.ml_client.models.get(name=
|
|
424
|
+
model_entity = self.ml_client.models.get(name=registered_name, version=version)
|
|
448
425
|
if model_entity.tags:
|
|
449
426
|
run_id = model_entity.tags.get("run_id", "")
|
|
450
427
|
|
|
451
428
|
result = TrainingResult(
|
|
452
|
-
|
|
453
|
-
record=record,
|
|
429
|
+
model_artifact=model_artifact,
|
|
454
430
|
run_id=run_id,
|
|
455
431
|
)
|
|
456
432
|
return result, local_path, download_dir
|
|
457
433
|
|
|
458
434
|
def load_registered(
|
|
459
435
|
self,
|
|
460
|
-
|
|
461
|
-
version: str
|
|
436
|
+
registered_name: str,
|
|
437
|
+
version: str,
|
|
462
438
|
) -> TrainingResult:
|
|
463
439
|
"""Download a registered model and reconstruct a TrainingResult.
|
|
464
440
|
|
|
@@ -466,13 +442,12 @@ class AzureMLBackend(Backend):
|
|
|
466
442
|
discards the local download path.
|
|
467
443
|
|
|
468
444
|
Args:
|
|
469
|
-
|
|
470
|
-
version: Version to download.
|
|
471
|
-
version is used.
|
|
445
|
+
registered_name: Name of the model in the AzureML Model Registry.
|
|
446
|
+
version: Version to download.
|
|
472
447
|
|
|
473
448
|
Returns:
|
|
474
449
|
The reconstructed :class:`TrainingResult`.
|
|
475
450
|
"""
|
|
476
|
-
result, _, download_dir = self.download_registered(
|
|
451
|
+
result, _, download_dir = self.download_registered(registered_name, version)
|
|
477
452
|
shutil.rmtree(download_dir)
|
|
478
453
|
return result
|
|
@@ -1,57 +1,31 @@
|
|
|
1
1
|
"""Modelzone CLI – train, register, and fetch models.
|
|
2
2
|
|
|
3
3
|
Provides ``modelzone train``, ``modelzone register``, and
|
|
4
|
-
``modelzone fetch`` as console entry points
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
All commands expect to be run from the project root directory
|
|
8
|
-
containing ``project.json`` and model packages with ``model.json``.
|
|
4
|
+
``modelzone fetch`` as console entry points. All commands expect
|
|
5
|
+
a ``pyproject.toml`` with a ``[tool.modelzone]`` section.
|
|
9
6
|
"""
|
|
10
7
|
|
|
11
8
|
from __future__ import annotations
|
|
12
9
|
|
|
13
10
|
import argparse
|
|
14
11
|
import importlib
|
|
15
|
-
import json
|
|
16
12
|
import os
|
|
17
13
|
import sys
|
|
18
14
|
|
|
19
|
-
from modelzone import
|
|
20
|
-
from modelzone.core import Model
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _load_project() -> dict:
|
|
24
|
-
if not os.path.isfile(PROJECT_CONFIG):
|
|
25
|
-
print(f"Error: {PROJECT_CONFIG} not found in {os.getcwd()}", file=sys.stderr)
|
|
26
|
-
sys.exit(1)
|
|
27
|
-
with open(PROJECT_CONFIG) as f:
|
|
28
|
-
return json.load(f)
|
|
29
|
-
|
|
15
|
+
from modelzone import ModelDefinition, project_config
|
|
30
16
|
|
|
31
|
-
def _load_model_config(model_package: str) -> dict:
|
|
32
|
-
path = os.path.join(model_package, MODEL_CONFIG)
|
|
33
|
-
if not os.path.isfile(path):
|
|
34
|
-
print(f"Error: {path} not found in {os.getcwd()}", file=sys.stderr)
|
|
35
|
-
sys.exit(1)
|
|
36
|
-
with open(path) as f:
|
|
37
|
-
return json.load(f)
|
|
38
17
|
|
|
39
|
-
|
|
40
|
-
def _load_root_model_config() -> dict:
|
|
41
|
-
if not os.path.isfile(MODEL_CONFIG):
|
|
42
|
-
print(f"Error: {MODEL_CONFIG} not found in {os.getcwd()}", file=sys.stderr)
|
|
43
|
-
sys.exit(1)
|
|
44
|
-
with open(MODEL_CONFIG) as f:
|
|
45
|
-
return json.load(f)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _discover_model_class(model_package: str) -> type[Model]:
|
|
18
|
+
def _discover_model_class(model_package: str) -> type[ModelDefinition]:
|
|
49
19
|
mod = importlib.import_module(model_package)
|
|
50
20
|
for v in vars(mod).values():
|
|
51
|
-
if
|
|
21
|
+
if (
|
|
22
|
+
isinstance(v, type)
|
|
23
|
+
and issubclass(v, ModelDefinition)
|
|
24
|
+
and v is not ModelDefinition
|
|
25
|
+
):
|
|
52
26
|
return v
|
|
53
27
|
print(
|
|
54
|
-
f"Error: no
|
|
28
|
+
f"Error: no ModelDefinition subclass found in {model_package}",
|
|
55
29
|
file=sys.stderr,
|
|
56
30
|
)
|
|
57
31
|
sys.exit(1)
|
|
@@ -78,7 +52,7 @@ def _build_db(config: dict, cache: bool = True):
|
|
|
78
52
|
|
|
79
53
|
|
|
80
54
|
def train(args: argparse.Namespace) -> None:
|
|
81
|
-
config =
|
|
55
|
+
config = project_config.load_model(args.model_package)
|
|
82
56
|
db, tags = _build_db(config, cache=not args.no_cache)
|
|
83
57
|
|
|
84
58
|
params = {}
|
|
@@ -88,12 +62,12 @@ def train(args: argparse.Namespace) -> None:
|
|
|
88
62
|
if args.azureml:
|
|
89
63
|
from modelzone.azureml import AzureMLBackend
|
|
90
64
|
|
|
91
|
-
project =
|
|
92
|
-
|
|
65
|
+
project = project_config.load_project()
|
|
66
|
+
azureml_backend = AzureMLBackend(
|
|
93
67
|
**project["workspace"],
|
|
94
|
-
experiment_name=config["
|
|
68
|
+
experiment_name=config["name"],
|
|
95
69
|
)
|
|
96
|
-
result =
|
|
70
|
+
result = azureml_backend.run(
|
|
97
71
|
_discover_model_class(args.model_package)(),
|
|
98
72
|
seed=42,
|
|
99
73
|
db=db,
|
|
@@ -105,8 +79,8 @@ def train(args: argparse.Namespace) -> None:
|
|
|
105
79
|
else:
|
|
106
80
|
from modelzone.training import LocalBackend
|
|
107
81
|
|
|
108
|
-
|
|
109
|
-
result =
|
|
82
|
+
local_backend = LocalBackend(root="runs")
|
|
83
|
+
result = local_backend.run(
|
|
110
84
|
_discover_model_class(args.model_package)(),
|
|
111
85
|
seed=42,
|
|
112
86
|
db=db,
|
|
@@ -116,58 +90,58 @@ def train(args: argparse.Namespace) -> None:
|
|
|
116
90
|
)
|
|
117
91
|
print(f"\nLocal Run ID: {result.run_id}")
|
|
118
92
|
|
|
119
|
-
for m in result.record.metrics:
|
|
120
|
-
print(f" {m.name} = {m.value}")
|
|
121
|
-
|
|
122
93
|
|
|
123
94
|
def register(args: argparse.Namespace) -> None:
|
|
124
95
|
from modelzone.azureml import AzureMLBackend
|
|
125
96
|
|
|
126
|
-
project =
|
|
127
|
-
config =
|
|
97
|
+
project = project_config.load_project()
|
|
98
|
+
config = project_config.load_model(args.model_package)
|
|
128
99
|
|
|
129
100
|
backend = AzureMLBackend(
|
|
130
101
|
**project["workspace"],
|
|
131
|
-
experiment_name=config["
|
|
102
|
+
experiment_name=config["name"],
|
|
132
103
|
)
|
|
133
104
|
registered = backend.register(
|
|
134
105
|
run_id=args.run_id,
|
|
135
|
-
|
|
106
|
+
registered_name=config["name"],
|
|
136
107
|
)
|
|
137
108
|
print(f"Registered: {registered.name} v{registered.version}")
|
|
138
109
|
|
|
139
110
|
|
|
140
|
-
def
|
|
111
|
+
def _fetch_one(
|
|
112
|
+
config: dict,
|
|
113
|
+
workspace: dict,
|
|
114
|
+
) -> None:
|
|
141
115
|
import pickle
|
|
142
116
|
import shutil
|
|
143
117
|
|
|
144
118
|
from modelzone.azureml import AzureMLBackend
|
|
145
119
|
|
|
146
|
-
|
|
147
|
-
if args.model_package:
|
|
148
|
-
config = _load_model_config(args.model_package)
|
|
149
|
-
else:
|
|
150
|
-
config = _load_root_model_config()
|
|
151
|
-
model_name = config["model_name"]
|
|
120
|
+
model_name = config["name"]
|
|
152
121
|
|
|
153
122
|
backend = AzureMLBackend(
|
|
154
|
-
**
|
|
155
|
-
experiment_name=config["
|
|
123
|
+
**workspace,
|
|
124
|
+
experiment_name=config["name"],
|
|
156
125
|
)
|
|
157
126
|
|
|
158
|
-
version =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
127
|
+
version = config.get("model_version")
|
|
128
|
+
if version is None:
|
|
129
|
+
print(
|
|
130
|
+
f"Error: model_version is required for {model_name}",
|
|
131
|
+
file=sys.stderr,
|
|
132
|
+
)
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
version = str(version)
|
|
135
|
+
print(f"Downloading {model_name} (version={version})...")
|
|
136
|
+
result, model_dir, download_dir = backend.download_registered(
|
|
137
|
+
model_name, version=version
|
|
138
|
+
)
|
|
162
139
|
|
|
163
|
-
out =
|
|
140
|
+
out = config["path"]
|
|
164
141
|
os.makedirs(out, exist_ok=True)
|
|
165
142
|
|
|
166
143
|
with open(os.path.join(out, "trained_model.pkl"), "wb") as f:
|
|
167
|
-
pickle.dump(result.
|
|
168
|
-
|
|
169
|
-
with open(os.path.join(out, "record.pkl"), "wb") as f:
|
|
170
|
-
pickle.dump(result.record, f)
|
|
144
|
+
pickle.dump(result.model_artifact, f)
|
|
171
145
|
|
|
172
146
|
src_code = os.path.join(model_dir, "code")
|
|
173
147
|
dst_code = os.path.join(out, "code")
|
|
@@ -180,6 +154,13 @@ def fetch(args: argparse.Namespace) -> None:
|
|
|
180
154
|
print(f"Saved to {out}")
|
|
181
155
|
|
|
182
156
|
|
|
157
|
+
def fetch(args: argparse.Namespace) -> None:
|
|
158
|
+
project = project_config.load_project()
|
|
159
|
+
models = project_config.load_all_models()
|
|
160
|
+
for config in models:
|
|
161
|
+
_fetch_one(config, project["workspace"])
|
|
162
|
+
|
|
163
|
+
|
|
183
164
|
def main() -> None:
|
|
184
165
|
parser = argparse.ArgumentParser(prog="modelzone", description="Modelzone CLI")
|
|
185
166
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -204,19 +185,9 @@ def main() -> None:
|
|
|
204
185
|
r.add_argument("model_package", help="Model package name")
|
|
205
186
|
r.add_argument("run_id", help="MLflow run ID to register")
|
|
206
187
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"model_package",
|
|
210
|
-
nargs="?",
|
|
211
|
-
default=None,
|
|
212
|
-
help="Model package name (omit to read model.json from cwd)",
|
|
213
|
-
)
|
|
214
|
-
f.add_argument(
|
|
215
|
-
"--version",
|
|
216
|
-
default=None,
|
|
217
|
-
help="Model version (default: model_version from config, or latest)",
|
|
188
|
+
sub.add_parser(
|
|
189
|
+
"fetch", help="Download all registered models defined in pyproject.toml"
|
|
218
190
|
)
|
|
219
|
-
f.add_argument("--output-dir", default=".model", help="Local output directory")
|
|
220
191
|
|
|
221
192
|
args = parser.parse_args()
|
|
222
193
|
if args.command == "train":
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Modelzone core – minimal runtime classes for training and prediction."""
|
|
2
|
+
|
|
3
|
+
from modelzone.core.constants import CODE_DIR, TRAINED_MODEL_FILENAME # noqa: F401
|
|
4
|
+
from modelzone.core.model import ModelDefinition # noqa: F401
|
|
5
|
+
from modelzone.core.model_artifact import ModelArtifact # noqa: F401
|
|
6
|
+
from modelzone.core.predict_context import PredictContext # noqa: F401
|
|
7
|
+
from modelzone.core.training_context import TrainingContext # noqa: F401
|
|
8
|
+
from modelzone.core.training_result import TrainingResult # noqa: F401
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Modelzone core – shared filename conventions for run directories."""
|
|
2
|
+
|
|
3
|
+
# Filename used for the pickled ModelArtifact inside every run directory.
|
|
4
|
+
TRAINED_MODEL_FILENAME = "trained_model.pkl"
|
|
5
|
+
RUN_INFO_FILENAME = "run_info.json"
|
|
6
|
+
METRICS_FILENAME = "metrics.jsonl"
|
|
7
|
+
LOG_FILENAME = "output.log"
|
|
8
|
+
FILES_DIR = "files"
|
|
9
|
+
CODE_DIR = "code"
|