[Support inference params-4] Support inference params for model serving#8976
Conversation
Signed-off-by: Serena Ruan <[email protected]>
|
Documentation preview for 7f123d6 will be available here when this CircleCI job completes successfully. More info
|
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
BenWilson2
left a comment
There was a problem hiding this comment.
LGTM! Thanks @serena-ruan :)
| if isinstance(datetime_value, int): | ||
| raise MlflowException.invalid_parameter_value( | ||
| f"Invalid value for param {name}, it should " | ||
| f"be convertible to datetime.date/datetime, got {value}" | ||
| ) |
There was a problem hiding this comment.
I think we don't need this because datetime_value = np.datetime64(value).item() already converts the value ?
There was a problem hiding this comment.
The strange thing is that np.datetime64('20230701').item() is actually an integer 😅
WeichenXu123
left a comment
There was a problem hiding this comment.
LGTM but one minor comment
Signed-off-by: Serena Ruan <[email protected]>
| "datetime_param" | ||
| ] == np.datetime64("2023-06-26 00:00:00") | ||
|
|
||
| # Test model serving |
There was a problem hiding this comment.
Not a blocker, but we should consider splitting this giant test. A long test is evil because:
- Harder to tell what's being tested.
- Harder to tell what's going on.
- More code = More likely to make mistakes.
- Harder to update.
- Takes longer to run.
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
| prediction = json.loads(response.content.decode("utf-8"))["predictions"] | ||
| for param, value in test_params.items(): | ||
| if param == "double_array": | ||
| assert (prediction[param] == value).all() |
There was a problem hiding this comment.
Not a blocker, but when cheking a numpy array or pandas dataframe, we should use np.testing.assert_array_equal or pandas.testting.assert_frame_equal. assert (prediction[param] == value).all() tells us True or False, but assert_xxx_yyy functions gives more information which is helpful for debugging.
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
dbczumar
left a comment
There was a problem hiding this comment.
@serena-ruan @harupy @BenWilson2
Looks like we don't have backwards compatibility with old models. if we release this and users try deploying old models created before MLflow 2.5.0, they won't be able to score them (huge potential incident)
Here's how I tested:
-
Check out
branch-2.4 -
Create a pyfunc model without params and log it:
import mlflow
from mlflow.models import infer_signature
import numpy as np
# s = infer_signature(["k"], params={"foo": np.datetime64("323432"), "bar": [True, False]})
s = infer_signature(["k"])
class MyMod(mlflow.pyfunc.PythonModel):
def predict(self, ctx, inp):
return [7 for item in inp]
with mlflow.start_run():
m = mlflow.pyfunc.log_model(
python_model=MyMod(),
artifact_path="foo",
signature=s,
pip_requirements=[],
registered_model_name="testmodel",
)
print(m.model_uri)
print(m.signature)
m = mlflow.pyfunc.load_model(m.model_uri)
print("TYPE", type(m))
print(hasattr(m, 'predict'))
-
Check out the PR branch
-
Serve the model
mlflow models serve -m runs:/c134dd5a60084efa903797b66733cffd/foo --env-manager local
- Query the model:
import requests
resp = requests.post(
url="http://127.0.0.1:8000/invocations",
json={
"dataframe_records": [{"test": "input"}]
},
)
print(resp)
print(resp.text)
The following error occurs:
{"error_code": "BAD_REQUEST", "message": "Encountered an unexpected error while evaluating the model. Verify that the serialized input Dataframe is compatible with the model for inference.", "stack_trace": "Traceback (most recent call last):\n File \"/Users/corey.zumar/mlflow/mlflow/pyfunc/scoring_server/__init__.py\", line 276, in transformation\n charset = parameter_values.get(\"charset\", \"utf-8\").lower()\n File \"/Users/corey.zumar/mlflow/mlflow/pyfunc/__init__.py\", line 428, in predict\n column values will be cast as the required tensor spec type.\n File \"/Users/corey.zumar/mlflow/mlflow/pyfunc/model.py\", line 365, in predict\nTypeError: predict() missing 1 required positional argument: 'params'\n"}
If we've already checked in code with a similar problem on Databricks, can we revert it for now? I want to make sure we aren't introducing serious regressions here
Signed-off-by: Serena Ruan <[email protected]>
This is so strange.. I added unit test for python model 38e1c15#diff-592813f9fb198dda0982794f2221a408f316709190b3bf02c0e77eb2c1107075, I also manually tested your approach locally, but mine works fine 🤔
And my MLmodel file looks like this: So I'm wondering how could I reproduce your error 🤔 |
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>
|
Can we do a manual validation of spark_udf (mlflow 2.3.x model, use as spark_udf after installing this branch)? I'm really interested on seeing how the udf logic will handle the optional |
|
@dbczumar @serena-ruan I tried what's described in #8976 (review) and it worked. |
Thanks Haru! |
dbczumar
left a comment
There was a problem hiding this comment.
@serena-ruan I've confirmed that we have backwards compatibility for model serving, batch inference, and spark UDF.
I found one small issue where the following code throws an error, but I'd expect it to work (let me know if I'm missing something):
s = infer_signature(["k"], params={"foo": np.datetime64("323432"), "bar": [True, False]})
mlflow.exceptions.MlflowException: Failed to infer schema for parameters: [('foo', numpy.datetime64('323432'), MlflowException("Invalid value for param 'foo': datetime (default: 323432) with shape None, it should be convertible to datetime.date/datetime, got 323432"))]
Otherwise, LGTM and feel free to merge! Thanks so much, @serena-ruan !
| # spark_master = os.environ.get("SPARK_MASTER", "local[1]") | ||
| spark_master = os.environ.get("SPARK_MASTER", "local[1]") | ||
| # If doing local testing, comment-out the following line. | ||
| spark_master = os.environ.get("SPARK_MASTER", "local-cluster[2, 1, 1024]") | ||
| # spark_master = os.environ.get("SPARK_MASTER", "local-cluster[2, 1, 1024]") |
There was a problem hiding this comment.
Ah I forgot to change it back for local testing! I'll change it back
This is expected, because when we convert numpy.datetime64('323432') to datetime.datetime, it actually becomes a number, which means it's not a valid timestamp, so we throw the error, and this is added to test case as well :) |
Signed-off-by: Serena Ruan <[email protected]>
Co-authored-by: Harutaka Kawamura <[email protected]> Signed-off-by: Serena Ruan <[email protected]>
Signed-off-by: Serena Ruan <[email protected]>

Related Issues/PRs
Depends on #8974
What changes are proposed in this pull request?
Support passing
paramsas part of payload for model servingHow is this patch tested?
Does this PR change the documentation?
Release Notes
Is this a user-facing change?
Support passing
paramsas part of payload for model servingWhat component(s), interfaces, languages, and integrations does this PR affect?
Components
area/artifacts: Artifact stores and artifact loggingarea/build: Build and test infrastructure for MLflowarea/docs: MLflow documentation pagesarea/examples: Example codearea/model-registry: Model Registry service, APIs, and the fluent client calls for Model Registryarea/models: MLmodel format, model serialization/deserialization, flavorsarea/recipes: Recipes, Recipe APIs, Recipe configs, Recipe Templatesarea/projects: MLproject format, project running backendsarea/scoring: MLflow Model server, model deployment tools, Spark UDFsarea/server-infra: MLflow Tracking server backendarea/tracking: Tracking Service, tracking client APIs, autologgingInterface
area/uiux: Front-end, user experience, plotting, JavaScript, JavaScript dev serverarea/docker: Docker use across MLflow's components, such as MLflow Projects and MLflow Modelsarea/sqlalchemy: Use of SQLAlchemy in the Tracking Service or Model Registryarea/windows: Windows supportLanguage
language/r: R APIs and clientslanguage/java: Java APIs and clientslanguage/new: Proposals for new client languagesIntegrations
integrations/azure: Azure and Azure ML integrationsintegrations/sagemaker: SageMaker integrationsintegrations/databricks: Databricks integrationsHow should the PR be classified in the release notes? Choose one:
rn/breaking-change- The PR will be mentioned in the "Breaking Changes" sectionrn/none- No description will be included. The PR will be mentioned only by the PR number in the "Small Bugfixes and Documentation Updates" sectionrn/feature- A new user-facing feature worth mentioning in the release notesrn/bug-fix- A user-facing bug fix worth mentioning in the release notesrn/documentation- A user-facing documentation change worth mentioning in the release notes