Skip to content

Allow overriding past_present_share_buffer in LMEvaluator ortgenai backend#2569

Merged
jiafatom merged 7 commits into
mainfrom
jiafa/lmeval-genai-model-args
Jul 14, 2026
Merged

Allow overriding past_present_share_buffer in LMEvaluator ortgenai backend#2569
jiafatom merged 7 commits into
mainfrom
jiafa/lmeval-genai-model-args

Conversation

@jiafatom

@jiafatom jiafatom commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Describe your changes

The ortgenai lm-eval backend (LMEvalORTGenAIEvaluator) always derived past_present_share_buffer from the exported genai_config.json, with no way to override it from a recipe/evaluator config. Some models (e.g. Gemma 4) export with shared past/present buffers enabled but require it disabled for correct KV-cache handling during evaluation. Additionally, LMEvaluator gave recipe authors no way to enable a model's chat template or few-shot prompting, so instruction-tuned models were evaluated 0-shot on raw prompts and scored near-random. Today these gaps force recipe authors to bypass LMEvaluator and write a standalone eval.py that calls lm_eval.simple_evaluate directly — the declarative Olive config path simply produces wrong results for these models.

This PR makes these settings overridable end-to-end and adds optional sample logging:

  • LMEvalORTGenAIEvaluator now accepts an explicit past_present_share_buffer argument that takes precedence over the exported value. The resolution logic is extracted into a small _resolve_past_present_share_buffer(override, genai_config) helper (explicit override wins; otherwise fall back to the exported value, defaulting to False).
  • LMEvaluator gains a model_args dict that is forwarded verbatim to the lm-eval model backend constructor, so genai-specific options can be set from the evaluator config. User-provided model_args override the Olive-derived defaults (batch_size, max_length, ep, ...). Default is {}, so existing configs are unaffected.
  • LMEvaluator prompting controls: new apply_chat_template, system_instruction, fewshot_as_multiturn and num_fewshot options, forwarded to lm_eval.simple_evaluate. Instruction-tuned models score near-random on multiple-choice tasks when evaluated 0-shot on raw prompts; wrapping requests in the model's chat template and matching the card's few-shot protocol fixes this. Empirically, on a Gemma-4 E2B export a German MMMLU subset went from ~0.16–0.24 (0-shot, no template) to ~0.52–0.68 (5-shot + chat template). Defaults preserve lm-eval's legacy behavior (no chat template, no system prompt, task-default few-shot), so existing configs are unaffected.
  • LMEvaluator sample logging: a new sample_log_num (and optional sample_log_dir) option. When sample_log_num > 0, lm-eval's log_samples is enabled and the first N per-task predictions — question, options, resolved prediction (argmax of choice loglikelihoods to letter + text, or the raw generation), target, and acc — are written to <task>_samples.jsonl. Rather than duplicating the writer, OliveEvaluator.save_sample_log is decoupled to take a (name, sample_log_dir) pair instead of a Metric, so LMEvaluator reuses the exact same JSONL writer as the accuracy evaluators; a small _lmeval_samples_to_output helper maps lm-eval sample records into an OliveModelOutput (carrying question/options/prediction_index/acc via extras). This makes it possible to inspect why a task scores low (e.g. distinguishing a genuinely weak/quantized model from a broken eval). Default is 0, so existing configs are unaffected.

With this, such models can be evaluated with a standard LMEvaluator config instead of a bespoke script:

{
    "evaluators": {
        "mmlu": {
            "type": "LMEvaluator",
            "tasks": ["mmlu"],
            "model_class": "ortgenai",
            "num_fewshot": 5,
            "apply_chat_template": true,
            "fewshot_as_multiturn": true,
            "sample_log_num": 100,
            "model_args": { "past_present_share_buffer": false }
        }
    }
}

Checklist before requesting a review

  • Add unit tests for this change.
  • Make sure all tests can pass.
  • Update documents if necessary.
  • Lint and apply fixes to your code by running lintrunner -a
  • Is this a user-facing change? If yes, give a description of this change to be included in the release notes.

Release note: LMEvaluator now accepts a model_args dict forwarded to the lm-eval model backend; apply_chat_template, system_instruction, fewshot_as_multiturn and num_fewshot prompting options forwarded to lm_eval.simple_evaluate (so instruction-tuned models can be evaluated with their chat template and the card's few-shot protocol); and a sample_log_num/sample_log_dir option to log the first N per-task predictions vs. targets to a JSONL file for debugging (reusing the accuracy evaluators' save_sample_log writer). The ortgenai backend accepts an explicit past_present_share_buffer override (previously only read from genai_config.json).

(Optional) Issue link

…ckend

The `ortgenai` lm-eval backend always derived `past_present_share_buffer`
from the exported `genai_config.json`, with no way to override it from a
recipe config. Some models (e.g. Gemma 4) export with shared buffers
enabled but require it disabled for correct KV-cache handling during
evaluation, forcing users to write a standalone eval script instead of a
declarative Olive config.

- `LMEvalORTGenAIEvaluator` now accepts an explicit `past_present_share_buffer`
  argument that takes precedence over the exported value (extracted into a
  small `_resolve_past_present_share_buffer` helper for testability).
- `LMEvaluator` gains a `model_args` dict that is forwarded verbatim to the
  lm-eval model backend constructor, so genai-specific options can be set
  from the evaluator config. User-provided `model_args` override the
  Olive-derived defaults (batch_size, max_length, ep, ...).

This lets recipes evaluate such models with a standard `LMEvaluator` config,
e.g. `"model_args": {"past_present_share_buffer": false}`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37949b3b-0064-4033-8d7a-8127271bcf39
Copilot AI review requested due to automatic review settings July 14, 2026 17:41

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

This PR enhances Olive’s LMEvaluator integration with lm-eval by allowing recipe configs to pass backend-specific constructor kwargs via a new model_args dict, and by enabling an explicit override of past_present_share_buffer for the ortgenai backend to address KV-cache correctness issues for certain exported models (e.g., Gemma 4).

Changes:

  • Add model_args support to LMEvaluator, forwarding kwargs to the selected lm-eval backend constructor with user values overriding Olive-derived defaults.
  • Add a past_present_share_buffer override path to LMEvalORTGenAIEvaluator, with precedence over the exported genai_config.json value (and defaulting to False when absent).
  • Add unit tests covering the new resolution helper and the forwarding behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
olive/evaluator/olive_evaluator.py Introduces model_args forwarding into lm-eval backend construction.
olive/evaluator/lmeval_ort.py Adds past_present_share_buffer override resolution for the ortgenai backend.
test/evaluator/test_olive_evaluator.py Adds a test ensuring model_args reaches the backend constructor.
test/evaluator/test_lmeval_ort.py Adds tests for _resolve_past_present_share_buffer precedence/default behavior.

Comment thread olive/evaluator/olive_evaluator.py
Comment thread olive/evaluator/olive_evaluator.py
Comment thread test/evaluator/test_olive_evaluator.py
jiafatom added a commit to microsoft/olive-recipes that referenced this pull request Jul 14, 2026
Add eval/mmlu_cpu.json and eval/mmlu_cuda.json that evaluate the exported
mixed ONNX model on leaderboard_mmlu_pro via Olive's LMEvaluator (ortgenai
backend). Gemma 4 exports with past_present_share_buffer enabled but the
decoder needs it disabled during eval, so the configs pass
"model_args": {"past_present_share_buffer": false} (requires microsoft/Olive#2569).

Document the new configs in eval/README.md and the top-level README.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37949b3b-0064-4033-8d7a-8127271bcf39
- Validate that model_args is a dict, failing early with a clear error
  instead of a confusing unpacking error later.
- Use the effective batch size (after model_args overrides) for the
  lm-eval simple_evaluate call so backend batching and lm-eval batching
  stay consistent.
- Assert simple_evaluate receives the overridden batch size, and add a
  test that a non-dict model_args is rejected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37949b3b-0064-4033-8d7a-8127271bcf39
@jiafatom

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Addressed all three in 02d7818:

  1. simple_evaluate batch size inconsistency — now uses effective_batch_size = model_init_args["batch_size"] so lm-eval batching matches the backend after any model_args override.
  2. Non-dict model_args — validated in __init__ with an early ValueError("model_args must be a dict, ...").
  3. Test coverage — the forwarding test now also asserts simple_evaluate receives the overridden batch size, plus a new test that a non-dict model_args is rejected.

When sample_log_num > 0, LMEvaluator now enables lm-eval log_samples and writes
the first N per-task predictions (question, options, resolved prediction, target,
acc) to <task>_samples.jsonl, mirroring the accuracy evaluators' sample logging.
This makes it possible to inspect why a task scores low.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37949b3b-0064-4033-8d7a-8127271bcf39
Comment thread test/evaluator/test_olive_evaluator.py Fixed
Comment thread test/evaluator/test_olive_evaluator.py Fixed
Comment thread test/evaluator/test_olive_evaluator.py Fixed
Comment thread test/evaluator/test_olive_evaluator.py Fixed
jiafatom and others added 3 commits July 14, 2026 18:24
Add # pylint: disable=protected-access to the tests that call the
_extract_prediction / _save_lmeval_samples helpers directly, matching
the existing pattern in this file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 37949b3b-0064-4033-8d7a-8127271bcf39
- Remove redundant function-local `import json` in test_olive_evaluator.py
  (json is imported at module scope), fixing pylint W0621/W0404 that failed
  the Python format lint job.
- Relax the dynamic-shapes tuning assertion to match torch.ones' message
  across torch versions ("ones() received an invalid combination..." vs
  "ones(): argument 'size' ... must be tuple of ints"), fixing the Linux/
  Windows unit test failure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 770571fd-fbfe-46d2-8c21-67c2941eede4
Decouple OliveEvaluator.save_sample_log to take (name, sample_log_dir)
instead of a Metric, so LMEvaluator can reuse it. Replace
_save_lmeval_samples with _lmeval_samples_to_output, which maps lm-eval
sample records into an OliveModelOutput (question/options/prediction_index/acc
carried via extras) fed to the shared writer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 770571fd-fbfe-46d2-8c21-67c2941eede4
@jiafatom jiafatom enabled auto-merge (squash) July 14, 2026 21:49
@jiafatom jiafatom disabled auto-merge July 14, 2026 21:55
Forward apply_chat_template, system_instruction, fewshot_as_multiturn and
num_fewshot from the evaluator config to lm-eval's simple_evaluate. Instruction-
tuned models (e.g. Gemma) score near-random on multiple-choice tasks when
evaluated 0-shot without their chat template; these options let recipes match a
model card's reported protocol. Defaults preserve lm-eval's legacy behavior, so
existing configs are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 770571fd-fbfe-46d2-8c21-67c2941eede4
@jiafatom jiafatom merged commit 2f70f0f into main Jul 14, 2026
11 checks passed
@jiafatom jiafatom deleted the jiafa/lmeval-genai-model-args branch July 14, 2026 22:54
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