Udostępnij za pośrednictwem


Przewidywanie przy użyciu języka ONNX w modelach przetwarzania obrazów z rozwiązania AutoML (wersja 1)

DOTYCZY: Zestaw SDK języka Python w wersji 1

Ważne

Niektóre polecenia interfejsu wiersza polecenia platformy Azure w tym artykule używają azure-cli-mlrozszerzenia , lub w wersji 1 dla usługi Azure Machine Learning. Obsługa rozszerzenia w wersji 1 zakończy się 30 września 2025 r. Będzie można zainstalować rozszerzenie v1 i używać go do tej daty.

Zalecamy przejście do mlrozszerzenia , lub w wersji 2 przed 30 września 2025 r. Aby uzyskać więcej informacji na temat rozszerzenia w wersji 2, zobacz Rozszerzenie interfejsu wiersza polecenia usługi Azure ML i zestaw Python SDK w wersji 2.

Z tego artykułu dowiesz się, jak używać programu Open Neural Network Exchange (ONNX) do przewidywania modeli przetwarzania obrazów generowanych na podstawie zautomatyzowanego uczenia maszynowego (AutoML) w usłudze Azure Machine Learning.

Aby użyć polecenia ONNX do przewidywania, należy wykonać następujące kroki:

  1. Pobierz pliki modelu ONNX z przebiegu trenowania rozwiązania AutoML.
  2. Omówienie danych wejściowych i wyjściowych modelu ONNX.
  3. Wstępnie przetwarza dane, aby był w wymaganym formacie dla obrazów wejściowych.
  4. Wnioskowanie za pomocą środowiska uruchomieniowego ONNX dla języka Python.
  5. Wizualizuj przewidywania pod kątem zadań wykrywania obiektów i segmentacji wystąpień.

ONNX to otwarty standard dla modeli uczenia maszynowego i uczenia głębokiego. Umożliwia importowanie i eksportowanie modelu (współdziałanie) w popularnych strukturach sztucznej inteligencji. Aby uzyskać więcej informacji, zapoznaj się z projektem ONNX GitHub.

Środowisko uruchomieniowe ONNX to projekt typu open source, który obsługuje wnioskowanie międzyplatformowe. Środowisko uruchomieniowe ONNX udostępnia interfejsy API w różnych językach programowania (w tym Python, C++, C#, C, Java i JavaScript). Te interfejsy API umożliwiają wnioskowanie na obrazach wejściowych. Po wyeksportowaniu modelu do formatu ONNX możesz użyć tych interfejsów API w dowolnym języku programowania, którego potrzebuje projekt.

W tym przewodniku dowiesz się, jak używać interfejsów API języka Python dla środowiska uruchomieniowego ONNX do przewidywania obrazów na potrzeby popularnych zadań przetwarzania obrazów. Możesz użyć tych wyeksportowanych modeli ONNX w różnych językach.

Wymagania wstępne

Pobieranie plików modelu ONNX

Pliki modelu ONNX można pobrać z poziomu przebiegów rozwiązania AutoML przy użyciu interfejsu użytkownika usługi Azure Machine Learning Studio lub zestawu SDK języka Python usługi Azure Machine Learning. Zalecamy pobranie za pośrednictwem zestawu SDK z nazwą eksperymentu i nadrzędnym identyfikatorem przebiegu.

Azure Machine Learning Studio

W usłudze Azure Machine Learning Studio przejdź do eksperymentu, używając hiperlinku do eksperymentu wygenerowanego w notesie szkoleniowym lub wybierając nazwę eksperymentu na karcie Eksperymenty w obszarze Zasoby. Następnie wybierz najlepszy przebieg podrzędny.

W ramach najlepszego uruchomienia podrzędnego przejdź do pozycji Dane wyjściowe i dzienniki>train_artifacts. Użyj przycisku Pobierz, aby ręcznie pobrać następujące pliki:

  • labels.json: plik zawierający wszystkie klasy lub etykiety w zestawie danych trenowania.
  • model.onnx: model w formacie ONNX.

Zrzut ekranu przedstawiający opcje pobierania plików modelu ONNX.

Zapisz pobrane pliki modelu w katalogu. W przykładzie w tym artykule jest używany katalog ./automl_models .

Zestaw SDK języka Python usługi Azure Machine Learning

Za pomocą zestawu SDK możesz wybrać najlepszy przebieg podrzędny (według podstawowej metryki) z nazwą eksperymentu i nadrzędnym identyfikatorem uruchomienia. Następnie możesz pobrać pliki labels.json i model.onnx .

Poniższy kod zwraca najlepszy przebieg podrzędny na podstawie odpowiedniej metryki podstawowej.

from azureml.train.automl.run import AutoMLRun

# Select the best child run
run_id = '' # Specify the run ID
automl_image_run = AutoMLRun(experiment=experiment, run_id=run_id)
best_child_run = automl_image_run.get_best_child()

Pobierz plik labels.json zawierający wszystkie klasy i etykiety w zestawie danych trenowania.

labels_file = 'automl_models/labels.json'
best_child_run.download_file(name='train_artifacts/labels.json', output_file_path=labels_file)

Pobierz plik model.onnx.

onnx_model_path = 'automl_models/model.onnx'
best_child_run.download_file(name='train_artifacts/model.onnx', output_file_path=onnx_model_path)

Generowanie modelu na potrzeby oceniania wsadowego

Domyślnie rozwiązanie AutoML dla obrazów obsługuje ocenianie wsadowe klasyfikacji. Jednak modele wykrywania obiektów i segmentacji wystąpień nie obsługują wnioskowania wsadowego. W przypadku wnioskowania wsadowego na potrzeby wykrywania obiektów i segmentacji wystąpień użyj poniższej procedury, aby wygenerować model ONNX dla wymaganego rozmiaru partii. Modele generowane dla określonego rozmiaru partii nie działają w przypadku innych rozmiarów partii.

from azureml.core.script_run_config import ScriptRunConfig
from azureml.train.automl.run import AutoMLRun
from azureml.core.workspace import Workspace
from azureml.core import Experiment

# specify experiment name
experiment_name = ''
# specify workspace parameters
subscription_id = ''
resource_group = ''
workspace_name = ''
# load the workspace and compute target
ws = ''
compute_target = ''
experiment = Experiment(ws, name=experiment_name)

# specify the run id of the automl run
run_id = ''
automl_image_run = AutoMLRun(experiment=experiment, run_id=run_id)
best_child_run = automl_image_run.get_best_child()

Użyj następujących argumentów specyficznych dla modelu, aby przesłać skrypt. Aby uzyskać więcej informacji na temat argumentów, zapoznaj się z sekcją Dotyczącą hiperparametrów specyficznych dla modelu i obsługiwanymi nazwami modeli wykrywania obiektów.

Aby uzyskać wartości argumentów potrzebne do utworzenia modelu oceniania wsadowego, zapoznaj się ze skryptami oceniania wygenerowanymi w folderze outputs przebiegów trenowania rozwiązania AutoML. Użyj wartości hiperparametrów dostępnych w zmiennej ustawień modelu wewnątrz pliku oceniania, aby uzyskać najlepszy przebieg podrzędny.

W przypadku klasyfikacji obrazów wieloklasowych wygenerowany model ONNX dla najlepszego podrzędnego uruchamiania domyślnie obsługuje ocenianie wsadowe. W związku z tym nie są potrzebne żadne argumenty specyficzne dla modelu dla tego typu zadania i można przejść do sekcji Ładowanie etykiet i plików modelu ONNX.

Pobierz i zachowaj ONNX_batch_model_generator_automl_for_images.py plik w bieżącym katalogu i prześlij skrypt. Użyj polecenia ScriptRunConfig , aby przesłać skrypt ONNX_batch_model_generator_automl_for_images.py dostępny w repozytorium GitHub azureml-examples, aby wygenerować model ONNX o określonym rozmiarze partii. W poniższym kodzie wytrenowane środowisko modelu służy do przesyłania tego skryptu w celu wygenerowania i zapisania modelu ONNX w katalogu outputs.

script_run_config = ScriptRunConfig(source_directory='.',
                                    script='ONNX_batch_model_generator_automl_for_images.py',
                                    arguments=arguments,
                                    compute_target=compute_target,
                                    environment=best_child_run.get_environment())

remote_run = experiment.submit(script_run_config)
remote_run.wait_for_completion(wait_post_processing=True)

Po wygenerowaniu modelu wsadowego pobierz go z danych wyjściowych +dzienników>wyjściowych ręcznie lub użyj następującej metody:

batch_size= 8  # use the batch size used to generate the model
onnx_model_path = 'automl_models/model.onnx'  # local path to save the model
remote_run.download_file(name='outputs/model_'+str(batch_size)+'.onnx', output_file_path=onnx_model_path)

Po kroku pobierania modelu należy użyć pakietu języka Python środowiska uruchomieniowego ONNX do wnioskowania przy użyciu pliku model.onnx . W celach demonstracyjnych w tym artykule użyto zestawów danych z artykułu How to prepare image datasets for each vision task (Jak przygotować zestawy danych obrazów do każdego zadania przetwarzania obrazów ).

Wytrenowaliśmy modele dla wszystkich zadań przetwarzania obrazów przy użyciu odpowiednich zestawów danych, aby zademonstrować wnioskowanie modelu ONNX.

Ładowanie etykiet i plików modelu ONNX

Poniższy fragment kodu ładuje labels.json, gdzie nazwy klas są uporządkowane. Oznacza to, że jeśli model ONNX przewiduje identyfikator etykiety jako 2, odpowiada nazwie etykiety podanej w trzecim indeksie w pliku 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))

Pobieranie oczekiwanych danych wejściowych i wyjściowych dla modelu ONNX

Jeśli masz model, ważne jest, aby poznać szczegóły dotyczące określonego modelu i zadania. Te szczegóły obejmują liczbę danych wejściowych i liczbę danych wyjściowych, oczekiwany kształt wejściowy lub format przetwarzania wstępnego obrazu oraz kształt danych wyjściowych, aby znać dane wyjściowe specyficzne dla modelu lub dane wyjściowe specyficzne dla danego modelu.

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

Oczekiwane formaty danych wejściowych i wyjściowych dla modelu ONNX

Każdy model ONNX ma wstępnie zdefiniowany zestaw formatów wejściowych i wyjściowych.

W tym przykładzie model jest stosowany wytrenowany na zestawie danych fridgeObjects z 134 obrazami i 4 klasami/etykietami w celu wyjaśnienia wnioskowania modelu ONNX. Aby uzyskać więcej informacji na temat trenowania zadania klasyfikacji obrazów, zobacz notes klasyfikacji obrazów wieloklasowych.

Format danych wejściowych

Dane wejściowe to wstępnie przetworzony obraz.

Nazwa danych wejściowych Kształt wejściowy Input type opis
input1 (batch_size, num_channels, height, width) ndarray(float) Dane wejściowe to wstępnie przetworzony obraz o kształcie (1, 3, 224, 224) partii o rozmiarze 1 oraz wysokości i szerokości 224. Te liczby odpowiadają wartościom używanym crop_size w przykładzie trenowania.

Format wyjściowy

Dane wyjściowe to tablica logits dla wszystkich klas/etykiet.

Nazwa danych wyjściowych Kształt danych wyjściowych Typ danych wyjściowych opis
output1 (batch_size, num_classes) ndarray(float) Model zwraca logits (bez softmaxelementu ). Na przykład w przypadku klas o rozmiarze partii 1 i 4 zwraca wartość (1, 4).

Wstępnego przetwarzania

Wykonaj następujące kroki przetwarzania wstępnego dla wnioskowania modelu ONNX:

  1. Przekonwertuj obraz na RGB.
  2. Zmień rozmiar obrazu na valid_resize_size i valid_resize_size wartości, które odpowiadają wartościom używanym podczas przekształcania zestawu danych weryfikacji podczas trenowania. Wartość domyślna parametru valid_resize_size to 256.
  3. Wyśrodkuj obraz do height_onnx_crop_size i width_onnx_crop_size. Odpowiada valid_crop_size on wartości domyślnej 224.
  4. Zmień HxWxC na CxHxW.
  5. Przekonwertuj na typ zmiennoprzecinkowy.
  6. Normalizuj przy użyciu sieci mean = [0.485, 0.456, 0.406] ImageNet i std = [0.229, 0.224, 0.225].

Jeśli wybrano różne wartości hiperparametrów valid_resize_size i valid_crop_size podczas trenowania, należy użyć tych wartości.

Pobierz kształt wejściowy wymagany dla modelu 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 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]

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

Wnioskowanie za pomocą środowiska uruchomieniowego ONNX

Wnioskowanie przy użyciu środowiska uruchomieniowego ONNX różni się w przypadku każdego zadania przetwarzania obrazów.

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

Zastosuj softmax() wartości przewidywane, aby uzyskać wyniki ufności klasyfikacji (prawdopodobieństwa) dla każdej klasy. Następnie przewidywanie będzie klasą o najwyższym prawdopodobieństwie.

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

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

Wizualizowanie przewidywań

Wizualizowanie obrazu wejściowego przy użyciu etykiet

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

Następne kroki