[WIP] Shared-input Hessian dedup: :in=<tag> plan metadata + CPU probe + looper leader/follower capture - #3052
[WIP] Shared-input Hessian dedup: :in=<tag> plan metadata + CPU probe + looper leader/follower capture#3052Qubitium wants to merge 3 commits into
:in=<tag> plan metadata + CPU probe + looper leader/follower capture#3052Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
:in=<tag>) + CPU forward probe:in=<tag>) + CPU forward probe
Review: P1 — do not infer shared-Hessian eligibility from subset grouping by defaultThe current default rule — same immediate parent + same expanded quantization subset/block => same input tensor — is not a safe semantic contract. The numeric subset/block historically describes quantization/execution grouping, not tensor identity. This PR itself demonstrates the problem: MLA definitions had modules in the same subset that receive different activations, so they needed explicit The risk becomes correctness-critical in the follow-up that skips follower The real-forward suite is good, but I recommend one of these safer contracts before wiring this into the looper:
The optimization is valuable, but the failure mode here is silent quantization error, so the default should be conservative rather than inferred from an unrelated grouping primitive. |
Review: P1 —
|
Review: P2 —
|
Review summaryI reviewed the planner/probe implementation, the model-definition annotations, the targeted synthetic tests, the tiny real-forward coverage, existing PR discussion, and current CI state. Verdict: changes requested before this becomes a Hessian-dedup contract. The implementation is well-tested structurally, but there are two correctness hazards that can turn into silent quantization error once follower Hessian collection is skipped:
I also flagged the probe API semantics because Current Ruff workflow is green; I did not find a CI failure driving these findings. The concerns are semantic/correctness issues in the new contract rather than formatting or test hygiene. |
…nflicting leaf metadata; strict probe ok - untagged modules are singleton groups; only same-parent same-:in= leaves dedup - collect_leaf_specs raises ValueError when a template is redefined with different flags - SharedInputProbeReport: has_errors / fully_verified; ok == fully_verified - :in= tags added only to definitions verified by the tiny CPU forward suite (llama family, qwen3, phi3, qwen3/qwen2 moe, mixtral, deepseek v3, glm4 moe, qwen3.5 dense/moe, qwen3-next, gpt-oss, llama4)
|
Re "P1 — do not infer shared-Hessian eligibility from subset grouping by default" (comment 5536991865): agreed, fixed in e7cfd32. Untagged modules are now singleton groups (key = module path); dedup only happens for same-parent leaves carrying the same |
|
Re "P1 — |
|
Re " |
…t groups - HessianConfig.dedup_shared_inputs (default True, dynamic-overridable) - GPTQ.adopt_hessian_from(leader): private fp32 copy of leader H/nsamples/fwd_counter - GPTQProcessor elects one leader per explicit group within a subset; follower hooks are no-ops; followers adopt after the forward, before coverage/quantize - only plain GPTQ tasks with matching columns participate; singletons/untagged never - stage_subset wires begin/end_shared_input_capture around hook install/removal - unit tests (adopt semantics, election, capture/adopt) + tiny-Llama CPU e2e: dedup on/off yields bit-identical quantized weights
:in=<tag>) + CPU forward probe:in=<tag> plan metadata + CPU probe + looper leader/follower capture
|
@ZX-ModelCloud Validate |
Follow-up validation reviewI reviewed the current head ( P1 —
|
Summary
Model-definition metadata that identifies decoder-layer modules consuming the same input activation, and looper wiring so the GPTQ Hessian (
H = XᵀX) is collected once per group and copied to the other members (e.g.q/k/v_proj,gate/up_proj).Phase 1 (8bf42b7, e7cfd32): metadata + CPU probe. Phase 2 (e7ec666): looper dedup.
Contract (after review): dedup is explicit opt-in only. Every quantizable module is a singleton group unless sibling leaves under the same parent carry the same
:in=<tag>. Subset digits are never used to infer sharing.What Changed
gptqmodel/models/shared_input.py::in=<tag>module_tree leaf flag (ordinary flag to existing parsers; does not alter emitted blocks).build_shared_input_plan(module_tree, layer_modules) -> SharedInputPlanwith frozenSharedInputGroup(key, parent, modules, subset_indices, explicit); helpersleader_for,followers_of,shares_input,is_explicit,for_subset,filter_modules,with_prefix,dedup_count.key == module path); same parent + same:in=<tag>-> shared (key == "<parent>:in=<tag>", may span subsets).:!/:?leaves excluded;experts.{i}expand per expert (routed experts never share with each other).collect_leaf_specs()raisesValueErrorwhen a leaf is repeated acrossmodule_treevariants with differentsubset_tag/input_tag/quantize; identical duplicates are fine.probe_shared_inputs(layer, plan, forward) -> SharedInputProbeReport: pre-hooks every planned module, runs a real forward, compares captured inputs (shape/dtype/values/call-count). Status:has_errors(mismatches or undeclared identical inputs),fully_verified(no errors, no missing modules, no uncalled groups),ok == fully_verified(strict).BaseQModel.shared_input_plan(model_config, quantize_config, is_awq_quantize=False)classmethod.:in=xtags added only to definitions covered by the tiny-CPU real-forward suite: llama (+ qwen2/mistral/gemma2/gemma3 via inheritance), qwen3, phi3, qwen3_moe, qwen2_moe, mixtral, deepseek_v3 (q_a_proj/kv_a_proj_with_mqa->in=h), glm4_moe (incl. nestedshared_expertsspanning blocks), qwen3_5 / qwen3_5_text / qwen3_5_moe / qwen3_5_moe_text (incl.linear_attn.in_proj_qkv:0+in_proj_z:1), qwen3_next, gpt_oss, llama4.q_b_proj:…:in=q_a,kv_b_proj:…:in=kv_aas documented singletons — the probe showed they receive different tensors ((2,6,16)vs(2,1,6,16)).full_layer_modules()/simple_layer_modules()output unchanged for all definitions (:in=is transparent to block emission).Phase 2 — looper Hessian dedup
HessianConfig.dedup_shared_inputs: bool = True(serialized to quant meta; dynamic-overridable per module, e.g.{".*\\.v_proj$": {"hessian": {"dedup_shared_inputs": False}}}).GPTQ.adopt_hessian_from(leader):leader.materialize_global_hessian()then a private fp32 copy ofH(copy=True, follower's target device) plusnsamples/fwd_counter; clears follower partials. Raises on column mismatch. Independence matters becausequantize()mutatesHin place andfree()drops it.LoopProcessor.begin_shared_input_capture(model, subset_names, is_lm_head_module) -> {follower: leader}/end_shared_input_capture(subset_names): no-op defaults;GPTQProcessoroverrides them:model.shared_input_plan(model_config, quantize_config);explicitgroup: candidates = members present in this subset, with a task of type exactlyGPTQ(GPTAQ/FOEM excluded), per-modulehessian.dedup_shared_inputson, samecolumns; first in subset order leads, others follow; <2 candidates -> no dedup;pre_process_fwd_hook(name)returns a no-op hook for followers (leader/singleton hooks unchanged, incl. keep-mask splitting);end_shared_input_capture->follower.adopt_hessian_from(leader), marks follower as having captured input ids, clears election state;shared_input_dedup_counttracks adoptions.stage_subset._run_single_subset_pass:begin_…before hooks are installed (only whenexecute_forward),end_…right after hooks are removed and before coverage validation / worker quantization.Tests
tests/module_tree/test_shared_input.py: flag parsing, leaf-spec extraction incl. repeated nested keys, expert placeholder resolution, singleton default, explicit same-subset / cross-subset grouping, conflicting-variant metadata raises (tag,:!,:?, subset) while identical duplicates pass,:!/:?exclusion, block output unchanged with:in=, prefix/filter, concurrent determinism, MoE, every registered definition covers its quantizable paths and has no non-explicit shared group, synthetic probe pass/fail modes (shape, dtype, value, missing, uncalled, partial, multi-call, kwarg tensors, hook cleanup on success/exception, undeclared pairs,has_errors/fully_verified/strictok).tests/module_tree/test_shared_input_cpu_forward.py: tiny HF configs on CPU (hidden 32–64, 2 layers) throughconvert_model+ realmodel(input_ids)probe for Llama, Qwen2/3, Mistral, Gemma2/3, Phi3, Qwen2/3-MoE, Mixtral, DeepSeek-V3, GLM4-MoE, Qwen3.5 dense + MoE, Qwen3-Next, GPT-OSS, Llama4. Every layer must befully_verifiedwithundeclared == ()and every shared groupexplicit; negative tests show a wrong MLA tag ->mismatches, an untagged q/k/v triple ->undeclared, a wrongly split q/k ->undeclared, and un-routed experts ->unverified(strictokFalse,has_errorsFalse).tests/test_shared_input_hessian_dedup.py(26, CPU):adopt_hessian_fromequal-but-independentH(mutating follower leaves leader intact), nsamples/fwd_counter carry-over, idempotent multi-follower, uncalled leader -> zeroH, self no-op, column mismatch raises,quantize()after adopt == independent collection; election: first-in-subset leads, singletons/untagged never, subset-restricted members, missing tasks,dedup_shared_inputs=False(global + dynamic), lm_head, model without plan API, non-plain-GPTQ excluded, column mismatch skipped, state reset on nextbegin; capture: followers get 0add_batchthen adopt, hooks resume normal afterend, dedup vs independentHallclose, keep-mask path,endwithoutbegin/ with pruned tasks.tests/test_shared_input_hessian_dedup_e2e.py: realGPTQModel.load+quantize()of a 2-layer tiny Llama (GQA, hidden 64) on CPU with dedup on vs off: elections are{k,v -> q}/{up -> gate}per layer, followers'fwd_counter == 0, and all 14 quantized weight tensors are bit-identical between the two runs.Pre-existing on
main(unrelated, also fail without this branch):test_moe_flag_parsing.py::TestMoEModuleName::test_get_moe_module_name_none_tree,test_subset.py::test_qwen3_5_moe_subset_early_stop…(needs CUDA).Review Requirements
Notes
WIP/draft. Review round 1 (comments 5536991865 / 5536994939 / 5536996271) addressed in e7cfd32: singleton default + explicit opt-in, conflict detection across variants, strict
ok.Qwen3_5_MoeQModel/Llama4QModel(vision-language wrappers) are tagged alongside their text-only classes because they share the same decoder layer module; only the text variants are exercised by the CPU suite.Link to Devin session: https://app.devin.ai/sessions/62474a853aa048bf95738e1e03f06e20
Open in Devin Desktop: https://app.devin.ai/desktop/session/62474a853aa048bf95738e1e03f06e20?variant=devin
Requested by: @Qubitium