203 lines
6.0 KiB
Python
203 lines
6.0 KiB
Python
import json
|
|
import logging
|
|
import os
|
|
from abc import ABC, abstractmethod
|
|
|
|
import mlflow
|
|
from mlflow.recipes.utils.execution import get_step_output_path
|
|
from mlflow.tracking import MlflowClient
|
|
from mlflow.tracking._tracking_service.utils import _use_tracking_uri
|
|
from mlflow.utils.file_utils import chdir
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Artifact(ABC):
|
|
@abstractmethod
|
|
def name(self):
|
|
pass
|
|
|
|
@abstractmethod
|
|
def path(self):
|
|
pass
|
|
|
|
@abstractmethod
|
|
def load(self):
|
|
pass
|
|
|
|
|
|
class DataframeArtifact(Artifact):
|
|
def __init__(self, name, recipe_root, step_name, rel_path=""):
|
|
self._name = name
|
|
self._path = get_step_output_path(recipe_root, step_name, rel_path)
|
|
self._step_name = step_name
|
|
|
|
def name(self):
|
|
return self._name
|
|
|
|
def path(self):
|
|
return self._path
|
|
|
|
def load(self):
|
|
import pandas as pd
|
|
|
|
if os.path.exists(self._path):
|
|
return pd.read_parquet(self._path)
|
|
log_artifact_not_found_warning(self._name, self._step_name)
|
|
return None
|
|
|
|
|
|
class ModelArtifact(Artifact):
|
|
def __init__(self, name, recipe_root, step_name, tracking_uri):
|
|
self._name = name
|
|
self._path = get_step_output_path(recipe_root, step_name, "model/model.pkl")
|
|
self._recipe_root = recipe_root
|
|
self._step_name = step_name
|
|
self._tracking_uri = tracking_uri
|
|
|
|
def name(self):
|
|
return self._name
|
|
|
|
def path(self):
|
|
return self._path
|
|
|
|
def load(self):
|
|
run_id = read_run_id(self._recipe_root)
|
|
if run_id:
|
|
with _use_tracking_uri(self._tracking_uri), chdir(self._recipe_root):
|
|
return mlflow.pyfunc.load_model(f"runs:/{run_id}/{self._step_name}/model")
|
|
log_artifact_not_found_warning(self._name, self._step_name)
|
|
return None
|
|
|
|
|
|
class TransformerArtifact(Artifact):
|
|
def __init__(self, name, recipe_root, step_name, tracking_uri):
|
|
self._name = name
|
|
self._path = get_step_output_path(recipe_root, step_name, "transformer.pkl")
|
|
self._recipe_root = recipe_root
|
|
self._step_name = step_name
|
|
self._tracking_uri = tracking_uri
|
|
|
|
def name(self):
|
|
return self._name
|
|
|
|
def path(self):
|
|
return self._path
|
|
|
|
def load(self):
|
|
run_id = read_run_id(self._recipe_root)
|
|
if run_id:
|
|
with _use_tracking_uri(self._tracking_uri), chdir(self._recipe_root):
|
|
return mlflow.sklearn.load_model(f"runs:/{run_id}/{self._step_name}/transformer")
|
|
log_artifact_not_found_warning(self._name, self._step_name)
|
|
return None
|
|
|
|
|
|
class RunArtifact(Artifact):
|
|
def __init__(self, name, recipe_root, step_name, tracking_uri):
|
|
self._name = name
|
|
self._path = get_step_output_path(recipe_root, step_name, "run_id")
|
|
self._recipe_root = recipe_root
|
|
self._step_name = step_name
|
|
self._tracking_uri = tracking_uri
|
|
|
|
def name(self):
|
|
return self._name
|
|
|
|
def path(self):
|
|
return self._path
|
|
|
|
def load(self):
|
|
run_id = read_run_id(self._recipe_root)
|
|
if run_id:
|
|
with _use_tracking_uri(self._tracking_uri), chdir(self._recipe_root):
|
|
return MlflowClient().get_run(run_id)
|
|
log_artifact_not_found_warning(self._name, self._step_name)
|
|
return None
|
|
|
|
|
|
class ModelVersionArtifact(Artifact):
|
|
def __init__(self, name, recipe_root, step_name, tracking_uri):
|
|
self._name = name
|
|
self._path = get_step_output_path(recipe_root, step_name, "registered_model_version.json")
|
|
self._recipe_root = recipe_root
|
|
self._step_name = step_name
|
|
self._tracking_uri = tracking_uri
|
|
|
|
def name(self):
|
|
return self._name
|
|
|
|
def path(self):
|
|
return self._path
|
|
|
|
def load(self):
|
|
if os.path.exists(self._path):
|
|
registered_model_info = RegisteredModelVersionInfo.from_json(path=self._path)
|
|
with _use_tracking_uri(self._tracking_uri), chdir(self._recipe_root):
|
|
return MlflowClient().get_model_version(
|
|
name=registered_model_info.name, version=registered_model_info.version
|
|
)
|
|
log_artifact_not_found_warning(self._name, self._step_name)
|
|
return None
|
|
|
|
|
|
class HyperParametersArtifact(Artifact):
|
|
def __init__(self, name, recipe_root, step_name):
|
|
self._name = name
|
|
self._path = get_step_output_path(recipe_root, step_name, "best_parameters.yaml")
|
|
|
|
def name(self):
|
|
return self._name
|
|
|
|
def path(self):
|
|
return self._path
|
|
|
|
def load(self):
|
|
if os.path.exists(self._path):
|
|
with open(self._path) as f:
|
|
return f.read()
|
|
|
|
|
|
def log_artifact_not_found_warning(artifact_name, step_name):
|
|
_logger.warning(
|
|
f"The artifact with name '{artifact_name}' was not found."
|
|
f" Re-run the '{step_name}' step to generate it."
|
|
)
|
|
|
|
|
|
def read_run_id(recipe_root):
|
|
run_id_file_path = get_step_output_path(recipe_root, "train", "run_id")
|
|
if os.path.exists(run_id_file_path):
|
|
with open(run_id_file_path) as f:
|
|
return f.read().strip()
|
|
return None
|
|
|
|
|
|
class RegisteredModelVersionInfo:
|
|
_KEY_REGISTERED_MODEL_NAME = "registered_model_name"
|
|
_KEY_REGISTERED_MODEL_VERSION = "registered_model_version"
|
|
|
|
def __init__(self, name: str, version: int):
|
|
self.name = name
|
|
self.version = version
|
|
|
|
def to_json(self, path):
|
|
registered_model_info_dict = {
|
|
RegisteredModelVersionInfo._KEY_REGISTERED_MODEL_NAME: self.name,
|
|
RegisteredModelVersionInfo._KEY_REGISTERED_MODEL_VERSION: self.version,
|
|
}
|
|
with open(path, "w") as f:
|
|
json.dump(registered_model_info_dict, f)
|
|
|
|
@classmethod
|
|
def from_json(cls, path):
|
|
with open(path) as f:
|
|
registered_model_info_dict = json.load(f)
|
|
|
|
return cls(
|
|
name=registered_model_info_dict[RegisteredModelVersionInfo._KEY_REGISTERED_MODEL_NAME],
|
|
version=registered_model_info_dict[
|
|
RegisteredModelVersionInfo._KEY_REGISTERED_MODEL_VERSION
|
|
],
|
|
)
|