Membuat prediksi dengan ONNX pada model visi komputer dari AutoML

BERLAKU UNTUK: Python SDK azure-ai-ml v2 (saat ini)

Dalam artikel ini, Anda akan mempelajari cara menggunakan Open Neural Network Exchange (ONNX) untuk membuat prediksi pada model visi komputer yang dihasilkan dari pembelajaran mesin otomatis (AutoML) di Azure Pembelajaran Mesin.

Agar dapat menggunakan ONNX untuk prediksi, Anda perlu:

  1. Unduh file model ONNX dari eksekusi pelatihan AutoML.
  2. Memahami input dan output dari model ONNX.
  3. Praproses data Anda sehingga berada dalam format yang diperlukan untuk gambar input.
  4. Lakukan inferensi dengan ONNX Runtime untuk Python.
  5. Memvisualkan prediksi untuk tugas deteksi objek dan segmentasi instans.

ONNX adalah standar terbuka untuk pembelajaran mesin dan model pembelajaran mendalam. Ini memungkinkan impor dan ekspor model (interoperabilitas) di seluruh kerangka kerja AI yang populer. Untuk detail lebih lanjut, jelajahi proyek ONNX GitHub.

ONNX Runtime adalah proyek open-source yang mendukung inferensi lintas platform. ONNX Runtime menyediakan API di seluruh bahasa pemrograman (termasuk Python, C++, C#, C, Java, dan JavaScript). Anda dapat menggunakan API ini untuk melakukan inferensi pada gambar input. Setelah Anda mendapatkan model yang telah diekspor ke format ONNX, Anda dapat menggunakan API ini pada bahasa pemrograman apa pun yang dibutuhkan proyek Anda.

Dalam panduan ini, Anda mempelajari cara menggunakan API Python untuk ONNX Runtime untuk membuat prediksi pada gambar untuk tugas visi populer. Anda dapat menggunakan model yang diekspor ONNX ini dalam berbagai bahasa.

Prasyarat

Unduh file model ONNX

Anda dapat mengunduh file model ONNX dari eksekusi AutoML dengan menggunakan UI studio Azure Machine Learning atau Azure Machine Learning Python SDK. Sebaiknya Anda melakukan pengunduhan melalui SDK dengan nama eksperimen dan ID yang dijalankan induk.

Studio Azure Machine Learning

Di studio Azure Machine Learning, buka eksperimen Anda dengan menggunakan hyperlink ke eksperimen yang dihasilkan di buku catatan pelatihan, atau dengan memilih nama eksperimen pada tab Eksperimen di bawah Aset. Kemudian pilih eksekusi anak terbaik.

Dalam eksekusi anak terbaik, buka Outputs + logs>train_artifacts. Gunakan tombol Unduh untuk mengunduh file berikut secara manual:

  • labels.json: File yang berisi semua kelas atau label dalam himpunan data pelatihan.
  • model.onnx: Model dalam format ONNX.

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

Simpan file model yang diunduh dalam direktori. Contoh dalam artikel ini menggunakan direktori ./automl_models.

Azure Machine Learning Python SDK

Dengan SDK, Anda dapat memilih eksekusi anak terbaik (berdasarkan metrik utama) dengan nama eksperimen dan ID eksekusi induk. Kemudian, Anda dapat mengunduh file labels.json dan model.onnx.

Kode berikut mengembalikan eksekusi anak terbaik berdasarkan metrik utama yang relevan.

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)

Unduh file labels.json, yang berisi semua kelas atau label dalam himpunan data pelatihan.

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
)

Unduh file model.onnx.

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

Dalam kasus inferensi batch untuk Deteksi Objek dan Segmentasi Instans menggunakan model ONNX, lihat bagian tentang pembuatan model untuk penilaian batch.

Pembuatan model untuk penilaian batch

Secara default, AutoML untuk Gambar mendukung penilaian batch untuk klasifikasi. Tetapi deteksi objek dan segmentasi instans model ONNX tidak mendukung inferensi batch. Dalam hal inferensi batch untuk deteksi objek dan segmentasi instans, gunakan prosedur berikut untuk menghasilkan model ONNX untuk ukuran batch yang diperlukan. Model yang dihasilkan untuk ukuran batch tertentu tidak berfungsi untuk ukuran batch lainnya.

Unduh file lingkungan conda dan buat objek lingkungan yang akan digunakan dengan pekerjaan perintah.

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

Gunakan argumen spesifik model berikut untuk mengirimkan skrip. Untuk detail selengkapnya tentang argumen, lihat model hiperparameter tertentu dan untuk nama model deteksi objek yang didukung, lihat bagian arsitektur model yang didukung.

Untuk mendapatkan nilai argumen yang diperlukan untuk membuat model penilaian batch, lihat skrip penilaian yang dihasilkan di bawah folder output dari eksekusi pelatihan AutoML. Gunakan nilai hiperparameter yang tersedia dalam variabel pengaturan model di dalam file penilaian untuk menjalankan eksekusi turunan.

Untuk klasifikasi gambar multikelas, model ONNX yang dihasilkan untuk eksekusi turunan yang dijalankan mendukung penilaian batch secara default. Oleh karena itu, tidak diperlukan argumen khusus model untuk jenis tugas ini dan Anda dapat melompat ke bagian Muat label dan file model ONNX.

Unduh dan simpan file ONNX_batch_model_generator_automl_for_images.py di direktori saat ini dan kirimkan skrip. Gunakan pekerjaan perintah berikut untuk mengirimkan skrip ONNX_batch_model_generator_automl_for_images.pyyang tersedia di repositori GitHub azureml-examples, untuk menghasilkan model ONNX dengan ukuran batch tertentu. Dalam kode berikut, lingkungan model terlatih digunakan untuk mengirimkan skrip ini untuk menghasilkan dan menyimpan model ONNX ke direktori output.

Untuk klasifikasi gambar multikelas, model ONNX yang dihasilkan untuk eksekusi turunan yang dijalankan mendukung penilaian batch secara default. Oleh karena itu, tidak diperlukan argumen khusus model untuk jenis tugas ini dan Anda dapat melompat ke bagian Muat label dan file model ONNX.

Setelah model batch dihasilkan, unduh dari output>Outputs+logs secara manual, atau gunakan metode berikut:

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
)

Setelah langkah pengunduhan model, Anda menggunakan paket ONNX Runtime Python untuk melakukan inferensi dengan menggunakan file model.onnx. Untuk tujuan demonstrasi, artikel ini menggunakan himpunan data dari Cara menyiapkan himpunan data gambar untuk setiap tugas visi.

Kami melatih model untuk semua tugas visi dengan himpunan data masing-masing untuk menunjukkan inferensi model ONNX.

Memuat label dan file model ONNX

Cuplikan kode berikut memuat label.json, tempat nama kelas dipesan. Artinya, jika model ONNX memprediksi ID label sebagai 2, maka sesuai dengan nama label yang diberikan pada indeks ketiga dalam file 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))

Dapatkan detail input dan output yang diharapkan untuk model ONNX

Ketika Anda memiliki model, penting untuk mengetahui beberapa detail dengan model spesifik dan tugas spesifik. Detail ini mencakup jumlah input dan jumlah output, bentuk atau format input yang diharapkan untuk pra-pemrosesan gambar, dan bentuk output sehingga Anda mengetahui output dengan model spesifik atau tugas spesifik.

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

Format input dan output yang diharapkan untuk model ONNX

Setiap model ONNX memiliki seperangkat format input dan output yang telah ditentukan.

Contoh ini menerapkan model yang dilatih pada himpunan data fridgeObjects dengan 134 citra dan 4 kelas/label untuk menjelaskan inferensi model ONNX. Untuk informasi selengkapnya tentang pelatihan tugas klasifikasi gambar, lihat buku catatan klasifikasi gambar multi-kelas.

Format input

Input adalah citra yang telah diproses sebelumnya.

Masukkan nama Bentuk input Jenis input Deskripsi
input1 (batch_size, num_channels, height, width) ndarray(float) Input adalah gambar yang telah diproses sebelumnya, dengan bentuk (1, 3, 224, 224) untuk ukuran batch 1, dan tinggi serta lebar 224. Angka-angka ini sesuai dengan nilai-nilai yang digunakan untuk crop_size dalam contoh pelatihan.

Format output

Output adalah array logits untuk semua kelas/label.

Nama output Bentuk output Jenis output Deskripsi
output1 (batch_size, num_classes) ndarray(float) Model mengembalikan logits (tanpa softmax). Misalnya, untuk kelas ukuran batch 1 dan 4, ia mengembalikan (1, 4).

Pra-pemrosesan

Lakukan langkah-langkah prapemrosesan berikut untuk inferensi model ONNX:

  1. Konversikan gambar ke RGB.
  2. Ubah ukuran gambar menjadi nilai valid_resize_size dan valid_resize_size yang sesuai dengan nilai yang digunakan dalam transformasi himpunan data validasi selama pelatihan. Nilai default untuk valid_resize_size adalah 256.
  3. Pangkas bagian tengah gambar ke height_onnx_crop_size dan width_onnx_crop_size. Ini sesuai dengan valid_crop_size, dengan nilai default 224.
  4. Ubah HxWxC ke CxHxW.
  5. Konversi ke jenis float.
  6. Normalisasi dengan mean = [0.485, 0.456, 0.406] dan std = [0.229, 0.224, 0.225] dari ImageNet.

Jika Anda memilih nilai yang berbeda untuk hiperparametervalid_resize_size dan valid_crop_size selama pelatihan, nilai-nilai tersebut harus digunakan.

Dapatkan bentuk input yang diperlukan untuk 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

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

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

Inferensi dengan ONNX Runtime

Kesimpulan dengan ONNX Runtime berbeda untuk setiap tugas visi komputer.

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)

Pasca-pemrosesan

Terapkan softmax() pada nilai yang diprediksi untuk mendapatkan skor kepercayaan klasifikasi (probabilitas) untuk setiap kelas. Maka prediksi akan menjadi kelas dengan probabilitas tertinggi.

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

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

Visualisasikan prediksi

Visualisasikan gambar input dengan label.

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

Langkah berikutnya