badaoui HF Staff commited on
Commit
3b3fb78
·
verified ·
1 Parent(s): 3092c7d

Update optimum_neuron_export.py

Browse files
Files changed (1) hide show
  1. optimum_neuron_export.py +100 -119
optimum_neuron_export.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import os
3
  import shutil
4
  from tempfile import TemporaryDirectory, NamedTemporaryFile
@@ -36,25 +35,25 @@ from optimum.neuron import (
36
  NeuronModelForCausalLM,
37
  NeuronModelForSeq2SeqLM,
38
  )
39
- from diffusers import (
40
- StableDiffusionPipeline,
41
- StableDiffusionImg2ImgPipeline,
42
- StableDiffusionInpaintPipeline,
43
- StableDiffusionInstructPix2PixPipeline,
44
- LatentConsistencyModelPipeline,
45
- StableDiffusionXLPipeline,
46
- StableDiffusionXLImg2ImgPipeline,
47
- StableDiffusionXLInpaintPipeline,
48
- StableDiffusionControlNetPipeline,
49
- StableDiffusionXLControlNetPipeline,
50
- PixArtAlphaPipeline,
51
- PixArtSigmaPipeline,
52
- FluxPipeline
 
53
  )
54
 
55
  from optimum.neuron.cache import synchronize_hub_cache
56
  from synchronizer import synchronize_hub_cache_with_pr
57
- from optimum.exporters.neuron import main_export, build_stable_diffusion_components_mandatory_shapes
58
 
59
  SPACES_URL = "https://huggingface.co/spaces/optimum/neuron-export"
60
  CUSTOM_CACHE_REPO = os.getenv("CUSTOM_CACHE_REPO")
@@ -82,26 +81,22 @@ TASK_TO_MODEL_CLASS = {
82
 
83
  # Diffusion pipeline mapping
84
  DIFFUSION_PIPELINE_MAPPING = {
85
- "text-to-image": StableDiffusionPipeline,
86
- "image-to-image": StableDiffusionImg2ImgPipeline,
87
- "inpaint": StableDiffusionInpaintPipeline,
88
- "instruct-pix2pix": StableDiffusionInstructPix2PixPipeline,
89
- "latent-consistency": LatentConsistencyModelPipeline,
90
- "stable-diffusion": StableDiffusionPipeline,
91
- "stable-diffusion-xl": StableDiffusionXLPipeline,
92
- "stable-diffusion-xl-img2img": StableDiffusionXLImg2ImgPipeline,
93
- "stable-diffusion-xl-inpaint": StableDiffusionXLInpaintPipeline,
94
- "controlnet": StableDiffusionControlNetPipeline,
95
- "controlnet-xl": StableDiffusionXLControlNetPipeline,
96
- "pixart-alpha": PixArtAlphaPipeline,
97
- "pixart-sigma": PixArtSigmaPipeline,
98
- "flux": FluxPipeline,
99
  }
100
 
101
- ENCODER_TASKS = {"feature-extraction","sentence-transformers","fill-mask","question-answering","text-classification","token-classification","multiple-choice","image-classification","semantic-segmentation","object-detection","audio-classification","audio-frame-classification","automatic-speech-recognition","audio-xvector"}
102
- DECODER_TASKS = {"text-generation"}
103
- SEQ2SEQ_TAKS = {"text2text-generation"}
104
-
105
  def get_default_inputs(task_or_pipeline: str) -> Dict[str, int]:
106
  """Get default input shapes based on task type or diffusion pipeline type."""
107
  if task_or_pipeline in ["feature-extraction", "sentence-transformers", "fill-mask", "question-answering", "text-classification", "token-classification","text-generation"]:
@@ -120,6 +115,30 @@ def get_default_inputs(task_or_pipeline: str) -> Dict[str, int]:
120
  # Default to text-based shapes
121
  return {"batch_size": 1, "sequence_length": 128}
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  def previous_pr(api: "HfApi", model_id: str, pr_title: str) -> Optional["Discussion"]:
124
  try:
125
  discussions = api.get_repo_discussions(repo_id=model_id)
@@ -134,102 +153,69 @@ def previous_pr(api: "HfApi", model_id: str, pr_title: str) -> Optional["Discuss
134
  return discussion
135
  return None
136
 
137
- def export(model_id: str, task_or_pipeline: str, model_type: str, folder: str):
138
- """Export model to Neuron format. This is NOT a generator."""
139
-
140
- print(f"📦 Exporting model `{model_id}` for task `{task_or_pipeline}`...")
141
-
142
- inputs = get_default_inputs(task_or_pipeline)
143
- print(f"🔧 Using default inputs: {inputs}")
144
 
145
- # ENCODER and SEQ2SEQ tasks
146
- if task_or_pipeline in ENCODER_TASKS or task_or_pipeline in SEQ2SEQ_TAKS:
147
- result = main_export(
148
- model_name_or_path=model_id,
149
- output=folder,
150
- token=HF_TOKEN,
151
- task=task_or_pipeline,
152
- cpu_backend=True,
153
- do_validation=False,
154
- compiler_kwargs={},
155
- **inputs,
156
- )
157
 
158
- # DECODER tasks
159
- elif task_or_pipeline in DECODER_TASKS:
160
- neuron_config = NeuronModelForCausalLM.get_neuron_config(
161
- model_name_or_path=model_id,
162
- **inputs
163
- )
164
- neuron_model = NeuronModelForCausalLM.export(
165
- model_id=model_id,
166
- neuron_config=neuron_config,
167
- token=HF_TOKEN,
168
- )
169
- neuron_model.save_pretrained(folder)
170
 
171
- # DIFFUSION tasks
172
- elif task_or_pipeline in DIFFUSION_PIPELINE_MAPPING:
173
- model_class = DIFFUSION_PIPELINE_MAPPING.get(task_or_pipeline)
174
- model = model_class.from_pretrained(model_id)
175
- input_shapes = build_stable_diffusion_components_mandatory_shapes(**inputs)
176
- compiler_kwargs = {"auto_cast": "matmul", "auto_cast_type": "bf16"}
177
-
178
- result = main_export(
179
- model_name_or_path=model_id,
180
- output=folder,
181
- compiler_kwargs=compiler_kwargs,
182
- torch_dtype= torch.bfloat16,
183
- token=HF_TOKEN,
184
- library_name=model_type,
185
- tensor_parallel_size=4,
186
- model=model,
187
- **inputs_shapes,
188
- )
189
-
190
- else:
191
- raise ValueError(f"Unsupported task or pipeline: {task_or_pipeline}")
192
 
193
- print(f"✅ Export completed successfully to {folder}")
194
-
195
-
196
- def export_and_git_add(model_id: str, task_or_pipeline: str, model_type: str, folder: str, token: str):
197
- """Export model and prepare git operations. This IS a generator."""
 
 
 
198
 
199
  try:
200
- # Actually execute the export (not a generator anymore)
201
- export(model_id, task_or_pipeline, model_type, folder)
202
- yield "✅ Export completed successfully."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  except Exception as e:
204
  yield f"❌ Export failed with error: {e}"
205
  raise
206
 
207
- # Verify that files were actually created
208
- if not os.path.exists(folder) or not os.listdir(folder):
209
- error_msg = f"❌ Export folder is empty or doesn't exist: {folder}"
210
- yield error_msg
211
- raise Exception(error_msg)
212
-
213
- yield f"📁 Found exported files in {folder}"
214
-
215
- # Collect all files for git operations
216
  operations = []
217
- file_count = 0
218
  for root, _, files in os.walk(folder):
219
  for filename in files:
220
  file_path = os.path.join(root, filename)
221
  repo_path = os.path.relpath(file_path, folder)
222
  operations.append(CommitOperationAdd(path_in_repo=repo_path, path_or_fileobj=file_path))
223
- file_count += 1
224
-
225
- yield f"📦 Prepared {file_count} files for upload"
226
 
227
- if file_count == 0:
228
- error_msg = "❌ No files found to upload after export"
229
- yield error_msg
230
- raise Exception(error_msg)
231
-
232
- # Update model card
233
  try:
234
  card = ModelCard.load(model_id, token=token)
235
  if not hasattr(card.data, "tags") or card.data.tags is None:
@@ -246,18 +232,14 @@ def export_and_git_add(model_id: str, task_or_pipeline: str, model_type: str, fo
246
  readme_op.path_or_fileobj = readme_path
247
  else:
248
  operations.append(CommitOperationAdd(path_in_repo="README.md", path_or_fileobj=readme_path))
249
-
250
- yield "📝 Updated model card with neuron tag"
251
 
252
  except Exception as e:
253
  yield f"⚠️ Warning: Could not update model card: {e}"
254
 
255
- # Return the operations
256
  yield ("__RETURN__", operations)
257
 
258
  def generate_neuron_repo_name(api, original_model_id: str, task_or_pipeline: str, token:str) -> str:
259
  """Generate a name for the Neuron-optimized repository."""
260
- # Replace '©' with '-' and add neuron suffix
261
  requesting_user = api.whoami(token=token)["name"]
262
  base_name = original_model_id.replace('/', '-')
263
  return f"{requesting_user}/{base_name}-neuron"
@@ -475,7 +457,6 @@ Generated using: [Optimum Neuron Compiler Space]({SPACES_URL})
475
  yield f"❌ Failed to create README PR: {e}"
476
  raise
477
 
478
- # --- Updated upload_to_custom_repo function ---
479
  def upload_to_custom_repo(
480
  operations: List[CommitOperationAdd],
481
  custom_repo_id: str,
@@ -684,4 +665,4 @@ These files were generated to accelerate model loading on AWS Neuron devices.
684
  except Exception as e:
685
  yield "1", f"❌ Conversion failed with a critical error: {e}"
686
  # Re-raise the exception to be caught by the outer try-except in the Gradio app if needed
687
- raise
 
 
1
  import os
2
  import shutil
3
  from tempfile import TemporaryDirectory, NamedTemporaryFile
 
35
  NeuronModelForCausalLM,
36
  NeuronModelForSeq2SeqLM,
37
  )
38
+ from optimum.neuron import (
39
+ NeuronDiffusionPipelineBase,
40
+ NeuronStableDiffusionPipeline,
41
+ NeuronStableDiffusionImg2ImgPipeline,
42
+ NeuronStableDiffusionInpaintPipeline,
43
+ NeuronStableDiffusionInstructPix2PixPipeline,
44
+ NeuronLatentConsistencyModelPipeline,
45
+ NeuronStableDiffusionXLPipeline,
46
+ NeuronStableDiffusionXLImg2ImgPipeline,
47
+ NeuronStableDiffusionXLInpaintPipeline,
48
+ NeuronStableDiffusionControlNetPipeline,
49
+ NeuronStableDiffusionXLControlNetPipeline,
50
+ NeuronPixArtAlphaPipeline,
51
+ NeuronPixArtSigmaPipeline,
52
+ NeuronFluxPipeline
53
  )
54
 
55
  from optimum.neuron.cache import synchronize_hub_cache
56
  from synchronizer import synchronize_hub_cache_with_pr
 
57
 
58
  SPACES_URL = "https://huggingface.co/spaces/optimum/neuron-export"
59
  CUSTOM_CACHE_REPO = os.getenv("CUSTOM_CACHE_REPO")
 
81
 
82
  # Diffusion pipeline mapping
83
  DIFFUSION_PIPELINE_MAPPING = {
84
+ "text-to-image": NeuronStableDiffusionPipeline,
85
+ "image-to-image": NeuronStableDiffusionImg2ImgPipeline,
86
+ "inpaint": NeuronStableDiffusionInpaintPipeline,
87
+ "instruct-pix2pix": NeuronStableDiffusionInstructPix2PixPipeline,
88
+ "latent-consistency": NeuronLatentConsistencyModelPipeline,
89
+ "stable-diffusion": NeuronStableDiffusionPipeline,
90
+ "stable-diffusion-xl": NeuronStableDiffusionXLPipeline,
91
+ "stable-diffusion-xl-img2img": NeuronStableDiffusionXLImg2ImgPipeline,
92
+ "stable-diffusion-xl-inpaint": NeuronStableDiffusionXLInpaintPipeline,
93
+ "controlnet": NeuronStableDiffusionControlNetPipeline,
94
+ "controlnet-xl": NeuronStableDiffusionXLControlNetPipeline,
95
+ "pixart-alpha": NeuronPixArtAlphaPipeline,
96
+ "pixart-sigma": NeuronPixArtSigmaPipeline,
97
+ "flux": NeuronFluxPipeline,
98
  }
99
 
 
 
 
 
100
  def get_default_inputs(task_or_pipeline: str) -> Dict[str, int]:
101
  """Get default input shapes based on task type or diffusion pipeline type."""
102
  if task_or_pipeline in ["feature-extraction", "sentence-transformers", "fill-mask", "question-answering", "text-classification", "token-classification","text-generation"]:
 
115
  # Default to text-based shapes
116
  return {"batch_size": 1, "sequence_length": 128}
117
 
118
+ def find_neuron_cache_artifacts(cache_base_dir: str = "/var/tmp/neuron-compile-cache") -> Optional[str]:
119
+ """
120
+ Find the most recently created Neuron cache artifacts directory.
121
+ Returns the path to the MODULE directory containing the compiled artifacts.
122
+ """
123
+ if not os.path.exists(cache_base_dir):
124
+ return None
125
+
126
+ # Find all MODULE directories
127
+ module_dirs = []
128
+ for root, dirs, files in os.walk(cache_base_dir):
129
+ for d in dirs:
130
+ if d.startswith("MODULE_"):
131
+ full_path = os.path.join(root, d)
132
+ # Check if it contains the expected files
133
+ if os.path.exists(os.path.join(full_path, "model.neuron")):
134
+ module_dirs.append(full_path)
135
+
136
+ if not module_dirs:
137
+ return None
138
+
139
+ # Return the most recently modified directory
140
+ return max(module_dirs, key=os.path.getmtime)
141
+
142
  def previous_pr(api: "HfApi", model_id: str, pr_title: str) -> Optional["Discussion"]:
143
  try:
144
  discussions = api.get_repo_discussions(repo_id=model_id)
 
153
  return discussion
154
  return None
155
 
156
+ def export_and_git_add(model_id: str, task_or_pipeline: str, model_type: str, folder: str, token: str) -> Any:
 
 
 
 
 
 
157
 
158
+ yield f"📦 Exporting model `{model_id}` for task `{task_or_pipeline}`..."
 
 
 
 
 
 
 
 
 
 
 
159
 
160
+ model_class = TASK_TO_MODEL_CLASS.get(task_or_pipeline) if model_type == "transformers" else DIFFUSION_PIPELINE_MAPPING.get(task_or_pipeline)
161
+ if model_class is None:
162
+ supported = list(TASK_TO_MODEL_CLASS.keys()) if model_type == "transformers" else list(DIFFUSION_PIPELINE_MAPPING.keys())
163
+ raise Exception(f"❌ Unsupported task/pipeline: {task_or_pipeline}. Supported: {supported}")
 
 
 
 
 
 
 
 
164
 
165
+ inputs = get_default_inputs(task_or_pipeline)
166
+ compiler_configs = {"auto_cast": "matmul", "auto_cast_type": "bf16", "instance_type": "inf2"}
167
+ yield f"🔧 Using default inputs: {inputs}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
+ # Clear any old cache artifacts before export
170
+ cache_base_dir = "/var/tmp/neuron-compile-cache"
171
+ initial_cache_dirs = set()
172
+ if os.path.exists(cache_base_dir):
173
+ for root, dirs, _ in os.walk(cache_base_dir):
174
+ for d in dirs:
175
+ if d.startswith("MODULE_"):
176
+ initial_cache_dirs.add(os.path.join(root, d))
177
 
178
  try:
179
+ # Trigger the export/compilation
180
+ model = model_class.from_pretrained(
181
+ model_id,
182
+ export=True,
183
+ tensor_parallel_size=4,
184
+ token=HF_TOKEN,
185
+ **compiler_configs,
186
+ **inputs,
187
+ )
188
+
189
+ yield "✅ Export/compilation completed successfully."
190
+
191
+ # Find the newly created cache artifacts
192
+ yield "🔍 Locating compiled artifacts in Neuron cache..."
193
+ cache_artifact_dir = find_neuron_cache_artifacts(cache_base_dir)
194
+
195
+ if not cache_artifact_dir:
196
+ raise Exception("❌ Could not find compiled artifacts in Neuron cache")
197
+
198
+ yield f"📂 Found artifacts at: {cache_artifact_dir}"
199
+
200
+ # Copy artifacts from cache to our target folder
201
+ yield f"📋 Copying artifacts to export folder..."
202
+ if os.path.exists(folder):
203
+ shutil.rmtree(folder)
204
+ shutil.copytree(cache_artifact_dir, folder)
205
+
206
+ yield f"✅ Artifacts successfully copied to {folder}"
207
+
208
  except Exception as e:
209
  yield f"❌ Export failed with error: {e}"
210
  raise
211
 
 
 
 
 
 
 
 
 
 
212
  operations = []
 
213
  for root, _, files in os.walk(folder):
214
  for filename in files:
215
  file_path = os.path.join(root, filename)
216
  repo_path = os.path.relpath(file_path, folder)
217
  operations.append(CommitOperationAdd(path_in_repo=repo_path, path_or_fileobj=file_path))
 
 
 
218
 
 
 
 
 
 
 
219
  try:
220
  card = ModelCard.load(model_id, token=token)
221
  if not hasattr(card.data, "tags") or card.data.tags is None:
 
232
  readme_op.path_or_fileobj = readme_path
233
  else:
234
  operations.append(CommitOperationAdd(path_in_repo="README.md", path_or_fileobj=readme_path))
 
 
235
 
236
  except Exception as e:
237
  yield f"⚠️ Warning: Could not update model card: {e}"
238
 
 
239
  yield ("__RETURN__", operations)
240
 
241
  def generate_neuron_repo_name(api, original_model_id: str, task_or_pipeline: str, token:str) -> str:
242
  """Generate a name for the Neuron-optimized repository."""
 
243
  requesting_user = api.whoami(token=token)["name"]
244
  base_name = original_model_id.replace('/', '-')
245
  return f"{requesting_user}/{base_name}-neuron"
 
457
  yield f"❌ Failed to create README PR: {e}"
458
  raise
459
 
 
460
  def upload_to_custom_repo(
461
  operations: List[CommitOperationAdd],
462
  custom_repo_id: str,
 
665
  except Exception as e:
666
  yield "1", f"❌ Conversion failed with a critical error: {e}"
667
  # Re-raise the exception to be caught by the outer try-except in the Gradio app if needed
668
+ raise