Skip to content

Commit b39aa75

Browse files
author
pytorchbot
committed
2026-10-19 nightly release (adc713a)
1 parent 5e73fa2 commit b39aa75

14 files changed

Lines changed: 1208 additions & 28 deletions

File tree

‎.ci/scripts/export_model_artifact.sh‎

Lines changed: 150 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55
# This source code is licensed under the BSD-style license found in the
66
# LICENSE file in the root directory of this source tree.
77

8-
# Export model to CUDA/Metal/XNNPACK format with optional quantization
8+
# Export model to CUDA/Metal/XNNPACK/Vulkan format with optional quantization
99

1010
show_help() {
1111
cat << EOF
1212
Usage: export_model_artifact.sh <device> <hf_model> [quant_name] [output_dir] [mode]
1313
14-
Export a HuggingFace model to CUDA/Metal/XNNPACK format with optional quantization.
14+
Export a HuggingFace model to CUDA/Metal/XNNPACK/Vulkan format with optional quantization.
1515
1616
Arguments:
17-
device cuda, metal, or xnnpack (required)
17+
device cuda, metal, xnnpack, or vulkan (required)
1818
1919
hf_model HuggingFace model ID (required)
2020
Supported models:
@@ -26,14 +26,20 @@ Arguments:
2626
- nvidia/parakeet-tdt
2727
- facebook/dinov2-small-imagenet1k-1-layer
2828
- meta-models/Muse-Glimmer-30B-GGUF
29+
- Qwen/Qwen3-0.6B
30+
- unsloth/Llama-3.2-1B-Instruct
31+
- microsoft/Phi-4-mini-instruct
2932
3033
quant_name Quantization type (optional, default: non-quantized)
3134
Options:
3235
- non-quantized
3336
- quantized-int4-tile-packed (CUDA only)
34-
- quantized-int4-weight-only (CUDA only)
37+
- quantized-int4-weight-only (CUDA/Vulkan)
3538
- quantized-int4-metal (Metal only)
36-
- quantized-8da4w (XNNPACK only)
39+
- quantized-8da4w (XNNPACK/Vulkan)
40+
- quantized-8da4w-fp16 (Vulkan only)
41+
- quantized-8da4w-embedding (Vulkan only)
42+
- quantized-8da4w-fp16-embedding (Vulkan only)
3743
- kquant-17gb (Muse Glimmer only)
3844
- kquant-dynamic (Muse Glimmer only)
3945
@@ -85,6 +91,9 @@ QUANT_NAME="${3:-non-quantized}"
8591
OUTPUT_DIR="${4:-.}"
8692
MODE="${5:-}"
8793

94+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
95+
EXECUTORCH_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
96+
8897
# Validate mode if specified
8998
if [ -n "$MODE" ]; then
9099
case "$MODE" in
@@ -118,15 +127,18 @@ case "$DEVICE" in
118127
;;
119128
metal)
120129
;;
121-
xnnpack)
130+
xnnpack|vulkan)
122131
;;
123132
*)
124133
echo "Error: Unsupported device '$DEVICE'"
125-
echo "Supported devices: cuda, cuda-windows, metal, xnnpack"
134+
echo "Supported devices: cuda, cuda-windows, metal, xnnpack, vulkan"
126135
exit 1
127136
;;
128137
esac
129138

139+
CHECKPOINT_SHA256=""
140+
EXPORT_METADATA=""
141+
130142
# Determine model configuration based on HF model ID
131143
case "$HF_MODEL" in
132144
mistralai/Voxtral-Mini-3B-2507)
@@ -168,6 +180,41 @@ case "$HF_MODEL" in
168180
EXTRA_PIP=""
169181
PREPROCESSOR_FEATURE_SIZE=""
170182
PREPROCESSOR_OUTPUT=""
183+
if [ "$DEVICE" = "vulkan" ]; then
184+
test "$QUANT_NAME" = quantized-8da4w-fp16-embedding
185+
MODEL_NAME=qwen3_0_6b
186+
REVISION=c1899de289a04d12100db370d81485cdf75e47ca
187+
CONVERTER=executorch.examples.models.qwen3.convert_weights
188+
PARAMS="$EXECUTORCH_ROOT/examples/models/qwen3/config/0_6b_config.json"
189+
PROMPT=$'<|im_start|>user\nWhat is the capital of France?<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n'
190+
EXPECTED_OUTPUT=Paris
191+
fi
192+
;;
193+
unsloth/Llama-3.2-1B-Instruct)
194+
if [ "$DEVICE" != "vulkan" ]; then
195+
echo "Error: $HF_MODEL is only supported with Vulkan"
196+
exit 1
197+
fi
198+
MODEL_NAME=llama3_2
199+
REVISION=5a8abab4a5d6f164389b1079fb721cfab8d7126c
200+
CONVERTER=executorch.examples.models.llama.convert_weights
201+
CHECKPOINT_SHA256=1ff795ff6a07e6a68085d206fb84417da2f083f68391c2843cd2b8ac6df8538f
202+
EXPORT_METADATA='{"append_eos_to_prompt":0,"get_bos_id":128000,"get_eos_ids":[128009,128001]}'
203+
PROMPT=$'<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\nWhat is the capital of France?<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n'
204+
EXPECTED_OUTPUT="The capital of France is Paris"
205+
;;
206+
microsoft/Phi-4-mini-instruct)
207+
if [ "$DEVICE" != "vulkan" ]; then
208+
echo "Error: $HF_MODEL is only supported with Vulkan"
209+
exit 1
210+
fi
211+
test "$QUANT_NAME" = quantized-8da4w-embedding
212+
MODEL_NAME=phi_4_mini
213+
REVISION=cfbefacb99257ffa30c83adab238a50856ac3083
214+
CONVERTER=executorch.examples.models.phi_4_mini.convert_weights
215+
PARAMS="$EXECUTORCH_ROOT/examples/models/phi_4_mini/config/config.json"
216+
PROMPT=$'<|user|>\nWhat is the capital of France?<|end|>\n<|assistant|>\n'
217+
EXPECTED_OUTPUT="The capital of France is Paris"
171218
;;
172219
nvidia/parakeet-tdt)
173220
MODEL_NAME="parakeet"
@@ -232,6 +279,10 @@ case "$HF_MODEL" in
232279
;;
233280
esac
234281

282+
QMODE=8da4w
283+
FORCE_FP16=false
284+
EMBEDDING_QUANTIZE=""
285+
235286
# Determine quantization args based on quant name
236287
case "$QUANT_NAME" in
237288
non-quantized)
@@ -250,6 +301,7 @@ case "$QUANT_NAME" in
250301
exit 1
251302
fi
252303
EXTRA_ARGS="--qlinear_encoder 4w"
304+
QMODE=4w
253305
;;
254306
quantized-int4-metal)
255307
if [ "$DEVICE" != "metal" ]; then
@@ -259,12 +311,25 @@ case "$QUANT_NAME" in
259311
EXTRA_ARGS="--qlinear fpa4w --qlinear_encoder fpa4w"
260312
;;
261313
quantized-8da4w)
262-
if [ "$DEVICE" != "xnnpack" ]; then
263-
echo "Error: quantized-8da4w is only supported with xnnpack device"
314+
if [ "$DEVICE" != "xnnpack" ] && [ "$DEVICE" != "vulkan" ]; then
315+
echo "Error: quantized-8da4w is only supported with xnnpack or vulkan device"
264316
exit 1
265317
fi
266318
EXTRA_ARGS="--qlinear 8da4w --qlinear_group_size 32 --qlinear_encoder 8da4w --qlinear_encoder_group_size 32"
267319
;;
320+
quantized-8da4w-fp16|quantized-8da4w-embedding|quantized-8da4w-fp16-embedding)
321+
if [ "$DEVICE" != "vulkan" ]; then
322+
echo "Error: Quantization '$QUANT_NAME' is only supported with Vulkan"
323+
exit 1
324+
fi
325+
case "$QUANT_NAME" in
326+
*-fp16*) FORCE_FP16=true ;;
327+
esac
328+
case "$QUANT_NAME" in
329+
*-embedding) EMBEDDING_QUANTIZE=4,64 ;;
330+
esac
331+
EXTRA_ARGS=""
332+
;;
268333
kquant-17gb|kquant-dynamic)
269334
if [ "$HF_MODEL" != "meta-models/Muse-Glimmer-30B-GGUF" ]; then
270335
echo "Error: Quantization '$QUANT_NAME' can only be used with Muse Glimmer model"
@@ -295,6 +360,82 @@ if [ "$MODEL_NAME" = "muse_glimmer" ]; then
295360
fi
296361
fi
297362

363+
if [ "$DEVICE" = "vulkan" ]; then
364+
case "$MODEL_NAME" in
365+
qwen3_0_6b|llama3_2|phi_4_mini) ;;
366+
*) echo "Error: Unsupported Vulkan model: $HF_MODEL"; exit 1 ;;
367+
esac
368+
case "$QUANT_NAME" in
369+
quantized-8da4w|quantized-8da4w-fp16|quantized-8da4w-embedding|quantized-8da4w-fp16-embedding|quantized-int4-weight-only) ;;
370+
*) echo "Error: Unsupported Vulkan quantization: $QUANT_NAME"; exit 1 ;;
371+
esac
372+
mkdir -p "$OUTPUT_DIR"
373+
OUTPUT_DIR="$(cd "$OUTPUT_DIR" && pwd)"
374+
(
375+
cd "$EXECUTORCH_ROOT"
376+
MAX_SIZE_MIB=""
377+
if [ "$MODEL_NAME" = "llama3_2" ] && [ -n "$EMBEDDING_QUANTIZE" ]; then MAX_SIZE_MIB=770; fi
378+
HASH_ARGS=(--metadata-path "$OUTPUT_DIR/metadata.json")
379+
if [ -n "${CHECKPOINT_SHA256:-}" ]; then HASH_ARGS+=(--expected-sha256 "$CHECKPOINT_SHA256"); fi
380+
python - "$OUTPUT_DIR" "$HF_MODEL" "$REVISION" "$QUANT_NAME" "$QMODE" "$FORCE_FP16" \
381+
"$EMBEDDING_QUANTIZE" "$MAX_SIZE_MIB" "$PROMPT" "$EXPECTED_OUTPUT" <<'PY'
382+
import json
383+
import sys
384+
from pathlib import Path
385+
386+
output, model, revision, variant, qmode, fp16, embedding, size, prompt, expected = sys.argv[1:]
387+
metadata = {
388+
"model": model, "revision": revision, "variant": variant, "quantization": qmode,
389+
"group_size": 64, "embedding_quantize": embedding, "dtype": "fp32",
390+
"vulkan_force_fp16": fp16 == "true", "max_seq_length": 2048, "max_context_length": 2048,
391+
"use_kv_cache": True, "use_sdpa_with_kv_cache": True, "max_size_mib": int(size) if size else None,
392+
"prompt": prompt, "expected": expected, "temperature": 0, "max_new_tokens": 50,
393+
}
394+
if model == "unsloth/Llama-3.2-1B-Instruct":
395+
metadata["original_model"] = "meta-llama/Llama-3.2-1B-Instruct"
396+
output = Path(output)
397+
(output / "metadata.json").write_text(json.dumps(metadata, indent=2) + "\n")
398+
(output / "prompt.txt").write_text(prompt)
399+
(output / "expected_output.txt").write_text(expected + "\n")
400+
PY
401+
SNAPSHOT=$(python -m executorch.examples.models.llama.hf_download snapshot "$HF_MODEL" \
402+
--revision "$REVISION" "${HASH_ARGS[@]}" \
403+
2> "$OUTPUT_DIR/download.log")
404+
cp "$SNAPSHOT/tokenizer.json" "$SNAPSHOT/tokenizer_config.json" "$OUTPUT_DIR/"
405+
VULKAN_CHECKPOINT_DIR=$(mktemp -d "${RUNNER_TEMP:-/tmp}/vulkan_llm_XXXXXX")
406+
trap 'rm -rf "$VULKAN_CHECKPOINT_DIR"' EXIT
407+
CONVERT_ARGS=("$SNAPSHOT" "$VULKAN_CHECKPOINT_DIR/checkpoint.pth")
408+
if [ "$MODEL_NAME" = "llama3_2" ]; then
409+
PARAMS="$OUTPUT_DIR/params.json"
410+
CONVERT_ARGS=(hf-to-meta "${CONVERT_ARGS[@]}" --params-output "$PARAMS")
411+
fi
412+
python -m "$CONVERTER" "${CONVERT_ARGS[@]}" > "$OUTPUT_DIR/convert.log" 2>&1
413+
VULKAN_EXPORT_ARGS=(
414+
"base.model_class=$MODEL_NAME" "base.checkpoint=\"$VULKAN_CHECKPOINT_DIR/checkpoint.pth\""
415+
"base.params=\"$PARAMS\"" "base.tokenizer_path=\"$OUTPUT_DIR/tokenizer.json\""
416+
model.dtype_override=fp32 model.use_kv_cache=true model.use_sdpa_with_kv_cache=true
417+
model.enable_dynamic_shape=true backend.vulkan.enabled=true "backend.vulkan.force_fp16=$FORCE_FP16"
418+
"quantization.qmode=$QMODE" quantization.group_size=64
419+
export.max_seq_length=2048 export.max_context_length=2048
420+
"export.output_dir=\"$OUTPUT_DIR\"" "export.output_name=\"$OUTPUT_DIR/model.pte\"" debug.verbose=true
421+
)
422+
if [ -n "$EMBEDDING_QUANTIZE" ]; then
423+
VULKAN_EXPORT_ARGS+=("quantization.embedding_quantize=\"$EMBEDDING_QUANTIZE\"")
424+
fi
425+
if [ -n "${EXPORT_METADATA:-}" ]; then VULKAN_EXPORT_ARGS+=("base.metadata='$EXPORT_METADATA'"); fi
426+
rm -f "$OUTPUT_DIR/model.pte"
427+
python -m extension.llm.export.export_llm "${VULKAN_EXPORT_ARGS[@]}" > "$OUTPUT_DIR/export.log" 2>&1
428+
test -f "$OUTPUT_DIR/model.pte"
429+
python - "$OUTPUT_DIR/model.pte" "$QMODE" "$EMBEDDING_QUANTIZE" "$MAX_SIZE_MIB" <<'PY'
430+
import sys
431+
from executorch.backends.vulkan.test.utils import verify_vulkan_llm_pte
432+
433+
verify_vulkan_llm_pte(sys.argv[1], sys.argv[2], bool(sys.argv[3]), int(sys.argv[4]) if sys.argv[4] else None)
434+
PY
435+
)
436+
exit 0
437+
fi
438+
298439
# Downloads and compiler caches go in scratch dirs outside OUTPUT_DIR because the CI job
299440
# templates upload OUTPUT_DIR even when the job fails. A failed export also empties
300441
# OUTPUT_DIR, but only if it started out empty, so a local run with output_dir=. cannot

0 commit comments

Comments
 (0)