Vytváření předpovědí pomocí ONNX na modelech počítačového zpracování obrazu z AutoML

PLATÍ PRO: Python SDK azure-ai-ml v2 (aktuální)

V tomto článku se dozvíte, jak pomocí Open Neural Network Exchange (ONNX) vytvářet předpovědi na modelech počítačového zpracování obrazu vygenerovaných z automatizovaného strojového učení (AutoML) ve službě Azure Machine Učení.

Pokud chcete pro predikce použít ONNX, musíte:

  1. Stáhněte si soubory modelu ONNX z trénovacího spuštění AutoML.
  2. Seznamte se se vstupy a výstupy modelu ONNX.
  3. Předzpracuje data tak, aby byla v požadovaném formátu pro vstupní obrázky.
  4. Proveďte odvozování s modulem runtime ONNX pro Python.
  5. Vizualizujte předpovědi pro úlohy detekce objektů a segmentace instancí.

ONNX je otevřený standard pro modely strojového učení a hlubokého učení. Umožňuje importovat a exportovat model (interoperabilitu) mezi oblíbenými architekturami AI. Další podrobnosti najdete v projektu ONNX Na GitHubu.

ONNX Runtime je opensourcový projekt, který podporuje odvozování mezi platformami. MODUL RUNTIME ONNX poskytuje rozhraní API napříč programovacími jazyky (včetně Pythonu, C++, C#, C, Javy a JavaScriptu). Tato rozhraní API můžete použít k odvozování vstupních imagí. Po exportu modelu do formátu ONNX můžete tato rozhraní API použít v libovolném programovacím jazyce, který váš projekt potřebuje.

V této příručce se dozvíte, jak pomocí rozhraní Python API pro modul RUNTIME ONNX vytvářet předpovědi obrázků pro oblíbené úlohy zpracování obrazu. Tyto exportované modely ONNX můžete použít napříč jazyky.

Požadavky

Stažení souborů modelu ONNX

Soubory modelu ONNX můžete stáhnout z AutoML spuštění pomocí uživatelského rozhraní studio Azure Machine Learning nebo sady Azure Machine Učení Python SDK. Doporučujeme stáhnout sadu SDK s názvem experimentu a ID nadřazeného spuštění.

Studio Azure Machine Learning

V studio Azure Machine Learning přejděte na experiment pomocí hypertextového odkazu na experiment vygenerovaný v poznámkovém bloku pro trénování nebo výběrem názvu experimentu na kartě Experimenty v části Prostředky. Pak vyberte nejlepší podřízené spuštění.

V rámci nejlepšího podřízeného spuštění přejděte na výstupy a protokoly>train_artifacts. Pomocí tlačítka Stáhnout ručně stáhněte následující soubory:

  • labels.json: Soubor, který obsahuje všechny třídy nebo popisky v trénovací datové sadě.
  • model.onnx: Model ve formátu ONNX.

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

Uložte stažené soubory modelu do adresáře. Příklad v tomto článku používá adresář ./automl_models .

Sada Azure Machine Learning Python SDK

Pomocí sady SDK můžete vybrat nejlepší podřízený běh (podle primární metriky) s názvem experimentu a ID nadřazeného spuštění. Pak si můžete stáhnout soubory labels.json a model.onnx .

Následující kód vrátí nejlepší podřízené spuštění na základě relevantní primární metriky.

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)

Stáhněte si soubor labels.json, který obsahuje všechny třídy a popisky v trénovací datové sadě.

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
)

Stáhněte si soubor model.onnx.

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

V případě dávkového odvozování pro rozpoznávání objektů a segmentaci instancí pomocí modelů ONNX si projděte část o generování modelu pro dávkové vyhodnocování.

Generování modelů pro dávkové bodování

AutoML pro obrázky ve výchozím nastavení podporuje dávkové bodování pro klasifikaci. Modely ONNX pro detekci objektů a segmentaci instancí ale nepodporují dávkové odvozování. V případě odvozování dávek pro detekci objektů a segmentaci instancí použijte následující postup k vygenerování modelu ONNX pro požadovanou velikost dávky. Modely vygenerované pro konkrétní velikost dávky nefungují u jiných velikostí dávek.

Stáhněte soubor prostředí Conda a vytvořte objekt prostředí, který se má použít s úlohou příkazu.

#  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,
)

K odeslání skriptu použijte následující argumenty specifické pro model. Další podrobnosti oargumentch kódech najdete v části Věnované podporovaným hyperparametr

Pokud chcete získat hodnoty argumentů potřebné k vytvoření modelu dávkového bodování, projděte si skripty bodování vygenerované ve složce výstupů trénovacích běhů AutoML. Pro nejlepší podřízené spuštění použijte hodnoty hyperparametrů dostupné v proměnné nastavení modelu v souboru bodování.

Pro klasifikaci obrázků s více třídami podporuje vygenerovaný model ONNX pro nejlepší podřízené spuštění ve výchozím nastavení dávkové vyhodnocování. Proto nejsou pro tento typ úlohy potřeba žádné argumenty specifické pro model a můžete přeskočit na část Načíst popisky a soubory modelu ONNX.

Stáhněte a zachovejte ONNX_batch_model_generator_automl_for_images.py soubor v aktuálním adresáři a odešlete skript. Pomocí následující úlohy příkazu odešlete skript ONNX_batch_model_generator_automl_for_images.py dostupný v úložišti GitHubu s příklady azureml a vygenerujte model ONNX konkrétní velikosti dávky. V následujícím kódu se vytrénované prostředí modelu používá k odeslání tohoto skriptu pro vygenerování a uložení modelu ONNX do výstupního adresáře.

Pro klasifikaci obrázků s více třídami podporuje vygenerovaný model ONNX pro nejlepší podřízené spuštění ve výchozím nastavení dávkové vyhodnocování. Proto nejsou pro tento typ úlohy potřeba žádné argumenty specifické pro model a můžete přeskočit na část Načíst popisky a soubory modelu ONNX.

Po vygenerování dávkového modelu si ho buď stáhněte z výstupů a protokolů>ručně prostřednictvím uživatelského rozhraní, nebo použijte následující metodu:

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
)

Po stažení modelu použijete balíček PYTHON modulu runtime ONNX k odvozování pomocí souboru model.onnx . Pro demonstrační účely tento článek používá datové sady z návodu k přípravě datových sad obrázků pro jednotlivé úlohy zpracování obrazu.

Vytrénovali jsme modely pro všechny úlohy zpracování obrazu pomocí příslušných datových sad, abychom ukázali odvození modelu ONNX.

Načtení popisků a souborů modelů ONNX

Následující fragment kódu načte labels.json, kde jsou názvy tříd seřazeny. To znamená, že pokud model ONNX predikuje ID popisku jako 2, pak odpovídá názvu popisku zadanému při třetím indexu v souboru labels.json .

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))

Získání očekávaných vstupních a výstupních podrobností pro model ONNX

Pokud máte model, je důležité znát některé podrobnosti specifické pro konkrétní model a úlohu. Tyto podrobnosti zahrnují počet vstupů a počet výstupů, očekávaný vstupní obrazec nebo formát pro předběžné zpracování obrázku a výstupní obrazec, takže znáte výstupy specifické pro model nebo výstupy specifické pro úlohy.

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}") 

Očekávané vstupní a výstupní formáty pro model ONNX

Každý model ONNX má předdefinovanou sadu vstupních a výstupních formátů.

Tento příklad použije model natrénovaný na datové sadě fridgeObjects s 134 obrázky a 4 třídami/popisky k vysvětlení odvozování modelu ONNX. Další informace o trénování úlohy klasifikace obrázků najdete v poznámkovém bloku klasifikace obrázků s více třídami.

Vstupní formát

Vstup je předzpracovaná image.

Název vstupu Vstupní obrazec Input type Popis
input1 (batch_size, num_channels, height, width) ndarray(float) Vstup je předzpracovaný obrázek s obrazcem (1, 3, 224, 224) pro velikost dávky 1 a výškou a šířkou 224. Tato čísla odpovídají hodnotám použitým crop_size v příkladu trénování.

Výstupní formát

Výstup je pole logit pro všechny třídy/popisky.

Název výstupu Výstupní obrazec Typ výstupu Popis
výstup 1 (batch_size, num_classes) ndarray(float) Model vrátí logits (bez softmax). Například pro dávkové třídy 1 a 4 vrátí (1, 4)hodnotu .

Předzpracování

Proveďte následující kroky předběžného zpracování pro odvozování modelu ONNX:

  1. Převeďte obrázek na RGB.
  2. Změňte velikost obrázku na valid_resize_size hodnoty, valid_resize_size které odpovídají hodnotám použitým při transformaci ověřovací datové sady během trénování. Výchozí hodnota je valid_resize_size 256.
  3. Na střed oříznout obrázek na height_onnx_crop_size a width_onnx_crop_size. valid_crop_size Odpovídá výchozí hodnotě 224.
  4. Změňte HxWxC na CxHxW.
  5. Převeďte na typ float.
  6. Normalizuje se pomocí imagí mean = [0.485, 0.456, 0.406] a .std = [0.229, 0.224, 0.225]

Pokud jste pro hyperparametryvalid_resize_size a valid_crop_size během trénování zvolili jiné hodnoty, měly by se tyto hodnoty použít.

Získejte vstupní obrazec potřebný pro model ONNX.

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

Bez PyTorchu

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]

S PyTorchem

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]

Odvozovat s modulem runtime ONNX

Odvozování s modulem RUNTIME ONNX se pro každou úlohu počítačového zpracování obrazu liší.

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

Pokud chcete získat skóre spolehlivosti klasifikace (pravděpodobnosti) pro každou třídu, použijte softmax() předpovězené hodnoty. Pak bude predikce třídou s nejvyšší pravděpodobností.

Bez PyTorchu

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]))

S PyTorchem

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]))

Vizualizace předpovědí

Vizualizujte vstupní obrázek s popisky.

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()

Další kroky