Add LMMSEvaluator (integrate lmms-eval toolkit) for multimodal (vision+audio) evaluation#2531
Add LMMSEvaluator (integrate lmms-eval toolkit) for multimodal (vision+audio) evaluation#2531DelwinKim wants to merge 10 commits into
Conversation
| return prompt_template.format( | ||
| system_prompt=system_prompt, | ||
| user_content=user_content, | ||
| text=user_text, | ||
| image_tokens=image_tokens, | ||
| audio_tokens=audio_tokens, | ||
| model_type=model_type, | ||
| ) |
| Avoids constructing a real HfModelHandler (which would require a real HF | ||
| model on disk) while still exercising the dispatch logic. | ||
| """ | ||
| import olive.evaluator.olive_evaluator as oe |
Sample Benchmark Results — Gemma-4 E2B-it, per-component INT4 (KQuant) sensitivityTo show the evaluator end-to-end (and that it produces literature-comparable numbers), here is a small quantization sweep (weight only) on Pipeline:
Vision (higher is better; ai2d/ocrbench = accuracy, chartqa = relaxed-overall, docvqa = ANLS):
Audio (WER, lower is better):
Observations:
Setup: CUDA EP, |
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>
|
@DelwinKim please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
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
08dfd47 to
1c2b96a
Compare
Describe your changes
This PR adds multimodal (vision + audio) evaluation to Olive by integrating the lmms-eval harness as an Olive evaluator, plus the supporting pass needed to make quantized multi-component ORT-GenAI packages evaluable.
What's added
1.
LMMSEvaluator(olive/evaluator/olive_evaluator.py)A new evaluator (
"type": "LMMSEvaluator") that runs lmms-eval benchmarks from within a singleolive run(can also run eval standalone). It supports two model handler types:ONNXModelHandlerpointing at an ORT-GenAI multimodal package (genai_config.json+ quantized ONNX, e.g. fromMobiusBuilder+OnnxKQuantQuantization) → dispatches to the newortgenai_mmadapter.HfModelHandlerfor HuggingFace PyTorch multimodal models → dispatches to lmms-eval's native wrapper, auto-detected from the HFmodel_type(overridable viamodel_class).Benchmarks are selected purely by config (
"tasks": ["ocrbench", "docvqa_val_lite", "librispeech_test_clean", "fleurs_en", ...]), and the harness's official scorers are used (OCRBench scoring, DocVQA ANLS, WER, etc.), so results can be compared directly against published literature.2.
ortgenai_mmadapter (olive/evaluator/lmms_ort.py)A lmms-eval model adapter that drives ORT-GenAI multimodal generation. Implements
generate_untilandloglikelihood, handles image and audio inputs (og.Images/og.Audios), per-model-type processor argument shapes (Phi-4-MM vs Whisper), and EOS/stop handling (including Whisper's BOS==EOS collision).3.
CompositeToOnnxPackagepass (olive/passes/onnx/composite_to_onnx_package.py)MobiusBuilderemits a multi-componentCompositeModelHandler(decoder / vision / audio / embedding in subdirectories). A composite can't be evaluated directly — it has no single session, andLocalSystem.evaluate_modelrejects composite handlers. This pass converts the fanned-out components into a single runnableONNXModelHandler(an ORT-GenAI package), so the evaluator can load it.4. Registration
olive/olive_config.json: registers the new evaluator and pass.Example recipe
A complete example recipe — build + quantize Gemma‑4 E2B, package to an ORT‑GenAI package, then evaluate vision and audio benchmarks:
{ "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 }, "flatten": { "type": "CompositeToOnnxPackage" } }, "evaluators": { "evaluator": { "type": "LMMSEvaluator", "tasks": ["ai2d_lite"], "batch_size": 1, "max_length": 16384, "limit": 10, "log_samples": false, "ignore_stop_strings": ["\n\n"], "output_path": "results/gemma4_vision_audio.json" } }, "evaluator": "evaluator", "evaluate_input_model": false, "target": "local_system", "output_dir": "models/gemma4_vision_audio", "cache_dir": "cache/gemma4_vision_audio", "no_artifacts": true }A complete example recipe, evaluate the HF Pytorch model:
{ "input_model": { "type": "HfModel", "model_path": "google/gemma-4-E2B-it" }, "systems": { "local_system": { "type": "LocalSystem", "accelerators": [ { "device": "gpu", "execution_providers": ["CUDAExecutionProvider"] } ] } }, "evaluators": { "evaluator": { "type": "LMMSEvaluator", "model_class": "gemma4", "tasks": ["ai2d_lite"], "batch_size": 1, "max_length": 16384, "limit": 10, "log_samples": false, "output_path": "results/gemma4_vision_audio.json" } }, "evaluator": "evaluator", "evaluate_input_model": true, "target": "local_system", "output_dir": "models/gemma4_vision_audio", "cache_dir": "cache/gemma4_vision_audio", "no_artifacts": true }Run it with:
LMMSEvaluatorconfig referencetasksocrbench,docvqa_val_lite,chartqa_lite,ai2d_lite,mmmu_val,textvqa_val,librispeech_test_clean,librispeech_test_other,fleurs_en.limitNone(full set)batch_size11is supported (see limitations).max_new_tokens256generate_untilrequest.max_length327684096) to bound memory.system_promptNone(auto)prompt_templateNoneignore_stop_stringsNoneuntillist (e.g.["\n\n"]). Use with a largermax_new_tokensfor step-by-step reasoning tasks that lmms-eval would otherwise truncate at the first blank line.log_samplesfalseoutput_pathNoneTests
test/evaluator/test_lmms_ort.py— adapter unit tests (generation, loglikelihood, image/audio handling, processor branching, EOS logic, prompt building / media-token auto-detection).test/passes/onnx/test_composite_to_onnx_package.py— composite→ONNX pass (entry-point component selection, genai_config discovery).Notes / limitations
batch_sizeis currently fixed at 1: ORT-GenAI's per-requestGenerator+set_inputsflow is single-sequence, and batching ragged, per-request multimodal (image/audio) inputs isn't currently supported by the runtime.lmms-evalis imported lazily, so non-multimodal Olive usage is unaffected if it isn't installed. Running theLMMSEvaluator(and the full adapter test suite) requirespip install lmms-eval.Checklist before requesting a review
lmms-evalinstalled; mocked tests pass without it, but the tests that instantiate the real adapter need the dependency.)lintrunner -aLMMSEvaluatorfor multimodal (vision + audio) evaluation via the lmms-eval harness, andCompositeToOnnxPackageto convert multi-component ORT-GenAI packages for evaluation.(Optional) Issue link