Skip to content

Commit

Permalink
Merge branch 'main' into add-appinsights-logging
Browse files Browse the repository at this point in the history
  • Loading branch information
diondrapeck authored Nov 28, 2023
2 parents d249969 + 106ff57 commit 03781cf
Show file tree
Hide file tree
Showing 10 changed files with 2,881 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$schema: https://azuremlschemas.azureedge.net/latest/batchDeployment.schema.json
description: "Batch endpoint for for text-to-image task"
description: "Batch endpoint for for (image-)text-to-image task"
type: model
resources:
instance_count: 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
set -x
# the commands in this file map to steps in this notebook: https://aka.ms/azureml-infer-batch-sdk-text-classification

# script inputs
registry_name="azureml"

subscription_id="<SUBSCRIPTION_ID>"
resource_group_name="<RESOURCE_GROUP>"
workspace_name="<WORKSPACE_NAME>"

# This is the model from system registry that needs to be deployed
model_name="stabilityai-stable-diffusion-xl-refiner-1-0"
model_label="latest"

version=$(date +%s)
endpoint_name="image-text-to-image-$version"
deployment_name="image-text-to-image-batch-deploy"

deployment_compute="gpu-cluster"
compute_sku="Standard_NC6s_v3"

# 1. Setup pre-requisites
if [ "$subscription_id" = "<SUBSCRIPTION_ID>" ] || \
["$resource_group_name" = "<RESOURCE_GROUP>" ] || \
[ "$workspace_name" = "<WORKSPACE_NAME>" ]; then
echo "Please update the script with the subscription_id, resource_group_name and workspace_name"
exit 1
fi

az account set -s $subscription_id
workspace_info="--resource-group $resource_group_name --workspace-name $workspace_name"

# 2. Check if the model exists in the registry
# Need to confirm model show command works for registries outside the tenant (aka system registry)
if ! az ml model show --name $model_name --label $model_label --registry-name $registry_name
then
echo "Model $model_name:$model_label does not exist in registry $registry_name"
exit 1
fi

model_version=$(az ml model show --name $model_name --label $model_label --registry-name $registry_name --query version --output tsv)

# 3. Check if compute $deployment_compute exists, else create it
if az ml compute show --name $deployment_compute $workspace_info
then
echo "Compute cluster $deployment_compute already exists"
else
echo "Creating compute cluster $deployment_compute"
az ml compute create --name $deployment_compute --type amlcompute --min-instances 0 --max-instances 2 --size $compute_sku $workspace_info || {
echo "Failed to create compute cluster $deployment_compute"
exit 1
}
fi

# 4. Deploy the model to an endpoint
# create batch endpoint
az ml batch-endpoint create --name $endpoint_name $workspace_info || {
echo "endpoint create failed"; exit 1;
}

# deploy model from registry to endpoint in workspace
az ml batch-deployment create --file batch-deploy.yml $workspace_info --set \
endpoint_name=$endpoint_name \
name=$deployment_name \
compute=$deployment_compute \
model=azureml://registries/$registry_name/models/$model_name/versions/$model_version || {
echo "deployment create failed"; exit 1;
}

# 4. Submit a sample request to endpoint
data_path="./inpainting_data/batch_data"
python utils/prepare_data_image_text_to_image.py --payload-path $data_path --mode "batch"
# Path where the processes csvs are dumped. This is the input to the endpoint
processed_data_path="./inpainting_data/batch_data/processed_batch_data"

# Check if scoring folder exists
if [ -d $processed_data_path ]; then
echo "Invoking endpoint $endpoint_name with following input:\n\n"
ls $processed_data_path
echo "\n\n"
else
echo "Scoring folder $processed_data_path does not exist"
exit 1
fi

# 5. Invoke a job on the batch endpoint
job_name=$(az ml batch-endpoint invoke --name $endpoint_name \
--deployment-name $deployment_name \
--input $processed_data_path \
--input-type uri_folder --query name --output tsv $workspace_info) || {
echo "endpoint invoke failed"; exit 1;
}

# 6. Stream the job logs
az ml job stream --name $job_name $workspace_info || {
echo "job stream-logs failed. If the job failed with Assertion Error stating actual size of csv exceeds 100 MB, then try splitting input csv file into multiple csv files."; exit 1;
}

# 7. Download the job output
az ml job download --name $job_name --download-path "generated_images" $workspace_info || {
echo "job output download failed"; exit 1;
}

# 8. Delete the endpoint
az ml batch-endpoint delete --name $endpoint_name $workspace_info --yes || {
echo "endpoint delete failed"; exit 1;
}

# 9. Delete the compute cluster (Uncomment the below lines to delete the created cluster)
# az ml compute delete --name $deployment_compute $workspace_info --yes || {
# echo "compute delete failed"; exit 1;
# }

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
set -x
# the commands in this file map to steps in this notebook: https://aka.ms/azureml-infer-batch-sdk-text-classification

# script inputs
registry_name="azureml"

subscription_id="<SUBSCRIPTION_ID>"
resource_group_name="<RESOURCE_GROUP>"
workspace_name="<WORKSPACE_NAME>"

# This is the model from system registry that needs to be deployed
model_name="stabilityai-stable-diffusion-xl-refiner-1-0"
model_label="latest"
response_file="generated_image.json"

version=$(date +%s)
endpoint_name="image-text-to-image-$version"
deployment_name="image-text-to-image-deploy"

deployment_sku="Standard_NC6s_v3"

# sample_request_data
sample_request_data="inpainting_data/sample_request_data.json"

# 1. Setup pre-requisites
if [ "$subscription_id" = "<SUBSCRIPTION_ID>" ] || \
["$resource_group_name" = "<RESOURCE_GROUP>" ] || \
[ "$workspace_name" = "<WORKSPACE_NAME>" ]; then
echo "Please update the script with the subscription_id, resource_group_name and workspace_name"
exit 1
fi

az account set -s $subscription_id
workspace_info="--resource-group $resource_group_name --workspace-name $workspace_name"

# 2. Check if the model exists in the registry

if ! az ml model show --name $model_name --label $model_label --registry-name $registry_name
then
echo "Model $model_name:$model_label does not exist in registry $registry_name"
exit 1
fi

# Get the latest model version
model_version=$(az ml model show --name $model_name --label $model_label --registry-name $registry_name --query version --output tsv)

# 3. Deploy the model to an endpoint
# Create online endpoint
az ml online-endpoint create --name $endpoint_name $workspace_info || {
echo "endpoint create failed"; exit 1;
}

# Deploy model from registry to endpoint in workspace
az ml online-deployment create --file deploy-online.yaml $workspace_info --all-traffic --set \
endpoint_name=$endpoint_name model=azureml://registries/$registry_name/models/$model_name/versions/$model_version \
name=$deployment_name \
instance_type=$deployment_sku || {
echo "deployment create failed"; exit 1;
}

# 4. Submit a sample request to endpoint
python utils/prepare_data_image_text_to_image.py --payload-path $sample_request_data --mode "online"

# Check if scoring data file exists
if [ -f $sample_request_data ]; then
echo "Invoking endpoint $endpoint_name with $sample_request_data:\n\n"
echo "\n\n"
else
echo "Request file $sample_request_data does not exist"
exit 1
fi

az ml online-endpoint invoke --name $endpoint_name --request-file $sample_request_data $workspace_info -o json > $response_file || {
echo "endpoint invoke failed"; exit 1;
}

# 5. Convert bas64 form of image to jpeg
python ./utils/base64_to_jpeg.py --response_file $response_file

# 6. Delete the endpoint and sample_request_data.json
az ml online-endpoint delete --name $endpoint_name $workspace_info --yes || {
echo "endpoint delete failed"; exit 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# Prepare request payload for the image-text to image task

import argparse
import glob
import json
import io
import base64
import os
import pandas as pd
from PIL import Image
from pathlib import Path


def read_image(image_path: str) -> bytes:
"""Reads an image from a file path into a byte array.
:param image_path: Path to image file.
:type image_path: str
:return: Byte array of image.
:rtype: bytes
"""
with open(image_path, "rb") as f:
return f.read()


def prepare_batch_payload(payload_path: str) -> None:
"""Prepare payload for online deployment.
:param payload_path: Path to payload csv file.
:type payload_path: str
:return: None
"""

base_image1 = "inpainting_data/images/dog_on_bench.png"
base_image2 = "inpainting_data/images/teapot.png"

os.makedirs(payload_path, exist_ok=True)

input_data = {
"columns": ["image", "prompt"],
"data": [
{
"image": base64.encodebytes(read_image(base_image1)).decode("utf-8"),
"prompt": "A yellow cat, high resolution, sitting on a park bench",
},
{
"image": base64.encodebytes(read_image(base_image2)).decode("utf-8"),
"prompt": "A small flower featuring a blend of pink and purple colors.",
},
],
}
pd.DataFrame(**input_data).to_csv(
os.path.join(payload_path, "input1.csv"), index=False
)

input_data = {
"columns": ["image", "prompt"],
"data": [
{
"image": base64.encodebytes(read_image(base_image1)).decode("utf-8"),
"prompt": "Pikachu, cinematic, digital art, sitting on bench",
},
{
"image": base64.encodebytes(read_image(base_image2)).decode("utf-8"),
"prompt": "A woman with red hair in the style of Tamara de Lempicka.",
},
],
}
pd.DataFrame(**input_data).to_csv(
os.path.join(payload_path, "input2.csv"), index=False
)

# Use glob to get a list of CSV files in the folder
csv_files = glob.glob(os.path.join(payload_path, "*.csv"))

# Read all CSV files into a single DataFrame using pd.concat
batch_df = pd.concat((pd.read_csv(file) for file in csv_files), ignore_index=True)

# Specify the folder where your CSV files should be saved
processed_dataset_parent_dir = os.path.join(payload_path, "processed_batch_data")
os.makedirs(processed_dataset_parent_dir, exist_ok=True)
batch_input_file = "batch_input.csv"

# Divide this into files of <x> rows each
batch_size_per_predict = 2
for i in range(0, len(batch_df), batch_size_per_predict):
j = i + batch_size_per_predict
batch_df[i:j].to_csv(
os.path.join(processed_dataset_parent_dir, str(i) + batch_input_file)
)

# Check out the first and last file name created
input_paths = sorted(
Path(processed_dataset_parent_dir).iterdir(), key=os.path.getmtime
)
input_files = [os.path.basename(path) for path in input_paths]
print(f"{input_files[0]} to {str(i)}{batch_input_file}.")


def prepare_online_payload(payload_path: str) -> None:
"""Prepare payload for online deployment.
:param payload_path: Path to payload json file.
:type payload_path: str
:return: None
"""
base_directory = os.path.dirname(os.path.dirname(__file__))

base_image = os.path.join(
base_directory, "inpainting_data", "images", "dog_on_bench.png"
)

request_json = {
"input_data": {
"columns": ["image", "prompt"],
"index": [0],
"data": [
{
"image": base64.encodebytes(read_image(base_image)).decode("utf-8"),
"prompt": "A yellow cat, high resolution, sitting on a park bench",
}
],
}
}
print(base_directory)
print(payload_path)
payload_path = os.path.join(base_directory, payload_path)
with open(payload_path, "w") as request_file:
json.dump(request_json, request_file)


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Prepare sample data for image-text to image"
)
parser.add_argument("--payload-path", type=str, help="payload file/ folder path")
parser.add_argument(
"--mode",
type=str,
default="online",
help="Generate payload for online or batch deployment.",
)
args, unknown = parser.parse_known_args()

if args.mode == "online":
prepare_online_payload(args.payload_path)
else:
prepare_batch_payload(args.payload_path)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dependencies:
- mlflow==2.3.2
- torch==1.13.0
- transformers==4.29.1
- diffusers==0.20.0
- diffusers==0.23.0
- accelerate==0.22.0
- azureml-core==1.52.0
- azureml-mlflow==1.52.0
Expand Down
Loading

0 comments on commit 03781cf

Please sign in to comment.