Voorspellingen doen met ONNX op Computer Vision-modellen van AutoML

VAN TOEPASSING OP: Python SDK azure-ai-ml v2 (actueel)

In dit artikel leert u hoe u Open Neural Network Exchange (ONNX) gebruikt om voorspellingen te doen op computer vision-modellen die zijn gegenereerd op basis van geautomatiseerde machine learning (AutoML) in Azure Machine Learning.

Als u ONNX wilt gebruiken voor voorspellingen, moet u het volgende doen:

  1. Download ONNX-modelbestanden uit een AutoML-trainingsuitvoering.
  2. Inzicht in de invoer en uitvoer van een ONNX-model.
  3. Verwerkt uw gegevens vooraf zodat deze de vereiste indeling hebben voor invoerafbeeldingen.
  4. Deductie uitvoeren met ONNX Runtime voor Python.
  5. Visualiseer voorspellingen voor objectdetectie en exemplaarsegmentatietaken.

ONNX is een open standaard voor machine learning- en Deep Learning-modellen. Hiermee kunt u model importeren en exporteren (interoperabiliteit) in de populaire AI-frameworks. Bekijk het ONNX GitHub-project voor meer informatie.

ONNX Runtime is een opensource-project dat ondersteuning biedt voor platformoverschrijdende deductie. ONNX Runtime biedt API's voor programmeertalen (waaronder Python, C++, C#, C, Java en JavaScript). U kunt deze API's gebruiken om deductie uit te voeren op invoerafbeeldingen. Nadat u het model hebt geëxporteerd naar de ONNX-indeling, kunt u deze API's gebruiken voor elke programmeertaal die uw project nodig heeft.

In deze handleiding leert u hoe u Python-API's voor ONNX Runtime gebruikt om voorspellingen te doen over afbeeldingen voor populaire vision-taken. U kunt deze geëxporteerde ONNX-modellen in verschillende talen gebruiken.

Vereisten

ONNX-modelbestanden downloaden

U kunt ONNX-modelbestanden downloaden van AutoML-uitvoeringen met behulp van de Azure Machine Learning-studio-gebruikersinterface of de Azure Machine Learning Python SDK. U kunt het beste downloaden via de SDK met de naam van het experiment en de bovenliggende uitvoerings-id.

Azure Machine Learning Studio

Ga in Azure Machine Learning-studio naar uw experiment met behulp van de hyperlink naar het experiment dat is gegenereerd in het trainingsnotitieblok of door de naam van het experiment te selecteren op het tabblad Experimenten onder Assets. Selecteer vervolgens de beste onderliggende uitvoering.

Ga binnen de beste onderliggende uitvoering naar Outputs+logs>train_artifacts. Gebruik de knop Downloaden om de volgende bestanden handmatig te downloaden:

  • labels.json: bestand dat alle klassen of labels in de trainingsgegevensset bevat.
  • model.onnx: Model in ONNX-indeling.

Screenshot that shows selections for downloading O N N X model files.

Sla de gedownloade modelbestanden op in een map. In het voorbeeld in dit artikel wordt de map ./automl_models gebruikt.

Python SDK voor Azure Machine Learning

Met de SDK kunt u de beste onderliggende uitvoering (op primaire metrische gegevens) selecteren met de naam van het experiment en de bovenliggende uitvoerings-id. Vervolgens kunt u de labels.json- en model.onnx-bestanden downloaden.

De volgende code retourneert de beste onderliggende uitvoering op basis van de relevante primaire metrische gegevens.

from azure.identity import DefaultAzureCredential
from azure.ai.ml import MLClient
mlflow_client = MlflowClient()

credential = DefaultAzureCredential()
ml_client = None
try:
    ml_client = MLClient.from_config(credential)
except Exception as ex:
    print(ex)
    # Enter details of your Azure Machine Learning workspace
    subscription_id = ''   
    resource_group = ''  
    workspace_name = ''
    ml_client = MLClient(credential, subscription_id, resource_group, workspace_name)
import mlflow
from mlflow.tracking.client import MlflowClient

# Obtain the tracking URL from MLClient
MLFLOW_TRACKING_URI = ml_client.workspaces.get(
    name=ml_client.workspace_name
).mlflow_tracking_uri

mlflow.set_tracking_uri(MLFLOW_TRACKING_URI)

# Specify the job name
job_name = ''

# Get the parent run
mlflow_parent_run = mlflow_client.get_run(job_name)
best_child_run_id = mlflow_parent_run.data.tags['automl_best_child_run_id']
# get the best child run
best_run = mlflow_client.get_run(best_child_run_id)

Download het labels.json-bestand , dat alle klassen en labels in de trainingsgegevensset bevat.

local_dir = './automl_models'
if not os.path.exists(local_dir):
    os.mkdir(local_dir)

labels_file = mlflow_client.download_artifacts(
    best_run.info.run_id, 'train_artifacts/labels.json', local_dir
)

Download het bestand model.onnx .

onnx_model_path = mlflow_client.download_artifacts(
    best_run.info.run_id, 'train_artifacts/model.onnx', local_dir
)

In het geval van batchdeductie voor objectdetectie en exemplaarsegmentatie met behulp van ONNX-modellen raadpleegt u de sectie over het genereren van modellen voor batchgewijs scoren.

Modelgeneratie voor batchgewijs scoren

AutoML voor afbeeldingen ondersteunt standaard batchgewijs scoren voor classificatie. Maar objectdetectie en exemplaarsegmentatie ONNX-modellen bieden geen ondersteuning voor batchdeductie. In het geval van batchdeductie voor objectdetectie en exemplaarsegmentatie gebruikt u de volgende procedure om een ONNX-model te genereren voor de vereiste batchgrootte. Modellen die zijn gegenereerd voor een specifieke batchgrootte werken niet voor andere batchgrootten.

Download het conda-omgevingsbestand en maak een omgevingsobject dat moet worden gebruikt met de opdrachttaak.

#  Download conda file and define the environment

conda_file = mlflow_client.download_artifacts(
    best_run.info.run_id, "outputs/conda_env_v_1_0_0.yml", local_dir
)
from azure.ai.ml.entities import Environment
env = Environment(
    name="automl-images-env-onnx",
    description="environment for automl images ONNX batch model generation",
    image="mcr.microsoft.com/azureml/openmpi4.1.0-cuda11.1-cudnn8-ubuntu18.04",
    conda_file=conda_file,
)

Gebruik de volgende modelspecifieke argumenten om het script in te dienen. Raadpleeg voor meer informatie over argumenten modelspecifieke hyperparameters en voor ondersteunde objectdetectiemodelnamen de sectie ondersteunde modelarchitectuur.

Als u de argumentwaarden wilt ophalen die nodig zijn om het batchscoremodel te maken, raadpleegt u de scorescripts die zijn gegenereerd onder de uitvoermap van de AutoML-trainingsuitvoeringen. Gebruik de hyperparameterwaarden die beschikbaar zijn in de variabele modelinstellingen in het scorebestand voor de beste onderliggende uitvoering.

Voor afbeeldingsclassificatie met meerdere klassen ondersteunt het gegenereerde ONNX-model voor de beste onderliggende uitvoering standaard batchscores. Daarom zijn er geen modelspecifieke argumenten nodig voor dit taaktype en kunt u doorgaan naar de sectie Labels en ONNX-modelbestanden laden.

Download en bewaar het ONNX_batch_model_generator_automl_for_images.py bestand in de huidige map om het script in te dienen. Gebruik de volgende opdrachttaak om het script ONNX_batch_model_generator_automl_for_images.py te verzenden dat beschikbaar is in de GitHub-opslagplaats met azureml-voorbeelden om een ONNX-model van een specifieke batchgrootte te genereren. In de volgende code wordt de getrainde modelomgeving gebruikt om dit script te verzenden om het ONNX-model te genereren en op te slaan in de uitvoermap.

Voor afbeeldingsclassificatie met meerdere klassen ondersteunt het gegenereerde ONNX-model voor de beste onderliggende uitvoering standaard batchscores. Daarom zijn er geen modelspecifieke argumenten nodig voor dit taaktype en kunt u doorgaan naar de sectie Labels en ONNX-modelbestanden laden.

Zodra het batchmodel is gegenereerd, downloadt u het vanuit Outputs+logs> handmatig via de gebruikersinterface of gebruikt u de volgende methode:

batch_size = 8  # use the batch size used to generate the model
returned_job_run = mlflow_client.get_run(returned_job.name)

# Download run's artifacts/outputs
onnx_model_path = mlflow_client.download_artifacts(
    returned_job_run.info.run_id, 'outputs/model_'+str(batch_size)+'.onnx', local_dir
)

Na de stap voor het downloaden van het model gebruikt u het PYTHON-pakket ONNX Runtime om deductie uit te voeren met behulp van het bestand model.onnx . Voor demonstratiedoeleinden gebruikt dit artikel de gegevenssets uit Het voorbereiden van afbeeldingsgegevenssets voor elke vision-taak.

We hebben de modellen getraind voor alle vision-taken met hun respectieve gegevenssets om deductie van ONNX-modellen te demonstreren.

De labels en ONNX-modelbestanden laden

Met het volgende codefragment wordt labels.json geladen, waarbij klassenamen worden geordend. Dat wil zeggen dat als het ONNX-model een label-id voorspelt als 2, het overeenkomt met de labelnaam die is opgegeven bij de derde index in het labels.json bestand.

import json
import onnxruntime

labels_file = "automl_models/labels.json"
with open(labels_file) as f:
    classes = json.load(f)
print(classes)
try:
    session = onnxruntime.InferenceSession(onnx_model_path)
    print("ONNX model loaded...")
except Exception as e: 
    print("Error loading ONNX file: ", str(e))

Verwachte invoer- en uitvoerdetails voor een ONNX-model ophalen

Wanneer u het model hebt, is het belangrijk om bepaalde modelspecifieke en taakspecifieke details te kennen. Deze details omvatten het aantal invoer- en uitvoerwaarden, de verwachte invoervorm of -indeling voor het vooraf verwerken van de afbeelding en de uitvoershape, zodat u de modelspecifieke of taakspecifieke uitvoer kent.

sess_input = session.get_inputs()
sess_output = session.get_outputs()
print(f"No. of inputs : {len(sess_input)}, No. of outputs : {len(sess_output)}")

for idx, input_ in enumerate(range(len(sess_input))):
    input_name = sess_input[input_].name
    input_shape = sess_input[input_].shape
    input_type = sess_input[input_].type
    print(f"{idx} Input name : { input_name }, Input shape : {input_shape}, \
    Input type  : {input_type}")  

for idx, output in enumerate(range(len(sess_output))):
    output_name = sess_output[output].name
    output_shape = sess_output[output].shape
    output_type = sess_output[output].type
    print(f" {idx} Output name : {output_name}, Output shape : {output_shape}, \
    Output type  : {output_type}") 

Verwachte invoer- en uitvoerindelingen voor het ONNX-model

Elk ONNX-model heeft een vooraf gedefinieerde set invoer- en uitvoerindelingen.

In dit voorbeeld wordt het model toegepast dat is getraind op de gegevensset koelkastobjecten met 134 afbeeldingen en 4 klassen/labels om deductie van ONNX-modellen uit te leggen. Zie het notitieblok voor afbeeldingsclassificatie met meerdere klassen voor meer informatie over het trainen van een afbeeldingsclassificatietaak.

Invoerindeling

De invoer is een vooraf verwerkte afbeelding.

Invoernaam Invoershape Input type Beschrijving
input1 (batch_size, num_channels, height, width) ndarray(float) Invoer is een vooraf verwerkte afbeelding, met de shape (1, 3, 224, 224) voor een batchgrootte van 1 en een hoogte en breedte van 224. Deze getallen komen overeen met de waarden die in het trainingsvoorbeeld worden gebruikt crop_size .

Uitvoerindeling

De uitvoer is een matrix met logboeken voor alle klassen/labels.

Uitvoernaam Uitvoershape Uitvoertype Beschrijving
output1 (batch_size, num_classes) ndarray(float) Model retourneert logboeken (zonder softmax). Voor batchgrootte 1 en 4 klassen retourneert (1, 4)deze bijvoorbeeld .

Preprocessing

Voer de volgende voorverwerkingsstappen uit voor de deductie van het ONNX-model:

  1. Converteer de afbeelding naar RGB.
  2. Wijzig het formaat van de afbeelding valid_resize_size en valid_resize_size waarden die overeenkomen met de waarden die tijdens de training worden gebruikt bij de transformatie van de validatiegegevensset. De standaardwaarde is valid_resize_size 256.
  3. Centreren van de afbeelding tot height_onnx_crop_size en width_onnx_crop_sizemet . Deze komt overeen valid_crop_size met de standaardwaarde 224.
  4. Wijzig HxWxC in CxHxW.
  5. Converteren naar floattype.
  6. Normaliseren met ImageNet's mean = [0.485, 0.456, 0.406] en std = [0.229, 0.224, 0.225].

Als u verschillende waarden hebt gekozen voor de hyperparametersvalid_resize_size en valid_crop_size tijdens de training, moeten deze waarden worden gebruikt.

Haal de invoershape op die nodig is voor het ONNX-model.

batch, channel, height_onnx_crop_size, width_onnx_crop_size = session.get_inputs()[0].shape
batch, channel, height_onnx_crop_size, width_onnx_crop_size

Zonder PyTorch

import glob
import numpy as np
from PIL import Image

def preprocess(image, resize_size, crop_size_onnx):
    """Perform pre-processing on raw input image
    
    :param image: raw input image
    :type image: PIL image
    :param resize_size: value to resize the image
    :type image: Int
    :param crop_size_onnx: expected height of an input image in onnx model
    :type crop_size_onnx: Int
    :return: pre-processed image in numpy format
    :rtype: ndarray 1xCxHxW
    """

    image = image.convert('RGB')
    # resize
    image = image.resize((resize_size, resize_size))
    #  center  crop
    left = (resize_size - crop_size_onnx)/2
    top = (resize_size - crop_size_onnx)/2
    right = (resize_size + crop_size_onnx)/2
    bottom = (resize_size + crop_size_onnx)/2
    image = image.crop((left, top, right, bottom))

    np_image = np.array(image)
    # HWC -> CHW
    np_image = np_image.transpose(2, 0, 1) # CxHxW
    # normalize the image
    mean_vec = np.array([0.485, 0.456, 0.406])
    std_vec = np.array([0.229, 0.224, 0.225])
    norm_img_data = np.zeros(np_image.shape).astype('float32')
    for i in range(np_image.shape[0]):
        norm_img_data[i,:,:] = (np_image[i,:,:]/255 - mean_vec[i])/std_vec[i]
             
    np_image = np.expand_dims(norm_img_data, axis=0) # 1xCxHxW
    return np_image

# following code loads only batch_size number of images for demonstrating ONNX inference
# make sure that the data directory has at least batch_size number of images

test_images_path = "automl_models_multi_cls/test_images_dir/*" # replace with path to images
# Select batch size needed
batch_size = 8
# you can modify resize_size based on your trained model
resize_size = 256
# height and width will be the same for classification
crop_size_onnx = height_onnx_crop_size 

image_files = glob.glob(test_images_path)
img_processed_list = []
for i in range(batch_size):
    img = Image.open(image_files[i])
    img_processed_list.append(preprocess(img, resize_size, crop_size_onnx))
    
if len(img_processed_list) > 1:
    img_data = np.concatenate(img_processed_list)
elif len(img_processed_list) == 1:
    img_data = img_processed_list[0]
else:
    img_data = None

assert batch_size == img_data.shape[0]

Met PyTorch

import glob
import torch
import numpy as np
from PIL import Image
from torchvision import transforms

def _make_3d_tensor(x) -> torch.Tensor:
    """This function is for images that have less channels.

    :param x: input tensor
    :type x: torch.Tensor
    :return: return a tensor with the correct number of channels
    :rtype: torch.Tensor
    """
    return x if x.shape[0] == 3 else x.expand((3, x.shape[1], x.shape[2]))

def preprocess(image, resize_size, crop_size_onnx):
    transform = transforms.Compose([
        transforms.Resize(resize_size),
        transforms.CenterCrop(crop_size_onnx),
        transforms.ToTensor(),
        transforms.Lambda(_make_3d_tensor),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
    
    img_data = transform(image)
    img_data = img_data.numpy()
    img_data = np.expand_dims(img_data, axis=0)
    return img_data

# following code loads only batch_size number of images for demonstrating ONNX inference
# make sure that the data directory has at least batch_size number of images

test_images_path = "automl_models_multi_cls/test_images_dir/*"  # replace with path to images
# Select batch size needed
batch_size = 8
# you can modify resize_size based on your trained model
resize_size = 256
# height and width will be the same for classification
crop_size_onnx = height_onnx_crop_size 

image_files = glob.glob(test_images_path)
img_processed_list = []
for i in range(batch_size):
    img = Image.open(image_files[i])
    img_processed_list.append(preprocess(img, resize_size, crop_size_onnx))
    
if len(img_processed_list) > 1:
    img_data = np.concatenate(img_processed_list)
elif len(img_processed_list) == 1:
    img_data = img_processed_list[0]
else:
    img_data = None

assert batch_size == img_data.shape[0]

Deductie met ONNX Runtime

Deductie met ONNX Runtime verschilt voor elke computer vision-taak.

def get_predictions_from_ONNX(onnx_session, img_data):
    """Perform predictions with ONNX runtime
    
    :param onnx_session: onnx model session
    :type onnx_session: class InferenceSession
    :param img_data: pre-processed numpy image
    :type img_data: ndarray with shape 1xCxHxW
    :return: scores with shapes
            (1, No. of classes in training dataset) 
    :rtype: numpy array
    """

    sess_input = onnx_session.get_inputs()
    sess_output = onnx_session.get_outputs()
    print(f"No. of inputs : {len(sess_input)}, No. of outputs : {len(sess_output)}")    
    # predict with ONNX Runtime
    output_names = [ output.name for output in sess_output]
    scores = onnx_session.run(output_names=output_names,\
                                               input_feed={sess_input[0].name: img_data})
    
    return scores[0]

scores = get_predictions_from_ONNX(session, img_data)

Postprocessing

Pas softmax() op voorspelde waarden toe om betrouwbaarheidsscores (waarschijnlijkheden) voor elke klasse op te halen. Vervolgens is de voorspelling de klasse met de hoogste waarschijnlijkheid.

Zonder PyTorch

def softmax(x):
    e_x = np.exp(x - np.max(x, axis=1, keepdims=True))
    return e_x / np.sum(e_x, axis=1, keepdims=True)

conf_scores = softmax(scores)
class_preds = np.argmax(conf_scores, axis=1)
print("predicted classes:", ([(class_idx, classes[class_idx]) for class_idx in class_preds]))

Met PyTorch

conf_scores = torch.nn.functional.softmax(torch.from_numpy(scores), dim=1)
class_preds = torch.argmax(conf_scores, dim=1)
print("predicted classes:", ([(class_idx.item(), classes[class_idx]) for class_idx in class_preds]))

Voorspellingen visualiseren

Visualiseer een invoerafbeelding met labels.

import matplotlib.image as mpimg
import matplotlib.pyplot as plt
%matplotlib inline

sample_image_index = 0 # change this for an image of interest from image_files list
IMAGE_SIZE = (18, 12)
plt.figure(figsize=IMAGE_SIZE)
img_np = mpimg.imread(image_files[sample_image_index])

img = Image.fromarray(img_np.astype('uint8'), 'RGB')
x, y = img.size

fig,ax = plt.subplots(1, figsize=(15, 15))
# Display the image
ax.imshow(img_np)

label = class_preds[sample_image_index]
if torch.is_tensor(label):
    label = label.item()
    
conf_score = conf_scores[sample_image_index]
if torch.is_tensor(conf_score):
    conf_score = np.max(conf_score.tolist())
else:
    conf_score = np.max(conf_score)

display_text = '{} ({})'.format(label, round(conf_score, 3))
print(display_text)

color = 'red'
plt.text(30, 30, display_text, color=color, fontsize=30)

plt.show()

Volgende stappen