expops 0.1.3__py3-none-any.whl
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.
- expops-0.1.3.dist-info/METADATA +826 -0
- expops-0.1.3.dist-info/RECORD +86 -0
- expops-0.1.3.dist-info/WHEEL +5 -0
- expops-0.1.3.dist-info/entry_points.txt +3 -0
- expops-0.1.3.dist-info/licenses/LICENSE +674 -0
- expops-0.1.3.dist-info/top_level.txt +1 -0
- mlops/__init__.py +0 -0
- mlops/__main__.py +11 -0
- mlops/_version.py +34 -0
- mlops/adapters/__init__.py +12 -0
- mlops/adapters/base.py +86 -0
- mlops/adapters/config_schema.py +89 -0
- mlops/adapters/custom/__init__.py +3 -0
- mlops/adapters/custom/custom_adapter.py +447 -0
- mlops/adapters/plugin_manager.py +113 -0
- mlops/adapters/sklearn/__init__.py +3 -0
- mlops/adapters/sklearn/adapter.py +94 -0
- mlops/cluster/__init__.py +3 -0
- mlops/cluster/controller.py +496 -0
- mlops/cluster/process_runner.py +91 -0
- mlops/cluster/providers.py +258 -0
- mlops/core/__init__.py +95 -0
- mlops/core/custom_model_base.py +38 -0
- mlops/core/dask_networkx_executor.py +1265 -0
- mlops/core/executor_worker.py +1239 -0
- mlops/core/experiment_tracker.py +81 -0
- mlops/core/graph_types.py +64 -0
- mlops/core/networkx_parser.py +135 -0
- mlops/core/payload_spill.py +278 -0
- mlops/core/pipeline_utils.py +162 -0
- mlops/core/process_hashing.py +216 -0
- mlops/core/step_state_manager.py +1298 -0
- mlops/core/step_system.py +956 -0
- mlops/core/workspace.py +99 -0
- mlops/environment/__init__.py +10 -0
- mlops/environment/base.py +43 -0
- mlops/environment/conda_manager.py +307 -0
- mlops/environment/factory.py +70 -0
- mlops/environment/pyenv_manager.py +146 -0
- mlops/environment/setup_env.py +31 -0
- mlops/environment/system_manager.py +66 -0
- mlops/environment/utils.py +105 -0
- mlops/environment/venv_manager.py +134 -0
- mlops/main.py +527 -0
- mlops/managers/project_manager.py +400 -0
- mlops/managers/reproducibility_manager.py +575 -0
- mlops/platform.py +996 -0
- mlops/reporting/__init__.py +16 -0
- mlops/reporting/context.py +187 -0
- mlops/reporting/entrypoint.py +292 -0
- mlops/reporting/kv_utils.py +77 -0
- mlops/reporting/registry.py +50 -0
- mlops/runtime/__init__.py +9 -0
- mlops/runtime/context.py +34 -0
- mlops/runtime/env_export.py +113 -0
- mlops/storage/__init__.py +12 -0
- mlops/storage/adapters/__init__.py +9 -0
- mlops/storage/adapters/gcp_kv_store.py +778 -0
- mlops/storage/adapters/gcs_object_store.py +96 -0
- mlops/storage/adapters/memory_store.py +240 -0
- mlops/storage/adapters/redis_store.py +438 -0
- mlops/storage/factory.py +199 -0
- mlops/storage/interfaces/__init__.py +6 -0
- mlops/storage/interfaces/kv_store.py +118 -0
- mlops/storage/path_utils.py +38 -0
- mlops/templates/premier-league/charts/plot_metrics.js +70 -0
- mlops/templates/premier-league/charts/plot_metrics.py +145 -0
- mlops/templates/premier-league/charts/requirements.txt +6 -0
- mlops/templates/premier-league/configs/cluster_config.yaml +13 -0
- mlops/templates/premier-league/configs/project_config.yaml +207 -0
- mlops/templates/premier-league/data/England CSV.csv +12154 -0
- mlops/templates/premier-league/models/premier_league_model.py +638 -0
- mlops/templates/premier-league/requirements.txt +8 -0
- mlops/templates/sklearn-basic/README.md +22 -0
- mlops/templates/sklearn-basic/charts/plot_metrics.py +85 -0
- mlops/templates/sklearn-basic/charts/requirements.txt +3 -0
- mlops/templates/sklearn-basic/configs/project_config.yaml +64 -0
- mlops/templates/sklearn-basic/data/train.csv +14 -0
- mlops/templates/sklearn-basic/models/model.py +62 -0
- mlops/templates/sklearn-basic/requirements.txt +10 -0
- mlops/web/__init__.py +3 -0
- mlops/web/server.py +585 -0
- mlops/web/ui/index.html +52 -0
- mlops/web/ui/mlops-charts.js +357 -0
- mlops/web/ui/script.js +1244 -0
- mlops/web/ui/styles.css +248 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, Tuple
|
|
6
|
+
|
|
7
|
+
def _series(metric: Any) -> Tuple[list[int], list[float]]:
|
|
8
|
+
"""Convert {"1": 0.1, "2": 0.2, ...} into ([1,2], [0.1,0.2])."""
|
|
9
|
+
if not isinstance(metric, dict):
|
|
10
|
+
return ([], [])
|
|
11
|
+
pts: list[tuple[int, float]] = []
|
|
12
|
+
for k, v in metric.items():
|
|
13
|
+
try:
|
|
14
|
+
step = int(k)
|
|
15
|
+
val = float(v)
|
|
16
|
+
except Exception:
|
|
17
|
+
continue
|
|
18
|
+
pts.append((step, val))
|
|
19
|
+
pts.sort(key=lambda x: x[0])
|
|
20
|
+
return ([p[0] for p in pts], [p[1] for p in pts])
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _last(metric: Any) -> float | None:
|
|
24
|
+
xs, ys = _series(metric)
|
|
25
|
+
return ys[-1] if ys else None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def plot_metrics(metrics: Dict[str, Any], ctx: Any) -> None:
|
|
29
|
+
"""Chart entrypoint invoked by ExpOps chart runner.
|
|
30
|
+
|
|
31
|
+
- When a KV backend is configured (Redis/Firestore), `metrics` contains probe metrics.
|
|
32
|
+
- When no backend is configured, this template falls back to a local JSON written by `models/model.py`.
|
|
33
|
+
"""
|
|
34
|
+
import matplotlib.pyplot as plt # type: ignore
|
|
35
|
+
|
|
36
|
+
train_block = metrics.get("train") if isinstance(metrics, dict) else None
|
|
37
|
+
eval_block = metrics.get("eval") if isinstance(metrics, dict) else None
|
|
38
|
+
|
|
39
|
+
train_block = train_block if isinstance(train_block, dict) else {}
|
|
40
|
+
eval_block = eval_block if isinstance(eval_block, dict) else {}
|
|
41
|
+
|
|
42
|
+
# 1) Training loss curve
|
|
43
|
+
xs, ys = _series(train_block.get("loss"))
|
|
44
|
+
fig, ax = plt.subplots(figsize=(6, 3))
|
|
45
|
+
if ys:
|
|
46
|
+
ax.plot(xs, ys, marker="o", linewidth=1.5)
|
|
47
|
+
ax.set_xlabel("epoch")
|
|
48
|
+
ax.set_ylabel("loss")
|
|
49
|
+
else:
|
|
50
|
+
ax.text(0.5, 0.5, "No loss metrics found", ha="center", va="center", transform=ax.transAxes)
|
|
51
|
+
ax.set_xticks([])
|
|
52
|
+
ax.set_yticks([])
|
|
53
|
+
ax.set_title("Training loss")
|
|
54
|
+
ax.grid(True, alpha=0.25)
|
|
55
|
+
fig.tight_layout()
|
|
56
|
+
ctx.savefig("training_loss.png", fig=fig)
|
|
57
|
+
plt.close(fig)
|
|
58
|
+
|
|
59
|
+
# 2) Accuracy bars (final train vs eval)
|
|
60
|
+
train_acc = _last(train_block.get("accuracy"))
|
|
61
|
+
eval_acc = _last(eval_block.get("accuracy"))
|
|
62
|
+
labels: list[str] = []
|
|
63
|
+
values: list[float] = []
|
|
64
|
+
if train_acc is not None:
|
|
65
|
+
labels.append("train")
|
|
66
|
+
values.append(float(train_acc))
|
|
67
|
+
if eval_acc is not None:
|
|
68
|
+
labels.append("eval")
|
|
69
|
+
values.append(float(eval_acc))
|
|
70
|
+
|
|
71
|
+
fig, ax = plt.subplots(figsize=(4, 3))
|
|
72
|
+
if values:
|
|
73
|
+
ax.bar(labels, values)
|
|
74
|
+
ax.set_ylim(0.0, 1.0)
|
|
75
|
+
ax.set_ylabel("accuracy")
|
|
76
|
+
else:
|
|
77
|
+
ax.text(0.5, 0.5, "No accuracy metrics found", ha="center", va="center", transform=ax.transAxes)
|
|
78
|
+
ax.set_xticks([])
|
|
79
|
+
ax.set_yticks([])
|
|
80
|
+
ax.set_title("Accuracy")
|
|
81
|
+
ax.grid(True, axis="y", alpha=0.25)
|
|
82
|
+
fig.tight_layout()
|
|
83
|
+
ctx.savefig("accuracy.png", fig=fig)
|
|
84
|
+
plt.close(fig)
|
|
85
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
metadata:
|
|
2
|
+
name: "sklearn-basic"
|
|
3
|
+
description: "Template project: custom pipeline (sklearn inside) + simple reporting chart"
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
|
|
6
|
+
environment:
|
|
7
|
+
venv:
|
|
8
|
+
name: "sklearn-basic-env"
|
|
9
|
+
requirements_file: "projects/sklearn-basic/requirements.txt"
|
|
10
|
+
reporting:
|
|
11
|
+
name: "sklearn-basic-env-reporting"
|
|
12
|
+
requirements_file: "projects/sklearn-basic/charts/requirements.txt"
|
|
13
|
+
|
|
14
|
+
reproducibility:
|
|
15
|
+
random_seed: 42
|
|
16
|
+
|
|
17
|
+
model:
|
|
18
|
+
framework: "custom"
|
|
19
|
+
language: "python"
|
|
20
|
+
name: "sklearn_basic_custom"
|
|
21
|
+
version: "1.0.0"
|
|
22
|
+
parameters:
|
|
23
|
+
custom_script_path: "projects/sklearn-basic/models/model.py"
|
|
24
|
+
cache:
|
|
25
|
+
backend:
|
|
26
|
+
# Local-first default
|
|
27
|
+
type: memory
|
|
28
|
+
# --- GCP example (Firestore + Pub/Sub) ---
|
|
29
|
+
# type: gcp
|
|
30
|
+
# gcp_project: your-gcp-project-id
|
|
31
|
+
# credentials_json: keys/firestore.json
|
|
32
|
+
# emulator_host: 127.0.0.1:8080
|
|
33
|
+
# Optional object store for cache artifacts.
|
|
34
|
+
# object_store:
|
|
35
|
+
# type: gcs
|
|
36
|
+
# bucket: your-gcs-bucket-name
|
|
37
|
+
# prefix: projects/{{PROJECT_ID}}/cache/steps
|
|
38
|
+
executor:
|
|
39
|
+
n_workers: 2
|
|
40
|
+
pipeline:
|
|
41
|
+
process_adjlist: |
|
|
42
|
+
train_model evaluate_model
|
|
43
|
+
evaluate_model plot_metrics
|
|
44
|
+
processes:
|
|
45
|
+
- name: "train_model"
|
|
46
|
+
description: "Train a tiny classifier and log loss/accuracy"
|
|
47
|
+
code_function: "train_model"
|
|
48
|
+
|
|
49
|
+
- name: "evaluate_model"
|
|
50
|
+
description: "Evaluate the trained model and log accuracy"
|
|
51
|
+
code_function: "evaluate_model"
|
|
52
|
+
|
|
53
|
+
- name: "plot_metrics"
|
|
54
|
+
type: chart
|
|
55
|
+
description: "Render a basic PNG report from recorded metrics"
|
|
56
|
+
|
|
57
|
+
reporting:
|
|
58
|
+
static_entrypoint: "projects/sklearn-basic/charts/plot_metrics.py"
|
|
59
|
+
charts:
|
|
60
|
+
- name: "plot_metrics"
|
|
61
|
+
probe_paths:
|
|
62
|
+
train: "train_model"
|
|
63
|
+
eval: "evaluate_model"
|
|
64
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from sklearn.linear_model import LogisticRegression
|
|
7
|
+
from sklearn.metrics import accuracy_score
|
|
8
|
+
|
|
9
|
+
from mlops.core import process, log_metric
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _load_xy(csv_path: str | Path):
|
|
13
|
+
df = pd.read_csv(Path(csv_path))
|
|
14
|
+
y = df.pop("label").values
|
|
15
|
+
x = df.values
|
|
16
|
+
return x, y
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _training_csv_path() -> Path:
|
|
20
|
+
return Path(__file__).resolve().parents[1] / "data" / "train.csv"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _validation_csv_path() -> Path:
|
|
24
|
+
return _training_csv_path()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@process(description="Train a tiny classifier")
|
|
28
|
+
def train_model(data):
|
|
29
|
+
train_path = _training_csv_path()
|
|
30
|
+
if not train_path.exists():
|
|
31
|
+
raise FileNotFoundError(
|
|
32
|
+
f"Training CSV not found at {train_path}. "
|
|
33
|
+
"Expected the template dataset at projects/<project_id>/data/train.csv."
|
|
34
|
+
)
|
|
35
|
+
x, y = _load_xy(train_path)
|
|
36
|
+
model = LogisticRegression(max_iter=200)
|
|
37
|
+
model.fit(x, y)
|
|
38
|
+
|
|
39
|
+
train_acc = float(accuracy_score(y, model.predict(x)))
|
|
40
|
+
log_metric("accuracy", train_acc, step=1)
|
|
41
|
+
|
|
42
|
+
return {"model": model, "train_accuracy": train_acc}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@process(description="Evaluate the model")
|
|
46
|
+
def evaluate_model(data):
|
|
47
|
+
model = (data or {}).get("train_model", {}).get("model")
|
|
48
|
+
if model is None:
|
|
49
|
+
raise ValueError("Missing upstream model. Expected data['train_model']['model'].")
|
|
50
|
+
|
|
51
|
+
val_path = _validation_csv_path()
|
|
52
|
+
if not val_path.exists():
|
|
53
|
+
raise FileNotFoundError(
|
|
54
|
+
f"Validation CSV not found at {val_path}. "
|
|
55
|
+
"Expected the template dataset at projects/<project_id>/data/train.csv."
|
|
56
|
+
)
|
|
57
|
+
x, y = _load_xy(val_path)
|
|
58
|
+
eval_acc = float(accuracy_score(y, model.predict(x)))
|
|
59
|
+
log_metric("accuracy", eval_acc, step=1)
|
|
60
|
+
|
|
61
|
+
return {"evaluation_accuracy": eval_acc}
|
|
62
|
+
|
mlops/web/__init__.py
ADDED