-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added snippets for Connection, Model Registry #160
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Hopsworks Model Management | ||
|
||
<p align="center"> | ||
<a href="https://community.hopsworks.ai"><img | ||
src="https://img.shields.io/discourse/users?label=Hopsworks%20Community&server=https%3A%2F%2Fcommunity.hopsworks.ai" | ||
alt="Hopsworks Community" | ||
/></a> | ||
<a href="https://docs.hopsworks.ai"><img | ||
src="https://img.shields.io/badge/docs-HSML-orange" | ||
alt="Hopsworks Model Management Documentation" | ||
/></a> | ||
<a href="https://pypi.org/project/hsml/"><img | ||
src="https://img.shields.io/pypi/v/hsml?color=blue" | ||
alt="PyPiStatus" | ||
/></a> | ||
<a href="https://archiva.hops.works/#artifact/com.logicalclocks/hsml"><img | ||
src="https://img.shields.io/badge/java-HSML-green" | ||
alt="Scala/Java Artifacts" | ||
/></a> | ||
<a href="https://pepy.tech/project/hsml/month"><img | ||
src="https://pepy.tech/badge/hsml/month" | ||
alt="Downloads" | ||
/></a> | ||
<a href="https://github.com/psf/black"><img | ||
src="https://img.shields.io/badge/code%20style-black-000000.svg" | ||
alt="CodeStyle" | ||
/></a> | ||
<a><img | ||
src="https://img.shields.io/pypi/l/hsml?color=green" | ||
alt="License" | ||
/></a> | ||
</p> | ||
|
||
HSML is the library to interact with the Hopsworks Model Registry and Model Serving. The library makes it easy to export, manage and deploy models. | ||
|
||
The library automatically configures itself based on the environment it is run. | ||
However, to connect from an external Python environment additional connection information, such as host and port, is required. For more information about the setup from external environments, see the setup section. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you connect to Hopsworks from an external Python environment to your own managed Hopsworks cluster, you need to provide additional connection information - the Hopsworks hostname (or IP address) and port (if it is not port 443). |
||
|
||
## Getting Started On Hopsworks | ||
|
||
Instantiate a connection and get the project model registry and serving handles | ||
```python | ||
import hsml | ||
|
||
# Create a connection | ||
connection = hsml.connection() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. put the hostname and project in the example here: connection = hsml.connection(host="my.cluster.com", project="fraud") |
||
|
||
# Get the model registry handle for the project's model registry | ||
mr = connection.get_model_registry() | ||
|
||
# Get the model serving handle for the current model registry | ||
ms = connection.get_model_serving() | ||
``` | ||
|
||
Create a new model | ||
```python | ||
model = mr.tensorflow.create_model(name="mnist", | ||
version=1, | ||
metrics={"accuracy": 0.94}, | ||
description="mnist model description") | ||
model.save("/tmp/model_directory") # or /tmp/model_file | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a line of code before this where you export the tensorflow model to the local directory: Otherwise this code snippet doesn't actually upload a tensorflow model |
||
``` | ||
|
||
Download a model | ||
```python | ||
model = mr.get_model("mnist", version=1) | ||
|
||
model_path = model.download() | ||
``` | ||
|
||
Delete a model | ||
```python | ||
model.delete() | ||
``` | ||
|
||
Get best performing model | ||
```python | ||
best_model = mr.get_best_model('mnist', 'accuracy', 'max') | ||
|
||
``` | ||
|
||
Deploy a model | ||
```python | ||
deployment = model.deploy() | ||
``` | ||
|
||
Start a deployment | ||
```python | ||
deployment.start() | ||
``` | ||
|
||
javierdlrm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Make predictions with a deployed model | ||
```python | ||
data = { "instances": model.input_example } | ||
|
||
predictions = deployment.predict(data) | ||
``` | ||
|
||
You can find more examples on how to use the library in [examples.hopsworks.ai](https://examples.hopsworks.ai). | ||
|
||
## Documentation | ||
|
||
Documentation is available at [Hopsworks Model Management Documentation](https://docs.hopsworks.ai/). | ||
|
||
## Issues | ||
|
||
For general questions about the usage of Hopsworks Machine Learning please open a topic on [Hopsworks Community](https://community.hopsworks.ai/). | ||
|
||
Please report any issue using [Github issue tracking](https://github.com/logicalclocks/machine-learning-api/issues). | ||
|
||
|
||
## Contributing | ||
|
||
If you would like to contribute to this library, please see the [Contribution Guidelines](CONTRIBUTING.md). |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -119,6 +119,12 @@ def get_model_registry(self, project: str = None): | |||||
"""Get a reference to a model registry to perform operations on, defaulting to the project's default model registry. | ||||||
Shared model registries can be retrieved by passing the `project` argument. | ||||||
|
||||||
!!! example | ||||||
```python | ||||||
mr = conn.get_model_registry() # Get the project's default model registry | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
``` | ||||||
|
||||||
|
||||||
# Arguments | ||||||
project: The name of the project that owns the shared model registry, | ||||||
the model registry must be shared with the project the connection was established for, defaults to `None`. | ||||||
|
@@ -131,6 +137,11 @@ def get_model_registry(self, project: str = None): | |||||
def get_model_serving(self): | ||||||
"""Get a reference to model serving to perform operations on. Model serving operates on top of a model registry, defaulting to the project's default model registry. | ||||||
|
||||||
!!! example | ||||||
```python | ||||||
ms = conn.get_model_serving() | ||||||
``` | ||||||
|
||||||
# Returns | ||||||
`ModelServing`. A model serving handle object to perform operations on. | ||||||
""" | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -84,18 +84,41 @@ def __init__( | |||||||||||||||||||||
self._model_engine = model_engine.ModelEngine() | ||||||||||||||||||||||
|
||||||||||||||||||||||
def save(self, model_path, await_registration=480): | ||||||||||||||||||||||
"""Persist this model including model files and metadata to the model registry.""" | ||||||||||||||||||||||
"""Persist this model including model files and metadata to the model registry. | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! example | ||||||||||||||||||||||
```python | ||||||||||||||||||||||
model.save('model.pkl') | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
""" | ||||||||||||||||||||||
return self._model_engine.save( | ||||||||||||||||||||||
self, model_path, await_registration=await_registration | ||||||||||||||||||||||
) | ||||||||||||||||||||||
|
||||||||||||||||||||||
def download(self): | ||||||||||||||||||||||
"""Download the model files to a local folder.""" | ||||||||||||||||||||||
"""Download the model files to a local folder. | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! example | ||||||||||||||||||||||
```python | ||||||||||||||||||||||
model_dir = model.download() | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
""" | ||||||||||||||||||||||
return self._model_engine.download(self) | ||||||||||||||||||||||
|
||||||||||||||||||||||
def delete(self): | ||||||||||||||||||||||
"""Delete the model | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! example | ||||||||||||||||||||||
```python | ||||||||||||||||||||||
# get model object | ||||||||||||||||||||||
model = mr.get_model("citibike_mlp_model", version=1) | ||||||||||||||||||||||
|
||||||||||||||||||||||
# delete this model version | ||||||||||||||||||||||
model.delete() | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! danger "Potentially dangerous operation" | ||||||||||||||||||||||
This operation drops all metadata associated with **this version** of the | ||||||||||||||||||||||
model **and** deletes the model files. | ||||||||||||||||||||||
|
@@ -370,6 +393,11 @@ def set_tag(self, name: str, value: Union[str, dict]): | |||||||||||||||||||||
A tag consists of a <name,value> pair. Tag names are unique identifiers across the whole cluster. | ||||||||||||||||||||||
The value of a tag can be any valid json - primitives, arrays or json objects. | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! example | ||||||||||||||||||||||
```python | ||||||||||||||||||||||
model.set_tag(name="tag_name", value=42) | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Arguments | ||||||||||||||||||||||
name: Name of the tag to be added. | ||||||||||||||||||||||
value: Value of the tag to be added. | ||||||||||||||||||||||
|
@@ -382,6 +410,11 @@ def set_tag(self, name: str, value: Union[str, dict]): | |||||||||||||||||||||
def delete_tag(self, name: str): | ||||||||||||||||||||||
"""Delete a tag attached to a model. | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! example | ||||||||||||||||||||||
```python | ||||||||||||||||||||||
model.delete_tag("tag_name") | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Arguments | ||||||||||||||||||||||
name: Name of the tag to be removed. | ||||||||||||||||||||||
# Raises | ||||||||||||||||||||||
|
@@ -392,6 +425,11 @@ def delete_tag(self, name: str): | |||||||||||||||||||||
def get_tag(self, name: str): | ||||||||||||||||||||||
"""Get the tags of a model. | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! example | ||||||||||||||||||||||
```python | ||||||||||||||||||||||
tag_value = model.get_tag("tag_name") | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Arguments | ||||||||||||||||||||||
name: Name of the tag to get. | ||||||||||||||||||||||
# Returns | ||||||||||||||||||||||
|
@@ -404,6 +442,11 @@ def get_tag(self, name: str): | |||||||||||||||||||||
def get_tags(self): | ||||||||||||||||||||||
"""Retrieves all tags attached to a model. | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! example | ||||||||||||||||||||||
```python | ||||||||||||||||||||||
tags = model.get_tags() | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
# Returns | ||||||||||||||||||||||
`Dict[str, obj]` of tags. | ||||||||||||||||||||||
# Raises | ||||||||||||||||||||||
|
@@ -415,6 +458,14 @@ def __repr__(self): | |||||||||||||||||||||
return f"Model(name: {self._name!r}, version: {self._version!r})" | ||||||||||||||||||||||
|
||||||||||||||||||||||
def get_url(self): | ||||||||||||||||||||||
"""Returns URL of the specific model. | ||||||||||||||||||||||
|
||||||||||||||||||||||
!!! example | ||||||||||||||||||||||
```python | ||||||||||||||||||||||
model_url = model.get_url() | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
""" | ||||||||||||||||||||||
path = ( | ||||||||||||||||||||||
"/p/" | ||||||||||||||||||||||
+ str(client.get_instance()._project_id) | ||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -63,6 +63,20 @@ def get_model(self, name: str, version: int = None): | |||||
Getting a model from the Model Registry means getting its metadata handle | ||||||
so you can subsequently download the model directory. | ||||||
|
||||||
!!! example | ||||||
```python | ||||||
import hsml | ||||||
|
||||||
# connect with Hopsworks | ||||||
conn = hsml.connection() | ||||||
|
||||||
# get Hopsworks Model Registry | ||||||
mr = conn.get_model_registry(project="my_project") | ||||||
|
||||||
# get model object | ||||||
model = mr.get_model("citibike_mlp_model", version=1) | ||||||
``` | ||||||
|
||||||
# Arguments | ||||||
name: Name of the model to get. | ||||||
version: Version of the model to retrieve, defaults to `None` and will | ||||||
|
@@ -94,6 +108,11 @@ def get_models(self, name: str): | |||||
Getting all models from the Model Registry for a given name returns a list of model entities, one for each version registered under | ||||||
the specified model name. | ||||||
|
||||||
!!! example | ||||||
```python | ||||||
models = mr.get_models("churnmodel") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
``` | ||||||
|
||||||
# Arguments | ||||||
name: Name of the model to get. | ||||||
# Returns | ||||||
|
@@ -114,6 +133,17 @@ def get_best_model(self, name: str, metric: str, direction: str): | |||||
name corresponding to one of the keys in the training_metrics dict of the model and a direction. For example to | ||||||
get the model version with the highest accuracy, specify metric='accuracy' and direction='max'. | ||||||
|
||||||
!!! example | ||||||
```python | ||||||
EVALUATION_METRIC = "r2_score" | ||||||
SORT_METRICS_BY = "max" # your sorting criteria | ||||||
|
||||||
# get best model based on custom metrics | ||||||
best_model = mr.get_best_model("citibike_mlp_model", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
EVALUATION_METRIC, | ||||||
SORT_METRICS_BY) | ||||||
|
||||||
``` | ||||||
# Arguments | ||||||
name: Name of the model to get. | ||||||
metric: Name of the key in the training metrics field to compare. | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -23,6 +23,14 @@ | |||||||||||||||||||||||
class ModelSchema: | ||||||||||||||||||||||||
"""Create a schema for a model. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
!!! example | ||||||||||||||||||||||||
```python | ||||||||||||||||||||||||
from hsml.schema import Schema | ||||||||||||||||||||||||
from hsml.model_schema import ModelSchema | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
model_schema = ModelSchema(input_schema=Schema(X_train), output_schema=Schema(y_train)) | ||||||||||||||||||||||||
``` | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
# Arguments | ||||||||||||||||||||||||
input_schema: Schema to describe the inputs. | ||||||||||||||||||||||||
output_schema: Schema to describe the outputs. | ||||||||||||||||||||||||
|
@@ -50,6 +58,12 @@ def json(self): | |||||||||||||||||||||||
def to_dict(self): | ||||||||||||||||||||||||
""" | ||||||||||||||||||||||||
Get dict representation of the ModelSchema. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
!!! example | ||||||||||||||||||||||||
```python | ||||||||||||||||||||||||
model_schema.to_dict() | ||||||||||||||||||||||||
``` | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
""" | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is thought to be used internally only. No need for example here. |
||||||||||||||||||||||||
return json.loads(self.json()) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -35,6 +35,17 @@ def create_model( | |||||||||||||||||||||||||||||
): | ||||||||||||||||||||||||||||||
"""Create a generic Python model metadata object. | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
!!! example | ||||||||||||||||||||||||||||||
```python | ||||||||||||||||||||||||||||||
model = mr.python.create_model( | ||||||||||||||||||||||||||||||
name="knn_iris_model", | ||||||||||||||||||||||||||||||
metrics=metrics, | ||||||||||||||||||||||||||||||
model_schema=model_schema, | ||||||||||||||||||||||||||||||
input_example=X_train.sample(), | ||||||||||||||||||||||||||||||
description="Iris Flower Predictor" | ||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
!!! note "Lazy" | ||||||||||||||||||||||||||||||
This method is lazy and does not persist any metadata or uploads model artifacts in the | ||||||||||||||||||||||||||||||
model registry on its own. To save the model object and the model artifacts, call the `save()` method with a | ||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -25,6 +25,14 @@ | |||||||||||||||||||||||
class Schema: | ||||||||||||||||||||||||
"""Create a schema for a model input or output. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
!!! example | ||||||||||||||||||||||||
```python | ||||||||||||||||||||||||
from hsml.schema import Schema | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
input_schema = Schema(X_train) | ||||||||||||||||||||||||
output_schema = Schema(y_train) | ||||||||||||||||||||||||
``` | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
# Arguments | ||||||||||||||||||||||||
object: The object to construct the schema from. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
|
@@ -74,6 +82,12 @@ def json(self): | |||||||||||||||||||||||
def to_dict(self): | ||||||||||||||||||||||||
""" | ||||||||||||||||||||||||
Get dict representation of the Schema. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
!!! example | ||||||||||||||||||||||||
```python | ||||||||||||||||||||||||
input_schema.to_dict() | ||||||||||||||||||||||||
``` | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
""" | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to add example for internal method. |
||||||||||||||||||||||||
return json.loads(self.json()) | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -35,6 +35,17 @@ def create_model( | |||||||||||||||||||||||||||||
): | ||||||||||||||||||||||||||||||
"""Create an SkLearn model metadata object. | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
!!! example | ||||||||||||||||||||||||||||||
```python | ||||||||||||||||||||||||||||||
model = mr.sklearn.create_model( | ||||||||||||||||||||||||||||||
name="nyc_taxi_fares_model", | ||||||||||||||||||||||||||||||
metrics=metrics, | ||||||||||||||||||||||||||||||
description="LogisticRegression.", | ||||||||||||||||||||||||||||||
input_example=X_test.sample(), | ||||||||||||||||||||||||||||||
model_schema=model_schema | ||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
!!! note "Lazy" | ||||||||||||||||||||||||||||||
This method is lazy and does not persist any metadata or uploads model artifacts in the | ||||||||||||||||||||||||||||||
model registry on its own. To save the model object and the model artifacts, call the `save()` method with a | ||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence doesn't mean anything.
configures itself how?
Technically, it should be
"The library automatically configures itself based on the environment it is run in."
Do you mean?
The library automatically configures itself based on the environment it is run in, either Python or Spark.
If you mean - it configures itself based on whether it is run inside Hopsworks or outside Hopsworks - in this case, just remove the sentence. Users will expect it should work inside/outside Hopsworks. We shouldn't need to tell them.