Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ used by ort-extensions image processors. For an overview, see the parent
"attrs": { "color_space": "RGB" }
}
},
{
"operation": {
"name": "convert_to_rgb",
"type": "ConvertRGB"
}
},
{
"operation": {
"name": "resize",
Expand Down Expand Up @@ -63,8 +57,7 @@ used by ort-extensions image processors. For an overview, see the parent
"type": "Normalize",
"attrs": {
"mean": [0.48145466, 0.4578275, 0.40821073],
"std": [0.26862954, 0.26130258, 0.27577711],
"qwen2_5_vl": 1
"std": [0.26862954, 0.26130258, 0.27577711]
}
}
},
Expand All @@ -91,12 +84,22 @@ used by ort-extensions image processors. For an overview, see the parent
| Type | Purpose | Key attrs |
|---|---|---|
| `DecodeImage` | Decode from bytes | `color_space` |
| `ConvertRGB` | Ensure RGB | — |
| `ConvertRGB` | Swap BGR to RGB; omit after RGB decode | — |
| `Resize` | Smart resize | `width`, `height`, `smart_resize`, `min_pixels`, `max_pixels`, `patch_size`, `merge_size` |
| `Rescale` | Scale pixel values | `rescale_factor` |
| `Normalize` | Mean/std normalization | `mean`, `std` |
| `PatchImage` | Extract patches | `patch_size`, `temporal_patch_size`, `merge_size` |

After `DecodeImage(color_space="RGB")`, use ordinary `Normalize`: both
`ConvertRGB` and the nonzero `qwen2_5_vl`/`qwen3_vl` Normalize flags swap red
and blue. Neither owns patch interleaving; `PatchImage` owns that layout.
Do not add either swap to an already-RGB pipeline.

Native smart resize and HF can disagree at half-factor boundaries:
the inspected ORT-Extensions implementation uses `std::round`, while HF uses
Python ties-to-even `round`. Grid/token equality must be checked separately
from RGB correctness and successful generation.

---

## Generating from HuggingFace config
Expand All @@ -113,7 +116,6 @@ processor_config = {
"transforms": [
{"operation": {"name": "decode_image", "type": "DecodeImage",
"attrs": {"color_space": "RGB"}}},
{"operation": {"name": "convert_to_rgb", "type": "ConvertRGB"}},
{"operation": {"name": "resize", "type": "Resize",
"attrs": {
"width": 540, "height": 360, "smart_resize": 1,
Expand All @@ -125,7 +127,6 @@ processor_config = {
"attrs": {"rescale_factor": ip.rescale_factor}}},
{"operation": {"name": "normalize", "type": "Normalize", "attrs": {
"mean": list(ip.image_mean), "std": list(ip.image_std),
"qwen2_5_vl": 1,
}}},
{"operation": {"name": "patch_image", "type": "PatchImage", "attrs": {
"patch_size": ip.patch_size,
Expand All @@ -150,7 +151,6 @@ def _write_processor_config(processor, output_dir):
"transforms": [
{"operation": {"name": "decode_image", "type": "DecodeImage",
"attrs": {"color_space": "RGB"}}},
{"operation": {"name": "convert_to_rgb", "type": "ConvertRGB"}},
{"operation": {"name": "resize", "type": "Resize", "attrs": {
"width": 540, "height": 360, "smart_resize": 1,
"min_pixels": ip.size.get("shortest_edge", 3136),
Expand All @@ -161,7 +161,6 @@ def _write_processor_config(processor, output_dir):
"attrs": {"rescale_factor": ip.rescale_factor}}},
{"operation": {"name": "normalize", "type": "Normalize", "attrs": {
"mean": list(ip.image_mean), "std": list(ip.image_std),
"qwen2_5_vl": 1,
}}},
{"operation": {"name": "patch_image", "type": "PatchImage", "attrs": {
"patch_size": ip.patch_size,
Expand Down
64 changes: 64 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,70 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Qwen3.8-27B standard-only multimodal qualification

#### Added

- Corrected-RGB native ORT GenAI CUDA first-200 AI2D execution completes at
174/200, versus frozen HF 175/200 and raw ORT 174/200 references. Native
smart-resize tie rounding differs from HF for 28 examples; task accuracy
and token agreement do not establish preprocessing or logit parity.
- Offline tiny standard VLM CLI tests cover ordinary and existing
`--runtime ort-genai` packaging, sparse global cache indices, and FP16
convolution/FP32 recurrent state. A separate opt-in native hybrid probe
checks state-dependent tokens and fresh-generator reset; a metadata-only
qualification example selects CUDA through `og.Config`, without a new
Mobius CLI flag or rebuilding existing ONNX weights.
- Dense Qwen3.5-family composites, including Qwen3.8-27B, support text-only
export through the public Transformers builder. Offline tests cover the
pinned full-size text and complete VLM graphs, plus reduced random-weight
CUDA FP32/FP16 text/image/video/mixed-media prefill/decode parity, not the
official 27B checkpoint weights.

#### Fixed

- Native Qwen image processor metadata no longer swaps red/blue after RGB
decode. Ordinary normalization preserves checkpoint mean/std, while
`PatchImage` retains patch packing. Static contracts and an opt-in toy native
before-prefill RGB regression cover the correction; existing metadata must
be regenerated. Real-image resize and numerical parity remain unqualified.
- ORT-GenAI processor generation uses local `--config` HF assets instead of
silently retaining CLIP defaults. Unwrapped `qwen3_5_text` VLM packages
retain Qwen patch preprocessing and vision metadata.
- Explicit `mamba_ssm_dtype` is retained in DeltaNet computation and exported
recurrent-state inputs/outputs. Re-exported FP16/BF16 hybrid models whose
config requests FP32 state now expose FP32 recurrent slots; callers must
allocate state from graph input/output types, not the model weight dtype.
Existing saved models are unchanged. Missing or `auto` policies preserve
the previous model-dtype state behavior.
- Shared Qwen3.5 dense/MoE/VL DeltaNet layers use the HF reference's fixed
`1e-6` Q/K L2 normalization epsilon. Native GenAI and other variants'
official-checkpoint qualification remain separate.
- Shared Qwen3/Qwen3.5 split vision graphs cast processor FLOAT32 pixels to
model dtype before reduced-precision convolution.
- Shared Qwen3/Qwen3.5 split embedding graphs recognize both image and video
placeholders and use feature offsets across the entire batch. Their
existing `image_features` input carries visual rows in flattened
batch/token placeholder order; no new graph input is required.
- Learned offset RMS normalization keeps normalization and `1 + weight`
scaling in FP32 until the final model-dtype cast, avoiding premature
rounding of small trained offsets.

#### Qualification status

- Installed GenAI `0.16.0-dev` passed native sparse hybrid state progression
and fresh-generator reset with FP16 convolution/FP32 recurrent state and
non-shared caches. The unchanged real standard graphs then passed bounded
native text/reset and single-/two-image four-token generation. The CUDA
qualification overlay sets matching providers on all three sessions before
the required `og.Config` execution override: explicit CPU auxiliary sessions
did not follow that override and segfaulted during prefill. This is bounded
execution evidence, not numerical parity or full-checkpoint accuracy.
- The complete real-weight FP16 vision/embedding/hybrid-decoder package was
exported, saved and reloaded with standard ONNX operators only. Official
image vision and text prefill comparisons still exceed the unchanged
FP16 tolerance; this is not a full-checkpoint accuracy or numerical-parity pass.

### GPT-OSS MXFP4 export

#### Added
Expand Down
70 changes: 70 additions & 0 deletions docs/cli_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,60 @@ mobius build --model meta-llama/Llama-3.2-1B --output output/ --ep webgpu --dtyp
mobius build --model meta-llama/Llama-3.2-1B --output output/ --ep onnx-standard
```

#### Standard Qwen3.8-27B VLM packaging versus runtime selection

The full VLM supports the ordinary build route and the existing ORT-GenAI
metadata route. Both retain `decoder/model.onnx`, `vision_encoder/model.onnx`,
and `embedding/model.onnx` when built with `--ep onnx-standard`:

```bash
mobius build --config /absolute/local-qwen-checkpoint --output /absolute/standard-vlm \
--ep onnx-standard --dtype f16
mobius build --config /absolute/local-qwen-checkpoint --output /absolute/standard-genai \
--ep onnx-standard --dtype f16 --runtime ort-genai
```

`--ep` controls **graph construction**, not independent runtime selection.
There is no new runtime-EP CLI flag. Prepare CUDA **runtime metadata** for all
three already-standard graphs using the qualification overlay example (or
`write_ort_genai_config(pkg, directory, ep="cuda")`, without saving/rebuilding
graphs). Then select CUDA at execution:

```python
import onnxruntime_genai as og

config = og.Config("/absolute/cuda-runtime-overlay")
config.clear_providers()
config.append_provider("CUDAExecutionProvider")
model = og.Model(config)
```

In the qualified GenAI `0.16.0-dev` build, explicit CPU vision/embedding session
options do not follow the execution provider override. Mixing those CPU sessions
with CUDA decoder/device buffers segfaulted during native prefill; in-memory
`Config.overlay()` provider patches did not resolve it. Matching per-component
CUDA metadata passed. This is why the example prepares CUDA session metadata
while retaining the exact standard graphs. Do not change build-time `--ep` to
`cuda` to obtain this runtime-only configuration.

Standard dynamic KV caches require `past_present_share_buffer=false`; CUDA
graph capture remains off. Hybrid cache slots retain global indices and
independent FP16 convolution/FP32 recurrent types. Successful packaging does
not prove a particular native GenAI build can execute those states.

Qwen processor metadata now preserves RGB from `DecodeImage` through ordinary
`Normalize`; `PatchImage` owns patch packing. Regenerate processor metadata to
remove the former channel-swap flag from existing configurations, without
changing graphs or weights. The residual real-image resize difference is not
qualified as bitwise pixel parity.

For an existing large artifact, do **not** rebuild or save the weights to add
metadata. Use `examples/qwen38_standard_genai.py` to reconstruct a lazy package
and create a symlink overlay. See
[Qwen3.8 qualification](design/qwen38-27b-standard-onnx.md#native-ort-genai-bounded-qualification)
for the executed bounded evidence and opt-in probe. No CPU/DML, performance,
numerical-parity, video, serving, or graph-capture guarantee is implied.

### Optimization Rules (`--optimize`)

```
Expand Down Expand Up @@ -306,6 +360,22 @@ For a full ORT-GenAI text-only package (with `genai_config.json`), use
`auto_export(..., text_only=True)` — see
`examples/gemma4_12b_text_ort_genai.py`.

Dense Qwen3.5-family composites, including `Qwen/Qwen3.8-27B`, also support
`--features text-only`. Use `--ep onnx-standard` for standard-operator export.
Omit `--features text-only` to retain the complete vision/embedding/hybrid
decoder package. Both forms retain all hybrid decoder layers. The vision
graph accepts processor FLOAT32 pixels and casts internally to model dtype.
The split embedding graph's `image_features` input carries both image and
video features in flattened batch/token placeholder order; its feature
indices do not restart per batch row.
When the checkpoint declares `mamba_ssm_dtype="float32"`, FP32 recurrent state
is retained with FP16 weights/activations; without an explicit policy, state
remains model dtype. Qwen3.8-27B evidence covers graph construction, reduced
random-weight text/image/video parity, and a saved/reloaded real-weight
standard-only package. Official FP16 numerical qualification is not yet
passing. See
[Qwen3.8-27B qualification](design/qwen38-27b-standard-onnx.md).

### More Examples

```bash
Expand Down
Loading
Loading