aws / sagemaker-python-sdk

A library for training and deploying machine learning models on Amazon SageMaker
https://sagemaker.readthedocs.io/
Apache License 2.0
2.09k stars 1.13k forks source link

[Feature] application/x-npz (numpy archive) deserialization support #3696

Closed athewsey closed 1 year ago

athewsey commented 1 year ago

Describe the feature you'd like

npz format is a native serialization option in numpy that can save multiple arrays in a single object via np.savez_compressed(stream, arr1=..., arr2=..., ...))

Although .npz data can be loaded just like .npy (with np.load(obj)), SageMaker's deserializers.NumpyDeserializer currently explicitly checks returned content type from the endpoint, and therefore cannot be used with npz archives even if users set the accept argument in the constructor.

We should update the provided deserializer to enable using this format easily, allowing user's endpoints to return multiple array objects in a native numpy way if they want.

How would this feature be used? Please describe.

From an endpoint, users would return multiple objects compressed together similarly to the below (SKLearn script mode) example code:

def output_fn(prediction, content_type):
    scores, confidences, explains = prediction
    if content_type == "application/json":
        return {"scores": scores, "conf": confidences, "explains": explains}
    elif content_type == "application/x-npz":
        buffer = BytesIO()
        np.savez_compressed(buffer, scores=scores, conf=confidences, explains=explains)
        return buffer.getvalue()

On the client side, users would instantiate the predictor with deserializer configured for npz data:

predictor = sagemaker.Predictor(
    "endpoint_name",
    serializer=sagemaker.serializers.NumpySerializer(),
    deserializer=sagemaker.deserializers.NumpyDeserializer(accept="application/x-npz"),
)

...The endpoint would be able to return, and the client side un-pack, multiple arrays compressed together:

result = predictor.predict(mydata)
result["explains"]

As of today, the above would fail with ValueError "cannot read content type application/x-npz" from the NumpyDeserializer class.

Describe alternatives you've considered

Additional context

N/A

david-waterworth commented 1 year ago

This would be useful. As a workaround I've been been using the NumpySerializer to send a single X ndarry to my model. The model produces multiple outputs (probabilities, labels, embeddings) which I return as

np.savez_compressed(buffer, probabilities=probabilities, labels=labels, embeddings=embeddings), "application/x-npy"

On the client I use the existing sagemaker.deserializers.NumpyDeserializer which works fine, but it returns a numpy.lib.npyio.NpzFile which is dict-like. I then manually extract the contains, i.e.

response = predictor.predict(batch)
probabilities, embeddings, labels = response["probabilities"], response["embeddings"], response["labels"]

Given how well this works it would be nice if sagemaker.deserializers.NumpyDeserializer could handle application/x-npz in a similar format - i.e. after calling np.load it should convert the NpzFile to a tuple

athewsey commented 1 year ago

I believe this is fixed by the merge of the linked PR - closing the issue