ONNX: Huấn luyện trên bất kỳ framework nào, triển khai trên mọi phần cứng
Tìm hiểu cách chuyển đổi mô hình sang định dạng ONNX, tối ưu bằng lượng tử hóa và triển khai trên bất kỳ nền tảng nào — từ thiết bị biên tới máy chủ đám mây — mà không bị khóa nhà cung cấp.
Bạn đã huấn luyện một mô hình trong PyTorch, nhưng hệ thống production lại chạy TensorFlow. Có cần viết lại mọi thứ từ đầu không?
Không, bạn không cần.
Khả năng tương tác giữa các mô hình có thể khiến kỹ sư học máy đau đầu. Mỗi framework dùng một định dạng khác nhau, và việc chuyển đổi giữa chúng có thể làm hỏng mô hình hoặc gây ra hành vi không mong muốn. Bạn không muốn điều đó, nhưng cũng không muốn duy trì nhiều phiên bản của cùng một mô hình.
ONNX (Open Neural Network Exchange) giải quyết vấn đề bằng cách cung cấp một định dạng phổ quát cho các mô hình học máy. Nó cho phép bạn huấn luyện trên một framework và triển khai trên framework khác mà không cần viết lại mã hay xử lý lỗi vặt.
Trong bài viết này, tôi sẽ hướng dẫn bạn cách chuyển đổi mô hình sang định dạng ONNX, chạy suy luận với ONNX Runtime, tối ưu mô hình cho production và triển khai trên nhiều nền tảng, từ thiết bị biên đến máy chủ đám mây.
Bắt đầu với ONNX
Bạn không thể dùng ONNX nếu chưa thiết lập môi trường phát triển.
Trong phần này, tôi sẽ hướng dẫn từng bước bạn cần — từ yêu cầu phần mềm đến tạo workspace sạch, có thể tái lập. Tôi sẽ chỉ bạn cách cài ONNX và ONNX Runtime trên Windows, Linux và macOS.
Điều kiện tiên quyết
ONNX hoạt động trên mọi hệ điều hành phổ biến.
Bạn cần cài Python 3.8 trở lên trên máy. Vậy là đủ cho phần cơ bản. ONNX hỗ trợ ABI3 (Application Binary Interface) của Python, nghĩa là bạn có thể dùng các gói nhị phân dựng sẵn mà không phải biên dịch từ mã nguồn.
Với người dùng Windows, bạn có thể cài uv bằng PowerShell:
powershell -c"irm <https://astral.sh/uv/install.ps1> | iex"Được hỗ trợ bởi
Nếu gặp lỗi chính sách thực thi, hãy chạy PowerShell với quyền quản trị trước.
Người dùng Linux có thể cài uv bằng lệnh shell sau:
curl-LsSf<https://astral.sh/uv/install.sh>|shĐược hỗ trợ bởi
Lệnh này hoạt động trên mọi bản phân phối Linux.
Cuối cùng, người dùng macOS có thể cài uv qua Homebrew, hoặc bằng lệnh curl:
brew install uv
# orcurl-LsSf<https://astral.sh/uv/install.sh>|shĐược hỗ trợ bởi
Thiết lập môi trường
Cách thiết lập một dự án với uv:
mkdir onnx-project
cd onnx-project
# Initialize a uv project with Python 3.10
uv init --python3.13Được hỗ trợ bởi
uv tạo thư mục .venv và tệp pyproject.toml. Môi trường ảo được kích hoạt tự động khi bạn chạy lệnh qua uv.
Giờ hãy cài ONNX và ONNX Runtime:
uv add onnx onnxruntime
# Verify the installation
uv run python -c"import onnx; print(onnx.__version__)"
uv run python -c"import onnxruntime; print(onnxruntime.__version__)"Được hỗ trợ bởi
Các lệnh kiểm tra sẽ in ra số phiên bản:
Các phụ thuộc đã được cố định phiên bản. uv tự động ghi tất cả phiên bản gói vào uv.lock. Điều này giúp build có thể tái lập — ai cũng có thể tạo lại đúng môi trường của bạn bằng cách chạy uv sync.
Đây là nội dung pyproject.toml của bạn:
[project]
name = "onnx-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"onnx>=1.19.1",
"onnxruntime>=1.23.2",
]Được hỗ trợ bởi
Vậy là xong, môi trường đã được cấu hình! Hãy tiếp tục với những kiến thức nền tảng.
Tìm hiểu những điều cơ bản về ONNX
Bạn cần hiểu ONNX hoạt động thế nào trước khi bắt đầu chuyển đổi mô hình.
Trong phần này, tôi sẽ phân tích định dạng ONNX và giải thích kiến trúc dựa trên đồ thị của nó. Tôi sẽ chỉ bạn bên trong một tệp ONNX có gì và dữ liệu chảy qua nó ra sao.
Định dạng mô hình ONNX
Một mô hình ONNX là tệp chứa cấu trúc và trọng số của mạng nơ-ron.
Hãy coi nó như một bản vẽ thiết kế. Tệp mô tả cần thực hiện những phép toán nào, theo thứ tự nào, và với tham số gì. Trọng số đã huấn luyện được lưu kèm bản thiết kế này, nên mô hình sẵn sàng chạy mà không cần tệp bổ sung.
ONNX ra mắt năm 2017 nhờ hợp tác giữa Microsoft và Facebook (nay là Meta). Mục tiêu rất đơn giản — không còn phải viết lại mô hình mỗi lần đổi framework. PyTorch, TensorFlow và các framework khác liên tục cải tiến, nhưng mô hình không thể di chuyển giữa chúng nếu không chuyển đổi thủ công.
ONNX đã thay đổi điều đó. Phiên bản 1.0 hỗ trợ các phép toán cơ bản cho thị giác máy tính và mạng nơ-ron đơn giản. ONNX ngày nay hỗ trợ transformers, mô hình ngôn ngữ lớn và các kiến trúc phức tạp chưa tồn tại vào năm 2017.
Nó sử dụng Protocol Buffers (protobuf) để tuần tự hóa dữ liệu. Protobuf là định dạng nhị phân do Google phát triển để lưu trữ và truyền dữ liệu hiệu quả. Đọc/ghi nhanh hơn JSON hay XML, và tệp cũng nhỏ hơn. Được cả đôi đường.
Bên trong một mô hình ONNX gồm có:
Đồ thị: Kiến trúc mạng
Trọng số: Tham số đã huấn luyện
Siêu dữ liệu: Thông tin phiên bản, tên nhà sản xuất, và tài liệu mô hình
Phiên bản opset: Các phép toán khả dụng
Phiên bản opset rất quan trọng. ONNX phát triển theo thời gian và tiếp tục bổ sung, cải tiến các phép toán. Tệp mô hình chỉ rõ sử dụng opset nào để runtime biết cách thực thi.
Khái niệm và kiến trúc cốt lõi
ONNX biểu diễn mô hình của bạn như một đồ thị tính toán.
Đồ thị có các nút và cạnh. Nút là các phép toán (như nhân ma trận hay hàm kích hoạt), còn cạnh là các tensor (dữ liệu của bạn) chảy giữa những phép toán đó. Đây là cách ONNX mô tả "nhân các ma trận này, rồi áp dụng ReLU, rồi nhân tiếp".
Ví dụ đơn giản:
import onnx
from onnx import helper, TensorProto
# Create input and output tensors
input_tensor = helper.make_tensor_value_info("input", TensorProto.FLOAT,[1,3,224,224])
output_tensor = helper.make_tensor_value_info("output", TensorProto.FLOAT,[1,1000])# Create a node (operation)
node = helper.make_node("Relu",# Operation type["input"],# Input edges ["output"],# Output edges)# Create the graph
graph = helper.make_graph([node],# List of nodes"simple_model",# Graph name[input_tensor],# Inputs[output_tensor],# Outputs)# Create the model
model = helper.make_model(graph)
onnx.save(model,"simple_model.onnx")Được hỗ trợ bởi
Mỗi nút thực hiện một phép toán. Kiểu phép toán (như Relu, Conv, hoặc MatMul) đến từ bộ toán tử của ONNX. Bạn không thể tự đặt tên phép toán — chúng phải tồn tại trong opset bạn dùng.
Các cạnh nối các nút và mang tensor. Tensor có hình dạng (shape) và kiểu dữ liệu. Khi bạn định nghĩa [1, 3, 224, 224], tức là "tensor này có 4 chiều với các kích thước như vậy". Runtime dùng thông tin này để cấp phát bộ nhớ và kiểm tra hợp lệ của đồ thị.
Đồ thị chảy theo một hướng. Dữ liệu đi vào qua các nút đầu vào, đi qua các phép toán, và thoát ra qua các nút đầu ra. Không cho phép chu trình — ONNX không hỗ trợ kết nối hồi quy trực tiếp. Bạn cần unroll vòng lặp hoặc dùng các phép toán dành cho chuỗi.
Cấu trúc đồ thị cho phép khả năng tương tác giữa các framework. PyTorch thiên về thực thi eager, TensorFlow dùng đồ thị tĩnh, còn scikit-learn lại có trừu tượng khác. Nhưng tất cả đều có thể xuất ra cùng một định dạng đồ thị.
Đồ thị cũng cho phép tối ưu dùng chung. Bộ tối ưu ONNX có thể hợp nhất phép toán (gộp nhiều nút thành một), loại bỏ mã chết (xóa nút không dùng), hoặc thay thế phép toán chậm bằng tương đương nhanh hơn. Các tối ưu này hoạt động bất kể framework tạo mô hình.
Tiếp theo, tôi sẽ chỉ bạn chi tiết chuyển đổi mô hình sang định dạng ONNX.
Chuyển đổi mô hình sang định dạng ONNX
Trong phần này, tôi sẽ chỉ cách xuất mô hình từ PyTorch, TensorFlow và scikit-learn. Tôi sẽ dẫn bạn qua quy trình chuyển đổi và cách xác thực mô hình hoạt động đúng sau khi chuyển.
Hỗ trợ framework và công cụ
ONNX hỗ trợ hầu hết các framework học máy phổ biến.
PyTorch có xuất ONNX tích hợp qua torch.onnx.export(). Đây là bộ chuyển đổi trưởng thành nhất vì cả PyTorch và ONNX đều do Meta duy trì.
TensorFlow dùng tf2onnx để chuyển đổi. Đây là thư viện riêng chuyển đồ thị TensorFlow sang ONNX. Cài bằng uv add tf2onnx là dùng được.
scikit-learn chuyển đổi thông qua skl2onnx. Thư viện này xử lý các mô hình ML truyền thống như random forest, hồi quy tuyến tính và SVM. Framework học sâu được chú ý nhiều hơn, nhưng mô hình scikit-learn cũng hoạt động tốt.
Ba cái trên là phổ biến nhất, nhưng bạn có thể dùng bất kỳ framework được hỗ trợ nào khác — chỉ cần cài phụ thuộc qua uv add <name>:
Keras: Dùng tf2onnx (Keras là một phần của TensorFlow)
XGBoost: Dùng onnxmltools
LightGBM: Dùng onnxmltools
MATLAB: Xuất ONNX tích hợp sẵn
Paddle: Dùng paddle2onnx
Mỗi framework có bộ chuyển đổi riêng vì cách biểu diễn mô hình khác nhau. PyTorch dùng đồ thị tính toán động, TensorFlow dùng đồ thị tĩnh, còn scikit-learn không dùng đồ thị. Các bộ chuyển đổi dịch các biểu diễn này sang đồ thị của ONNX.
Bạn cũng có thể dùng mô hình do người khác huấn luyện. ONNX Model Zoo lưu trữ các mô hình tiền huấn luyện sẵn sàng sử dụng. Bạn sẽ tìm thấy mô hình thị giác máy tính (ResNet, YOLO, EfficientNet), NLP (BERT, GPT-2), và hơn thế nữa. Tải từ kho GitHub chính thức.
Hugging Face cũng lưu trữ mô hình ONNX. Tìm các mô hình có "onnx" trong tên hoặc lọc theo định dạng ONNX. Nhiều mô hình transformer phổ biến sẵn có ở định dạng ONNX đã được tối ưu cho suy luận.
Trước khi tiếp tục, chạy lệnh này để lấy mọi thư viện cần thiết bằng uv:
import torch
import torch.nn as nn
# Define a simple modelclassSimpleModel(nn.Module):def__init__(self):super(SimpleModel, self).__init__()
self.fc = nn.Linear(10,5)defforward(self, x):return self.fc(x)# Create and prepare the model
model = SimpleModel()
model.eval()# Set to evaluation mode# Create dummy input with the same shape as your real data
dummy_input = torch.randn(1,10)# Export to ONNX
torch.onnx.export(
model,# Model to export
dummy_input,# Example input"simple_model.onnx",# Output file
input_names=["input"],# Name for the input
output_names=["output"],# Name for the output
dynamic_shapes={"x":{0:"batch_size"}},# Allow variable batch size)Được hỗ trợ bởi
Mới dùng PyTorch? Đừng để điều đó cản trở bạn. Khóa học Deep Learning với PyTorch của chúng tôi sẽ giúp bạn nắm vững nền tảng chỉ trong vài giờ.
Dummy input rất quan trọng. ONNX cần biết shape đầu vào để xây đồ thị. Nếu mô hình của bạn chấp nhận chuỗi độ dài biến đổi hoặc kích thước batch khác nhau, hãy dùng dynamic_shapes để đánh dấu các chiều đó là động.
Luôn đặt mô hình vào chế độ đánh giá bằng model.eval() trước khi xuất. Điều này vô hiệu hóa hành vi huấn luyện của dropout và batch normalization. Nếu quên bước này, mô hình đã chuyển sẽ không khớp với bản gốc.
Dynamic shape dễ gây vấn đề. Nếu bộ chuyển đổi báo lỗi về kích thước không xác định, hãy đảm bảo bạn khai báo dynamic_shapes đúng hoặc cung cấp shape cụ thể.
Chuyển sang TensorFlow, đây là đoạn mã thay thế bạn có thể chạy:
import numpy as np
# Temporary compatibility fixes for tf2onnxifnothasattr(np,"object"):
np.object=objectifnothasattr(np,"cast"):
np.cast =lambda dtype: np.asarray
import tensorflow as tf
import tf2onnx
# Define a simple Keras model (same spirit as your PyTorch one)
inputs = tf.keras.Input(shape=(10,), name="input")
outputs = tf.keras.layers.Dense(5, name="output")(inputs)
model = tf.keras.Model(inputs, outputs)# Save the model (optional, if you want a .h5 file)
model.save("simple_tf_model.h5")# Define a dynamic input spec (None = dynamic batch dimension)
spec =(tf.TensorSpec((None,10), tf.float32, name="input"),)# Convert to ONNX
model_proto, _ = tf2onnx.convert.from_keras(
model, input_signature=spec, output_path="simple_tf_model.onnx")Được hỗ trợ bởi
Đáng tiếc, có chút xung đột với phiên bản Numpy mới nhất, nên tôi đã thêm bản vá tạm thời. Tại thời điểm bạn đọc, hy vọng bạn có thể chạy đoạn mã mà không cần sáu dòng đầu.
Và cuối cùng, hãy xem chuyển đổi ONNX cho các mô hình scikit-learn:
from sklearn.ensemble import RandomForestClassifier
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
# Train a simple model
model = RandomForestClassifier(n_estimators=10)
model.fit(X_train, y_train)# Define input type and shape
initial_type =[('float_input', FloatTensorType([None,4]))]# Convert to ONNX
onnx_model = convert_sklearn(model, initial_types=initial_type)# Save the modelwithopen("sklearn_model.onnx","wb")as f:
f.write(onnx_model.SerializeToString())Được hỗ trợ bởi
Giờ bạn có thể chạy đoạn mã này để xác thực mô hình đã chuyển:
import onnx
import onnxruntime as ort
import numpy as np
# Load and check the ONNX model
onnx_model = onnx.load("simple_model.onnx")
onnx.checker.check_model(onnx_model)# Run inference with ONNX Runtime
session = ort.InferenceSession("simple_model.onnx")
input_name = session.get_inputs()[0].name
# Create test input
test_input = np.random.randn(1,10).astype(np.float32)# Get prediction
onnx_output = session.run(None,{input_name: test_input})# Compare with original modelwith torch.no_grad():
original_output = model(torch.from_numpy(test_input))# Check if outputs match (within floating point tolerance)
np.testing.assert_allclose(
original_output.numpy(),
onnx_output[0],
rtol=1e-3,
atol=1e-5)print("Model conversion successful - outputs match!")Được hỗ trợ bởi
Hãy chạy xác thực này mỗi lần bạn chuyển đổi mô hình. Sai khác số học nhỏ là bình thường (toán dấu phẩy động không tuyệt đối), nhưng sai khác lớn nghĩa là có vấn đề trong quá trình chuyển đổi.
Để kết thúc, có hai lưu ý quan trọng bạn cần biết:
Phép toán tùy biến sẽ không chuyển đổi: Nếu bạn viết các phép toán PyTorch hay lớp TensorFlow tùy biến, bộ chuyển đổi không biết cách dịch chúng. Bạn cần viết lại bằng các phép toán chuẩn hoặc đăng ký toán tử ONNX tùy biến.
Không khớp phiên bản sẽ gây lỗi: Phiên bản framework, phiên bản bộ chuyển đổi và phiên bản ONNX phải tương thích. Kiểm tra tài liệu của bộ chuyển đổi để biết các tổ hợp phiên bản được hỗ trợ.
Tiếp theo, hãy bàn về ONNX Runtime.
Chạy suy luận với ONNX Runtime
Trong phần này, tôi sẽ chỉ bạn cách dùng ONNX Runtime để tải mô hình và tạo dự đoán. Tôi sẽ hướng dẫn các tùy chọn tăng tốc phần cứng và cho thấy ONNX Runtime tỏa sáng thế nào trong production.
Giới thiệu về ONNX Runtime
ONNX Runtime là động cơ suy luận đa nền tảng. Microsoft phát triển nó để chạy mô hình ONNX nhanh trên mọi phần cứng — CPU, GPU, chip di động và các bộ gia tốc AI chuyên dụng.
Cách nó hoạt động: Bạn tải mô hình ONNX, và ONNX Runtime xây dựng kế hoạch thực thi. Nó phân tích đồ thị tính toán, áp dụng tối ưu, và tìm cách tốt nhất để thực thi các phép toán trên phần cứng của bạn. Sau đó nó chạy suy luận theo kế hoạch đã tối ưu.
Kiến trúc tách rời logic thực thi và mã phụ thuộc phần cứng. Execution provider xử lý giao tiếp phần cứng. Muốn chạy trên GPU NVIDIA? Dùng CUDA execution provider. Muốn Apple Silicon? Mã nền của bạn vẫn giữ nguyên.
ONNX Runtime có các execution provider sau:
CPUExecutionProvider: Mặc định, hoạt động ở mọi nơi
CUDAExecutionProvider: GPU NVIDIA với CUDA
TensorRTExecutionProvider: GPU NVIDIA với tối ưu TensorRT
CoreMLExecutionProvider: Thiết bị Apple (macOS, iOS)
DmlExecutionProvider: DirectML cho Windows (hoạt động với mọi GPU)
OpenVINOExecutionProvider: CPU và GPU Intel
ROCMExecutionProvider: GPU AMD
Mỗi provider dịch các phép toán ONNX thành chỉ thị đặc thù phần cứng. Provider CPU dùng phép toán CPU tiêu chuẩn. Provider CUDA dùng cuDNN và cuBLAS. Provider TensorRT biên dịch mô hình thành các kernel GPU tối ưu, v.v.
Thiết lập và thực thi mô hình
Bạn có thể tải mô hình ONNX với ba dòng mã:
import onnxruntime as ort
# Create an inference session
session = ort.InferenceSession("simple_model.onnx")# Get input and output names
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].nameĐược hỗ trợ bởi
InferenceSession làm hết phần nặng. Nó tải mô hình, xác thực đồ thị và chuẩn bị chạy suy luận. Mặc định, nó dùng provider CPU.
Giờ hãy chạy suy luận:
import numpy as np
import onnxruntime as ort
# Create an inference session
session = ort.InferenceSession("simple_model.onnx")# Get input and output names
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
# Prepare input data
input_data = np.random.randn(1,10).astype(np.float32)# Run inference
outputs = session.run([output_name],{input_name: input_data},)# Get predictions
predictions = outputs[0]print(predictions.shape)Được hỗ trợ bởi
Phương thức run() nhận hai tham số. Đầu tiên là danh sách tên đầu ra (hoặc None để lấy mọi đầu ra). Thứ hai là từ điển ánh xạ tên đầu vào tới mảng numpy.
Shape đầu vào phải khớp. Nếu mô hình mong đợi [1, 10] mà bạn truyền [1, 10, 3], suy luận sẽ thất bại.
Nếu muốn tăng tốc bằng GPU, chỉ cần chỉ định provider khi tạo session:
# For NVIDIA GPUs with CUDA
session = ort.InferenceSession("model.onnx",
providers=['CUDAExecutionProvider','CPUExecutionProvider'])# For Apple Silicon
session = ort.InferenceSession("model.onnx",
providers=['CoreMLExecutionProvider','CPUExecutionProvider'])# For Intel hardware
session = ort.InferenceSession("model.onnx",
providers=['OpenVINOExecutionProvider','CPUExecutionProvider'])Được hỗ trợ bởi
Liệt kê provider theo thứ tự ưu tiên. ONNX Runtime thử provider đầu tiên, nếu không có sẽ rơi xuống provider tiếp theo. Luôn bao gồm CPUExecutionProvider làm dự phòng cuối cùng.
Bạn có thể kiểm tra provider nào thực sự được dùng:
import numpy as np
import onnxruntime as ort
# Create an inference session
session = ort.InferenceSession("simple_model.onnx", providers=["CoreMLExecutionProvider","CPUExecutionProvider"])# Get input and output names
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
# Prepare input data
input_data = np.random.randn(1,10).astype(np.float32)# Run inference
outputs = session.run([output_name],{input_name: input_data},)# Get predictions
predictions = outputs[0]print(predictions.shape)print("Providers:")print(session.get_providers())Được hỗ trợ bởi
Nếu bạn thấy ["CPUExecutionProvider"] trong khi mong GPU, nghĩa là provider GPU không khả dụng. Lý do thường gặp: thiếu driver, cài sai gói ONNX Runtime, hoặc GPU không tương thích.
Cấu hình chưa dừng ở đây. Bạn có thể tối ưu provider hơn nữa để có hiệu năng tốt hơn:
providers =[("TensorRTExecutionProvider",{"device_id":0,"trt_max_workspace_size":2147483648,# 2GB"trt_fp16_enable":True,# Use FP16 precision},),"CUDAExecutionProvider","CPUExecutionProvider",]
session = ort.InferenceSession("simple_model.onnx", providers=providers)Được hỗ trợ bởi
Điểm ấn tượng của ONNX Runtime là nó chạy ở mọi nơi — máy chủ, trình duyệt và thiết bị di động.
ONNX Runtime Web mang suy luận lên trình duyệt bằng WebAssembly và WebGL. Mô hình chạy phía client mà không gửi dữ liệu lên máy chủ. Rất phù hợp cho các ứng dụng nhạy cảm về quyền riêng tư như phân tích ảnh y tế hoặc xử lý tài liệu.
Đây là ví dụ đơn giản cách dùng ONNX Runtime trong một tệp HTML:
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>ONNXRuntime Web Demo</title></head><body><h1>ONNXRuntime Web Demo</h1><!-- Load ONNXRuntime-Web from CDN --><scriptsrc="<https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js>"></script><script>asyncfunctionrunModel(){// Create inference sessionconst session =await ort.InferenceSession.create('./simple_model.onnx');// Prepare inputconst inputData =newFloat32Array(10).fill(0).map(()=> Math.random());const tensor =newort.Tensor('float32', inputData,[1,10]);// Run inferenceconst results =await session.run({input: tensor });const output = results.output;
console.log('Output shape:', output.dims);
console.log('Output values:', output.data);}runModel();</script></body></html>Được hỗ trợ bởi
Với triển khai di động, bạn có thể dùng ONNX Runtime Mobile — phiên bản gọn nhẹ tối ưu cho iOS và Android. Nó lược bỏ các tính năng máy chủ không cần thiết và giảm kích thước nhị phân.
Đây là ví dụ cấu hình iOS:
import onnxruntime_objc
let modelPath =Bundle.main.path(forResource:"simple_model", ofType:"onnx")!let session =tryORTSession(modelPath: modelPath)let inputData =// Your input as Datalet inputTensor =tryORTValue(tensorData: inputData,
elementType:.float,
shape:[1,10])let outputs =try session.run(withInputs:["input": inputTensor])Được hỗ trợ bởi
Hoặc, nếu bạn dùng Python để xây REST API, bạn có thể đưa ONNX Runtime vào một endpoint như sau:
from fastapi import FastAPI
import onnxruntime as ort
import numpy as np
app = FastAPI()
session = ort.InferenceSession("simple_model.onnx")@app.post("/predict")asyncdefpredict(data:dict):
input_data = np.array(data["input"]).astype(np.float32)
outputs = session.run(None,{"input": input_data})return{"prediction": outputs[0].tolist()}Được hỗ trợ bởi
Vậy là xong! Các môi trường khác cũng dùng cách tiếp cận tương tự, nhưng bạn đã nắm ý chính — ONNX Runtime dễ dùng. Tiếp theo, hãy bàn về tối ưu.
Tối ưu hóa mô hình ONNX
Trong phần này, tôi sẽ minh họa cách làm mô hình ONNX nhanh hơn và nhỏ hơn thông qua lượng tử hóa và tối ưu đồ thị. Tôi sẽ hướng dẫn các kỹ thuật quan trọng nhất cho triển khai thực tế.
Kỹ thuật lượng tử hóa
Lượng tử hóa giảm độ chính xác của mô hình để làm nó nhanh hơn và nhỏ hơn.
Thay vì lưu trọng số ở số thực 32-bit, bạn lưu chúng ở số nguyên 8-bit. Điều này giảm 75% bộ nhớ và tăng tốc suy luận vì toán học số nguyên nhanh hơn dấu phẩy động trên hầu hết phần cứng.
Lượng tử hóa động chuyển trọng số sang độ chính xác thấp hơn nhưng giữ activation (giá trị trung gian khi suy luận) ở độ chính xác đầy đủ. Đây là phương pháp đơn giản nhất vì không cần dữ liệu hiệu chỉnh.
Cách áp dụng lượng tử hóa động:
from onnxruntime.quantization import quantize_dynamic, QuantType
# Quantize the model
quantize_dynamic(
model_input="model.onnx",
model_output="model_quantized.onnx",
weight_type=QuantType.QUInt8 # 8-bit unsigned integers)Được hỗ trợ bởi
Vậy là xong. Mô hình đã lượng tử dùng số nguyên 8-bit không dấu và sẵn sàng sử dụng.
Lượng tử hóa tĩnh chuyển cả trọng số và activation sang độ chính xác thấp hơn. Nó mạnh mẽ và nhanh hơn lượng tử hóa động, nhưng bạn cần dữ liệu hiệu chỉnh đại diện để đo dải giá trị của activation.
Chiến lược tổng quát để áp dụng lượng tử hóa tĩnh:
from onnxruntime.quantization import quantize_static, CalibrationDataReader
import numpy as np
# Create a calibration data readerclassDataReader(CalibrationDataReader):def__init__(self, calibration_data):
self.data = calibration_data
self.index =0defget_next(self):if self.index >=len(self.data):returnNone
batch ={"input": self.data[self.index]}
self.index +=1return batch
# Load calibration data (100-1000 samples from your dataset)
calibration_data =[np.random.randn(1,10).astype(np.float32)for _ inrange(100)]
data_reader = DataReader(calibration_data)# Quantize
quantize_static(
model_input="model.onnx",
model_output="model_static_quantized.onnx",
calibration_data_reader=data_reader
)Được hỗ trợ bởi
Dữ liệu hiệu chỉnh phải đại diện cho khối lượng công việc suy luận thực tế của bạn, nên đừng dùng dữ liệu ngẫu nhiên như tôi minh họa ở trên. Hãy dùng mẫu thực từ bộ dữ liệu của bạn.
Nhìn chung, lượng tử hóa có một số lợi ích và đánh đổi bạn cần hiểu với tư cách kỹ sư học máy. Đây là lợi ích:
Mô hình nhỏ hơn 4 lần: INT8 dùng 1/4 bộ nhớ so với FP32
Suy luận nhanh hơn 2-4 lần: Toán số nguyên chạy nhanh hơn trên CPU
Tiêu thụ điện năng thấp hơn: Ít tính toán hơn đồng nghĩa ít năng lượng hơn
Tận dụng cache tốt hơn: Mô hình nhỏ vừa cache CPU
Và các đánh đổi:
Sụt độ chính xác nhẹ: Thường 1-2% với INT8, chấp nhận được với đa số ứng dụng
Không phải phép toán nào cũng lượng tử hóa tốt: Batch normalization và softmax khá khó
Phụ thuộc phần cứng: Tăng tốc trên GPU thay đổi, một số GPU không tăng tốc INT8
Lượng tử hóa cũng tỏa sáng với mô hình ngôn ngữ lớn và AI sinh tạo. Mô hình 7B tham số ở FP32 cần 28GB bộ nhớ. Lượng tử INT8 còn 7GB. Lượng tử INT4 còn 3.5GB — đủ nhỏ để chạy trên phần cứng người dùng.
Tối ưu đồ thị
Tối ưu đồ thị viết lại đồ thị tính toán của mô hình để chạy nhanh hơn.
Khái niệm hợp nhất nút kết hợp nhiều phép toán thành một. Nếu mô hình có lớp Conv rồi BatchNorm rồi ReLU, bộ tối ưu sẽ hợp nhất thành một nút ConvBnRelu. Điều này giảm lưu chuyển bộ nhớ và tăng tốc thực thi.
Có hai khái niệm nữa bạn cần biết khi tối ưu đồ thị:
Constant folding tính trước các phép toán không phụ thuộc vào dữ liệu đầu vào. Nếu mô hình nhân ma trận trọng số với hằng số, bộ tối ưu sẽ thực hiện phép nhân này một lần khi tối ưu thay vì ở mỗi lần suy luận.
Dead code elimination loại bỏ các phép toán không dùng. Nếu mô hình có nhánh không bao giờ chạy hoặc đầu ra bạn không dùng, bộ tối ưu sẽ cắt bỏ.
Cách bật tối ưu đồ thị trong ONNX Runtime:
import onnxruntime as ort
# Set optimization level
sess_options = ort.SessionOptions()
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
# Create session with optimizations
session = ort.InferenceSession("simple_model.onnx",
sess_options,
providers=["CPUExecutionProvider"])Được hỗ trợ bởi
ONNX Runtime có các mức tối ưu sau:
ORT_DISABLE_ALL: Không tối ưu (hữu ích khi debug)
ORT_ENABLE_BASIC: Tối ưu an toàn, không thay đổi kết quả số học
ORT_ENABLE_EXTENDED: Tối ưu mạnh có thể có sai khác số học nhỏ
ORT_ENABLE_ALL: Tất cả tối ưu, bao gồm biến đổi bố cục
Dùng ORT_ENABLE_ALL cho production. Mức tăng tốc xứng đáng với sai khác số học rất nhỏ.
Bạn có thể chạy đoạn mã này để lưu mô hình đã tối ưu dùng lại sau:
sess_options = ort.SessionOptions()
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
sess_options.optimized_model_filepath ="model_optimized.onnx"# This creates and saves the optimized model
session = ort.InferenceSession("simple_model.onnx", sess_options)Được hỗ trợ bởi
Tối ưu đồ thị hoạt động bất kể framework tạo mô hình. Mẫu Conv-BN-ReLU của PyTorch và Conv-BN-ReLU của TensorFlow đều được hợp nhất theo cách giống nhau. Đây là lợi ích của tối ưu dùng chung — bạn viết tối ưu một lần và áp dụng cho mô hình từ bất kỳ framework nào.
Tiếp theo, hãy bàn về triển khai.
Kịch bản triển khai
Trong phần này, tôi sẽ điểm qua ba kịch bản triển khai — thiết bị biên, hạ tầng đám mây và trình duyệt web. Tôi sẽ chỉ ra thách thức và giải pháp cho từng môi trường.
Triển khai trên thiết bị biên
Thách thức cốt lõi của thiết bị biên là tài nguyên hạn chế.
Điện thoại của bạn có 4-16GB RAM. Raspberry Pi còn ít hơn. Thiết bị IoT có thể chỉ 512MB hoặc ít hơn. Bạn không thể ném một mô hình 7B tham số vào các thiết bị này và mong nó chạy được.
Bắt đầu với lượng tử hóa. INT8 nên là tiêu chuẩn cơ bản của bạn. Thậm chí dùng INT4 nếu bạn chịu được sụt độ chính xác. Như vậy mô hình đủ nhỏ để vừa bộ nhớ.
Khi triển khai trên thiết bị biên, ONNX Runtime Mobile là trợ thủ. Nó lược bỏ tính năng máy chủ không cần thiết và tối ưu cho pin. Nhị phân nhỏ hơn, khởi động nhanh hơn, và tiêu thụ điện thấp hơn.
Cách thêm nó vào dự án ứng dụng di động của bạn:
# iOS
pod 'onnxruntime-mobile-objc'
# Android
implementation 'com.microsoft.onnxruntime:onnxruntime-android:latest.version'Được hỗ trợ bởi
Execution provider xử lý khác biệt phần cứng. Thiết bị Android dùng CPU ARM, một số có GPU từ các nhà cung cấp khác nhau, và điện thoại mới có NPU (Neural Processing Unit). Bạn không muốn viết mã cho từng con chip.
Tham khảo đoạn mã sau để chọn provider phù hợp:
# iOS - use CoreML for Apple Silicon optimization
session = ort.InferenceSession("simple_model.onnx",
providers=["CoreMLExecutionProvider","CPUExecutionProvider"])# Android - use NNAPI for hardware acceleration
session = ort.InferenceSession("simple_model.onnx",
providers=["NnapiExecutionProvider","CPUExecutionProvider"])Được hỗ trợ bởi
CoreML trên iOS dùng Neural Engine (NPU của Apple). NNAPI trên Android dùng bộ gia tốc sẵn có — GPU, DSP, hoặc NPU. Provider trừu tượng hóa phần cứng để mã của bạn giữ nguyên. Khi có chip mới mạnh hơn, chỉ cần cập nhật provider và ứng dụng chạy nhanh hơn mà không cần đổi mã.
Triển khai trên đám mây
Triển khai trên đám mây mang đến khả năng mở rộng không giới hạn và phần cứng mới nhất.
Bạn có thể triển khai mô hình ONNX trên bất kỳ nền tảng đám mây nào — AWS, Azure, GCP, hoặc máy chủ riêng. Mẫu triển khai giống nhau — đóng gói dịch vụ suy luận trong container và đặt sau load balancer.
Ví dụ nhỏ với Python FastAPI:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import onnxruntime as ort
import numpy as np
import logging
app = FastAPI()
logger = logging.getLogger(__name__)# Load model at startup
session =None@app.on_event("startup")asyncdefload_model():global session
session = ort.InferenceSession("simple_model.onnx",
providers=["CUDAExecutionProvider","CoreMLExecutionProvider","CPUExecutionProvider",],)
logger.info("Model loaded successfully")classPredictionRequest(BaseModel):input:listclassPredictionResponse(BaseModel):
prediction:list
inference_time_ms:float@app.post("/predict", response_model=PredictionResponse)asyncdefpredict(request: PredictionRequest):try:import time
start = time.time()# Input shape: (1, 10)
input_data = np.array(request.input, dtype=np.float32).reshape(1,10)
input_name = session.get_inputs()[0].name
outputs = session.run(None,{input_name: input_data})
inference_time =(time.time()- start)*1000return PredictionResponse(
prediction=outputs[0].tolist(), inference_time_ms=inference_time
)except Exception as e:
logger.error(f"Prediction failed: {str(e)}")raise HTTPException(status_code=500, detail=str(e))@app.get("/health")asyncdefhealth():return{"status":"healthy","model_loaded": session isnotNone}Được hỗ trợ bởi
Ví dụ FastAPI này, dù nhỏ, không phải dễ hiểu nhất với các kỹ sư học máy. Bạn có thể nắm vững nền tảng với khóa Introduction to FastAPI miễn phí của chúng tôi.
Sau đó bạn có thể đóng gói bằng Docker:
FROM python:3.13-slimWORKDIR /app# Install dependenciesRUN pip install onnx onnxruntime onnxscript fastapi pydantic numpy uvicorn# Copy model and codeCOPY simple_model.onnx .COPY main.py .# Expose portEXPOSE 8000# Run the serviceCMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]Được hỗ trợ bởi
Mọi người yêu ML đều nên biết Docker cơ bản. Hãy học khóa Introduction to Docker để cuối cùng triển khai được một mô hình học máy.
Bạn sẽ có ứng dụng chạy ở cổng 8000, nơi bạn có thể mở tài liệu trực tiếp và thử endpoint dự đoán:
Nếu bạn dùng Azure Machine Learning, bạn sẽ vui khi biết nó tích hợp trực tiếp với ONNX. Bạn có thể triển khai mô hình trong ba bước:
from azureml.core import Workspace, Model
from azureml.core.webservice import AciWebservice, Webservice
# Register model
ws = Workspace.from_config()
model = Model.register(
workspace=ws, model_path="simple_model.onnx", model_name="my-onnx-model")# Deploy
deployment_config = AciWebservice.deploy_configuration(cpu_cores=1, memory_gb=1)
service = Model.deploy(
workspace=ws,
name="onnx-service",
models=[model],
deployment_config=deployment_config,)Được hỗ trợ bởi
Azure xử lý mở rộng, giám sát và cập nhật. Bạn có log tự động, truy vết yêu cầu và kiểm tra sức khỏe.
Khi nói về quy trình MLOps cần các thành phần sau:
Quản lý phiên bản: Theo dõi phiên bản mô hình với DVC hoặc MLflow
CI/CD: Tự động hóa kiểm thử và triển khai với GitHub Actions hoặc Azure Pipelines
Giám sát: Theo dõi độ trễ suy luận, thông lượng và tỷ lệ lỗi
A/B testing: So sánh phiên bản mô hình trong production
Rollback: Quay lại phiên bản trước khi gặp sự cố
Đây là ví dụ bạn có thể dùng:
import prometheus_client as prom
import onnxruntime as ort
import numpy as np
# Define metrics
inference_duration = prom.Histogram("model_inference_duration_seconds","Time spent processing inference")
inference_count = prom.Counter("model_inference_total","Total number of inferences")# Load model
session = ort.InferenceSession("simple_model.onnx")@app.post("/predict")@inference_duration.time()asyncdefpredict(request: PredictionRequest):
inference_count.inc()# Input shape: (1, 10)
input_data = np.array(request.input, dtype=np.float32).reshape(1,10)
input_name = session.get_inputs()[0].name
outputs = session.run(None,{input_name: input_data})return{"prediction": outputs[0].tolist()}Được hỗ trợ bởi
Từ đó, bạn có thể mở rộng theo chiều ngang bằng cách chạy nhiều phiên bản dịch vụ sau load balancer. ONNX Runtime là stateless, nên bất kỳ phiên bản nào cũng xử lý được yêu cầu bất kỳ.
Suy luận trên trình duyệt
ONNX Runtime Web chạy mô hình trực tiếp trong trình duyệt bằng WebAssembly và WebGL.
Thay vì gửi dữ liệu lên máy chủ, bạn gửi mô hình đến trình duyệt một lần và chạy suy luận cục bộ. Người dùng có phản hồi tức thì không phải chờ độ trễ mạng.
Để bắt đầu, chỉ cần tạo tệp HTML dùng script JS inline:
Bạn có thể xem đầu ra trong console ngay khi mở tệp bằng live server:
Và cuối cùng, hãy bàn về vài chủ đề nâng cao.
Chủ đề nâng cao
Các phép toán ONNX tiêu chuẩn bao phủ hầu hết trường hợp, nhưng nếu bạn cần nhiều hơn, đây là phần dành cho bạn.
Tôi sẽ đề cập đến toán tử tùy biến và triển khai mô hình ngôn ngữ lớn. Tôi sẽ giải thích khi nào bạn cần các tính năng nâng cao này và những đánh đổi đi kèm.
Toán tử tùy biến
ONNX đi kèm hàng trăm phép toán, nhưng mô hình của bạn có thể dùng thứ gì đó độc đáo.
Toán tử tùy biến cho phép bạn định nghĩa các phép toán không tồn tại trong bộ toán tử chuẩn của ONNX. Bạn tự viết logic phép toán và chỉ cho ONNX Runtime cách thực thi. Điều này mở rộng khả năng của ONNX vượt ngoài những gì tích hợp sẵn.
Khi nào cần toán tử tùy biến? Khi bạn dùng nghiên cứu mới nhất mà ONNX chưa hỗ trợ. Hoặc khi bạn xây các phép toán độc quyền cho lĩnh vực của bạn. Hoặc khi bạn cần tối ưu đặc thù phần cứng mà các phép toán chuẩn không đáp ứng.
Nhưng có điều — toán tử tùy biến làm mất tính di động. Mô hình của bạn sẽ không chạy trên hệ thống không có triển khai toán tử tùy biến của bạn. Nếu bạn xuất mô hình PyTorch với toán tử tùy biến sang ONNX, bạn cần đóng gói các toán tử đó riêng và đăng ký với ONNX Runtime.
Triển khai trở nên khó hơn. Mọi môi trường đều cần thư viện toán tử tùy biến của bạn. Thiết bị biên, máy chủ đám mây và trình duyệt đều cần cùng một triển khai. Bạn đánh mất lợi thế lớn nhất của ONNX: viết một lần, chạy mọi nơi.
Đây là ví dụ nhỏ hoạt động về toán tử tùy biến, để bạn thấy cách chúng hoạt động:
import numpy as np
import onnxruntime as ort
from onnxruntime import InferenceSession, SessionOptions
# Define the custom operationdefcustom_square(x):return x * x
# Register the custom op with ONNX Runtime# This tells ONNX Runtime how to execute "CustomSquare" operationsclassCustomSquareOp:@staticmethoddefforward(x):return np.square(x).astype(np.float32)# Create a simple ONNX model with a custom op using the helperfrom onnx import helper, TensorProto
import onnx
# Create a graph with custom operator
node = helper.make_node("CustomSquare",# Custom operation name["input"],["output"],
domain="custom.ops",# Custom domain)
graph = helper.make_graph([node],"custom_op_model",[helper.make_tensor_value_info("input", TensorProto.FLOAT,[1,10])],[helper.make_tensor_value_info("output", TensorProto.FLOAT,[1,10])],)
model = helper.make_model(graph)
onnx.save(model,"custom_op_model.onnx")# To use this model, you'd need to implement the custom op in C++# and register it with ONNX Runtime - Python-only custom ops aren't supportedprint("Custom operator model created - requires C++ implementation to run")Được hỗ trợ bởi
Hầu hết các đội ngũ tránh dùng toán tử tùy biến nếu có thể. Hãy thử dùng tổ hợp các phép toán chuẩn trước. Chỉ dùng tùy biến khi phép toán chuẩn không thể biểu đạt thao tác của bạn hoặc khi hiệu năng đòi hỏi.
Xử lý mô hình ngôn ngữ lớn
Bộ nhớ là vấn đề với LLM. Mô hình 7B tham số cần 28GB ở FP32, 14GB ở FP16, và 7GB ở INT8. Hầu hết phần cứng người dùng không chứa nổi. Bạn cần lượng tử hóa, và thường là lượng tử mạnh đến INT4 hoặc INT3.
Quản lý bộ đệm KV rất quan trọng cho sinh tự hồi quy. Mỗi token sinh ra cần tham chiếu tất cả token trước đó. Bộ đệm tăng theo độ dài chuỗi. Cuộc hội thoại dài ngốn bộ nhớ nhanh. ONNX Runtime GenAI có xử lý bộ đệm KV tối ưu, tái sử dụng bộ nhớ và giảm overhead.
Tốc độ suy luận phụ thuộc phần cứng và kích thước mô hình. Mô hình nhỏ (dưới 3B tham số) chạy tốt trên GPU người dùng. Mô hình lớn cần nhiều GPU hoặc bộ gia tốc suy luận chuyên dụng. ONNX Runtime hỗ trợ suy luận đa GPU thông qua provider, nhưng bạn cần tự chia mô hình qua các thiết bị.
Flash Attention tăng tốc cơ chế attention trong transformer. Attention chuẩn có độ phức tạp O(n²) theo độ dài chuỗi — chậm rất nhanh. Flash Attention giảm di chuyển bộ nhớ và cải thiện tốc độ mà không thay đổi kết quả. ONNX Runtime bao gồm tối ưu Flash Attention cho phần cứng được hỗ trợ.
Huấn luyện LLM với ONNX Runtime là có thể nhưng hiếm. Hầu hết đội ngũ huấn luyện bằng PyTorch hoặc JAX, rồi xuất sang ONNX chỉ để suy luận. ONNX Runtime Training tồn tại cho fine-tuning và tiếp tục tiền huấn luyện, nhưng hệ sinh thái xung quanh huấn luyện bằng PyTorch trưởng thành hơn.
Tải công việc AI sinh tạo được ưu tiên đặc biệt trong ONNX Runtime. Phần mở rộng GenAI cung cấp API cấp cao cho sinh văn bản, duy trì trạng thái giữa các lần gọi, và quản lý beam search hoặc chiến lược sampling. Điều này giúp bạn không phải tự cài đặt logic sinh.
Thế giới LLM thay đổi nhanh, và ONNX Runtime cố gắng cập nhật. Bộ toán tử và tối ưu được cập nhật thường xuyên, nhưng luôn có độ trễ giữa nghiên cứu và hỗ trợ sẵn sàng production.
Kết luận
Giờ bạn đã biết cách chuyển đổi mô hình từ bất kỳ framework nào, tối ưu cho production và triển khai ở bất kỳ đâu.
ONNX loại bỏ ma sát giữa huấn luyện và triển khai. Huấn luyện với PyTorch vì nó tuyệt vời cho nghiên cứu. Triển khai với ONNX Runtime vì nó nhanh và chạy mọi nơi. Lượng tử hóa xuống INT8 để giảm một nửa chi phí suy luận. Chuyển từ CPU sang GPU mà không đổi mã. Di chuyển mô hình giữa các nhà cung cấp đám mây mà không bị khóa nhà cung cấp.
ONNX là công cụ khiến điều đó trở thành hiện thực.
Hệ sinh thái không ngừng cải thiện. Hỗ trợ AI sinh tạo tốt hơn qua từng bản phát hành — suy luận LLM nhanh hơn, quản lý bộ nhớ tốt hơn, và kỹ thuật tối ưu mới. Hỗ trợ phần cứng mở rộng đến nhiều bộ gia tốc và thiết bị biên hơn. Cộng đồng xây dựng công cụ giúp ONNX dễ dùng hơn. Bạn có thể tham gia bằng cách thử tính năng mới, báo lỗi, hoặc đóng góp mã qua các kho GitHub.
Chuyên gia Khoa học Dữ liệu Cấp cao, làm việc tại Croatia. Tác giả Công nghệ Hàng đầu với hơn 700 bài viết đã xuất bản, đạt trên 10 triệu lượt xem. Tác giả cuốn sách Tự động hóa Machine Learning với TPOT.
Câu hỏi thường gặp
ONNX là gì và vì sao tôi nên dùng?⌄
ONNX (Open Neural Network Exchange) là một định dạng phổ quát cho các mô hình học máy, hoạt động xuyên suốt các framework khác nhau. Nó cho phép bạn huấn luyện mô hình trong PyTorch hoặc TensorFlow và triển khai ở bất kỳ đâu mà không cần viết lại mã. Bạn có được tính độc lập với framework, hiệu năng tốt hơn nhờ tối ưu hóa, và khả năng chạy mô hình trên mọi phần cứng, từ thiết bị biên đến máy chủ đám mây.
ONNX cải thiện hiệu năng mô hình như thế nào?⌄
ONNX Runtime áp dụng các tối ưu đồ thị như hợp nhất nút và constant folding để tăng tốc suy luận. Lượng tử hóa giảm kích thước mô hình tới 75% và giúp suy luận nhanh hơn 2-4 lần bằng cách chuyển trọng số từ số thực 32-bit sang số nguyên 8-bit. Các tối ưu này hoạt động tự động và không yêu cầu bạn tinh chỉnh mô hình thủ công.
ONNX có chạy được trên thiết bị di động và trình duyệt không?⌄
Có. ONNX Runtime Mobile chạy trên thiết bị iOS và Android với tối ưu cho pin và sử dụng bộ nhớ. ONNX Runtime Web chạy mô hình trực tiếp trong trình duyệt bằng WebAssembly và WebGL, vì vậy bạn có thể suy luận phía client mà không gửi dữ liệu lên máy chủ. Cả hai tùy chọn đều hỗ trợ tăng tốc phần cứng thông qua các execution provider như CoreML cho thiết bị Apple và NNAPI cho Android.
Sự khác nhau giữa lượng tử hóa động và tĩnh là gì?⌄
Lượng tử hóa động chuyển trọng số sang độ chính xác thấp hơn nhưng giữ activation ở độ chính xác đầy đủ, và không cần dữ liệu hiệu chỉnh. Lượng tử hóa tĩnh chuyển cả trọng số và activation sang độ chính xác thấp hơn, nhanh hơn nhưng cần dữ liệu hiệu chỉnh đại diện để đo dải giá trị của activation. Lượng tử hóa tĩnh cho hiệu năng tốt hơn nhưng cần chuẩn bị nhiều hơn.
Tôi xử lý các mô hình có phép toán tùy biến khi chuyển sang ONNX thế nào?⌄
Các phép toán tùy biến cần triển khai bằng C++ và đăng ký với ONNX Runtime — toán tử chỉ có Python không được hỗ trợ cho production. Bạn cần triển khai toán tử tùy biến bằng C++, biên dịch thành thư viện dùng chung, và đăng ký với ONNX Runtime trong mọi môi trường triển khai. Hầu hết các đội ngũ tránh dùng toán tử tùy biến nếu có thể và dùng tổ hợp các phép toán ONNX tiêu chuẩn, vì toán tử tùy biến làm mất tính di động giữa các nền tảng.
Bạn đã huấn luyện một mô hình trong PyTorch, nhưng hệ thống production lại chạy TensorFlow. Có cần viết lại mọi thứ từ đầu không?
Không, bạn không cần.
Khả năng tương tác giữa các mô hình có thể khiến kỹ sư học máy đau đầu. Mỗi framework dùng một định dạng khác nhau, và việc chuyển đổi giữa chúng có thể làm hỏng mô hình hoặc gây ra hành vi không mong muốn. Bạn không muốn điều đó, nhưng cũng không muốn duy trì nhiều phiên bản của cùng một mô hình.
ONNX (Open Neural Network Exchange) giải quyết vấn đề bằng cách cung cấp một định dạng phổ quát cho các mô hình học máy. Nó cho phép bạn huấn luyện trên một framework và triển khai trên framework khác mà không cần viết lại mã hay xử lý lỗi vặt.
Trong bài viết này, tôi sẽ hướng dẫn bạn cách chuyển đổi mô hình sang định dạng ONNX, chạy suy luận với ONNX Runtime, tối ưu mô hình cho production và triển khai trên nhiều nền tảng, từ thiết bị biên đến máy chủ đám mây.
Bắt đầu với ONNX
Bạn không thể dùng ONNX nếu chưa thiết lập môi trường phát triển.
Trong phần này, tôi sẽ hướng dẫn từng bước bạn cần — từ yêu cầu phần mềm đến tạo workspace sạch, có thể tái lập. Tôi sẽ chỉ bạn cách cài ONNX và ONNX Runtime trên Windows, Linux và macOS.
Điều kiện tiên quyết
ONNX hoạt động trên mọi hệ điều hành phổ biến.
Bạn cần cài Python 3.8 trở lên trên máy. Vậy là đủ cho phần cơ bản. ONNX hỗ trợ
ABI3(Application Binary Interface) của Python, nghĩa là bạn có thể dùng các gói nhị phân dựng sẵn mà không phải biên dịch từ mã nguồn.Bạn sẽ cài đặt những thứ sau:
uv(tải từ astral.sh/uv)Python 3.8+ (
uvxử lý cho bạn)pip(đi kèm vớiuv)Nếu bạn muốn tìm hiểu thêm uv hoạt động thế nào và vì sao nên dùng nó, hãy đọc hướng dẫn về trình quản lý gói Python nhanh nhất.
Với người dùng Windows, bạn có thể cài uv bằng PowerShell:
Nếu gặp lỗi chính sách thực thi, hãy chạy PowerShell với quyền quản trị trước.
Người dùng Linux có thể cài uv bằng lệnh shell sau:
Lệnh này hoạt động trên mọi bản phân phối Linux.
Cuối cùng, người dùng macOS có thể cài
uvqua Homebrew, hoặc bằng lệnh curl:Thiết lập môi trường
Cách thiết lập một dự án với uv:
uv tạo thư mục
.venvvà tệppyproject.toml. Môi trường ảo được kích hoạt tự động khi bạn chạy lệnh qua uv.Giờ hãy cài ONNX và ONNX Runtime:
Các lệnh kiểm tra sẽ in ra số phiên bản:
Các phụ thuộc đã được cố định phiên bản.
uvtự động ghi tất cả phiên bản gói vàouv.lock. Điều này giúp build có thể tái lập — ai cũng có thể tạo lại đúng môi trường của bạn bằng cách chạyuv sync.Đây là nội dung
pyproject.tomlcủa bạn:Vậy là xong, môi trường đã được cấu hình! Hãy tiếp tục với những kiến thức nền tảng.
Tìm hiểu những điều cơ bản về ONNX
Bạn cần hiểu ONNX hoạt động thế nào trước khi bắt đầu chuyển đổi mô hình.
Trong phần này, tôi sẽ phân tích định dạng ONNX và giải thích kiến trúc dựa trên đồ thị của nó. Tôi sẽ chỉ bạn bên trong một tệp ONNX có gì và dữ liệu chảy qua nó ra sao.
Định dạng mô hình ONNX
Một mô hình ONNX là tệp chứa cấu trúc và trọng số của mạng nơ-ron.
Hãy coi nó như một bản vẽ thiết kế. Tệp mô tả cần thực hiện những phép toán nào, theo thứ tự nào, và với tham số gì. Trọng số đã huấn luyện được lưu kèm bản thiết kế này, nên mô hình sẵn sàng chạy mà không cần tệp bổ sung.
ONNX ra mắt năm 2017 nhờ hợp tác giữa Microsoft và Facebook (nay là Meta). Mục tiêu rất đơn giản — không còn phải viết lại mô hình mỗi lần đổi framework. PyTorch, TensorFlow và các framework khác liên tục cải tiến, nhưng mô hình không thể di chuyển giữa chúng nếu không chuyển đổi thủ công.
ONNX đã thay đổi điều đó. Phiên bản 1.0 hỗ trợ các phép toán cơ bản cho thị giác máy tính và mạng nơ-ron đơn giản. ONNX ngày nay hỗ trợ transformers, mô hình ngôn ngữ lớn và các kiến trúc phức tạp chưa tồn tại vào năm 2017.
Nó sử dụng Protocol Buffers (protobuf) để tuần tự hóa dữ liệu. Protobuf là định dạng nhị phân do Google phát triển để lưu trữ và truyền dữ liệu hiệu quả. Đọc/ghi nhanh hơn JSON hay XML, và tệp cũng nhỏ hơn. Được cả đôi đường.
Bên trong một mô hình ONNX gồm có:
Phiên bản opset rất quan trọng. ONNX phát triển theo thời gian và tiếp tục bổ sung, cải tiến các phép toán. Tệp mô hình chỉ rõ sử dụng opset nào để runtime biết cách thực thi.
Khái niệm và kiến trúc cốt lõi
ONNX biểu diễn mô hình của bạn như một đồ thị tính toán.
Đồ thị có các nút và cạnh. Nút là các phép toán (như nhân ma trận hay hàm kích hoạt), còn cạnh là các tensor (dữ liệu của bạn) chảy giữa những phép toán đó. Đây là cách ONNX mô tả "nhân các ma trận này, rồi áp dụng ReLU, rồi nhân tiếp".
Ví dụ đơn giản:
Mỗi nút thực hiện một phép toán. Kiểu phép toán (như
Relu,Conv, hoặcMatMul) đến từ bộ toán tử của ONNX. Bạn không thể tự đặt tên phép toán — chúng phải tồn tại trong opset bạn dùng.Các cạnh nối các nút và mang tensor. Tensor có hình dạng (shape) và kiểu dữ liệu. Khi bạn định nghĩa
[1, 3, 224, 224], tức là "tensor này có 4 chiều với các kích thước như vậy". Runtime dùng thông tin này để cấp phát bộ nhớ và kiểm tra hợp lệ của đồ thị.Đồ thị chảy theo một hướng. Dữ liệu đi vào qua các nút đầu vào, đi qua các phép toán, và thoát ra qua các nút đầu ra. Không cho phép chu trình — ONNX không hỗ trợ kết nối hồi quy trực tiếp. Bạn cần unroll vòng lặp hoặc dùng các phép toán dành cho chuỗi.
Cấu trúc đồ thị cho phép khả năng tương tác giữa các framework. PyTorch thiên về thực thi eager, TensorFlow dùng đồ thị tĩnh, còn scikit-learn lại có trừu tượng khác. Nhưng tất cả đều có thể xuất ra cùng một định dạng đồ thị.
Đồ thị cũng cho phép tối ưu dùng chung. Bộ tối ưu ONNX có thể hợp nhất phép toán (gộp nhiều nút thành một), loại bỏ mã chết (xóa nút không dùng), hoặc thay thế phép toán chậm bằng tương đương nhanh hơn. Các tối ưu này hoạt động bất kể framework tạo mô hình.
Tiếp theo, tôi sẽ chỉ bạn chi tiết chuyển đổi mô hình sang định dạng ONNX.
Chuyển đổi mô hình sang định dạng ONNX
Trong phần này, tôi sẽ chỉ cách xuất mô hình từ PyTorch, TensorFlow và scikit-learn. Tôi sẽ dẫn bạn qua quy trình chuyển đổi và cách xác thực mô hình hoạt động đúng sau khi chuyển.
Hỗ trợ framework và công cụ
ONNX hỗ trợ hầu hết các framework học máy phổ biến.
PyTorch có xuất ONNX tích hợp qua
torch.onnx.export(). Đây là bộ chuyển đổi trưởng thành nhất vì cả PyTorch và ONNX đều do Meta duy trì.TensorFlow dùng
tf2onnxđể chuyển đổi. Đây là thư viện riêng chuyển đồ thị TensorFlow sang ONNX. Cài bằnguv add tf2onnxlà dùng được.scikit-learn chuyển đổi thông qua
skl2onnx. Thư viện này xử lý các mô hình ML truyền thống như random forest, hồi quy tuyến tính và SVM. Framework học sâu được chú ý nhiều hơn, nhưng mô hình scikit-learn cũng hoạt động tốt.Ba cái trên là phổ biến nhất, nhưng bạn có thể dùng bất kỳ framework được hỗ trợ nào khác — chỉ cần cài phụ thuộc qua
uv add <name>:Keras: Dùng
tf2onnx(Keras là một phần của TensorFlow)XGBoost: Dùng
onnxmltoolsLightGBM: Dùng
onnxmltoolsMATLAB: Xuất ONNX tích hợp sẵn
Paddle: Dùng
paddle2onnxMỗi framework có bộ chuyển đổi riêng vì cách biểu diễn mô hình khác nhau. PyTorch dùng đồ thị tính toán động, TensorFlow dùng đồ thị tĩnh, còn scikit-learn không dùng đồ thị. Các bộ chuyển đổi dịch các biểu diễn này sang đồ thị của ONNX.
Bạn cũng có thể dùng mô hình do người khác huấn luyện. ONNX Model Zoo lưu trữ các mô hình tiền huấn luyện sẵn sàng sử dụng. Bạn sẽ tìm thấy mô hình thị giác máy tính (ResNet, YOLO, EfficientNet), NLP (BERT, GPT-2), và hơn thế nữa. Tải từ kho GitHub chính thức.
Hugging Face cũng lưu trữ mô hình ONNX. Tìm các mô hình có "onnx" trong tên hoặc lọc theo định dạng ONNX. Nhiều mô hình transformer phổ biến sẵn có ở định dạng ONNX đã được tối ưu cho suy luận.
Trước khi tiếp tục, chạy lệnh này để lấy mọi thư viện cần thiết bằng uv:
Quy trình chuyển đổi từng bước
Cách chuyển một mô hình PyTorch sang ONNX:
Mới dùng PyTorch? Đừng để điều đó cản trở bạn. Khóa học Deep Learning với PyTorch của chúng tôi sẽ giúp bạn nắm vững nền tảng chỉ trong vài giờ.
Dummy input rất quan trọng. ONNX cần biết shape đầu vào để xây đồ thị. Nếu mô hình của bạn chấp nhận chuỗi độ dài biến đổi hoặc kích thước batch khác nhau, hãy dùng
dynamic_shapesđể đánh dấu các chiều đó là động.Luôn đặt mô hình vào chế độ đánh giá bằng
model.eval()trước khi xuất. Điều này vô hiệu hóa hành vi huấn luyện của dropout và batch normalization. Nếu quên bước này, mô hình đã chuyển sẽ không khớp với bản gốc.Dynamic shape dễ gây vấn đề. Nếu bộ chuyển đổi báo lỗi về kích thước không xác định, hãy đảm bảo bạn khai báo
dynamic_shapesđúng hoặc cung cấp shape cụ thể.Chuyển sang TensorFlow, đây là đoạn mã thay thế bạn có thể chạy:
Nếu bạn chưa từng làm việc với TensorFlow, chúng tôi khuyến nghị khóa học Introduction to TensorFlow in Python.
Đáng tiếc, có chút xung đột với phiên bản Numpy mới nhất, nên tôi đã thêm bản vá tạm thời. Tại thời điểm bạn đọc, hy vọng bạn có thể chạy đoạn mã mà không cần sáu dòng đầu.
Và cuối cùng, hãy xem chuyển đổi ONNX cho các mô hình scikit-learn:
Tương tự TensorFlow, DataCamp cũng có khóa Supervised Learning với scikit-learn miễn phí — xem thử nếu bạn thấy đoạn mã trên khó hiểu.
Vậy là xong!
Giờ bạn có thể chạy đoạn mã này để xác thực mô hình đã chuyển:
Hãy chạy xác thực này mỗi lần bạn chuyển đổi mô hình. Sai khác số học nhỏ là bình thường (toán dấu phẩy động không tuyệt đối), nhưng sai khác lớn nghĩa là có vấn đề trong quá trình chuyển đổi.
Để kết thúc, có hai lưu ý quan trọng bạn cần biết:
Tiếp theo, hãy bàn về ONNX Runtime.
Chạy suy luận với ONNX Runtime
Trong phần này, tôi sẽ chỉ bạn cách dùng ONNX Runtime để tải mô hình và tạo dự đoán. Tôi sẽ hướng dẫn các tùy chọn tăng tốc phần cứng và cho thấy ONNX Runtime tỏa sáng thế nào trong production.
Giới thiệu về ONNX Runtime
ONNX Runtime là động cơ suy luận đa nền tảng. Microsoft phát triển nó để chạy mô hình ONNX nhanh trên mọi phần cứng — CPU, GPU, chip di động và các bộ gia tốc AI chuyên dụng.
Cách nó hoạt động: Bạn tải mô hình ONNX, và ONNX Runtime xây dựng kế hoạch thực thi. Nó phân tích đồ thị tính toán, áp dụng tối ưu, và tìm cách tốt nhất để thực thi các phép toán trên phần cứng của bạn. Sau đó nó chạy suy luận theo kế hoạch đã tối ưu.
Kiến trúc tách rời logic thực thi và mã phụ thuộc phần cứng. Execution provider xử lý giao tiếp phần cứng. Muốn chạy trên GPU NVIDIA? Dùng CUDA execution provider. Muốn Apple Silicon? Mã nền của bạn vẫn giữ nguyên.
ONNX Runtime có các execution provider sau:
CPUExecutionProvider: Mặc định, hoạt động ở mọi nơiCUDAExecutionProvider: GPU NVIDIA với CUDATensorRTExecutionProvider: GPU NVIDIA với tối ưu TensorRTCoreMLExecutionProvider: Thiết bị Apple (macOS, iOS)DmlExecutionProvider: DirectML cho Windows (hoạt động với mọi GPU)OpenVINOExecutionProvider: CPU và GPU IntelROCMExecutionProvider: GPU AMDMỗi provider dịch các phép toán ONNX thành chỉ thị đặc thù phần cứng. Provider CPU dùng phép toán CPU tiêu chuẩn. Provider CUDA dùng cuDNN và cuBLAS. Provider TensorRT biên dịch mô hình thành các kernel GPU tối ưu, v.v.
Thiết lập và thực thi mô hình
Bạn có thể tải mô hình ONNX với ba dòng mã:
InferenceSessionlàm hết phần nặng. Nó tải mô hình, xác thực đồ thị và chuẩn bị chạy suy luận. Mặc định, nó dùng provider CPU.Giờ hãy chạy suy luận:
Phương thức
run()nhận hai tham số. Đầu tiên là danh sách tên đầu ra (hoặcNoneđể lấy mọi đầu ra). Thứ hai là từ điển ánh xạ tên đầu vào tới mảng numpy.Shape đầu vào phải khớp. Nếu mô hình mong đợi
[1, 10]mà bạn truyền[1, 10, 3], suy luận sẽ thất bại.Nếu muốn tăng tốc bằng GPU, chỉ cần chỉ định provider khi tạo session:
Liệt kê provider theo thứ tự ưu tiên. ONNX Runtime thử provider đầu tiên, nếu không có sẽ rơi xuống provider tiếp theo. Luôn bao gồm
CPUExecutionProviderlàm dự phòng cuối cùng.Bạn có thể kiểm tra provider nào thực sự được dùng:
Nếu bạn thấy
["CPUExecutionProvider"]trong khi mong GPU, nghĩa là provider GPU không khả dụng. Lý do thường gặp: thiếu driver, cài sai gói ONNX Runtime, hoặc GPU không tương thích.Cấu hình chưa dừng ở đây. Bạn có thể tối ưu provider hơn nữa để có hiệu năng tốt hơn:
Bạn có thể tham khảo tài liệu ONNX Runtime chính thức để có danh sách đầy đủ các tùy chọn cho provider bạn chọn.
Ứng dụng của ONNX Runtime
Điểm ấn tượng của ONNX Runtime là nó chạy ở mọi nơi — máy chủ, trình duyệt và thiết bị di động.
ONNX Runtime Web mang suy luận lên trình duyệt bằng WebAssembly và WebGL. Mô hình chạy phía client mà không gửi dữ liệu lên máy chủ. Rất phù hợp cho các ứng dụng nhạy cảm về quyền riêng tư như phân tích ảnh y tế hoặc xử lý tài liệu.
Đây là ví dụ đơn giản cách dùng ONNX Runtime trong một tệp HTML:
Với triển khai di động, bạn có thể dùng ONNX Runtime Mobile — phiên bản gọn nhẹ tối ưu cho iOS và Android. Nó lược bỏ các tính năng máy chủ không cần thiết và giảm kích thước nhị phân.
Đây là ví dụ cấu hình iOS:
Hoặc, nếu bạn dùng Python để xây REST API, bạn có thể đưa ONNX Runtime vào một endpoint như sau:
Vậy là xong! Các môi trường khác cũng dùng cách tiếp cận tương tự, nhưng bạn đã nắm ý chính — ONNX Runtime dễ dùng. Tiếp theo, hãy bàn về tối ưu.
Tối ưu hóa mô hình ONNX
Trong phần này, tôi sẽ minh họa cách làm mô hình ONNX nhanh hơn và nhỏ hơn thông qua lượng tử hóa và tối ưu đồ thị. Tôi sẽ hướng dẫn các kỹ thuật quan trọng nhất cho triển khai thực tế.
Kỹ thuật lượng tử hóa
Lượng tử hóa giảm độ chính xác của mô hình để làm nó nhanh hơn và nhỏ hơn.
Thay vì lưu trọng số ở số thực 32-bit, bạn lưu chúng ở số nguyên 8-bit. Điều này giảm 75% bộ nhớ và tăng tốc suy luận vì toán học số nguyên nhanh hơn dấu phẩy động trên hầu hết phần cứng.
Lượng tử hóa động chuyển trọng số sang độ chính xác thấp hơn nhưng giữ activation (giá trị trung gian khi suy luận) ở độ chính xác đầy đủ. Đây là phương pháp đơn giản nhất vì không cần dữ liệu hiệu chỉnh.
Cách áp dụng lượng tử hóa động:
Vậy là xong. Mô hình đã lượng tử dùng số nguyên 8-bit không dấu và sẵn sàng sử dụng.
Lượng tử hóa tĩnh chuyển cả trọng số và activation sang độ chính xác thấp hơn. Nó mạnh mẽ và nhanh hơn lượng tử hóa động, nhưng bạn cần dữ liệu hiệu chỉnh đại diện để đo dải giá trị của activation.
Chiến lược tổng quát để áp dụng lượng tử hóa tĩnh:
Dữ liệu hiệu chỉnh phải đại diện cho khối lượng công việc suy luận thực tế của bạn, nên đừng dùng dữ liệu ngẫu nhiên như tôi minh họa ở trên. Hãy dùng mẫu thực từ bộ dữ liệu của bạn.
Nhìn chung, lượng tử hóa có một số lợi ích và đánh đổi bạn cần hiểu với tư cách kỹ sư học máy. Đây là lợi ích:
Và các đánh đổi:
Lượng tử hóa cũng tỏa sáng với mô hình ngôn ngữ lớn và AI sinh tạo. Mô hình 7B tham số ở FP32 cần 28GB bộ nhớ. Lượng tử INT8 còn 7GB. Lượng tử INT4 còn 3.5GB — đủ nhỏ để chạy trên phần cứng người dùng.
Tối ưu đồ thị
Tối ưu đồ thị viết lại đồ thị tính toán của mô hình để chạy nhanh hơn.
Khái niệm hợp nhất nút kết hợp nhiều phép toán thành một. Nếu mô hình có lớp Conv rồi BatchNorm rồi ReLU, bộ tối ưu sẽ hợp nhất thành một nút
ConvBnRelu. Điều này giảm lưu chuyển bộ nhớ và tăng tốc thực thi.Đồ thị trước khi hợp nhất:
Được đơn giản hóa sau khi hợp nhất:
Có hai khái niệm nữa bạn cần biết khi tối ưu đồ thị:
Cách bật tối ưu đồ thị trong ONNX Runtime:
ONNX Runtime có các mức tối ưu sau:
ORT_DISABLE_ALL: Không tối ưu (hữu ích khi debug)ORT_ENABLE_BASIC: Tối ưu an toàn, không thay đổi kết quả số họcORT_ENABLE_EXTENDED: Tối ưu mạnh có thể có sai khác số học nhỏORT_ENABLE_ALL: Tất cả tối ưu, bao gồm biến đổi bố cụcDùng
ORT_ENABLE_ALLcho production. Mức tăng tốc xứng đáng với sai khác số học rất nhỏ.Bạn có thể chạy đoạn mã này để lưu mô hình đã tối ưu dùng lại sau:
Tối ưu đồ thị hoạt động bất kể framework tạo mô hình. Mẫu Conv-BN-ReLU của PyTorch và Conv-BN-ReLU của TensorFlow đều được hợp nhất theo cách giống nhau. Đây là lợi ích của tối ưu dùng chung — bạn viết tối ưu một lần và áp dụng cho mô hình từ bất kỳ framework nào.
Tiếp theo, hãy bàn về triển khai.
Kịch bản triển khai
Trong phần này, tôi sẽ điểm qua ba kịch bản triển khai — thiết bị biên, hạ tầng đám mây và trình duyệt web. Tôi sẽ chỉ ra thách thức và giải pháp cho từng môi trường.
Triển khai trên thiết bị biên
Thách thức cốt lõi của thiết bị biên là tài nguyên hạn chế.
Điện thoại của bạn có 4-16GB RAM. Raspberry Pi còn ít hơn. Thiết bị IoT có thể chỉ 512MB hoặc ít hơn. Bạn không thể ném một mô hình 7B tham số vào các thiết bị này và mong nó chạy được.
Bắt đầu với lượng tử hóa. INT8 nên là tiêu chuẩn cơ bản của bạn. Thậm chí dùng INT4 nếu bạn chịu được sụt độ chính xác. Như vậy mô hình đủ nhỏ để vừa bộ nhớ.
Khi triển khai trên thiết bị biên, ONNX Runtime Mobile là trợ thủ. Nó lược bỏ tính năng máy chủ không cần thiết và tối ưu cho pin. Nhị phân nhỏ hơn, khởi động nhanh hơn, và tiêu thụ điện thấp hơn.
Cách thêm nó vào dự án ứng dụng di động của bạn:
Execution provider xử lý khác biệt phần cứng. Thiết bị Android dùng CPU ARM, một số có GPU từ các nhà cung cấp khác nhau, và điện thoại mới có NPU (Neural Processing Unit). Bạn không muốn viết mã cho từng con chip.
Tham khảo đoạn mã sau để chọn provider phù hợp:
CoreML trên iOS dùng Neural Engine (NPU của Apple). NNAPI trên Android dùng bộ gia tốc sẵn có — GPU, DSP, hoặc NPU. Provider trừu tượng hóa phần cứng để mã của bạn giữ nguyên. Khi có chip mới mạnh hơn, chỉ cần cập nhật provider và ứng dụng chạy nhanh hơn mà không cần đổi mã.
Triển khai trên đám mây
Triển khai trên đám mây mang đến khả năng mở rộng không giới hạn và phần cứng mới nhất.
Bạn có thể triển khai mô hình ONNX trên bất kỳ nền tảng đám mây nào — AWS, Azure, GCP, hoặc máy chủ riêng. Mẫu triển khai giống nhau — đóng gói dịch vụ suy luận trong container và đặt sau load balancer.
Ví dụ nhỏ với Python FastAPI:
Ví dụ FastAPI này, dù nhỏ, không phải dễ hiểu nhất với các kỹ sư học máy. Bạn có thể nắm vững nền tảng với khóa Introduction to FastAPI miễn phí của chúng tôi.
Sau đó bạn có thể đóng gói bằng Docker:
Mọi người yêu ML đều nên biết Docker cơ bản. Hãy học khóa Introduction to Docker để cuối cùng triển khai được một mô hình học máy.
Bạn sẽ có ứng dụng chạy ở cổng 8000, nơi bạn có thể mở tài liệu trực tiếp và thử endpoint dự đoán:
Nếu bạn dùng Azure Machine Learning, bạn sẽ vui khi biết nó tích hợp trực tiếp với ONNX. Bạn có thể triển khai mô hình trong ba bước:
Azure xử lý mở rộng, giám sát và cập nhật. Bạn có log tự động, truy vết yêu cầu và kiểm tra sức khỏe.
Nếu bạn mới dùng Azure Machine Learning, hãy đọc Hướng dẫn cho người mới bắt đầu để nhanh chóng nắm cơ bản.
Khi nói về quy trình MLOps cần các thành phần sau:
Đây là ví dụ bạn có thể dùng:
Từ đó, bạn có thể mở rộng theo chiều ngang bằng cách chạy nhiều phiên bản dịch vụ sau load balancer. ONNX Runtime là stateless, nên bất kỳ phiên bản nào cũng xử lý được yêu cầu bất kỳ.
Suy luận trên trình duyệt
ONNX Runtime Web chạy mô hình trực tiếp trong trình duyệt bằng WebAssembly và WebGL.
Thay vì gửi dữ liệu lên máy chủ, bạn gửi mô hình đến trình duyệt một lần và chạy suy luận cục bộ. Người dùng có phản hồi tức thì không phải chờ độ trễ mạng.
Để bắt đầu, chỉ cần tạo tệp HTML dùng script JS inline:
Bạn có thể xem đầu ra trong console ngay khi mở tệp bằng live server:
Và cuối cùng, hãy bàn về vài chủ đề nâng cao.
Chủ đề nâng cao
Các phép toán ONNX tiêu chuẩn bao phủ hầu hết trường hợp, nhưng nếu bạn cần nhiều hơn, đây là phần dành cho bạn.
Tôi sẽ đề cập đến toán tử tùy biến và triển khai mô hình ngôn ngữ lớn. Tôi sẽ giải thích khi nào bạn cần các tính năng nâng cao này và những đánh đổi đi kèm.
Toán tử tùy biến
ONNX đi kèm hàng trăm phép toán, nhưng mô hình của bạn có thể dùng thứ gì đó độc đáo.
Toán tử tùy biến cho phép bạn định nghĩa các phép toán không tồn tại trong bộ toán tử chuẩn của ONNX. Bạn tự viết logic phép toán và chỉ cho ONNX Runtime cách thực thi. Điều này mở rộng khả năng của ONNX vượt ngoài những gì tích hợp sẵn.
Khi nào cần toán tử tùy biến? Khi bạn dùng nghiên cứu mới nhất mà ONNX chưa hỗ trợ. Hoặc khi bạn xây các phép toán độc quyền cho lĩnh vực của bạn. Hoặc khi bạn cần tối ưu đặc thù phần cứng mà các phép toán chuẩn không đáp ứng.
Nhưng có điều — toán tử tùy biến làm mất tính di động. Mô hình của bạn sẽ không chạy trên hệ thống không có triển khai toán tử tùy biến của bạn. Nếu bạn xuất mô hình PyTorch với toán tử tùy biến sang ONNX, bạn cần đóng gói các toán tử đó riêng và đăng ký với ONNX Runtime.
Triển khai trở nên khó hơn. Mọi môi trường đều cần thư viện toán tử tùy biến của bạn. Thiết bị biên, máy chủ đám mây và trình duyệt đều cần cùng một triển khai. Bạn đánh mất lợi thế lớn nhất của ONNX: viết một lần, chạy mọi nơi.
Đây là ví dụ nhỏ hoạt động về toán tử tùy biến, để bạn thấy cách chúng hoạt động:
Hầu hết các đội ngũ tránh dùng toán tử tùy biến nếu có thể. Hãy thử dùng tổ hợp các phép toán chuẩn trước. Chỉ dùng tùy biến khi phép toán chuẩn không thể biểu đạt thao tác của bạn hoặc khi hiệu năng đòi hỏi.
Xử lý mô hình ngôn ngữ lớn
Bộ nhớ là vấn đề với LLM. Mô hình 7B tham số cần 28GB ở FP32, 14GB ở FP16, và 7GB ở INT8. Hầu hết phần cứng người dùng không chứa nổi. Bạn cần lượng tử hóa, và thường là lượng tử mạnh đến INT4 hoặc INT3.
Nếu bạn mới làm quen LLM và muốn học thêm, khóa Large Language Models (LLMs) Concepts của chúng tôi là điểm khởi đầu tuyệt vời.
Quản lý bộ đệm KV rất quan trọng cho sinh tự hồi quy. Mỗi token sinh ra cần tham chiếu tất cả token trước đó. Bộ đệm tăng theo độ dài chuỗi. Cuộc hội thoại dài ngốn bộ nhớ nhanh. ONNX Runtime GenAI có xử lý bộ đệm KV tối ưu, tái sử dụng bộ nhớ và giảm overhead.
Tốc độ suy luận phụ thuộc phần cứng và kích thước mô hình. Mô hình nhỏ (dưới 3B tham số) chạy tốt trên GPU người dùng. Mô hình lớn cần nhiều GPU hoặc bộ gia tốc suy luận chuyên dụng. ONNX Runtime hỗ trợ suy luận đa GPU thông qua provider, nhưng bạn cần tự chia mô hình qua các thiết bị.
Flash Attention tăng tốc cơ chế attention trong transformer. Attention chuẩn có độ phức tạp O(n²) theo độ dài chuỗi — chậm rất nhanh. Flash Attention giảm di chuyển bộ nhớ và cải thiện tốc độ mà không thay đổi kết quả. ONNX Runtime bao gồm tối ưu Flash Attention cho phần cứng được hỗ trợ.
Huấn luyện LLM với ONNX Runtime là có thể nhưng hiếm. Hầu hết đội ngũ huấn luyện bằng PyTorch hoặc JAX, rồi xuất sang ONNX chỉ để suy luận. ONNX Runtime Training tồn tại cho fine-tuning và tiếp tục tiền huấn luyện, nhưng hệ sinh thái xung quanh huấn luyện bằng PyTorch trưởng thành hơn.
Tải công việc AI sinh tạo được ưu tiên đặc biệt trong ONNX Runtime. Phần mở rộng GenAI cung cấp API cấp cao cho sinh văn bản, duy trì trạng thái giữa các lần gọi, và quản lý beam search hoặc chiến lược sampling. Điều này giúp bạn không phải tự cài đặt logic sinh.
Thế giới LLM thay đổi nhanh, và ONNX Runtime cố gắng cập nhật. Bộ toán tử và tối ưu được cập nhật thường xuyên, nhưng luôn có độ trễ giữa nghiên cứu và hỗ trợ sẵn sàng production.
Kết luận
Giờ bạn đã biết cách chuyển đổi mô hình từ bất kỳ framework nào, tối ưu cho production và triển khai ở bất kỳ đâu.
ONNX loại bỏ ma sát giữa huấn luyện và triển khai. Huấn luyện với PyTorch vì nó tuyệt vời cho nghiên cứu. Triển khai với ONNX Runtime vì nó nhanh và chạy mọi nơi. Lượng tử hóa xuống INT8 để giảm một nửa chi phí suy luận. Chuyển từ CPU sang GPU mà không đổi mã. Di chuyển mô hình giữa các nhà cung cấp đám mây mà không bị khóa nhà cung cấp.
ONNX là công cụ khiến điều đó trở thành hiện thực.
Hệ sinh thái không ngừng cải thiện. Hỗ trợ AI sinh tạo tốt hơn qua từng bản phát hành — suy luận LLM nhanh hơn, quản lý bộ nhớ tốt hơn, và kỹ thuật tối ưu mới. Hỗ trợ phần cứng mở rộng đến nhiều bộ gia tốc và thiết bị biên hơn. Cộng đồng xây dựng công cụ giúp ONNX dễ dùng hơn. Bạn có thể tham gia bằng cách thử tính năng mới, báo lỗi, hoặc đóng góp mã qua các kho GitHub.
Sẵn sàng lên cấp tiếp theo? Hãy làm chủ Các khái niệm về Trí tuệ nhân tạo có khả năng giải thích (XAI) để thôi coi mô hình như hộp đen.