export / onnx_export.py
Felix Marty
add sketch
f75daf5
raw
history blame
5.13 kB
from optimum.exporters.tasks import TasksManager
from optimum.exporters.onnx import OnnxConfigWithPast, export, validate_model_outputs
from tempfile import TemporaryDirectory
from transformers import AutoConfig, is_torch_available
from transformers import AutoConfig
from pathlib import Path
import os
import shutil
import argparse
from typing import Optional
from huggingface_hub import CommitOperationAdd, HfApi, hf_hub_download, get_repo_discussions
from huggingface_hub.file_download import repo_folder_name
def previous_pr(api: "HfApi", model_id: str, pr_title: str) -> Optional["Discussion"]:
try:
discussions = api.get_repo_discussions(repo_id=model_id)
except Exception:
return None
for discussion in discussions:
if discussion.status == "open" and discussion.is_pull_request and discussion.title == pr_title:
return discussion
def convert_onnx(model_id: str, task: str, folder: str):
model_class = TasksManager.get_model_class_for_task(task)
config = AutoConfig.from_pretrained(model_id)
model = model_class.from_config(config)
device = "cpu" # ?
# Dynamic axes aren't supported for YOLO-like models. This means they cannot be exported to ONNX on CUDA devices.
# See: https://github.com/ultralytics/yolov5/pull/8378
if model.__class__.__name__.startswith("Yolos") and device != "cpu":
return
onnx_config_class_constructor = TasksManager.get_exporter_config_constructor(model_type=config.model_type, exporter="onnx", task=task, model_name=model_id)
onnx_config = onnx_config_class_constructor(model.config)
# We need to set this to some value to be able to test the outputs values for batch size > 1.
if (
isinstance(onnx_config, OnnxConfigWithPast)
and getattr(model.config, "pad_token_id", None) is None
and task == "sequence-classification"
):
model.config.pad_token_id = 0
if is_torch_available():
from optimum.exporters.onnx.utils import TORCH_VERSION
if not onnx_config.is_torch_support_available:
print(
"Skipping due to incompatible PyTorch version. Minimum required is"
f" {onnx_config.MIN_TORCH_VERSION}, got: {TORCH_VERSION}"
)
onnx_inputs, onnx_outputs = export(
model, onnx_config, onnx_config.DEFAULT_ONNX_OPSET, Path(folder), device=device
)
atol = onnx_config.ATOL_FOR_VALIDATION
if isinstance(atol, dict):
atol = atol[task.replace("-with-past", "")]
validate_model_outputs(
onnx_config,
model,
Path(folder),
onnx_outputs,
atol,
)
# TODO: iterate in folder and add all
operations = [CommitOperationAdd(path_in_repo=local.split("/")[-1], path_or_fileobj=local) for local in local_filenames]
return operations
def convert(api: "HfApi", model_id: str, task:str, force: bool=False) -> Optional["CommitInfo"]:
pr_title = "Adding ONNX file of this model"
info = api.model_info(model_id)
filenames = set(s.rfilename for s in info.siblings)
with TemporaryDirectory() as d:
folder = os.path.join(d, repo_folder_name(repo_id=model_id, repo_type="models"))
os.makedirs(folder)
new_pr = None
try:
pr = previous_pr(api, model_id, pr_title)
if "model.onnx" in filenames and not force:
raise Exception(f"Model {model_id} is already converted, skipping..")
elif pr is not None and not force:
url = f"https://huggingface.co/{model_id}/discussions/{pr.num}"
new_pr = pr
raise Exception(f"Model {model_id} already has an open PR check out {url}")
else:
convert_onnx(model_id, task, folder)
finally:
shutil.rmtree(folder)
return new_pr
if __name__ == "__main__":
DESCRIPTION = """
Simple utility tool to convert automatically a model on the hub to onnx format.
It is PyTorch exclusive for now.
It works by downloading the weights (PT), converting them locally, and uploading them back
as a PR on the hub.
"""
parser = argparse.ArgumentParser(description=DESCRIPTION)
parser.add_argument(
"model_id",
type=str,
help="The name of the model on the hub to convert. E.g. `gpt2` or `facebook/wav2vec2-base-960h`",
)
parser.add_argument(
"task",
type=str,
help="The task the model is performing",
)
parser.add_argument(
"--force",
action="store_true",
help="Create the PR even if it already exists of if the model was already converted.",
)
args = parser.parse_args()
api = HfApi()
convert(api, args.model_id, task=args.task, force=args.force)