Transformers documentation

Trình xuất

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Trình xuất

Xuất bất kỳ PreTrainedModel nào sang ONNX, ExecuTorch hoặc một chương trình PyTorch độc lập, bất kể runtime đích.

exporter = DynamoExporter()  # or OnnxExporter, ExecutorchExporter
config = DynamoConfig(dynamic=True)
exported = exporter.export(model, inputs, config=config)

Các trình xuất nằm trong Transformers thay vì một thư viện hạ nguồn, do đó các thay đổi kiến trúc, các mẫu chú ý mới và các kiểu bộ nhớ đệm tùy chỉnh được hỗ trợ tại thời điểm xuất ngay khi chúng được đưa vào mã mô hình hóa.

Các trình xuất (exporter) đang ở giai đoạn thử nghiệm. Nhiều bản vá trong module này được tạo ra để khắc phục các lỗi cụ thể từ các dự án thượng nguồn (Torch, ONNX Script, ONNX Runtime, ExecuTorch) và sẽ được loại bỏ ngay khi bản sửa lỗi được tích hợp lên thượng nguồn. Cho đến khi API ổn định, hãy coi các bản vá này là gắn liền với các phiên bản được sử dụng trong bộ kiểm thử. Hãy cố định (pin) các phiên bản đó trong công cụ sản xuất, và dự kiến các bản vá mới sẽ xuất hiện trong khi các bản vá cũ sẽ biến mất khi các thay đổi từ thượng nguồn được áp dụng.

ExporterĐầu raThời gian chạy
DynamoExporterExportedProgramBất kỳ runtime PyTorch nào, biên dịch AOT
OnnxExporterONNXProgramBất kỳ runtime ONNX nào (ORT, TensorRT, OpenVINO)
ExecutorchExporterExecutorchProgramManagerThiết bị di động và biên (ExecuTorch)
OpenVINOExporteropenvino.ModelTrình chạy OpenVINO (CPU/GPU/NPU của Intel)

AutoHfExporter chọn exporter phù hợp từ một config, và AutoExportConfig chọn lớp config phù hợp từ một dict. Cả hai đều tuân theo cùng một mẫu auto-class trong Transformers, điều này hữu ích khi backend được chọn vào lúc runtime thay vì được hardcode tại vị trí gọi.

from transformers.exporters import AutoExportConfig, AutoHfExporter

export_config_dict = {"export_format": "onnx", "dynamic": True}
config = AutoExportConfig.from_dict(export_config_dict)
exporter = AutoHfExporter.from_config(config)

onnx_program = exporter.export(model, inputs, config=config)

Cài đặt

Cài đặt các phụ thuộc cho backend mà bạn dự định xuất ra.

Các phiên bản bên dưới là những phiên bản mà bộ kiểm thử exporter được cố định. Các bản phát hành mới hơn hoặc cũ hơn thường vẫn hoạt động, nhưng các bản vá của exporter nhắm đến một bề mặt API cụ thể, vì vậy đối với công cụ sản xuất, hãy cố định các phiên bản này và kỳ vọng HfExporter sẽ ghi nhật ký một cảnh báo khi phát hiện sự lệch lạc.

Dynamo
ONNX
ExecuTorch XNNPACK
ExecuTorch MLX
OpenVINO
pip install transformers "torch==2.12.0"

Xuất mô hình

Tất cả các exporter chia sẻ cùng một giao diện. Tạo một exporter với một config, và gọi export().

Chuyển đổi giữa các runtime bằng cách thay thế lớp exporter.

Dynamo
ONNX
ExecuTorch
OpenVINO
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.exporters import DynamoExporter, DynamoConfig

model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-0.6B")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-0.6B")
inputs = tokenizer("Hello, world!", return_tensors="pt")

exporter = DynamoExporter()
config = DynamoConfig(dynamic=True)
exported = exporter.export(model, inputs, config=config)

# run the exported graph directly
outputs = exported.module()(**inputs)

Hình dạng động

Việc truyền dynamic=True đánh dấu mọi chiều tensor là động, cho phép đồ thị đã xuất nhận đầu vào có kích thước bất kỳ lúc runtime mà không cần trace lại.

Để kiểm soát chi tiết hơn các chiều nào là động, hãy truyền dynamic_shapes rõ ràng thay vào đó, giá trị này sẽ được chuyển trực tiếp đến torch.export.export.

Dynamo
ONNX
ExecuTorch
OpenVINO
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.exporters import DynamoExporter, DynamoConfig

model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-0.6B")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-0.6B")
inputs = tokenizer(["Hello, world!", "Hi"], padding=True, return_tensors="pt")

batch = torch.export.Dim("batch", min=1, max=32)
seq = torch.export.Dim("seq", min=1, max=2048)

exporter = DynamoExporter()
config = DynamoConfig(
    dynamic_shapes={"input_ids": {0: batch, 1: seq}, "attention_mask": {0: batch, 1: seq}},
    # Emit data-dependent shape guards as runtime asserts instead of failing the export when a
    # guard wouldn't hold across the explicit symbolic range. Most LLMs need this under fine-grained
    # ``Dim(min=, max=)`` bounds. Not needed with ``dynamic=True`` / ``Dim.AUTO``, where torch.export
    # infers shape relations instead of verifying them against user-stated bounds.
    prefer_deferred_runtime_asserts_over_guards=True,
)
exported = exporter.export(model, inputs, config=config)

Mô hình sinh

Đối với sinh tự hồi quy, forward của mô hình có các hình dạng khác nhau ở bước prefill (đề bài đầy đủ, không có bộ nhớ đệm KV) so với bước decode (một token duy nhất, bộ nhớ đệm KV đã được nạp). Các trình xuất (exporter) cung cấp ~HfExporter.export_for_generation, tách cả hai giai đoạn và xuất từng giai đoạn.

Đối với các mô hình sinh đa phương thức, quá trình prefill được tách thêm thành bộ mã hóa hình ảnh hoặc âm thanh, mô hình ngôn ngữ và lm_head. Việc phát hiện bộ mã hóa và mô hình ngôn ngữ sử dụng các bộ truy cập get_encoder() (modality="image" hoặc "audio") và get_decoder(), do đó bất kỳ kiến trúc mới nào sử dụng các thành phần này đều hoạt động ngay lập tức.

Một thành phần projector chỉ xuất hiện khi model công bố một projector dưới tên thuộc tính (multi_modal_projector, connector, embed_vision, embed_audio). Qwen2-VL bên dưới gộp projector của nó vào vision tower, do đó dict thành phần của nó không có khóa multi_modal_projector riêng biệt. Các kiến trúc mới phải căn chỉnh thuộc tính projector của chúng với một trong những tên này thay vì mở rộng danh sách.

Dynamo
ONNX
ExecuTorch
OpenVINO
from transformers import AutoModelForImageTextToText, AutoProcessor
from transformers.exporters import DynamoExporter, DynamoConfig

model = AutoModelForImageTextToText.from_pretrained("Qwen/Qwen2-VL-2B-Instruct")
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-VL-2B-Instruct")
messages = [{"role": "user", "content": [{"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"}, {"type": "text", "text": "Describe this image."}]}]
text = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
inputs = processor(text=text, images=messages[0]["content"][0]["url"], return_tensors="pt").to(model.device)

exporter = DynamoExporter()
config = DynamoConfig(dynamic=True)
components = exporter.export_for_generation(model, inputs, config=config)
# components = {"image_encoder": ExportedProgram, "language_model": ExportedProgram, "lm_head": ExportedProgram, "decode": ExportedProgram}

Các thành phần được xuất ra là các đồ thị độc lập, không phải một pipeline suy luận sẵn sàng để chạy. Người gọi chịu trách nhiệm chạy từng encoder, chiếu các embedding và điều phối vòng lặp sinh.

Cách export_for_generation hoạt động

decompose_for_generation() chạy model.generate(**inputs, max_new_tokens=2) một lần và gắn hook vào model.forward để bắt các kwargs thực tế của prefill và decode (cũng như kwargs cho từng submodule thông qua các hook trên mỗi encoder/projector/language model nếu mô hình là đa phương thức). Đó là lý do nó hoạt động với mọi kiến trúc, bao gồm decoder-only, SSM, encoder-decoder và các mô hình đa phương thức, mà không cần code kết nối riêng cho từng mô hình. export_for_generation là một dòng duy nhất dựa trên nó.

The capture chạy model một cách trực tiếp trên inputs, vì vậy hãy truyền các giá trị nhỏ nhưng đại diện, chẳng hạn như một prompt ngắn, một hình ảnh nhỏ duy nhất, hoặc một vài khung âm thanh. Chương trình được xuất ra không bị ràng buộc bởi các kích thước đó (các hình dạng động vẫn được truyền qua), nhưng các đầu vào capture nhỏ hơn sẽ giúp decompose_for_generation rẻ hơn và giữ cho việc suy luận hình dạng biểu tượng ở mức khả thi.

Gọi trực tiếp decompose_for_generation để thực hiện các thao tác giữa bước phân rã và xuất, chẳng hạn như chạy một lần forward kiểu eager để xác minh, thay đổi đầu vào của một submodule, hoặc bỏ qua một giai đoạn.

from transformers.exporters.utils import decompose_for_generation

components = decompose_for_generation(model, inputs)
# {"image_encoder": (submodel, fwd_kwargs), "language_model": (...), ..., "decode": (...)}

exported = {}
for name, (submodel, subinputs) in components.items():
    eager_outputs = submodel(**subinputs)  # sanity-check the eager forward before exporting
    exported[name] = exporter.export(submodel, subinputs, config=config)

Giải mã đa token

Mặc định, thành phần decode là một bước single-token — một token truy vấn đối với bộ nhớ đệm KV — do đó torch.export đặc biệt hóa trục chuỗi truy vấn của nó thành 1. Truyền multi_token_decode=True để bắt decode như một bước giải mã multi-token thay thế: decompose_for_generation() gộp hai bước decode liên tiếp (nó bắt với max_new_tokens=3) thành một lần forward, nhờ đó trục đó vẫn mang tính biểu tượng. Một đồ thị duy nhất sau đó phục vụ mọi độ dài truy vấn — một token (giải mã thông thường), nhiều token cùng lúc (tiếp nối từ quá khứ, ví dụ chấp nhận một khối token suy đoán), và một prefill thông thường khi bộ nhớ đệm trống.

Dynamo
ONNX
ExecuTorch
from transformers.exporters import DynamoExporter, DynamoConfig

exporter = DynamoExporter()
config = DynamoConfig(dynamic=True)
components = exporter.export_for_generation(model, inputs, config=config, multi_token_decode=True)
# components["decode"] now accepts a variable number of query tokens

Trục query chỉ giữ ở dạng biểu tượng khi xuất với hình dạng động (dynamic=True); một bản xuất tĩnh sẽ đóng băng nó ở độ dài đã bắt được, tạo ra một đồ thị đa token cố định. Nó kết hợp với bộ nhớ đệm KV tĩnh ở bên dưới — bước decode hợp nhất sẽ ghi các token của từng bước vào bộ nhớ đệm có kích thước cố định tại chỗ, và bộ nhớ đệm tự xử lý vị trí chúng được đặt vào.

Bộ đệm KV tĩnh

Các ví dụ ExecuTorch trong phần này, bao gồm cập nhật zero-copy và vòng lặp giải mã, chỉ dành cho XNNPACK. Trình xuất MLX từ chối StaticCache; hãy sử dụng DynamicCache với MLX.

generate() mặc định tạo ra một DynamicCache, cấp phát lại bộ nhớ khi chuỗi dài thêm — một mục tiêu di chuyển đối với đồ thị đã xuất. Một cache static là một bộ đệm có kích thước cố định, được cấp phát một lần và ghi tại chỗ ở vị trí hiện tại ở mỗi bước. Kết hợp với multi-token decode, nó thu gọn quá trình sinh thành một đồ thị xuất duy nhất: đồ thị decode nhận một cache có kích thước cố định và một số lượng token truy vấn biến đổi, do đó một đồ thị phục vụ cả prompt (cache trống → prefill) và mỗi token được sinh (cache đã có dữ liệu → decode). Hãy xuất nó bằng cách truyền một GenerationConfig với cache_implementation="static" (và một max_cache_len) cùng với multi_token_decode=True:

Dynamo
ONNX
ExecuTorch XNNPACK
from transformers import GenerationConfig
from transformers.exporters import DynamoExporter, DynamoConfig

exporter = DynamoExporter()
gen_config = GenerationConfig(cache_implementation="static", max_cache_len=2048)
components = exporter.export_for_generation(
    model, inputs, config=DynamoConfig(dynamic=True), generation_config=gen_config, multi_token_decode=True
)

Đồ thị decode hiện có hai trục biểu tượng — độ dài truy vấn (số lượng token bạn đưa vào) và độ dài bộ nhớ đệm (max_cache_len, có thể điều chỉnh kích thước khi tải). dynamic=True đánh dấu các trục này (và mọi trục khác) là Dim.AUTO, do đó đồ thị xuất ra sẽ chấp nhận bất kỳ độ dài prompt và kích thước bộ nhớ đệm nào khi tải.

Cập nhật tại chỗ không sao chép

Bộ đệm tĩnh được truyền vào và thay đổi tại chỗ, do đó một bộ đệm duy nhất mang trạng thái qua các bước giải mã mà không cần sao chép trên host — miễn là thời gian thực thi liên kết các bộ đệm của người gọi thay vì sao chép thông qua vùng nhớ riêng của nó. Điều cần thiết cho việc này là phần duy nhất còn lại cho từng backend:

  • Dynamo — chương trình đã xuất mô hình hóa thao tác ghi cache dưới dạng USER_INPUT_MUTATION, do đó việc gọi components["decode"].module()(...) sẽ cập nhật trực tiếp các tensor cache mà bạn truyền vào. Hãy tái sử dụng cùng các tensor đó ở mỗi bước; không cần cấu hình gì thêm.

  • ONNX Runtime — đồ thị decode phơi bày cache dưới dạng các cặp input.<name> / output.<name> khớp nhau. CudaSession.set_buffer_sharing của ORT (onnxruntime.transformers.io_binding_helper) gắn mỗi cặp với một bộ đệm thiết bị, nhờ đó cache được đọc và cập nhật tại chỗ qua vòng lặp mà không cần các lượt đi về phía host.

  • ExecuTorch (XNNPACK) — tắt các cấp phát bộ nhớ lập kế hoạch trên ExecutorchConfig để thao tác ghi tại chỗ có thể thực hiện trên chính tensor của người gọi (xem tài liệu tham khảo để biết ý nghĩa của từng cờ):

    config = ExecutorchConfig(
        backend="xnnpack",
        dynamic=True,
        alloc_graph_input=False,
        alloc_graph_output=False,
        alloc_mutable_buffers=False,
    )

    Việc ghi tại chỗ không sao chép (zero-copy in-place write) cũng yêu cầu người gọi phải liên kết các bộ đệm đầu ra vào thời điểm chạy thông qua Method::set_output_data_ptr — không được hiển thị bởi môi trường chạy Python (executorch.runtime.Method chỉ cung cấp execute/set_inputs/get_outputs). Các cờ ở trên thiết lập điều này, nhưng việc ghi tại chỗ là một đường dẫn chỉ dành cho C++ (xem ví dụ vòng lặp giải mã ExecuTorch bên dưới). Từ Python, hãy đọc lại bộ nhớ đệm đã cập nhật từ các đầu ra của phương pháp ở mỗi bước.

Decode-loop inference examples

Vòng lặp có cùng hình dạng trên mọi backend — đó là cùng một đồ thị xuyên suốt. Bắt đầu từ một bộ đệm tĩnh có kích thước cố định, nạp toàn bộ prompt một lần (bộ đệm trống → prefill), sau đó lần lượt từng token (bộ đệm đã có dữ liệu → decode). Mỗi lần gọi truyền vào input_ids, một attention_mask nhân quả, và position_ids (được tăng lên theo số lượng token mới ở mỗi bước), cùng với bộ đệm, và nhận lại logits cho mọi vị trí truy vấn. Vị trí mỗi token nằm trong bộ đệm được theo dõi bên trong bởi bộ đệm tĩnh, nên không có gì thêm để truyền qua các lần gọi. Cách thiết lập bộ đệm khác nhau tùy theo runtime (một đối tượng StaticCache cho Dynamo, các bộ đệm thiết bị thô cho ONNX Runtime, các mảng do người gọi cung cấp trong C++ cho ExecuTorch), vì vậy mỗi tab sẽ tự xây dựng phần của mình bên dưới. Các tab Dynamo và ONNX Runtime cập nhật bộ đệm tại chỗ; đường dẫn tại chỗ của ExecuTorch là C++ (runtime Python của nó không thể làm điều này, như đã đề cập ở trên).

Dynamo
ONNX Runtime
ExecuTorch XNNPACK

torch.export ghi nhận thao tác ghi vào static-cache dưới dạng USER_INPUT_MUTATION, do đó module() của đồ thị đã tải sẽ cập nhật trực tiếp StaticCache mà bạn truyền vào trực tiếp — một cache duy nhất mang trạng thái xuyên suốt toàn bộ vòng lặp mà không cần phải ràng buộc hay truyền ngược ra. register_pytree_node(StaticCache) cho phép torch.export.load unflatten đầu vào StaticCache. Cache phải được khởi tạo trước (torch.export đóng gói K/V đã cấp phát vào input spec, nên một cache trống khởi tạo theo kiểu lazy sẽ không khớp) — nhưng chương trình đã lưu chứa sẵn example_inputs của riêng nó, vì vậy hãy tái sử dụng mẫu StaticCache đã khởi tạo đó, đặt lại về trống:

import copy
import torch
from transformers import StaticCache
from transformers.exporters.exporter_dynamo import register_pytree_node

register_pytree_node(StaticCache)
exported = torch.export.load("decode.pt2")
decode = exported.module()   # runs on the device its inputs / cache live on (CUDA here)

# the artifact carries an initialized StaticCache template — reuse it (reset to empty)
_, example_kwargs = exported.example_inputs
past_key_values = copy.deepcopy(example_kwargs["past_key_values"])
past_key_values.reset()

def causal_mask(positions, cache_len):   # [1, 1, len(positions), cache_len]
    return (torch.arange(cache_len, device="cuda")[None, :] <= positions[:, None])[None, None]

# prefill: the whole prompt in one call
positions = torch.arange(prompt_len, device="cuda")
logits = decode(input_ids=prompt_ids, attention_mask=causal_mask(positions, max_cache_len),
                position_ids=positions[None], past_key_values=past_key_values).logits
next_token = logits[:, -1:].argmax(-1)

# decode: query=1 buffers reused in place
input_ids = torch.empty((1, 1), dtype=torch.long, device="cuda")
position_ids = torch.empty((1, 1), dtype=torch.long, device="cuda")
attention_mask = torch.empty((1, 1, 1, max_cache_len), dtype=torch.bool, device="cuda")
slots = torch.arange(max_cache_len, device="cuda")
for position in range(prompt_len, max_cache_len):
    input_ids.copy_(next_token)
    position_ids.fill_(position)
    attention_mask[0, 0, 0].copy_(slots <= position)
    logits = decode(input_ids=input_ids, attention_mask=attention_mask,
                    position_ids=position_ids, past_key_values=past_key_values).logits
    next_token = logits[:, -1:].argmax(-1)

Lượng tử hóa

Mọi cấu hình xuất đều nhận một quantizer để lượng tử hóa mô hình trong quá trình xuất, trên đồ thị FX của torch.export (PT2E hoặc NNCF, cho mọi backend) hoặc trên mô hình ONNX hoặc OpenVINO đã chuyển đổi (ONNX Runtime hoặc NNCF). Xem Lượng tử hóa các bản xuất để biết về các quantizer, backend của chúng, hiệu chuẩn và cách thêm một quantizer.

Hạn chế và cách khắc phục

torch.export, torch.onnx.export và ExecuTorch đều có những điểm chưa hoàn thiện xung quanh các mẫu PyTorch cụ thể. Các trình xuất xử lý những điểm này bằng một tập hợp nhỏ các bản vá có thể đảo ngược và các sửa chữa ở cấp FX được áp dụng tại các điểm xác định rõ ràng trong luồng xuất. Không có gì trong số này hiển thị từ API export công khai, nhưng những điều phổ biến nhất cần biết:

  • FlashAttention và FlexAttention không thể xuất trên bất kỳ backend nào. sdpa là cài đặt được ưu tiên và eager cũng hoạt động (chậm hơn). Hãy đặt một trong hai trên model trước khi gọi export nếu model đang sử dụng cài đặt khác.
  • grouped_mm được trace tốt qua DynamoExporter và được tự động chuyển đổi cho OnnxExporter. Với ExecutorchExporter sử dụng XNNPACK hoặc MLX, exporter sẽ thay thế các chuyên gia MoE bằng batched_mm trước khi xuất.
  • Các lớp sliding-window (Mistral, Gemma 3) được xuất với bộ nhớ đệm key-value đầy đủ; mặt nạ trượt vẫn áp dụng cửa sổ.

Các bước tiếp theo

Update on GitHub