Skip to content

[Support inference params-5]Add docs for inference params#8977

Merged
serena-ruan merged 8 commits into
mlflow:inf_paramsfrom
serena-ruan:doc
Jul 21, 2023
Merged

[Support inference params-5]Add docs for inference params#8977
serena-ruan merged 8 commits into
mlflow:inf_paramsfrom
serena-ruan:doc

Conversation

@serena-ruan

Copy link
Copy Markdown
Collaborator

Related Issues/PRs

Depends on #8976

What changes are proposed in this pull request?

Add docs for inference params and new ModelSignature

How is this patch tested?

  • Existing unit/integration tests
  • New unit/integration tests
  • Manual tests (describe details, including test results, below)

Does this PR change the documentation?

  • No. You can skip the rest of this section.
  • Yes. Make sure the changed pages / sections render correctly in the documentation preview.

Release Notes

Is this a user-facing change?

  • No. You can skip the rest of this section.
  • Yes. Give a description of this change to be included in the release notes for MLflow users.

Add docs for supporting params in model inference.

What component(s), interfaces, languages, and integrations does this PR affect?

Components

  • area/artifacts: Artifact stores and artifact logging
  • area/build: Build and test infrastructure for MLflow
  • area/docs: MLflow documentation pages
  • area/examples: Example code
  • area/model-registry: Model Registry service, APIs, and the fluent client calls for Model Registry
  • area/models: MLmodel format, model serialization/deserialization, flavors
  • area/recipes: Recipes, Recipe APIs, Recipe configs, Recipe Templates
  • area/projects: MLproject format, project running backends
  • area/scoring: MLflow Model server, model deployment tools, Spark UDFs
  • area/server-infra: MLflow Tracking server backend
  • area/tracking: Tracking Service, tracking client APIs, autologging

Interface

  • area/uiux: Front-end, user experience, plotting, JavaScript, JavaScript dev server
  • area/docker: Docker use across MLflow's components, such as MLflow Projects and MLflow Models
  • area/sqlalchemy: Use of SQLAlchemy in the Tracking Service or Model Registry
  • area/windows: Windows support

Language

  • language/r: R APIs and clients
  • language/java: Java APIs and clients
  • language/new: Proposals for new client languages

Integrations

  • integrations/azure: Azure and Azure ML integrations
  • integrations/sagemaker: SageMaker integrations
  • integrations/databricks: Databricks integrations

How should the PR be classified in the release notes? Choose one:

  • rn/breaking-change - The PR will be mentioned in the "Breaking Changes" section
  • rn/none - No description will be included. The PR will be mentioned only by the PR number in the "Small Bugfixes and Documentation Updates" section
  • rn/feature - A new user-facing feature worth mentioning in the release notes
  • rn/bug-fix - A user-facing bug fix worth mentioning in the release notes
  • rn/documentation - A user-facing documentation change worth mentioning in the release notes

@github-actions github-actions Bot added area/docs Documentation issues rn/documentation Mention under Documentation Changes in Changelogs. labels Jul 6, 2023
@mlflow-automation

mlflow-automation commented Jul 6, 2023

Copy link
Copy Markdown
Contributor

Documentation preview for eebc0da will be available here when this CircleCI job completes successfully.

More info

@serena-ruan
serena-ruan marked this pull request as draft July 7, 2023 00:16
Comment thread docs/source/models.rst
a shape of ``(-1,)``. If the parameter's type or shape is incompatible, an exception will be raised.
Additionally, the value of the parameter is validated against the specified type in the signature. We attempt
to convert the value to the specified type, and if this conversion fails, an MlflowException will be raised.

@BenWilson2 BenWilson2 Jul 11, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we provide a list with generic examples of what parameter types are supported?
Can there also be an explanation added of why this feature exists and how it can be used as an intro to a new section above this? This is a pretty important and long asked-for feature that I think another ^^^^^ section is warranted to explain that inference parameters are supported now :)

Signed-off-by: Serena Ruan <[email protected]>
@serena-ruan
serena-ruan changed the base branch from master to inf_params July 19, 2023 06:58
@serena-ruan
serena-ruan marked this pull request as ready for review July 19, 2023 06:59
@serena-ruan
serena-ruan requested review from BenWilson2 and harupy July 19, 2023 06:59
@harupy

harupy commented Jul 19, 2023

Copy link
Copy Markdown
Member

@serena-ruan Can you push a commit? I want to see the doc preview.

@serena-ruan

Copy link
Copy Markdown
Collaborator Author

@serena-ruan Can you push a commit? I want to see the doc preview.

Can I just rerun https://github.com/mlflow/mlflow/actions/runs/5596007324 this job?

Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
@harupy

harupy commented Jul 19, 2023

Copy link
Copy Markdown
Member

Can I just rerun https://github.com/mlflow/mlflow/actions/runs/5596007324 this job?

Yes, but I'm not sure if it's rerunable.

Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Signed-off-by: Serena Ruan <[email protected]>
Comment thread docs/source/models.rst
Comment thread docs/source/models.rst
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst
Comment on lines +2843 to +2870
from mlflow.models import infer_signature
from mlflow.transformers import generate_signature_output

# Infer the signature
signature = infer_signature(
data,
generate_signature_output(model, data),
)

# Define an inference_config
inference_config = {
"num_beams": 5,
"max_length": 30,
"do_sample": True,
"remove_invalid_values": True,
}

# Saving inference_config with the model
mlflow.transformers.save_model(
model,
path=model_path,
inference_config=inference_config,
signature=signature,
)

pyfunc_loaded = mlflow.pyfunc.load_model(model_path)
# inference_config will be applied
result = pyfunc_loaded.predict(data)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example contains undefined variables and modules. Can we add them?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a long complete example is better than a short incomplete example.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

Comment thread docs/source/models.rst
from mlflow.models import infer_signature
import transformers

architecture = "mrm8488/t5-base-finetuned-common_gen"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How large is this model?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

roughly 1GB

Signed-off-by: Serena Ruan <[email protected]>
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst
Comment thread docs/source/models.rst Outdated
Comment thread docs/source/models.rst Outdated

@BenWilson2 BenWilson2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some very minor nits to address, otherwise looks great! Thanks @serena-ruan :)

@harupy harupy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Comment thread mlflow/pyfunc/__init__.py
params_schema = model_metadata.get_params_schema()
return _enforce_params_schema(params, params_schema)
else:
if hasattr(model_metadata, "get_params_schema"):

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harupy I fixed this to make sure if user doesn't pass params the default values are still applied, and added two tests in test_pyfunc_schema_enforcement.py, could you pls review again? Thanks 😄

Comment thread mlflow/pyfunc/__init__.py Outdated
Comment on lines +375 to +376
else:
if params:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
else:
if params:
if params:

I think we can remove this else.

Comment on lines +1284 to +1318
def test_enforce_params_schema_errors_with_python_model():
class MyModel(mlflow.pyfunc.PythonModel):
def predict(self, ctx, model_input, params=None):
return list(params.values()) if isinstance(params, dict) else None

params = {"str_param": "string", "int_array": [1, 2, 3], "123": 123}
signature = infer_signature(["input"])
signature_with_params = infer_signature(["input"], params=params)

with mlflow.start_run():
model_info = mlflow.pyfunc.log_model(
python_model=MyModel(), artifact_path="model1", signature=signature
)
model_info_with_params = mlflow.pyfunc.log_model(
python_model=MyModel(), artifact_path="model2", signature=signature_with_params
)

loaded_model = mlflow.pyfunc.load_model(model_info.model_uri)

with pytest.raises(
MlflowException,
match=r"`params` can only be specified at inference time if the model signature",
):
loaded_model.predict(["input"], params=params)

loaded_model_with_params = mlflow.pyfunc.load_model(model_info_with_params.model_uri)
with pytest.raises(MlflowException, match=r"Parameters must be a dictionary. Got type 'list'"):
loaded_model_with_params.predict(["input"], params=[1, 2, 3])

with mock.patch("mlflow.models.utils._logger.warning") as mock_warning:
loaded_model_with_params.predict(["input"], params={123: 456})
mock_warning.assert_called_with(
"Keys in parameters should be of type `str`, but received non-string keys."
"Converting all keys to string..."
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/ai split this test into two

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harupy Sure, here are the two tests split:

Test 1

def test_enforce_params_schema_errors_with_model_without_params():
    class MyModel(mlflow.pyfunc.PythonModel):
        def predict(self, ctx, model_input, params=None):
            return list(params.values()) if isinstance(params, dict) else None

    params = {"str_param": "string", "int_array": [1, 2, 3], "123": 123}
    signature = infer_signature(["input"])

    with mlflow.start_run():
        model_info = mlflow.pyfunc.log_model(
            python_model=MyModel(), artifact_path="model1", signature=signature
        )

    loaded_model = mlflow.pyfunc.load_model(model_info.model_uri)

    with pytest.raises(
        MlflowException,
        match=r"`params` can only be specified at inference time if the model signature",
    ):
        loaded_model.predict(["input"], params=params)

Test 2

def test_enforce_params_schema_errors_with_model_with_params():
    class MyModel(mlflow.pyfunc.PythonModel):
        def predict(self, ctx, model_input, params=None):
            return list(params.values()) if isinstance(params, dict) else None

    params = {"str_param": "string", "int_array": [1, 2, 3], "123": 123}
    signature_with_params = infer_signature(["input"], params=params)

    with mlflow.start_run():
        model_info_with_params = mlflow.pyfunc.log_model(
            python_model=MyModel(), artifact_path="model2", signature=signature_with_params
        )

    loaded_model_with_params = mlflow.pyfunc.load_model(model_info_with_params.model_uri)
    with pytest.raises(MlflowException, match=r"Parameters must be a dictionary. Got type 'list'"):
        loaded_model_with_params.predict(["input"], params=[1, 2, 3])

    with mock.patch("mlflow.models.utils._logger.warning") as mock_warning:
        loaded_model_with_params.predict(["input"], params={123: 456})
    mock_warning.assert_called_with(
        "Keys in parameters should be of type `str`, but received non-string keys."
        "Converting all keys to string..."
    )
Click here to see the prompt and usage data

Prompt:

split this test into two

```
def test_enforce_params_schema_errors_with_python_model():
    class MyModel(mlflow.pyfunc.PythonModel):
        def predict(self, ctx, model_input, params=None):
            return list(params.values()) if isinstance(params, dict) else None

    params = {"str_param": "string", "int_array": [1, 2, 3], "123": 123}
    signature = infer_signature(["input"])
    signature_with_params = infer_signature(["input"], params=params)

    with mlflow.start_run():
        model_info = mlflow.pyfunc.log_model(
            python_model=MyModel(), artifact_path="model1", signature=signature
        )
        model_info_with_params = mlflow.pyfunc.log_model(
            python_model=MyModel(), artifact_path="model2", signature=signature_with_params
        )

    loaded_model = mlflow.pyfunc.load_model(model_info.model_uri)

    with pytest.raises(
        MlflowException,
        match=r"`params` can only be specified at inference time if the model signature",
    ):
        loaded_model.predict(["input"], params=params)

    loaded_model_with_params = mlflow.pyfunc.load_model(model_info_with_params.model_uri)
    with pytest.raises(MlflowException, match=r"Parameters must be a dictionary. Got type 'list'"):
        loaded_model_with_params.predict(["input"], params=[1, 2, 3])

    with mock.patch("mlflow.models.utils._logger.warning") as mock_warning:
        loaded_model_with_params.predict(["input"], params={123: 456})
    mock_warning.assert_called_with(
        "Keys in parameters should be of type `str`, but received non-string keys."
        "Converting all keys to string..."
    )
```

Usage:

{
  "prompt_tokens": 420,
  "completion_tokens": 474,
  "total_tokens": 894,
  "estimated_cost_in_usd": 0.04104
}

@harupy harupy Jul 20, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serena-ruan Can we split this test?

@harupy harupy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-LGTM!

Signed-off-by: Serena Ruan <[email protected]>
@serena-ruan
serena-ruan merged commit c05cb51 into mlflow:inf_params Jul 21, 2023
@serena-ruan
serena-ruan deleted the doc branch July 21, 2023 02:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation issues rn/documentation Mention under Documentation Changes in Changelogs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants