|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# This file is documentation for how to get started with Qwen3-Omni-30B-A3B. |
| 4 | +# This e2e script was tested on a v5p-8 TPU VM. |
| 5 | + |
| 6 | +# This file runs Step 1 on CPU/TPU. |
| 7 | +# 1. Convert the HuggingFace checkpoint (bf16) to MaxText-compatible checkpoint (bf16): |
| 8 | +# Unscanned format is used here as it is better suited for decoding. |
| 9 | +# 2. Run multimodal decoding: text+image, and text+video+audio. |
| 10 | +# --- |
| 11 | +# Example Usage: |
| 12 | +# |
| 13 | +# export HF_TOKEN=<your_hf_token> |
| 14 | +# export BASE_OUTPUT_PATH=gs://your-gcs-bucket/qwen3-omni-30b-a3b_maxtext_ckpt |
| 15 | +# bash tests/end_to_end/tpu/qwen/moe/qwen3-omni-30b-a3b/1_test_qwen3_omni_30b_a3b.sh |
| 16 | +# --- |
| 17 | + |
| 18 | +set -ex |
| 19 | + |
| 20 | +export MODEL_NAME="${MODEL_NAME:-qwen3-omni-30b-a3b}" |
| 21 | +export TOKENIZER_PATH="${TOKENIZER_PATH:-Qwen/Qwen3-Omni-30B-A3B-Instruct}" |
| 22 | + |
| 23 | +# (Optional) Path to your local Hugging Face checkpoint |
| 24 | +export HF_MODEL_PATH="${HF_MODEL_PATH:-}" |
| 25 | + |
| 26 | +# Base output path for MaxText checkpoint and SFT output. |
| 27 | +export BASE_OUTPUT_PATH="${BASE_OUTPUT_PATH:-gs://your-gcs-bucket/qwen3-omni-30b-a3b_maxtext_ckpt}" |
| 28 | + |
| 29 | +if [ -z "${HF_TOKEN}" ]; then |
| 30 | + echo "Error: HF_TOKEN environment variable is not set. Please export your Hugging Face token." |
| 31 | + echo "Example: export HF_TOKEN=hf_..." |
| 32 | + exit 1 |
| 33 | +fi |
| 34 | + |
| 35 | +# Strip trailing slash from base path to avoid malformed URIs |
| 36 | +BASE_OUTPUT_PATH=${BASE_OUTPUT_PATH%/} |
| 37 | +echo "Using BASE_OUTPUT_PATH = ${BASE_OUTPUT_PATH}" |
| 38 | + |
| 39 | +# Install torch for checkpoint conversion |
| 40 | +python3 -m pip install torch --index-url https://download.pytorch.org/whl/cpu |
| 41 | + |
| 42 | +# Setup local HF path argument if one was provided |
| 43 | +HF_LOCAL_ARG="" |
| 44 | +if [ -n "${HF_MODEL_PATH}" ]; then |
| 45 | + HF_LOCAL_ARG="hf_model_path=${HF_MODEL_PATH}" |
| 46 | +fi |
| 47 | + |
| 48 | +# --- |
| 49 | +# Step 1: Checkpoint Conversion |
| 50 | +# Convert HuggingFace checkpoint to MaxText unscanned format (better for decoding). |
| 51 | +# use_multimodal=true is required to include vision/audio encoder weights. |
| 52 | +# --- |
| 53 | +JAX_PLATFORMS=cpu python3 -m maxtext.checkpoint_conversion.to_maxtext src/maxtext/configs/base.yml \ |
| 54 | + model_name=${MODEL_NAME} \ |
| 55 | + base_output_directory=${BASE_OUTPUT_PATH}/unscanned \ |
| 56 | + hf_access_token=${HF_TOKEN} \ |
| 57 | + scan_layers=false \ |
| 58 | + use_multimodal=true \ |
| 59 | + --lazy_load_tensors=False \ |
| 60 | + ${HF_LOCAL_ARG} |
| 61 | + |
| 62 | +UNSCANNED_CKPT_PATH=${BASE_OUTPUT_PATH}/unscanned/0/items |
| 63 | + |
| 64 | +# --- |
| 65 | +# Step 2a: Multimodal Decode — text + image |
| 66 | +# Uses a test image from the repo assets. |
| 67 | +# max_prefill_predict_length accounts for image tokens (~256) + text prompt tokens. |
| 68 | +# --- |
| 69 | +python3 -m maxtext.inference.decode src/maxtext/configs/base.yml \ |
| 70 | + model_name=${MODEL_NAME} \ |
| 71 | + tokenizer_path=${TOKENIZER_PATH} \ |
| 72 | + tokenizer_type=huggingface \ |
| 73 | + load_parameters_path=${UNSCANNED_CKPT_PATH} \ |
| 74 | + hf_access_token=${HF_TOKEN} \ |
| 75 | + per_device_batch_size=1 \ |
| 76 | + run_name=qwen3_omni_decode_image \ |
| 77 | + steps=1 \ |
| 78 | + async_checkpointing=false \ |
| 79 | + scan_layers=false \ |
| 80 | + use_multimodal=true \ |
| 81 | + prompt='Describe this image in one sentence.' \ |
| 82 | + image_path='tests/assets/test_image.jpg' \ |
| 83 | + max_prefill_predict_length=512 \ |
| 84 | + max_target_length=542 \ |
| 85 | + add_bos=false \ |
| 86 | + attention=dot_product \ |
| 87 | + ici_tensor_parallelism=4 |
| 88 | + |
| 89 | +# --- |
| 90 | +# Step 2b: Multimodal Decode — text + video + audio |
| 91 | +# Passes a video file and enables audio processing from the video track. |
| 92 | +# max_prefill_predict_length is set higher to accommodate video frame tokens (~1126) |
| 93 | +# plus audio tokens (~77) plus text prompt tokens. |
| 94 | +# --- |
| 95 | +python3 -m maxtext.inference.decode src/maxtext/configs/base.yml \ |
| 96 | + model_name=${MODEL_NAME} \ |
| 97 | + tokenizer_path=${TOKENIZER_PATH} \ |
| 98 | + tokenizer_type=huggingface \ |
| 99 | + load_parameters_path=${UNSCANNED_CKPT_PATH} \ |
| 100 | + hf_access_token=${HF_TOKEN} \ |
| 101 | + per_device_batch_size=1 \ |
| 102 | + run_name=qwen3_omni_decode_video \ |
| 103 | + steps=1 \ |
| 104 | + async_checkpointing=false \ |
| 105 | + scan_layers=false \ |
| 106 | + use_multimodal=true \ |
| 107 | + use_audio_in_video=true \ |
| 108 | + prompt='What can you see and hear? Answer in one short sentence.' \ |
| 109 | + video_path='tests/assets/test_video.mp4' \ |
| 110 | + max_prefill_predict_length=1240 \ |
| 111 | + max_target_length=1280 \ |
| 112 | + add_bos=false \ |
| 113 | + attention=dot_product \ |
| 114 | + ici_tensor_parallelism=4 |
0 commit comments