echarlaix HF staff commited on
Commit
d1c5472
β€’
1 Parent(s): 3f279d0

Push new repo instead of PR

Browse files
Files changed (2) hide show
  1. app.py +161 -98
  2. export.py +0 -170
app.py CHANGED
@@ -1,104 +1,166 @@
1
- import csv
2
  import os
3
- from datetime import datetime
4
- from typing import Optional, Union
5
  import gradio as gr
6
- from huggingface_hub import HfApi, Repository
7
- from export import convert
8
  from gradio_huggingfacehub_search import HuggingfaceHubSearch
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
 
11
- DATASET_REPO_URL = "https://huggingface.co/datasets/optimum/exporters"
12
- DATA_FILENAME = "data.csv"
13
- DATA_FILE = os.path.join("openvino", DATA_FILENAME)
14
- HF_TOKEN = os.environ.get("HF_WRITE_TOKEN")
15
- DATA_DIR = "exporters_data"
 
 
16
 
17
- repo = None
18
- # if HF_TOKEN:
19
- # repo = Repository(local_dir=DATA_DIR, clone_from=DATASET_REPO_URL, token=HF_TOKEN)
20
 
21
- def export(model_id: str, task: str, oauth_token: gr.OAuthToken) -> str:
 
 
 
 
22
 
23
- if oauth_token.token is None:
24
- raise ValueError("You must be logged in to use this space")
25
- token = oauth_token.token
26
-
27
- if model_id == "" or token == "":
28
- return """
29
- ### Invalid input 🐞
30
- Please fill a token and model name.
31
- """
32
- try:
33
- api = HfApi(token=token)
34
-
35
- error, commit_info = convert(api=api, model_id=model_id, task=task, force=False)
36
- if error != "0":
37
- return error
38
-
39
- print("[commit_info]", commit_info)
40
-
41
- # save in a private dataset
42
- if repo is not None:
43
- repo.git_pull(rebase=True)
44
- with open(os.path.join(DATA_DIR, DATA_FILE), "a") as csvfile:
45
- writer = csv.DictWriter(csvfile, fieldnames=["model_id", "pr_url", "time"])
46
- writer.writerow(
47
- {
48
- "model_id": model_id,
49
- "pr_url": commit_info.pr_url,
50
- "time": str(datetime.now()),
51
- }
52
- )
53
- commit_url = repo.push_to_hub()
54
- print("[dataset]", commit_url)
55
-
56
- return f"#### Success πŸ”₯ Yay! This model was successfully exported and a PR was open using your token, here: [{commit_info.pr_url}]({commit_info.pr_url})"
57
- except Exception as e:
58
- return f"#### Error: {e}"
59
-
60
-
61
- TTILE_IMAGE = """
62
- <div
63
- style="
64
- display: block;
65
- margin-left: auto;
66
- margin-right: auto;
67
- width: 50%;
68
- "
69
- >
70
- <img src="https://huggingface.co/spaces/echarlaix/openvino-export/resolve/main/header.png"/>
71
- </div>
72
- """
73
-
74
- TITLE = """
75
- <div
76
- style="
77
- display: inline-flex;
78
- align-items: center;
79
- text-align: center;
80
- max-width: 1400px;
81
- gap: 0.8rem;
82
- font-size: 2.2rem;
83
- "
84
- >
85
- <h1 style="text-align:center; font-weight: 1200">
86
- Export your model to OpenVINO
87
- </h1>
88
- </div>
89
- """
90
-
91
- DESCRIPTION = """
92
- This Space uses [Optimum Intel](https://huggingface.co/docs/optimum/intel/inference) to automatically export your model to the OpenVINO format.
93
-
94
- After the model conversion, we will open a PR against the source repo to add the resulting model.
95
-
96
- To export your model you need:
97
- - A Model ID from the Hub
98
-
99
- That's it ! πŸ”₯
100
- """
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
 
104
  model_id = HuggingfaceHubSearch(
@@ -106,21 +168,22 @@ model_id = HuggingfaceHubSearch(
106
  placeholder="Search for model id on the hub",
107
  search_type="model",
108
  )
109
- task = gr.Textbox(
110
- value="auto",
111
- label="Task : can be left to auto, will be automatically inferred",
 
112
  )
113
  interface = gr.Interface(
114
  fn=export,
115
  inputs=[
116
  model_id,
117
- task,
118
  ],
119
  outputs=[
120
  gr.Markdown(label="output"),
121
  ],
122
- title=TITLE,
123
- description=DESCRIPTION,
124
  api_name=False,
125
  )
126
 
 
 
1
  import os
2
+ import shutil
3
+ import torch
4
  import gradio as gr
5
+ from huggingface_hub import HfApi, whoami, ModelCard
 
6
  from gradio_huggingfacehub_search import HuggingfaceHubSearch
7
+ from textwrap import dedent
8
+ from pathlib import Path
9
+
10
+ from tempfile import TemporaryDirectory
11
+
12
+ from huggingface_hub.file_download import repo_folder_name
13
+ from optimum.exporters.tasks import TasksManager
14
+ from optimum.intel.utils.constant import _TASK_ALIASES
15
+ from optimum.intel.openvino.utils import _HEAD_TO_AUTOMODELS
16
+ from optimum.exporters import TasksManager
17
+
18
+ from optimum.intel.utils.modeling_utils import _find_files_matching_pattern
19
+ from optimum.intel import (
20
+ OVModelForAudioClassification,
21
+ OVModelForCausalLM,
22
+ OVModelForFeatureExtraction,
23
+ OVModelForImageClassification,
24
+ OVModelForMaskedLM,
25
+ OVModelForQuestionAnswering,
26
+ OVModelForSeq2SeqLM,
27
+ OVModelForSequenceClassification,
28
+ OVModelForTokenClassification,
29
+ OVStableDiffusionPipeline,
30
+ OVStableDiffusionXLPipeline,
31
+ OVLatentConsistencyModelPipeline,
32
+ OVModelForPix2Struct,
33
+ OVWeightQuantizationConfig,
34
+ )
35
+ from optimum.intel.openvino.modeling_diffusion import OVStableDiffusionPipelineBase
36
 
37
 
38
+ def export(
39
+ model_id: str,
40
+ private_repo: bool,
41
+ oauth_token: gr.OAuthToken,
42
+ ):
43
+ if oauth_token.token is None:
44
+ raise ValueError("You must be logged in to use this space")
45
 
46
+ model_name = model_id.split("/")[-1]
47
+ username = whoami(oauth_token.token)["name"]
48
+ new_repo_id = f"{username}/{model_name}-openvino"
49
 
50
+ task = TasksManager.infer_task_from_model(model_id)
51
+ if task not in _HEAD_TO_AUTOMODELS:
52
+ raise ValueError(
53
+ f"The task '{task}' is not supported, only {_HEAD_TO_AUTOMODELS.keys()} tasks are supported"
54
+ )
55
 
56
+ if task == "text2text-generation":
57
+ raise ValueError("Export of Seq2Seq models is currently disabled.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ auto_model_class = _HEAD_TO_AUTOMODELS[task]
60
+ ov_files = _find_files_matching_pattern(
61
+ model_id,
62
+ pattern=r"(.*)?openvino(.*)?\_model.xml",
63
+ use_auth_token=oauth_token.token,
64
+ )
65
+
66
+ if len(ov_files) > 0:
67
+ raise Exception(f"Model {model_id} is already converted, skipping..")
68
+
69
+ api = HfApi(token=oauth_token.token)
70
+
71
+ with TemporaryDirectory() as d:
72
+ folder = os.path.join(d, repo_folder_name(repo_id=model_id, repo_type="models"))
73
+ os.makedirs(folder)
74
+ try:
75
+ api.snapshot_download(
76
+ repo_id=model_id, local_dir=folder, allow_patterns=["*.json"]
77
+ )
78
+
79
+ ov_model = eval(auto_model_class).from_pretrained(model_id, export=True)
80
+ ov_model.save_pretrained(folder)
81
+
82
+ """
83
+ if not isinstance(ov_model, OVStableDiffusionPipelineBase):
84
+ model = TasksManager.get_model_from_task(task, model_id)
85
+ exporter_config_class = TasksManager.get_exporter_config_constructor(
86
+ exporter="openvino",
87
+ model=model,
88
+ task=task,
89
+ model_name=model_id,
90
+ model_type=model.config.model_type.replace("_", "-"),
91
+ )
92
+ openvino_config = exporter_config_class(model.config)
93
+ inputs = openvino_config.generate_dummy_inputs(framework="pt")
94
+ ov_outputs = ov_model(**inputs)
95
+ outputs = model(**inputs)
96
+
97
+ for output_name in ov_outputs:
98
+ if isinstance(outputs, torch.Tensor) and not torch.allclose(outputs[output_name], ov_outputs[output_name], atol=1e-3):
99
+ raise ValueError(
100
+ "The exported model does not have the same outputs as the original model. Export interrupted."
101
+ )
102
+ """
103
+
104
+ new_repo_url = api.create_repo(repo_id=new_repo_id, exist_ok=True, private=private_repo)
105
+ new_repo_id = new_repo_url.repo_id
106
+ print("Repo created successfully!", new_repo_url)
107
+
108
+ folder = Path(folder)
109
+ folder_parts = len(folder.parts)
110
+ for file_path in folder.glob("**/*"):
111
+ name = Path(*file_path.parts[folder_parts:])
112
+ if not file_path.is_file() or any(part_name.startswith(".") for part_name in name.parts):
113
+ continue
114
+ try:
115
+ api.upload_file(
116
+ path_or_fileobj=file_path,
117
+ path_in_repo=str(name),
118
+ repo_id=new_repo_id,
119
+ )
120
+ except Exception as e:
121
+ raise Exception(f"Error uploading file {file_path}: {e}")
122
+
123
+ try:
124
+ card = ModelCard.load(model_id, token=oauth_token.token)
125
+ except:
126
+ card = ModelCard("")
127
+
128
+ if card.data.tags is None:
129
+ card.data.tags = []
130
+ card.data.tags.append("openvino")
131
+ card.data.base_model = model_id
132
+ card.text = dedent(
133
+ f"""
134
+ This model was converted to OpenVINO from [`{model_id}`](https://huggingface.co/{model_id}) using [optimum-intel](https://github.com/huggingface/optimum-intel)
135
+ via the [export](https://huggingface.co/spaces/echarlaix/openvino-export) space.
136
+
137
+ First make sure you have optimum-intel installed:
138
+
139
+ ```bash
140
+ pip install optimum[openvino]
141
+ ```
142
+
143
+ To load your model you can do as follows:
144
+
145
+ ```python
146
+ from optimum.intel import {auto_model_class}
147
+
148
+ model_id = "{new_repo_id}"
149
+ model = {auto_model_class}.from_pretrained(model_id)
150
+ ```
151
+ """
152
+ )
153
+ card_path = os.path.join(folder, "README.md")
154
+ card.save(card_path)
155
+
156
+ api.upload_file(
157
+ path_or_fileobj=card_path,
158
+ path_in_repo="README.md",
159
+ repo_id=new_repo_id,
160
+ )
161
+ return f"This model was successfully exported, find it under your repo {new_repo_url}'"
162
+ finally:
163
+ shutil.rmtree(folder, ignore_errors=True)
164
 
165
 
166
  model_id = HuggingfaceHubSearch(
 
168
  placeholder="Search for model id on the hub",
169
  search_type="model",
170
  )
171
+ private_repo = gr.Checkbox(
172
+ value=False,
173
+ label="Private Repo",
174
+ info="Create a private repo under your username",
175
  )
176
  interface = gr.Interface(
177
  fn=export,
178
  inputs=[
179
  model_id,
180
+ private_repo,
181
  ],
182
  outputs=[
183
  gr.Markdown(label="output"),
184
  ],
185
+ title="Export your model to OpenVINO",
186
+ description="This space converts your model to the OpenVINO format using [optimum-intel](https://huggingface.co/docs/optimum/main/intel/openvino/inference) The resulting model will then be pushed on the Hub under your HF user namespace",
187
  api_name=False,
188
  )
189
 
export.py DELETED
@@ -1,170 +0,0 @@
1
- import argparse
2
- import os
3
- import shutil
4
- from pathlib import Path
5
- from tempfile import TemporaryDirectory
6
- from typing import List, Optional, Tuple
7
- import torch
8
-
9
- from huggingface_hub import (
10
- CommitInfo,
11
- CommitOperationAdd,
12
- Discussion,
13
- HfApi,
14
- get_repo_discussions,
15
- hf_hub_download,
16
- )
17
- from huggingface_hub.file_download import repo_folder_name
18
- from optimum.exporters.onnx import validate_model_outputs
19
- from optimum.exporters.tasks import TasksManager
20
- from transformers import AutoConfig, AutoTokenizer, is_torch_available
21
- from optimum.intel.openvino import (
22
- OVModelForAudioClassification,
23
- OVModelForCausalLM,
24
- OVModelForFeatureExtraction,
25
- OVModelForImageClassification,
26
- OVModelForMaskedLM,
27
- OVModelForQuestionAnswering,
28
- OVModelForSeq2SeqLM,
29
- OVModelForSequenceClassification,
30
- OVModelForTokenClassification,
31
- OVStableDiffusionPipeline,
32
- )
33
- from optimum.intel.utils.constant import _TASK_ALIASES
34
- from optimum.intel.openvino.utils import _HEAD_TO_AUTOMODELS
35
- from optimum.exporters import TasksManager
36
-
37
- SPACES_URL = "https://huggingface.co/spaces/echarlaix/openvino-export"
38
-
39
-
40
- def previous_pr(api: "HfApi", model_id: str, pr_title: str) -> Optional["Discussion"]:
41
- try:
42
- discussions = api.get_repo_discussions(repo_id=model_id)
43
- except Exception:
44
- return None
45
- for discussion in discussions:
46
- if (
47
- discussion.status == "open"
48
- and discussion.is_pull_request
49
- and discussion.title == pr_title
50
- ):
51
- return discussion
52
-
53
-
54
- def convert_openvino(model_id: str, task: str, folder: str) -> List:
55
- task = TasksManager.map_from_synonym(task)
56
- if task == "auto":
57
- try:
58
- task = TasksManager.infer_task_from_model(model_id)
59
- except KeyError as e:
60
- raise KeyError(
61
- f"The task could not be automatically inferred. Please provide the task argument with the relevant task from {', '.join(TasksManager.get_all_tasks())}. {e}"
62
- )
63
-
64
- task = _TASK_ALIASES.get(task, task)
65
- if task not in _HEAD_TO_AUTOMODELS:
66
- raise ValueError(f"The task '{task}' is not supported, only {_HEAD_TO_AUTOMODELS.keys()} tasks are supported")
67
-
68
- if task == "text2text-generation":
69
- raise ValueError("Export of Seq2Seq models is currently disabled.")
70
-
71
- auto_model_class = eval(_HEAD_TO_AUTOMODELS[task])
72
- ov_model = auto_model_class.from_pretrained(model_id, export=True)
73
- ov_model.save_pretrained(folder)
74
- if not isinstance(ov_model, OVStableDiffusionPipeline):
75
- try:
76
- model = TasksManager.get_model_from_task(task, model_id)
77
- exporter_config_class = TasksManager.get_exporter_config_constructor(
78
- exporter="openvino",
79
- model=model,
80
- task=task,
81
- model_name=model_id,
82
- model_type=model.config.model_type.replace("_", "-"),
83
- )
84
- openvino_config = exporter_config_class(model.config)
85
- inputs = openvino_config.generate_dummy_inputs(framework="pt")
86
- ov_outputs = ov_model(**inputs)
87
- outputs = model(**inputs)
88
-
89
- for output_name in ov_outputs:
90
- if isinstance(outputs, torch.Tensor) and not torch.allclose(outputs[output_name], ov_outputs[output_name], atol=1e-3):
91
- raise ValueError(
92
- "The exported model does not have the same outputs as the original model. Export interrupted."
93
- )
94
- except Exception as e:
95
- raise
96
-
97
- file_names = {elem for elem in os.listdir(folder) if os.path.isfile(os.path.join(folder, elem))}
98
-
99
- operations = [
100
- CommitOperationAdd(
101
- path_in_repo=file_name, path_or_fileobj=os.path.join(folder, file_name)
102
- )
103
- for file_name in file_names if "openvino" in file_name
104
- ]
105
-
106
- dir_names = set(os.listdir(folder)) - file_names
107
-
108
- for dir_name in dir_names.intersection({"vae_encoder", "vae_decoder", "text_encoder", "unet"}):
109
- operations += [
110
- CommitOperationAdd(
111
- path_in_repo=os.path.join(dir_name, file_name),
112
- path_or_fileobj=os.path.join(folder, dir_name, file_name),
113
- )
114
- for file_name in os.listdir(os.path.join(folder, dir_name)) if "openvino" in file_name
115
- ]
116
-
117
- return operations
118
-
119
-
120
- def convert(
121
- api: "HfApi",
122
- model_id: str,
123
- task: str,
124
- force: bool = False,
125
- ) -> Tuple[int, "CommitInfo"]:
126
- pr_title = "Adding OpenVINO file of this model"
127
- info = api.model_info(model_id)
128
- filenames = set(s.rfilename for s in info.siblings)
129
-
130
- requesting_user = api.whoami()["name"]
131
-
132
- if task == "auto":
133
- try:
134
- task = TasksManager.infer_task_from_model(model_id)
135
- except Exception as e:
136
- return (
137
- f"### Error: {e}. Please pass explicitely the task as it could not be infered.",
138
- None,
139
- )
140
-
141
- with TemporaryDirectory() as d:
142
- folder = os.path.join(d, repo_folder_name(repo_id=model_id, repo_type="models"))
143
- os.makedirs(folder)
144
- new_pr = None
145
- try:
146
- pr = previous_pr(api, model_id, pr_title)
147
- if "openvino_model.xml" in filenames and not force:
148
- raise Exception(f"Model {model_id} is already converted, skipping..")
149
- elif pr is not None and not force:
150
- url = f"https://huggingface.co/{model_id}/discussions/{pr.num}"
151
- new_pr = pr
152
- raise Exception(
153
- f"Model {model_id} already has an open PR check out [{url}]({url})"
154
- )
155
- else:
156
- operations = convert_openvino(model_id, task, folder)
157
-
158
- commit_description = f"""
159
- Beep boop I am the [OpenVINO exporter bot πŸ€–]({SPACES_URL}). On behalf of [{requesting_user}](https://huggingface.co/{requesting_user}), I would like to add to this repository the exported OpenVINO model.
160
- """
161
- new_pr = api.create_commit(
162
- repo_id=model_id,
163
- operations=operations,
164
- commit_message=pr_title,
165
- commit_description=commit_description,
166
- create_pr=True,
167
- )
168
- finally:
169
- shutil.rmtree(folder)
170
- return "0", new_pr