def _get_run_dir(self, experiment_id, run_uuid): _validate_run_id(run_uuid) if not self._has_experiment(experiment_id): return None return os.path.join( self._get_experiment_path(experiment_id, assert_exists=True), run_uuid)
def update_run_info(self, run_id, run_status, end_time): _validate_run_id(run_id) run_info = self._get_run_info(run_id) check_run_is_active(run_info) new_info = run_info._copy_with_overrides(run_status, end_time) self._overwrite_run_info(new_info) return new_info
def get_run(self, run_id): """ Note: Will get both active and deleted runs. """ _validate_run_id(run_id) run_info = self._get_run_info(run_id) if run_info is None: raise MlflowException( "Run '%s' metadata is in invalid state." % run_id, databricks_pb2.INVALID_STATE) return self._get_run_from_info(run_info)
def test_validate_run_id(): for good_id in [ "a" * 32, "f0" * 16, "abcdef0123456789" * 2, "a" * 33, "a" * 31, "a" * 256, "A" * 32, "g" * 32, "a_" * 32, "abcdefghijklmnopqrstuvqxyz" ]: _validate_run_id(good_id) for bad_id in ["a/bc" * 8, "", "a" * 400, "*" * 5]: with pytest.raises(MlflowException, match="Invalid run ID") as e: _validate_run_id(bad_id) assert e.value.error_code == ErrorCode.Name(INVALID_PARAMETER_VALUE)
def _find_run_root(self, run_uuid): _validate_run_id(run_uuid) self._check_root_dir() all_experiments = self._get_active_experiments( True) + self._get_deleted_experiments(True) for experiment_dir in all_experiments: runs = find(experiment_dir, run_uuid, full_path=True) if len(runs) == 0: continue return os.path.basename(os.path.abspath(experiment_dir)), runs[0] return None, None
def delete_tag(self, run_id, key): """ Delete a tag from a run. This is irreversible. :param run_id: String ID of the run :param key: Name of the tag """ _validate_run_id(run_id) run_info = self._get_run_info(run_id) check_run_is_active(run_info) tag_path = self._get_tag_path(run_info.experiment_id, run_id, key) if not exists(tag_path): raise MlflowException( "No tag with name: {} in run with id {}".format(key, run_id), error_code=RESOURCE_DOES_NOT_EXIST) os.remove(tag_path)
def log_batch(self, run_id, metrics, params, tags): _validate_run_id(run_id) _validate_batch_log_data(metrics, params, tags) _validate_batch_log_limits(metrics, params, tags) run_info = self._get_run_info(run_id) check_run_is_active(run_info) try: for param in params: self._log_run_param(run_info, param) for metric in metrics: self._log_run_metric(run_info, metric) for tag in tags: self._set_run_tag(run_info, tag) except Exception as e: raise MlflowException(e, INTERNAL_ERROR)
def get_run(self, run_id): """ Fetch the run from backend store. The resulting :py:class:`Run <mlflow.entities.Run>` contains a collection of run metadata -- :py:class:`RunInfo <mlflow.entities.RunInfo>`, as well as a collection of run parameters, tags, and metrics -- :py:class:`RunData <mlflow.entities.RunData>`. In the case where multiple metrics with the same key are logged for the run, the :py:class:`RunData <mlflow.entities.RunData>` contains the most recently logged value at the largest step for each metric. :param run_id: Unique identifier for the run. :return: A single :py:class:`mlflow.entities.Run` object, if the run exists. Otherwise, raises an exception. """ _validate_run_id(run_id) return self.store.get_run(run_id)
def log_batch(self, run_id, metrics, params, tags): _validate_run_id(run_id) _validate_batch_log_data(metrics, params, tags) _validate_batch_log_limits(metrics, params, tags) with self.ManagedSessionMaker() as session: run = self._get_run(run_uuid=run_id, session=session) self._check_run_is_active(run) try: for param in params: self.log_param(run_id, param) for metric in metrics: self.log_metric(run_id, metric) for tag in tags: self.set_tag(run_id, tag) except MlflowException as e: raise e except Exception as e: raise MlflowException(e, INTERNAL_ERROR)
def record_logged_model(self, run_id, mlflow_model): if not isinstance(mlflow_model, Model): raise TypeError( "Argument 'mlflow_model' should be mlflow.models.Model, got '{}'" .format(type(mlflow_model))) _validate_run_id(run_id) run_info = self._get_run_info(run_id) check_run_is_active(run_info) model_dict = mlflow_model.to_dict() run_info = self._get_run_info(run_id) path = self._get_tag_path(run_info.experiment_id, run_info.run_id, MLFLOW_LOGGED_MODELS) if os.path.exists(path): with open(path, "r") as f: model_list = json.loads(f.read()) else: model_list = [] tag = RunTag(MLFLOW_LOGGED_MODELS, json.dumps(model_list + [model_dict])) try: self._set_run_tag(run_info, tag) except Exception as e: raise MlflowException(e, INTERNAL_ERROR)
def start_run(run_id=None, experiment_id=None, run_name=None, nested=False): """ Start a new MLflow run, setting it as the active run under which metrics and parameters will be logged. The return value can be used as a context manager within a ``with`` block; otherwise, you must call ``end_run()`` to terminate the current run. If you pass a ``run_id`` or the ``MLFLOW_RUN_ID`` environment variable is set, ``start_run`` attempts to resume a run with the specified run ID and other parameters are ignored. ``run_id`` takes precedence over ``MLFLOW_RUN_ID``. MLflow sets a variety of default tags on the run, as defined in :ref:`MLflow system tags <system_tags>`. :param run_id: If specified, get the run with the specified UUID and log parameters and metrics under that run. The run's end time is unset and its status is set to running, but the run's other attributes (``source_version``, ``source_type``, etc.) are not changed. :param experiment_id: ID of the experiment under which to create the current run (applicable only when ``run_id`` is not specified). If ``experiment_id`` argument is unspecified, will look for valid experiment in the following order: activated using ``set_experiment``, ``MLFLOW_EXPERIMENT_NAME`` environment variable, ``MLFLOW_EXPERIMENT_ID`` environment variable, or the default experiment as defined by the tracking server. :param run_name: Name of new run (stored as a ``mlflow.runName`` tag). Used only when ``run_id`` is unspecified. :param nested: Controls whether run is nested in parent run. ``True`` creates a nest run. :return: :py:class:`mlflow.ActiveRun` object that acts as a context manager wrapping the run's state. """ global _active_run_stack # back compat for int experiment_id experiment_id = str(experiment_id) if isinstance(experiment_id, int) else experiment_id if len(_active_run_stack) > 0 and not nested: raise Exception(( "Run with UUID {} is already active. To start a new run, first end the " + "current run with mlflow.end_run(). To start a nested " + "run, call start_run with nested=True").format( _active_run_stack[0].info.run_id)) if run_id: existing_run_id = run_id elif _RUN_ID_ENV_VAR in os.environ: existing_run_id = os.environ[_RUN_ID_ENV_VAR] del os.environ[_RUN_ID_ENV_VAR] else: existing_run_id = None if existing_run_id: _validate_run_id(existing_run_id) active_run_obj = MlflowClient().get_run(existing_run_id) # Check to see if experiment_id from environment matches experiment_id from set_experiment() if (_active_experiment_id is not None and _active_experiment_id != active_run_obj.info.experiment_id): raise MlflowException( "Cannot start run with ID {} because active run ID " "does not match environment run ID. Make sure --experiment-name " "or --experiment-id matches experiment set with " "set_experiment(), or just use command-line " "arguments".format(existing_run_id)) # Check to see if current run isn't deleted if active_run_obj.info.lifecycle_stage == LifecycleStage.DELETED: raise MlflowException( "Cannot start run with ID {} because it is in the " "deleted state.".format(existing_run_id)) else: if len(_active_run_stack) > 0: parent_run_id = _active_run_stack[-1].info.run_id else: parent_run_id = None exp_id_for_run = experiment_id if experiment_id is not None else _get_experiment_id( ) user_specified_tags = {} if parent_run_id is not None: user_specified_tags[MLFLOW_PARENT_RUN_ID] = parent_run_id if run_name is not None: user_specified_tags[MLFLOW_RUN_NAME] = run_name tags = context_registry.resolve_tags(user_specified_tags) active_run_obj = MlflowClient().create_run( experiment_id=exp_id_for_run, tags=tags) _active_run_stack.append(ActiveRun(active_run_obj)) return _active_run_stack[-1]
def _get_metric_path(self, experiment_id, run_uuid, metric_key): _validate_run_id(run_uuid) _validate_metric_name(metric_key) return os.path.join(self._get_run_dir(experiment_id, run_uuid), FileStore.METRICS_FOLDER_NAME, metric_key)
def set_tag(self, run_id, tag): _validate_run_id(run_id) _validate_tag_name(tag.key) run_info = self._get_run_info(run_id) check_run_is_active(run_info) self._set_run_tag(run_info, tag)
def log_param(self, run_id, param): _validate_run_id(run_id) _validate_param_name(param.key) run_info = self._get_run_info(run_id) check_run_is_active(run_info) self._log_run_param(run_info, param)
def log_metric(self, run_id, metric): _validate_run_id(run_id) _validate_metric_name(metric.key) run_info = self._get_run_info(run_id) check_run_is_active(run_info) self._log_run_metric(run_info, metric)
def get_all_tags(self, run_uuid): _validate_run_id(run_uuid) run_info = self._get_run_info(run_uuid) return self._get_all_tags(run_info)
def get_metric_history(self, run_id, metric_key): _validate_run_id(run_id) _validate_metric_name(metric_key) run_info = self._get_run_info(run_id) return self._get_metric_history(run_info, metric_key)
def _get_param_path(self, experiment_id, run_uuid, param_name): _validate_run_id(run_uuid) _validate_param_name(param_name) return os.path.join(self._get_run_dir(experiment_id, run_uuid), FileStore.PARAMS_FOLDER_NAME, param_name)
def _get_tag_path(self, experiment_id, run_uuid, tag_name): _validate_run_id(run_uuid) _validate_tag_name(tag_name) return os.path.join(self._get_run_dir(experiment_id, run_uuid), FileStore.TAGS_FOLDER_NAME, tag_name)
def _get_artifact_dir(self, experiment_id, run_uuid): _validate_run_id(run_uuid) return append_to_uri_path( self.get_experiment(experiment_id).artifact_location, run_uuid, FileStore.ARTIFACTS_FOLDER_NAME)