WinML Series: Konversi Model ML kamu ke format Onnx

Hi Rekan Makers,

Melanjutkan series sebelumnya kali ini kita akan membahas bagaimana mengonversi model ML kamu ke dalam format onnx (Open Neural Network Exchange). Penjelasan detail mengenai Onnx model ini bisa dilihat pada link ini. Pertanyaan selanjutnya mengapa onnx model ? jawaban paling tepat adalah tentang interoperabilitas, saat ini kita hidup di dunia multi-platform dimana kita menggunakan berbagai jenis device mulai dari mobile, pc, smart device, smart TV, dsb. Kemudia device tersebut memiliki jenis arsitektur CPU yang berbeda, OS yang berbeda, nah onnx ini memungkinkan kita menggunakan model ML yang sama di platform yang berbeda dan juga dioptimasi secara hardware ketika model tersebut digunakan untuk inferensi. Jika rekan-rekan ingin mengetahui bagaimana menggunakan model onnx di windows modern app (uwp) silakan lihat link berikut.

WinML sendiri mendukung onnx dengan versi dibawah ini:

Windows release ONNX versions supported ONNX opsets supported
Windows 10, version 1903 (build 18362) 1.2.2 and 1.3 7 and 8
Windows 10, version 1809 (build 17763) 1.2.2 7

Jika ternyata rekan-rekan memiliki model onnx yang tidak didukung, silakan gunakan tools pada link ini. Berikut contoh penggunaannya dengan python:

import onnx

# Load the model
model = onnx.load("path_to/resnet18.onnx")

# Check that the IR is well formed
onnx.checker.check_model(model)

from onnx import version_converter

# Convert to version 8
converted_model = version_converter.convert_version(onnx_model, 8)

# Save model
onnx.save(converted_model, "path_to/resnet18_v8.onnx")

Nah jika rekan-rekan ingin menggunakan model onnx, maka ada beberapa cara yang dapat ditempuh:

Menggunakan model yang sudah ditraining sebelumnya di onnx model zoo. Silakan buka linknya disini. Pada repo tersebut terdapat beberapa state-of-art model ML yang sudah diklasifikasi berdasarkan fungsinya seperti image classification, machine translation, object detection, image manipulation, speech and audio processing dan lain sebagainya. Silakan download dan pastikan versi onnx-nya disupport oleh WinML.

Cara kedua adalah menggunakan WinML tools untuk melakukan konversi dari Model ML yang dibuat dengan framework lain ke Onnx. Ini beberapa framework ML yang disupport oleh winmltools:

  • Apple Core ML
  • Keras
  • scikit-learn
  • lightgbm
  • xgboost
  • libSVM
  • TensorFlow (experimental)

Cara instalasinya sebagai berikut:

  1. Install python3 dari link berikut: https://www.python.org/downloads/
  2. Buka command prompt / terminal
  3. Lalu install tools dengan perintah : pip install -U winmltools
  4. Jika mau konversi core ml model di windows install juga core ml tools dengan perintah: pip install git+https://github.com/apple/coremltools
  5. Nah tools sudah siap digunakan

Berikut adalah contoh kode untuk konversi onnx dari core ml:

from coremltools.models.utils import load_spec
# Load model file
model_coreml = load_spec('contoh.mlmodel')
from winmltools import convert_coreml
# Convert it!
# The automatic code generator (mlgen) uses the name parameter to generate class names.
model_onnx = convert_coreml(model_coreml, 7, name='ContohModel')

Kita juga dapat mengonversi onnx ke format teks seperti berikut:

from winmltools.utils import save_model
# Save the produced ONNX model in binary format
save_model(model_onnx, 'example.onnx')
# Save the produced ONNX model in text format
from winmltools.utils import save_text
save_text(model_onnx, 'example.txt')

Onnx tidak support input dan output data berupa gambar, jadi kita perlu melakukan pre-processing / post-processing. Berikut adalah contoh mengonversi model onnx dengan input dan output berupa gambar:

from coremltools.models.utils import load_spec
from winmltools import convert_coreml
model_coreml = load_spec('FNS-Candy.mlmodel')
model_coreml.description # Print the content of Core ML model description
# The automatic code generator (mlgen) uses the name parameter to generate class names.
model_onnx = convert_coreml(model_coreml, 7, name='FNSCandy')

Contoh diatas menggunakan model style transfer dengan coreml. Nah untuk mengkonversi gambar menjadi input, kita harus menginput dalam bentuk [C,H,W] dimana C adalah untuk masing-masing channel R, G, B maka kalau image input adalah y maka input akan memiliki bentuk seperti ini : Y[0, :, :]/Y[1, :, :]/Y[2, :, :] . jika grayscale hanya 1 channel: [1, H, W]. Kalau untuk mengonversi dari output menjadi gambar, silakan mengganti nilai < 0 menjadi 0 dan nilai > 255 menjadi 255. Lalu tambah nilai dengan 0.5 kemudian buang desimalnya (cth: 1.6 + 0.5 = 2.1 maka jadi 2).

Dibawah ini adalah contoh kode untuk mengonversi model scikit-learn:

# First, we create a toy data set.
# The training matrix X contains three examples, with two features each.
# The label vector, y, stores the labels of all examples.
X = [[0.5, 1.], [-1., -1.5], [0., -2.]]
y = [1, -1, -1]

# Then, we create a linear classifier and train it.
from sklearn.svm import LinearSVC
linear_svc = LinearSVC()
linear_svc.fit(X, y)

# To convert scikit-learn models, we need to specify the input feature's name and type for our converter.
# The following line means we have a 2-D float feature vector, and its name is "input" in ONNX.
# The automatic code generator (mlgen) uses the name parameter to generate class names.
from winmltools import convert_sklearn
from winmltools.convert.common.data_types import FloatTensorType
linear_svc_onnx = convert_sklearn(linear_svc, 7, name='LinearSVC',
initial_types=[('input', FloatTensorType([1, 2]))])

# Now, we save the ONNX model into binary format.
from winmltools.utils import save_model
save_model(linear_svc_onnx, 'linear_svc.onnx')

# If you'd like to load an ONNX binary file, our tool can also help.
from winmltools.utils import load_model
linear_svc_onnx = load_model('linear_svc.onnx')

# To see the produced ONNX model, we can print its contents or save it in text format.
print(linear_svc_onnx)
from winmltools.utils import save_text
save_text(linear_svc_onnx, 'linear_svc.txt')

# The conversion of linear regression is very similar. See the example below.
from sklearn.svm import LinearSVR
linear_svr = LinearSVR()
linear_svr.fit(X, y)
linear_svr_onnx = convert_sklearn(linear_svr, 7, name='LinearSVR',
initial_types=[('input', FloatTensorType([1, 2]))])

Jika berbentuk pipelines maka ini contoh kode konversinya:

# First, we create a toy data set.
# Notice that the first example's last feature value, 300, is large.
X = [[0.5, 1., 300.], [-1., -1.5, -4.], [0., -2., -1.]]
y = [1, -1, -1]

# Then, we declare a linear classifier.
from sklearn.svm import LinearSVC
linear_svc = LinearSVC()

# One common trick to improve a linear model's performance is to normalize the input data.
from sklearn.preprocessing import Normalizer
normalizer = Normalizer()

# Here, we compose our scikit-learn pipeline.
# First, we apply our normalization.
# Then we feed the normalized data into the linear model.
from sklearn.pipeline import make_pipeline
pipeline = make_pipeline(normalizer, linear_svc)
pipeline.fit(X, y)

# Now, we convert the scikit-learn pipeline into ONNX format.
# Compared to the previous example, notice that the specified feature dimension becomes 3.
# The automatic code generator (mlgen) uses the name parameter to generate class names.
from winmltools import convert_sklearn
from winmltools.convert.common.data_types import FloatTensorType, Int64TensorType
pipeline_onnx = convert_sklearn(linear_svc, name='NormalizerLinearSVC',
input_features=[('input', FloatTensorType([1, 3]))])

# We can print the fresh ONNX model.
print(pipeline_onnx)

# We can also save the ONNX model into a binary file for later use.
from winmltools.utils import save_model
save_model(pipeline_onnx, 'pipeline.onnx')

# Our conversion framework provides limited support of heterogeneous feature values.
# We cannot have numerical types and string types in one feature vector.
# Let's assume that the first two features are floats and the last feature is integers (encoded a categorical attribute).
X_heter = [[0.5, 1., 300], [-1., -1.5, 400], [0., -2., 100]]

# One popular way to represent categorical is one-hot encoding.
from sklearn.preprocessing import OneHotEncoder
one_hot_encoder = OneHotEncoder(categorical_features=[2])

# Let's initialize a classifier.
# It will be right after the one-hot encoder in our pipeline.
linear_svc = LinearSVC()

# Then, we form a two-stage pipeline.
another_pipeline = make_pipeline(one_hot_encoder, linear_svc)
another_pipeline.fit(X_heter, y)

# Now, we convert, save, and load the converted model.
# For heterogeneous feature vectors, we need to separately specify their types for
# all homogeneous segments.
# The automatic code generator (mlgen) uses the name parameter to generate class names.
another_pipeline_onnx = convert_sklearn(another_pipeline, name='OneHotLinearSVC',
input_features=[('input', FloatTensorType([1, 2])),
('another_input', Int64TensorType([1, 1]))])
save_model(another_pipeline_onnx, 'another_pipeline.onnx')
from winmltools.utils import load_model
loaded_onnx_model = load_model('another_pipeline.onnx')

# Of course, we can print the ONNX model to see if anything went wrong.
print(another_pipeline_onnx)

Berikut adalah contoh kode untuk mengonversi model tensorflow:

import winmltools
import tensorflow

filename = 'frozen-model.pb'
output_names = ['output:0']

graph_def = graph_pb2.GraphDef()
with open(filename, 'rb') as file:
graph_def.ParseFromString(file.read())
g = tf.import_graph_def(graph_def, name='')

with tf.Session(graph=g) as sess:
converted_model = winmltools.convert_tensorflow(sess.graph, 7, output_names=['output:0'])
winmltools.save_model(converted_model)

nama Output model lihat saja dengan netron, download disini.

Konversi model keras dengan kode berikut:

import numpy as np
from keras.preprocessing import image
from keras.applications.resnet50 import preprocess_input
import keras2onnx
import onnxruntime
import onnx

# image preprocessing
img_path = 'street.jpg' # make sure the image is in img_path
img_size = 224
img = image.load_img(img_path, target_size=(img_size, img_size))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)

# load keras model
from keras.applications.resnet50 import ResNet50
model = ResNet50(include_top=True, weights='imagenet')

# convert to onnx model
onnx_model = keras2onnx.convert_keras(model, model.name)

# Save to disk
temp_model_file = 'model.onnx'
onnx.save_model(onnx_model, temp_model_file)
sess = onnxruntime.InferenceSession(temp_model_file)

# runtime prediction
x = x if isinstance(x, list) else [x]
feed = dict([(input.name, x[n]) for n, input in enumerate(sess.get_inputs())])
pred_onnx = sess.run(None, feed)

Kadang kita perlu mengurangi size dari model ML kita, salah satu caranya dengan mengonversi float 32 menjadi 16 (perhatian: akurasi dapat berkurang). Berikut adalah contoh kodenya:

from winmltools.utils import convert_float_to_float16
from winmltools.utils import load_model, save_model
onnx_model = load_model('model.onnx')
new_onnx_model = convert_float_to_float16(onnx_model)
save_model(new_onnx_model, 'model_fp16.onnx')

Dengan metode post-weight quantization kita bisa menyimpan float 32 dalam bentuk data 8 bit, hasilnya adalah pengurangan ukuran model (perhatian: akurasi model berkurang). Berikut adalah kodenya:

import winmltools

model = winmltools.load_model('model.onnx')
packed_model = winmltools.quantize(model, per_channel=True, nbits=8, use_dequantize_linear=True)
winmltools.save_model(packed_model, 'quantized.onnx')

Keterangan dari parameter method ‘quantize’:

  1. per_channel: Defaultnya adalah true, jika true maka setiap channel akan di quantize secara linier untuk setiap tensor dalam format [n,c,h,w].
  2. nbits: jumlah bit untuk quantize. Saat ini cuma dukung 8 bit.
  3. use_dequantize_linear: defaultnya true, ini akan secara linier melakukan dequantize channel untuk operator Conv dengan format [n,c,h,w].

Oke, artikel ini mengakhiri seri WinML. kita akan bertemu lagi di seri baru selanjutnya. Semoga bermanfaat dan silakan bereksperiment.

Salam Makers ;D

Loading

You May Also Like