Skip to content

Add multimodal evaluation and component-aware quantization support - #2531

Draft
Delwin Kim (DelwinKim) wants to merge 44 commits into
microsoft:mainfrom
DelwinKim:t-delwinkim/lmms-ort-evaluator
Draft

Add multimodal evaluation and component-aware quantization support#2531
Delwin Kim (DelwinKim) wants to merge 44 commits into
microsoft:mainfrom
DelwinKim:t-delwinkim/lmms-ort-evaluator

Conversation

@DelwinKim

@DelwinKim Delwin Kim (DelwinKim) commented Jun 19, 2026

Copy link
Copy Markdown

Describe your changes

Important

The evaluator and ORT-GenAI package-handling changes (i.e. just the LMMSEvaluator integration) have been isolated in draft PR #2615, from DelwinKim/Olive:t-delwinkim/lmms-evaluator-only at 08a6a2b2. The remaining quantization and calibration features should be reviewed as separate changes.
Results are tracked in microsoft/Olive#2613.

This draft adds an end-to-end multimodal evaluation and quantization workflow to Olive. It integrates the lmms-eval benchmark harness, makes Mobius multi-component ORT-GenAI packages directly evaluable, and adds component-aware quantization/calibration support for vision-language and omni models.

The PR branch intentionally serves as a single reproduction branch (for reproducing results) for the relevant Olive changes, including the multimodal evaluator, sensitivity-based mixed precision, and HQQ fixes, etc.

Validated environment

Component Version / revision
Python 3.12.9
Olive olive-ai==0.11.0.dev0; t-delwinkim/lmms-ort-evaluator at ed853908
Mobius mobius-ai==0.1.0; published integration branch t-delwinkim/multimodal-quant-integration-20260803 at d369f992
lmms-eval lmms-eval==0.7.2; evaluation-fix revision 8a252a73
ONNX Runtime onnxruntime-gpu==1.29.0; source revision 761185d2
ORT-GenAI onnxruntime-genai-cuda==0.15.0.dev0; source revision 9f7a24fb; ONNX Runtime Extensions 319f8d2c
Build/evaluation stack torch==2.12.0; transformers==5.11.0; datasets==5.0.0; onnxscript==0.7.1; protobuf==6.33.6

All benchmark results in the updated sample-results comment come from deployed ONNX/ORT-GenAI packages evaluated through LMMSEvaluator, not from PyTorch proxy evaluations.

This is the current public reproduction environment. Historical pre-integration result rows retain their recorded package/build provenance; updating this table does not relabel those artifacts as having been built by the current Mobius revision.

Post-review validation covered 463 affected Olive tests and 50 focused lmms-eval tests, changed-file lint/format checks, Olive import/CLI checks, and fresh deployed-package probes for external task resolution, package-native audio preprocessing, PNG/JPEG85 image profiles, MMLU loglikelihood, AI2D direct extraction, and limit-aware OCRBench. The exact fast-test workflow also passed against onnxruntime-genai==0.15.1 (4 tests and 13 subtests), while preserving compatibility with the pinned 0.15.0 development runtime. The pinned lmms-eval revision also includes the exact Google-prompt fleurs_en_google_asr task as a built-in task. Machine-local artifacts remain outside this PR.

Reported quantization variants

Label Exact evaluated variant
FP16 Original Hugging Face checkpoint exported by MobiusBuilder(precision="fp16"); no weight quantization
KQ-all Post-export ONNX K-Quant W4A16 group-32 asymmetric on every eligible MatMul
KQ-dec Post-export ONNX K-Quant W4A16 group-32 asymmetric on decoder projections; LM head/embedding/encoders remain floating point
RTN-all Post-export ONNX blockwise RTN W4A16 group-32 asymmetric on every eligible MatMul
RTN-dec Post-export ONNX blockwise RTN W4A16 group-32 asymmetric on decoder projections; LM head/embedding/encoders remain floating point
HQQ Post-export ONNX HQQ W4A16 with exact/glob node scoping and corrected MatMulNBits layout; replaced weights are removed from root and nested graph scopes
Olive GPTQ PyTorch GPTQ W4A16 group-32 asymmetric decoder-only; desc_act=false, 1% Hessian damping
QAT W4A16-CT Google gemma-4-E2B-it-qat-w4a16-ct compressed-tensors QAT checkpoint: symmetric W4A16 group-32 Linear weights with BF16 activations; vision/audio/projectors/embeddings/LM head remain BF16; no additional PTQ pass
Q4_0 GGUF Google's QAT Q4_0 decoder imported as block-32 INT4 MatMulNBits; encoders/embeddings/LM head remain floating point
Q4_0 + INT8 embeds Q4_0 decoder plus ONNX RTN W8A16 group-32 asymmetric token/per-layer embeddings and embedding projection

Standalone W8 RTN and symmetric Google-policy RTN results are intentionally excluded. W8 packages appear only as source controls in the matched INT8-embedding experiment.

What is added

1. LMMSEvaluator

A new evaluator ("type": "LMMSEvaluator") runs official lmms-eval tasks inside an Olive workflow.

Supported input handlers:

  • ONNXModelHandler pointing into a complete ORT-GenAI package (genai_config.json plus decoder/vision/audio/embedding graphs). This dispatches to the new ortgenai_mm adapter.
  • HfModelHandler for native Hugging Face multimodal evaluation. The lmms-eval model wrapper is inferred from model_type or selected explicitly with model_class.

The evaluator:

  • implements image and audio generate_until;
  • provides text loglikelihood for compatible tasks;
  • handles og.Images and og.Audios;
  • discovers genai_config.json from nested package layouts;
  • resolves model-family prompt defaults and structured-content templates;
  • supports per-model processor argument shapes, including Phi-4-MM and Whisper;
  • preserves official lmms-eval scorers for OCRBench, DocVQA ANLS, ChartQA, TextVQA, AI2D, WER, BLEU, and other registered tasks;
  • writes compact aggregate JSON and optional per-sample generations.

2. CompositeToOnnxPackage

MobiusBuilder emits a CompositeModelHandler containing decoder, vision, audio, and embedding components. Generic evaluation cannot open that handler as one inference session.

CompositeToOnnxPackage:

  • hardlink-copies the complete composite package while preserving its nested graph layout;
  • selects an entry component (default: decoder);
  • returns a single ONNXModelHandler;
  • preserves the complete package layout so ORT-GenAI can discover every component.

3. Component-aware mixed precision

MultiModalMixedPrecision classifies quantizable modules into:

  • vision
  • audio
  • text
  • projector
  • lm_head
  • embeds

It writes mixed_precision_info consumed by downstream PyTorch RTN, GPTQ, and K-Quant passes:

  • 16/32-bit components are excluded from quantization;
  • 2/4/8-bit components can receive per-module overrides;
  • custom component-name rules can extend the built-in architecture mappings.

The pass plans component precision but does not quantize by itself.

SelectiveMixedPrecision provides the complementary layer-sensitivity path. FP16/FP32 promotions are emitted as quantization exclusions, and optional module globs are validated against fused QKV groups and tied embedding/LM-head pairs.

4. Multimodal calibration

  • MultimodalActivationRangeCalibration records per-component activation ranges and derived qparams for vision/audio/text/projector analysis. It is a diagnostic/planning pass; it does not claim to implement MQuant's MSQ, AIFS, or runtime activation kernels.
  • PyTorch RTN/GPTQ calibration replay now supports Gemma-4 per-layer inputs, Qwen2.5-VL visual inputs, and Qwen3-VL DeepStack feature injection.
  • Quantizers preserve MultiModalMixedPrecision exclusions and avoid quantizing modules for which calibration statistics were not collected.

5. Quantizer and workflow integration

  • ONNX blockwise RTN accepts nodes_to_include and nodes_to_exclude; ONNX K-Quant accepts nodes_to_exclude plus per-node customized_weight_config overrides. Both filters match exact node names and */? glob patterns.
  • K-Quant component scopes remain attached to composite component names.
  • Native Olive GPTQ supports multimodal calibration inputs and component exclusions.
  • ONNX HQQ supports exact and full fnmatch-style node selection, corrected output-channel-major MatMulNBits packing, and recursive orphan-initializer cleanup.
  • GptqModel accepts the current GPTQModel API/configuration surface.
  • Cache/resource handling preserves additional processor/tokenizer files across pass boundaries.
  • New passes are registered in olive/olive_config.json and documented in the pass reference and quantization guide.

Current validated recipes

The following examples use the exact current pass/evaluator field names and the tested quantization values. Machine-local snapshot, cache, model, and result paths from the validation workspace are replaced with portable paths.

Recipe A: Gemma-4 E2B decoder-only ONNX K-Quant plus AI2D evaluation

{
  "input_model": {
    "type": "HfModel",
    "model_path": "google/gemma-4-E2B-it"
  },
  "systems": {
    "local_system": {
      "type": "LocalSystem",
      "accelerators": [
        {
          "device": "gpu",
          "execution_providers": ["CUDAExecutionProvider"]
        }
      ]
    }
  },
  "passes": {
    "mobius_build": {
      "type": "MobiusBuilder",
      "precision": "fp16"
    },
    "int4_quantize": {
      "type": "OnnxKQuantQuantization",
      "bits": 4,
      "block_size": 32,
      "save_as_external_data": true,
      "nodes_to_exclude": [
        "vision_encoder/*",
        "audio_encoder/*",
        "embedding/*",
        "decoder/lm_head/*"
      ]
    },
    "package": {
      "type": "CompositeToOnnxPackage"
    }
  },
  "evaluators": {
    "evaluator": {
      "type": "LMMSEvaluator",
      "tasks": ["ai2d_direct"],
      "batch_size": 1,
      "max_length": 32768,
      "ignore_stop_strings": ["\n\n"],
      "log_samples": true,
      "output_path": "results/gemma4_kquant_decoder_ai2d.json",
      "fail_on_error": true
    }
  },
  "evaluator": "evaluator",
  "evaluate_input_model": false,
  "target": "local_system",
  "output_dir": "models/gemma4_kquant_decoder",
  "cache_dir": "cache/gemma4_kquant_decoder",
  "no_artifacts": true,
  "clean_evaluation_cache": true
}

Run one benchmark per Olive process. To evaluate English audio instead, replace ai2d_direct and the output filename with one of librispeech_test_clean, librispeech_test_other, or fleurs_en. Keep custom tasks such as ai2d_direct in a versioned lmms-eval task bundle when they are not built into the pinned dependency.

Recipe B: Qwen2.5-VL component-aware PyTorch RTN

{
  "input_model": {
    "type": "HfModel",
    "model_path": "Qwen/Qwen2.5-VL-3B-Instruct",
    "task": "image-text-to-text",
    "load_kwargs": {
      "torch_dtype": "bfloat16",
      "attn_implementation": "sdpa"
    }
  },
  "systems": {
    "local_system": {
      "type": "LocalSystem",
      "accelerators": [
        {
          "device": "gpu",
          "execution_providers": ["CUDAExecutionProvider"]
        }
      ]
    }
  },
  "passes": {
    "component_plan": {
      "type": "MultiModalMixedPrecision",
      "component_precision": {
        "vision": 16,
        "projector": 16,
        "text": 4,
        "lm_head": 16,
        "embeds": 16
      },
      "bits": 4,
      "group_size": 32,
      "sym": false
    },
    "rtn": {
      "type": "Rtn",
      "bits": 4,
      "group_size": 32,
      "sym": false,
      "lm_head": false,
      "embeds": false
    },
    "mobius_build": {
      "type": "MobiusBuilder",
      "precision": "fp16"
    },
    "package": {
      "type": "CompositeToOnnxPackage"
    }
  },
  "evaluators": {
    "evaluator": {
      "type": "LMMSEvaluator",
      "tasks": ["ai2d_direct"],
      "batch_size": 1,
      "max_length": 32768,
      "ignore_stop_strings": ["\n\n"],
      "log_samples": true,
      "output_path": "results/qwen25vl_component_rtn_ai2d.json",
      "fail_on_error": true
    }
  },
  "evaluator": "evaluator",
  "evaluate_input_model": false,
  "target": "local_system",
  "output_dir": "models/qwen25vl_component_rtn",
  "cache_dir": "cache/qwen25vl_component_rtn",
  "no_artifacts": true,
  "clean_evaluation_cache": true
}

Standalone Hugging Face evaluation

{
  "input_model": {
    "type": "HfModel",
    "model_path": "Qwen/Qwen2.5-VL-3B-Instruct"
  },
  "evaluators": {
    "evaluator": {
      "type": "LMMSEvaluator",
      "model_class": "qwen2_5_vl",
      "trust_remote_code": true,
      "tasks": ["ai2d_direct"],
      "batch_size": 1,
      "log_samples": true,
      "output_path": "results/qwen25vl_hf_ai2d.json",
      "fail_on_error": true
    }
  },
  "evaluator": "evaluator",
  "evaluate_input_model": true,
  "output_dir": "models/qwen25vl_hf",
  "cache_dir": "cache/qwen25vl_hf",
  "no_artifacts": true,
  "clean_evaluation_cache": true
}

Run any recipe with:

olive run --config <recipe.json>

LMMSEvaluator configuration

Field Default Description
tasks required lmms-eval task names
model_class auto Explicit lmms-eval wrapper for HF evaluation
limit full split Samples per task
batch_size 1 Only single-request generation is currently supported
max_new_tokens 256 Fallback completion budget when a task does not override it
max_length 32768 ORT-GenAI prompt + media + completion sequence budget
system_prompt model-specific Explicit system prompt override
include_path none One external lmms-eval task directory or a list of directories
audio_target_sample_rate package-defined Optional explicit host-resampling override; otherwise the package processor handles its declared/default rates
image_serialization_profile lossless ORT input serialization: lossless PNG or explicit jpeg85 parity mode
ignore_stop_strings none Task stop strings to remove, such as "\n\n"
trust_remote_code false HF model/processor loading behavior
log_samples false Preserve per-sample generations
output_path none Aggregate result path
fail_on_error true Raise instead of recording a failed evaluator result

Tests

  • test/evaluator/test_lmms_ort.py
  • test/evaluator/test_olive_evaluator.py
  • test/test_cache.py
  • test/passes/onnx/test_composite_to_onnx_package.py
  • test/passes/onnx/test_hqq_quantization.py
  • test/passes/onnx/test_kquant_quantization.py
  • test/passes/onnx/test_rtn_quantization.py
  • test/passes/pytorch/test_multimodal_activation_range.py
  • test/passes/pytorch/test_multimodal_mixed_precision.py
  • test/passes/pytorch/test_multimodal_quantization.py
  • test/passes/pytorch/test_quant_utils.py
  • test/passes/pytorch/test_selective_mixed_precision.py

Current limitations

  • ORT-GenAI multimodal generation is single-request (batch_size=1).
  • The adapter is invoked through Olive/Python; it is not registered as an lmms-eval v2 CLI entry point.
  • MultimodalActivationRangeCalibration is diagnostic metadata only; it does not add MQuant runtime kernels.
  • Multiround/interleaved lmms-eval generation is not implemented.
  • Gemma-4 HF evaluation uses the corresponding wrapper from the pinned lmms-eval development revision above.
  • Phi-4-MM packages whose tokenizer chat template accepts only flat string content cannot currently route image/audio requests through the structured-content adapter; those packages require a compatible exported template.

Checklist

  • Add evaluator, adapter, packaging pass, multimodal planning/calibration passes, documentation, and tests.
  • Validate deployed ONNX/ORT-GenAI evaluation on Gemma-4, Qwen2.5-VL, and Qwen3-VL.
  • Publish final protocol-matched sample results in the PR comment.
  • Keep machine-local recipes, caches, models, and generated result artifacts out of the PR.

Release note

Add multimodal vision/audio evaluation through lmms-eval, plus component-aware mixed precision, multimodal calibration and deployable ORT-GenAI package evaluation for Olive workflows.

Comment thread olive/evaluator/lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/evaluator/test_lmms_ort.py Fixed
Comment thread test/passes/onnx/test_composite_to_onnx_package.py Fixed
@DelwinKim

Delwin Kim (DelwinKim) commented Jul 1, 2026

Copy link
Copy Markdown
Author

Outdated benchmark snapshot (historical only)

Warning

This July 28 snapshot is outdated and is not part of the current PR validation or merge claims. It is retained only for historical provenance. Current results are intentionally omitted from this draft.

Show outdated July 28 benchmark snapshot

Outdated July 28 benchmark snapshot

Updated 2026-07-28. This replaced the earlier July 1 *_lite screening table with the then-current protocol-matched results. Debug/smoke outputs and legacy flexible-extraction AI2D scores are excluded.

These runs exercise LMMSEvaluator end to end on deployed ONNX/ORT-GenAI packages. Vision, audio, translation, and fusion metrics use lmms-eval task scorers; AI2D uses a direct letter-only prompt with exact-match extraction.

Protocols

  • AI2D direct: all 3,088 test examples; direct letter-only prompt and exact-match extraction.
  • Full image: complete ChartQA, DocVQA, TextVQA, OCRBench, and AI2D splits.
  • Full audio: complete FLEURS, LibriSpeech, and CoVoST splits.
  • MathVision 500: the same ordered first 500 examples for screening comparisons.
  • All rows are evaluated ONNX packages, not PyTorch proxies. Higher is better except WER/CER.

Exact variant definitions

Label Exact evaluated variant
FP16 Original Hugging Face checkpoint exported by Mobius to FP16 ONNX; no weight quantization
PR515 olive-recipes PR #515 CUDA mixed package: post-export ONNX K-Quant W4A16 group-32 asymmetric decoder, post-export ONNX RTN W8A16 group-32 asymmetric vision/audio encoders, FP16 embedding
PR558 olive-recipes PR #558 CUDA five-recipe package built from pinned Gemma-4 E2B snapshot 9dbdf8a: decoder K-Quant group-32 asymmetric with 135 W4A16 and 141 W8A16 MatMuls; vision/audio/embedding use ONNX RTN W8A16 group-32 asymmetric
KQ-all / KQ-dec Post-export ONNX K-Quant W4A16 group-32 asymmetric on all eligible MatMuls / decoder projections only
RTN-all / RTN-dec Post-export ONNX blockwise RTN W4A16 group-32 asymmetric on all eligible MatMuls / decoder projections only
Olive GPTQ PyTorch GPTQ W4A16 group-32 asymmetric decoder-only; desc_act=false, 1% Hessian damping
QAT W4A16-CT Google gemma-4-E2B-it-qat-w4a16-ct compressed-tensors checkpoint: symmetric W4A16 group-32 Linear weights, BF16 activations, BF16 vision/audio/projectors/embeddings/LM head; exported without another PTQ pass
Q4_0 GGUF Google's QAT Q4_0 decoder imported as block-32 INT4 MatMulNBits; encoders/embeddings/LM head remain floating point
Q4_0 + INT8 embeds Q4_0 decoder plus ONNX RTN W8A16 group-32 asymmetric token/per-layer embeddings and embedding projection

Standalone W8 RTN and symmetric Google-policy RTN results are intentionally excluded. W8 source packages remain only in the matched INT8-embedding overlay experiment.

Gemma-4 E2B - package size

Variant Bytes Decimal GB
FP16 11,273,429,042 11.27
PR515 7,611,546,812 7.61
PR558 6,184,173,889 6.18
KQ-all 7,378,649,735 7.38
KQ-dec 8,624,417,851 8.62
Olive GPTQ 8,624,596,305 8.62
Q4_0 GGUF 8,595,362,354 8.60
Q4_0 + INT8 embeds 6,089,331,756 6.09
RTN-all 7,378,649,735 7.38
RTN-dec 8,624,417,851 8.62
QAT W4A16-CT 8,575,604,690 8.58

Gemma-4 E2B - full image and fusion benchmarks

Higher is better. Values are percentages. The top four scores in each row
carry their rank as a subscript (1 = best); ties share the same rank.
Image/fusion rows use complete benchmark splits; MathVision uses the same
ordered first 500 examples.

Metric FP16 PR515 PR558 KQ-all KQ-dec RTN-all RTN-dec QAT W4A16-CT Q4_0 GGUF Q4_0 + INT8 embeds Olive GPTQ
AI2D direct 63.501 61.04 62.822 61.08 61.14 60.78 61.08 62.05 62.604 62.633 61.85
ChartQA 51.564 50.76 50.68 51.16 50.80 51.48 51.683 51.44 53.242 53.401 50.88
DocVQA ANLS 74.941 71.26 72.714 70.53 71.34 70.17 70.88 71.51 72.743 72.714 73.492
TextVQA 61.772 58.83 60.05 58.74 59.05 60.75 61.21 59.81 61.294 61.363 62.601
OCRBench 71.401 69.904 69.80 69.20 69.80 69.20 70.302 68.30 70.103 69.80 68.70
OmniBench 40.27 40.884 40.00 40.53 40.973 41.062 41.421 39.73 40.62 40.53 36.55
MathVision standard (first 500) 35.81 26.0 34.22 28.6 27.2 29.4 28.4 29.6 30.24 31.23 28.4

Gemma-4 E2B - full audio benchmarks

WER/CER is lower-is-better; BLEU is higher-is-better. The top four scores in
each row carry their rank as a subscript (1 = best); ties share the same
rank. The original combined FP16 run OOMed, so its replacement values below
are assembled from independent full-split task runs, all of which completed
successfully.

Metric FP16 PR515 PR558 KQ-all KQ-dec RTN-all RTN-dec QAT W4A16-CT Q4_0 + INT8 embeds Olive GPTQ
FLEURS English WER 9.813 10.00 9.511 11.48 10.16 10.30 10.91 9.974 10.46 9.752
FLEURS Mandarin CER 30.922 43.51 29.311 39.83 38.85 41.15 41.77 33.153 38.33 37.814
FLEURS Cantonese CER 48.851 66.10 53.092 75.55 64.294 70.69 64.51 67.85 68.79 59.803
LibriSpeech clean WER 5.521 6.85 6.35 6.84 6.79 5.903 5.542 6.40 6.06 5.984
LibriSpeech other WER 12.161 13.97 13.25 15.32 13.84 12.543 12.492 14.17 13.24 12.884
CoVoST EN-to-ZH BLEU 32.541 30.85 32.132 30.00 30.98 30.27 30.65 31.364 31.533 29.99
CoVoST ZH-to-EN BLEU 5.901 5.193 5.592 4.81 5.174 5.06 5.15 4.81 5.07 5.01

All 12 newly added PR558 runs completed with status 0 and zero empty
generations.

PR558 recipe reproduction and harness compatibility

You built PR558's original CUDA package by running its five component
recipes (export, text, vision, audio, and embedding) and then
evaluated the resulting ORT-GenAI package through both the recipe-native
Olive evaluators and LMMSEvaluator.

Full-split measurement Result
Package size 6.18 GB
Recipe-native AI2D exact match (3,088 examples) 62.40% (1,927/3,088)
Recipe-native FLEURS English, Olive normalization (647 examples) 8.92 WER
LMMSEvaluator FLEURS English, lmms-eval normalization (647 examples) 9.51 WER

The FLEURS numbers use different prompts and normalizers and therefore are
not expected to be identical. On the full set, applying both normalizers to
both prediction streams gives:

Prediction source Olive normalization lmms-eval normalization
Recipe strict-ASR prompt 8.92% 7.84%
Harness generic prompt 10.21% 9.51%

On matched 64-example compatibility runs, the recipe-native score was 10.33
WER and the harness score was 10.26 WER. The original five-recipe package and
the later unified package produced byte-identical prediction logs on both
AI2D-100 and FLEURS-64, confirming that the unified workflow reproduced the
evaluated PR558 package behavior.

Gemma-4 E2B Q4_0 GGUF import

Both Q4_0 variants now have complete full-image results and are included in
the primary image/fusion table above. The Q4_0 + INT8-embedding package also
completed the full-audio suite and is included in the primary audio table.
The corresponding FP16-embedding full-audio run reached 28,068/28,399
generations (98.8%) before a 391 MB CUDA BFCArena allocation failed, so it has
no full-audio aggregate.

The deployed package imports Google's official QAT Q4_0 decoder as 275
block-32 INT4 MatMulNBits nodes. The LM head, vision/audio encoders, and
embedding component remain floating point.

Benchmark Q4_0 GGUF Q4_0 + INT8 embeds
AI2D direct, full (3,088) 62.60% 62.63%
MathVision standard, first 500 30.2% 31.2%

Verified image/fusion results on the first 500 examples:

Benchmark Q4_0 GGUF Q4_0 + INT8 embeds QAT W4A16-CT FP16
AI2D direct 63.4% 63.4% 64.6% 62.2%
ChartQA 42.6% 42.8% 41.4% 41.4%
DocVQA ANLS 71.65% 71.59% 73.12% 74.22%
TextVQA 62.02% 61.94% 59.42% 61.68%
OCRBench¹ 84.0% 83.6% 83.0% 84.4%
OmniBench 38.8% 38.8% 38.6% 42.8%

¹ Renormalized over the evaluated 500 samples; the saved aggregate uses
OCRBench's full 1,000-example denominator.

Corrected audio results on the first 500 examples:

Benchmark Q4_0 GGUF Q4_0 + INT8 embeds QAT W4A16-CT FP16
LibriSpeech clean WER ↓ 4.66 4.65 4.91 4.03
LibriSpeech other WER ↓ 20.62 20.67 21.78 19.50
FLEURS English WER ↓ 10.46 10.42 9.76 9.74
FLEURS Mandarin WER ↓ 37.51 38.16 35.02 28.17
FLEURS Cantonese WER ↓ 66.50 67.09 73.75 48.29
CoVoST EN-to-ZH BLEU ↑ 27.67 27.81 28.00 29.19
CoVoST ZH-to-EN BLEU ↑ 4.70 4.62 3.96 5.27

These are the corrected v13 audio packages. The original GGUF audio import
was invalid due to swapped light-convolution norms and double application of
Softplus; those superseded values are not reported. Corrected runs had zero
empty generations except Q4_0 GGUF FLEURS Cantonese (7/500).

Qwen2.5-VL-3B - full image benchmarks

Image rows use complete benchmark splits. MathVision uses the same ordered
first 500 examples.

Metric FP16 KQ-all KQ-dec RTN-dec
AI2D direct (full) 75.81 74.38 75.13 72.09
ChartQA (full) 80.68 80.12 79.92 80.40
DocVQA (full) 82.59 80.77 79.98 77.74
TextVQA (full) 72.69 72.63 71.67 71.94
OCRBench (full) 76.30 75.60 75.30 72.80
MathVision standard (first 500) 21.6 20.8 22.4 23.6

The post-export RTN-dec package produced empty generations on several tasks. K-Quant retained most FP16 quality without empty outputs. A separate matched INT8-token-embedding matrix completed 14/14 runs; each first-500 metric changed by at most 0.8 percentage points with no consistent direction.
RTN-dec produced 24 empty generations in the MathVision-500 run; the other
three MathVision rows produced none.

Qwen2.5-VL-3B - INT8 token-embedding overlays

Each row is a matched first-500 source → overlay pair. The overlay changes
only the token-embedding table to INT8 block-32
GatherBlockQuantized; every other component is unchanged. Values are
percentages except DocVQA, which is ANLS × 100. OCRBench retains the saved
full-1,000 denominator despite limit=500.

Source package AI2D direct ChartQA DocVQA TextVQA OCRBench MathVision
FP16 80.2→80.2 66.2→66.2 82.99→82.69 77.0→77.0 40.4→40.4 22.6→22.0
GPTQ mixed 64+64, W4A16 G32 asym dec 79.2→79.0 65.0→65.0 82.08→82.08 76.7→76.7 39.6→39.7 22.2→21.4
KQ-all W4A16 G32 asym 78.6→78.6 65.0→65.0 77.05→77.05 77.0→77.2 40.7→40.7 21.2→20.6
KQ-dec W4A16 G32 asym 78.8→79.0 65.8→66.0 79.01→78.81 75.1→75.0 40.2→40.2 22.6→22.2
RTN8-all W8A16 G32 asym 80.0→80.0 66.2→66.2 82.96→82.92 77.4→77.6 40.2→40.2 21.6→22.2
RTN8-dec W8A16 G32 asym 80.8→80.8 66.2→66.2 82.65→82.65 77.1→77.3 40.3→40.3 22.0→21.8
RTN-dec W4A16 G32 asym 76.8→76.6 65.6→65.4 76.63→76.42 74.8→74.7 39.5→39.4 22.8→23.0

All 14 source/overlay runs completed successfully. Across these bundles, each
metric changed by at most 0.8 percentage points and there is no consistent
degradation or improvement.

Qwen3-VL-2B - full image benchmarks

Image rows use complete benchmark splits. MathVision uses the same ordered
first 500 examples.

Metric FP16 KQ-all KQ-dec RTN-dec
AI2D direct (full) 66.71 65.38 65.19 65.19
ChartQA (full) 61.76 59.16 59.64 58.60
DocVQA ANLS (full) 76.25 71.92 72.92 73.00
TextVQA (full) 65.79 62.81 64.59 64.22
OCRBench (full) 64.50 60.30 63.50 61.20
MathVision standard (first 500) 11.8 8.6 8.8 8.6

Decoder-only K-Quant preserves more quality than all-MatMul K-Quant on Qwen3-VL, particularly OCRBench and TextVQA.

Main takeaways

  • Quantization quality is model-, component-, and benchmark-dependent; no single scheme wins every task.
  • Decoder-only/component-aware policies usually protect multimodal quality better than indiscriminate all-MatMul W4.
  • Multimodal calibration matters: mixed 64+64 GPTQ calibration reaches 75.58% AI2D on Qwen2.5-VL versus its 75.81% FP16 baseline, while Qwen3-VL still retains a larger GPTQ gap.
  • INT8 token embeddings were effectively neutral in the completed Qwen2.5 matched matrix.

These tables are evidence that the evaluator handles official vision, document/OCR, audio, translation, and cross-modal scorers on deployed quantized packages. They should not be read as a claim that every difference is statistically significant.

Delwin Kim (DelwinKim) and others added 10 commits July 6, 2026 22:29
 Adds LMMSEvaluator (olive/evaluator/olive_evaluator.py) and an
 ORT-GenAI multimodal adapter (olive/evaluator/lmms_ort.py) for
 evaluating multimodal ONNX models via lmms-eval.
Build on top of the LMMSEvaluator + ORT-GenAI multimodal adapter foundation:

- LMMSEvaluator now dispatches HfModelHandler inputs to lmms-eval's native
  per-architecture wrappers (phi4_multimodal, qwen2_5_vl, whisper, ...),
  with auto-detection from HF model_type and a forwarded-kwargs filter
  that only passes args the target wrapper actually declares (handles
  wrappers like qwen2_5_vl which assert kwargs == {}). Enables
  FP-vs-quantized comparison in a single recipe via evaluate_input_model.

- lmms_ort.py adapter: tolerant audio/image disambiguation (audio dicts
  with "path" no longer get mis-routed to PIL.Image.open), Whisper-specific
  prompt + EOS-collision handling so ASR works end-to-end through
  ortgenai_mm without the Phi-4-MM chat-template scaffolding interfering.

- New CompositeToOnnxPackage pass: flattens nested CompositeModel ORT-GenAI
  packages (subdir-per-component or root-level) into the flat layout
  LMMSEvaluator expects. Tolerates extensionless component filenames
  produced by some upstream quant passes.

- Tests: 32 in test_lmms_ort.py (entry-point/registry, HF dispatch,
  kwargs filter, prompt builder, score_continuation, partition_visuals,
  run_generation), 9 in test_composite_to_onnx_package.py (flatten +
  external-data rewrites + fallback entry-point).

Validated end-to-end:
- whisper-large-v3 via HfModel -> ModelBuilder fp16 -> KQuant int8 ->
  CompositeToOnnxPackage -> ortgenai_mm eval on LibriSpeech.
  FP HF WER 1.52/2.26 (clean/other), INT8 ONNX WER 1.68/2.36.
…ocessor args

- MobiusBuilder: add `mobius_ep_override` config knob. Lets a workflow force
  the mobius execution_provider (e.g. "default") independent of the Olive
  accelerator EP. Needed because mobius's cuda-EP attention fusions
  (PackedMultiHeadAttention for Qwen2.5-VL vision, GQA for Gemma-4 decoder)
  produce graphs the ORT-GenAI fused-attention kernels reject. "default" EP
  skips those fusions; the resulting INT4 graph is numerically equivalent.
- lmms_ort: support torchcodec.AudioDecoder visuals (HF datasets 5.x audio
  feature) in _normalize_audio via duck-typed get_all_samples().
- lmms_ort: branch processor-arg shape on model type - Phi-4-MM needs a bare
  string, Whisper needs [prompt]. Passing a list to Phi-4-MM raised
  "Number of image tokens does not match the number of images".

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the setup.py lmms_eval.models entry point, the _model_manifest
factory, and its registration tests. The Olive LMMSEvaluator path imports
LMMSORTGenAIEvaluator directly, so the entry point only affected the
standalone lmms-eval CLI; dropping it keeps setup.py out of this change.
LMMSEvaluator/ortgenai_mm gains two knobs:
- ignore_stop_strings: drops spurious stop strings (e.g. lmms-eval's default
  until=["\n\n"] fewshot_delimiter) from a task's until list so step-by-step
  reasoning runs to EOS instead of truncating at the first blank line.
- system_prompt now defaults to None and is resolved per model family
  (_default_system_prompt_for_model_type) to match lmms-eval's per-model
  wrappers, keeping HF-input and ORT-GenAI prompts identical without per-run config.

Adds unit tests for both.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… preserve nested layout

Adopts upstream microsoft#2529's _find_genai_config/_get_genai_model_dir discovery in
LMMSEvaluator (replacing the naive 2-level _resolve_model_dir), so nested
multi-component ORT-GenAI packages are located by searching upward from the
entry ONNX file.

CompositeToOnnxPackage no longer flattens the package: since ORT-GenAI loads
nested layouts directly and the evaluator now discovers genai_config.json, the
pass just hardlink-copies the nested tree and returns an ONNXModelHandler
pointing at the entry component (default 'decoder'). This drops the expensive
onnx re-serialization / external-data rewriting while keeping the essential
Composite->ONNX handler conversion (LocalSystem.evaluate_model rejects composite
models). Rewrites the pass tests for the nested-preserving behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Drop local 'import json as _json' in LMMSEvaluator.evaluate (json already
  imported at module level) to clear PYLINT reimport warning.
- Apply ruff-format (single-line create_pass_from_dict call) in the
  CompositeToOnnxPackage test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lanning

Introduce a planner pass that annotates a model with mixed_precision_info
(components to keep in full precision) consumed by prepare_model in
quant_utils.py, which skips excluded modules by name. Registers the pass
in olive_config.json.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align HF and ORT-GenAI request construction, generation settings, prompts, metrics, package sizing, and result persistence. Correct multimodal continuation scoring and expand regression coverage for media, Whisper, and composite packages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a07152ad-b18a-4e15-9af2-2a9a0149483e
@DelwinKim
Delwin Kim (DelwinKim) force-pushed the t-delwinkim/lmms-ort-evaluator branch from 08dfd47 to 1c2b96a Compare July 10, 2026 20:46
Implement MBQ decoder reparameterization with modality-balanced calibration masks and downstream quantizer consistency checks. Add MQuant-inspired component activation range diagnostics and shared multimodal calibration primitives, with tests and documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9e5f326b-249c-4121-a665-8eadd74782d1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a07152ad-b18a-4e15-9af2-2a9a0149483e
Add Gemma 4-aware native GPTQ calibration and module discovery, preserve uncalibrated modules in float, support GPTQModel API variants, and retain nested ORT-GenAI packages. Add RTN node globs and robust audio decoding fallback with focused tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a07152ad-b18a-4e15-9af2-2a9a0149483e
Comment thread olive/cache.py Fixed
Comment thread olive/passes/pytorch/quant_utils.py Fixed
Delwin Kim (DelwinKim) and others added 8 commits July 21, 2026 20:23
Combine the multimodal evaluator and deployment-package work with the multimodal quantization pass stack while preserving the original pass commits and merge history.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a07152ad-b18a-4e15-9af2-2a9a0149483e
Add processor-ready AI2D and TextVQA calibration recipes, support the current Qwen2.5-VL decoder layout, and preserve BF16 during MBQ reconstruction search. Include full deployed AI2D evaluation recipes for component-aware RTN and MBQ+RTN.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a07152ad-b18a-4e15-9af2-2a9a0149483e
Classify every architecture-defined embedding module, serialize mixed-precision exclusions for downstream exporters, and add matched Gemma-4 PyTorch KQuant AI2D recipes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a07152ad-b18a-4e15-9af2-2a9a0149483e
Track the complete Delwin_testing recipe and validation workspace for Gemma-4, Qwen2.5-VL, Qwen3-VL, Phi-4-MM, and related benchmark experiments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a07152ad-b18a-4e15-9af2-2a9a0149483e
Remove Delwin_testing from the repository index and ignore the local workspace while preserving its files on disk.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: eeac47b5-2bf9-470e-a3c4-b3205d220039
Add Qwen3-VL module mappings and DeepStack-aware calibration replay. Support glob-based KQuant node overrides with exact-name precedence, add coverage, and ignore generated submissions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7271f78d-9d14-4ce5-a154-5a7ffa9a70c2
Comment thread olive/passes/pytorch/quant_utils.py Fixed
Delwin Kim (DelwinKim) and others added 8 commits August 3, 2026 07:48
Clear the remaining cache CodeQL finding without changing package copy behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Retain generated token IDs so ORT-GenAI evaluations can exclude Gemma's optional thought channel before task scoring. Fail loudly when Gemma response delimiters do not resolve to single tokenizer IDs and preserve incremental decoding for other model families.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Delwin Kim <139003345+DelwinKim@users.noreply.github.com>
Preserve lmms-eval's default model-aware TaskManager when no external path is configured, and pass the active model when resolving custom tasks. Preserve tuple-shaped loglikelihood generations in sample artifacts without misclassifying them as empty.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirror AudioDecoderEx selection for speech processors that advertise multiple target rates. Preserve compatible 8 kHz input, select 16 kHz for higher-rate audio, and persist the supported-rate provenance.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve standard ORT-GenAI Whisper packages through audio_processor_config.json and use the unparameterized AudioDecoder runtime default of 16 kHz while retaining strict failures for unsupported package shapes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Treat AudioDecoder target_sample_rate as a validated scalar package declaration alongside sample_rate and sampling_rate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Let package-declared AudioDecoder pipelines perform their native resampling, reserving host resampling for explicit overrides. Include media construction in fail_on_error handling and record the effective preprocessing mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Apply explicit audio_target_sample_rate overrides directly, including valid upsampling, while retaining downsample-only selection for package-declared AudioDecoder rates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread olive/passes/pytorch/quant_utils.py Fixed
Delwin Kim (DelwinKim) and others added 9 commits August 3, 2026 18:13
Adapt ModelBuilder to the new validated option-preparation API without logging tokens or truncating values, while preserving legacy typed options. Resolve new kv_cache_dim symbols from genai_config for discrepancy checks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Represent FP16 promotions as exclusions and add validated module scoping for multimodal sensitivity runs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Delwin Kim <139003345+DelwinKim@users.noreply.github.com>
The HQQ pass replaced each MatMul with a MatMulNBits node consuming newly
registered quantized initializers, but never removed the original weight
tensor it superseded. Because no node referenced it any more, the weight
stayed in the graph as an orphaned initializer and was still serialized,
inflating the saved model by the full size of the original weights while
changing nothing numerically.

On a Gemma-4 E2B decoder this left all 275 replaced FP16 weights in place,
adding 3.73 GB to a decoder-only package (4.53 GB for all-MatMul) on top of
the 0.94 GB of packed INT4 payload, so HQQ packages could not be compared
against K-Quant or RTN ones by size.

Prune unused initializers after quantization, matching the existing step in
kquant_quantization.py and rtn_quantization.py. Initializers still consumed
by a node, such as the weight of an excluded MatMul, are preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nodes_to_exclude and nodes_to_include were matched with exact string
equality, so a caller had to enumerate every MatMul name to target a
subgraph. Other Olive quantization passes accept wildcard patterns, and
listing hundreds of generated node names is impractical for transformer
decoders where the interesting selections are structural, such as every
node under a vision encoder prefix.

Match both lists with fnmatch when the entry contains a wildcard, keeping
exact matching for plain names so existing configurations behave the same.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MatMul stores its B operand as (K, N), but MatMulNBits expects each output
channel's K-axis blocks laid out contiguously as (N, k_blocks, blob_size).
The pass quantized the (K, N) tensor directly and then reshaped the packed
result to (cols, k_blocks, blob_size), so quantized weights, scales, and zero
points were all written in input-channel-major order while the runtime read
them as output-channel-major.

Blocks therefore spanned unrelated output channels and were paired with the
wrong scales and zero points. Packages built this way loaded and generated
non-empty text, but the output was incoherent, which made the corruption easy
to mistake for a quality regression rather than a layout bug.

Transpose to (N, K) and quantize along K so all three tensors share the
runtime's ordering, and derive the reshape dimensions from the original
weight rather than the transposed tensor. Non-zero axis values are now
rejected explicitly, since only axis 0 was ever handled correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When HQQ replaces a weight that is also exposed as a graph input, remove both the unused initializer and its obsolete input declaration so the quantized model remains runnable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Recognize bracket-expression globs and remove replaced weight initializers recursively in every graph scope while preserving captured parent initializers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Serialize mapping-valued ORT-GenAI options with their documented selector syntax and treat both FP16 and FP32 SelectiveMixedPrecision promotions as quantization exclusions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the standard AudioDecoder 16 kHz fallback when only unrelated decoder attributes are configured and no explicit sample rate is declared.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 38 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

olive/passes/pytorch/multimodal_mixed_precision.py:333

  • _load_meta_model unconditionally imports accelerate.init_empty_weights. If accelerate isn't installed (it’s treated as optional elsewhere, e.g. SelectiveMixedPrecision), this pass will raise ImportError and fail even though it can fall back to a real model load for classification.
        import transformers
        from accelerate import init_empty_weights
        from transformers import AutoModel

Comment thread olive/passes/pytorch/quant_utils.py
Fall back to real model loading when Accelerate is unavailable, document the intentional calibration sentinel, and consolidate the evaluator test import used for dispatch monkeypatching.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Delwin Kim <139003345+DelwinKim@users.noreply.github.com>
import PIL.Image
import pytest

import olive.evaluator.olive_evaluator as olive_evaluator_module

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 38 changed files in this pull request and generated no new comments.

Suppressed comments (1)

olive/passes/pytorch/train_utils.py:302

  • get_calibration_dataset assumes every dataloader batch is a 2-tuple (input_data, labels) and unpacks it unconditionally. Olive datasets can also be unsupervised and yield a single dict (see BaseDataset), and in that case this will crash with an unpacking error. Consider accepting both shapes: (dict, labels) and dict (labels=None), and raise a clear error for anything else.
    for input_data, labels in dataloader:

@DelwinKim

Copy link
Copy Markdown
Author

Follow-up: handling tasks (and metrics) not covered by LMMSEvaluator

Two related questions given the overlap with Olive's existing eval: (a) how do we add a benchmark that isn't available through LMMSEvaluator, and (b) how do we add a metric that isn't there? Both are valid; here's how each works.

Missing tasks

Three paths depending on why it's missing.

1. Dataset/scorer isn't in lmms-eval → add it as an lmms-eval task. lmms-eval tasks are a YAML + optional utils.py (doc_to_text, doc_to_visual, doc_to_target, process_results, metric/aggregation). Author it and either contribute upstream or keep it in a local task dir registered via TaskManager(include_path=<dir>).

⚠️ Code gap: LMMSEvaluator.evaluate currently calls lmms_eval.evaluator.simple_evaluate(model=lm, tasks=tasks, ...) with no task_manager/include_path, so only lmms-eval's built-in task registry resolves — custom local tasks can't be used today. Suggest adding a config field (e.g. include_path/task_dir) and passing task_manager=TaskManager(include_path=...) into simple_evaluate, mirroring what the text LMEvaluator already does with TaskManager().

2. Bespoke/proprietary task → use Olive's existing custom evaluation path. Reuse AccuracyMetric + custom pre/post-process + the existing _inference_vision* / _inference_text_genai* paths, or register a fully custom evaluator via user_script (OliveEvaluatorConfig imports the user module to register it). A reason to keep the custom stack as an escape hatch rather than delete it.

3. Capability gap, not a dataset → extend the ortgenai_mm adapter. If the task exists but fails due to an adapter limitation (batch_size>1, multi-round/interleaved generation — generate_until_multi_round is a stub, a new model_type/processor shape, or a new media type), fix it in olive/evaluator/lmms_ort.py (generate_until / loglikelihood / _build_prompt_for_request).

Missing metrics

Valid question, and mostly a sub-case of the above — in lmms-eval, metrics live with the task, not globally:

  • Per-task metric (most common): compute it directly in the task's process_results in utils.py and return it in the results dict; declare its metric_list + aggregation + higher_is_better in the task YAML. No global registration needed.
  • Reusable metric across tasks: register it in lmms-eval's metric registry (lmms_eval/api/metrics.py: register_metric / register_aggregation / register_higher_is_better), then reference it from any task YAML.
  • Olive side needs no change: LMMSEvaluator converts results by iterating the numeric entries in results["results"][task], so any metric lmms-eval reports numerically flows into Olive's MetricResult automatically. (One caveat: Olive assumes higher_is_better=True in that conversion — error-rate style metrics like WER/CER would be surfaced with the wrong direction and should honor lmms-eval's higher_is_better map instead.)

Recommendation: for anything with a published protocol, prefer adding it as an lmms-eval task/metric and close the include_path gap so custom tasks/metrics are first-class; reserve Olive's custom evaluator for truly internal benchmarks.

Thanks for the suggestion, implemented these solutions to these 2 Olive "gaps"

include_path now accepts one task directory or a list, validates each path, and passes a model-aware TaskManager(include_path=..., model_name=lm) for both ONNX and HF dispatch. When include_path is absent, Olive does not construct a manager, preserving lmms-eval's normal default-manager behavior. End-to-end resolution was tested with an external task directory and a real ORT-GenAI package.

Metric direction now comes from lmms-eval's per-task higher_is_better map rather than defaulting every metric to True; the regression test verifies that WER is lower-is-better. Numeric metrics returned by custom task process_results continue to flow into Olive without a new Olive metric registration.

One limitation remains important: include_path can add collision-free task names, but it cannot override a built-in task with the same name. Corrections to existing lmms-eval tasks still need to go upstream rather than being hidden in a local task directory.

For a benchmark that is absent from lmms-eval, I would follow the three paths you outlined:

For a public benchmark with a published protocol, add a versioned lmms-eval YAML/utils.py task, use include_path while it is under development, and contribute it upstream where possible.
For a proprietary or highly application-specific benchmark, retain Olive's existing custom evaluator/metric path as the escape hatch rather than forcing it into lmms-eval.
If the task already exists and the failure is an inference capability issue, extend ortgenai_mm. This PR currently supports single-request image and audio generation plus compatible text loglikelihood; it does not currently support batched generation, multi-round/interleaved generation, every processor signature, or additional media types.
I also agree with the metric split. A task-specific metric belongs in that task's process_results and YAML aggregation declaration; a genuinely reusable metric belongs in lmms-eval's metric registry. In either case, numeric results and their direction now pass through LMMSEvaluator into Olive's MetricResult.

So the intended boundary is: use lmms-eval for public, protocol-defined benchmarks; use Olive's custom evaluation path for internal/proprietary workloads; and change the adapter only when the benchmark exists but deployed ORT-GenAI inference lacks a required capability.

Avoid invoking the native ORT-GenAI tokenizer with an empty generated sequence or an empty response-channel suffix. Return the existing semantic empty response instead and cover both cases with a regression test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Delwin Kim <139003345+DelwinKim@users.noreply.github.com>
@microsoft-github-policy-service

Copy link
Copy Markdown
Contributor

Delwin Kim (@DelwinKim) please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"
Contributor License Agreement

Contribution License Agreement

This Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
and conveys certain license rights to Microsoft Corporation and its affiliates (“Microsoft”) for Your
contributions to Microsoft open source projects. This Agreement is effective as of the latest signature
date below.

  1. Definitions.
    “Code” means the computer software code, whether in human-readable or machine-executable form,
    that is delivered by You to Microsoft under this Agreement.
    “Project” means any of the projects owned or managed by Microsoft and offered under a license
    approved by the Open Source Initiative (www.opensource.org).
    “Submit” is the act of uploading, submitting, transmitting, or distributing code or other content to any
    Project, including but not limited to communication on electronic mailing lists, source code control
    systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of
    discussing and improving that Project, but excluding communication that is conspicuously marked or
    otherwise designated in writing by You as “Not a Submission.”
    “Submission” means the Code and any other copyrightable material Submitted by You, including any
    associated comments and documentation.
  2. Your Submission. You must agree to the terms of this Agreement before making a Submission to any
    Project. This Agreement covers any and all Submissions that You, now or in the future (except as
    described in Section 4 below), Submit to any Project.
  3. Originality of Work. You represent that each of Your Submissions is entirely Your original work.
    Should You wish to Submit materials that are not Your original work, You may Submit them separately
    to the Project if You (a) retain all copyright and license information that was in the materials as You
    received them, (b) in the description accompanying Your Submission, include the phrase “Submission
    containing materials of a third party:” followed by the names of the third party and any licenses or other
    restrictions of which You are aware, and (c) follow any other instructions in the Project’s written
    guidelines concerning Submissions.
  4. Your Employer. References to “employer” in this Agreement include Your employer or anyone else
    for whom You are acting in making Your Submission, e.g. as a contractor, vendor, or agent. If Your
    Submission is made in the course of Your work for an employer or Your employer has intellectual
    property rights in Your Submission by contract or applicable law, You must secure permission from Your
    employer to make the Submission before signing this Agreement. In that case, the term “You” in this
    Agreement will refer to You and the employer collectively. If You change employers in the future and
    desire to Submit additional Submissions for the new employer, then You agree to sign a new Agreement
    and secure permission from the new employer before Submitting those Submissions.
  5. Licenses.
  • Copyright License. You grant Microsoft, and those who receive the Submission directly or
    indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license in the
    Submission to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute
    the Submission and such derivative works, and to sublicense any or all of the foregoing rights to third
    parties.
  • Patent License. You grant Microsoft, and those who receive the Submission directly or
    indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license under
    Your patent claims that are necessarily infringed by the Submission or the combination of the
    Submission with the Project to which it was Submitted to make, have made, use, offer to sell, sell and
    import or otherwise dispose of the Submission alone or with the Project.
  • Other Rights Reserved. Each party reserves all rights not expressly granted in this Agreement.
    No additional licenses or rights whatsoever (including, without limitation, any implied licenses) are
    granted by implication, exhaustion, estoppel or otherwise.
  1. Representations and Warranties. You represent that You are legally entitled to grant the above
    licenses. You represent that each of Your Submissions is entirely Your original work (except as You may
    have disclosed under Section 3). You represent that You have secured permission from Your employer to
    make the Submission in cases where Your Submission is made in the course of Your work for Your
    employer or Your employer has intellectual property rights in Your Submission by contract or applicable
    law. If You are signing this Agreement on behalf of Your employer, You represent and warrant that You
    have the necessary authority to bind the listed employer to the obligations contained in this Agreement.
    You are not expected to provide support for Your Submission, unless You choose to do so. UNLESS
    REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, AND EXCEPT FOR THE WARRANTIES
    EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE SUBMISSION PROVIDED UNDER THIS AGREEMENT IS
    PROVIDED WITHOUT WARRANTY OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF
    NONINFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
  2. Notice to Microsoft. You agree to notify Microsoft in writing of any facts or circumstances of which
    You later become aware that would make Your representations in this Agreement inaccurate in any
    respect.
  3. Information about Submissions. You agree that contributions to Projects and information about
    contributions may be maintained indefinitely and disclosed publicly, including Your name and other
    information that You submit with Your Submission.
  4. Governing Law/Jurisdiction. This Agreement is governed by the laws of the State of Washington, and
    the parties consent to exclusive jurisdiction and venue in the federal courts sitting in King County,
    Washington, unless no federal subject matter jurisdiction exists, in which case the parties consent to
    exclusive jurisdiction and venue in the Superior Court of King County, Washington. The parties waive all
    defenses of lack of personal jurisdiction and forum non-conveniens.
  5. Entire Agreement/Assignment. This Agreement is the entire agreement between the parties, and
    supersedes any and all prior agreements, understandings or communications, written or oral, between
    the parties relating to the subject matter hereof. This Agreement may be assigned by Microsoft.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants